Merged HEAD-QA to HEAD (4.2) (including moving test classes into separate folders)

51903 to 54309 


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@54310 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Samuel Langlois
2013-08-20 17:17:31 +00:00
parent 0a36e2af67
commit ab4ca7177f
1576 changed files with 36419 additions and 8603 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -20,18 +20,24 @@ package org.alfresco.repo.admin.patch;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.repo.batch.BatchProcessWorkProvider;
import org.alfresco.repo.batch.BatchProcessor;
import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.Tenant;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.admin.PatchException;
@@ -43,7 +49,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* Base implementation of the patch. This class ensures that the patch is thread- and transaction-safe.
@@ -61,6 +66,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
*/
public static final String ERR_PROPERTY_NOT_SET = "patch.general.property_not_set";
private static final String MSG_PROGRESS = "patch.progress";
private static final String MSG_DEFERRED = "patch.genericBootstrap.result.deferred";
private static final long RANGE_10 = 1000 * 60 * 90;
private static final long RANGE_5 = 1000 * 60 * 60 * 4;
@@ -75,6 +81,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
private int targetSchema;
private boolean force;
private String description;
private boolean ignored;
/** a list of patches that this one depends on */
private List<Patch> dependsOn;
/** a list of patches that, if already present, mean that this one should be ignored */
@@ -87,6 +94,8 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
/** start time * */
long startTime;
private boolean deferred = false;
// Does the patch require an enclosing transaction?
private boolean requiresTransaction = true;
@@ -114,6 +123,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
this.applyToTenants = true; // by default, apply to each tenant, if tenant service is enabled
this.dependsOn = Collections.emptyList();
this.alternatives = Collections.emptyList();
this.ignored = false;
}
@Override
@@ -126,6 +136,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
.append(", fixesFromSchema=").append(fixesFromSchema)
.append(", fixesToSchema=").append(fixesToSchema)
.append(", targetSchema=").append(targetSchema)
.append(", ignored=").append(ignored)
.append("]");
return sb.toString();
}
@@ -190,7 +201,10 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
{
throw new AlfrescoRuntimeException("Mandatory property not set: patchService");
}
patchService.registerPatch(this);
if(false == isIgnored())
{
patchService.registerPatch(this);
}
}
public String getId()
@@ -315,6 +329,22 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
{
this.description = description;
}
/**
* @return the ignored
*/
public boolean isIgnored()
{
return ignored;
}
/**
* @param ignored the ignored to set
*/
public void setIgnored(boolean ignored)
{
this.ignored = ignored;
}
public List<Patch> getDependsOn()
{
@@ -404,32 +434,122 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
{
// downgrade integrity checking
IntegrityChecker.setWarnInTransaction();
if(logger.isDebugEnabled())
{
logger.debug("call applyInternal for main context id:" + id);
}
String report = applyInternal();
if ((tenantAdminService != null) && tenantAdminService.isEnabled() && applyToTenants)
if ((tenantAdminService != null) && tenantAdminService.isEnabled() && applyToTenants)
{
List<Tenant> tenants = tenantAdminService.getAllTenants();
for (Tenant tenant : tenants)
if(logger.isDebugEnabled())
{
String tenantDomain = tenant.getTenantDomain();
String tenantReport = TenantUtil.runAsSystemTenant(new TenantRunAsWork<String>()
{
public String doWork() throws Exception
{
return applyInternal();
}
}, tenantDomain);
logger.debug("call applyInternal for all tennants");
}
final List<Tenant> tenants = tenantAdminService.getAllTenants();
BatchProcessWorkProvider<Tenant> provider = new BatchProcessWorkProvider<Tenant>()
{
Iterator<Tenant> i = tenants.iterator();
report = report + "\n (also applied to " + tenants.size() + " tenants)";
@Override
public int getTotalEstimatedWorkSize()
{
return tenants.size();
}
@Override
public Collection<Tenant> getNextWork()
{
// return chunks of 10 tenants
ArrayList<Tenant> chunk = new ArrayList<Tenant>(100);
while(i.hasNext() && chunk.size() <= 100)
{
chunk.add(i.next());
}
return chunk;
}
};
BatchProcessor<Tenant> batchProcessor = new BatchProcessor<Tenant>(
"AbstractPatch Processor for " + id,
transactionHelper,
provider, // collection of tenants
10, // worker threads,
100, // batch size 100,
applicationEventPublisher,
logger,
1000);
BatchProcessWorker worker = new BatchProcessWorker<Tenant>()
{
@Override
public String getIdentifier(Tenant entry)
{
return entry.getTenantDomain();
}
@Override
public void beforeProcess() throws Throwable
{
}
@Override
public void process(Tenant entry) throws Throwable
{
String tenantDomain = entry.getTenantDomain();
String tenantReport = AuthenticationUtil.runAs(new RunAsWork<String>()
{
public String doWork() throws Exception
{
return applyInternal();
}
}, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain));
}
@Override
public void afterProcess() throws Throwable
{
}
};
// Now do the work
int numberOfInvocations = batchProcessor.process(worker, true);
return report;
if(logger.isDebugEnabled())
{
logger.debug("batch worker finished processing id:" + id);
}
if(batchProcessor.getTotalErrors() > 0)
{
report = report + "\n" + " and failure during update of tennants total success: " + batchProcessor.getSuccessfullyProcessedEntries() + " number of errors: " +batchProcessor.getTotalErrors() + " lastError" + batchProcessor.getLastError();
}
else
{
report = report + "\n" + " and successful batch update of " + batchProcessor.getTotalResults() + "tennants";
}
}
// done?
return report;
}
/**
* Apply the patch, regardless of the deferred flag. So if the patch has not run due to it being deferred earlier
* then this will run it now. Also ignores the "applied" lock. So the patch can be executed many times.
*
* @return the patch report
* @throws PatchException if the patch failed to be applied
*/
public String applyAsync() throws PatchException
{
return apply(true);
}
/**
* Sets up the transaction and ensures thread-safety.
@@ -438,11 +558,27 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
*/
public synchronized String apply() throws PatchException
{
// ensure that this has not been executed already
if (applied)
{
throw new AlfrescoRuntimeException("The patch has already been executed: \n" + " patch: " + this);
}
return apply(false);
}
private String apply(boolean async)
{
if(!async)
{
// Do we bug out of patch execution
if (deferred)
{
return I18NUtil.getMessage(MSG_DEFERRED);
}
// ensure that this has not been executed already
if (applied)
{
throw new AlfrescoRuntimeException("The patch has already been executed: \n" + " patch: " + this);
}
}
// check properties
checkProperties();
@@ -594,6 +730,21 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher
}
}
}
/**
* Should the patch be deferred? And not run at bootstrap.
* @param deferred
*/
public void setDeferred(boolean deferred) {
this.deferred = deferred;
}
/*
*
*/
public boolean isDeferred() {
return deferred;
}
private int getReportingInterval(long soFar, long toGo)
{

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.admin.patch;
import org.alfresco.service.descriptor.Descriptor;
import org.alfresco.service.descriptor.DescriptorService;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
/**
* @author Andy
*/
public class OptionalPatchApplicationCheckBootstrapBean extends AbstractLifecycleBean
{
PatchService patchService;
Patch patch;
DescriptorService descriptorService;
volatile boolean patchApplied = false;
/**
* @param patchService
* the patchService to set
*/
public void setPatchService(PatchService patchService)
{
this.patchService = patchService;
}
/**
* @param patch
* the patch to set
*/
public void setPatch(Patch patch)
{
this.patch = patch;
}
/**
* @param descriptorService
* the descriptorService to set
*/
public void setDescriptorService(DescriptorService descriptorService)
{
this.descriptorService = descriptorService;
}
/*
* (non-Javadoc)
* @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.
* ApplicationEvent)
*/
@Override
protected void onBootstrap(ApplicationEvent event)
{
Descriptor descriptor = descriptorService.getInstalledRepositoryDescriptor();
if (patch == null)
{
patchApplied = true;
}
else
{
AppliedPatch appliedPatch = patchService.getPatch(patch.getId());
if (appliedPatch == null)
{
patchApplied = patch.getFixesToSchema() < descriptor.getSchema();
}
else
{
patchApplied = appliedPatch.getSucceeded();
}
}
}
/*
* (non-Javadoc)
* @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onShutdown(org.springframework.context.
* ApplicationEvent)
*/
@Override
protected void onShutdown(ApplicationEvent event)
{
}
/**
* Was the patch applied - or was it not applied
*
* @return
*/
public boolean getPatchApplied()
{
return patchApplied;
}
}

View File

@@ -98,4 +98,10 @@ public interface Patch
* @throws PatchException if the patch failed to be applied
*/
public String apply() throws PatchException;
/**
* Is this patch just ignored - never considered for application
* @return
*/
public boolean isIgnored();
}

