/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.brixcms.workspace; import javax.jcr.RepositoryException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; /** * Contains common and convenience Workspace Manager methods. * * @author Matej Knopp */ public abstract class AbstractWorkspaceManager implements WorkspaceManager { protected static final String NODE_NAME = "brix:workspace"; protected static final String NODE_PATH = "/" + NODE_NAME; protected static final String PROPERTIES_NODE = "properties"; protected static final String DELETED_PROPERTY = "deleted"; // for compatibility with existing workspaces protected final String[] LEGACY_PREFIXES = new String[]{ "brix-workspace-", "brix_ws_"}; protected final String WORKSPACE_PREFIX = "bx_"; private Map<AttributeKeyAndValue, Set<String>> attributeToWorkspaceListMap = new HashMap<AttributeKeyAndValue, Set<String>>(); private Map<String, Map<String, String>> workspaceToWorkspaceAttributesMap = new HashMap<String, Map<String, String>>(); public synchronized List<Workspace> getWorkspacesFiltered( Map<String, String> workspaceAttributes) { if (workspaceAttributes.isEmpty()) { return getWorkspaces(); } else { List<Set<String>> workspaces = new ArrayList<Set<String>>(); for (Entry<String, String> entry : workspaceAttributes.entrySet()) { AttributeKeyAndValue keyAndValue = new AttributeKeyAndValue(entry.getKey(), entry .getValue()); Set<String> w = attributeToWorkspaceListMap.get(keyAndValue); if (w == null || w.isEmpty()) { return Collections.emptyList(); } else { workspaces.add(w); } } Set<String> intersection = intersect(workspaces); List<Workspace> result = new ArrayList<Workspace>(intersection.size()); for (String s : intersection) { result.add(getWorkspace(s)); } return result; } } public Workspace getWorkspace(String workspaceId) { if (workspaceExists(workspaceId)) { return new WorkspaceImpl(workspaceId); } else { return null; } } abstract protected void delete(String workspaceId) throws RepositoryException; abstract protected String getAttribute(String workspaceId, String key); abstract protected Iterator<String> getAttributeKeys(String workspaceId); protected synchronized Iterator<String> getCachedAttributeKeys(String workspaceId) { Map<String, String> attributes = workspaceToWorkspaceAttributesMap.get(workspaceId); if (attributes == null || attributes.isEmpty()) { List<String> empty = Collections.emptyList(); return empty.iterator(); } else { return new ArrayList<String>(attributes.keySet()).iterator(); } } protected String getWorkspaceId(UUID uuid) { final long most = uuid.getMostSignificantBits(); final long least = uuid.getLeastSignificantBits(); final String smost = LongEncoder.encode(most); final String sleast = LongEncoder.encode(least); return WORKSPACE_PREFIX + smost + sleast; } protected AbstractWorkspaceManager initialize() { attributeToWorkspaceListMap.clear(); workspaceToWorkspaceAttributesMap.clear(); return this; } private Set<String> intersect(List<Set<String>> sets) { if (sets.isEmpty()) { return Collections.emptySet(); } else if (sets.size() == 1) { return sets.get(0); } Collections.sort(sets, new Comparator<Set<String>>() { public int compare(Set<String> o1, Set<String> o2) { return o1.size() - o2.size(); } }); Set<String> current = sets.get(0); for (int i = 1; i < sets.size(); ++i) { current = intersect(current, sets.get(i)); } return current; } private Set<String> intersect(Set<String> s1, Set<String> s2) { Set<String> result = new HashSet<String>(s1.size()); for (String s : s1) { if (s2.contains(s)) { result.add(s); } } return result; } protected boolean isBrixWorkspace(String id) { if (id.startsWith(WORKSPACE_PREFIX)) { return true; } for (int i = 0; i < LEGACY_PREFIXES.length; i++) { if (id.startsWith(LEGACY_PREFIXES[i])) { return true; } } return false; } protected synchronized void removeCachedWorkspaceAttributes(String workspaceId) { workspaceToWorkspaceAttributesMap.remove(workspaceId); for (Iterator<Entry<AttributeKeyAndValue, Set<String>>> i = attributeToWorkspaceListMap .entrySet().iterator(); i.hasNext();) { Entry<AttributeKeyAndValue, Set<String>> e = i.next(); if (e.getValue().contains(workspaceId)) { e.getValue().remove(workspaceId); if (e.getValue().isEmpty()) { i.remove(); } } } } abstract protected void setAttribute(String workspaceId, String attributeKey, String attributeValue) throws RepositoryException; protected void setCachedAttribute(String workspaceId, String key, String value) { removeCachedAttribute(workspaceId, key); if (value != null) { Map<String, String> workspaceAttributes = workspaceToWorkspaceAttributesMap .get(workspaceId); if (workspaceAttributes == null) { workspaceAttributes = new HashMap<String, String>(); workspaceToWorkspaceAttributesMap.put(workspaceId, workspaceAttributes); } workspaceAttributes.put(key, value); AttributeKeyAndValue keyAndValue = new AttributeKeyAndValue(key, value); Set<String> workspaces = attributeToWorkspaceListMap.get(keyAndValue); if (workspaces == null) { workspaces = new HashSet<String>(); attributeToWorkspaceListMap.put(keyAndValue, workspaces); } if (!workspaces.contains(workspaceId)) { workspaces.add(workspaceId); } } } protected void removeCachedAttribute(String workspaceId, String key) { String value = getCachedAttribute(workspaceId, key); if (value != null) { Map<String, String> attributes = workspaceToWorkspaceAttributesMap.get(workspaceId); attributes.remove(key); AttributeKeyAndValue keyAndValue = new AttributeKeyAndValue(key, value); Set<String> workspaces = attributeToWorkspaceListMap.get(keyAndValue); if (workspaces != null) { workspaces.remove(workspaceId); } } } protected String getCachedAttribute(String workspaceId, String key) { Map<String, String> attributes = workspaceToWorkspaceAttributesMap.get(workspaceId); if (attributes != null) { return attributes.get(key); } else { return null; } } private class AttributeKeyAndValue { private final String key; private final String value; public AttributeKeyAndValue(String key, String value) { this.key = key; this.value = value; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof AttributeKeyAndValue == false) return false; AttributeKeyAndValue that = (AttributeKeyAndValue) obj; return equals(key, that.key) && equals(value, that.value); } @Override public int hashCode() { return hashCode(key) + 31 * hashCode(value); } private boolean equals(Object o1, Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2)); } private int hashCode(Object o) { return o != null ? o.hashCode() : 0; } } protected class WorkspaceImpl implements Workspace { private final String id; public WorkspaceImpl(String id) { this.id = id; } public String getId() { return id; } public String getAttribute(String attributeKey) { return AbstractWorkspaceManager.this.getAttribute(getId(), attributeKey); } public Iterator<String> getAttributeKeys() { return AbstractWorkspaceManager.this.getAttributeKeys(getId()); } public void setAttribute(String attributeKey, String attributeValue) { try { AbstractWorkspaceManager.this.setAttribute(getId(), attributeKey, attributeValue); } catch (RepositoryException e) { throw new JcrException(e); } } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof WorkspaceImpl == false) { return false; } WorkspaceImpl that = (WorkspaceImpl) obj; if (id == that.id) { return true; } else if (id == null || that.id == null) { return false; } return id.equals(that.id); } @Override public int hashCode() { return id != null ? id.hashCode() : 0; } public void delete() { try { AbstractWorkspaceManager.this.delete(getId()); } catch (RepositoryException e) { throw new JcrException(e); } } } ; }