[exo-jcr-commits] exo-jcr SVN: r3478 - in jcr/trunk/exo.jcr.component.core/src: main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache and 4 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Thu Nov 18 04:45:36 EST 2010


Author: sergiykarpenko
Date: 2010-11-18 04:45:36 -0500 (Thu, 18 Nov 2010)
New Revision: 3478

Added:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryCreationSynchronizer.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryServiceImpl.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/util/jdbc/DBInitializer.java
   jcr/trunk/exo.jcr.component.core/src/main/resources/conf/portal/configuration.xml
   jcr/trunk/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-configuration.xml
   jcr/trunk/exo.jcr.component.core/src/test/resources/test.policy
Log:
EXOJCR-887: patch applied. Warnings added to DBInitializer

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryContainer.java	2010-11-18 09:45:36 UTC (rev 3478)
@@ -505,6 +505,11 @@
       // Init Root and jcr:system if workspace is system workspace
       WorkspaceInitializer wsInitializer =
          (WorkspaceInitializer)workspaceContainer.getComponentInstanceOfType(WorkspaceInitializer.class);
+      RepositoryCreationSynchronizer synchronizer =
+         (RepositoryCreationSynchronizer)getComponentInstanceOfType(RepositoryCreationSynchronizer.class);
+      // The synchronizer will be used to synchronize all the cluster
+      // nodes to prevent any concurrent jcr initialization i.e. EXOJCR-887
+      synchronizer.waitForApproval(wsInitializer.isWorkspaceInitialized());
       wsInitializer.initWorkspace();
    }
 

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryCreationSynchronizer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryCreationSynchronizer.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryCreationSynchronizer.java	2010-11-18 09:45:36 UTC (rev 3478)
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2010 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl;
+
+import org.exoplatform.container.BaseContainerLifecyclePlugin;
+import org.exoplatform.container.ExoContainer;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.container.xml.InitParams;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.rpc.RPCException;
+import org.exoplatform.services.rpc.RPCService;
+import org.exoplatform.services.rpc.RemoteCommand;
+import org.exoplatform.services.rpc.TopologyChangeEvent;
+import org.exoplatform.services.rpc.TopologyChangeListener;
+
+import java.io.Serializable;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This class is used to be able to synchronize the cluster nodes to avoid initializing
+ * several times the same repository which could cause exceptions and prevent the nodes to start.
+ * 
+ * @author <a href="mailto:nicolas.filotto at exoplatform.com">Nicolas Filotto</a>
+ * @version $Id$
+ */
+public class RepositoryCreationSynchronizer
+{
+
+   /**
+    * The logger.
+    */
+   private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.RepositoryCreationSynchronizer");
+
+   /**
+    * The RPC Service used to communicate with other nodes
+    */
+   private final RPCService rpcService;
+
+   /**
+    * The {@link RemoteCommand} used to indicate to the nodes that at least one workspace needs
+    * to be initialized
+    */
+   private RemoteCommand needToInitWorkspaceCommand;
+
+   /**
+    * The {@link RemoteCommand} used to know whether the node have to wait or
+    * not
+    */
+   private RemoteCommand shouldIWaitCommand;
+
+   /**
+    * Indicates whether at least one workspace has to be initialized
+    */
+   private boolean needToInitWorkspace;
+
+   /**
+    * This lock is used to synchronize the start process of all the nodes
+    */
+   private final CountDownLatch lock = new CountDownLatch(1);
+
+   /**
+    * Indicates whether this component has to be disabled or not
+    */
+   private final boolean disabled;
+
+   /**
+    * The default constructor that should be used in a non cluster environment
+    */
+   public RepositoryCreationSynchronizer(ExoContainerContext ctx)
+   {
+      this(null, ctx, null);
+   }
+
+   /**
+    * The default constructor that should be used in a cluster environment
+    */
+   public RepositoryCreationSynchronizer(final RPCService rpcService, ExoContainerContext ctx, InitParams params)
+   {
+      this.rpcService = rpcService;
+      this.disabled =
+         rpcService == null
+            || (params != null && params.getValueParam("disabled") != null && Boolean.valueOf(params.getValueParam(
+               "disabled").getValue()));
+      if (disabled && LOG.isDebugEnabled())
+      {
+         LOG.debug("The RepositoryCreationSynchronizer has been disabled");
+      }
+      if (rpcService != null)
+      {
+         shouldIWaitCommand = rpcService.registerCommand(new RemoteCommand()
+         {
+
+            public String getId()
+            {
+               return "exo.jcr.component.core.RepositoryCreationSynchronizer-shouldIWaitCommand";
+            }
+
+            public Serializable execute(Serializable[] args) throws Throwable
+            {
+               return shouldIWait();
+            }
+         });
+         needToInitWorkspaceCommand = rpcService.registerCommand(new RemoteCommand()
+         {
+
+            public String getId()
+            {
+               return "exo.jcr.component.core.RepositoryCreationSynchronizer-needToInitWorkspaceCommand";
+            }
+
+            public Serializable execute(Serializable[] args) throws Throwable
+            {
+               needToInitWorkspace();
+               return null;
+            }
+         });
+         final RemoteCommand releaseCommand = rpcService.registerCommand(new RemoteCommand()
+         {
+
+            public String getId()
+            {
+               return "exo.jcr.component.core.RepositoryCreationSynchronizer-releaseCommand";
+            }
+
+            public Serializable execute(Serializable[] args) throws Throwable
+            {
+               release();
+               return null;
+            }
+         });
+
+         // Used to release all the nodes if needed once the current node is fully started
+         // We need to wait for a full container start since upper applications could also not
+         // support concurrent JCR initialization
+         ctx.getContainer().addContainerLifecylePlugin(new BaseContainerLifecyclePlugin()
+         {
+            @Override
+            public void startContainer(ExoContainer container) throws Exception
+            {
+               if (needToInitWorkspace)
+               {
+                  needToInitWorkspace = false;
+                  try
+                  {
+                     if (LOG.isDebugEnabled())
+                     {
+                        LOG.debug("Release the other cluster nodes if needed.");
+                     }
+                     rpcService.executeCommandOnAllNodes(releaseCommand, false);
+                  }
+                  catch (Exception e)
+                  {
+                     LOG.error("Could not release all the nodes", e);
+                  }
+               }
+            }
+         });
+         // Used to release the coordinator
+         rpcService.registerTopologyChangeListener(new TopologyChangeListener()
+         {
+            public void onChange(TopologyChangeEvent event)
+            {
+               if (event.isCoordinator())
+               {
+                  release();
+               }
+            }
+         });
+      }
+   }
+
+   /**
+    * Make the current thread wait until the {@link RepositoryCreationSynchronizer}
+    * allows to proceed
+    */
+   public void waitForApproval(boolean isWorkspaceInitialized)
+   {
+
+      if (disabled)
+      {
+         if (LOG.isTraceEnabled())
+         {
+            LOG.trace("The RepositoryCreationSynchronizer has been disabled so no need to make it wait.");
+         }
+         return;
+      }
+      if (rpcService == null)
+      {
+         if (LOG.isTraceEnabled())
+         {
+            LOG.trace("No RPCService has been defined so we assume that we are in"
+               + " a non cluster environment so no need to make it wait.");
+         }
+         return;
+      }
+
+      // The RPC Service has been defined so we assume that we are in a cluster environment
+      if (!isWorkspaceInitialized)
+      {
+         // Set locally the value
+         needToInitWorkspace();
+         // A workspace has not been initialized, we need to notify everybody
+         if (LOG.isDebugEnabled())
+         {
+            LOG.debug("Notify all the nodes that there is at least one workspace to initialize.");
+         }
+         try
+         {
+            rpcService.executeCommandOnAllNodes(needToInitWorkspaceCommand, false);
+         }
+         catch (Exception e)
+         {
+            LOG.warn("Could not notify all the nodes that there is at least one workspace to initialize.", e);
+         }
+      }
+      if (lock.getCount() <= 0)
+      {
+         // We already have been released so no need to wait
+         if (LOG.isTraceEnabled())
+         {
+            LOG.trace("We already have been released so no need to make it wait.");
+         }
+         return;
+      }
+      try
+      {
+         if (!needToInitWorkspace && isWorkspaceInitialized)
+         {
+            // Asking the coordinator if we have to wait since the workspace could be currently
+            // initialized by the coordinator
+            if (LOG.isDebugEnabled())
+            {
+               LOG.debug("Ask the coordinator if the local node needs to wait.");
+            }
+            Object result = rpcService.executeCommandOnCoordinator(shouldIWaitCommand, 0);
+            if (LOG.isDebugEnabled())
+            {
+               LOG.debug("The response from the coordinator was " + result);
+            }
+            if (result instanceof Boolean && (Boolean)result)
+            {
+               // We wait to be release by the coordinator
+               waitForCoordinator();
+            }
+         }
+         else
+         {
+            // We wait to be release by the coordinator
+            waitForCoordinator();
+         }
+      }
+      catch (RPCException e)
+      {
+         LOG.warn("An error occured while executing the method waitForApproval()", e);
+      }
+   }
+
+   /**
+    * Make the current node wait until being released by the coordinator
+    */
+   private void waitForCoordinator()
+   {
+      LOG.info("Waiting to be released by the coordinator");
+      try
+      {
+         lock.await();
+      }
+      catch (InterruptedException e)
+      {
+         Thread.currentThread().interrupt();
+      }
+   }
+
+   /**
+    * Used to know if the nodes need to wait
+    */
+   private boolean shouldIWait()
+   {
+      return needToInitWorkspace;
+   }
+
+   /**
+    * Indicates that there is at least one workspace to initialize
+    */
+   private void needToInitWorkspace()
+   {
+      this.needToInitWorkspace = true;
+   }
+
+   /**
+    * Releases the local node if needed
+    */
+   private void release()
+   {
+      if (lock.getCount() > 0)
+      {
+         LOG.info("The local node has been released.");
+         lock.countDown();
+      }
+   }
+}

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryServiceImpl.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryServiceImpl.java	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/RepositoryServiceImpl.java	2010-11-18 09:45:36 UTC (rev 3478)
@@ -44,8 +44,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.StringTokenizer;
 import java.util.Map.Entry;
