diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties
index e3c32f2055..893f4c5fad 100644
--- a/config/alfresco/messages/patch-service.properties
+++ b/config/alfresco/messages/patch-service.properties
@@ -449,4 +449,9 @@ patch.updateFollowingEmailTemplatesPatch.error=Error retrieving base template wh
patch.addDutchEmailTemplatesPatch.description=Patch to add Dutch email templates.
patch.addDutchEmailTemplatesPatch.result=Dutch email templates have been successfully added.
-patch.addDutchEmailTemplatesPatch.error=Error retrieving base template when trying to add Dutch email templates.
\ No newline at end of file
+patch.addDutchEmailTemplatesPatch.error=Error retrieving base template when trying to add Dutch email templates.
+
+patch.fixBpmPackages.description=Corrects workflow package types and associations
+patch.fixBpmPackages.result=Patch successful. {0} packages converted.
+patch.fixBpmPackages.invalidBootsrapStore=Bootstrap store has not been set
+patch.fixBpmPackages.emptyContainer={0} node has no children
diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml
index 8bc6794f4d..67f5554c06 100644
--- a/config/alfresco/patch/patch-services-context.xml
+++ b/config/alfresco/patch/patch-services-context.xml
@@ -2966,7 +2966,7 @@
true
-
+
patch.updateFollowingEmailTemplatesPatch
patch.updateFollowingEmailTemplatesPatch.description
@@ -3004,4 +3004,15 @@
+
+ patch.fixBpmPackages
+ patch.fixBpmPackages.description
+ 0
+ 5024
+ 5025
+
+
+
+
+
diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml
index c4f872e3b3..84cd1e7ad7 100644
--- a/config/alfresco/public-services-context.xml
+++ b/config/alfresco/public-services-context.xml
@@ -68,7 +68,7 @@
-
+
diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties
index cae3dcda32..58f33e3984 100644
--- a/config/alfresco/version.properties
+++ b/config/alfresco/version.properties
@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
-version.schema=5024
+version.schema=5025
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java
new file mode 100644
index 0000000000..e5c5efddc3
--- /dev/null
+++ b/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Alfresco. If not, see .
+ */
+package org.alfresco.repo.admin.patch.impl;
+
+import java.util.List;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.admin.patch.AbstractPatch;
+import org.alfresco.repo.importer.ImporterBootstrap;
+import org.alfresco.repo.workflow.WorkflowModel;
+import org.alfresco.service.cmr.admin.PatchException;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+/**
+ * Patch that updates workflow package type and package items associations
+ *
+ * @see https://issues.alfresco.com/jira/browse/ETHREEOH-3613
+ * @see https://issues.alfresco.com/jira/browse/ALF-11499
+ * @author Arseny Kovalchuk
+ * @since 3.4.7
+ */
+public class FixBpmPackagesPatch extends AbstractPatch
+{
+ private static final String MSG_SUCCESS = "patch.fixBpmPackages.result";
+
+ private static final String ERR_MSG_INVALID_BOOTSTRAP_STORE = "patch.fixBpmPackages.invalidBootsrapStore";
+ private static final String ERR_MSG_EMPTY_CONTAINER = "patch.fixBpmPackages.emptyContainer";
+
+ private static final Log logger = LogFactory.getLog(FixBpmPackagesPatch.class);
+
+ private ImporterBootstrap importerBootstrap;
+
+ public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
+ {
+ this.importerBootstrap = importerBootstrap;
+ }
+
+ @Override
+ protected String applyInternal() throws Exception
+ {
+ // Package counter for report
+ int packagesCount = 0;
+ StoreRef store = importerBootstrap.getStoreRef();
+ if (store == null)
+ {
+ throw new PatchException(ERR_MSG_INVALID_BOOTSTRAP_STORE);
+ }
+
+ // Get root node for store
+ NodeRef rootRef = nodeService.getRootNode(store);
+
+ if (logger.isDebugEnabled())
+ logger.debug("StoreRef:" + store + " RootNodeRef: " + rootRef);
+
+ // Get /sys:system container path, if it doesn't exist there is something wrong with the repo
+ String sysContainer = importerBootstrap.getConfiguration().getProperty("system.system_container.childname");
+ QName sysContainerQName = QName.createQName(sysContainer, namespaceService);
+
+ List refs = nodeService.getChildAssocs(rootRef, ContentModel.ASSOC_CHILDREN, sysContainerQName);
+
+ if (refs == null || refs.size() == 0)
+ throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysContainer);
+
+ NodeRef sysNodeRef = refs.get(0).getChildRef();
+
+ // Get /sys:system/sys:workflow container, if it doesn't exist there is something wrong with the repo
+ String sysWorkflowContainer = importerBootstrap.getConfiguration().getProperty("system.workflow_container.childname");
+ QName sysWorkflowQName = QName.createQName(sysWorkflowContainer, namespaceService);
+
+ refs = nodeService.getChildAssocs(sysNodeRef, ContentModel.ASSOC_CHILDREN, sysWorkflowQName);
+
+ if (refs == null || refs.size() == 0)
+ throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysWorkflowContainer);
+
+ NodeRef workflowContainerRef = refs.get(0).getChildRef();
+
+ // Try to get /sys:system/sys:workflow/cm:packages, if there is no such node, then it wasn't created yet,
+ // so there is nothing to convert
+ refs = nodeService.getChildAssocs(workflowContainerRef, ContentModel.ASSOC_CHILDREN, RegexQNamePattern.MATCH_ALL);
+
+ if (refs == null || refs.size() == 0)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("There are no any packages in the container " + sysWorkflowContainer);
+ return I18NUtil.getMessage(MSG_SUCCESS, packagesCount);
+ }
+ // Get /sys:system/sys:workflow/cm:packages container NodeRef
+ NodeRef packagesContainerRef = refs.get(0).getChildRef();
+ // Get workflow packages to be converted
+ refs = nodeService.getChildAssocs(packagesContainerRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Found " + refs.size() + " packages to convert");
+
+ NodeRef packageRef = null;
+ // For each package we get package items and convert their type from cm:systemfolder to bpm:package
+ // Also we convert associations between packages and their items from cm:contains to bpm:packageContains
+ for (ChildAssociationRef assocRef : refs)
+ {
+ packageRef = assocRef.getChildRef();
+ QName typeQname = nodeService.getType(packageRef);
+ String name = (String) nodeService.getProperty(packageRef, ContentModel.PROP_NAME);
+ if (logger.isDebugEnabled())
+ logger.debug("Package " + name + " type " + typeQname);
+ // New type of the package is bpm:package
+ nodeService.setType(packageRef, WorkflowModel.TYPE_PACKAGE);
+ // Get all package items
+ List packageItemsAssocs = nodeService.getChildAssocs(packageRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ for (ChildAssociationRef itemAssoc : packageItemsAssocs)
+ {
+ NodeRef parentRef = itemAssoc.getParentRef();
+ NodeRef childRef = itemAssoc.getChildRef();
+ String itemName = (String) nodeService.getProperty(childRef, ContentModel.PROP_NAME);
+ // To avoid unnecessary deletion of the child item, we check if the association is not primary
+ // For the package item it should be not primary association.
+ if (itemAssoc.isPrimary())
+ {
+ logger.error("Association between package: " + name + " and item: " + itemName + " is primary association, so removing this assiciation will result in child node deletion");
+ continue;
+ }
+ boolean assocRemoved = nodeService.removeChildAssociation(itemAssoc);
+ if (assocRemoved)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Association between package: " + name + " and item: " + itemName + " was removed");
+ }
+ else
+ {
+ if (logger.isErrorEnabled())
+ logger.error("Association between package: " + name + " and item: " + itemName + " doesn't exist");
+ // If there is no association we won't create a new one
+ continue;
+ }
+ // Recreate new association between package and particular item as bpm:packageContains
+ /* ChildAssociationRef newChildAssoc = */nodeService.addChild(parentRef, childRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS,
+ QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(itemName)));
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("New association has been created between package: " + name + " and item: " + itemName);
+ }
+ }
+ packagesCount++;
+
+ }
+ return I18NUtil.getMessage(MSG_SUCCESS, packagesCount);
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrPropBackedBeanPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrPropBackedBeanPatch.java
index 88303a2fc2..b35518f5b8 100644
--- a/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrPropBackedBeanPatch.java
+++ b/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrPropBackedBeanPatch.java
@@ -19,9 +19,15 @@
package org.alfresco.repo.admin.patch.impl;
import java.io.Serializable;
+import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.domain.patch.PatchDAO;
import org.alfresco.service.cmr.attributes.AttributeService;
@@ -29,6 +35,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.extensions.surf.util.I18NUtil;
/**
@@ -37,7 +46,7 @@ import org.springframework.extensions.surf.util.I18NUtil;
* @author janv
* @since 3.4
*/
-public class MigrateAttrPropBackedBeanPatch extends AbstractPatch
+public class MigrateAttrPropBackedBeanPatch extends AbstractPatch implements ApplicationContextAware
{
private Log logger = LogFactory.getLog(this.getClass());
@@ -47,6 +56,7 @@ public class MigrateAttrPropBackedBeanPatch extends AbstractPatch
private AttributeService attributeService;
private PatchDAO patchDAO;
+ private MBeanServerConnection mbeanServer;
public void setAttributeService(AttributeService attributeService)
{
@@ -58,6 +68,16 @@ public class MigrateAttrPropBackedBeanPatch extends AbstractPatch
this.patchDAO = patchDAO;
}
+ /* (non-Javadoc)
+ * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
+ */
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
+ {
+ // Optional dependency - may not exist in community builds
+ this.mbeanServer = (MBeanServerConnection) applicationContext.getBean("alfrescoMBeanServer");
+ }
+
@Override
protected String applyInternal() throws Exception
{
@@ -124,10 +144,55 @@ public class MigrateAttrPropBackedBeanPatch extends AbstractPatch
{
return;
}
- attributeService.setAttribute(
- (Serializable) attributeMap,
- ROOT_KEY_PBB, componentName);
+ boolean done = false;
+ try
+ {
+ // Go through the subsystem MBean interface in case the subsystem is already live and the cluster needs
+ // to be resynced
+
+ // Decode the bean ID to a hierarchical object name
+ String[] components = componentName.split("\\$");
+ StringBuilder nameBuff = new StringBuilder(200).append("Alfresco:Type=Configuration,Category=").append(
+ URLDecoder.decode(components[0], "UTF-8"));
+ for (int i = 1; i < components.length; i++)
+ {
+ nameBuff.append(",id").append(i).append('=').append(URLDecoder.decode(components[i], "UTF-8"));
+ }
+
+ ObjectName name = new ObjectName(nameBuff.toString());
+ if (mbeanServer != null && mbeanServer.isRegistered(name))
+ {
+ AttributeList attributeList = new AttributeList();
+ for (Map.Entry entry : attributeMap.entrySet())
+ {
+ attributeList.add(new Attribute(entry.getKey(), entry.getValue()));
+ }
+ mbeanServer.setAttributes(name, attributeList);
+ // We've successfully persisted the attributes. Job done
+ done = true;
+ }
+ }
+ catch (Exception e)
+ {
+ if (logger.isWarnEnabled())
+ {
+ logger
+ .warn(
+ "Exception migrating attributes of subsystem "
+ + componentName
+ + ". Falling back to repository-only operation. Subsystem may remain out of sync until reboot.",
+ e);
+ }
+ }
+
+ // Fallback: perhaps the subsystem isn't up yet and hasn't exported its bean. Or perhaps an error occurred
+ // above. Let's persist the new property anyway.
+ if (!done)
+ {
+ attributeService.setAttribute((Serializable) attributeMap, ROOT_KEY_PBB, componentName);
+ }
+
if (logger.isTraceEnabled())
{
logger.trace("Set PBB component attr [name="+componentName+", attributeMap="+attributeMap+"]");