View File

@@ -66,4 +66,12 @@ public interface PatchService
* @return Returns all applied patches (successful or not)
*/
public List<AppliedPatch> getPatches(Date fromDate, Date toDate);
/**
* Retrieve an existing patch
*
* @param id the patch unique ID
* @return Returns the patch instance or <tt>null</tt> if one has not been persisted
*/
public AppliedPatch getPatch(String id);
}

View File

@@ -587,4 +587,13 @@ public class PatchServiceImpl implements PatchService
return i1.compareTo(i2);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.admin.patch.PatchService#getAppliedPatch(java.lang.String)
*/
@Override
public AppliedPatch getPatch(String id)
{
return appliedPatchDAO.getAppliedPatch(id);
}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.admin.patch;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
/**
* @see org.alfresco.repo.admin.patch.Patch
* @see org.alfresco.repo.admin.patch.AbstractPatch
* @see org.alfresco.repo.admin.patch.PatchService
*
* @author Derek Hulley
*/
public class PatchTest extends TestCase
{
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
private NamespaceService namespaceService;
private NodeService nodeService;
private SearchService searchService;
private AuthenticationContext authenticationContext;
private TenantAdminService tenantAdminService;
private PatchService patchService;
public PatchTest(String name)
{
super(name);
}
public void setUp() throws Exception
{
transactionService = (TransactionService) ctx.getBean("transactionComponent");
namespaceService = (NamespaceService) ctx.getBean("namespaceService");
nodeService = (NodeService) ctx.getBean("nodeService");
searchService = (SearchService) ctx.getBean("searchService");
authenticationContext = (AuthenticationContext) ctx.getBean("authenticationContext");
tenantAdminService = (TenantAdminService) ctx.getBean("tenantAdminService");
patchService = (PatchService) ctx.getBean("PatchService");
// get the patches to play with
patchService.registerPatch((Patch)ctx.getBean("patch.sample.02"));
patchService.registerPatch((Patch)ctx.getBean("patch.sample.01"));
patchService.registerPatch((Patch)ctx.getBean("patch.sample.03"));
}
public void testSetup() throws Exception
{
assertNotNull(transactionService);
assertNotNull(patchService);
}
private SamplePatch constructSamplePatch(boolean mustFail)
{
SamplePatch patch = new SamplePatch(mustFail, transactionService);
patch.setNamespaceService(namespaceService);
patch.setNodeService(nodeService);
patch.setSearchService(searchService);
patch.setAuthenticationContext(authenticationContext);
patch.setTenantAdminService(tenantAdminService);
patch.setApplicationEventPublisher(ctx);
// done
return patch;
}
// private SimplePatch constructSimplePatch(boolean requiresTransaction)
// {
// SimplePatch patch = new SimplePatch(transactionService, requiresTransaction);
// patch.setNamespaceService(namespaceService);
// patch.setNodeService(nodeService);
// patch.setSearchService(searchService);
// patch.setAuthenticationContext(authenticationContext);
// patch.setTenantAdminService(tenantAdminService);
// patch.setApplicationEventPublisher(ctx);
// // done
// return patch;
// }
public void testSimplePatchSuccess() throws Exception
{
Patch patch = constructSamplePatch(false);
String report = patch.apply();
// check that the report was generated
assertEquals("Patch report incorrect", SamplePatch.MSG_SUCCESS, report);
}
public void testPatchReapplication()
{
// successfully apply a patch
Patch patch = constructSamplePatch(false);
patch.apply();
// check that the patch cannot be reapplied
try
{
patch.apply();
fail("AbstractPatch failed to prevent reapplication");
}
catch (AlfrescoRuntimeException e)
{
// expected
}
// apply an unsuccessful patch
patch = constructSamplePatch(true);
try
{
patch.apply();
fail("Failed patch didn't throw PatchException");
}
catch (PatchException e)
{
// expected
}
// repeat
try
{
patch.apply();
fail("Reapplication of failed patch didn't throw PatchException");
}
catch (PatchException e)
{
// expected
}
}
public void testApplyOutstandingPatches() throws Exception
{
// apply outstanding patches
boolean success = patchService.applyOutstandingPatches();
assertTrue(success);
// get applied patches
List<AppliedPatch> appliedPatches = patchService.getPatches(null, null);
// check that the patch application was recorded
boolean found01 = false;
boolean found02 = false;
boolean found03 = false;
for (AppliedPatch appliedPatch : appliedPatches)
{
if (appliedPatch.getId().equals("Sample01"))
{
found01 = true;
assertTrue("Patch info didn't indicate success: " + appliedPatch, appliedPatch.getSucceeded());
}
else if (appliedPatch.getId().equals("Sample02"))
{
found02 = true;
assertTrue("Patch info didn't indicate success: " + appliedPatch, appliedPatch.getSucceeded());
}
else if (appliedPatch.getId().equals("Sample03"))
{
found03 = true;
assertTrue("Patch info didn't indicate success: " + appliedPatch, appliedPatch.getSucceeded());
}
}
assertTrue("Sample 01 not in list of applied patches", found01);
assertTrue("Sample 02 not in list of applied patches", found02);
assertTrue("Sample 03 not in list of applied patches", found03);
}
public void testGetPatchesByDate() throws Exception
{
// ensure that there are some applied patches
testApplyOutstandingPatches();
// get the number of applied patches
List<AppliedPatch> appliedPatches = patchService.getPatches(null, null);
assertTrue("Expected at least 2 applied patches", appliedPatches.size() >= 2);
// now requery using null dates
List<AppliedPatch> appliedPatchesAllDates = patchService.getPatches(null, null);
assertEquals("Applied patches by all dates doesn't match all applied patches",
appliedPatches.size(), appliedPatchesAllDates.size());
// perform another query with dates that should return no results
List<AppliedPatch> appliedPatchesFutureDates = patchService.getPatches(new Date(), new Date());
assertEquals("Query returned results for dates when no patches should exist", 0, appliedPatchesFutureDates.size());
}
}

