Index: NodeTypeRegistry.java =================================================================== --- NodeTypeRegistry.java (revision 368126) +++ NodeTypeRegistry.java (working copy) @@ -222,56 +222,8 @@ return def; } - /** - * Validates and registers the specified collection of NodeTypeDef - * objects. An InvalidNodeTypeDefException is thrown if the - * validation of any of the contained NodeTypeDef objects fails. - *

- * Note that in the case an exception is thrown some node types might have - * been nevertheless successfully registered. - * - * @param ntDefs collection of NodeTypeDef objects - * @throws InvalidNodeTypeDefException - * @throws RepositoryException - * @see #registerNodeType - */ - private synchronized void internalRegister(Collection ntDefs) - throws InvalidNodeTypeDefException, RepositoryException { - ArrayList list = new ArrayList(ntDefs); - // iterate over definitions until there are no more definitions with - // unresolved (i.e. unregistered) dependencies or an error occurs; - int count = -1; // number of registered nt's per iteration - while (list.size() > 0 && count != 0) { - count = 0; - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - NodeTypeDef ntd = (NodeTypeDef) iterator.next(); - // check if definition has unresolved dependencies - if (registeredNTDefs.keySet().containsAll(ntd.getDependencies())) { - // try to register it - internalRegister(ntd); - // remove it from list - iterator.remove(); - // increase count - count++; - } - } - } - if (list.size() > 0) { - StringBuffer msg = new StringBuffer(); - msg.append("the following node types could not be registered because of unresolvable dependencies: "); - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - msg.append(((NodeTypeDef) iterator.next()).getName()); - msg.append(" "); - } - log.error(msg.toString()); - throw new InvalidNodeTypeDefException(msg.toString()); - } - } - private EffectiveNodeType internalRegister(NodeTypeDef ntd) throws InvalidNodeTypeDefException, RepositoryException { QName name = ntd.getName(); @@ -281,7 +233,7 @@ throw new InvalidNodeTypeDefException(msg); } - EffectiveNodeType ent = validateNodeTypeDef(ntd); + EffectiveNodeType ent = validateNodeTypeDef(ntd, this.entCache, this.registeredNTDefs); // store new effective node type instance entCache.put(ent); @@ -428,7 +380,17 @@ } } - private EffectiveNodeType validateNodeTypeDef(NodeTypeDef ntd) + /** + * Validates the specified NodeTypeDef within the context of the two other parameter and + * returns an EffectiveNodeType. + * @param ntd + * @param anEntCache + * @param aRegisteredNTDefCache + * @return The EffectiveNodeType + * @throws InvalidNodeTypeDefException + * @throws RepositoryException + */ + private EffectiveNodeType validateNodeTypeDef(NodeTypeDef ntd, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache) throws InvalidNodeTypeDefException, RepositoryException { /** @@ -462,7 +424,7 @@ log.debug(msg); throw new InvalidNodeTypeDefException(msg); } - if (!registeredNTDefs.containsKey(supertypes[i])) { + if (!aRegisteredNTDefCache.containsKey(supertypes[i])) { String msg = "[" + name + "] invalid supertype: " + supertypes[i]; log.debug(msg); @@ -476,7 +438,7 @@ */ Stack inheritanceChain = new Stack(); inheritanceChain.push(name); - checkForCircularInheritance(supertypes, inheritanceChain); + checkForCircularInheritance(supertypes, inheritanceChain, aRegisteredNTDefCache); } /** @@ -492,7 +454,7 @@ */ if (supertypes != null && supertypes.length > 0) { try { - EffectiveNodeType est = getEffectiveNodeType(supertypes); + EffectiveNodeType est = getEffectiveNodeType(supertypes, anEntCache, aRegisteredNTDefCache); // make sure that all primary types except nt:base extend from nt:base if (!ntd.isMixin() && !QName.NT_BASE.equals(ntd.getName()) && !est.includesNodeType(QName.NT_BASE)) { @@ -620,7 +582,7 @@ for (int j = 0; j < constraints.length; j++) { ReferenceConstraint rc = (ReferenceConstraint) constraints[j]; QName ntName = rc.getNodeTypeName(); - if (!name.equals(ntName) && !registeredNTDefs.containsKey(ntName)) { + if (!name.equals(ntName) && !aRegisteredNTDefCache.containsKey(ntName)) { String msg = "[" + name + "#" + pd.getName() + "] invalid REFERENCE value constraint '" + ntName + "' (unknown node type)"; @@ -674,7 +636,7 @@ * the default primary type must be registered, with one notable * exception: the node type just being registered */ - if (!name.equals(dpt) && !registeredNTDefs.containsKey(dpt)) { + if (!name.equals(dpt) && !aRegisteredNTDefCache.containsKey(dpt)) { String msg = "[" + name + "#" + cnd.getName() + "] invalid default primary type '" + dpt + "'"; log.debug(msg); @@ -686,14 +648,14 @@ */ try { if (!referenceToSelf) { - defaultENT = getEffectiveNodeType(dpt); + defaultENT = getEffectiveNodeType(dpt, anEntCache, aRegisteredNTDefCache); } else { /** * the default primary type is identical with the node * type just being registered; we have to instantiate it * 'manually' */ - ent = EffectiveNodeType.create(this, ntd); + ent = EffectiveNodeType.create(this, ntd, anEntCache, aRegisteredNTDefCache); defaultENT = ent; } if (cnd.isAutoCreated()) { @@ -704,7 +666,7 @@ */ Stack definingNTs = new Stack(); definingNTs.push(name); - checkForCircularNodeAutoCreation(defaultENT, definingNTs); + checkForCircularNodeAutoCreation(defaultENT, definingNTs, anEntCache, aRegisteredNTDefCache); } } catch (NodeTypeConflictException ntce) { String msg = "[" + name + "#" + cnd.getName() @@ -736,7 +698,7 @@ * the required primary type must be registered, with one * notable exception: the node type just being registered */ - if (!name.equals(rpt) && !registeredNTDefs.containsKey(rpt)) { + if (!name.equals(rpt) && !aRegisteredNTDefCache.containsKey(rpt)) { String msg = "[" + name + "#" + cnd.getName() + "] invalid required primary type: " + rpt; log.debug(msg); @@ -759,7 +721,7 @@ */ try { if (!referenceToSelf) { - getEffectiveNodeType(rpt); + getEffectiveNodeType(rpt,anEntCache, aRegisteredNTDefCache); } else { /** * the required primary type is identical with the @@ -767,7 +729,7 @@ * instantiate it 'manually' */ if (ent == null) { - ent = EffectiveNodeType.create(this, ntd); + ent = EffectiveNodeType.create(this, ntd, anEntCache, aRegisteredNTDefCache); } } } catch (NodeTypeConflictException ntce) { @@ -792,7 +754,7 @@ */ if (ent == null) { try { - ent = EffectiveNodeType.create(this, ntd); + ent = EffectiveNodeType.create(this, ntd, anEntCache, aRegisteredNTDefCache); } catch (NodeTypeConflictException ntce) { String msg = "[" + name + "] failed to resolve node type definition"; log.debug(msg); @@ -823,29 +785,50 @@ return rootNodeDef; } + /** + * * @param ntName * @return * @throws NoSuchNodeTypeException */ - public synchronized EffectiveNodeType getEffectiveNodeType(QName ntName) + public EffectiveNodeType getEffectiveNodeType(QName ntName) throws NoSuchNodeTypeException { + return this.getEffectiveNodeType(ntName, this.entCache, this.registeredNTDefs); + } + + /** + * @param ntName + * @return + * @throws NoSuchNodeTypeException + */ + public synchronized EffectiveNodeType getEffectiveNodeType(QName ntName, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache) throws NoSuchNodeTypeException { - // 1. make sure that the specified node type is registered - if (!registeredNTDefs.containsKey(ntName)) { + // 1. make sure that the specified node type exists + if (!aRegisteredNTDefCache.containsKey(ntName)) { throw new NoSuchNodeTypeException(ntName.toString()); } // 2. check if effective node type has already been built WeightedKey key = new WeightedKey(new QName[]{ntName}); - if (entCache.contains(key)) { - return entCache.get(key); + if (anEntCache.contains(key)) { + return anEntCache.get(key); } // 3. build effective node type try { - EffectiveNodeType ent = EffectiveNodeType.create(this, ntName); + NodeTypeDef def = (NodeTypeDef) aRegisteredNTDefCache.get(ntName); + NodeTypeDef ntDef4ENT = null; + // return clone to make sure nobody messes around with the 'live' definition + try { + ntDef4ENT = (NodeTypeDef) def.clone(); + } catch (CloneNotSupportedException e) { + // should never get here + log.fatal("internal error", e); + throw new InternalError(e.getMessage()); + } + EffectiveNodeType ent = EffectiveNodeType.create(this, ntDef4ENT, anEntCache, aRegisteredNTDefCache); // store new effective node type - entCache.put(ent); + anEntCache.put(ent); return ent; } catch (NodeTypeConflictException ntce) { // should never get here as all registered node types have to be valid! @@ -855,17 +838,21 @@ } } + + public EffectiveNodeType getEffectiveNodeType(QName[] ntNames) throws NodeTypeConflictException, NoSuchNodeTypeException { + return this.getEffectiveNodeType(ntNames, this.entCache, this.registeredNTDefs); + } /** * @param ntNames * @return * @throws NodeTypeConflictException * @throws NoSuchNodeTypeException */ - public synchronized EffectiveNodeType getEffectiveNodeType(QName[] ntNames) + public synchronized EffectiveNodeType getEffectiveNodeType(QName[] ntNames, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache) throws NodeTypeConflictException, NoSuchNodeTypeException { - // 1. make sure every single node type is registered + // 1. make sure every single node type exists for (int i = 0; i < ntNames.length; i++) { - if (!registeredNTDefs.containsKey(ntNames[i])) { + if (!aRegisteredNTDefCache.containsKey(ntNames[i])) { throw new NoSuchNodeTypeException(ntNames[i].toString()); } } @@ -873,8 +860,8 @@ WeightedKey key = new WeightedKey(ntNames); // 2. check if aggregate has already been built - if (entCache.contains(key)) { - return entCache.get(key); + if (anEntCache.contains(key)) { + return anEntCache.get(key); } // 3. build aggregate @@ -884,8 +871,8 @@ ArrayList tmpResults = new ArrayList(); while (key.size() > 0) { // check if we've already built this aggregate - if (entCache.contains(key)) { - tmpResults.add(entCache.get(key)); + if (anEntCache.contains(key)) { + tmpResults.add(anEntCache.get(key)); // subtract the result from the temporary key // (which is 'empty' now) key = key.subtract(key); @@ -896,7 +883,7 @@ * aggregate (i.e. the cost of building it) */ boolean foundSubResult = false; - Iterator iter = entCache.keys(); + Iterator iter = anEntCache.keys(); while (iter.hasNext()) { WeightedKey k = (WeightedKey) iter.next(); /** @@ -904,7 +891,7 @@ * we're looking for */ if (key.contains(k)) { - tmpResults.add(entCache.get(k)); + tmpResults.add(anEntCache.get(k)); // subtract the result from the temporary key key = key.subtract(k); foundSubResult = true; @@ -918,16 +905,27 @@ */ QName[] remainder = key.toArray(); for (int i = 0; i < remainder.length; i++) { + NodeTypeDef def = (NodeTypeDef) aRegisteredNTDefCache.get(remainder[i]); + NodeTypeDef clonedDef = null; + // return clone to make sure nobody messes around with the 'live' definition + try { + clonedDef = (NodeTypeDef) def.clone(); + } catch (CloneNotSupportedException e) { + // should never get here + log.fatal("internal error", e); + throw new InternalError(e.getMessage()); + } + EffectiveNodeType ent = - EffectiveNodeType.create(this, remainder[i]); + EffectiveNodeType.create(this, clonedDef, anEntCache, aRegisteredNTDefCache); // store new effective node type - entCache.put(ent); + anEntCache.put(ent); if (result == null) { result = ent; } else { result = result.merge(ent); // store intermediate result (sub-aggregate) - entCache.put(result); + anEntCache.put(result); } } // add aggregate of remaining node types to result list @@ -942,14 +940,14 @@ } else { result = result.merge((EffectiveNodeType) tmpResults.get(i)); // store intermediate result - entCache.put(result); + anEntCache.put(result); } } // we're done return result; } - void checkForCircularInheritance(QName[] supertypes, Stack inheritanceChain) + void checkForCircularInheritance(QName[] supertypes, Stack inheritanceChain, Map aRegisteredNTDefCache) throws InvalidNodeTypeDefException, RepositoryException { for (int i = 0; i < supertypes.length; i++) { QName nt = supertypes[i]; @@ -969,11 +967,13 @@ } try { - QName[] sta = getNodeTypeDef(nt).getSupertypes(); + + NodeTypeDef ntd = (NodeTypeDef) aRegisteredNTDefCache.get(nt); + QName[] sta = ntd.getSupertypes(); if (sta != null && sta.length > 0) { // check recursively inheritanceChain.push(nt); - checkForCircularInheritance(sta, inheritanceChain); + checkForCircularInheritance(sta, inheritanceChain, aRegisteredNTDefCache); inheritanceChain.pop(); } } catch (NoSuchNodeTypeException nsnte) { @@ -985,7 +985,7 @@ } void checkForCircularNodeAutoCreation(EffectiveNodeType childNodeENT, - Stack definingParentNTs) + Stack definingParentNTs, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache) throws InvalidNodeTypeDefException { // check for circularity through default node types of auto-created child nodes // (node type 'a' defines auto-created child node with default node type 'a') @@ -1019,8 +1019,8 @@ if (dnt != null) { // check recursively definingParentNTs.push(definingNT); - checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt), - definingParentNTs); + checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt,anEntCache, aRegisteredNTDefCache), + definingParentNTs,anEntCache, aRegisteredNTDefCache); definingParentNTs.pop(); } } catch (NoSuchNodeTypeException nsnte) { @@ -1098,18 +1098,15 @@ * @throws InvalidNodeTypeDefException * @throws RepositoryException */ - public synchronized void registerNodeTypes(Collection ntDefs) + public synchronized void registerNodeTypes(Collection newNTDefs) throws InvalidNodeTypeDefException, RepositoryException { // exceptions that might be thrown by internalRegister(Collection) RepositoryException re = null; InvalidNodeTypeDefException intde = null; - // store names of currently registered node types before proceeding - HashSet oldNTNames = new HashSet(registeredNTDefs.keySet()); - try { - // validate and register new node type definitions - internalRegister(ntDefs); + internalRegister(newNTDefs); + } catch (RepositoryException e) { // store exception so it can be re-thrown later on re = e; @@ -1117,39 +1114,108 @@ // store exception so it can be re-thrown later on intde = e; } - - /** - * build set of names of actually registered new node types - * (potentially a subset of those specified in ntDefs if an exception - * had been thrown) - */ - HashSet newNTNames = new HashSet(registeredNTDefs.keySet()); - newNTNames.removeAll(oldNTNames); - - if (newNTNames.size() > 0) { - // persist new node type definitions - for (Iterator iter = newNTNames.iterator(); iter.hasNext();) { - QName ntName = (QName) iter.next(); - customNTDefs.add((NodeTypeDef) registeredNTDefs.get(ntName)); - } + boolean allNodeTypeDefsAreValid = re==null && intde==null; + if (allNodeTypeDefsAreValid) { + Iterator validNTDsIterator = newNTDefs.iterator(); + while (validNTDsIterator.hasNext()){ + NodeTypeDef ntd = (NodeTypeDef) validNTDsIterator.next(); + // store property & child node definitions of new node type by id + customNTDefs.add( ntd ); + } persistCustomNodeTypeDefs(customNTDefs); - // notify listeners - for (Iterator iter = newNTNames.iterator(); iter.hasNext();) { - QName ntName = (QName) iter.next(); - notifyRegistered(ntName); + for (Iterator iter = newNTDefs.iterator(); iter.hasNext();) { + NodeTypeDef ntDef = (NodeTypeDef) iter.next(); + notifyRegistered(ntDef.getName()); } + } else { + // re-throw the exception + if (re != null) { + throw re; + } else if (intde != null) { + throw intde; + } } - - // re-throw exception as necessary - if (re != null) { - throw re; - } else if (intde != null) { - throw intde; - } } /** + * Validates and registers the specified collection of NodeTypeDef + * objects. An InvalidNodeTypeDefException is thrown if the + * validation of any of the contained NodeTypeDef objects fails. + *

+ * Note that in the case an exception is thrown no node type will be registered. + * + * @param newNTDefs collection of NodeTypeDef objects + * @throws InvalidNodeTypeDefException + * @throws RepositoryException + * @see #registerNodeType + */ + private synchronized void internalRegister(Collection newNTDefs) throws InvalidNodeTypeDefException, RepositoryException { + + // cache of pre-built aggregations of node types + EffectiveNodeTypeCache anEntCache = new EffectiveNodeTypeCache(this.entCache); + + // map of node type names and node type definitions + Map aRegisteredNTDefCache = new HashMap(this.registeredNTDefs); + + // temporarily register a clone of the node type definition + // and do some checks by the way + Iterator ntdNameIterator = newNTDefs.iterator(); + while (ntdNameIterator.hasNext()){ + Object ntdObject = ntdNameIterator.next(); + // check if the right type is used + if (! (ntdObject instanceof NodeTypeDef) ){ + String msg = "The specified object is not of type org.apache.jackrabbit.core.nodetype.NodeTypeDef"; + log.debug(msg); + throw new InvalidNodeTypeDefException(msg); + } else { + // check if the ntd is new + NodeTypeDef ntd = (NodeTypeDef) ntdObject; + QName name = ntd.getName(); + if (name != null && registeredNTDefs.containsKey(name)) { + String msg = name + " already exists"; + log.debug(msg); + throw new InvalidNodeTypeDefException(msg); + } + // clone the ntd and add it to the cache + NodeTypeDef clonedNTD=null; + try { + clonedNTD = (NodeTypeDef) ntd.clone(); + } catch (CloneNotSupportedException e) { + // should never get here + log.fatal("internal error", e); + throw new InternalError(e.getMessage()); + } + aRegisteredNTDefCache.put(clonedNTD.getName(), clonedNTD); + } + } + Iterator ntdIterator = newNTDefs.iterator(); + while (ntdIterator.hasNext()){ + NodeTypeDef ntd = (NodeTypeDef) ntdIterator.next(); + + EffectiveNodeType ent = validateNodeTypeDef(ntd, anEntCache, aRegisteredNTDefCache); + + // store new effective node type instance + anEntCache.put(ent); + } + // as no exception occured at this point, the ntds are valid + Iterator validNTDsIterator = newNTDefs.iterator(); + while (validNTDsIterator.hasNext()){ + NodeTypeDef ntd = (NodeTypeDef) validNTDsIterator.next(); + this.registeredNTDefs.put(ntd.getName(), ntd); + // store property & child node definitions of new node type by id + PropDef[] pda = ntd.getPropertyDefs(); + for (int i = 0; i < pda.length; i++) { + propDefs.put(pda[i].getId(), pda[i]); + } + NodeDef[] nda = ntd.getChildNodeDefs(); + for (int i = 0; i < nda.length; i++) { + nodeDefs.put(nda[i].getId(), nda[i]); + } + } + } + + /** * @param nodeTypeName * @throws NoSuchNodeTypeException * @throws RepositoryException @@ -1216,7 +1282,7 @@ /** * validate new node type definition */ - validateNodeTypeDef(ntd); + validateNodeTypeDef(ntd, this.entCache, this.registeredNTDefs); /** * build diff of current and new definition and determine type of change @@ -1810,101 +1876,4 @@ return set.toString() + " (" + weight + ")"; } } - - /** - * EfectiveNodeTypeCache ... - */ - private class EffectiveNodeTypeCache { - // ordered set of keys - final TreeSet sortedKeys; - // cache of pre-build aggregations of node types - final HashMap aggregates; - - EffectiveNodeTypeCache() { - sortedKeys = new TreeSet(); - aggregates = new HashMap(); - } - - void put(EffectiveNodeType ent) { - // we define the weight as the total number of included node types - // (through aggregation and inheritance) - int weight = ent.getAllNodeTypes().length; - // the effective node type is identified by the list of merged - // (i.e. aggregated) node types - WeightedKey k = new WeightedKey(ent.getMergedNodeTypes(), weight); - aggregates.put(k, ent); - sortedKeys.add(k); - } - - boolean contains(QName[] ntNames) { - return aggregates.containsKey(new WeightedKey(ntNames)); - } - - boolean contains(WeightedKey key) { - return aggregates.containsKey(key); - } - - EffectiveNodeType get(QName[] ntNames) { - return (EffectiveNodeType) aggregates.get(new WeightedKey(ntNames)); - } - - EffectiveNodeType get(WeightedKey key) { - return (EffectiveNodeType) aggregates.get(key); - } - - EffectiveNodeType remove(QName[] ntNames) { - return remove(new WeightedKey(ntNames)); - } - - EffectiveNodeType remove(WeightedKey key) { - EffectiveNodeType removed = (EffectiveNodeType) aggregates.remove(key); - if (removed != null) { - // remove index entry - - // FIXME: can't simply call TreeSet.remove(key) because the entry - // in sortedKeys might have a different weight and would thus - // not be found - Iterator iter = sortedKeys.iterator(); - while (iter.hasNext()) { - WeightedKey k = (WeightedKey) iter.next(); - // WeightedKey.equals(Object) ignores the weight - if (key.equals(k)) { - sortedKeys.remove(k); - break; - } - } - } - return removed; - } - - /** - * Returns an iterator over the keys. The order of the returned keys is: - *

- * - * @see NodeTypeRegistry.WeightedKey#compareTo - */ - Iterator keys() { - return sortedKeys.iterator(); - } - - //---------------------------------------------------------< Dumpable > - /** - * {@inheritDoc} - */ - public void dump(PrintStream ps) { - ps.println("EffectiveNodeTypeCache (" + this + ")"); - ps.println(); - ps.println("EffectiveNodeTypes in cache:"); - ps.println(); - Iterator iter = sortedKeys.iterator(); - while (iter.hasNext()) { - WeightedKey k = (WeightedKey) iter.next(); - //EffectiveNodeType ent = (EffectiveNodeType) aggregates.get(k); - ps.println(k); - } - } - } }