Merged V2.2 to HEAD

8397: Fix for AR-2003, AR-2127, AR-2128 (Hibernate patch)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8506 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2008-03-11 12:33:25 +00:00
parent dc9c207c88
commit fcf5487c5a
22 changed files with 1969 additions and 242 deletions

View File

@@ -27,9 +27,11 @@ package org.alfresco.repo.admin.patch;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -46,8 +48,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Base implementation of the patch. This class ensures that the patch is
* thread- and transaction-safe.
* Base implementation of the patch. This class ensures that the patch is thread- and transaction-safe.
*
* @author Derek Hulley
*/
@@ -56,14 +57,20 @@ public abstract class AbstractPatch implements Patch
/**
* I18N message when properties not set.
* <ul>
* <li>{0} = property name</li>
* <li>{1} = patch instance</li>
* <li>{0} = property name</li>
* <li>{1} = patch instance</li>
* </ul>
*/
public static final String ERR_PROPERTY_NOT_SET = "patch.general.property_not_set";
private static final String MSG_PROGRESS = "patch.progress";
private static final long RANGE_10 = 1000 * 60 * 90;
private static final long RANGE_5 = 1000 * 60 * 60 * 4;
private static final long RANGE_2 = 1000 * 60 * 90 * 10;
private static Log logger = LogFactory.getLog(AbstractPatch.class);
private static Log progress_logger = LogFactory.getLog(PatchExecuter.class);
private String id;
private int fixesFromSchema;
private int fixesToSchema;
@@ -73,7 +80,8 @@ public abstract class AbstractPatch implements Patch
private List<Patch> dependsOn;
/** flag indicating if the patch was successfully applied */
private boolean applied;
private boolean applyToTenants = true; // by default, apply to each tenant, if tenant service is enabled
private boolean applyToTenants;
/** the service to register ourselves with */
private PatchService patchService;
/** used to ensure a unique transaction per execution */
@@ -90,15 +98,22 @@ public abstract class AbstractPatch implements Patch
protected TenantDeployerService tenantDeployerService;
/** track completion * */
int percentComplete = 0;
/** start time * */
long startTime;
public AbstractPatch()
{
this.fixesFromSchema = -1;
this.fixesToSchema = -1;
this.targetSchema = -1;
this.applied = false;
this.applyToTenants = true; // by default, apply to each tenant, if tenant service is enabled
this.dependsOn = Collections.emptyList();
}
@Override
public String toString()
{
@@ -136,7 +151,7 @@ public abstract class AbstractPatch implements Patch
{
this.namespaceService = namespaceService;
}
/**
* Set a generally-used service
*/
@@ -167,7 +182,7 @@ public abstract class AbstractPatch implements Patch
}
/**
* This ensures that this bean gets registered with the appropriate {@link PatchService service}.
* This ensures that this bean gets registered with the appropriate {@link PatchService service}.
*/
public void init()
{
@@ -184,8 +199,8 @@ public abstract class AbstractPatch implements Patch
}
/**
*
* @param id the unique ID of the patch. This dictates the order in which patches are applied.
* @param id
* the unique ID of the patch. This dictates the order in which patches are applied.
*/
public void setId(String id)
{
@@ -200,7 +215,8 @@ public abstract class AbstractPatch implements Patch
/**
* Set the smallest schema number that this patch may be applied to.
*
* @param version a schema number not smaller than 0
* @param version
* a schema number not smaller than 0
*/
public void setFixesFromSchema(int version)
{
@@ -224,8 +240,8 @@ public abstract class AbstractPatch implements Patch
/**
* Set the largest schema version number that this patch may be applied to.
*
* @param version a schema version number not smaller than the
* {@link #setFixesFromSchema(int) from version} number.
* @param version
* a schema version number not smaller than the {@link #setFixesFromSchema(int) from version} number.
*/
public void setFixesToSchema(int version)
{
@@ -242,12 +258,11 @@ public abstract class AbstractPatch implements Patch
}
/**
* Set the schema version that this patch attempts to take the existing schema to.
* This is for informational purposes only, acting as an indicator of intention rather
* than having any specific effect.
* Set the schema version that this patch attempts to take the existing schema to. This is for informational
* purposes only, acting as an indicator of intention rather than having any specific effect.
*
* @param version a schema version number that must be greater than the
* {@link #fixesToSchema max fix schema number}
* @param version
* a schema version number that must be greater than the {@link #fixesToSchema max fix schema number}
*/
public void setTargetSchema(int version)
{
@@ -264,7 +279,8 @@ public abstract class AbstractPatch implements Patch
}
/**
* @param description a thorough description of the patch
* @param description
* a thorough description of the patch
*/
public void setDescription(String description)
{
@@ -275,12 +291,12 @@ public abstract class AbstractPatch implements Patch
{
return this.dependsOn;
}
/**
* Set all the dependencies for this patch. It should not be executed
* before all the dependencies have been applied.
* Set all the dependencies for this patch. It should not be executed before all the dependencies have been applied.
*
* @param dependsOn a list of dependencies
* @param dependsOn
* a list of dependencies
*/
public void setDependsOn(List<Patch> dependsOn)
{
@@ -291,12 +307,14 @@ public abstract class AbstractPatch implements Patch
{
return ((this.fixesFromSchema <= version) && (version <= fixesToSchema));
}
/**
* Performs a null check on the supplied value.
*
* @param value value to check
* @param name name of the property to report
* @param value
* value to check
* @param name
* name of the property to report
*/
protected final void checkPropertyNotNull(Object value, String name)
{
@@ -312,9 +330,8 @@ public abstract class AbstractPatch implements Patch
}
/**
* Check that the schema version properties have been set appropriately.
* Derived classes can override this method to perform their own validation provided
* that this method is called by the derived class.
* Check that the schema version properties have been set appropriately. Derived classes can override this method to
* perform their own validation provided that this method is called by the derived class.
*/
protected void checkProperties()
{
@@ -328,12 +345,11 @@ public abstract class AbstractPatch implements Patch
checkPropertyNotNull(authenticationComponent, "authenticationComponent");
if (fixesFromSchema == -1 || fixesToSchema == -1 || targetSchema == -1)
{
throw new AlfrescoRuntimeException(
"Patch properties 'fixesFromSchema', 'fixesToSchema' and 'targetSchema' have not all been set on this patch: \n" +
" patch: " + this);
throw new AlfrescoRuntimeException("Patch properties 'fixesFromSchema', 'fixesToSchema' and 'targetSchema' have not all been set on this patch: \n"
+ " patch: " + this);
}
}
/**
* Sets up the transaction and ensures thread-safety.
*
@@ -344,8 +360,7 @@ public abstract class AbstractPatch implements Patch
// ensure that this has not been executed already
if (applied)
{
throw new AlfrescoRuntimeException("The patch has already been executed: \n" +
" patch: " + this);
throw new AlfrescoRuntimeException("The patch has already been executed: \n" + " patch: " + this);
}
// check properties
checkProperties();
@@ -354,9 +369,7 @@ public abstract class AbstractPatch implements Patch
{
if (logger.isDebugEnabled())
{
logger.debug("\n" +
"Patch will be applied: \n" +
" patch: " + this);
logger.debug("\n" + "Patch will be applied: \n" + " patch: " + this);
}
AuthenticationUtil.RunAsWork<String> authorisedPathWork = new AuthenticationUtil.RunAsWork<String>()
{
@@ -399,16 +412,14 @@ public abstract class AbstractPatch implements Patch
return transactionService.getRetryingTransactionHelper().doInTransaction(patchWork);
}
};
startTime = System.currentTimeMillis();
String report = AuthenticationUtil.runAs(authorisedPathWork, AuthenticationUtil.getSystemUserName());
// the patch was successfully applied
applied = true;
// done
if (logger.isDebugEnabled())
{
logger.debug("\n" +
"Patch successfully applied: \n" +
" patch: " + this + "\n" +
" report: " + report);
logger.debug("\n" + "Patch successfully applied: \n" + " patch: " + this + "\n" + " report: " + report);
}
return report;
}
@@ -431,11 +442,12 @@ public abstract class AbstractPatch implements Patch
throw new PatchException(report);
}
}
/**
* Dumps the error's full message and trace to the String
*
* @param e the throwable
* @param e
* the throwable
* @return Returns a String representative of the printStackTrace method
*/
private String makeReport(Throwable e)
@@ -454,13 +466,91 @@ public abstract class AbstractPatch implements Patch
}
/**
* This method does the work. All transactions and thread-safety will be taken care of by this class.
* Any exception will result in the transaction being rolled back. Integrity checks are downgraded
* for the duration of the transaction.
* This method does the work. All transactions and thread-safety will be taken care of by this class. Any exception
* will result in the transaction being rolled back. Integrity checks are downgraded for the duration of the
* transaction.
*
* @return Returns the report (only success messages).
* @see #apply()
* @throws Exception anything can be thrown. This must be used for all failures.
* @throws Exception
* anything can be thrown. This must be used for all failures.
*/
protected abstract String applyInternal() throws Exception;
/**
* Support to report patch completion and estimated completion time.
*
* @param estimatedTotal
* @param currentInteration
*/
protected void reportProgress(long estimatedTotal, long currentInteration)
{
if (progress_logger.isDebugEnabled())
{
progress_logger.debug(currentInteration + "/" + estimatedTotal);
}
if (currentInteration == 0)
{
// No point reporting the start - we have already done that elsewhere ....
percentComplete = 0;
}
else if (currentInteration * 100l / estimatedTotal > percentComplete)
{
int previous = percentComplete;
percentComplete = (int) (currentInteration * 100l / estimatedTotal);
if (percentComplete < 100)
{
// conditional report
long currentTime = System.currentTimeMillis();
long timeSoFar = currentTime - startTime;
long timeRemaining = timeSoFar * (100 - percentComplete) / percentComplete;
int report = -1;
if (timeRemaining > 60000)
{
int reportInterval = getreportingInterval(timeSoFar, timeRemaining);
for (int i = previous + 1; i <= percentComplete; i++)
{
if (i % reportInterval == 0)
{
report = i;
}
}
if (report > 0)
{
Date end = new Date(currentTime + timeRemaining);
String msg = I18NUtil.getMessage(MSG_PROGRESS, report, end);
progress_logger.info(msg);
}
}
}
}
}
private int getreportingInterval(long soFar, long toGo)
{
long total = soFar + toGo;
if (total < RANGE_10)
{
return 10;
}
else if (total < RANGE_5)
{
return 5;
}
else if (total < RANGE_2)
{
return 2;
}
else
{
return 1;
}
}
}

View File

@@ -29,7 +29,10 @@ import java.util.Map;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.domain.AccessControlListDAO;
import org.alfresco.repo.domain.hibernate.AclDaoComponentImpl;
import org.alfresco.repo.security.permissions.ACLType;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
/**
* Migrate permissions from the OLD format to defining, shared and layered
@@ -40,12 +43,23 @@ public class AVMPermissionsPatch extends AbstractPatch
private static final String MSG_SUCCESS = "patch.updateAvmPermissions.result";
private AccessControlListDAO accessControlListDao;
private AclDaoComponentImpl aclDaoComponent;
@Override
protected String applyInternal() throws Exception
{
Long toDo = aclDaoComponent.getAVMHeadNodeCount();
Long maxId = aclDaoComponent.getMaxAclId();
Thread progressThread = new Thread(new ProgressWatcher(toDo, maxId), "WCMPactchProgressWatcher");
progressThread.start();
Map<ACLType, Integer> summary = accessControlListDao.patchAcls();
progressThread.interrupt();
progressThread.join();
// build the result message
String msg = I18NUtil.getMessage(MSG_SUCCESS, summary.get(ACLType.DEFINING), summary.get(ACLType.LAYERED));
// done
@@ -57,4 +71,57 @@ public class AVMPermissionsPatch extends AbstractPatch
this.accessControlListDao = accessControlListDao;
}
public void setAclDaoComponent(AclDaoComponentImpl aclDaoComponent)
{
this.aclDaoComponent = aclDaoComponent;
}
private class ProgressWatcher implements Runnable
{
private boolean running = true;
Long toDo;
Long max;
ProgressWatcher(Long toDo, Long max)
{
this.toDo = toDo;
this.max = max;
}
public void run()
{
while (running)
{
try
{
Thread.sleep(60000);
}
catch (InterruptedException e)
{
running = false;
}
if (running)
{
RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper();
txHelper.setMaxRetries(1);
Long done = txHelper.doInTransaction(new RetryingTransactionCallback<Long>()
{
public Long execute() throws Throwable
{
return aclDaoComponent.getAVMNodeCountWithNewACLS(max);
}
}, true, true);
reportProgress(toDo, done);
}
}
}
}
}

View File

@@ -32,7 +32,10 @@ import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.avm.AVMRepository;
import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.repo.domain.hibernate.AclDaoComponentImpl;
import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avm.AVMStoreDescriptor;
@@ -43,6 +46,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
/**
* Remove ACLs on all but staging area stores On staging area stores, set ACls according to the users and roles as set
@@ -60,6 +64,8 @@ public class WCMPermissionPatch extends AbstractPatch
PermissionService permissionService;
AclDaoComponentImpl aclDaoComponent;
public void setAvmService(AVMService avmService)
{
this.avmService = avmService;
@@ -75,9 +81,20 @@ public class WCMPermissionPatch extends AbstractPatch
this.permissionService = permissionService;
}
public void setAclDaoComponent(AclDaoComponentImpl aclDaoComponent)
{
this.aclDaoComponent = aclDaoComponent;
}
@Override
protected String applyInternal() throws Exception
{
Long toDo = aclDaoComponent.getAVMHeadNodeCount();
Long maxId = aclDaoComponent.getMaxAclId();
Thread progressThread = new Thread(new ProgressWatcher(toDo, maxId), "WCMPactchProgressWatcher");
progressThread.start();
List<AVMStoreDescriptor> stores = avmService.getStores();
for (AVMStoreDescriptor store : stores)
{
@@ -113,6 +130,9 @@ public class WCMPermissionPatch extends AbstractPatch
}
}
progressThread.interrupt();
progressThread.join();
// build the result message
String msg = I18NUtil.getMessage(MSG_SUCCESS);
// done
@@ -178,7 +198,7 @@ public class WCMPermissionPatch extends AbstractPatch
PropertyValue pValue = avmService.getStoreProperty(stagingAreaName, propQName);
permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ, true);
if (pValue != null)
{
NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF);
@@ -213,7 +233,7 @@ public class WCMPermissionPatch extends AbstractPatch
int end = name.indexOf("--", start + 1);
if (end == -1)
{
return name.substring(start+2);
return name.substring(start + 2);
}
return name.substring(start + 2, end);
}
@@ -228,4 +248,50 @@ public class WCMPermissionPatch extends AbstractPatch
return name.substring(0, index);
}
private class ProgressWatcher implements Runnable
{
private boolean running = true;
Long toDo;
Long max;
ProgressWatcher(Long toDo, Long max)
{
this.toDo = toDo;
this.max = max;
}
public void run()
{
while (running)
{
try
{
Thread.sleep(60000);
}
catch (InterruptedException e)
{
running = false;
}
if (running)
{
RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper();
txHelper.setMaxRetries(1);
Long done = txHelper.doInTransaction(new RetryingTransactionCallback<Long>()
{
public Long execute() throws Throwable
{
return aclDaoComponent.getAVMNodeCountWithNewACLS(max);
}
}, true, true);
reportProgress(toDo, done);
}
}
}
}
}