Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/NameResolver.java
===================================================================
--- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/NameResolver.java (revision 0)
+++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/NameResolver.java (revision 0)
@@ -0,0 +1,46 @@
+/*
+ * 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.name;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Resolver for prefixed JCR names and namespace-qualified
+ * {@link QName QNames}.
+ */
+public interface NameResolver {
+
+ /**
+ * Returns the qualified name for the given prefixed JCR name.
+ *
+ * @param name prefixed JCR name
+ * @return qualified name
+ * @throws NameException if the JCR name format is invalid
+ * @throws NamespaceException if the namespace prefix can not be resolved
+ */
+ QName getQName(String name) throws NameException, NamespaceException;
+
+ /**
+ * Returns the prefixed JCR name for the given qualified name.
+ *
+ * @param name qualified name
+ * @return prefixed JCR name
+ * @throws NamespaceException if the namespace URI can not be resolved
+ */
+ String getJCRName(QName name) throws NamespaceException;
+
+}
Property changes on: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/NameResolver.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/CachingNameResolver.java
===================================================================
--- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/CachingNameResolver.java (revision 0)
+++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/CachingNameResolver.java (revision 0)
@@ -0,0 +1,208 @@
+/*
+ * 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.name;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Name resolver decorator that uses a generational cache to speed up
+ * parsing of prefixed JCR names. Uncached names are resolved using the
+ * decorated name resolver.
+ *
+ * The cache consists of three parts: a long term cache and two generations
+ * of recent cache entries. The two generations are used to collect recent new
+ * entries, and those entries that are used within two successive generations
+ * get promoted to the long term cache. The entries within the long term cache
+ * are discarded only when the size of the cache exceeds the given maximum
+ * cache size.
+ */
+public class CachingNameResolver implements NameResolver {
+
+ /**
+ * Default maximum cache size.
+ */
+ private static final int DEFAULT_MAX_CACHE_SIZE = 1000;
+
+ /**
+ * Divisor used to determine the default generation age from the
+ * maximum cache size.
+ */
+ private static final int DEFAULT_AGE_DIVISOR = 10;
+
+ /**
+ * Decorated name resolver.
+ */
+ private final NameResolver resolver;
+
+ /**
+ * Maximum size of the name cache.
+ */
+ private final int maxCacheSize;
+
+ /**
+ * Maximum age of a cache generation.
+ */
+ private final int maxGenerationAge;
+
+ /**
+ * Long term name cache. The cache map is always read-only, cache updates
+ * are performed by atomically replacing the map reference.
+ */
+ private Map cache = new HashMap();
+
+ /**
+ * Old cache generation.
+ */
+ private Map old = new HashMap();
+
+ /**
+ * Young cache generation.
+ */
+ private Map young = Collections.synchronizedMap(new HashMap());
+
+ /**
+ * Age of the young cache generation.
+ */
+ private int generationAge = 0;
+
+ /**
+ * Creates a caching name resolver.
+ *
+ * @param resolver decorated name resolver
+ * @param maxCacheSize maximum size of the long term cache
+ * @param maxGenerationAge maximum age of a cache generation
+ */
+ public CachingNameResolver(
+ NameResolver resolver, int maxCacheSize, int maxGenerationAge) {
+ this.resolver = resolver;
+ this.maxCacheSize = maxCacheSize;
+ this.maxGenerationAge = maxGenerationAge;
+ }
+
+ /**
+ * Creates a caching name resolver using the default generation age for
+ * the given cache size.
+ *
+ * @param resolver decorated name resolver
+ * @param maxCacheSize maximum size of the long term cache
+ */
+ public CachingNameResolver(NameResolver resolver, int maxCacheSize) {
+ this(resolver, maxCacheSize, maxCacheSize / DEFAULT_AGE_DIVISOR);
+ }
+
+ /**
+ * Creates a caching name resolver using the default size and
+ * generation age.
+ *
+ * @param resolver decorated name resolver
+ */
+ public CachingNameResolver(NameResolver resolver) {
+ this(resolver, DEFAULT_MAX_CACHE_SIZE);
+ }
+
+ /**
+ * Increases the age of the current cache generation. When the maximum
+ * age of a generation is reached, the following steps are taken:
+ *
+ * - The union of the two cache generations is calculated
+ * - The union is added to the long term name cache
+ * - If the cache size exceeds the maximum, only the union is kept
+ * - A new cache generation is started
+ *
+ */
+ private synchronized void increaseGenerationAge() {
+ if (++generationAge == maxGenerationAge) {
+ Map persistentEntries = new HashMap();
+ synchronized (old) {
+ Iterator iterator = old.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ if (young.containsKey(entry.getKey())) {
+ persistentEntries.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ if (!persistentEntries.isEmpty()) {
+ Map newCache = new HashMap();
+ newCache.putAll(cache);
+ newCache.putAll(persistentEntries);
+ if (newCache.size() <= maxCacheSize) {
+ cache = newCache;
+ } else {
+ cache = persistentEntries;
+ }
+ }
+
+ old = young;
+ young = Collections.synchronizedMap(new HashMap());
+ generationAge = 0;
+ }
+ }
+
+ //--------------------------------------------------------< NameResolver >
+
+ /**
+ * Returns the qualified name for the given prefixed JCR name. The name
+ * is first looked up form the generational cache and the call gets
+ * delegated to the decorated name resolver only if the cache misses.
+ *
+ * @param name prefixed JCR name
+ * @return qualified name
+ * @throws NameException if the JCR name format is invalid
+ * @throws NamespaceException if the namespace prefix can not be resolved
+ */
+ public QName getQName(String name)
+ throws NameException, NamespaceException {
+ // Check for the name in the long-time cache
+ QName qname = (QName) cache.get(name);
+ if (qname == null) {
+ // Have we already seen this name during this generation?
+ qname = (QName) young.get(name);
+ if (qname == null) {
+ // No, check the old generation or delegate the call
+ qname = (QName) old.get(name);
+ if (qname == null) {
+ qname = resolver.getQName(name);
+ }
+ // Mark the name as resolved during this generation
+ young.put(name, qname);
+ }
+ increaseGenerationAge();
+ }
+ return qname;
+ }
+
+
+ /**
+ * Returns the prefixed JCR name for the given qualified name.
+ * The call is delegated to the decorated name resolver.
+ *
+ * @param name qualified name
+ * @return prefixed JCR name
+ * @throws NamespaceException if the namespace URI can not be resolved
+ */
+ public String getJCRName(QName name) throws NamespaceException {
+ return resolver.getJCRName(name);
+ }
+
+}
Property changes on: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/CachingNameResolver.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/ParsingNameResolver.java
===================================================================
--- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/ParsingNameResolver.java (revision 0)
+++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/ParsingNameResolver.java (revision 0)
@@ -0,0 +1,105 @@
+/*
+ * 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.name;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Name resolver that parsers and formats prefixed JCR names.
+ * A {@link NamespaceResolver} is used for resolving the namespace prefixes.
+ */
+public class ParsingNameResolver implements NameResolver {
+
+ /**
+ * Namespace resolver.
+ */
+ private final NamespaceResolver resolver;
+
+ /**
+ * Creates a parsing name resolver.
+ *
+ * @param resolver namespace resolver
+ */
+ public ParsingNameResolver(NamespaceResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ //--------------------------------------------------------< NameResolver >
+
+ /**
+ * Parses the prefixed JCR name and returns the resolved qualified name.
+ *
+ * @param name prefixed JCR name
+ * @return qualified name
+ * @throws NameException if the JCR name format is invalid
+ * @throws NamespaceException if the namespace prefix can not be resolved
+ */
+ public QName getQName(String name)
+ throws NameException, NamespaceException {
+ // Check for invalid names
+ if (name == null || ".".equals(name) || "..".equals(name)) {
+ throw new IllegalNameException("Illegal name: " + name);
+ }
+
+ // Find the prefix colon, and check for an empty name
+ int colon = name.indexOf(':');
+ int length = name.length();
+ if (colon + 1 == length) {
+ throw new IllegalNameException("Empty local name: " + name);
+ }
+
+ // Check for invalid characters within the local part of the name
+ for (int i = colon + 1; i < length; i++) {
+ char ch = name.charAt(i);
+ if (ch == '/' || ch == ':' || ch == '[' || ch == ']'
+ || ch == '*' || ch == '\'' || ch == '"' || ch == '|'
+ || (Character.isSpaceChar(ch)
+ && !(ch == ' ' && i > colon + 1 && i < length - 1))) {
+ throw new IllegalNameException(
+ "Invalid character in name: " + name);
+ }
+ }
+
+ // Construct and return the QName
+ if (colon == -1) {
+ return new QName(QName.NS_DEFAULT_URI, name);
+ } else {
+ String uri = resolver.getURI(name.substring(0, colon));
+ return new QName(uri, name.substring(colon + 1));
+ }
+ }
+
+ /**
+ * Returns the prefixed JCR name for the given qualified name.
+ * If the name is in the default namespace, then the local name
+ * is returned without a prefix. Otherwise the prefix for the
+ * namespace is resolved and used to construct returned the JCR name.
+ *
+ * @param name qualified name
+ * @return prefixed JCR name
+ * @throws NamespaceException if the namespace URI can not be resolved
+ */
+ public String getJCRName(QName name) throws NamespaceException {
+ String uri = name.getNamespaceURI();
+ if (QName.NS_DEFAULT_URI.equals(uri)) {
+ return name.getLocalName();
+ } else {
+ return resolver.getPrefix(uri) + ":" + name.getLocalName();
+ }
+ }
+
+}
Property changes on: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/name/ParsingNameResolver.java
___________________________________________________________________
Name: svn:eol-style
+ native
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java (revision 493464)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java (working copy)
@@ -21,7 +21,11 @@
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.fs.FileSystemResource;
import org.apache.jackrabbit.name.AbstractNamespaceResolver;
+import org.apache.jackrabbit.name.CachingNameResolver;
import org.apache.jackrabbit.name.NameCache;
+import org.apache.jackrabbit.name.NameException;
+import org.apache.jackrabbit.name.NameResolver;
+import org.apache.jackrabbit.name.ParsingNameResolver;
import org.apache.jackrabbit.name.QName;
import org.apache.jackrabbit.util.XMLChar;
import org.slf4j.Logger;
@@ -83,7 +87,7 @@
private int lastIndex = 0;
- private final CachingNamespaceResolver resolver;
+ private NameResolver resolver;
private final FileSystem nsRegStore;
@@ -102,7 +106,7 @@
throws RepositoryException {
super(true); // enable listener support
this.nsRegStore = nsRegStore;
- resolver = new CachingNamespaceResolver(this, 1000);
+ resolver = new CachingNameResolver(new ParsingNameResolver(this));
load();
}
@@ -383,7 +387,7 @@
}
return uri;
}
-
+
//----------------------------------------------------< NamespaceRegistry >
/**
* {@inheritDoc}
@@ -524,23 +528,28 @@
* {@inheritDoc}
*/
public QName retrieveName(String jcrName) {
- // just delegate to internal cache
- return resolver.retrieveName(jcrName);
+ try {
+ return resolver.getQName(jcrName);
+ } catch (NameException e) {
+ return null;
+ } catch (NamespaceException e) {
+ return null;
+ }
}
public String retrieveName(QName name) {
- // just delegate to internal cache
- return resolver.retrieveName(name);
+ try {
+ return resolver.getJCRName(name);
+ } catch (NamespaceException e) {
+ return null;
+ }
}
public void cacheName(String jcrName, QName name) {
- // just delegate to internal cache
- resolver.cacheName(jcrName, name);
}
public void evictAllNames() {
- // just delegate to internal cache
- resolver.evictAllNames();
+ resolver = new CachingNameResolver(new ParsingNameResolver(this));
}
//-----------------------------------------------< NamespaceEventListener >
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/CachingNamespaceResolver.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/CachingNamespaceResolver.java (revision 493464)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/CachingNamespaceResolver.java (working copy)
@@ -1,164 +0,0 @@
-/*
- * 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.core;
-
-import org.apache.commons.collections.map.LRUMap;
-import org.apache.jackrabbit.name.AbstractNamespaceResolver;
-import org.apache.jackrabbit.name.IllegalNameException;
-import org.apache.jackrabbit.name.NameFormat;
-import org.apache.jackrabbit.name.NamespaceListener;
-import org.apache.jackrabbit.name.NamespaceResolver;
-import org.apache.jackrabbit.name.NoPrefixDeclaredException;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.NameCache;
-import org.apache.jackrabbit.name.UnknownPrefixException;
-
-import javax.jcr.NamespaceException;
-import java.util.Map;
-
-/**
- * Implements a {@link NamespaceResolver} that caches QName to resolved jcr names
- * and vice versa. The cache is invalidated when a namespace uri to prefix
- * mapping is changed.
- */
-class CachingNamespaceResolver
- implements NamespaceResolver, NamespaceListener, NameCache {
-
- /**
- * The base namespace resolver.
- */
- private final AbstractNamespaceResolver base;
-
- /**
- * Maps QName instances to resolved jcr name Strings.
- */
- private final Map qnameToJCRName;
-
- /**
- * Maps resolved jcr name Strings to QName instances.
- */
- private final Map jcrNameToQName;
-
- /**
- * Creates a new CachingNamespaceResolver.
- *
- * @param base a base namespace resolver with support for listener
- * registration.
- * @param cacheSize number of mappings this resolver may cache.
- */
- public CachingNamespaceResolver(AbstractNamespaceResolver base, int cacheSize) {
- this.base = base;
- qnameToJCRName = new LRUMap(cacheSize);
- jcrNameToQName = new LRUMap(cacheSize);
- this.base.addListener(this);
- }
-
- /**
- * @inheritDoc
- */
- public String getURI(String prefix) throws NamespaceException {
- return base.getURI(prefix);
- }
-
- /**
- * @inheritDoc
- */
- public String getPrefix(String uri) throws NamespaceException {
- return base.getPrefix(uri);
- }
-
- /**
- * @deprecated use {@link NameFormat#parse(String, NamespaceResolver)}
- */
- public QName getQName(String name)
- throws IllegalNameException, UnknownPrefixException {
- return NameFormat.parse(name, this);
- }
-
- /**
- * @deprecated use {@link NameFormat#format(QName, NamespaceResolver)}
- */
- public String getJCRName(QName name)
- throws NoPrefixDeclaredException {
- return NameFormat.format(name, this);
- }
-
- /**
- * Disposes this CachingNamespaceResolver.
- */
- public void dispose() {
- base.removeListener(this);
- }
-
- //------------------------------------------------------------< NameCache >
-
- /**
- * @inheritDoc
- */
- public synchronized QName retrieveName(String jcrName) {
- return (QName) jcrNameToQName.get(jcrName);
- }
-
- /**
- * @inheritDoc
- */
- public synchronized String retrieveName(QName name) {
- return (String) qnameToJCRName.get(name);
- }
-
- /**
- * @inheritDoc
- */
- public synchronized void cacheName(String jcrName, QName name) {
- qnameToJCRName.put(name, jcrName);
- jcrNameToQName.put(jcrName, name);
- }
-
- /**
- * @inheritDoc
- */
- public synchronized void evictAllNames() {
- qnameToJCRName.clear();
- jcrNameToQName.clear();
- }
-
- //----------------------------------------------------< NamespaceListener >
-
- /**
- * @inheritDoc
- */
- public void namespaceAdded(String prefix, String uri) {
- // since it is a new namespace there's no need to flush the
- // cached mappings
- }
-
- /**
- * @inheritDoc
- * Invalidates all cached mappings.
- */
- public void namespaceRemapped(String oldPrefix, String newPrefix, String uri) {
- evictAllNames();
- }
-
- /**
- * @inheritDoc
- * Invalidates all cached mappings.
- */
- public void namespaceRemoved(String uri) {
- evictAllNames();
- }
-}