diff --git a/config/alfresco/activities/activities-feed-context.xml b/config/alfresco/activities/activities-feed-context.xml
index 2e10391e66..0e837163b0 100644
--- a/config/alfresco/activities/activities-feed-context.xml
+++ b/config/alfresco/activities/activities-feed-context.xml
@@ -20,7 +20,7 @@
-
+ classpath:alfresco/activities/activities-SqlMapConfig.xml
diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index bd991ef1d7..72813d2765 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -100,8 +100,8 @@
-
- ${system.cluster.name}
+
+ ${alfresco.cluster.name}
@@ -506,6 +506,9 @@
+
+
+
diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index 7c99d30b6c..09264d4250 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -27,6 +27,9 @@
+
+ SYSTEM_PROPERTIES_MODE_OVERRIDE
+
diff --git a/config/alfresco/domain/hibernate-cfg.properties b/config/alfresco/domain/hibernate-cfg.properties
index d90bbe5bd2..9ba44d22b8 100644
--- a/config/alfresco/domain/hibernate-cfg.properties
+++ b/config/alfresco/domain/hibernate-cfg.properties
@@ -13,4 +13,7 @@ hibernate.default_batch_fetch_size=1
hibernate.jdbc.batch_size=32
hibernate.connection.release_mode=auto
hibernate.connection.isolation=2
-#hibernate.jdbc.use_get_generated_keys=true
+
+#hibernate.query.substitutions=
+#hibernate.jdbc.use_get_generated_keys=false
+#hibernate.default_schema=
diff --git a/config/alfresco/extension/custom-repository-context.xml.sample b/config/alfresco/extension/custom-repository-context.xml.sample
index 60077ad653..c32edb00f1 100644
--- a/config/alfresco/extension/custom-repository-context.xml.sample
+++ b/config/alfresco/extension/custom-repository-context.xml.sample
@@ -23,13 +23,23 @@
classpath:alfresco/extension/custom-repository.properties
+
+ SYSTEM_PROPERTIES_MODE_OVERRIDE
+
-
+
+
+
+ hibernate.dialect
+ hibernate.query.substitutions
+ hibernate.jdbc.use_get_generated_keys
+ hibernate.default_schema
+
+ classpath:alfresco/domain/hibernate-cfg.properties
-
classpath:alfresco/extension/custom-hibernate-dialect.properties
diff --git a/config/alfresco/extension/custom-repository.properties.sample b/config/alfresco/extension/custom-repository.properties.sample
index 6e055eca46..3fb9e8b048 100644
--- a/config/alfresco/extension/custom-repository.properties.sample
+++ b/config/alfresco/extension/custom-repository.properties.sample
@@ -16,10 +16,11 @@
#db.pool.max=100
#
-# Activate index tracking and recovery
+# Sample settings to activate a cluster
#
#index.tracking.cronExpression=0/5 * * * * ?
#index.recovery.mode=AUTO
+#alfresco.cluster.name=alfresco-cluster
#
# Property to control whether schema updates are performed automatically.
diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml
index e75d7126f2..fe7867c761 100644
--- a/config/alfresco/hibernate-context.xml
+++ b/config/alfresco/hibernate-context.xml
@@ -4,7 +4,15 @@
-
+
+
+
+ hibernate.dialect
+ hibernate.query.substitutions
+ hibernate.jdbc.use_get_generated_keys
+ hibernate.default_schema
+
+ classpath:alfresco/domain/hibernate-cfg.properties
diff --git a/config/alfresco/index-recovery-context.xml b/config/alfresco/index-recovery-context.xml
index 8efd2c2663..4cb4597592 100644
--- a/config/alfresco/index-recovery-context.xml
+++ b/config/alfresco/index-recovery-context.xml
@@ -121,6 +121,9 @@
+
+ ${alfresco.cluster.name}
+
@@ -161,6 +164,9 @@
+
+ ${alfresco.cluster.name}
+
diff --git a/config/alfresco/jgroups-default.xml b/config/alfresco/jgroups-default.xml
index b95066fac3..4ea0e87e69 100644
--- a/config/alfresco/jgroups-default.xml
+++ b/config/alfresco/jgroups-default.xml
@@ -11,14 +11,14 @@
description="Using TCP as transport">
-
+
+
+
+ eps
+
+
+
+ jp2
+ jpx
+ jpm
+ jpc
+ j2k
+ jpf
+
+
+
+ mpp
+
+
+
+ psd
+
+
+
+ fm
+
+
+
+ pmd
+ pm6
+ p65
+ pm
+
+
+
+ prn
+
+
txtcsv
@@ -23,15 +60,14 @@
body
- mw
+ mwxhtml
-
+ ai
- epsps
@@ -42,6 +78,12 @@
acp
+
+ vsd
+
+
+ xdp
+ ausnd
@@ -142,11 +184,6 @@
jpegjpe
-
- jpx
- jp2
- jpm
- svg
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index f92c899687..3b886c144c 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -61,7 +61,7 @@ system.bootstrap.config_check.strict=true
# The name of the cluster
# Leave this empty to disable cluster entry
-system.cluster.name=
+alfresco.cluster.name=
#
# How long should shutdown wait to complete normally before
diff --git a/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatReceiver.java b/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatReceiver.java
index df3a14ff03..de6aebd83e 100644
--- a/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatReceiver.java
+++ b/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatReceiver.java
@@ -58,6 +58,7 @@ public class JGroupsKeepAliveHeartbeatReceiver extends ReceiverAdapter
private final JGroupsKeepAliveHeartbeatSender heartbeatSender;
private final Channel channel;
private boolean stopped;
+ private View lastView;
private final ThreadPoolExecutor threadPool;
private final Set rmiUrlsProcessingQueue;
@@ -189,9 +190,28 @@ public class JGroupsKeepAliveHeartbeatReceiver extends ReceiverAdapter
@Override
public void viewAccepted(View newView)
{
- if (logger.isDebugEnabled())
+ if (EqualsHelper.nullSafeEquals(lastView, newView))
{
- logger.debug("Cluster view changed: " + newView);
+ // No change, so ignore
+ return;
}
+ int lastSize = (lastView == null) ? 0 : lastView.getMembers().size();
+ int newSize = newView.getMembers().size();
+ // Report
+ if (newSize < lastSize)
+ {
+ logger.warn("\n" +
+ "New cluster view with fewer members: \n" +
+ " Last View: " + lastView + "\n" +
+ " New View: " + newView);
+ }
+ else
+ {
+ logger.info("\n" +
+ "New cluster view with additional members: \n" +
+ " Last View: " + lastView + "\n" +
+ " New View: " + newView);
+ }
+ lastView = newView;
}
}
diff --git a/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatSender.java b/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatSender.java
index b8ee2d3197..45e6626240 100644
--- a/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatSender.java
+++ b/source/java/org/alfresco/repo/cache/jgroups/JGroupsKeepAliveHeartbeatSender.java
@@ -31,6 +31,7 @@ import net.sf.ehcache.CacheManager;
import net.sf.ehcache.distribution.CachePeer;
import org.alfresco.repo.jgroups.AlfrescoJGroupsChannelFactory;
+import org.alfresco.util.VmShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jgroups.Address;
@@ -60,6 +61,8 @@ public final class JGroupsKeepAliveHeartbeatSender
lastHeartbeatSendUrls = heartbeatUrls;
}
+ private VmShutdownListener vmShutdownListener;
+
private final CacheManager cacheManager;
private final Channel heartbeatChannel;
private long heartBeatInterval;
@@ -77,7 +80,7 @@ public final class JGroupsKeepAliveHeartbeatSender
Channel heartbeatChannel,
long heartBeatInterval)
{
-
+ this.vmShutdownListener = new VmShutdownListener("JGroupsKeepAliveHeartbeatSender");
this.cacheManager = cacheManager;
this.heartbeatChannel = heartbeatChannel;
this.heartBeatInterval = heartBeatInterval;
@@ -112,6 +115,7 @@ public final class JGroupsKeepAliveHeartbeatSender
public void init()
{
serverThread = new HeartbeatSenderThread();
+ serverThread.setDaemon(true);
serverThread.start();
}
@@ -149,7 +153,7 @@ public final class JGroupsKeepAliveHeartbeatSender
logger.debug("\n" +
"Starting cache peer URLs heartbeat");
}
- while (!stopped)
+ while (!stopped && !vmShutdownListener.isVmShuttingDown())
{
try
{
@@ -172,6 +176,11 @@ public final class JGroupsKeepAliveHeartbeatSender
{
logger.debug("Heartbeat sending failed: ", e);
}
+ // Quick exit if necessary
+ if (stopped || vmShutdownListener.isVmShuttingDown())
+ {
+ break;
+ }
// Wait for the next heartbeat
synchronized (this)
{
diff --git a/source/java/org/alfresco/repo/domain/ibatis/AlfrescoSqlMapClientFactoryBean.java b/source/java/org/alfresco/repo/domain/ibatis/AlfrescoSqlMapClientFactoryBean.java
new file mode 100644
index 0000000000..920e014e69
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/ibatis/AlfrescoSqlMapClientFactoryBean.java
@@ -0,0 +1,47 @@
+package org.alfresco.repo.domain.ibatis;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.hibernate.cfg.Environment;
+import org.springframework.core.io.Resource;
+import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
+
+import com.ibatis.sqlmap.client.SqlMapClient;
+
+/**
+ * Extension to the SQLMap factory to produce SqlMapClient instances that
+ * cater for Alfresco extensions.
+ *
+ * Currently, this is just a hack to find the Hibernate dialect and provide that as
+ * a property to the factory code. This will go away if we move over to iBatis; to be
+ * replaced with something similar to the schema script loading that uses a hierarchy
+ * of databases.
+ *
+ * @author Derek Hulley
+ * @since 3.1
+ */
+public class AlfrescoSqlMapClientFactoryBean extends SqlMapClientFactoryBean
+{
+
+ @Override
+ protected SqlMapClient buildSqlMapClient(Resource configLocation, Properties properties) throws IOException
+ {
+ // Get the Hibernate dialect from the system properties
+ String hibernateDialect = System.getProperty(Environment.DIALECT);
+ if (hibernateDialect == null)
+ {
+ return super.buildSqlMapClient(configLocation, properties);
+ }
+ else
+ {
+ if (properties == null)
+ {
+ properties = new Properties();
+ }
+ properties.put("hibernate.dialect", hibernateDialect);
+ return super.buildSqlMapClient(configLocation, properties);
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
index 15c16a6667..c0c1d8d995 100644
--- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
+++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
@@ -970,8 +970,14 @@ public class SchemaBootstrap extends AbstractLifecycleBean
private void changeDialect(Configuration cfg)
{
String dialectName = cfg.getProperty(Environment.DIALECT);
- if (dialectName == null)
+ if (dialectName == null || dialectName.length() == 0)
{
+ // Look for it on the system properties
+ dialectName = System.getProperty("hibernate.dialect");
+ if (dialectName != null)
+ {
+ cfg.setProperty(Environment.DIALECT, dialectName);
+ }
return;
}
// TODO: https://issues.alfresco.com/jira/browse/ETHREEOH-679
diff --git a/source/java/org/alfresco/repo/jgroups/AlfrescoJGroupsChannelFactory.java b/source/java/org/alfresco/repo/jgroups/AlfrescoJGroupsChannelFactory.java
index 4452d845c5..cef949c17c 100644
--- a/source/java/org/alfresco/repo/jgroups/AlfrescoJGroupsChannelFactory.java
+++ b/source/java/org/alfresco/repo/jgroups/AlfrescoJGroupsChannelFactory.java
@@ -153,13 +153,33 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
}
/**
- * Close all channels. All the channels will continue to function, but will be replaced
- * internally with dummy channels. Effectively, all the cluster communications will be
- * closed down.
+ * Close all channels. All the channels will be closed and will cease to function.
*/
private static void closeChannels()
{
- changeClusterNamePrefix(null);
+ for (Map.Entry entry : channels.entrySet())
+ {
+ ChannelProxy channelProxy = entry.getValue();
+
+ // Close the channel via the proxy
+ try
+ {
+ channelProxy.close();
+ channelProxy.shutdown();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("\n" +
+ "Closed channel: " + channelProxy);
+ }
+ }
+ catch (Throwable e)
+ {
+ logger.warn(
+ "Unable to close channel: \n" +
+ " Channel: " + channelProxy,
+ e);
+ }
+ }
}
/**
@@ -358,11 +378,13 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
e);
}
// done
- if (logger.isDebugEnabled())
+ if (logger.isInfoEnabled())
{
- logger.debug("\n" +
+ logger.info("\n" +
"Created JChannelFactory: \n" +
- " configuration: " + AlfrescoJGroupsChannelFactory.configUrl);
+ " Cluster Name: " + (AlfrescoJGroupsChannelFactory.clusterNamePrefix == null ? "" : AlfrescoJGroupsChannelFactory.clusterNamePrefix) + "\n" +
+ " Stack Mapping: " + AlfrescoJGroupsChannelFactory.stacksByAppRegion + "\n" +
+ " Configuration: " + AlfrescoJGroupsChannelFactory.configUrl);
}
return AlfrescoJGroupsChannelFactory.channelFactory;
}
@@ -394,6 +416,13 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
try
{
oldChannel.close();
+ oldChannel.shutdown();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("\n" +
+ "Closed old channel during channel rebuild: \n" +
+ " Old channel: " + oldChannel);
+ }
}
catch (Throwable e)
{
@@ -420,12 +449,15 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
writeLock.lock();
try
{
- if (clusterNamePrefix == null || clusterNamePrefix.length() == 0)
+ if (clusterNamePrefix == null || clusterNamePrefix.trim().length() == 0 || clusterNamePrefix.startsWith("${"))
{
// Clear everything out
AlfrescoJGroupsChannelFactory.clusterNamePrefix = null;
}
- AlfrescoJGroupsChannelFactory.clusterNamePrefix = clusterNamePrefix;
+ else
+ {
+ AlfrescoJGroupsChannelFactory.clusterNamePrefix = clusterNamePrefix;
+ }
}
finally
{
@@ -490,11 +522,11 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
}
/**
- * @see AlfrescoJGroupsChannelFactory#changeClusterName(String)
+ * @see AlfrescoJGroupsChannelFactory#changeClusterNamePrefix(String)
*/
- public void setClusterNamePrefix(String clusterNamePrefix)
+ public void setClusterName(String clusterName)
{
- AlfrescoJGroupsChannelFactory.changeClusterNamePrefix(clusterNamePrefix);
+ AlfrescoJGroupsChannelFactory.changeClusterNamePrefix(clusterName);
}
/**
@@ -579,6 +611,8 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
/**
* Swap the channel. The old delegate will be disconnected before the swap occurs.
* This guarantees data consistency, assuming that any failures will be handled.
+ *
+ * Note that the old delegate is not closed or shutdown.
*
* @param the new delegate
* @return the old, disconnected delegate
@@ -593,8 +627,6 @@ public class AlfrescoJGroupsChannelFactory extends AbstractLifecycleBean
}
delegate.setUpHandler(null);
- // Close the old delegate
- delegate.close();
Channel oldDelegage = delegate;
// Assign the new delegate and carry the listeners over
diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java
index cd37ef64f3..bba53907e7 100644
--- a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java
+++ b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java
@@ -28,7 +28,6 @@ import java.io.Serializable;
import java.util.Locale;
import java.util.Map;
-import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.node.NodeServicePolicies;
@@ -163,7 +162,7 @@ public class MultilingualDocumentAspect implements
}
// if the local has been modified
- if (!localeBefore.equals(localeAfter))
+ if (localeBefore == null || !localeBefore.equals(localeAfter))
{
NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef);
@@ -191,7 +190,7 @@ public class MultilingualDocumentAspect implements
// if locale of the container is equals to the locale of
// the node (before update). The nodeRef is the pivot language
// and the locale of the mlContainer must be modified
- if(localeBefore.equals(localMlContainer))
+ if(localeBefore != null && localeBefore.equals(localMlContainer))
{
nodeService.setProperty(
mlContainer,
diff --git a/source/java/org/alfresco/repo/module/ModuleStarter.java b/source/java/org/alfresco/repo/module/ModuleStarter.java
index 07ee205881..4152f1f272 100644
--- a/source/java/org/alfresco/repo/module/ModuleStarter.java
+++ b/source/java/org/alfresco/repo/module/ModuleStarter.java
@@ -24,7 +24,9 @@
*/
package org.alfresco.repo.module;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.module.ModuleService;
+import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.AbstractLifecycleBean;
import org.alfresco.util.PropertyCheck;
import org.springframework.context.ApplicationEvent;
@@ -37,8 +39,18 @@ import org.springframework.context.ApplicationEvent;
*/
public class ModuleStarter extends AbstractLifecycleBean
{
+ private TransactionService transactionService;
private ModuleService moduleService;
+ /**
+ *
+ * @param transactionService provides the retrying transaction
+ */
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
/**
* @param moduleService the service that will do the actual work.
*/
@@ -51,7 +63,15 @@ public class ModuleStarter extends AbstractLifecycleBean
protected void onBootstrap(ApplicationEvent event)
{
PropertyCheck.mandatory(this, "moduleService", moduleService);
- moduleService.startModules();
+ RetryingTransactionCallback