Merged V2.2 to HEAD

7498: Deployment callback and event changes
   7499: Build fix
   7505: Fixed deployment script issue in IE
   7525: Added sample job that will clean deployment attempts older than 180 days (by default)
   7526: Typo compilation fix


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8383 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2008-02-26 15:13:48 +00:00
parent ff15cf4079
commit fecfd82767
9 changed files with 418 additions and 45 deletions

View File

@@ -0,0 +1,178 @@
/*
* 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.avm;
import java.util.Calendar;
import java.util.Date;
import org.alfresco.model.WCMAppModel;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Bean that is responsible for locating expired content and routing
* it for review to the most relevant user.
*
* @author gavinc
*/
public class AVMDeploymentAttemptCleaner
{
// defaults in case these properties are not configured in Spring
protected String adminUserName = "admin";
protected long maxAge = 180L;
protected NodeService nodeService;
protected TransactionService transactionService;
protected SearchService searchService;
protected ImporterBootstrap importerBootstrap;
private static Log logger = LogFactory.getLog(AVMDeploymentAttemptCleaner.class);
public AVMDeploymentAttemptCleaner()
{
}
public void setAdminUserName(String adminUserName)
{
this.adminUserName = adminUserName;
}
public void setMaxAge(long maxAge)
{
this.maxAge = new Long(maxAge);
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
{
this.importerBootstrap = importerBootstrap;
}
/**
* Executes the expired content processor.
* The work is performed within a transaction running as the system user.
*/
public void execute()
{
// setup a wrapper object to run the processor within a transaction.
AuthenticationUtil.RunAsWork<String> authorisedWork = new AuthenticationUtil.RunAsWork<String>()
{
public String doWork() throws Exception
{
RetryingTransactionCallback<String> expiredContentWork = new RetryingTransactionCallback<String>()
{
public String execute() throws Exception
{
cleanAttempts();
return null;
}
};
return transactionService.getRetryingTransactionHelper().doInTransaction(expiredContentWork);
}
};
// perform the work as the system user
AuthenticationUtil.runAs(authorisedWork, this.adminUserName);
}
/**
* Entry point.
*/
private void cleanAttempts()
{
// calculate the date 'maxAge' days before today
long daysInMs = 1000L*60L*60L*24L*this.maxAge;
Date toDate = new Date(new Date().getTime() - daysInMs);
// build the query to find deployment attempts older than this.maxAge
Calendar cal = Calendar.getInstance();
cal.setTime(toDate);
StringBuilder query = new StringBuilder("@");
query.append(NamespaceService.WCMAPP_MODEL_PREFIX);
query.append("\\:");
query.append(WCMAppModel.PROP_DEPLOYATTEMPTTIME.getLocalName());
query.append(":[0001\\-01\\-01T00:00:00 TO ");
query.append(cal.get(Calendar.YEAR));
query.append("\\-");
query.append((cal.get(Calendar.MONTH)+1));
query.append("\\-");
query.append(cal.get(Calendar.DAY_OF_MONTH));
query.append("T00:00:00]");
if (logger.isDebugEnabled())
logger.debug("Finding old deploymentattempt nodes using query: " + query.toString());
// do the query
ResultSet results = null;
try
{
// execute the query
results = searchService.query(this.importerBootstrap.getStoreRef(),
SearchService.LANGUAGE_LUCENE, query.toString());
if (logger.isDebugEnabled())
logger.debug("Deleting " + results.length() + " old deployment attempts");
// iterate through the attempt nodes and delete them
for (NodeRef attempt : results.getNodeRefs())
{
this.nodeService.deleteNode(attempt);
if (logger.isDebugEnabled())
logger.debug("Deleted deployment attempt: " + attempt);
}
}
finally
{
if (results != null)
{
results.close();
}
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.avm;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Job to periodically execute the deployment attempt cleaner.
*
* <p>
* The following parameters are required:
* <ul>
* <li><b>deploymentAttemptCleaner</b>: The deployment attempt cleaner instance</li>
* </ul>
*
* @author gavinc
*/
public class AVMDeploymentAttemptCleanerJob implements Job
{
/**
* Searches for old deployment attempts and removes them.
*
* @param context The job context
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
// get the expired content processor bean from the job context
AVMDeploymentAttemptCleaner cleaner =
(AVMDeploymentAttemptCleaner)context.getJobDetail().getJobDataMap().get("deploymentAttemptCleaner");
if (cleaner == null)
{
throw new JobExecutionException("Missing job data: deploymentAttemptCleaner");
}
// execute the cleaner to do the actual work
cleaner.execute();
}
}

View File

@@ -0,0 +1,46 @@
package org.alfresco.repo.avm;
import junit.framework.TestCase;
import org.alfresco.repo.avm.AVMDeploymentAttemptCleaner;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
/**
* @see org.alfresco.repo.avm.AVMDeploymentAttemptCleaner
*
* @author gavinc
*/
public class AVMDeploymentAttemptCleanerTest extends TestCase
{
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private AVMDeploymentAttemptCleaner cleaner;
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
ImporterBootstrap importerBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap");
NodeService nodeService = serviceRegistry.getNodeService();
SearchService searchService = serviceRegistry.getSearchService();
TransactionService transactionService = serviceRegistry.getTransactionService();
this.cleaner = new AVMDeploymentAttemptCleaner();
this.cleaner.setNodeService(nodeService);
this.cleaner.setSearchService(searchService);
this.cleaner.setTransactionService(transactionService);
this.cleaner.setImporterBootstrap(importerBootstrap);
// this.cleaner.setMaxAge(30);
}
public void testProcessor() throws Exception
{
this.cleaner.execute();
}
}

View File

@@ -28,7 +28,6 @@ package org.alfresco.repo.avm.actions;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@@ -80,6 +79,7 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
private String defaultRemoteUsername = "admin";
private String defaultRemotePassword = "admin";
private String defaultTargetName = "default";
private List<DeploymentCallback> configuredCallbacks;
private DeploymentService deployService;
private ContentService contentService;
private NodeService nodeService;
@@ -176,6 +176,11 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
{
this.defaultTargetName = defaultTargetName;
}
public void setCallbacks(List<DeploymentCallback> callbacks)
{
this.configuredCallbacks = callbacks;
}
/**
* @param service The NodeService instance
@@ -335,6 +340,20 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
patterns.add(excludes);
regexMatcher.setPatterns(patterns);
}
// create a list of all the callback objects
List<DeploymentCallback> callbacks = new ArrayList<DeploymentCallback>();
if (callback != null)
{
// if present add the callback passed as a parameter (usually for UI purposes)
callbacks.add(callback);
}
if (this.configuredCallbacks != null && this.configuredCallbacks.size() > 0)
{
// add the configured callbacks
callbacks.addAll(this.configuredCallbacks);
}
// take a note of the current date/time
Date startDate = new Date();
@@ -362,13 +381,6 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
logger.debug("Performing file server deployment to " + host + ":" + port +
" using deploymentserver: " + serverProps);
// TODO: Added new NameMatcher parameter to deploy methods. It acts as a filter.
// Any matching path names are ignored for deployment purposes.
List<DeploymentCallback> callbacks = new ArrayList<DeploymentCallback>();
if (callback != null)
{
callbacks.add(callback);
}
report = this.deployService.deployDifferenceFS(version, path, host, port,
remoteUsername, remotePassword, targetName, regexMatcher, true, false, false, callbacks);
}
@@ -378,13 +390,6 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
logger.debug("Performing Alfresco deployment to " + host + ":" + port +
" using deploymentserver: " + serverProps);
// TODO: Added new NameMatcher parameter to deploy methods. It acts as a filter.
// Any matching path names are ignored for deployment purposes.
List<DeploymentCallback> callbacks = new ArrayList<DeploymentCallback>();
if (callback != null)
{
callbacks.add(callback);
}
report = this.deployService.deployDifference(version, path, host, port,
remoteUsername, remotePassword, targetPath, regexMatcher, true, false, false, callbacks);
}
@@ -393,25 +398,6 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
{
deployError = err;
logger.error(deployError);
// report the error to the callback object
// TODO: See if this can be incorporated into the DeploymentCallback I/F
if (callback != null)
{
// to avoid a circular dependency use reflection to call the method
try
{
Method method = callback.getClass().getMethod("errorOccurred", new Class[] {Throwable.class});
if (method != null)
{
method.invoke(callback, new Object[] {err});
}
}
catch (Throwable e)
{
logger.warn("Failed to inform deployment monitor of deployment failure", e);
}
}
}
if (report != null)
@@ -472,8 +458,16 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase
reportProps.put(WCMAppModel.PROP_DEPLOYSUCCESSFUL, (report != null));
if (report == null && error != null)
{
// add error message as fail reason if appropriate
reportProps.put(WCMAppModel.PROP_DEPLOYFAILEDREASON, error.getMessage());
// add error message as fail reason if appropriate (the reported
// exception is a wrapper so get the detail from the cause)
String errorMsg = error.getMessage();
Throwable cause = error.getCause();
if (cause != null)
{
errorMsg = cause.getMessage();
}
reportProps.put(WCMAppModel.PROP_DEPLOYFAILEDREASON, errorMsg);
}
reportRef = this.nodeService.createNode(attempt,
WCMAppModel.ASSOC_DEPLOYMENTREPORTS, WCMAppModel.ASSOC_DEPLOYMENTREPORTS,

View File

@@ -30,6 +30,7 @@ import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.PolicyScope;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -137,9 +138,12 @@ public class WorkingCopyAspect
// Get the origional node
NodeRef origNodeRef = (NodeRef)this.nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE);
if (origNodeRef != null)
{
// Release the lock on the origional node
this.lockService.unlock(origNodeRef);
{
if (this.lockService.getLockStatus(origNodeRef).equals(LockStatus.NO_LOCK) == false)
{
// Release the lock on the origional node
this.lockService.unlock(origNodeRef);
}
}
}
}

View File

@@ -282,7 +282,7 @@ public class DeploymentServiceImpl implements DeploymentService
{
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED,
new Pair<Integer, String>(version, srcPath),
dstPath);
dstPath, e.getMessage());
for (DeploymentCallback callback : callbacks)
{
callback.eventOccurred(event);
@@ -310,7 +310,7 @@ public class DeploymentServiceImpl implements DeploymentService
{
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED,
new Pair<Integer, String>(version, srcPath),
dstPath);
dstPath, e.getMessage());
for (DeploymentCallback callback : callbacks)
{
callback.eventOccurred(event);
@@ -799,10 +799,11 @@ public class DeploymentServiceImpl implements DeploymentService
List<DeploymentCallback> callbacks)
{
DeploymentReport report = new DeploymentReport();
DeploymentReceiverService service = getReceiver(hostName, port);
DeploymentReceiverService service = null;
String ticket = null;
try
{
service = getReceiver(hostName, port);
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.START,
new Pair<Integer, String>(version, srcPath),
target);
@@ -840,12 +841,17 @@ public class DeploymentServiceImpl implements DeploymentService
{
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED,
new Pair<Integer, String>(version, srcPath),
target);
target, e.getMessage());
for (DeploymentCallback callback : callbacks)
{
callback.eventOccurred(event);
}
service.abort(ticket);
if (service != null)
{
service.abort(ticket);
}
throw new AVMException("Deployment to: " + target + " failed.", e);
}
}

View File

@@ -50,6 +50,7 @@ public class TransactionImpl extends LifecycleAdapter implements Transaction, Se
public TransactionImpl()
{
this.commitTimeMs = Long.valueOf(0);
}
@Override

View File

@@ -56,6 +56,8 @@ public class DeploymentEvent implements Serializable
private Pair<Integer, String> fSource;
private String fDestination;
private String fMessage;
public DeploymentEvent(Type type, Pair<Integer, String> source, String destination)
{
@@ -63,6 +65,13 @@ public class DeploymentEvent implements Serializable
fSource = source;
fDestination = destination;
}
public DeploymentEvent(Type type, Pair<Integer, String> source, String destination, String message)
{
this(type, source, destination);
fMessage = message;
}
/**
* Get the type of the event.
@@ -90,12 +99,28 @@ public class DeploymentEvent implements Serializable
{
return fDestination;
}
/**
* Get the message.
* @return
*/
public String getMessage()
{
return fMessage;
}
/**
* Get a String representation.
*/
public String toString()
{
return fType + ": " + fSource + " -> " + fDestination;
String str = fType + ": " + fSource + " -> " + fDestination;
if (fMessage != null)
{
str = str + " (" + fMessage + ")";
}
return str;
}
}