[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