You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2009/11/25 15:04:50 UTC

svn commit: r884108 [2/10] - in /jackrabbit/sandbox/JCR-1456: ./ jackrabbit-api/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/ jackrabbit-core/ jackrabbit-core/src...

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Wed Nov 25 14:04:38 2009
@@ -77,6 +77,7 @@
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.query.QueryManagerImpl;
 import org.apache.jackrabbit.core.security.authorization.Permission;
+import org.apache.jackrabbit.core.security.AccessManager;
 import org.apache.jackrabbit.core.state.ChildNodeEntry;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.core.state.ItemStateException;
@@ -520,16 +521,14 @@
         thisState.addChildNodeEntry(name, nodeState.getNodeId());
 
         // add 'auto-create' properties defined in node type
-        PropertyDefinition[] pda = nodeType.getAutoCreatedPropertyDefinitions();
-        for (int i = 0; i < pda.length; i++) {
-            PropertyDefinitionImpl pd = (PropertyDefinitionImpl) pda[i];
+        for (PropertyDefinition aPda : nodeType.getAutoCreatedPropertyDefinitions()) {
+            PropertyDefinitionImpl pd = (PropertyDefinitionImpl) aPda;
             node.createChildProperty(pd.unwrap().getName(), pd.getRequiredType(), pd);
         }
 
         // recursively add 'auto-create' child nodes defined in node type
-        NodeDefinition[] nda = nodeType.getAutoCreatedNodeDefinitions();
-        for (int i = 0; i < nda.length; i++) {
-            NodeDefinitionImpl nd = (NodeDefinitionImpl) nda[i];
+        for (NodeDefinition aNda : nodeType.getAutoCreatedNodeDefinitions()) {
+            NodeDefinitionImpl nd = (NodeDefinitionImpl) aNda;
             node.createChildNode(nd.unwrap().getName(), (NodeTypeImpl) nd.getDefaultPrimaryType(), null);
         }
 
@@ -918,9 +917,8 @@
             setMixinTypesProperty(mixins);
 
             // add 'auto-create' properties defined in mixin type
-            PropertyDefinition[] pda = mixin.getAutoCreatedPropertyDefinitions();
-            for (int i = 0; i < pda.length; i++) {
-                PropertyDefinitionImpl pd = (PropertyDefinitionImpl) pda[i];
+            for (PropertyDefinition aPda : mixin.getAutoCreatedPropertyDefinitions()) {
+                PropertyDefinitionImpl pd = (PropertyDefinitionImpl) aPda;
                 // make sure that the property is not already defined by primary type
                 // or existing mixin's
                 NodeTypeImpl declaringNT = (NodeTypeImpl) pd.getDeclaringNodeType();
@@ -930,9 +928,8 @@
             }
 
             // recursively add 'auto-create' child nodes defined in mixin type
-            NodeDefinition[] nda = mixin.getAutoCreatedNodeDefinitions();
-            for (int i = 0; i < nda.length; i++) {
-                NodeDefinitionImpl nd = (NodeDefinitionImpl) nda[i];
+            for (NodeDefinition aNda : mixin.getAutoCreatedNodeDefinitions()) {
+                NodeDefinitionImpl nd = (NodeDefinitionImpl) aNda;
                 // make sure that the child node is not already defined by primary type
                 // or existing mixin's
                 NodeTypeImpl declaringNT = (NodeTypeImpl) nd.getDeclaringNodeType();
@@ -1663,6 +1660,7 @@
             throw new ItemNotFoundException(
                     this + " has no child node with name " + name);
         }
+
         if (dstName != null && !hasNode(dstName.getName(), dstName.getIndex())) {
             String name;
             try {
@@ -1682,6 +1680,23 @@
                 | ItemValidator.CHECK_CONSTRAINTS;
         session.getValidator().checkModify(this, options, Permission.NONE);
 
+        /*
+        make sure the session is allowed to reorder child nodes.
+        since there is no specific privilege for reordering child nodes,
+        test if the the node to be reordered can be removed and added,
+        i.e. treating reorder similar to a move.
+        TODO: properly deal with sns in which case the index would change upon reorder.
+        */
+        AccessManager acMgr = session.getAccessManager();
+        PathBuilder pb = new PathBuilder(getPrimaryPath());
+        pb.addLast(srcName.getName(), srcName.getIndex());
+        Path childPath = pb.getPath();
+        if (!acMgr.isGranted(childPath, Permission.ADD_NODE | Permission.REMOVE_NODE)) {
+            String msg = "Not allowed to reorder child node " + session.getJCRPath(childPath) + ".";
+            log.debug(msg);
+            throw new AccessDeniedException(msg);
+        }
+        
         ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(data.getNodeState().getChildNodeEntries());
         int srcInd = -1, destInd = -1;
         for (int i = 0; i < list.size(); i++) {
@@ -1786,8 +1801,8 @@
         // create new child node
         NodeImpl node = addNode(nodeName, nodeTypeName, id);
         if (mixinNames != null) {
-            for (int i = 0; i < mixinNames.length; i++) {
-                node.addMixin(mixinNames[i]);
+            for (Name mixinName : mixinNames) {
+                node.addMixin(mixinName);
             }
         }
 
@@ -3409,7 +3424,7 @@
         if (!isNodeType(NameConstants.MIX_REFERENCEABLE)) {
             return PropertyIteratorAdapter.EMPTY;
         }
-        
+
         try {
             StringBuilder stmt = new StringBuilder();
             stmt.append("//*[@").append(ISO9075.encode(name));

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java Wed Nov 25 14:04:38 2009
@@ -27,6 +27,7 @@
 
 import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.lock.LockManagerImpl;
 import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.persistence.PersistenceCopier;
@@ -262,12 +263,18 @@
                 target.createWorkspace(name);
             }
 
+            // Copy all the workspace content
             PersistenceCopier copier = new PersistenceCopier(
                     source.getWorkspaceInfo(name).getPersistenceManager(),
                     target.getWorkspaceInfo(name).getPersistenceManager(),
                     target.getDataStore());
             copier.excludeNode(RepositoryImpl.SYSTEM_ROOT_NODE_ID);
             copier.copy(RepositoryImpl.ROOT_NODE_ID);
+
+            // Copy all the active open-scoped locks
+            LockManagerImpl sourceLockManager = source.getLockManager(name);
+            LockManagerImpl targetLockManager = target.getLockManager(name);
+            targetLockManager.copyOpenScopedLocksFrom(sourceLockManager);
         }
     }
 

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Wed Nov 25 14:04:38 2009
@@ -19,7 +19,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.StringReader;
@@ -33,7 +32,6 @@
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
-import java.util.Iterator;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -383,8 +381,15 @@
             throw e;
         } finally {
             if (!succeeded) {
-                // repository startup failed, clean up...
-                shutdown();
+                try {
+                    // repository startup failed, clean up...
+                    shutdown();
+                } catch (Throwable t) {
+                    // ensure this exception does not overlay the original
+                    // startup exception and only log it
+                    log.error("In addition to startup fail, another unexpected problem " +
+                    		"occurred while shutting down the repository again.", t);
+                }
             }
         }
     }
@@ -448,7 +453,7 @@
                 log.debug("No configuration entry for SecurityManager. Using org.apache.jackrabbit.core.security.simple.SimpleSecurityManager");
                 securityMgr = new SimpleSecurityManager();
             } else {
-                securityMgr = (JackrabbitSecurityManager) smc.newInstance();
+                securityMgr = smc.newInstance(JackrabbitSecurityManager.class);
             }
 
             securityMgr.init(this, securitySession);
@@ -916,7 +921,7 @@
      * @throws NoSuchWorkspaceException if such a workspace does not exist
      * @throws RepositoryException      if some other error occurs
      */
-    LockManager getLockManager(String workspaceName) throws
+    LockManagerImpl getLockManager(String workspaceName) throws
             NoSuchWorkspaceException, RepositoryException {
         // check sanity of this instance
         sanityCheck();
@@ -1065,7 +1070,7 @@
         } else {
             log.debug("Found preauthenticated Subject, try to extend authentication");
             // login either using JAAS or custom LoginModule
-            AuthContext authCtx = getSecurityManager().getAuthContext(null, subject);
+            AuthContext authCtx = getSecurityManager().getAuthContext(null, subject, workspaceName);
             try {
                 authCtx.login();
                 s = createSession(authCtx, workspaceName);
@@ -1322,8 +1327,8 @@
         // now set customized repository descriptor values (if any exist)
         Properties props = getCustomRepositoryDescriptors();
         if (props != null) {
-            for (Iterator it = props.keySet().iterator(); it.hasNext();) {
-                String key = (String) it.next();
+            for (Object o : props.keySet()) {
+                String key = (String) o;
                 setDescriptor(key, props.getProperty(key));
             }
         }
@@ -1396,7 +1401,7 @@
                                                                DataStore dataStore)
             throws RepositoryException {
         try {
-            PersistenceManager pm = (PersistenceManager) pmConfig.newInstance();
+            PersistenceManager pm = pmConfig.newInstance(PersistenceManager.class);
             pm.init(new PMContext(homeDir, fs, rootNodeId, nsReg, ntReg, dataStore));
             return pm;
         } catch (Exception e) {
@@ -1464,16 +1469,15 @@
                 }
             }
             // not preauthenticated -> try login with credentials
-            AuthContext authCtx = getSecurityManager().getAuthContext(credentials, new Subject());
+            AuthContext authCtx = getSecurityManager().getAuthContext(credentials, new Subject(), workspaceName);
             authCtx.login();
 
             // create session, and add SimpleCredentials attributes (JCR-1932)
             SessionImpl session = createSession(authCtx, workspaceName);
             if (credentials instanceof SimpleCredentials) {
                 SimpleCredentials sc = (SimpleCredentials) credentials;
-                String[] names = sc.getAttributeNames();
-                for (int i = 0; i < names.length; i++) {
-                    session.setAttribute(names[i], sc.getAttribute(names[i]));
+                for (String name : sc.getAttributeNames()) {
+                    session.setAttribute(name, sc.getAttribute(name));
                 }
             }
             return session;
@@ -1852,7 +1856,7 @@
          * @return the lock manager for this workspace
          * @throws RepositoryException if the lock manager could not be created
          */
-        protected LockManager getLockManager() throws RepositoryException {
+        protected LockManagerImpl getLockManager() throws RepositoryException {
             if (!isInitialized()) {
                 throw new IllegalStateException("workspace '" + getName()
                         + "' not initialized");

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java Wed Nov 25 14:04:38 2009
@@ -50,8 +50,6 @@
 import org.apache.jackrabbit.core.version.InternalVersionManagerImpl;
 import org.apache.jackrabbit.core.xml.ImportHandler;
 import org.apache.jackrabbit.core.xml.SessionImporter;
-import org.apache.jackrabbit.core.xml.AccessControlImporter;
-import org.apache.jackrabbit.core.xml.ProtectedNodeImporter;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
@@ -86,7 +84,6 @@
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.ValueFactory;
 import javax.jcr.Workspace;
-import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
@@ -280,7 +277,7 @@
         this.rep = rep;
         this.subject = subject;
 
-        userId = retrieveUserId(subject);
+        userId = retrieveUserId(subject, wspConfig.getName());
 
         namePathResolver = new DefaultNamePathResolver(this, this, true);
         ntMgr = new NodeTypeManagerImpl(rep.getNodeTypeRegistry(), this, rep.getDataStore());
@@ -300,8 +297,8 @@
      *
      * @return the userID.
      */
-    protected String retrieveUserId(Subject subject) throws RepositoryException {
-        return rep.getSecurityManager().getUserID(subject);
+    protected String retrieveUserId(Subject subject, String workspaceName) throws RepositoryException {
+        return rep.getSecurityManager().getUserID(subject, workspaceName);
     }
 
     /**
@@ -1166,9 +1163,7 @@
                 ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION;
         getValidator().checkModify(parent, options, Permission.NONE);
 
-        // TODO: make configurable
-        ProtectedNodeImporter pi = new AccessControlImporter(this, this, false, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
-        SessionImporter importer = new SessionImporter(parent, this, uuidBehavior, pi, null);
+        SessionImporter importer = new SessionImporter(parent, this, uuidBehavior, wsp.getConfig().getImportConfig());
         return new ImportHandler(importer, this);
     }
 

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java Wed Nov 25 14:04:38 2009
@@ -81,7 +81,7 @@
      *
      * @return the name of <code>SystemPrincipal</code>.
      */
-    protected String retrieveUserId(Subject subject) throws RepositoryException {
+    protected String retrieveUserId(Subject subject, String workspaceName) throws RepositoryException {
         return new SystemPrincipal().getName();
     }
 

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java Wed Nov 25 14:04:38 2009
@@ -835,7 +835,7 @@
         }
 
         Importer importer = new WorkspaceImporter(parentPath, this,
-                rep.getNodeTypeRegistry(), uuidBehavior);
+                rep.getNodeTypeRegistry(), uuidBehavior, wspConfig.getImportConfig());
         return new ImportHandler(importer, session);
     }
 

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/BeanConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/BeanConfig.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/BeanConfig.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/BeanConfig.java Wed Nov 25 14:04:38 2009
@@ -16,25 +16,28 @@
  */
 package org.apache.jackrabbit.core.config;
 
-import org.apache.commons.collections.BeanMap;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
 import org.apache.jackrabbit.core.util.db.ConnectionFactory;
 import org.apache.jackrabbit.core.util.db.DatabaseAware;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Properties;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
-import java.io.InputStream;
-import java.io.IOException;
-
 /**
  * Bean configuration class. BeanConfig instances contain the class name
  * and property information required to instantiate a class that conforms
  * with the JavaBean conventions.
  */
-public class BeanConfig<T> {
+public class BeanConfig {
 
     private static Logger log = LoggerFactory.getLogger(BeanConfig.class);
 
@@ -167,42 +170,42 @@
      * @return new bean instance
      * @throws ConfigurationException on bean configuration errors
      */
-    public Object newInstance() throws ConfigurationException {
+    @SuppressWarnings("unchecked")
+    public <T> T newInstance(Class<T> klass) throws ConfigurationException {
         try {
-            // Instantiate the object using the default constructor
             Class<?> objectClass =
                 Class.forName(getClassName(), true, getClassLoader());
-            Object object = objectClass.newInstance();
+            if (!klass.isAssignableFrom(objectClass)) {
+                throw new ConfigurationException(
+                        "Configured class "+getClassName()
+                        + " does not implement " + klass.getName()
+                        + ". Please fix the repository configuration.");
+            }
+
+            // Instantiate the object using the default constructor
+            Object instance = objectClass.newInstance();
 
             // Set all configured bean properties
-            BeanMap map = new BeanMap(object);
-            for (Object key : map.keySet()) {
-                String value = properties.getProperty(key.toString());
-                if (value != null) {
-                    map.put(key, value);
+            Map<String, Method> setters = getSetters(objectClass);
+            Enumeration<?> enumeration = properties.propertyNames();
+            while (enumeration.hasMoreElements()) {
+                String name = enumeration.nextElement().toString();
+                Method setter = setters.get(name);
+                if (setter != null) {
+                    String value = properties.getProperty(name);
+                    setProperty(instance, name, setter, value);
+                } else if (validate) {
+                    throw new ConfigurationException(
+                            "Configured class " + getClassName()
+                            + " does not contain a property named " + name);
                 }
             }
 
-            if (object instanceof DatabaseAware) {
-                ((DatabaseAware) object).setConnectionFactory(connectionFactory);
+            if (instance instanceof DatabaseAware) {
+                ((DatabaseAware) instance).setConnectionFactory(connectionFactory);
             }
 
-            if (validate) {
-                // Check that no invalid property names were configured
-                for (Object key : properties.keySet()) {
-                    if (!map.containsKey(key)
-                            && properties.getProperty(key.toString()) != null) {
-                        String msg =
-                            "Configured class " + object.getClass().getName()
-                            + " does not contain the property " + key
-                            + ". Please fix the repository configuration.";
-                        log.error(msg);
-                        throw new ConfigurationException(msg);
-                    }
-                }
-            }
-
-            return (T) object;
+            return (T) instance;
         } catch (ClassNotFoundException e) {
             throw new ConfigurationException(
                     "Configured bean implementation class " + getClassName()
@@ -218,6 +221,70 @@
         }
     }
 
+    private Map<String, Method> getSetters(Class<?> klass) {
+        Map<String, Method> methods = new HashMap<String, Method>();
+        for (Method method : klass.getMethods()) {
+            String name = method.getName();
+            if (name.startsWith("set") && name.length() > 3
+                    && Modifier.isPublic(method.getModifiers())
+                    && !Modifier.isStatic(method.getModifiers())
+                    && Void.TYPE.equals(method.getReturnType())
+                    && method.getParameterTypes().length == 1) {
+                methods.put(
+                        name.substring(3, 4).toLowerCase() + name.substring(4),
+                        method);
+            }
+        }
+        return methods;
+    }
+
+    private void setProperty(
+            Object instance, String name, Method setter, String value)
+            throws ConfigurationException {
+        Class<?> type = setter.getParameterTypes()[0];
+        try {
+            if (type.isAssignableFrom(String.class)
+                || type.isAssignableFrom(Object.class)) {
+                setter.invoke(instance, value);
+            } else if (type.isAssignableFrom(Boolean.TYPE)
+                    || type.isAssignableFrom(Boolean.class)) {
+                setter.invoke(instance, Boolean.valueOf(value));
+            } else if (type.isAssignableFrom(Integer.TYPE)
+                    || type.isAssignableFrom(Integer.class)) {
+                setter.invoke(instance, Integer.valueOf(value));
+            } else if (type.isAssignableFrom(Long.TYPE)
+                    || type.isAssignableFrom(Long.class)) {
+                setter.invoke(instance, Long.valueOf(value));
+            } else if (type.isAssignableFrom(Double.TYPE)
+                    || type.isAssignableFrom(Double.class)) {
+                setter.invoke(instance, Double.valueOf(value));
+            } else {
+                throw new ConfigurationException(
+                        "The type (" + type.getName()
+                        + ") of property " + name + " of class "
+                        + getClassName() + " is not supported");
+            }
+        } catch (NumberFormatException e) {
+            throw new ConfigurationException(
+                    "Invalid number format (" + value + ") for property "
+                    + name + " of class " + getClassName(), e);
+        } catch (InvocationTargetException e) {
+            throw new ConfigurationException(
+                    "Property " + name + " of class "
+                    + getClassName() + " can not be set to \"" + value + "\"",
+                    e);
+        } catch (IllegalAccessException e) {
+            throw new ConfigurationException(
+                    "The setter of property " + name
+                    + " of class " + getClassName() + " can not be accessed",
+                    e);
+        } catch (IllegalArgumentException e) {
+            throw new ConfigurationException(
+                    "Unable to call the setter of property "
+                    + name + " of class " + getClassName(), e);
+        }
+    }
+
     //---------- Configurable class loader support ----------------------------
 
     /**

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/LoginModuleConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/LoginModuleConfig.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/LoginModuleConfig.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/LoginModuleConfig.java Wed Nov 25 14:04:38 2009
@@ -60,12 +60,6 @@
     }
 
     public LoginModule getLoginModule() throws ConfigurationException {
-        Object result = newInstance();
-        if (result instanceof LoginModule) {
-            return (LoginModule) result;
-        } else {
-            throw new ConfigurationException("Invalid login module implementation class "
-                    + getClassName() + ".");
-        }
+        return newInstance(LoginModule.class);
     }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfigurationParser.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfigurationParser.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfigurationParser.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfigurationParser.java Wed Nov 25 14:04:38 2009
@@ -47,6 +47,8 @@
 import java.io.IOException;
 import java.util.Properties;
 import java.util.UUID;
+import java.util.List;
+import java.util.ArrayList;
 
 import javax.jcr.RepositoryException;
 
@@ -193,6 +195,19 @@
     private final ConnectionFactory connectionFactory;
 
     /**
+     * Element specifying the class of principals used to retrieve the userID
+     * in the 'class' attribute.
+     */
+    private static final String USERID_CLASS = "UserIdClass";
+
+    /**
+     * Name of the optional XmlImport config entry inside the workspace configuration.
+     */
+    private static final String IMPORT_ELEMENT = "Import";
+    private static final String IMPORT_PNI_ELEMENT = "ProtectedNodeImporter";
+    private static final String IMPORT_PPI_ELEMENT = "ProtectedPropertyImporter";
+
+    /**
      * Name of the cluster node id file.
      */
     private static final String CLUSTER_NODE_ID_FILE = "cluster_node.id";
@@ -385,12 +400,19 @@
                 wac = parseBeanConfig(smElement, WORKSPACE_ACCESS_ELEMENT);
             }
 
-            BeanConfig umc = null;
+            UserManagerConfig umc = null;
             element = getElement(smElement, USER_MANAGER_ELEMENT, false);
             if (element != null) {
-                umc = parseBeanConfig(smElement, USER_MANAGER_ELEMENT);
+                umc = new UserManagerConfig(parseBeanConfig(smElement, USER_MANAGER_ELEMENT));
             }
-            return new SecurityManagerConfig(bc, wspAttr, wac, umc);
+
+            BeanConfig uidcc = null;
+            element = getElement(smElement, USERID_CLASS, false);
+            if (element != null) {
+                uidcc = parseBeanConfig(element);
+            }
+
+            return new SecurityManagerConfig(bc, wspAttr, wac, umc, uidcc);
         } else {
             return null;
         }
@@ -480,7 +502,6 @@
      * @return workspace configuration
      * @throws ConfigurationException if the configuration is broken
      * @see #parseBeanConfig(Element, String)
-     * @see #parseSearchConfig(Element)
      * @see #parseWorkspaceSecurityConfig(Element)
      */
     public WorkspaceConfig parseWorkspaceConfig(InputSource xml)
@@ -509,7 +530,7 @@
 
         // Clustered attribute
         boolean clustered = Boolean.valueOf(
-                getAttribute(root, CLUSTERED_ATTRIBUTE, "true")).booleanValue();
+                getAttribute(root, CLUSTERED_ATTRIBUTE, "true"));
 
         // Create a temporary parser that contains the ${wsp.name} variable
         Properties tmpVariables = (Properties) getVariables().clone();
@@ -533,9 +554,12 @@
         // workspace specific security configuration
         WorkspaceSecurityConfig workspaceSecurityConfig = tmpParser.parseWorkspaceSecurityConfig(root);
 
+        // optinal config for import handling
+        ImportConfig importConfig = tmpParser.parseImportConfig(root);
+
         return new WorkspaceConfig(
                 home, name, clustered, fsf, pmc, qhf,
-                ismLockingFactory, workspaceSecurityConfig);
+                ismLockingFactory, workspaceSecurityConfig, importConfig);
     }
 
     /**
@@ -589,7 +613,7 @@
                                 className, parseParameters(element));
 
                         QueryHandler handler =
-                            (QueryHandler) config.newInstance();
+                            config.newInstance(QueryHandler.class);
                         try {
                             handler.init(fs, context);
                             return handler;
@@ -634,6 +658,49 @@
     }
 
     /**
+     * Read the optional XmlImport Element of Workspace's configuration. It uses
+     * the following format:
+     * <pre>
+     *   &lt;XmlImport&gt;
+     *     &lt;ProtectedNodeImporter class="..." (optional)&gt;
+     *     &lt;ProtectedNodeImporter class="..." (optional)&gt;
+     *     ...
+     *     &lt;ProtectedPropertyImporter class="..." (optional)&gt;
+     *   &lt;/XmlImport&gt;
+     * </pre>
+     *
+     * @param parent Workspace-Root-Element
+     * @return a new <code>XmlImportConfig</code>
+     * @throws ConfigurationException
+     */
+    public ImportConfig parseImportConfig(Element parent) throws ConfigurationException {
+        List<BeanConfig> protectedNodeImporters = new ArrayList();
+        List<BeanConfig> protectedPropertyImporters = new ArrayList();
+
+        Element element = getElement(parent, IMPORT_ELEMENT, false);
+        if (element != null) {
+            NodeList children = element.getChildNodes();
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child = children.item(i);
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    if (IMPORT_PNI_ELEMENT.equals(child.getNodeName())) {
+                        String className = getAttribute((Element) child, CLASS_ATTRIBUTE);
+                        BeanConfig bc = new BeanConfig(className, parseParameters((Element) child));
+                        bc.setValidate(false);
+                        protectedNodeImporters.add(bc);
+                    } else if (IMPORT_PPI_ELEMENT.equals(child.getNodeName())) {
+                        String className = getAttribute((Element) child, CLASS_ATTRIBUTE);
+                        BeanConfig bc = new BeanConfig(className, parseParameters((Element) child));
+                        bc.setValidate(false);
+                        protectedPropertyImporters.add(bc);
+                    } // else: some other entry -> ignore.
+                }
+            }
+        }
+        return new ImportConfig(protectedNodeImporters, protectedPropertyImporters);
+    }
+
+    /**
      * Returns an ISM locking factory that creates {@link ISMLocking} instances
      * based on the given configuration. ISM locking configuration uses the
      * following format:
@@ -659,14 +726,7 @@
             public ISMLocking getISMLocking() throws RepositoryException {
                 Element element = getElement(parent, ISM_LOCKING_ELEMENT, false);
                 if (element != null) {
-                    BeanConfig config = parseBeanConfig(element);
-                    try {
-                        return (ISMLocking) config.newInstance();
-                    } catch (ClassCastException e) {
-                        throw new RepositoryException(
-                                "Invalid ISMLocking class: "
-                                + config.getClassName(), e);
-                    }
+                    return parseBeanConfig(element).newInstance(ISMLocking.class);
                 } else {
                     return new DefaultISMLocking();
                 }
@@ -801,25 +861,18 @@
             public Journal getJournal(NamespaceResolver resolver)
                     throws RepositoryException {
                 BeanConfig config = parseBeanConfig(cluster, JOURNAL_ELEMENT);
-                Object object = config.newInstance();
-                if (object instanceof Journal) {
-                    Journal journal = (Journal) object;
-                    if (journal instanceof AbstractJournal) {
-                        ((AbstractJournal) journal).setRepositoryHome(home);
-                    }
-                    try {
-                        journal.init(id, resolver);
-                    } catch (JournalException e) {
-                        // TODO: Should JournalException extend RepositoryException?
-                        throw new RepositoryException(
-                                "Journal initialization failed: " + journal, e);
-                    }
-                    return journal;
-                } else {
+                Journal journal = config.newInstance(Journal.class);
+                if (journal instanceof AbstractJournal) {
+                    ((AbstractJournal) journal).setRepositoryHome(home);
+                }
+                try {
+                    journal.init(id, resolver);
+                } catch (JournalException e) {
+                    // TODO: Should JournalException extend RepositoryException?
                     throw new RepositoryException(
-                            "Invalid Journal implementation class: "
-                            + config.getClassName());
+                            "Journal initialization failed: " + journal, e);
                 }
+                return journal;
             }
         };
     }
@@ -885,7 +938,7 @@
                             && DATA_STORE_ELEMENT.equals(child.getNodeName())) {
                         BeanConfig bc =
                             parseBeanConfig(parent, DATA_STORE_ELEMENT);
-                        DataStore store = (DataStore) bc.newInstance();
+                        DataStore store = bc.newInstance(DataStore.class);
                         store.init(directory);
                         return store;
                     }
@@ -915,7 +968,6 @@
     protected RepositoryLockMechanismFactory getRepositoryLockMechanismFactory(final Element root) {
         return new RepositoryLockMechanismFactory() {
             public RepositoryLockMechanism getRepositoryLockMechanism() throws RepositoryException {
-                RepositoryLockMechanism lock = null;
                 NodeList children = root.getChildNodes();
                 for (int i = 0; i < children.getLength(); i++) {
                     Node child = children.item(i);
@@ -923,14 +975,10 @@
                             && REPOSITORY_LOCK_MECHANISM_ELEMENT.equals(child.getNodeName())) {
                         BeanConfig bc =
                             parseBeanConfig(root, REPOSITORY_LOCK_MECHANISM_ELEMENT);
-                        lock = (RepositoryLockMechanism) bc.newInstance();
-                        break;
+                        return bc.newInstance(RepositoryLockMechanism.class);
                     }
                 }
-                if (lock == null) {
-                    lock = new RepositoryLock();
-                }
-                return lock;
+                return new RepositoryLock();
             }
         };
     }
@@ -977,13 +1025,9 @@
         return new FileSystemFactory() {
             public FileSystem getFileSystem() throws RepositoryException {
                 try {
-                    FileSystem fs = (FileSystem) config.newInstance();
+                    FileSystem fs = config.newInstance(FileSystem.class);
                     fs.init();
                     return fs;
-                } catch (ClassCastException e) {
-                    throw new RepositoryException(
-                            "Invalid file system implementation class: "
-                            + config.getClassName(), e);
                 } catch (FileSystemException e) {
                     throw new RepositoryException(
                             "File system initialization failure.", e);

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/SecurityManagerConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/SecurityManagerConfig.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/SecurityManagerConfig.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/SecurityManagerConfig.java Wed Nov 25 14:04:38 2009
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.core.config;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  * Security manager configuration. This bean configuration class
  * is used to create configured security manager objects.
@@ -27,9 +30,19 @@
  */
 public class SecurityManagerConfig extends BeanConfig {
 
+    /**
+     * the default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(SecurityManagerConfig.class);
+
     private final String workspaceName;
     private final BeanConfig workspaceAccessConfig;
-    private final BeanConfig userManagerConfig;
+    private final UserManagerConfig userManagerConfig;
+
+    /**
+     * Optional class used to retrieve userID from the subject.
+     */
+    private final Class uidClass;
 
     /**
      * Creates an security manager configuration object from the
@@ -41,7 +54,7 @@
      */
     public SecurityManagerConfig(BeanConfig config, String workspaceName,
                                  BeanConfig workspaceAccessConfig) {
-        this(config, workspaceName, workspaceAccessConfig, null);
+        this(config, workspaceName, workspaceAccessConfig, null, null);
     }
 
     /**
@@ -55,11 +68,21 @@
      */
     public SecurityManagerConfig(BeanConfig config, String workspaceName,
                                  BeanConfig workspaceAccessConfig,
-                                 BeanConfig userManagerConfig) {
+                                 UserManagerConfig userManagerConfig,
+                                 BeanConfig uidClassConfig) {
         super(config);
         this.workspaceName = workspaceName;
         this.workspaceAccessConfig = workspaceAccessConfig;
         this.userManagerConfig = userManagerConfig;
+        Class cl = null;
+        if (uidClassConfig != null) {
+            try {
+                cl = Class.forName(uidClassConfig.getClassName(), true, uidClassConfig.getClassLoader());
+            } catch (ClassNotFoundException e) {
+                log.error("Configured bean implementation class " + uidClassConfig.getClassName() + " was not found -> Ignoring UserIdClass element.", e);
+            }
+        }
+        this.uidClass = cl;
     }
 
     /**
@@ -86,7 +109,16 @@
      * May be <code>null</code> if the configuration entry is missing (i.e.
      * the system default should be used).
      */
-    public BeanConfig getUserManagerConfig() {
+    public UserManagerConfig getUserManagerConfig() {
         return userManagerConfig;
     }
+    
+    /**
+     * @return Class which is used to retrieve the UserID from the Subject.
+     * @see org.apache.jackrabbit.core.security.JackrabbitSecurityManager#getUserID(javax.security.auth.Subject, String) 
+     * @see javax.security.auth.Subject#getPrincipals(Class)
+     */
+    public Class getUserIdClass() {
+        return uidClass;
+    }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/WorkspaceConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/WorkspaceConfig.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/WorkspaceConfig.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/WorkspaceConfig.java Wed Nov 25 14:04:38 2009
@@ -79,6 +79,15 @@
     private final WorkspaceSecurityConfig workspaceSecurityConfig;
 
     /**
+     * Optional configuration for the xml import behavior. Up to now this consists
+     * of a single configuration point: the treatment
+     * of protected nodes and properties that is defined by a set of classes
+     * implementing {@link org.apache.jackrabbit.core.xml.ProtectedNodeImporter}
+     * or {@link org.apache.jackrabbit.core.xml.ProtectedPropertyImporter}.
+     */
+    private final ImportConfig importConfig;
+
+    /**
      * Creates a workspace configuration object.
      *
      * @param home home directory
@@ -95,6 +104,27 @@
                            QueryHandlerFactory qhf,
                            ISMLockingFactory ismLockingFactory,
                            WorkspaceSecurityConfig workspaceSecurityConfig) {
+        this(home, name, clustered, fsf, pmc, qhf, ismLockingFactory, workspaceSecurityConfig, null);
+    }
+
+    /**
+     * Creates a workspace configuration object.
+     *
+     * @param home home directory
+     * @param name workspace name
+     * @param clustered
+     * @param fsf file system factory
+     * @param pmc persistence manager configuration
+     * @param qhf query handler factory, or <code>null</code> if not configured
+     * @param ismLockingFactory the item state manager locking factory
+     * @param workspaceSecurityConfig the workspace specific security configuration.
+     */
+    public WorkspaceConfig(String home, String name, boolean clustered,
+                           FileSystemFactory fsf, PersistenceManagerConfig pmc,
+                           QueryHandlerFactory qhf,
+                           ISMLockingFactory ismLockingFactory,
+                           WorkspaceSecurityConfig workspaceSecurityConfig,
+                           ImportConfig importConfig) {
         this.home = home;
         this.name = name;
         this.clustered = clustered;
@@ -103,6 +133,7 @@
         this.qhf = qhf;
         this.ismLockingFactory = ismLockingFactory;
         this.workspaceSecurityConfig = workspaceSecurityConfig;
+        this.importConfig = importConfig;
     }
 
     /**
@@ -193,4 +224,11 @@
     public WorkspaceSecurityConfig getSecurityConfig() {
         return workspaceSecurityConfig;
     }
+
+    /**
+     * @return xml import settings
+     */
+    public ImportConfig getImportConfig() {
+        return importConfig;
+    }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java Wed Nov 25 14:04:38 2009
@@ -30,6 +30,11 @@
     private static final long serialVersionUID = 5773949574212570258L;
 
     /**
+     * Chars in a UUID String.
+     */
+    public static final int UUID_FORMATTED_LENGTH = 36;
+
+    /**
      * Number of bytes in a UUID (16).
      */
     public static final int UUID_BYTE_LENGTH = 16;
@@ -113,12 +118,12 @@
     /**
      * Creates a node identifier from the given UUID string.
      *
-     * @see UUID#fromString(String)
+     * @see #fromString(String)
      * @param uuid UUID string
      * @throws IllegalArgumentException if the UUID string is invalid
      */
     public NodeId(String uuid) throws IllegalArgumentException {
-        this(UUID.fromString(uuid));
+        this(fromString(uuid));
     }
 
     /**
@@ -241,4 +246,47 @@
         return (int) ((msb >>> 32) ^ msb ^ (lsb >>> 32) ^ lsb);
     }
 
+    //------------------------------------------------------------< internal >
+
+    /**
+     * Constructs a UUID from a UUID formatted String.
+     *
+     * @param uuidString the String representing a UUID to construct this UUID
+     * @return the UUID created from the given string.
+     * @throws IllegalArgumentException String must be a properly formatted UUID
+     *                                  string
+     */
+    private static UUID fromString(String uuidString)
+            throws IllegalArgumentException {
+        // e.g. f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+        //      012345678901234567890123456789012345
+        int len = uuidString.length();
+        if (len != UUID_FORMATTED_LENGTH) {
+            throw new IllegalArgumentException();
+        }
+        long[] words = new long[2];
+        int b = 0;
+        for (int i = 0; i < UUID_FORMATTED_LENGTH; i++) {
+            int c = uuidString.charAt(i) | 0x20; // to lowercase (will lose some error checking)
+            if (i == 8 || i == 13 || i == 23) {
+                if (c != '-') {
+                    throw new IllegalArgumentException(String.valueOf(i));
+                }
+            } else if (i == 18) {
+                if (c != '-') {
+                    throw new IllegalArgumentException(String.valueOf(i));
+                }
+                b = 1;
+            } else {
+                byte h = (byte) (c & 0x0f);
+                if (c >= 'a' && c <= 'f') {
+                    h += 9;
+                } else if (c < '0' || c > '9') {
+                    throw new IllegalArgumentException();
+                }
+                words[b] = words[b] << 4 | h;
+            }
+        }
+        return new UUID(words[0], words[1]);
+    }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java Wed Nov 25 14:04:38 2009
@@ -490,6 +490,30 @@
     }
 
     /**
+     * Helper method that copies all the active open-scoped locks from the
+     * given source to this lock manager. This method is used when backing
+     * up repositories, and only works correctly when the source lock manager
+     * belongs to the original copy of the workspace being backed up.
+     *
+     * @see org.apache.jackrabbit.core.RepositoryCopier
+     * @param source source lock manager
+     */
+    public void copyOpenScopedLocksFrom(LockManagerImpl source) {
+        source.lockMap.traverse(new PathMap.ElementVisitor<LockInfo>() {
+            public void elementVisited(PathMap.Element<LockInfo> element) {
+                LockInfo info = element.get();
+                if (info.isLive() && !info.isSessionScoped()) {
+                    try {
+                        lockMap.put(element.getPath(), info);
+                    } catch (MalformedPathException e) {
+                        log.warn("Ignoring invalid lock path: " + info, e);
+                    }
+                }
+            }
+        }, false);
+    }
+
+    /**
      * Return the most appropriate lock information for a node. This is either
      * the lock info for the node itself, if it is locked, or a lock info for one
      * of its parents, if that is deep locked.

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java Wed Nov 25 14:04:38 2009
@@ -16,8 +16,21 @@
  */
 package org.apache.jackrabbit.core.observation;
 
-import org.apache.jackrabbit.core.id.ItemId;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.ItemId;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
@@ -25,18 +38,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
-
 /**
  * The <code>EventConsumer</code> class combines the {@link
  * javax.jcr.observation.EventListener} with the implementation of specified
@@ -241,7 +242,13 @@
         EventIterator it = new FilteredEventIterator(events.iterator(),
                 events.getTimestamp(), events.getUserData(), filter, denied);
         if (it.hasNext()) {
+            long time = System.currentTimeMillis();
             listener.onEvent(it);
+            time = System.currentTimeMillis() - time;
+            if (log.isDebugEnabled()) {
+                log.debug("listener {} processed events in {} ms.",
+                        listener.getClass().getName(), time);
+            }
         } else {
             // otherwise skip this listener
         }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java Wed Nov 25 14:04:38 2009
@@ -87,19 +87,22 @@
      *                belongs to.
      * @param itemMgr {@link org.apache.jackrabbit.core.ItemManager} of the passed
      *                <code>Session</code>.
-     * @throws NullPointerException if <code>session</code> or <code>itemMgr</code>
-     *                              is <code>null</code>.
+     * @throws NullPointerException if <code>dispatcher</code>, <code>session</code>
+     *                              or <code>itemMgr</code> is <code>null</code>.
      */
     public ObservationManagerImpl(
             ObservationDispatcher dispatcher, SessionImpl session,
             ItemManager itemMgr, ClusterNode clusterNode) {
+        if (dispatcher == null) {
+            throw new NullPointerException("dispatcher");
+        }
         if (session == null) {
             throw new NullPointerException("session");
         }
         if (itemMgr == null) {
             throw new NullPointerException("itemMgr");
         }
-
+        
         this.dispatcher = dispatcher;
         this.session = session;
         this.itemMgr = itemMgr;

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/SynchronousEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/SynchronousEventListener.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/SynchronousEventListener.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/SynchronousEventListener.java Wed Nov 25 14:04:38 2009
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.core.observation;
 
+import javax.jcr.observation.EventIterator;
 import javax.jcr.observation.EventListener;
 
 /**
@@ -25,6 +26,10 @@
  * the call to {@link javax.jcr.Item#save()} returns. In contrast, a regular
  * {@link javax.jcr.observation.EventListener} might be called after
  * <code>save()</code> returns.
+ * <p/>
+ * <b>Important note</b>: an implementation of {@link SynchronousEventListener}
+ * <b>must not</b> modify content with the thread that calls {@link
+ * #onEvent(EventIterator)} otherwise inconsistencies may occur.
  */
 public interface SynchronousEventListener extends EventListener {
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java Wed Nov 25 14:04:38 2009
@@ -393,8 +393,10 @@
                     val = InternalValue.create(readQName(in));
                     break;
                 case PropertyType.WEAKREFERENCE:
+                    val = InternalValue.create(readID(in), true);
+                    break;
                 case PropertyType.REFERENCE:
-                    val = InternalValue.create(readID(in));
+                    val = InternalValue.create(readID(in), false);
                     break;
                 default:
                     // because writeUTF(String) has a size limit of 64k,

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java Wed Nov 25 14:04:38 2009
@@ -23,6 +23,7 @@
 import org.apache.jackrabbit.core.fs.FileSystemPathUtil;
 import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
+import org.apache.jackrabbit.core.fs.mem.MemoryFileSystem;
 import org.apache.jackrabbit.core.persistence.AbstractPersistenceManager;
 import org.apache.jackrabbit.core.persistence.PMContext;
 import org.apache.jackrabbit.core.persistence.util.BLOBStore;
@@ -290,14 +291,14 @@
 
         wspFS = context.getFileSystem();
 
-        /**
-         * store BLOB data in local file system in a sub directory
-         * of the workspace home directory
-         */
-        LocalFileSystem blobFS = new LocalFileSystem();
-        blobFS.setRoot(new File(context.getHomeDir(), "blobs"));
+        // Choose a FileSystem for the BlobStore based on whether data is persistent or not 
+        if (persistent) {
+            blobFS = new LocalFileSystem();
+            ((LocalFileSystem) blobFS).setRoot(new File(context.getHomeDir(), "blobs"));
+        } else {
+            blobFS = new MemoryFileSystem();
+        }
         blobFS.init();
-        this.blobFS = blobFS;
         blobStore = new FileSystemBLOBStore(blobFS);
 
         if (persistent) {

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java Wed Nov 25 14:04:38 2009
@@ -152,4 +152,18 @@
     public static int getNameLength(String namedValue) {
         return namedValue.indexOf('[') + 1;
     }
+
+    /**
+     * Returns <code>true</code> if the given <code>fieldName</code> denotes a
+     * fulltext field like {@link #FULLTEXT} or a field with a
+     * {@link #FULLTEXT_PREFIX}.
+     *
+     * @param fieldName a field name.
+     * @return <code>true</code> if <code>fieldName</code> is a fulltext field;
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isFulltextField(String fieldName) {
+        return fieldName.equals(FULLTEXT)
+                || fieldName.indexOf(FULLTEXT_PREFIX) != -1;
+    }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java Wed Nov 25 14:04:38 2009
@@ -16,21 +16,23 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
+import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.BufferedOutputStream;
 import java.util.ArrayList;
-import java.util.List;
-import java.util.LinkedHashMap;
 import java.util.Iterator;
-import java.util.NoSuchElementException;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 
-import org.apache.lucene.store.Directory;
 import org.apache.jackrabbit.core.query.lucene.directory.IndexInputStream;
 import org.apache.jackrabbit.core.query.lucene.directory.IndexOutputStream;
+import org.apache.lucene.store.Directory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -147,8 +149,8 @@
         String newName = getFileName();
         boolean success = false;
         try {
-            OutputStream out = new IndexOutputStream(
-                    directory.createOutput(newName));
+            OutputStream out = new BufferedOutputStream(new IndexOutputStream(
+                    directory.createOutput(newName)));
             try {
                 log.debug("Writing IndexInfos {}", newName);
                 DataOutputStream dataOut = new DataOutputStream(out);
@@ -288,7 +290,8 @@
      */
     private void read() throws IOException {
         String fileName = getFileName(generation);
-        InputStream in = new IndexInputStream(directory.openInput(fileName));
+        InputStream in = new BufferedInputStream(new IndexInputStream(
+                directory.openInput(fileName)));
         try {
             LinkedHashMap<String, IndexInfo> indexes = new LinkedHashMap<String, IndexInfo>();
             DataInputStream di = new DataInputStream(in);

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java Wed Nov 25 14:04:38 2009
@@ -16,20 +16,23 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import org.apache.lucene.index.Term;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.List;
-import java.util.Collections;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Semaphore;
-import java.io.IOException;
-
 /**
  * Merges indexes in a separate daemon thread.
  */
@@ -41,11 +44,6 @@
     private static final Logger log = LoggerFactory.getLogger(IndexMerger.class);
 
     /**
-     * Marker task to signal the background thread to quit.
-     */
-    private static final Merge QUIT = new Merge(new Index[0]);
-
-    /**
      * minMergeDocs config parameter.
      */
     private int minMergeDocs = SearchIndex.DEFAULT_MIN_MERGE_DOCS;
@@ -61,11 +59,6 @@
     private int mergeFactor = SearchIndex.DEFAULT_MERGE_FACTOR;
 
     /**
-     * Queue of merge Tasks
-     */
-    private final BlockingQueue<Merge> mergeTasks = new LinkedBlockingQueue<Merge>();
-
-    /**
      * List of <code>IndexBucket</code>s in ascending document limit.
      */
     private final List<IndexBucket> indexBuckets = new ArrayList<IndexBucket>();
@@ -76,14 +69,34 @@
     private final MultiIndex multiIndex;
 
     /**
+     * The executor of the repository.
+     */
+    private final Executor executor;
+
+    /**
+     * Flag that indicates that this index merger is shuting down and should
+     * quit. 
+     */
+    private final AtomicBoolean quit = new AtomicBoolean(false);
+
+    /**
+     * Flag that indicates if this index merger has already been started.
+     * @see #start()
+     */
+    private final AtomicBoolean isStarted = new AtomicBoolean(false);
+
+    /**
      * Monitor object to synchronize merge calculation.
      */
     private final Object lock = new Object();
 
     /**
-     * Mutex that is acquired when replacing indexes on MultiIndex.
+     * Read/write lock for index segment replacement. A shared read lock is
+     * aquired for an index replacement. An exclusive write lock is acquired
+     * when this index merger is shuting down, to prevent further index
+     * replacements.
      */
-    private final Semaphore indexReplacement;
+    private final ReadWriteLock indexReplacement = new ReentrantReadWriteLock();
 
     /**
      * List of merger threads that are currently busy.
@@ -91,32 +104,23 @@
     private final List<Worker> busyMergers = new ArrayList<Worker>();
 
     /**
-     * List of merger threads.
-     */
-    private final List<Worker> workers = new ArrayList<Worker>();
-
-    /**
      * Creates an <code>IndexMerger</code>.
      *
      * @param multiIndex the <code>MultiIndex</code>.
-     * @param numWorkers the number of worker threads to use.
+     * @param executor   the executor of the repository.
      */
-    IndexMerger(MultiIndex multiIndex, int numWorkers) {
+    IndexMerger(MultiIndex multiIndex, Executor executor) {
         this.multiIndex = multiIndex;
-        for (int i = 0; i < numWorkers; i++) {
-            Worker w = new Worker();
-            workers.add(w);
-            busyMergers.add(w);
-        }
-        this.indexReplacement = new Semaphore(workers.size());
+        this.executor = executor;
     }
 
     /**
      * Starts this index merger.
      */
     void start() {
-        for (Thread t : workers) {
-            t.start();
+        isStarted.set(true);
+        for (Worker worker : busyMergers) {
+            worker.unblock();
         }
     }
 
@@ -190,7 +194,6 @@
                     }
                     addMergeTask(new Merge(idxs));
                     if (log.isDebugEnabled()) {
-                        log.debug("merge queue now contains " + mergeTasks.size() + " tasks.");
                         int numBusy;
                         synchronized (busyMergers) {
                             numBusy = busyMergers.size();
@@ -236,21 +239,17 @@
      */
     void dispose() {
         log.debug("dispose IndexMerger");
-        // get all permits for index replacements
+        // get exclusive lock on index replacements
         try {
-            indexReplacement.acquire(workers.size());
+            indexReplacement.writeLock().lockInterruptibly();
         } catch (InterruptedException e) {
-            log.warn("Interrupted while acquiring index replacement permits: " + e);
+            log.warn("Interrupted while acquiring index replacement exclusive lock: " + e);
             // try to stop IndexMerger without the sync
         }
 
-        log.debug("merge queue size: " + mergeTasks.size());
-        // clear task queue
-        mergeTasks.clear();
-
-        // send quit
-        addMergeTask(QUIT);
-        log.debug("quit sent");
+        // set quit
+        quit.set(true);
+        log.debug("quit flag set");
 
         try {
             // give the merger threads some time to quit,
@@ -259,9 +258,13 @@
             // die without being able to finish the merge.
             // at this point it is not possible anymore to replace indexes
             // on the MultiIndex because we hold all indexReplacement permits.
-            for (Thread t : workers) {
-                t.join(500);
-                if (t.isAlive()) {
+            Worker[] workers;
+            synchronized (busyMergers) {
+                workers = busyMergers.toArray(new Worker[busyMergers.size()]);
+            }
+            for (Worker w : workers) {
+                w.join(500);
+                if (w.isAlive()) {
                     log.info("Unable to stop IndexMerger.Worker. Daemon is busy.");
                 } else {
                     log.debug("IndexMerger.Worker thread stopped");
@@ -305,14 +308,17 @@
     //------------------------------< internal >--------------------------------
 
     private void addMergeTask(Merge task) {
-        for (;;) {
-            try {
-                mergeTasks.put(task);
-                break;
-            } catch (InterruptedException e) {
-                // try again
-                Thread.interrupted();
+        // only enqueue if still running
+        if (!quit.get()) {
+            Worker worker = new Worker(task);
+            if (isStarted.get()) {
+                // immediately unblock if this index merger is already started
+                worker.unblock();
+            }
+            synchronized (busyMergers) {
+                busyMergers.add(worker);
             }
+            executor.execute(worker);
         }
     }
 
@@ -448,7 +454,7 @@
         }
     }
 
-    private class Worker extends Thread implements IndexListener {
+    private class Worker implements Runnable, IndexListener {
 
         /**
          * List of id <code>Term</code> that identify documents that were deleted
@@ -456,53 +462,50 @@
          */
         private final List<Term> deletedDocuments = Collections.synchronizedList(new ArrayList<Term>());
 
-        public Worker() {
-            setName("IndexMerger.Worker");
-            setDaemon(true);
+        /**
+         * A latch that is set to zero when this worker is unblocked.
+         */
+        private final CountDownLatch start = new CountDownLatch(1);
+
+        /**
+         * Flat that indicates whether this woker has finished its work.
+         */
+        private final AtomicBoolean terminated = new AtomicBoolean(true);
+
+        /**
+         * The merge task.
+         */
+        private final Merge task;
+
+        /**
+         * Creates a new worker which is initially blocked. Call
+         * {@link #unblock()} to unblock it.
+         *
+         * @param task the merge task.
+         */
+        private Worker(Merge task) {
+            this.task = task;
         }
 
         /**
          * Implements the index merging.
          */
         public void run() {
-            for (;;) {
-                boolean isIdle = false;
-                if (mergeTasks.size() == 0) {
-                    synchronized (busyMergers) {
-                        busyMergers.remove(this);
-                        busyMergers.notifyAll();
-                    }
-                    isIdle = true;
-                }
-                Merge task;
-                for (;;) {
-                    try {
-                        task = mergeTasks.take();
-                        break;
-                    } catch (InterruptedException e) {
-                        // try again
-                        Thread.interrupted();
-                    }
-                }
-                if (task == QUIT) {
-                    synchronized (busyMergers) {
-                        busyMergers.remove(this);
-                    }
-                    // put back QUIT to signal other workers
-                    addMergeTask(task);
-                    break;
-                }
-                if (isIdle) {
-                    synchronized (busyMergers) {
-                        busyMergers.add(this);
+            // worker is initially suspended
+            try {
+                try {
+                    start.await();
+                } catch (InterruptedException e) {
+                    // check if we should quit
+                    if (!quit.get()) {
+                        // enqueue task again and retry with another thread
+                        addMergeTask(task);
                     }
+                    return;
                 }
 
                 log.debug("accepted merge request");
 
-                // reset deleted documents
-                deletedDocuments.clear();
-
                 // get readers
                 String[] names = new String[task.indexes.length];
                 for (int i = 0; i < task.indexes.length; i++) {
@@ -538,15 +541,16 @@
 
                         // inform multi index
                         // if we cannot get the sync immediately we have to quit
-                        if (!indexReplacement.tryAcquire()) {
+                        Lock shared = indexReplacement.readLock();
+                        if (!shared.tryLock()) {
                             log.debug("index merging canceled");
-                            break;
+                            return;
                         }
                         try {
                             log.debug("replace indexes");
                             multiIndex.replaceIndexes(names, index, deletedDocuments);
                         } finally {
-                            indexReplacement.release();
+                            shared.unlock();
                         }
 
                         success = true;
@@ -556,13 +560,24 @@
                             // delete index
                             log.debug("deleting index " + index.getName());
                             multiIndex.deleteIndex(index);
+                            // add task again and retry
+                            addMergeTask(task);
                         }
                     }
                 } catch (Throwable e) {
                     log.error("Error while merging indexes: ", e);
                 }
+            } finally {
+                synchronized (terminated) {
+                    terminated.set(true);
+                    terminated.notifyAll();
+                }
+                synchronized (busyMergers) {
+                    busyMergers.remove(this);
+                    busyMergers.notifyAll();
+                }
+                log.debug("Worker finished");
             }
-            log.info("IndexMerger.Worker terminated");
         }
 
         /**
@@ -572,5 +587,37 @@
             log.debug("document deleted: " + id.text());
             deletedDocuments.add(id);
         }
+
+        /**
+         * Unblocks this worker and allows it to start with the index merging.
+         */
+        void unblock() {
+            start.countDown();
+        }
+
+        /**
+         * Waits until this worker is finished or the specified amount of time
+         * has elapsed.
+         *
+         * @param timeout the timeout in milliseconds.
+         * @throws InterruptedException if the current thread is interrupted
+         *                              while waiting for this worker to
+         *                              terminate.
+         */
+        void join(long timeout) throws InterruptedException {
+            synchronized (terminated) {
+                while (!terminated.get()) {
+                    terminated.wait(timeout);
+                }
+            }
+        }
+
+        /**
+         * @return <code>true</code> if this worker is still alive and not yet
+         *         terminated.
+         */
+        boolean isAlive() {
+            return !terminated.get();
+        }
     }
 }

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java?rev=884108&r1=884107&r2=884108&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java Wed Nov 25 14:04:38 2009
@@ -16,23 +16,32 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.TermEnum;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.Fieldable;
+import org.apache.lucene.index.CorruptIndexException;
 import org.apache.lucene.index.FilterIndexReader;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.TermPositions;
 import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.index.TermPositions;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.Fieldable;
-import org.apache.lucene.document.Field;
-import org.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
-import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
-
-import java.io.IOException;
+import org.slf4j.LoggerFactory;
 
 /**
  * <code>IndexMigration</code> implements a utility that migrates a Jackrabbit
@@ -55,10 +64,12 @@
      *
      * @param index the index to check and migration if needed.
      * @param directoryManager the directory manager.
+     * @param oldSeparatorChar the old separator char that needs to be replaced.
      * @throws IOException if an error occurs while migrating the index.
      */
     public static void migrate(PersistentIndex index,
-                               DirectoryManager directoryManager)
+                               DirectoryManager directoryManager,
+                               char oldSeparatorChar)
             throws IOException {
         Directory indexDir = index.getDirectory();
         log.debug("Checking {} ...", indexDir);
@@ -76,7 +87,7 @@
             TermEnum terms = reader.terms(new Term(FieldNames.PROPERTIES, ""));
             try {
                 Term t = terms.term();
-                if (t.text().indexOf('\uFFFF') == -1) {
+                if (t.text().indexOf(oldSeparatorChar) == -1) {
                     log.debug("Index already migrated");
                     return;
                 }
@@ -102,7 +113,8 @@
                     IndexWriter.MaxFieldLength.UNLIMITED);
             try {
                 IndexReader r = new MigrationIndexReader(
-                        IndexReader.open(index.getDirectory()));
+                        IndexReader.open(index.getDirectory()),
+                        oldSeparatorChar);
                 try {
                     writer.addIndexes(new IndexReader[]{r});
                     writer.close();
@@ -131,8 +143,11 @@
      */
     private static class MigrationIndexReader extends FilterIndexReader {
 
-        public MigrationIndexReader(IndexReader in) {
+        private final char oldSepChar;
+
+        public MigrationIndexReader(IndexReader in, char oldSepChar) {
             super(in);
+            this.oldSepChar = oldSepChar;
         }
 
         public Document document(int n, FieldSelector fieldSelector)
@@ -143,7 +158,7 @@
                 doc.removeFields(FieldNames.PROPERTIES);
                 for (Fieldable field : fields) {
                     String value = field.stringValue();
-                    value = value.replace('\uFFFF', '[');
+                    value = value.replace(oldSepChar, '[');
                     doc.add(new Field(FieldNames.PROPERTIES, value, Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
                 }
             }
@@ -151,17 +166,60 @@
         }
 
         public TermEnum terms() throws IOException {
-            return new MigrationTermEnum(in.terms());
+            List<TermEnum> enums = new ArrayList<TermEnum>();
+            List<String> fieldNames = new ArrayList<String>();
+            for (Object obj : in.getFieldNames(FieldOption.ALL)) {
+                fieldNames.add((String) obj);
+            }
+            Collections.sort(fieldNames);
+            for (String fieldName : fieldNames) {
+                if (fieldName.equals(FieldNames.PROPERTIES)) {
+                    addPropertyTerms(enums);
+                } else {
+                    enums.add(new RangeScan(in, new Term(fieldName, ""), new Term(fieldName, "\uFFFF")));
+                }
+            }
+            return new MigrationTermEnum(new ChainedTermEnum(enums), oldSepChar);
         }
 
         public TermPositions termPositions() throws IOException {
-            return new MigrationTermPositions(in.termPositions());
+            return new MigrationTermPositions(in.termPositions(), oldSepChar);
+        }
+
+        private void addPropertyTerms(List<TermEnum> enums) throws IOException {
+            SortedMap<String, TermEnum> termEnums = new TreeMap<String, TermEnum>(
+                    new Comparator<String>() {
+                        public int compare(String s1, String s2) {
+                            s1 = s1.replace(oldSepChar, '[');
+                            s2 = s2.replace(oldSepChar, '[');
+                            return s1.compareTo(s2);
+                        }
+            });
+            // scan through terms and find embedded field names
+            TermEnum terms = new RangeScan(in,
+                    new Term(FieldNames.PROPERTIES, ""),
+                    new Term(FieldNames.PROPERTIES, "\uFFFF"));
+            String previous = null;
+            while (terms.next()) {
+                Term t = terms.term();
+                String name = t.text().substring(0, t.text().indexOf(oldSepChar) + 1);
+                if (!name.equals(previous)) {
+                    termEnums.put(name, new RangeScan(in,
+                            new Term(FieldNames.PROPERTIES, name),
+                            new Term(FieldNames.PROPERTIES, name + "\uFFFF")));
+                }
+                previous = name;
+            }
+            enums.addAll(termEnums.values());
         }
 
         private static class MigrationTermEnum extends FilterTermEnum {
 
-            public MigrationTermEnum(TermEnum in) {
+            private final char oldSepChar;
+
+            public MigrationTermEnum(TermEnum in, char oldSepChar) {
                 super(in);
+                this.oldSepChar = oldSepChar;
             }
 
             public Term term() {
@@ -171,7 +229,7 @@
                 }
                 if (t.field().equals(FieldNames.PROPERTIES)) {
                     String text = t.text();
-                    return t.createTerm(text.replace('\uFFFF', '['));
+                    return t.createTerm(text.replace(oldSepChar, '['));
                 } else {
                     return t;
                 }
@@ -184,14 +242,17 @@
 
         private static class MigrationTermPositions extends FilterTermPositions {
 
-            public MigrationTermPositions(TermPositions in) {
+            private final char oldSepChar;
+
+            public MigrationTermPositions(TermPositions in, char oldSepChar) {
                 super(in);
+                this.oldSepChar = oldSepChar;
             }
 
             public void seek(Term term) throws IOException {
                 if (term.field().equals(FieldNames.PROPERTIES)) {
                     char[] text = term.text().toCharArray();
-                    text[term.text().indexOf('[')] = '\uFFFF';
+                    text[term.text().indexOf('[')] = oldSepChar;
                     super.seek(term.createTerm(new String(text)));
                 } else {
                     super.seek(term);
@@ -207,4 +268,54 @@
             }
         }
     }
+
+    private static final class ChainedTermEnum extends TermEnum {
+
+        private Queue<TermEnum> queue = new LinkedList<TermEnum>();
+
+        public ChainedTermEnum(Collection<TermEnum> enums) {
+            super();
+            queue.addAll(enums);
+        }
+
+        public boolean next() throws IOException {
+            for (;;) {
+                TermEnum terms = queue.peek();
+                if (terms == null) {
+                    // no more enums
+                    break;
+                }
+                if (terms.next()) {
+                    return true;
+                } else {
+                    queue.remove();
+                    terms.close();
+                }
+            }
+            return false;
+        }
+
+        public Term term() {
+            TermEnum terms = queue.peek();
+            if (terms != null) {
+                return terms.term();
+            }
+            return null;
+        }
+
+        public int docFreq() {
+            TermEnum terms = queue.peek();
+            if (terms != null) {
+                return terms.docFreq();
+            }
+            return 0;
+        }
+
+        public void close() throws IOException {
+            // close remaining
+            while (!queue.isEmpty()) {
+                queue.remove().close();
+            }
+        }
+    }
 }