/* * #%L * Alfresco Records Management Module * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * - * 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 <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.module.org_alfresco_module_rm.job; import java.util.Date; import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor; import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.QueryConsistency; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Job to publish any pending updates on marked node references. * * @author Roy Wetherall */ public class PublishUpdatesJobExecuter extends RecordsManagementJobExecuter { /** Logger */ private static Log logger = LogFactory.getLog(PublishUpdatesJobExecuter.class); /** Node service */ private NodeService nodeService; /** Search service */ private SearchService searchService; /** Publish executor register */ private PublishExecutorRegistry publishExecutorRegistry; /** Dictionary service */ private DictionaryService dictionaryService; /** Behaviour filter */ private BehaviourFilter behaviourFilter; /** * @param nodeService node service */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param searchService search service */ public void setSearchService(SearchService searchService) { this.searchService = searchService; } /** * @param publishExecutorRegistry public executor registry */ public void setPublishExecutorRegistry(PublishExecutorRegistry publishExecutorRegistry) { this.publishExecutorRegistry = publishExecutorRegistry; } /** * @param behaviourFilter behaviour filter */ public void setBehaviourFilter(BehaviourFilter behaviourFilter) { this.behaviourFilter = behaviourFilter; } /** * @param dictionaryService dictionary service */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } /** * @see org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJobExecuter#executeImpl() */ public void executeImpl() { if (logger.isDebugEnabled()) { logger.debug("Job Starting"); } AuthenticationUtil.runAs(new RunAsWork<Object>() { public Object doWork() { if (rmLoaded()) { // Get a list of the nodes that have updates that need to be published List<NodeRef> nodeRefs = getUpdatedNodes(); // Deal with each updated disposition action in turn for (NodeRef nodeRef : nodeRefs) { if (nodeService.exists(nodeRef)) { boolean publishing = ((Boolean)nodeService.getProperty(nodeRef, PROP_PUBLISH_IN_PROGRESS)).booleanValue(); if (!publishing) { // Mark the update node as publishing in progress markPublishInProgress(nodeRef); try { Date start = new Date(); if (logger.isDebugEnabled()) { logger.debug("Starting publish of updates ..."); logger.debug(" - for " + nodeRef.toString()); logger.debug(" - at " + start.toString()); } // Publish updates publishUpdates(nodeRef); if (logger.isDebugEnabled()) { Date end = new Date(); long duration = end.getTime() - start.getTime(); logger.debug("Completed publish of updates ..."); logger.debug(" - for " + nodeRef.toString()); logger.debug(" - at " + end.toString()); logger.debug(" - duration " + Long.toString(duration)); } } finally { // Ensure the update node has either completed the publish or is marked as no longer in progress unmarkPublishInProgress(nodeRef); } } } } } return null; }; }, AuthenticationUtil.getSystemUserName()); if (logger.isDebugEnabled()) { logger.debug("Job Finished"); } } /** * Helper method to determine whether the RM content model has been loaded yet. * * @return boolean true if RM content model loaded, false otherwise */ private boolean rmLoaded() { boolean result = false; // ensure that the rm content model has been loaded if (dictionaryService != null && dictionaryService.getAspect(ASPECT_UNPUBLISHED_UPDATE) != null) { result = true; } return result; } /** * Get a list of the nodes with updates pending publish * @return List<NodeRef> list of node refences with updates pending publication */ private List<NodeRef> getUpdatedNodes() { RetryingTransactionCallback<List<NodeRef>> execution = new RetryingTransactionHelper.RetryingTransactionCallback<List<NodeRef>>() { @Override public List<NodeRef> execute() { // Build the query string StringBuilder sb = new StringBuilder(); sb.append("ASPECT:\"rma:").append(ASPECT_UNPUBLISHED_UPDATE.getLocalName()).append("\""); String query = sb.toString(); if (logger.isDebugEnabled()) { logger.debug("Executing query " + query); } // Execute query to find updates awaiting publishing List<NodeRef> resultNodes = null; SearchParameters searchParameters = new SearchParameters(); searchParameters.setQueryConsistency(QueryConsistency.TRANSACTIONAL); searchParameters.setQuery(query); searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); searchParameters.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); try { ResultSet results = searchService.query(searchParameters); try { resultNodes = results.getNodeRefs(); } finally { results.close(); } } catch (AlfrescoRuntimeException exception) { if (logger.isDebugEnabled()) { logger.debug("Error executing query, " + exception.getMessage()); } throw exception; } if (logger.isDebugEnabled()) { logger.debug("Found " + resultNodes.size() + " disposition action definitions updates awaiting publishing."); } return resultNodes; } }; return retryingTransactionHelper.doInTransaction(execution, true); } /** * Mark the node as publish in progress. This is often used as a marker to prevent any further updates * to a node. * @param nodeRef node reference */ private void markPublishInProgress(final NodeRef nodeRef) { RetryingTransactionHelper.RetryingTransactionCallback<Void> execution = new RetryingTransactionHelper.RetryingTransactionCallback<Void>() { @Override public Void execute() { if (logger.isDebugEnabled()) { logger.debug("Marking updated node as publish in progress. (node=" + nodeRef.toString() + ")"); } behaviourFilter.disableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); try { if (nodeService.exists(nodeRef)) { // Mark the node as publish in progress nodeService.setProperty(nodeRef, PROP_PUBLISH_IN_PROGRESS, true); } } finally { behaviourFilter.enableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); } return null; } }; retryingTransactionHelper.doInTransaction(execution); } /** * Publish the updates made to the node. * @param nodeRef node reference */ private void publishUpdates(final NodeRef nodeRef) { RetryingTransactionHelper.RetryingTransactionCallback<Void> execution = new RetryingTransactionHelper.RetryingTransactionCallback<Void>() { @Override public Void execute() { behaviourFilter.disableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); try { // Get the update to value for the node String updateTo = (String)nodeService.getProperty(nodeRef, PROP_UPDATE_TO); if (updateTo != null) { if (logger.isDebugEnabled()) { logger.debug("Node update to " + updateTo + " (noderef=" + nodeRef.toString() + ")"); } // Get the publish executor PublishExecutor executor = publishExecutorRegistry.get(updateTo); if (executor == null) { if (logger.isDebugEnabled()) { logger.debug("Unable to find a corresponding publish executor. (noderef=" + nodeRef.toString() + ", updateTo=" + updateTo + ")"); } throw new AlfrescoRuntimeException("Unable to find a corresponding publish executor. (noderef=" + nodeRef.toString() + ", updateTo=" + updateTo + ")"); } if (logger.isDebugEnabled()) { logger.debug("Attempting to publish updates. (nodeRef=" + nodeRef.toString() + ")"); } // Publish executor.publish(nodeRef); } else { if (logger.isDebugEnabled()) { logger.debug("Unable to publish, because publish executor is not set."); } } // Remove the unpublished update aspect nodeService.removeAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE); if (logger.isDebugEnabled()) { logger.debug("Publish updates complete. (nodeRef=" + nodeRef.toString() + ")"); } } finally { behaviourFilter.enableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); } return null; } }; retryingTransactionHelper.doInTransaction(execution); } /** * Unmark node as publish in progress, assuming publish failed. * @param nodeRef node reference */ private void unmarkPublishInProgress(final NodeRef nodeRef) { RetryingTransactionHelper.RetryingTransactionCallback<Void> execution = new RetryingTransactionHelper.RetryingTransactionCallback<Void>() { @Override public Void execute() { behaviourFilter.disableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); try { // Assuming the node still has unpublished information, then unmark it in progress if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE)) { if (logger.isDebugEnabled()) { logger.debug("Removing publish in progress marker from updated node, because update was not successful. (node=" + nodeRef.toString() + ")"); } nodeService.setProperty(nodeRef, PROP_PUBLISH_IN_PROGRESS, false); } } finally { behaviourFilter.enableBehaviour(nodeRef, TYPE_DISPOSITION_ACTION_DEFINITION); } return null; } }; retryingTransactionHelper.doInTransaction(execution); } }