View File

@@ -0,0 +1,243 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.admin.patch.impl;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.calendar.CalendarModel;
import org.alfresco.service.cmr.calendar.CalendarEntry;
import org.alfresco.service.cmr.calendar.CalendarEntryDTO;
import org.alfresco.service.cmr.calendar.CalendarService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.apache.log4j.Logger;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* This patch adjusts dates for Calendar Events. Share application in 3.4.x versions doesn't adjust specified in form dates with time zone offset value to. Web Script which saves a
* new event always performs correction of the dates in accordance with time zone for 'All Day' events. Date becomes on a day before, if time for date is set to '00:00' in this
* case. Share in 4.x does this adjustment automatically before sending request to the Web Script.<br />
* <br />
* See "<a href="https://issues.alfresco.com/jira/browse/MNT-8977">CMIS (OpenCMIS version) is sharing security context with other functionality in Alfresco</a>" for more details
*
* @since 4.1.5
* @author Dmitry Velichkevich
*/
public class CalendarAllDayEventDatesCorrectingPatch extends AbstractPatch
{
private static final String MSG_SUCCESS = "patch.calendarAllDayEventDatesCorrectingPatch.result";
private static final Logger LOGGER = Logger.getLogger(CalendarAllDayEventDatesCorrectingPatch.class);
private int batchSize = 1000;
private boolean batchEnabled = true;
private SiteService siteService;
private CalendarService calendarService;
public CalendarAllDayEventDatesCorrectingPatch()
{
}
public void setBatchSize(int batchSize)
{
this.batchSize = batchSize;
}
public void setBatchEnabled(boolean batchEnabled)
{
this.batchEnabled = batchEnabled;
}
public void setSiteService(SiteService siteService)
{
this.siteService = siteService;
}
public void setCalendarService(CalendarService calendarService)
{
this.calendarService = calendarService;
}
@Override
protected String applyInternal() throws Exception
{
int updatedEventsAmount = 0;
NodeRef siteRoot = siteService.getSiteRoot();
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Site root: " + siteRoot);
}
List<ChildAssociationRef> allSites = (null != siteRoot) ? (nodeService.getChildAssocs(siteRoot)) : (null);
if ((null != allSites) && !allSites.isEmpty())
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Starting processing of " + allSites.size() + " sites...");
}
PagingResults<CalendarEntry> entries = null;
String queryId = null;
int maxItems = (batchEnabled) ? (batchSize) : (Integer.MAX_VALUE);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Batching info:\n\t- batching enabled: " + batchEnabled + ";\n\t-batch size: " + batchSize);
}
for (ChildAssociationRef siteAssoc : allSites)
{
SiteInfo site = siteService.getSite(siteAssoc.getChildRef());
if (null != site)
{
int skipCount = 0;
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Processing a site: [short name: " + site.getShortName() + ", title: " + site.getTitle() + ", visibility: " + site.getVisibility() + "]");
}
do
{
PagingRequest paging = new PagingRequest(skipCount, maxItems, queryId);
entries = calendarService.listCalendarEntries(site.getShortName(), paging);
List<CalendarEntry> page = (null != entries) ? (entries.getPage()) : (null);
if (null != page)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Processing " + page.size() + " Calendar Events...");
}
queryId = entries.getQueryExecutionId();
for (CalendarEntry entry : page)
{
if (isAllDay(entry))
{
updatedEventsAmount++;
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("'All Day' Calendar event has been detected: [title: " + entry.getTitle() + ", start: " + entry.getStart() + ", end: "
+ entry.getEnd() + ", isOutlook: " + entry.isOutlook() + "]");
}
nodeService.setProperty(entry.getNodeRef(), CalendarModel.PROP_TO_DATE, adjustOldDate(entry.getEnd()));
nodeService.setProperty(entry.getNodeRef(), CalendarModel.PROP_FROM_DATE, adjustOldDate(entry.getStart()));
}
}
skipCount += maxItems;
}
} while (batchEnabled && entries.hasMoreItems());
}
else
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Not site object has been detected. Skipping...");
}
}
}
}
else
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("No one site has been found! Skipping patch execution...");
}
}
return I18NUtil.getMessage(MSG_SUCCESS, updatedEventsAmount);
}
/**
* Increases (or decreases) <code>propertyId</code> date-property by the extracted (from the specified property value) time zone offset
*
* @param oldDate - {@link Date} instance, which represents not adjusted date for an 'All Day' event
* @return {@link Date} instance, which represents adjusted date-property in accordance with time zone offset
*/
private Date adjustOldDate(Date oldDate)
{
Calendar result = Calendar.getInstance();
result.setTime(oldDate);
int offset = result.getTimeZone().getOffset(result.getTimeInMillis());
result.add(Calendar.MILLISECOND, offset);
return result.getTime();
}
/**
* Does the given {@link CalendarEntry} define an all-day
* event?
* An All Day Event is defined as one starting at midnight
* on a day, and ending at midnight.
*
* For a single day event, the start and end dates should be
* the same, and the times for both are UTC midnight.
* For a multi day event, the start and end times are UTC midnight,
* for the first and last days respectively.
*/
public static boolean isAllDay(CalendarEntry entry)
{
if (entry.getStart() == null || entry.getEnd() == null)
{
// One or both dates is missing
return false;
}
// Pre-4.0, the midnights were local time...
Calendar startLocal = Calendar.getInstance();
Calendar endLocal = Calendar.getInstance();
startLocal.setTime(entry.getStart());
endLocal.setTime(entry.getEnd());
if (startLocal.get(Calendar.HOUR_OF_DAY) == 0 &&
startLocal.get(Calendar.MINUTE) == 0 &&
startLocal.get(Calendar.SECOND) == 0 &&
endLocal.get(Calendar.HOUR_OF_DAY) == 0 &&
endLocal.get(Calendar.MINUTE) == 0 &&
endLocal.get(Calendar.SECOND) == 0)
{
// Both at midnight, counts as all day
return true;
}
// In any other case, it isn't an all-day
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,10 +18,18 @@
*/
package org.alfresco.repo.admin.patch.impl;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.admin.patch.PatchExecuter;
import org.alfresco.repo.batch.BatchProcessWorkProvider;
import org.alfresco.repo.batch.BatchProcessor;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.admin.PatchException;
@@ -31,6 +39,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
@@ -38,8 +47,8 @@ import org.springframework.extensions.surf.util.I18NUtil;
/**
* Patch that updates workflow package type and package items associations
*
* @see https://issues.alfresco.com/jira/browse/ETHREEOH-3613
* @see https://issues.alfresco.com/jira/browse/ALF-11499
* @see <a href=https://issues.alfresco.com/jira/browse/ETHREEOH-3613>ETHREEOH-3613</a>
* @see <a href=https://issues.alfresco.com/jira/browse/ALF-11499>ALF-11499</a>
* @author Arseny Kovalchuk
* @since 3.4.7
*/
@@ -51,9 +60,29 @@ public class FixBpmPackagesPatch extends AbstractPatch
private static final String ERR_MSG_EMPTY_CONTAINER = "patch.fixBpmPackages.emptyContainer";
private static final Log logger = LogFactory.getLog(FixBpmPackagesPatch.class);
private static Log progress_logger = LogFactory.getLog(PatchExecuter.class);
private int batchThreads = 4;
private int batchSize = 1000;
private ImporterBootstrap importerBootstrap;
/**
* @param batchThreads the number of threads that will write child association changes
*/
public void setBatchThreads(int batchThreads)
{
this.batchThreads = batchThreads;
}
/**
* @param batchSize the number of child associations that will be modified per transaction
*/
public void setBatchSize(int batchSize)
{
this.batchSize = batchSize;
}
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
{
this.importerBootstrap = importerBootstrap;
@@ -62,122 +91,246 @@ public class FixBpmPackagesPatch extends AbstractPatch
@Override
protected String applyInternal() throws Exception
{
// Package counter for report
int packagesCount = 0;
StoreRef store = importerBootstrap.getStoreRef();
if (store == null)
FixBpmPackagesPatchHelper helper = new FixBpmPackagesPatchHelper();
try
{
throw new PatchException(ERR_MSG_INVALID_BOOTSTRAP_STORE);
}
// Get root node for store
NodeRef rootRef = nodeService.getRootNode(store);
if (logger.isDebugEnabled())
logger.debug("StoreRef:" + store + " RootNodeRef: " + rootRef);
// Get /sys:system container path, if it doesn't exist there is something wrong with the repo
String sysContainer = importerBootstrap.getConfiguration().getProperty("system.system_container.childname");
QName sysContainerQName = QName.createQName(sysContainer, namespaceService);
List<ChildAssociationRef> refs = nodeService.getChildAssocs(rootRef, ContentModel.ASSOC_CHILDREN, sysContainerQName);
if (refs == null || refs.size() == 0)
throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysContainer);
NodeRef sysNodeRef = refs.get(0).getChildRef();
// Get /sys:system/sys:workflow container, if it doesn't exist there is something wrong with the repo
String sysWorkflowContainer = importerBootstrap.getConfiguration().getProperty("system.workflow_container.childname");
QName sysWorkflowQName = QName.createQName(sysWorkflowContainer, namespaceService);
refs = nodeService.getChildAssocs(sysNodeRef, ContentModel.ASSOC_CHILDREN, sysWorkflowQName);
if (refs == null || refs.size() == 0)
throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysWorkflowContainer);
NodeRef workflowContainerRef = refs.get(0).getChildRef();
// Try to get /sys:system/sys:workflow/cm:packages, if there is no such node, then it wasn't created yet,
// so there is nothing to convert
refs = nodeService.getChildAssocs(workflowContainerRef, ContentModel.ASSOC_CHILDREN, RegexQNamePattern.MATCH_ALL);
if (refs == null || refs.size() == 0)
{
if (logger.isDebugEnabled())
logger.debug("There are no any packages in the container " + sysWorkflowContainer);
return I18NUtil.getMessage(MSG_SUCCESS, packagesCount);
}
// Get /sys:system/sys:workflow/cm:packages container NodeRef
NodeRef packagesContainerRef = refs.get(0).getChildRef();
// Get workflow packages to be converted
refs = nodeService.getChildAssocs(packagesContainerRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
if (logger.isDebugEnabled())
logger.debug("Found " + refs.size() + " packages to convert");
NodeRef packageRef = null;
// For each package we get package items and convert their type from cm:systemfolder to bpm:package
// Also we convert associations between packages and their items from cm:contains to bpm:packageContains
for (ChildAssociationRef assocRef : refs)
{
packageRef = assocRef.getChildRef();
QName typeQname = nodeService.getType(packageRef);
String name = (String) nodeService.getProperty(packageRef, ContentModel.PROP_NAME);
if (logger.isDebugEnabled())
logger.debug("Package " + name + " type " + typeQname);
if (!nodeService.getType(packageRef).equals(WorkflowModel.TYPE_PACKAGE))
StoreRef store = importerBootstrap.getStoreRef();
if (store == null)
{
// New type of the package is bpm:package
nodeService.setType(packageRef, WorkflowModel.TYPE_PACKAGE);
throw new PatchException(ERR_MSG_INVALID_BOOTSTRAP_STORE);
}
// Get all package items
List<ChildAssociationRef> packageItemsAssocs = nodeService.getChildAssocs(packageRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef itemAssoc : packageItemsAssocs)
// Get root node for store
NodeRef rootRef = nodeService.getRootNode(store);
if (logger.isDebugEnabled())
logger.debug("StoreRef:" + store + " RootNodeRef: " + rootRef);
// Get /sys:system container path, if it doesn't exist there is something wrong with the repo
String sysContainer = importerBootstrap.getConfiguration().getProperty("system.system_container.childname");
QName sysContainerQName = QName.createQName(sysContainer, namespaceService);
List<ChildAssociationRef> refs = nodeService.getChildAssocs(rootRef, ContentModel.ASSOC_CHILDREN, sysContainerQName);
if (refs == null || refs.size() == 0)
throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysContainer);
NodeRef sysNodeRef = refs.get(0).getChildRef();
// Get /sys:system/sys:workflow container, if it doesn't exist there is something wrong with the repo
String sysWorkflowContainer = importerBootstrap.getConfiguration().getProperty("system.workflow_container.childname");
QName sysWorkflowQName = QName.createQName(sysWorkflowContainer, namespaceService);
refs = nodeService.getChildAssocs(sysNodeRef, ContentModel.ASSOC_CHILDREN, sysWorkflowQName);
if (refs == null || refs.size() == 0)
throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysWorkflowContainer);
NodeRef workflowContainerRef = refs.get(0).getChildRef();
// Try to get /sys:system/sys:workflow/cm:packages, if there is no such node, then it wasn't created yet,
// so there is nothing to convert
refs = nodeService.getChildAssocs(workflowContainerRef, ContentModel.ASSOC_CHILDREN, RegexQNamePattern.MATCH_ALL);
if (refs == null || refs.size() == 0)
{
NodeRef parentRef = itemAssoc.getParentRef();
NodeRef childRef = itemAssoc.getChildRef();
String itemName = (String) nodeService.getProperty(childRef, ContentModel.PROP_NAME);
// To avoid unnecessary deletion of the child item, we check if the association is not primary
// For the package item it should be not primary association.
if (itemAssoc.isPrimary())
{
logger.error("Association between package: " + name + " and item: " + itemName + " is primary association, so removing this assiciation will result in child node deletion");
continue;
}
if (itemAssoc.getTypeQName().equals(WorkflowModel.ASSOC_PACKAGE_CONTAINS))
{
continue;
}
boolean assocRemoved = nodeService.removeChildAssociation(itemAssoc);
if (assocRemoved)
{
if (logger.isDebugEnabled())
logger.debug("Association between package: " + name + " and item: " + itemName + " was removed");
}
else
{
if (logger.isErrorEnabled())
logger.error("Association between package: " + name + " and item: " + itemName + " doesn't exist");
// If there is no association we won't create a new one
continue;
}
// Recreate new association between package and particular item as bpm:packageContains
/* ChildAssociationRef newChildAssoc = */nodeService.addChild(parentRef, childRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(itemName)));
if (logger.isDebugEnabled())
{
logger.debug("New association has been created between package: " + name + " and item: " + itemName);
}
logger.debug("There are no any packages in the container " + sysWorkflowContainer);
return I18NUtil.getMessage(MSG_SUCCESS, 0);
}
packagesCount++;
// Get /sys:system/sys:workflow/cm:packages container NodeRef
NodeRef packagesContainerRef = refs.get(0).getChildRef();
// Get workflow packages to be converted
refs = nodeService.getChildAssocs(packagesContainerRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
if (logger.isDebugEnabled())
logger.debug("Found " + refs.size() + " packages to convert");
return helper.fixBpmPackages(refs);
}
finally
{
helper.closeWriter();
}
return I18NUtil.getMessage(MSG_SUCCESS, packagesCount);
}
private class FixBpmPackagesPatchHelper
{
private File logFile;
private FileChannel channel;
private Integer assocCount;
private int skipCount = 0;
private List<ChildAssociationRef> refs;
private FixBpmPackagesPatchHelper() throws IOException
{
// put the log file into a long life temp directory
File tempDir = TempFileProvider.getLongLifeTempDir("patches");
logFile = new File(tempDir, "FixBpmPackagesPatch.log");
// open the file for appending
RandomAccessFile outputFile = new RandomAccessFile(logFile, "rw");
channel = outputFile.getChannel();
// move to the end of the file
channel.position(channel.size());
// add a newline and it's ready
writeLine("").writeLine("");
writeLine("FixBpmPackagesPatch executing on " + new Date());
}
private FixBpmPackagesPatchHelper write(Object obj) throws IOException
{
channel.write(ByteBuffer.wrap(obj.toString().getBytes("UTF-8")));
return this;
}
private FixBpmPackagesPatchHelper writeLine(Object obj) throws IOException
{
write(obj);
write("\n");
return this;
}
private void closeWriter()
{
try
{
channel.close();
}
catch (IOException ioe)
{
// Nothing we can do
}
}
public String fixBpmPackages(List<ChildAssociationRef> references) throws Exception
{
this.refs = references;
this.assocCount = references.size();
BatchProcessWorkProvider<ChildAssociationRef> workProvider = new BatchProcessWorkProvider<ChildAssociationRef>()
{
@Override
public synchronized int getTotalEstimatedWorkSize()
{
return assocCount;
}
@Override
public synchronized Collection<ChildAssociationRef> getNextWork()
{
int nextMaxSize = skipCount + batchSize;
List<ChildAssociationRef> result;
if (assocCount < skipCount)
{
// we are finished, return empty list
result = Collections.emptyList();
}
else if (assocCount >= nextMaxSize)
{
// more jobs are available with full batch
result = refs.subList(skipCount, nextMaxSize);
}
else
{
// there are less items than batch size
result = refs.subList(skipCount, assocCount);
}
// increment the counter of batches
skipCount += batchSize;
return result;
}
};
// get the association types to check
BatchProcessor<ChildAssociationRef> batchProcessor = new BatchProcessor<ChildAssociationRef>(
"FixBpmPackagesPatch",
transactionHelper,
workProvider,
batchThreads, batchSize,
applicationEventPublisher,
progress_logger, 1000);
BatchProcessor.BatchProcessWorker<ChildAssociationRef> worker = new BatchProcessor.BatchProcessWorker<ChildAssociationRef>()
{
@Override
public String getIdentifier(ChildAssociationRef entry)
{
return entry.toString();
}
@Override
public void beforeProcess() throws Throwable
{
}
@Override
public void process(ChildAssociationRef assocRef) throws Throwable
{
NodeRef packageRef = assocRef.getChildRef();
QName typeQname = nodeService.getType(packageRef);
String name = (String) nodeService.getProperty(packageRef, ContentModel.PROP_NAME);
if (logger.isDebugEnabled())
logger.debug("Package " + name + " type " + typeQname);
if (!nodeService.getType(packageRef).equals(WorkflowModel.TYPE_PACKAGE))
{
// New type of the package is bpm:package
nodeService.setType(packageRef, WorkflowModel.TYPE_PACKAGE);
}
// Get all package items
List<ChildAssociationRef> packageItemsAssocs = nodeService.getChildAssocs(packageRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef itemAssoc : packageItemsAssocs)
{
NodeRef parentRef = itemAssoc.getParentRef();
NodeRef childRef = itemAssoc.getChildRef();
String itemName = (String) nodeService.getProperty(childRef, ContentModel.PROP_NAME);
// To avoid unnecessary deletion of the child item, we check if the association is not primary
// For the package item it should be not primary association.
if (itemAssoc.isPrimary())
{
logger.error("Association between package: " + name + " and item: " + itemName + " is primary association, so removing this assiciation will result in child node deletion");
continue;
}
if (itemAssoc.getTypeQName().equals(WorkflowModel.ASSOC_PACKAGE_CONTAINS))
{
continue;
}
boolean assocRemoved = nodeService.removeChildAssociation(itemAssoc);
if (assocRemoved)
{
if (logger.isDebugEnabled())
logger.debug("Association between package: " + name + " and item: " + itemName + " was removed");
}
else
{
if (logger.isErrorEnabled())
logger.error("Association between package: " + name + " and item: " + itemName + " doesn't exist");
// If there is no association we won't create a new one
continue;
}
// Recreate new association between package and particular item as bpm:packageContains
/* ChildAssociationRef newChildAssoc = */nodeService.addChild(parentRef, childRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(itemName)));
if (logger.isDebugEnabled())
{
logger.debug("New association has been created between package: " + name + " and item: " + itemName);
}
}
}
@Override
public void afterProcess() throws Throwable
{
}
};
int updated = batchProcessor.process(worker, true);
return I18NUtil.getMessage(MSG_SUCCESS, updated, logFile);
}
}
}

