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+"]");