/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.jcr2spi.nodetype;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QNodeTypeDefinition;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.commons.nodetype.NodeTypeStorage;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import java.util.Map;
import java.util.Iterator;
import java.util.WeakHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
/**
* <code>NodeTypeCache</code> implements a cache for <code>QNodeTypeDefinition</code>s
* on a userId basis.
*/
public class NodeTypeCache {
/**
* The caches per repository service instance
*/
private static final Map<RepositoryService, Map<String, NodeTypeCache>> CACHES_PER_SERVICE = new WeakHashMap<RepositoryService, Map<String, NodeTypeCache>>();
/**
* Maps node type Names to QNodeTypeDefinition
*/
private final Map<Name, QNodeTypeDefinition> nodeTypes = new HashMap<Name, QNodeTypeDefinition>();
/**
* @param service the repository service.
* @param userId the userId. If <code>null</code> this method will return a
* new cache instance for each such call.
* @return the <code>NodeTypeCache</code> instance for the given
* <code>service</code> and <code>userId</code>.
*/
public static NodeTypeCache getInstance(RepositoryService service, String userId) {
// if no userId is provided do not keep the cache
if (userId == null) {
return new NodeTypeCache();
}
Map<String, NodeTypeCache> caches;
synchronized (CACHES_PER_SERVICE) {
caches = CACHES_PER_SERVICE.get(service);
if (caches == null) {
// use soft references for the node type caches
caches = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
CACHES_PER_SERVICE.put(service, caches);
}
}
synchronized (caches) {
NodeTypeCache cache = caches.get(userId);
if (cache == null) {
cache = new NodeTypeCache();
caches.put(userId, cache);
}
return cache;
}
}
private NodeTypeCache() {
}
/**
* Returns an Iterator over all node type definitions registered.
*
* @return
* @throws javax.jcr.RepositoryException
*/
public Iterator<QNodeTypeDefinition> getAllDefinitions(NodeTypeStorage storage)
throws RepositoryException {
Map<Name, QNodeTypeDefinition> allNts = new HashMap<Name, QNodeTypeDefinition>();
for (Iterator<QNodeTypeDefinition> it = storage.getAllDefinitions(); it.hasNext(); ) {
QNodeTypeDefinition def = it.next();
allNts.put(def.getName(), def);
}
// update the cache
synchronized (nodeTypes) {
nodeTypes.clear();
nodeTypes.putAll(allNts);
}
return allNts.values().iterator();
}
/**
* Returns the <code>QNodeTypeDefinition</code>s for the given node type
* names. The implementation is free to return additional definitions e.g.
* dependencies.
*
* @param nodeTypeNames
* @return
* @throws javax.jcr.nodetype.NoSuchNodeTypeException
* @throws RepositoryException
*/
public Iterator<QNodeTypeDefinition> getDefinitions(NodeTypeStorage storage, Name[] nodeTypeNames)
throws NoSuchNodeTypeException, RepositoryException {
List<QNodeTypeDefinition> nts = new ArrayList<QNodeTypeDefinition>();
List<Name> missing = null;
synchronized (nodeTypes) {
for (int i = 0; i < nodeTypeNames.length; i++) {
QNodeTypeDefinition def = nodeTypes.get(nodeTypeNames[i]);
if (def == null) {
if (missing == null) {
missing = new ArrayList<Name>();
}
missing.add(nodeTypeNames[i]);
} else {
nts.add(def);
}
}
}
if (missing != null) {
Name[] ntNames = missing.toArray(new Name[missing.size()]);
Iterator<QNodeTypeDefinition> it = storage.getDefinitions(ntNames);
synchronized (nodeTypes) {
while (it.hasNext()) {
QNodeTypeDefinition def = it.next();
nts.add(def);
nodeTypes.put(def.getName(), def);
}
}
}
return nts.iterator();
}
public void registerNodeTypes(NodeTypeStorage storage,
QNodeTypeDefinition[] nodeTypeDefs,
boolean allowUpdate)
throws RepositoryException {
storage.registerNodeTypes(nodeTypeDefs, allowUpdate);
}
public void unregisterNodeTypes(NodeTypeStorage storage,
Name[] nodeTypeNames)
throws NoSuchNodeTypeException, RepositoryException {
storage.unregisterNodeTypes(nodeTypeNames);
}
/**
* Wraps this <code>NodeTypeCache</code> around the passed
* <code>storage</code> and exposes itself again as a
* <code>NodeTypeStorage</code>.
*
* @param storage the node type storage to wrap.
* @return node type storage instance using this cache.
*/
public NodeTypeStorage wrap(final NodeTypeStorage storage) {
return new NodeTypeStorage() {
public Iterator<QNodeTypeDefinition> getAllDefinitions() throws RepositoryException {
return NodeTypeCache.this.getAllDefinitions(storage);
}
public Iterator<QNodeTypeDefinition> getDefinitions(Name[] nodeTypeNames)
throws NoSuchNodeTypeException, RepositoryException {
return NodeTypeCache.this.getDefinitions(storage, nodeTypeNames);
}
public void registerNodeTypes(QNodeTypeDefinition[] nodeTypeDefs, boolean allowUpdate)
throws RepositoryException {
NodeTypeCache.this.registerNodeTypes(storage, nodeTypeDefs, allowUpdate);
}
public void unregisterNodeTypes(Name[] nodeTypeNames)
throws NoSuchNodeTypeException, RepositoryException {
NodeTypeCache.this.unregisterNodeTypes(storage, nodeTypeNames);
}
};
}
}