View File

@@ -41,13 +41,14 @@ import org.alfresco.service.cmr.repository.StoreRef;
*/
public class GenericBootstrapPatch extends AbstractPatch
{
private static final String MSG_EXISTS = "patch.genericBootstrap.result.exists";
private static final String MSG_CREATED = "patch.genericBootstrap.result.created";
private static final String ERR_MULTIPLE_FOUND = "patch.genericBootstrap.err.multiple_found";
protected static final String MSG_EXISTS = "patch.genericBootstrap.result.exists";
protected static final String MSG_CREATED = "patch.genericBootstrap.result.created";
protected static final String MSG_DEFERRED = "patch.genericBootstrap.result.deferred";
protected static final String ERR_MULTIPLE_FOUND = "patch.genericBootstrap.err.multiple_found";
private ImporterBootstrap importerBootstrap;
private String checkPath;
private Properties bootstrapView;
protected ImporterBootstrap importerBootstrap;
protected String checkPath;
protected Properties bootstrapView;
/**
* @param importerBootstrap the bootstrap bean that performs the user store bootstrap
@@ -87,6 +88,7 @@ public class GenericBootstrapPatch extends AbstractPatch
super.checkProperties();
}
@Override
protected String applyInternal() throws Exception
{
@@ -122,4 +124,6 @@ public class GenericBootstrapPatch extends AbstractPatch
// done
return I18NUtil.getMessage(MSG_CREATED, path, rootNodeRef);
}
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright (C) 2013-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.admin.patch.impl;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* The SharedFolderPatch is a Generic Bootstrap Patch with the extra ability to
* rename an existing folder that is in the way (in a different namespace).
* <p>
* The first use-case is when there is a child called cm:shared and we want to patch a folder with app:shared
*
* @author mrogers
*
*/
public class SharedFolderPatch extends GenericBootstrapPatch
{
private JobLockService jobLockService;
private long LOCK_TIME_TO_LIVE=10000;
private long LOCK_REFRESH_TIME=5000;
private String renamePath;
private Log logger = LogFactory.getLog(SharedFolderPatch.class);
private static final String MSG_RENAMED = "patch.sharedFolder.result.renamed";
/**
* Run the Shared Folder Patch asynchronously after bootstrap.
*/
public void executeAsync()
{
// Lock the push
QName lockQName = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "patch.sharedFolder");
String lockToken = jobLockService.getLock(lockQName, LOCK_TIME_TO_LIVE, 0, 1);
SharedFolderPatchCallback callback = new SharedFolderPatchCallback();
jobLockService.refreshLock(lockToken, lockQName, LOCK_REFRESH_TIME, callback);
try
{
if (logger.isDebugEnabled())
{
logger.debug("SharedFolderPatch: job lock held");
}
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
applyAsync();
return null;
}
});
}
finally
{
if (logger.isTraceEnabled())
{
logger.trace("PUSH: job finished");
}
// Release the locks on the job and stop refreshing
callback.isActive = false;
jobLockService.releaseLock(lockToken, lockQName);
}
}
@Override
protected String applyInternal() throws Exception
{
StoreRef storeRef = importerBootstrap.getStoreRef();
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
if (getRenamePath() != null)
{
List<NodeRef> results = searchService.selectNodes(
rootNodeRef,
getRenamePath(),
null,
namespaceService,
false);
if (results.size() > 1)
{
throw new PatchException(ERR_MULTIPLE_FOUND, renamePath);
}
else if (results.size() == 1)
{
if(logger.isDebugEnabled())
{
logger.debug("There is an existing node in the way path:" + getRenamePath());
}
// A node already exists that we must rename.
NodeRef existingNodeRef = results.get(0);
// get the path of the parent node e.g. company_home
LinkedList<String> folderElements = new LinkedList<String>(Arrays.asList(getRenamePath().split("/")));
folderElements.removeLast();
StringBuffer parentPath = new StringBuffer();
for(String folder : folderElements)
{
parentPath.append("/");
parentPath.append(folder);
}
List<NodeRef> parentResults = searchService.selectNodes(
rootNodeRef,
parentPath.toString(),
null,
namespaceService,
false);
if(parentResults.size()==1)
{
NodeRef parentNodeRef = parentResults.get(0);
if(logger.isDebugEnabled())
{
logger.debug("Found the parent node - doing a move parentNodeRef:" + parentNodeRef);
}
// rename the existing node
nodeService.moveNode(existingNodeRef, parentNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName( NamespaceService.APP_MODEL_1_0_URI, "shared"));
return I18NUtil.getMessage(MSG_RENAMED, renamePath);
}
else
{
// Something has gone horribly wrong if we get here - we have multiple parents, or none despite finding the node earlier
throw new PatchException(ERR_MULTIPLE_FOUND, parentPath.toString());
}
}
}
// Else run the normal GenericBootstrapPatch implementation
if(logger.isDebugEnabled())
{
logger.debug("Node does not already exist, Running the Generic Bootstrap Patch");
}
return super.applyInternal();
}
public void setRenamePath(String renamePath) {
this.renamePath = renamePath;
}
public String getRenamePath() {
return renamePath;
}
public void setJobLockService(JobLockService jobLockService) {
this.jobLockService = jobLockService;
}
public JobLockService getJobLockService() {
return jobLockService;
}
/**
* Job to initiate the {@link SharedFolderPatch} if it has been deferred
*
* @author Mark Rogers
* @since 4.2
*/
public static class SharedFolderPatchJob implements Job
{
public SharedFolderPatchJob()
{
}
/**
* Calls the cleaner to do its work
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
// extract the content cleaner to use
Object sharedFolderPatchObj = jobData.get("sharedFolderPatch");
if (sharedFolderPatchObj == null || !(sharedFolderPatchObj instanceof SharedFolderPatch))
{
throw new AlfrescoRuntimeException(
"'sharedFolderPatch' data must contain valid 'SharedFolderPatch' reference");
}
// Job Lock Here - should probably move into the patch service at some time.
SharedFolderPatch sharedFolderPatch = (SharedFolderPatch) sharedFolderPatchObj;
sharedFolderPatch.executeAsync();
}
}
private class SharedFolderPatchCallback implements JobLockRefreshCallback
{
public boolean isActive = true;
@Override
public boolean isActive()
{
return isActive;
}
@Override
public void lockReleased()
{
if (logger.isTraceEnabled())
{
logger.trace("lock released");
}
}
};
}