/* * 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 java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.jcr.NamespaceException; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Value; import javax.jcr.ValueFactory; import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeDefinition; import javax.jcr.nodetype.NodeTypeExistsException; import javax.jcr.nodetype.NodeTypeIterator; import javax.jcr.nodetype.NodeTypeManager; import javax.jcr.nodetype.PropertyDefinition; import javax.jcr.version.OnParentVersionAction; import org.apache.commons.collections.map.ReferenceMap; import org.apache.jackrabbit.commons.iterator.NodeTypeIteratorAdapter; import org.apache.jackrabbit.jcr2spi.ManagerProvider; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.QNodeDefinition; import org.apache.jackrabbit.spi.QNodeTypeDefinition; import org.apache.jackrabbit.spi.QPropertyDefinition; import org.apache.jackrabbit.spi.commons.QNodeTypeDefinitionImpl; import org.apache.jackrabbit.spi.commons.conversion.NameException; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; import org.apache.jackrabbit.spi.commons.nodetype.AbstractNodeTypeManager; import org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl; import org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A <code>NodeTypeManagerImpl</code> implements a session dependant * NodeTypeManager. */ public class NodeTypeManagerImpl extends AbstractNodeTypeManager implements NodeTypeDefinitionProvider, NodeTypeRegistryListener { /** * Logger instance for this class */ private static Logger log = LoggerFactory.getLogger(NodeTypeManagerImpl.class); /** * The ManagerProvider */ private final ManagerProvider mgrProvider; /** * The wrapped node type registry. */ private final NodeTypeRegistry ntReg; /** * The ValueFactory used to build property definitions. */ private final ValueFactory valueFactory; /** * A cache for <code>NodeType</code> instances created by this * <code>NodeTypeManager</code> */ private final Map<Name, NodeTypeImpl> ntCache; /** * A cache for <code>PropertyDefinition</code> instances created by this * <code>NodeTypeManager</code> */ private final Map<QPropertyDefinition, PropertyDefinition> pdCache; /** * A cache for <code>NodeDefinition</code> instances created by this * <code>NodeTypeManager</code> */ private final Map<QNodeDefinition, NodeDefinition> ndCache; /** * Creates a new <code>NodeTypeManagerImpl</code> instance. * * @param ntReg node type registry * @param mgrProvider the manager provider * @throws RepositoryException If an error occurs. */ public NodeTypeManagerImpl(NodeTypeRegistry ntReg, ManagerProvider mgrProvider) throws RepositoryException { this.mgrProvider = mgrProvider; this.ntReg = ntReg; this.ntReg.addListener(this); this.valueFactory = mgrProvider.getJcrValueFactory(); // setup caches with soft references to node type ntCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); pdCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); ndCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); } private EffectiveNodeTypeProvider entProvider() { return mgrProvider.getEffectiveNodeTypeProvider(); } //-------------------------------------------------------------------------- /** * @see AbstractNodeTypeManager#getNodeType(org.apache.jackrabbit.spi.Name) */ @Override public NodeTypeImpl getNodeType(Name name) throws NoSuchNodeTypeException { synchronized (ntCache) { NodeTypeImpl nt = ntCache.get(name); if (nt == null) { EffectiveNodeType ent = entProvider().getEffectiveNodeType(name); QNodeTypeDefinition def = ntReg.getNodeTypeDefinition(name); nt = new NodeTypeImpl(ent, def, this, mgrProvider); ntCache.put(name, nt); } return nt; } } /** * @see org.apache.jackrabbit.spi.commons.nodetype.AbstractNodeTypeManager#getNamePathResolver() */ @Override public NamePathResolver getNamePathResolver() { return mgrProvider.getNamePathResolver(); } /** * * @param nodeTypeName * @return */ public boolean hasNodeType(Name nodeTypeName) { boolean isRegistered = ntCache.containsKey(nodeTypeName); if (!isRegistered) { isRegistered = ntReg.isRegistered(nodeTypeName); } return isRegistered; } /** * Retrieve the <code>NodeDefinition</code> for the given * <code>QNodeDefinition</code>. * * @param def * @return */ @Override public NodeDefinition getNodeDefinition(QNodeDefinition def) { synchronized (ndCache) { NodeDefinition ndi = ndCache.get(def); if (ndi == null) { ndi = new NodeDefinitionImpl(def, this, getNamePathResolver()); ndCache.put(def, ndi); } return ndi; } } /** * Retrieve the <code>PropertyDefinition</code> for the given * <code>QPropertyDefinition</code>. * * @param def * @return */ @Override public PropertyDefinition getPropertyDefinition(QPropertyDefinition def) { synchronized (pdCache) { PropertyDefinition pdi = pdCache.get(def); if (pdi == null) { pdi = new PropertyDefinitionImpl(def, this, getNamePathResolver(), valueFactory); pdCache.put(def, pdi); } return pdi; } } /** * @return the NodeTypeRegistry */ NodeTypeRegistry getNodeTypeRegistry() { return ntReg; } //-----------------------------------------< NodeTypeDefinitionProvider >--- /** * @see NodeTypeDefinitionProvider#getNodeTypeDefinition(org.apache.jackrabbit.spi.Name) */ public QNodeTypeDefinition getNodeTypeDefinition(Name ntName) throws NoSuchNodeTypeException, RepositoryException { NodeTypeImpl nt = getNodeType(ntName); return nt.getDefinition(); } //-------------------------------------------< NodeTypeRegistryListener >--- /** * {@inheritDoc} */ public void nodeTypeRegistered(Name ntName) { // not interested, ignore } /** * {@inheritDoc} */ public void nodeTypeReRegistered(Name ntName) { // flush all affected cache entries ntCache.remove(ntName); try { String name = getNamePathResolver().getJCRName(ntName); synchronized (pdCache) { Iterator<PropertyDefinition> iter = pdCache.values().iterator(); while (iter.hasNext()) { PropertyDefinition pd = iter.next(); if (name.equals(pd.getDeclaringNodeType().getName())) { iter.remove(); } } } synchronized (ndCache) { Iterator<NodeDefinition> iter = ndCache.values().iterator(); while (iter.hasNext()) { NodeDefinition nd = iter.next(); if (name.equals(nd.getDeclaringNodeType().getName())) { iter.remove(); } } } } catch (NamespaceException e) { log.warn(e.getMessage() + " -> clear definition cache." ); synchronized (pdCache) { pdCache.clear(); } synchronized (ndCache) { ndCache.clear(); } } } /** * {@inheritDoc} */ public void nodeTypeUnregistered(Name ntName) { // flush all affected cache entries ntCache.remove(ntName); try { String name = getNamePathResolver().getJCRName(ntName); synchronized (pdCache) { Iterator<PropertyDefinition> iter = pdCache.values().iterator(); while (iter.hasNext()) { PropertyDefinition pd = iter.next(); if (name.equals(pd.getDeclaringNodeType().getName())) { iter.remove(); } } } synchronized (ndCache) { Iterator<NodeDefinition> iter = ndCache.values().iterator(); while (iter.hasNext()) { NodeDefinition nd = iter.next(); if (name.equals(nd.getDeclaringNodeType().getName())) { iter.remove(); } } } } catch (NamespaceException e) { log.warn(e.getMessage() + " -> clear definition cache." ); synchronized (pdCache) { pdCache.clear(); } synchronized (ndCache) { ndCache.clear(); } } } //----------------------------------------------------< NodeTypeManager >--- /** * {@inheritDoc} */ public NodeTypeIterator getAllNodeTypes() throws RepositoryException { Name[] ntNames = ntReg.getRegisteredNodeTypes(); ArrayList<NodeType> list = new ArrayList<NodeType>(ntNames.length); for (Name ntName : ntNames) { list.add(getNodeType(ntName)); } return new NodeTypeIteratorAdapter(list); } /** * {@inheritDoc} */ public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException { Name[] ntNames = ntReg.getRegisteredNodeTypes(); ArrayList<NodeType> list = new ArrayList<NodeType>(ntNames.length); for (Name ntName : ntNames) { NodeType nt = getNodeType(ntName); if (!nt.isMixin()) { list.add(nt); } } return new NodeTypeIteratorAdapter(list); } /** * {@inheritDoc} */ public NodeTypeIterator getMixinNodeTypes() throws RepositoryException { Name[] ntNames = ntReg.getRegisteredNodeTypes(); ArrayList<NodeType> list = new ArrayList<NodeType>(ntNames.length); for (Name ntName : ntNames) { NodeType nt = getNodeType(ntName); if (nt.isMixin()) { list.add(nt); } } return new NodeTypeIteratorAdapter(list); } /** * {@inheritDoc} */ public NodeType getNodeType(String nodeTypeName) throws NoSuchNodeTypeException { try { Name qName = getNamePathResolver().getQName(nodeTypeName); return getNodeType(qName); } catch (NamespaceException e) { throw new NoSuchNodeTypeException(nodeTypeName, e); } catch (NameException e) { throw new NoSuchNodeTypeException(nodeTypeName, e); } } /** * @see NodeTypeManager#hasNodeType(String) */ public boolean hasNodeType(String name) throws RepositoryException { try { Name qName = getNamePathResolver().getQName(name); return hasNodeType(qName); } catch (NamespaceException e) { return false; } catch (NameException e) { return false; } } /** * @see NodeTypeManager#registerNodeTypes(javax.jcr.nodetype.NodeTypeDefinition[], boolean) */ public NodeTypeIterator registerNodeTypes(NodeTypeDefinition[] ntds, boolean allowUpdate) throws RepositoryException { List<QNodeTypeDefinition> defs = new ArrayList<QNodeTypeDefinition>(ntds.length); for (NodeTypeDefinition definition : ntds) { QNodeTypeDefinition qdef = new QNodeTypeDefinitionImpl(definition, getNamePathResolver(), mgrProvider.getQValueFactory()); if (!allowUpdate && hasNodeType(qdef.getName())) { throw new NodeTypeExistsException("NodeType " + definition.getName() + " already exists."); } defs.add(qdef); } getNodeTypeRegistry().registerNodeTypes(defs, allowUpdate); List<NodeType> nts = new ArrayList<NodeType>(); for (QNodeTypeDefinition def : defs) { nts.add(getNodeType(def.getName())); } return new NodeTypeIteratorAdapter(nts); } /** * @see NodeTypeManager#unregisterNodeTypes(String[]) */ public void unregisterNodeTypes(String[] names) throws RepositoryException { HashSet<Name> ntNames = new HashSet<Name>(); for (String name : names) { ntNames.add(getNamePathResolver().getQName(name)); } getNodeTypeRegistry().unregisterNodeTypes(ntNames); } //-------------------------------------------------------------< Object >--- /** * Returns the the state of this instance in a human readable format. */ public String toString() { StringBuilder builder = new StringBuilder(); builder.append("NodeTypeManager (" + super.toString() + ")\n"); builder.append("All NodeTypes:\n"); try { NodeTypeIterator iter = this.getAllNodeTypes(); while (iter.hasNext()) { NodeType nt = iter.nextNodeType(); builder.append(nt.getName()); builder.append("\n\tSupertypes"); for (NodeType supertype : nt.getSupertypes()) { builder.append("\n\t\t" + supertype.getName()); } builder.append("\n\tMixin\t" + nt.isMixin()); builder.append("\n\tOrderableChildNodes\t" + nt.hasOrderableChildNodes()); builder.append("\n\tPrimaryItemName\t" + (nt.getPrimaryItemName() == null ? "<null>" : nt.getPrimaryItemName())); for (PropertyDefinition aPd : nt.getPropertyDefinitions()) { builder.append("\n\tPropertyDefinition"); builder.append(" (declared in " + aPd.getDeclaringNodeType().getName() + ") "); builder.append("\n\t\tName\t\t" + (aPd.getName())); String type = aPd.getRequiredType() == 0 ? "null" : PropertyType.nameFromValue(aPd.getRequiredType()); builder.append("\n\t\tRequiredType\t" + type); String[] vca = aPd.getValueConstraints(); StringBuffer constraints = new StringBuffer(); if (vca == null) { constraints.append("<null>"); } else { for (String aVca : vca) { if (constraints.length() > 0) { constraints.append(", "); } constraints.append(aVca); } } builder.append("\n\t\tValueConstraints\t" + constraints.toString()); Value[] defVals = aPd.getDefaultValues(); StringBuffer defaultValues = new StringBuffer(); if (defVals == null) { defaultValues.append("<null>"); } else { for (Value defVal : defVals) { if (defaultValues.length() > 0) { defaultValues.append(", "); } defaultValues.append(defVal.getString()); } } builder.append("\n\t\tDefaultValue\t" + defaultValues.toString()); builder.append("\n\t\tAutoCreated\t" + aPd.isAutoCreated()); builder.append("\n\t\tMandatory\t" + aPd.isMandatory()); builder.append("\n\t\tOnVersion\t" + OnParentVersionAction.nameFromValue(aPd.getOnParentVersion())); builder.append("\n\t\tProtected\t" + aPd.isProtected()); builder.append("\n\t\tMultiple\t" + aPd.isMultiple()); } for (NodeDefinition aNd : nt.getChildNodeDefinitions()) { builder.append("\n\tNodeDefinition"); builder.append(" (declared in " + aNd.getDeclaringNodeType() + ") "); builder.append("\n\t\tName\t\t" + aNd.getName()); NodeType[] reqPrimaryTypes = aNd.getRequiredPrimaryTypes(); if (reqPrimaryTypes != null && reqPrimaryTypes.length > 0) { for (NodeType reqPrimaryType : reqPrimaryTypes) { builder.append("\n\t\tRequiredPrimaryType\t" + reqPrimaryType.getName()); } } NodeType defPrimaryType = aNd.getDefaultPrimaryType(); if (defPrimaryType != null) { builder.append("\n\t\tDefaultPrimaryType\t" + defPrimaryType.getName()); } builder.append("\n\t\tAutoCreated\t" + aNd.isAutoCreated()); builder.append("\n\t\tMandatory\t" + aNd.isMandatory()); builder.append("\n\t\tOnVersion\t" + OnParentVersionAction.nameFromValue(aNd.getOnParentVersion())); builder.append("\n\t\tProtected\t" + aNd.isProtected()); builder.append("\n\t\tAllowsSameNameSiblings\t" + aNd.allowsSameNameSiblings()); } } } catch (RepositoryException e) { builder.append(e.getMessage()); } return builder.toString(); } }