From e98ab1750a0f979f800f9ab53997346878d4ee0c Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Thu, 25 Sep 2008 15:10:58 +0000 Subject: [PATCH] Enhancements to FSR. 1) Performance imporvements (client and server are now multi-threaded + other performance work) 2) Pluggable transport protocols (ENH-145) 3) Changes to initialisation (ALFCOM-135) 4) Changes to the action service to enable multiple async event queues. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@11022 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/action-services-context.xml | 136 ++- config/alfresco/application-context.xml | 1 + config/alfresco/core-services-context.xml | 14 +- .../alfresco/deployment-service-context.xml | 135 +++ config/alfresco/index-recovery-context.xml | 3 + config/alfresco/model/wcmAppModel.xml | 4 + config/alfresco/public-services-context.xml | 51 - .../java/org/alfresco/model/WCMAppModel.java | 1 + .../repo/action/ActionServiceImpl.java | 102 +- .../action/ActionTransactionListener.java | 11 +- .../repo/action/RuntimeActionService.java | 21 +- .../repo/action/executer/ActionExecuter.java | 6 + .../executer/ActionExecuterAbstractBase.java | 21 +- .../avm/actions/AVMDeployWebsiteAction.java | 41 +- ...actDeploymentReceiverTransportAdapter.java | 55 ++ .../deploy/DeploymentClientOutputStream.java | 125 +++ .../repo/deploy/DeploymentDestination.java | 77 ++ .../DeploymentReceiverServiceClient.java | 138 +++ .../DeploymentReceiverTransportAdapter.java | 69 ++ ...oymentReceiverTransportAdapterHessian.java | 71 ++ ...DeploymentReceiverTransportAdapterRMI.java | 53 + ...entReceiverTransportAdapterSpringHTTP.java | 72 ++ .../repo/deploy/DeploymentServiceImpl.java | 923 +++++++++++------- .../alfresco/repo/deploy/DeploymentWork.java | 68 ++ .../repo/deploy/FSDeploymentTest.java | 52 +- .../service/ServiceDescriptorRegistry.java | 11 + .../org/alfresco/service/ServiceRegistry.java | 9 + .../avm/deploy/DeploymentReportCallback.java | 46 + .../cmr/avm/deploy/DeploymentService.java | 45 +- .../util/ThreadPoolExecutorFactoryBean.java | 16 + .../alfresco/util/TraceableThreadFactory.java | 13 +- 31 files changed, 1890 insertions(+), 500 deletions(-) create mode 100644 config/alfresco/deployment-service-context.xml create mode 100644 source/java/org/alfresco/repo/deploy/AbstractDeploymentReceiverTransportAdapter.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentClientOutputStream.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentDestination.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentReceiverServiceClient.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapter.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterHessian.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterRMI.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterSpringHTTP.java create mode 100644 source/java/org/alfresco/repo/deploy/DeploymentWork.java create mode 100644 source/java/org/alfresco/service/cmr/avm/deploy/DeploymentReportCallback.java diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 04f990f6d5..970c05aac6 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -3,8 +3,62 @@ - + + + + + + + defaultAsyncAction + + + 2 + + + 10 + + + + + + + deploymentAsyncAction + + + 2 + + + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -18,9 +72,18 @@ - - - + + + + + + + + + + + + @@ -506,45 +569,50 @@ - + + + + deployment + + - + + + + + + false + + + admin + + + admin + + + ${alfresco.rmi.services.port} + + + 44100 + + + default + + + 30 + + + + false - - - admin - - - admin - - - ${alfresco.rmi.services.port} - - - 44100 - - - default - - - 30 - - - - - - - - - false - + diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index 9ecf87d8de..3d34d5eb42 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -36,6 +36,7 @@ + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index cfb0b11de3..9063e686b5 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -481,6 +481,9 @@ + + indexThread + 10 @@ -1085,18 +1088,7 @@ - - - - - - 2 - - - 10 - - diff --git a/config/alfresco/deployment-service-context.xml b/config/alfresco/deployment-service-context.xml new file mode 100644 index 0000000000..50e6abc632 --- /dev/null +++ b/config/alfresco/deployment-service-context.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + Alfresco + + + PBEWithMD5AndDES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://{1}:{2}/alfrescoFSR/deployment + + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + getRemoteActionService + + + + + + + + + + + deployDifferenceFS + deployDifference + + + + + + + + org.alfresco.service.cmr.avm.deploy.DeploymentService + + + + deploymentService + + + + deploymentServiceWriteTxnAdvisor + deploymentServiceReadTxnAdvisor + checkTxnAdvisor + + + + diff --git a/config/alfresco/index-recovery-context.xml b/config/alfresco/index-recovery-context.xml index 365f0c570c..fd3c03dfe9 100644 --- a/config/alfresco/index-recovery-context.xml +++ b/config/alfresco/index-recovery-context.xml @@ -7,6 +7,9 @@ The thread pool to use for index rebuilding and recovery --> + + indexTrackerThread + ${index.recovery.maximumPoolSize} diff --git a/config/alfresco/model/wcmAppModel.xml b/config/alfresco/model/wcmAppModel.xml index b1666dd28e..9909aa1715 100644 --- a/config/alfresco/model/wcmAppModel.xml +++ b/config/alfresco/model/wcmAppModel.xml @@ -306,6 +306,10 @@ Display Name d:text + + Deployment Server Adapter Name + d:text + Username d:text diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index b286310338..aa4b89b1fa 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -1117,57 +1117,6 @@ - - - - - - - - - - - - - - - - getRemoteActionService - - - - - - - - - - - deployDifferenceFS - deployDifference - - - - - - - - org.alfresco.service.cmr.avm.deploy.DeploymentService - - - - deploymentService - - - - deploymentServiceWriteTxnAdvisor - deploymentServiceReadTxnAdvisor - checkTxnAdvisor - - - - - diff --git a/source/java/org/alfresco/model/WCMAppModel.java b/source/java/org/alfresco/model/WCMAppModel.java index d75d5f3407..e884674334 100644 --- a/source/java/org/alfresco/model/WCMAppModel.java +++ b/source/java/org/alfresco/model/WCMAppModel.java @@ -84,6 +84,7 @@ public interface WCMAppModel static final QName PROP_DEPLOYSERVERUSERNAME = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deployserverusername"); static final QName PROP_DEPLOYSERVERPASSWORD = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deployserverpassword"); static final QName PROP_DEPLOYSERVERGROUP = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deployservergroup"); + static final QName PROP_DEPLOYSERVERADPTERNAME = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deployserveradaptername"); static final QName PROP_DEPLOYSERVERURL = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deployserverurl"); static final QName PROP_DEPLOYSERVERTARGET = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deployservertarget"); static final QName PROP_DEPLOYSOURCEPATH = QName.createQName(NamespaceService.WCMAPP_MODEL_1_0_URI, "deploysourcepath"); diff --git a/source/java/org/alfresco/repo/action/ActionServiceImpl.java b/source/java/org/alfresco/repo/action/ActionServiceImpl.java index 663ecd0657..c1aa039eea 100644 --- a/source/java/org/alfresco/repo/action/ActionServiceImpl.java +++ b/source/java/org/alfresco/repo/action/ActionServiceImpl.java @@ -112,11 +112,12 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A /** The authentication component */ private AuthenticationComponent authenticationComponent; - + /** - * The asynchronous action execution queue + * The asynchronous action execution queues + * map of name, queue */ - private AsynchronousActionExecutionQueue asynchronousActionExecutionQueue; + private Map asynchronousActionExecutionQueues; /** * Action transaction listener @@ -182,26 +183,16 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A { this.dictionaryService = dictionaryService; } - + /** - * Set the asynchronous action execution queue + * Set the asynchronous action execution queues * - * @param asynchronousActionExecutionQueue the asynchronous action execution queue + * @param asynchronousActionExecutionQueue the asynchronous action execution queues */ - public void setAsynchronousActionExecutionQueue( - AsynchronousActionExecutionQueue asynchronousActionExecutionQueue) + public void setAsynchronousActionExecutionQueues( + Map asynchronousActionExecutionQueues) { - this.asynchronousActionExecutionQueue = asynchronousActionExecutionQueue; - } - - /** - * Get the asychronous action execution queue - * - * @return the asynchronous action execution queue - */ - public AsynchronousActionExecutionQueue getAsynchronousActionExecutionQueue() - { - return asynchronousActionExecutionQueue; + this.asynchronousActionExecutionQueues = asynchronousActionExecutionQueues; } /** @@ -404,6 +395,71 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A addPostTransactionPendingAction(action, actionedUponNodeRef, checkConditions, actionChain); } } + + /** + * called by transaction service. + */ + public void postCommit() + { + for (PendingAction pendingAction : getPostTransactionPendingActions()) + { + queueAction(pendingAction); + } + } + + /** + * + */ + private void queueAction(PendingAction action) + { + // Get the right queue + AsynchronousActionExecutionQueue queue = getQueue(action.action); + + // Queue the action for execution + queue.executeAction( + this, + action.getAction(), + action.getActionedUponNodeRef(), + action.getCheckConditions(), + action.getActionChain()); + } + + /** + * + * @param compensatingAction + * @param actionedUponNodeRef + */ + private void queueAction(Action compensatingAction, NodeRef actionedUponNodeRef) + { + // Get the right queue + AsynchronousActionExecutionQueue queue = getQueue(compensatingAction); + + // Queue the action for execution + queue.executeAction(this, compensatingAction, actionedUponNodeRef, false, null); + } + + private AsynchronousActionExecutionQueue getQueue(Action action) + { + ActionExecuter executer = (ActionExecuter)this.applicationContext.getBean(action.getActionDefinitionName()); + AsynchronousActionExecutionQueue queue = null; + + String queueName = executer.getQueueName(); + if(queueName == null) + { + queue = asynchronousActionExecutionQueues.get(""); + } + else + { + queue = asynchronousActionExecutionQueues.get(queueName); + } + if(queue == null) + { + // can't get queue + throw new ActionServiceException("Unable to get AsynchronousActionExecutionQueue name: "+ queueName); + } + + return queue; + } /** * @see org.alfresco.repo.action.RuntimeActionService#executeActionImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef, boolean, org.alfresco.service.cmr.repository.NodeRef) @@ -501,9 +557,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A { // Set the current user ((ActionImpl)compensatingAction).setRunAsUser(currentUserName); - - // Queue the compensating action ready for execution - this.asynchronousActionExecutionQueue.executeAction(this, compensatingAction, actionedUponNodeRef, false, null); + queueAction(compensatingAction, actionedUponNodeRef); } } @@ -1195,7 +1249,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A * @see org.alfresco.repo.action.RuntimeActionService#getPostTransactionPendingActions() */ @SuppressWarnings("unchecked") - public List getPostTransactionPendingActions() + private List getPostTransactionPendingActions() { return (List)AlfrescoTransactionSupport.getResource(POST_TRANSACTION_PENDING_ACTIONS); } @@ -1203,7 +1257,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A /** * Pending action details class */ - public class PendingAction + private class PendingAction { /** * The action diff --git a/source/java/org/alfresco/repo/action/ActionTransactionListener.java b/source/java/org/alfresco/repo/action/ActionTransactionListener.java index f22ab80944..622734c97d 100644 --- a/source/java/org/alfresco/repo/action/ActionTransactionListener.java +++ b/source/java/org/alfresco/repo/action/ActionTransactionListener.java @@ -24,7 +24,6 @@ */ package org.alfresco.repo.action; -import org.alfresco.repo.action.ActionServiceImpl.PendingAction; import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.util.GUID; @@ -81,15 +80,7 @@ public class ActionTransactionListener implements TransactionListener */ public void afterCommit() { - for (PendingAction pendingAction : this.actionService.getPostTransactionPendingActions()) - { - this.actionService.getAsynchronousActionExecutionQueue().executeAction( - actionService, - pendingAction.getAction(), - pendingAction.getActionedUponNodeRef(), - pendingAction.getCheckConditions(), - pendingAction.getActionChain()); - } + this.actionService.postCommit(); } /** diff --git a/source/java/org/alfresco/repo/action/RuntimeActionService.java b/source/java/org/alfresco/repo/action/RuntimeActionService.java index d49418615a..c79c81c7b2 100644 --- a/source/java/org/alfresco/repo/action/RuntimeActionService.java +++ b/source/java/org/alfresco/repo/action/RuntimeActionService.java @@ -24,10 +24,8 @@ */ package org.alfresco.repo.action; -import java.util.List; import java.util.Set; -import org.alfresco.repo.action.ActionServiceImpl.PendingAction; import org.alfresco.repo.action.evaluator.ActionConditionEvaluator; import org.alfresco.repo.action.executer.ActionExecuter; import org.alfresco.service.cmr.action.Action; @@ -39,12 +37,17 @@ import org.alfresco.service.namespace.QName; */ public interface RuntimeActionService { + /** + * + */ + void postCommit(); + /** * Get the asynchronous action queue. * * @return the asynchronous action queue */ - AsynchronousActionExecutionQueue getAsynchronousActionExecutionQueue(); + //AsynchronousActionExecutionQueue getAsynchronousActionExecutionQueue(); /** * Register an action condition evaluator @@ -93,10 +96,10 @@ public interface RuntimeActionService */ public void directActionExecution(Action action, NodeRef actionedUponNodeRef); - /** - * Gets a list of the actions that are pending post transaction - * - * @return list of pending actions - */ - public List getPostTransactionPendingActions(); +// /** +// * Gets a list of the actions that are pending post transaction +// * +// * @return list of pending actions +// */ +// public List getPostTransactionPendingActions(); } diff --git a/source/java/org/alfresco/repo/action/executer/ActionExecuter.java b/source/java/org/alfresco/repo/action/executer/ActionExecuter.java index a1d2494159..dfb8b1f41d 100644 --- a/source/java/org/alfresco/repo/action/executer/ActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/ActionExecuter.java @@ -54,4 +54,10 @@ public interface ActionExecuter public void execute( Action action, NodeRef actionedUponNodeRef); + + /** + * Get the queueName that will execute this action + */ + String getQueueName(); + } diff --git a/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java b/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java index 8f84d9c7b7..c117cabebf 100644 --- a/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java +++ b/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java @@ -53,6 +53,13 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra /** List of types and aspects for which this action is applicable */ protected List applicableTypes = new ArrayList(); + + /** + * + */ + private String queueName = ""; + + /** * Init method @@ -127,6 +134,18 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra * @param actionedUponNodeRef the actioned upon node */ protected abstract void executeImpl(Action action, NodeRef actionedUponNodeRef); + + /** + * Set the queueName which will execute this action + * if blank or null then the action will be executed on the "default" queue + * @param the name of the execution queue which should execute this action. + */ + public void setQueueName(String queueName) + { + this.queueName = queueName; + } - + public String getQueueName() { + return queueName; + } } diff --git a/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java b/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java index 4af68183d6..4120445d73 100644 --- a/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java +++ b/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java @@ -45,6 +45,7 @@ import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.avm.deploy.DeploymentCallback; import org.alfresco.service.cmr.avm.deploy.DeploymentEvent; import org.alfresco.service.cmr.avm.deploy.DeploymentReport; +import org.alfresco.service.cmr.avm.deploy.DeploymentReportCallback; import org.alfresco.service.cmr.avm.deploy.DeploymentService; import org.alfresco.service.cmr.avmsync.AVMSyncException; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -72,6 +73,8 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase public static final String PARAM_SERVER = "server"; public static final String PARAM_ATTEMPT = "attempt"; public static final String PARAM_CALLBACK = "callback"; + + public static final String ASYNC_QUEUE_NAME = "deployment"; private int delay = -1; private int defaultAlfRmiPort = 50500; @@ -79,6 +82,7 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase private String defaultRemoteUsername = "admin"; private String defaultRemotePassword = "admin"; private String defaultTargetName = "default"; + private String defaultAdapterName = "default"; private List configuredCallbacks; private DeploymentService deployService; private ContentService contentService; @@ -275,6 +279,7 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase String sourcePath = (String)serverProps.get(WCMAppModel.PROP_DEPLOYSOURCEPATH); String excludes = (String)serverProps.get(WCMAppModel.PROP_DEPLOYEXCLUDES); String targetName = (String)serverProps.get(WCMAppModel.PROP_DEPLOYSERVERTARGET); + String adapterName = (String)serverProps.get(WCMAppModel.PROP_DEPLOYSERVERADPTERNAME); String targetPath = path; if (fileServerDeployment == false) @@ -289,6 +294,12 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase targetPath = storePath[0] + "live:" + storePath[1]; } } + else + { + if (adapterName == null) { + adapterName = defaultAdapterName; + } + } // get defaults for data not provided in server node if (port == null) @@ -371,7 +382,10 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase // make the deploy call passing in the DeploymentCallback, if present Throwable deployError = null; - DeploymentReport report = null; + + DeploymentReport report = new DeploymentReport(); + callbacks.add(new DeploymentReportCallback(report)); + try { // overwrite the password before logging @@ -384,8 +398,19 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase logger.debug("Performing file server deployment to " + host + ":" + port + " using deploymentserver: " + serverProps); - report = this.deployService.deployDifferenceFS(version, path, host, port, - remoteUsername, remotePassword, targetName, regexMatcher, true, false, false, callbacks); + this.deployService.deployDifferenceFS(version, + path, + adapterName, + host, + port, + remoteUsername, + remotePassword, + targetName, + regexMatcher, + true, + false, + false, + callbacks); } else { @@ -393,14 +418,14 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase logger.debug("Performing Alfresco deployment to " + host + ":" + port + " using deploymentserver: " + serverProps); - report = this.deployService.deployDifference(version, path, host, port, - remoteUsername, remotePassword, targetPath, regexMatcher, true, false, false, callbacks); + this.deployService.deployDifference(version, path, host, port, + remoteUsername, remotePassword, targetPath, regexMatcher, true, false, false, callbacks); } } catch (Throwable err) { deployError = err; - logger.error(deployError); + logger.error("Deployment Error", deployError); } if (report != null) @@ -458,7 +483,7 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase reportProps.put(WCMAppModel.PROP_DEPLOYSERVERURLUSED, serverProps.get(WCMAppModel.PROP_DEPLOYSERVERURL)); - reportProps.put(WCMAppModel.PROP_DEPLOYSUCCESSFUL, (report != null)); + reportProps.put(WCMAppModel.PROP_DEPLOYSUCCESSFUL, (report != null) && (error == null)); if (report == null && error != null) { // add error message as fail reason if appropriate (the reported @@ -515,4 +540,6 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase return reportRef; } + + } diff --git a/source/java/org/alfresco/repo/deploy/AbstractDeploymentReceiverTransportAdapter.java b/source/java/org/alfresco/repo/deploy/AbstractDeploymentReceiverTransportAdapter.java new file mode 100644 index 0000000000..498818b48c --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/AbstractDeploymentReceiverTransportAdapter.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.deploy; + +import java.util.List; + +import org.alfresco.deployment.DeploymentTransportOutputFilter; + +/** + * Abstract Transport Adapter. + * + */ +public abstract class AbstractDeploymentReceiverTransportAdapter { + + List transformers; + + /** + * Get the content transformers for this transport - if the transport does not support + * content transformation then simply return null; + * @return the content transformers or null if there are no transformers. + */ + public ListgetTransformers() { + return transformers; + } + /** + * Set the content transformers for this transport - if the transport does not support + * content transformation then simply set null or do not call this method. + */ + public void setTransformers( List transformers) { + this.transformers = transformers; + } + +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentClientOutputStream.java b/source/java/org/alfresco/repo/deploy/DeploymentClientOutputStream.java new file mode 100644 index 0000000000..eb30e33f2d --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentClientOutputStream.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ + +package org.alfresco.repo.deploy; + +import java.io.IOException; +import java.io.OutputStream; + +import org.alfresco.deployment.DeploymentReceiverTransport; + +/** + * OutputStream used by client side to talk to + * the deployment receiver. + * @author britt + */ +public class DeploymentClientOutputStream extends OutputStream +{ + private DeploymentReceiverTransport fTransport; + + private String fTicket; + + private String fOutputToken; + + /** + * Make one up. + * @param transport + * @param ticket + * @param outputToken + */ + public DeploymentClientOutputStream(DeploymentReceiverTransport transport, + String ticket, + String outputToken) + { + fTransport = transport; + fTicket = ticket; + fOutputToken = outputToken; + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(int b) throws IOException + { + byte[] buff = new byte[1]; + buff[0] = (byte)b; + write(buff); + } + + /* (non-Javadoc) + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException + { + // NO OP + } + + /* (non-Javadoc) + * @see java.io.OutputStream#flush() + */ + @Override + public void flush() throws IOException + { + // NO OP + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte[] b, int off, int len) throws IOException + { + fTransport.write(fTicket, fOutputToken, b, off, len); + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(byte[]) + */ + @Override + public void write(byte[] b) throws IOException + { + write(b, 0, b.length); + } + + /** + * Get the deployment ticket. + * @return + */ + public String getTicket() + { + return fTicket; + } + + /** + * Get the output token. + * @return + */ + public String getOutputToken() + { + return fOutputToken; + } +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentDestination.java b/source/java/org/alfresco/repo/deploy/DeploymentDestination.java new file mode 100644 index 0000000000..8aaf4d35f8 --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentDestination.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.deploy; + +/** + * Class to hold Deployment destination information. + * Used as a lock to serialize deployments to the same + * destination. + * @author britt + */ +public class DeploymentDestination +{ + private String fHost; + + private int fPort; + + DeploymentDestination(String host, int port) + { + fHost = host; + fPort = port; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof DeploymentDestination)) + { + return false; + } + DeploymentDestination other = (DeploymentDestination)obj; + return fHost.equals(other.fHost) && fPort == other.fPort; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + return fHost.hashCode() + fPort; + } + + public String toString() + { + return fHost; + } +} + diff --git a/source/java/org/alfresco/repo/deploy/DeploymentReceiverServiceClient.java b/source/java/org/alfresco/repo/deploy/DeploymentReceiverServiceClient.java new file mode 100644 index 0000000000..7920d0c130 --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentReceiverServiceClient.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ + +package org.alfresco.repo.deploy; + +import java.io.OutputStream; +import java.util.List; + +import org.alfresco.deployment.DeploymentReceiverService; +import org.alfresco.deployment.DeploymentReceiverTransport; +import org.alfresco.deployment.FileDescriptor; + +/** + * Client side implementation of DeploymentReceiverService which decorates a + * DeploymentReceiverTransport instance. + * + * This class adds code to the send and finishSend methods. + * + * @author britt + */ +public class DeploymentReceiverServiceClient implements + DeploymentReceiverService +{ + /** + * The underlying transport. + */ + private DeploymentReceiverTransport fTransport; + + public DeploymentReceiverServiceClient() + { + } + + public void setDeploymentReceiverTransport(DeploymentReceiverTransport transport) + { + fTransport = transport; + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#abort(java.lang.String) + */ + public void abort(String ticket) + { + fTransport.abort(ticket); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#begin(java.lang.String, java.lang.String, java.lang.String) + */ + public String begin(String target, String user, String password) + { + return fTransport.begin(target, user, password); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#commit(java.lang.String) + */ + public void commit(String ticket) + { + fTransport.commit(ticket); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#delete(java.lang.String, java.lang.String) + */ + public void delete(String ticket, String path) + { + fTransport.delete(ticket, path); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#finishSend(java.lang.String, java.io.OutputStream) + */ + public void finishSend(String ticket, OutputStream out) + { + DeploymentClientOutputStream dcOut = (DeploymentClientOutputStream)out; + fTransport.finishSend(dcOut.getTicket(), dcOut.getOutputToken()); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#getListing(java.lang.String, java.lang.String) + */ + public List getListing(String ticket, String path) + { + return fTransport.getListing(ticket, path); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#mkdir(java.lang.String, java.lang.String, java.lang.String) + */ + public void mkdir(String ticket, String path, String guid) + { + fTransport.mkdir(ticket, path, guid); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#send(java.lang.String, java.lang.String, java.lang.String) + */ + public OutputStream send(String ticket, String path, String guid) + { + String outputToken = fTransport.getSendToken(ticket, path, guid); + return new DeploymentClientOutputStream(fTransport, ticket, outputToken); + } + + /* (non-Javadoc) + * @see org.alfresco.deployment.DeploymentReceiverService#shutDown(java.lang.String, java.lang.String) + */ + public void shutDown(String user, String password) + { + fTransport.shutDown(user, password); + } + + public void setGuid(String ticket, String path, String guid) + { + fTransport.setGuid(ticket, path, guid); + } +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapter.java b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapter.java new file mode 100644 index 0000000000..d56bc1c1c9 --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapter.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.deploy; + +import java.util.List; + +import org.alfresco.deployment.DeploymentReceiverTransport; +import org.alfresco.deployment.DeploymentTransportOutputFilter; +/** + * + * DeploymentReceiverTransportAdapter are used to adapt the interface used by the FSR Client to the + * interface used by the underlying transport implementation. + * + * The DeploymentReceiverTransport objects returned will typically be proxy classes to a remote service. + * + * @see org.alfresco.deployment.impl.client.DeploymentReceiverTransportAdapterRMI + * @see org.alfresco.deployment.impl.client.DeploymentReceiverTransportAdapterSpringHTTP + * @see org.alfresco.deployment.impl.client.DeploymentReceiverTransportAdapterHessian + * + * @author mrogers + * + */ +public interface DeploymentReceiverTransportAdapter +{ + + /** + * getObject is a factory method to get a DeploymentReceiverTransport object, which will typically + * be a proxy to a remote service. + * + * It is up to the adapters themselves to decide whether hostName, port or URL takes precedence. + * + * @param adapterName the name of this adapter + * @param hostName the name of the host to connect to + * @param port the port to connect to + * @param version the version of the website + * @param the path of the website to be deployed + * @return a DeploymentRecieverTransport + */ + public DeploymentReceiverTransport getTransport(String hostName, int port, int version, String srcPath); + + /** + * Get the content transformers for this transport - if the transport does not support + * content transformation then simply return null; + * @return the content transformers or null if there are no transformers. + */ + public ListgetTransformers(); +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterHessian.java b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterHessian.java new file mode 100644 index 0000000000..29ef96a2e4 --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterHessian.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.deploy; + +import java.text.MessageFormat; + +import org.alfresco.deployment.DeploymentReceiverTransport; +import org.springframework.remoting.caucho.HessianProxyFactoryBean; + +/** + * This class adapts the Hessian protocol to DeploymentReceiverTransport + * + * @author mrogers + */ +public class DeploymentReceiverTransportAdapterHessian extends AbstractDeploymentReceiverTransportAdapter implements DeploymentReceiverTransportAdapter +{ + /** + * The pattern to use when constructing the URL from hostname and port + * + * eg http://localhost:8080/FSR/deployment + */ + private String urlPattern = "http://{1}:{2}/FSR/deployment"; + + public DeploymentReceiverTransport getTransport(String host, + int port, int version, String srcPath) + { + MessageFormat f = new MessageFormat(urlPattern); + Object[] objs = { host, port }; + String URL = f.format(objs); + + // Code to use Hessian transport provided via Spring + HessianProxyFactoryBean factory = new HessianProxyFactoryBean(); + factory.setServiceInterface(DeploymentReceiverTransport.class); + factory.setServiceUrl(URL); + factory.afterPropertiesSet(); + DeploymentReceiverTransport transport = (DeploymentReceiverTransport) factory.getObject(); + + return transport; + + } + + public void setUrlPattern(String urlPattern) { + this.urlPattern = urlPattern; + } + + public String getUrlPattern() { + return urlPattern; + } +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterRMI.java b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterRMI.java new file mode 100644 index 0000000000..b4879a9e74 --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterRMI.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.deploy; + +import org.alfresco.deployment.DeploymentReceiverTransport; +import org.springframework.remoting.rmi.RmiProxyFactoryBean; + +/** + * Transport adapter to connect to the FSR using RMI protocol. + * + * @author mrogers + * + */ +public class DeploymentReceiverTransportAdapterRMI extends AbstractDeploymentReceiverTransportAdapter implements DeploymentReceiverTransportAdapter { + + public DeploymentReceiverTransport getTransport(String hostName, + int port, int version, String srcPath) + { + + // Code to use RMI transport + RmiProxyFactoryBean factory = new RmiProxyFactoryBean(); + factory.setRefreshStubOnConnectFailure(true); + factory.setServiceInterface(DeploymentReceiverTransport.class); + factory.setServiceUrl("rmi://" + hostName + ":" + port + "/deployment"); + factory.afterPropertiesSet(); + DeploymentReceiverTransport transport = (DeploymentReceiverTransport)factory.getObject(); + + return transport; + } + +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterSpringHTTP.java b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterSpringHTTP.java new file mode 100644 index 0000000000..47f913cb62 --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentReceiverTransportAdapterSpringHTTP.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.deploy; + +import java.text.MessageFormat; + +import org.alfresco.deployment.DeploymentReceiverTransport; +import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean; + +/** + * Transport adapter to connect to the FSR using Spring over HTTP protocol. + * + * @author mrogers + * + */ +public class DeploymentReceiverTransportAdapterSpringHTTP extends AbstractDeploymentReceiverTransportAdapter implements DeploymentReceiverTransportAdapter { + /** + * The pattern to use when constructing the URL from hostname and port + * {1} substitues for hostname {2} substitues for port + * Default format results in the following URL http://localhost:8080/alfrescoFSR/deployment + */ + private String urlPattern = "http://{1}:{2}/alfrescoFSR/deployment"; + + public DeploymentReceiverTransport getTransport(String host, + int port, int version, String srcPath) + { + + MessageFormat f = new MessageFormat(getUrlPattern()); + Object[] objs = { host, port }; + String URL = f.format(objs); + + // Code to use HTTP spring transport + HttpInvokerProxyFactoryBean factory = new HttpInvokerProxyFactoryBean(); + factory.setServiceInterface(DeploymentReceiverTransport.class); + factory.setServiceUrl(URL); + factory.afterPropertiesSet(); + DeploymentReceiverTransport transport = (DeploymentReceiverTransport) factory.getObject(); + + return transport; + } + + public void setUrlPattern(String urlPattern) { + this.urlPattern = urlPattern; + } + + public String getUrlPattern() { + return urlPattern; + } + +} diff --git a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java index 370454b1e6..59753164f9 100644 --- a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java +++ b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java @@ -25,21 +25,29 @@ package org.alfresco.repo.deploy; +import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import org.alfresco.deployment.DeploymentReceiverService; import org.alfresco.deployment.DeploymentReceiverTransport; +import org.alfresco.deployment.DeploymentTransportOutputFilter; import org.alfresco.deployment.FileDescriptor; import org.alfresco.deployment.FileType; -import org.alfresco.deployment.impl.client.DeploymentReceiverServiceClient; import org.alfresco.repo.action.ActionServiceRemote; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.avm.util.SimplePath; @@ -48,6 +56,7 @@ import org.alfresco.repo.remote.AVMRemoteImpl; import org.alfresco.repo.remote.AVMSyncServiceRemote; import org.alfresco.repo.remote.ClientTicketHolder; import org.alfresco.repo.remote.ClientTicketHolderThread; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionServiceTransport; import org.alfresco.service.cmr.avm.AVMException; @@ -58,7 +67,6 @@ import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.avm.AVMWrongTypeException; import org.alfresco.service.cmr.avm.deploy.DeploymentCallback; import org.alfresco.service.cmr.avm.deploy.DeploymentEvent; -import org.alfresco.service.cmr.avm.deploy.DeploymentReport; import org.alfresco.service.cmr.avm.deploy.DeploymentService; import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.avmsync.AVMSyncService; @@ -82,57 +90,6 @@ public class DeploymentServiceImpl implements DeploymentService { private static Log fgLogger = LogFactory.getLog(DeploymentServiceImpl.class); - /** - * Class to hold Deployment destination information. - * Used as a lock to serialize deployments to the same - * destination. - * @author britt - */ - private static class DeploymentDestination - { - private String fHost; - - private int fPort; - - DeploymentDestination(String host, int port) - { - fHost = host; - fPort = port; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (!(obj instanceof DeploymentDestination)) - { - return false; - } - DeploymentDestination other = (DeploymentDestination)obj; - return fHost.equals(other.fHost) && fPort == other.fPort; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() - { - return fHost.hashCode() + fPort; - } - - public String toString() - { - return fHost; - } - }; - /** * Holds locks for all deployment destinations (alfresco->alfresco) */ @@ -147,6 +104,11 @@ public class DeploymentServiceImpl implements DeploymentService * The Ticket holder. */ private ClientTicketHolder fTicketHolder; + + /** + * number of concurrent sending threads + */ + private int numberOfSendingThreads = 3; /** * Default constructor. @@ -166,10 +128,12 @@ public class DeploymentServiceImpl implements DeploymentService fAVMService = service; } - /* (non-Javadoc) + /* + * Deploy differences to an ASR + * (non-Javadoc) * @see org.alfresco.service.cmr.avm.deploy.DeploymentService#deployDifference(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean) */ - public DeploymentReport deployDifference(int version, String srcPath, String hostName, + public void deployDifference(int version, String srcPath, String hostName, int port, String userName, String password, String dstPath, NameMatcher matcher, boolean createDst, boolean dontDelete, boolean dontDo, @@ -184,7 +148,6 @@ public class DeploymentServiceImpl implements DeploymentService } try { - DeploymentReport report = new DeploymentReport(); AVMRemote remote = getRemote(hostName, port, userName, password); if (callbacks != null) { @@ -250,17 +213,10 @@ public class DeploymentServiceImpl implements DeploymentService { fgLogger.debug(event); } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); if (dontDo) { - return report; + return; } copyDirectory(version, srcRoot, dstParent, remote, matcher); remote.createSnapshot(storePath[0], "Deployment", "Post Deployment Snapshot."); @@ -273,12 +229,10 @@ public class DeploymentServiceImpl implements DeploymentService { fgLogger.debug(event); } - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } + processEvent(event, callbacks); + } - return report; + return; } if (!dstRoot.isDirectory()) { @@ -287,23 +241,17 @@ public class DeploymentServiceImpl implements DeploymentService // The corresponding directory exists so recursively deploy. try { - deployDirectoryPush(version, srcRoot, dstRoot, remote, matcher, dontDelete, dontDo, report, callbacks); + deployDirectoryPush(version, srcRoot, dstRoot, remote, matcher, dontDelete, dontDo, callbacks); remote.createSnapshot(storePath[0], "Deployment", "Post Deployment Snapshot."); if (callbacks != null) { DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.END, new Pair(version, srcPath), dstPath); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } + processEvent(event, callbacks); + } - return report; + return; } catch (AVMException e) { @@ -340,10 +288,7 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED, new Pair(version, srcPath), dstPath, e.getMessage()); - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } + processEvent(event, callbacks); } throw new AVMException("Deployment to " + hostName + " failed.", e); } @@ -355,7 +300,7 @@ public class DeploymentServiceImpl implements DeploymentService } /** - * Deploy all the children of corresponding directories. + * Deploy all the children of corresponding directories. (ASR version) * @param src The source directory. * @param dst The destination directory. * @param remote The AVMRemote instance. @@ -363,11 +308,11 @@ public class DeploymentServiceImpl implements DeploymentService * @param dontDo Flag for dry run. */ private void deployDirectoryPush(int version, - AVMNodeDescriptor src, AVMNodeDescriptor dst, + AVMNodeDescriptor src, + AVMNodeDescriptor dst, AVMRemote remote, NameMatcher matcher, boolean dontDelete, boolean dontDo, - DeploymentReport report, List callbacks) { if (src.getGuid().equals(dst.getGuid())) @@ -389,7 +334,7 @@ public class DeploymentServiceImpl implements DeploymentService AVMNodeDescriptor dstNode = dstList.get(name); if (!excluded(matcher, srcNode.getPath(), dstNode != null ? dstNode.getPath() : null)) { - deploySinglePush(version, srcNode, dst, dstNode, remote, matcher, dontDelete, dontDo, report, callbacks); + deploySinglePush(version, srcNode, dst, dstNode, remote, matcher, dontDelete, dontDo, callbacks); } } // Delete nodes that are missing in the source. @@ -410,18 +355,7 @@ public class DeploymentServiceImpl implements DeploymentService new DeploymentEvent(DeploymentEvent.Type.DELETED, source, destination); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); if (dontDo) { continue; @@ -446,7 +380,6 @@ public class DeploymentServiceImpl implements DeploymentService AVMNodeDescriptor dst, AVMRemote remote, NameMatcher matcher, boolean dontDelete, boolean dontDo, - DeploymentReport report, List callbacks) { // Destination does not exist. @@ -461,18 +394,7 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, source, destination); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); if (dontDo) { return; @@ -486,18 +408,7 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, source, destination); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); if (dontDo) { return; @@ -515,7 +426,7 @@ public class DeploymentServiceImpl implements DeploymentService // If the destination is also a directory, recursively deploy. if (dst.isDirectory()) { - deployDirectoryPush(version, src, dst, remote, matcher, dontDelete, dontDo, report, callbacks); + deployDirectoryPush(version, src, dst, remote, matcher, dontDelete, dontDo, callbacks); return; } Pair source = @@ -523,18 +434,8 @@ public class DeploymentServiceImpl implements DeploymentService String destination = dst.getPath(); DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, source, destination); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); + if (dontDo) { return; @@ -561,14 +462,7 @@ public class DeploymentServiceImpl implements DeploymentService { fgLogger.debug(event); } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); if (dontDo) { return; @@ -585,18 +479,7 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.UPDATED, source, destination); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - report.add(event); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } + processEvent(event, callbacks); if (dontDo) { return; @@ -762,23 +645,64 @@ public class DeploymentServiceImpl implements DeploymentService } } - private DeploymentReceiverService getReceiver(String hostName, int port) - { + /** + * Utility method to get the payload transformers for a named transport + * + * The transport adapters are sprung into the deploymentReceiverTransportAdapters property + * + * @return the transformers + */ + private List getTransformers(String transportName) + { + + DeploymentReceiverTransportAdapter adapter = deploymentReceiverTransportAdapters.get(transportName); + + if(adapter == null) { + // Adapter does not exist + fgLogger.error("Deployment Receiver Transport adapter does not exist for transport. Name: " + transportName); + throw new AVMException("Deployment Receiver Transport adapter does not exist for transport. Name: " + transportName); + } + + List transformers = adapter.getTransformers(); + return transformers; + } + + + + /** + * Utility method to get a connection to a remote file system receiver (FSR) + * + * The transport adapters are sprung into the deploymentReceiverTransportAdapters property + * @param transportName the name of the adapter for the transport + * @param hostName the hostname or IP address to connect to + * @param port the port number + * @param version the version of the website to deploy + * @param srcPath the path of the website + * + * @return an implementation of the service + */ + private DeploymentReceiverService getDeploymentReceiverService(String transportName, String hostName, int port, int version, String srcPath) + { + + DeploymentReceiverTransportAdapter adapter = deploymentReceiverTransportAdapters.get(transportName); + + if(adapter == null) { + // Adapter does not exist + fgLogger.error("Deployment Receiver Transport adapter does not exist for transport. Name: " + transportName); + throw new AVMException("Deployment Receiver Transport adapter does not exist for transport. Name: " + transportName); + } try { - RmiProxyFactoryBean factory = new RmiProxyFactoryBean(); - factory.setRefreshStubOnConnectFailure(true); - factory.setServiceInterface(DeploymentReceiverTransport.class); - factory.setServiceUrl("rmi://" + hostName + ":" + port + "/deployment"); - factory.afterPropertiesSet(); - DeploymentReceiverTransport transport = (DeploymentReceiverTransport)factory.getObject(); + DeploymentReceiverTransport transport = adapter.getTransport(hostName, port, version, srcPath); + + // Now decorate the transport with the service client DeploymentReceiverServiceClient service = new DeploymentReceiverServiceClient(); service.setDeploymentReceiverTransport(transport); return service; } catch (Exception e) { - throw new AVMException("Could not connect to " + hostName + " at " + port, e); + throw new AVMException("Could not connect to remote FSR, transportName:" + transportName + ", hostName:" + hostName + ", port: " + port, e); } } @@ -842,85 +766,195 @@ public class DeploymentServiceImpl implements DeploymentService } } - /* (non-Javadoc) + /** + * Deploy differences to a File System Receiver, FSR + * + * @param version snapshot version to deploy. If 0 then a new snapshot is created. + * @param srcPath + * @param adapterName + * @param hostName + * @param port + * @param userName + * @param password + * @param target + * @param matcher + * @param createDst Not implemented + * @param dontDelete Not implemented + * @param dontDo Not implemented + * @param callbacks Event callbacks when a deployment Starts, Ends, Adds, Deletes etc. + * * @see org.alfresco.service.cmr.avm.deploy.DeploymentService#deployDifferenceFS(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean) */ - public DeploymentReport deployDifferenceFS(int version, String srcPath, String hostName, - int port, String userName, String password, - String target, NameMatcher matcher, boolean createDst, - boolean dontDelete, boolean dontDo, - List callbacks) + public void deployDifferenceFS(int version, + String srcPath, + String adapterName, + String hostName, + int port, + String userName, + String password, + String target, + NameMatcher matcher, + boolean createDst, + boolean dontDelete, + boolean dontDo, + List callbacks) { if (fgLogger.isDebugEnabled()) { - fgLogger.debug("Deploying To FileSystem Reciever on " + hostName + " to target " + target); + fgLogger.debug("Deploying To File System Reciever on " + hostName + " to target " + target); } - DeploymentReport report = new DeploymentReport(); + DeploymentReceiverService service = null; + Listtransformers = null; String ticket = null; - try - { - service = getReceiver(hostName, port); - DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.START, - new Pair(version, srcPath), - target); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } - report.add(event); - String storeName = srcPath.substring(0, srcPath.indexOf(':')); + + String currentEffectiveUser = AuthenticationUtil.getCurrentEffectiveUserName(); - if (version < 0) + try + { + // Kick off the event queue that will process deployment call-backs + LinkedBlockingQueue eventQueue = new LinkedBlockingQueue(); + EventQueueWorker eventQueueWorker = new EventQueueWorker(currentEffectiveUser, eventQueue, callbacks); + eventQueueWorker.setName(eventQueueWorker.getClass().getName()); + eventQueueWorker.setPriority(Thread.currentThread().getPriority()); + eventQueueWorker.start(); + + try { - version = fAVMService.createSnapshot(storeName, null, null).get(storeName); - } - ticket = service.begin(target, userName, password); - deployDirectoryPush(service, ticket, report, callbacks, version, srcPath, "/", matcher); - service.commit(ticket); - event = new DeploymentEvent(DeploymentEvent.Type.END, - new Pair(version, srcPath), - target); - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) + eventQueue.add(new DeploymentEvent(DeploymentEvent.Type.START, + new Pair(version, srcPath), + target)); + try { + + if (version < 0) + { + String storeName = srcPath.substring(0, srcPath.indexOf(':')); + version = fAVMService.createSnapshot(storeName, null, null).get(storeName); + } + + transformers = getTransformers(adapterName); + service = getDeploymentReceiverService(adapterName, hostName, port, version, srcPath); + } + catch (Exception e) { - callback.eventOccurred(event); + // unable to get service + eventQueue.add(new DeploymentEvent(DeploymentEvent.Type.FAILED, + new Pair(version, srcPath), + target, e.getMessage())); + throw e; } + + // Go parallel to reduce the problems of high network latency + + LinkedBlockingQueue sendQueue = new LinkedBlockingQueue(); + List errors = Collections.synchronizedList(new ArrayList()); + + SendQueueWorker[] workers = new SendQueueWorker[numberOfSendingThreads]; + for(int i = 0; i < numberOfSendingThreads; i++) + { + workers[i] = new SendQueueWorker(currentEffectiveUser, service, fAVMService, errors, eventQueue, sendQueue, transformers); + workers[i].setName(workers[i].getClass().getName()); + workers[i].setPriority(Thread.currentThread().getPriority()); + } + + for(SendQueueWorker sender : workers) + { + sender.start(); + } + + try + { + ticket = service.begin(target, userName, password); + deployDirectoryPushFSR(service, ticket, version, srcPath, "/", matcher, eventQueue, sendQueue, errors); + } + catch (Exception e) + { + errors.add(e); + } + finally + { + // clean up senders thread pool + fgLogger.debug("closing deployment workers"); + for(SendQueueWorker sender : workers) + { + sender.stopMeWhenIdle(); + } + for(SendQueueWorker sender : workers) + { + sender.join(); + } + fgLogger.debug("deployment workers closed"); + + if (errors.size() <= 0 && ticket != null) + { + try + { + service.commit(ticket); + } + catch (Exception e) + { + errors.add(e); + } + } + + if(errors.size() > 0) + { + Exception firstError = errors.get(0); + + eventQueue.add(new DeploymentEvent(DeploymentEvent.Type.FAILED, + new Pair(version, srcPath), + target, firstError.getMessage())); + + if (ticket != null) + { + try + { + service.abort(ticket); + } + catch (Exception ae) + { + // nothing we can do here + fgLogger.error("Unable to abort deployment. Error in exception handler", ae); + } + } + // yes there were errors, throw the first exception that was saved + MessageFormat f = new MessageFormat("Error during deployment srcPath: {0}, version:{1}, adapterName:{2}, hostName:{3}, port:{4}, error:{5}"); + Object[] objs = { srcPath, version, adapterName, hostName, port, firstError }; + + throw new AVMException(f.format(objs), firstError); + } + } // end of finally block + + // Success if we get here + eventQueue.add(new DeploymentEvent(DeploymentEvent.Type.END, + new Pair(version, srcPath), + target)); + + fgLogger.debug("deployment completed successfully"); + } + finally + { + // Now stutdown the event queue + fgLogger.debug("closing event queue"); + eventQueueWorker.stopMeWhenIdle(); + eventQueueWorker.join(); + fgLogger.debug("event queue closed"); } - report.add(event); - return report; } catch (Exception e) { - DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED, - new Pair(version, srcPath), - target, e.getMessage()); - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - - if (service != null && ticket != null) - { - // TODO MER - Consider what happens if abort throws an exception itself, then we loose e? - service.abort(ticket); - } - - throw new AVMException("Deployment to: " + target + " failed.", e); + // yes there were errors + MessageFormat f = new MessageFormat("Deployment exception, unable to deploy : srcPath:{0}, target:{1}, version:{2}, adapterName:{3}, hostName:{4}, port:{5}, error:{6}"); + Object[] objs = { srcPath, target, version, adapterName, hostName, port, e }; + throw new AVMException(f.format(objs), e); } } /** - * deployDirectoryPush + * deployDirectoryPush (FSR only) + * * Compares the source and destination listings and updates report with update events required to make * dest similar to src. + * * @param service * @param ticket * @param report @@ -930,21 +964,27 @@ public class DeploymentServiceImpl implements DeploymentService * @param dstPath * @param matcher */ - private void deployDirectoryPush(DeploymentReceiverService service, String ticket, - DeploymentReport report, List callbacks, - int version, - String srcPath, String dstPath, NameMatcher matcher) + private void deployDirectoryPushFSR(DeploymentReceiverService service, + String ticket, + int version, + String srcPath, + String dstPath, + NameMatcher matcher, + BlockingQueue eventQueue, + BlockingQueue sendQueue, + List errors) { Map srcListing = fAVMService.getDirectoryListing(version, srcPath); List dstListing = service.getListing(ticket, dstPath); Iterator srcIter = srcListing.values().iterator(); Iterator dstIter = dstListing.iterator(); + // Here with two sorted directory listings AVMNodeDescriptor src = null; FileDescriptor dst = null; // Step through both directory listings - while (srcIter.hasNext() || dstIter.hasNext() || src != null || dst != null) + while ((srcIter.hasNext() || dstIter.hasNext() || src != null || dst != null) && errors.size() <= 0) { if (src == null) { @@ -970,22 +1010,14 @@ public class DeploymentServiceImpl implements DeploymentService String newDstPath = extendPath(dstPath, dst.getName()); if (!excluded(matcher, null, newDstPath)) { - service.delete(ticket, newDstPath); - DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.DELETED, - new Pair(version, extendPath(srcPath, dst.getName())), - newDstPath); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } - report.add(event); +// service.delete(ticket, newDstPath); +// eventQueue.add(new DeploymentEvent(DeploymentEvent.Type.DELETED, +// new Pair(version, extendPath(srcPath, dst.getName())), +// newDstPath)); + + sendQueue.add(new DeploymentWork(new DeploymentEvent(DeploymentEvent.Type.DELETED, + new Pair(version, extendPath(srcPath, dst.getName())), + newDstPath), ticket)); } dst = null; continue; @@ -995,7 +1027,7 @@ public class DeploymentServiceImpl implements DeploymentService { if (!excluded(matcher, src.getPath(), null)) { - copy(service, ticket, report, callbacks, version, src, dstPath, matcher); + createOnFSR(service, ticket, version, src, dstPath, matcher, sendQueue); } src = null; continue; @@ -1008,7 +1040,7 @@ public class DeploymentServiceImpl implements DeploymentService // src is less than dst - must be new content in src if (!excluded(matcher, src.getPath(), null)) { - copy(service, ticket, report, callbacks, version, src, dstPath, matcher); + createOnFSR(service, ticket, version, src, dstPath, matcher, sendQueue); } src = null; continue; @@ -1024,11 +1056,18 @@ public class DeploymentServiceImpl implements DeploymentService } if (src.isFile()) { + // this is an update to a file String extendedPath = extendPath(dstPath, dst.getName()); if (!excluded(matcher, src.getPath(), extendedPath)) { - copyFile(service, ticket, report, callbacks, version, src, - extendedPath); + // Work in progress + sendQueue.add(new DeploymentWork( + new DeploymentEvent(DeploymentEvent.Type.UPDATED, + new Pair(version, src.getPath()), + extendedPath), ticket, src)); + // Work in progress +// copyFileToFSR(service, ticket, version, src, +// extendedPath, false); } src = null; dst = null; @@ -1040,7 +1079,7 @@ public class DeploymentServiceImpl implements DeploymentService String extendedPath = extendPath(dstPath, dst.getName()); if (!excluded(matcher, src.getPath(), extendedPath)) { - deployDirectoryPush(service, ticket, report, callbacks, version, src.getPath(), extendPath(dstPath, dst.getName()), matcher); + deployDirectoryPushFSR(service, ticket, version, src.getPath(), extendedPath, matcher, eventQueue, sendQueue, errors); } service.setGuid(ticket, extendedPath, src.getGuid()); src = null; @@ -1049,7 +1088,7 @@ public class DeploymentServiceImpl implements DeploymentService } if (!excluded(matcher, src.getPath(), null)) { - copy(service, ticket, report, callbacks, version, src, dstPath, matcher); + createOnFSR(service, ticket, version, src, dstPath, matcher, sendQueue); } src = null; dst = null; @@ -1058,71 +1097,26 @@ public class DeploymentServiceImpl implements DeploymentService // diff > 0 // Destination is missing in source, delete it. String newDstPath = extendPath(dstPath, dst.getName()); - service.delete(ticket, newDstPath); - DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.DELETED, - new Pair(version, extendPath(srcPath, dst.getName())), - newDstPath); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } - report.add(event); + + // service.delete(ticket, newDstPath); +// +// eventQueue.add(new DeploymentEvent(DeploymentEvent.Type.DELETED, +// new Pair(version, extendPath(srcPath, dst.getName())), +// newDstPath)); + + // + sendQueue.add(new DeploymentWork(new DeploymentEvent(DeploymentEvent.Type.DELETED, + new Pair(version, extendPath(srcPath, dst.getName())), + newDstPath), ticket)); + + // + dst = null; } } /** - * Copy or overwrite a single file. - * @param service - * @param ticket - * @param report - * @param callback - * @param version - * @param src - * @param dstPath - */ - private void copyFile(DeploymentReceiverService service, String ticket, - DeploymentReport report, List callbacks, int version, - AVMNodeDescriptor src, String dstPath) - { - InputStream in = fAVMService.getFileInputStream(src); - OutputStream out = service.send(ticket, dstPath, src.getGuid()); - try - { - copyStream(in, out); - service.finishSend(ticket, out); - DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, - new Pair(version, src.getPath()), - dstPath); - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(event); - } - if (callbacks != null) - { - for (DeploymentCallback callback : callbacks) - { - callback.eventOccurred(event); - } - } - report.add(event); - } - catch (Exception e) - { - service.abort(ticket); - throw new AVMException("Failed to copy " + src + ". Deployment aborted.", e); - } - } - - /** - * Copy a file or directory to an empty destination. + * Copy a file or directory to an empty destination on an FSR * @param service * @param ticket * @param report @@ -1131,21 +1125,45 @@ public class DeploymentServiceImpl implements DeploymentService * @param src * @param parentPath */ - private void copy(DeploymentReceiverService service, String ticket, - DeploymentReport report, List callbacks, - int version, AVMNodeDescriptor src, String parentPath, NameMatcher matcher) + private void createOnFSR(DeploymentReceiverService service, + String ticket, + int version, + AVMNodeDescriptor src, + String parentPath, + NameMatcher matcher, + BlockingQueue sendQueue) { String dstPath = extendPath(parentPath, src.getName()); + + sendQueue.add(new DeploymentWork( + new DeploymentEvent(DeploymentEvent.Type.COPIED, + new Pair(version, src.getPath()), + dstPath), ticket, src)); + if (src.isFile()) { - copyFile(service, ticket, report, callbacks, version, src, dstPath); + // copyFileToFSR(service, ticket, version, src, dstPath, true, transformers); return; } - // src is a directory. - service.mkdir(ticket, dstPath, src.getGuid()); - DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, - new Pair(version, src.getPath()), - dstPath); + + // Need to create directories in controlling thread since then need to be BEFORE any children are written + + // here if src is a directory. + service.mkdir(ticket, dstPath, src.getGuid()); + + // now copy the children over + Map listing = fAVMService.getDirectoryListing(src); + for (AVMNodeDescriptor child : listing.values()) + { + if (!excluded(matcher, child.getPath(), null)) + { + createOnFSR(service, ticket, version, child, dstPath, matcher, sendQueue); + } + } + } + + private void processEvent(DeploymentEvent event, List callbacks) + { if (fgLogger.isDebugEnabled()) { fgLogger.debug(event); @@ -1157,15 +1175,6 @@ public class DeploymentServiceImpl implements DeploymentService callback.eventOccurred(event); } } - report.add(event); - Map listing = fAVMService.getDirectoryListing(src); - for (AVMNodeDescriptor child : listing.values()) - { - if (!excluded(matcher, child.getPath(), null)) - { - copy(service, ticket, report, callbacks, version, child, dstPath, matcher); - } - } } /** @@ -1199,7 +1208,7 @@ public class DeploymentServiceImpl implements DeploymentService * Get the object to lock for an alfresco->alfresco target. * @param host * @param port - * @return + * @return the lock */ private synchronized DeploymentDestination getLock(String host, int port) { @@ -1212,4 +1221,256 @@ public class DeploymentServiceImpl implements DeploymentService } return dest; } + + + private Map deploymentReceiverTransportAdapters; + /** + * The deployment transport adapters provide the factories used to connect to a remote file system receiver. + */ + public void setDeploymentReceiverTransportAdapters(Map adapters) { + this.deploymentReceiverTransportAdapters=adapters; + } + + public Map getDeploymentReceiverTransportAdapters() { + return this.deploymentReceiverTransportAdapters; + } + + public Set getAdapterNames() + { + if(deploymentReceiverTransportAdapters != null) { + return(deploymentReceiverTransportAdapters.keySet()); + } + else + { + Set ret = new HashSet(1); + ret.add("default"); + return ret; + } + } + + public void setNumberOfSendingThreads(int numberOfSendingThreads) { + this.numberOfSendingThreads = numberOfSendingThreads; + } + + public int getNumberOfSendingThreads() { + return numberOfSendingThreads; + } + + /** + * This thread processes the event queue to do the callbacks + * @author mrogers + * + */ + private class EventQueueWorker extends Thread + { + private BlockingQueue eventQueue; + private List callbacks; + private String userName; + + private boolean stopMe = false; + + EventQueueWorker(String userName, BlockingQueue eventQueue, List callbacks) + { + this.eventQueue = eventQueue; + this.callbacks = callbacks; + this.userName = userName; + } + + public void run() + { + AuthenticationUtil.setCurrentEffectiveUser(userName); + AuthenticationUtil.setCurrentUser(userName); + + while (true) + { + DeploymentEvent event = null; + try { + event = eventQueue.poll(3, TimeUnit.SECONDS); + } catch (InterruptedException e1) { + fgLogger.debug("Interrupted ", e1); + } + + if(event == null) + { + if(stopMe) + { + fgLogger.debug("Event Queue Closing Normally"); + break; + } + continue; + } + + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } + if (callbacks != null) + { + for (DeploymentCallback callback : callbacks) + { + callback.eventOccurred(event); + } + } + } + } + + public void stopMeWhenIdle() + { + stopMe = true; + } + + } + + /** + * This thread processes the send queue + * @author mrogers + * + */ + private class SendQueueWorker extends Thread + { + private BlockingQueue eventQueue; + private BlockingQueue sendQueue; + private DeploymentReceiverService service; + private String userName; + private AVMService avmService; + List errors; + List transformers; + + private boolean stopMe = false; + + SendQueueWorker(String userName, + DeploymentReceiverService service, + AVMService avmService, + List errors, + BlockingQueue eventQueue, + BlockingQueue sendQueue, + List transformers + ) + { + this.eventQueue = eventQueue; + this.sendQueue = sendQueue; + this.service = service; + this.avmService = avmService; + this.errors = errors; + this.transformers = transformers; + this.userName = userName; + + + } + + public void run() + { + AuthenticationUtil.setCurrentEffectiveUser(userName); + AuthenticationUtil.setCurrentUser(userName); + + while (errors.size() <= 0) + { + DeploymentWork work = null; + try { + work = sendQueue.poll(3, TimeUnit.SECONDS); + } catch (InterruptedException e1) { + fgLogger.debug("Interrupted ", e1); + continue; + } + + if(work == null) + { + if(stopMe) + { + fgLogger.debug("Send Queue Worker Closing Normally"); + eventQueue = null; + sendQueue = null; + service = null; + errors = null; + break; + } + } + + if(work != null) + { + DeploymentEvent event = work.getEvent(); + String ticket = work.getTicket(); + try + { + if(event.getType().equals(DeploymentEvent.Type.DELETED)) + { + service.delete(ticket, event.getDestination()); + } + else if (event.getType().equals(DeploymentEvent.Type.COPIED)) + { + AVMNodeDescriptor src = work.getSrc(); + if(src.isFile()) + { + copyFileToFSR(src, event.getDestination(), ticket); + } + else + { + // Do nothing. mkdir done on main thread. + //makeDirectoryOnFSR(src, event.getDestination(), ticket); + } + } + else if (event.getType().equals(DeploymentEvent.Type.UPDATED)) + { + copyFileToFSR(work.getSrc(), event.getDestination(), ticket); + } + // success, now put the event onto the event queue + eventQueue.add(event); + } + catch (Exception e) + { + errors.add(e); + } + } + } + fgLogger.debug("Send Queue Worker finished"); + } + + public void stopMeWhenIdle() + { + stopMe = true; + } + + + /** + * Create or update a single file on a remote FSR. + * @param ticket + * @param src which file to copy + * @param dstPath where to copy the file + */ + private void copyFileToFSR( + AVMNodeDescriptor src, + String dstPath, + String ticket) + { + try + { + InputStream in = avmService.getFileInputStream(src); + + OutputStream out = service.send(ticket, dstPath, src.getGuid()); + OutputStream baseStream = out; // finish send needs out, not a decorated stream + + // Buffer the output, we don't want to send lots of small packets + out = new BufferedOutputStream(out, 10000); + + // Call content transformers here to transform from local to network format + if(transformers != null && transformers.size() > 0) { + // yes we have pay-load transformers + for(DeploymentTransportOutputFilter transformer : transformers) + { + out = transformer.addFilter(out, src.getPath()); + } + } + + copyStream(in, out); + service.finishSend(ticket, baseStream); + } + catch (Exception e) + { + fgLogger.debug("Failed to copy dstPath:" + dstPath , e); + + // throw first exception - this is the root of the problem. + throw new AVMException("Failed to copy filename:" + dstPath, e); + } + } + } } diff --git a/source/java/org/alfresco/repo/deploy/DeploymentWork.java b/source/java/org/alfresco/repo/deploy/DeploymentWork.java new file mode 100644 index 0000000000..a33f8c517e --- /dev/null +++ b/source/java/org/alfresco/repo/deploy/DeploymentWork.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.deploy; + +import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.deploy.DeploymentEvent; + +class DeploymentWork +{ + private DeploymentEvent event; + private AVMNodeDescriptor src; + private String ticket; + + public DeploymentWork(DeploymentEvent event, String ticket) + { + this.event = event; + this.ticket = ticket; + } + + public DeploymentWork(DeploymentEvent event, String ticket, AVMNodeDescriptor src) + { + this.event = event; + this.ticket = ticket; + this.setSrc(src); + + } + + public DeploymentEvent getEvent() + { + return event; + } + + public String getTicket() + { + return this.ticket; + } + + public void setSrc(AVMNodeDescriptor src) { + this.src = src; + } + + public AVMNodeDescriptor getSrc() { + return src; + } + +} diff --git a/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java b/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java index 23f9d9c36e..ccdb5dee87 100644 --- a/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java +++ b/source/java/org/alfresco/repo/deploy/FSDeploymentTest.java @@ -26,11 +26,15 @@ package org.alfresco.repo.deploy; import java.io.File; +import java.util.ArrayList; +import java.util.List; import org.alfresco.repo.avm.AVMServiceTestBase; import org.alfresco.repo.avm.util.BulkLoader; +import org.alfresco.service.cmr.avm.deploy.DeploymentCallback; import org.alfresco.service.cmr.avm.deploy.DeploymentEvent; import org.alfresco.service.cmr.avm.deploy.DeploymentReport; +import org.alfresco.service.cmr.avm.deploy.DeploymentReportCallback; import org.alfresco.service.cmr.avm.deploy.DeploymentService; import org.alfresco.util.Deleter; import org.alfresco.util.NameMatcher; @@ -62,7 +66,11 @@ public class FSDeploymentTest extends AVMServiceTestBase NameMatcher matcher = (NameMatcher)fContext.getBean("globalPathExcluder"); setupBasicTree(); fService.createFile("main:/a/b", "fudge.bak").close(); - DeploymentReport report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + DeploymentReport report = new DeploymentReport(); + List callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); int count = 0; for (DeploymentEvent event : report) { @@ -70,7 +78,14 @@ public class FSDeploymentTest extends AVMServiceTestBase count++; } assertEquals(10, count); - report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + + report = new DeploymentReport(); + callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + callbacks.add(new DeploymentReportCallback(report)); + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); count = 0; for (DeploymentEvent event : report) { @@ -80,7 +95,13 @@ public class FSDeploymentTest extends AVMServiceTestBase assertEquals(2, count); fService.createFile("main:/d", "jonathan").close(); fService.removeNode("main:/a/b"); - report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + + report = new DeploymentReport(); + callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); count = 0; for (DeploymentEvent event : report) { @@ -90,7 +111,12 @@ public class FSDeploymentTest extends AVMServiceTestBase assertEquals(4, count); fService.removeNode("main:/d/e"); fService.createFile("main:/d", "e").close(); - report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + + report = new DeploymentReport(); + callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); count = 0; for (DeploymentEvent event : report) { @@ -102,7 +128,11 @@ public class FSDeploymentTest extends AVMServiceTestBase fService.createDirectory("main:/d", "e"); fService.createFile("main:/d/e", "Warren.txt").close(); fService.createFile("main:/d/e", "It's a silly name.txt").close(); - report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + report = new DeploymentReport(); + callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); count = 0; for (DeploymentEvent event : report) { @@ -113,10 +143,18 @@ public class FSDeploymentTest extends AVMServiceTestBase BulkLoader loader = new BulkLoader(); loader.setAvmService(fService); loader.recursiveLoad("source/java/org/alfresco/repo/avm", "main:/"); - report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + report = new DeploymentReport(); + callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); fService.removeNode("main:/avm/hibernate"); fService.getFileOutputStream("main:/avm/AVMServiceTest.java").close(); - report = service.deployDifferenceFS(-1, "main:/", "localhost", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, null); + report = new DeploymentReport(); + callbacks = new ArrayList(); + callbacks.add(new DeploymentReportCallback(report)); + + service.deployDifferenceFS(-1, "main:/", "localhost", "default", 44100, "Giles", "Watcher", "sampleTarget", matcher, false, false, false, callbacks); count = 0; for (DeploymentEvent event : report) { diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index d3ebefcd6a..8a7ea32de0 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -34,6 +34,7 @@ import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.audit.AuditService; import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.deploy.DeploymentService; import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.coci.CheckOutCheckInService; @@ -451,4 +452,14 @@ public class ServiceDescriptorRegistry { return (TaggingService)getService(TAGGING_SERVICE); } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getDeploymentService() + */ + public DeploymentService getDeploymentService() { + return (DeploymentService) getService(DEPLOYMENT_SERVICE); + + } + + } diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index 751e663657..d4a825f206 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -62,6 +62,7 @@ import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.view.ExporterService; import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.avm.deploy.DeploymentService; import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.namespace.NamespaceService; @@ -125,6 +126,7 @@ public interface ServiceRegistry static final QName VIRT_SERVER_REGISTRY = QName.createQName(NamespaceService.ALFRESCO_URI, "VirtServerRegistry"); static final QName THUMBNAIL_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ThumbnailService"); static final QName TAGGING_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TaggingService"); + static final QName DEPLOYMENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DeploymentService"); /** * Get the list of services provided by the Repository @@ -411,4 +413,11 @@ public interface ServiceRegistry */ @NotAuditable TaggingService getTaggingService(); + + /** + * Get the Deployment Service + * @return the deployment service (or null, if one is not provided) + */ + @NotAuditable + DeploymentService getDeploymentService(); } diff --git a/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentReportCallback.java b/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentReportCallback.java new file mode 100644 index 0000000000..4429bc43be --- /dev/null +++ b/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentReportCallback.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.service.cmr.avm.deploy; + +public class DeploymentReportCallback implements DeploymentCallback +{ + private DeploymentReport report; + + public DeploymentReportCallback(DeploymentReport report ) + { + this.report = report; + } + + /** + * Called each time something happens during deployment. + * This is called synchronously by the deployer and should + * therefore be handled rapidly, if possible. + * @param event The event that occurred. + */ + public void eventOccurred(DeploymentEvent event){ + report.add(event); + } +} diff --git a/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentService.java b/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentService.java index d177d865fa..97b6d38963 100644 --- a/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentService.java +++ b/source/java/org/alfresco/service/cmr/avm/deploy/DeploymentService.java @@ -3,6 +3,7 @@ */ package org.alfresco.service.cmr.avm.deploy; +import java.util.Set; import java.util.List; import org.alfresco.service.cmr.action.ActionService; @@ -29,9 +30,11 @@ public interface DeploymentService * @param dontDo If this is set then this is a dry run. * @param callback A possibly null callback. */ - public DeploymentReport deployDifference(int version, String srcPath, - String hostName, int port, - String userName, String password, + public void deployDifference(int version, String srcPath, + String hostName, + int port, + String userName, + String password, String dstPath, NameMatcher matcher, boolean createDst, @@ -54,23 +57,37 @@ public interface DeploymentService * Deploy to a filesystem on another machine. * @param version The version to deploy from. * @param srcPath The path to deploy from. + * @param adapterName The name of the transport adapter to connect to the remote system. + * The value "default" means use the traditional RMI used for versions of Alfresco prior to 3.0 * @param hostName The hostname of the filesystem receiver. * @param port The port to connect to. - * @param userName The username for authentication - * @param password The password for authentication + * @param userName The username for authentication of the target + * @param password The password for authentication of the target * @param dstTarget The target on the deployment receiver. * @param createDst Flag for whether a missing destination should be created. * @param dontDelete Don't delete deleted nodes from destination. * @param dontDo If this is set, this is a dry run. * @param callback A possibly null callback. */ - public DeploymentReport deployDifferenceFS(int version, String srcPath, - String hostName, int port, - String userName, String password, - String dstTarget, - NameMatcher matcher, - boolean createDst, - boolean dontDelete, - boolean dontDo, - List callback); + public void deployDifferenceFS(int version, + String srcPath, + String adapterName, + String hostName, + int port, + String userName, + String password, + String dstTarget, + NameMatcher matcher, + boolean createDst, + boolean dontDelete, + boolean dontDo, + List callback); + + /** + * Get the names of the transport adapters. + * + * @return the adapters + */ + public Set getAdapterNames(); + } diff --git a/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java b/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java index 88d982ce2a..300cde5e8d 100644 --- a/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java +++ b/source/java/org/alfresco/util/ThreadPoolExecutorFactoryBean.java @@ -71,6 +71,7 @@ public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingB private static final boolean DEFAULT_THREAD_DAEMON = Boolean.TRUE; private static final int DEFAULT_WORK_QUEUE_SIZE = -1; private static final RejectedExecutionHandler DEFAULT_REJECTED_EXECUTION_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy(); + private String poolName = ""; private int corePoolSize; private int maximumPoolSize; @@ -183,6 +184,11 @@ public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingB threadFactory.setThreadDaemon(threadDaemon); threadFactory.setThreadPriority(threadPriority); + if(poolName.length() > 0) + { + threadFactory.setNamePrefix(poolName); + } + if (workQueueSize < 0) { workQueueSize = Integer.MAX_VALUE; @@ -227,4 +233,14 @@ public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingB { return ThreadPoolExecutor.class; } + + public String getPoolName() + { + return this.poolName; + } + + public void setPoolName(String poolName) + { + this.poolName = poolName; + } } diff --git a/source/java/org/alfresco/util/TraceableThreadFactory.java b/source/java/org/alfresco/util/TraceableThreadFactory.java index d3e15ef28d..45036c12a2 100644 --- a/source/java/org/alfresco/util/TraceableThreadFactory.java +++ b/source/java/org/alfresco/util/TraceableThreadFactory.java @@ -54,7 +54,7 @@ public class TraceableThreadFactory implements ThreadFactory } private final ThreadGroup group; - private final String namePrefix; + private String namePrefix; private final AtomicInteger threadNumber; private boolean threadDaemon; private int threadPriority; @@ -98,4 +98,15 @@ public class TraceableThreadFactory implements ThreadFactory return thread; } + + public void setNamePrefix(String namePrefix) + { + this.namePrefix = namePrefix; + } + + public String getNamePrefix() + { + return this.namePrefix; + } + }