-import java.util.StringTokenizer;
 
 import javax.jcr.RepositoryException;
 
@@ -84,6 +84,17 @@
 
    public RepositoryServiceImpl(RepositoryServiceConfiguration configuration, ExoContainerContext context)
    {
+      this(configuration, context, null);
+   }
+
+   /**
+    * @param synchronizer This component is used to synchronize the creation of the repositories between
+    * all the cluster nodes. If this component has been defined in the configuration, it has to
+    * be started before the {@link RepositoryServiceImpl} so we have to enforce the dependency
+    * with this component by adding it to the constructor
+    */
+   public RepositoryServiceImpl(RepositoryServiceConfiguration configuration, ExoContainerContext context, RepositoryCreationSynchronizer synchronizer)
+   {
       this.config = configuration;
       addNodeTypePlugins = new ArrayList<ComponentPlugin>();
       addNamespacesPlugins = new ArrayList<ComponentPlugin>();

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/dataflow/persistent/jbosscache/JBossCacheWorkspaceStorageCache.java	2010-11-18 09:45:36 UTC (rev 3478)
@@ -48,7 +48,9 @@
 import org.jboss.cache.Fqn;
 import org.jboss.cache.Node;
 import org.jboss.cache.config.EvictionRegionConfig;
+import org.jboss.cache.config.Configuration.CacheMode;
 import org.jboss.cache.eviction.ExpirationAlgorithmConfig;
+import org.picocontainer.Startable;
 
 import java.io.IOException;
 import java.io.Serializable;
@@ -98,7 +100,7 @@
  * @author <a href="mailto:peter.nedonosko at exoplatform.com">Peter Nedonosko</a>
  * @version $Id: JBossCacheWorkspaceStorageCache.java 13869 2008-05-05 08:40:10Z pnedonosko $
  */
-public class JBossCacheWorkspaceStorageCache implements WorkspaceStorageCache
+public class JBossCacheWorkspaceStorageCache implements WorkspaceStorageCache, Startable
 {
 
    private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.JBossCacheWorkspaceStorageCache");
@@ -157,6 +159,11 @@
    protected final Fqn<String> rootFqn;
 
    /**
+    * Indicates whether the cache has already been initialized or not
+    */
+   protected volatile boolean initialized;
+
+   /**
     * Node order comparator for getChildNodes().
     */
    class NodesOrderComparator<N extends NodeData> implements Comparator<NodeData>
@@ -349,6 +356,37 @@
       this.childNodesList = Fqn.fromRelativeElements(rootFqn, CHILD_NODES_LIST);
       this.childPropsList = Fqn.fromRelativeElements(rootFqn, CHILD_PROPS_LIST);
 
+      if (cache.getConfiguration().getCacheMode() == CacheMode.LOCAL)
+      {
+         // The cache is local so we have no risk to get replication exception
+         // during the initialization phase
+         init();
+      }
+   }
+
+   /**
+    * Cache constructor with JBossCache JTA transaction support.
+    * 
+    * @param wsConfig WorkspaceEntry workspace config
+    * @throws RepositoryException if error of initialization
+    * @throws RepositoryConfigurationException if error of configuration
+    */
+   public JBossCacheWorkspaceStorageCache(WorkspaceEntry wsConfig, ConfigurationManager cfm)
+      throws RepositoryException, RepositoryConfigurationException
+   {
+      this(wsConfig, null, cfm);
+   }
+
+   /**
+    * Creates, starts and initialize the cache
+    */
+   private void init()
+   {
+      if (initialized)
+      {
+         // The cache has already been initialized
+         return;
+      }
       this.cache.create();
       this.cache.start();
 
@@ -358,22 +396,28 @@
       createResidentNode(childProps);
       createResidentNode(childPropsList);
       createResidentNode(itemsRoot);
+
+      this.initialized = true;
    }
 
    /**
-    * Cache constructor with JBossCache JTA transaction support.
-    * 
-    * @param wsConfig WorkspaceEntry workspace config
-    * @throws RepositoryException if error of initialization
-    * @throws RepositoryConfigurationException if error of configuration
+    * {@inheritDoc} 
     */
-   public JBossCacheWorkspaceStorageCache(WorkspaceEntry wsConfig, ConfigurationManager cfm)
-      throws RepositoryException, RepositoryConfigurationException
+   public void start()
    {
-      this(wsConfig, null, cfm);
+      // To prevent any timeout replication during the initialization of a JCR in a
+      // cluster environment we create and start the cache only during the starting phase
+      init();
    }
 
    /**
+    * {@inheritDoc} 
+    */
+   public void stop()
+   {
+   }
+
+   /**
     * Checks if node with give FQN not exists and creates resident node.
     * @param fqn
     */
@@ -898,7 +942,7 @@
     */
    public boolean isEnabled()
    {
-      return enabled;
+      return enabled && initialized;
    }
 
    // non-public members

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/util/jdbc/DBInitializer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/util/jdbc/DBInitializer.java	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/util/jdbc/DBInitializer.java	2010-11-18 09:45:36 UTC (rev 3478)
@@ -416,7 +416,8 @@
                }
                final Statement finalSt = st;
                final String finalSql = sql;
-               SecurityHelper.doPriviledgedSQLExceptionAction(new PrivilegedExceptionAction<Object>(){
+               SecurityHelper.doPriviledgedSQLExceptionAction(new PrivilegedExceptionAction<Object>()
+               {
                   public Object run() throws Exception
                   {
                      finalSt.executeUpdate(finalSql);
@@ -442,19 +443,38 @@
             LOG.error("Rollback error " + e, e);
          }
 
-         SQLException next = e.getNextException();
-         String errorTrace = "";
-         while (next != null)
+         boolean isAlreadyCreated = false;
+         try
          {
-            errorTrace += next.getMessage() + "; ";
-            next = next.getNextException();
+            isAlreadyCreated = isObjectExists(connection, sql);
          }
-         Throwable cause = e.getCause();
-         String msg =
-            "Could not create db schema of DataSource: '" + containerName + "'. Reason: " + e.getMessage() + "; "
-               + errorTrace + (cause != null ? " (Cause: " + cause.getMessage() + ")" : "") + ". Last command: " + sql;
+         catch (SQLException ce)
+         {
+            LOG.warn("Can not check does the objects from " + sql + " exists");
+         }
 
-         throw new DBInitializerException(msg, e);
+         if (isAlreadyCreated)
+         {
+            LOG.warn("Could not create db schema of DataSource: '" + containerName + "'. Reason: Objects form " + sql
+               + " already exists");
+         }
+         else
+         {
+            SQLException next = e.getNextException();
+            String errorTrace = "";
+            while (next != null)
+            {
+               errorTrace += next.getMessage() + "; ";
+               next = next.getNextException();
+            }
+            Throwable cause = e.getCause();
+            String msg =
+               "Could not create db schema of DataSource: '" + containerName + "'. Reason: " + e.getMessage() + "; "
+                  + errorTrace + (cause != null ? " (Cause: " + cause.getMessage() + ")" : "") + ". Last command: "
+                  + sql;
+
+            throw new DBInitializerException(msg, e);
+         }
       }
       finally
       {

Modified: jcr/trunk/exo.jcr.component.core/src/main/resources/conf/portal/configuration.xml
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/resources/conf/portal/configuration.xml	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/main/resources/conf/portal/configuration.xml	2010-11-18 09:45:36 UTC (rev 3478)
@@ -40,5 +40,13 @@
       <type>org.exoplatform.services.jcr.impl.RepositoryServiceImpl</type>
    </component
    -->
-
+  <component>
+    <type>org.exoplatform.services.jcr.impl.RepositoryCreationSynchronizer</type>
+    <init-params>
+       <value-param>
+          <name>disabled</name>
+          <value>false</value>
+       </value-param>
+    </init-params>
+  </component>
 </configuration>

Modified: jcr/trunk/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-configuration.xml
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-configuration.xml	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/test/resources/conf/standalone/cluster/test-configuration.xml	2010-11-18 09:45:36 UTC (rev 3478)
@@ -215,6 +215,24 @@
     </init-params>   
   </component>
   
+  <component>
+    <key>org.exoplatform.services.rpc.RPCService</key>
+    <type>org.exoplatform.services.rpc.impl.RPCServiceImpl</type>
+    <init-params>
+      <value-param>
+        <name>jgroups-configuration</name>
+        <value>jar:/conf/standalone/cluster/udp-mux.xml</value>
+      </value-param>
+      <value-param>
+        <name>jgroups-cluster-name</name>
+        <value>RPCService-Cluster</value>
+      </value-param>
+      <value-param>
+        <name>jgroups-default-timeout</name>
+        <value>0</value>
+      </value-param>
+    </init-params>
+  </component>  
   <!-- component>
     <key>org.exoplatform.services.transaction.TransactionService</key>
     <type>org.exoplatform.services.transaction.impl.jotm.TransactionServiceJotmImpl</type>

Modified: jcr/trunk/exo.jcr.component.core/src/test/resources/test.policy
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/resources/test.policy	2010-11-18 09:35:51 UTC (rev 3477)
+++ jcr/trunk/exo.jcr.component.core/src/test/resources/test.policy	2010-11-18 09:45:36 UTC (rev 3478)
@@ -9,6 +9,7 @@
 grant codeBase "@TEST_CLASSES at -"{
    permission java.lang.RuntimePermission "createSystemSession";
    permission java.lang.RuntimePermission "manageRepository";
+   permission java.lang.RuntimePermission "accessRPCService";
    permission java.lang.RuntimePermission "invokeInternalAPI";
 };
 



More information about the exo-jcr-commits mailing list