mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@130811 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
			
				
	
	
		
			679 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			679 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * Alfresco is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  * 
 | |
|  * Alfresco is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU Lesser General Public License for more details.
 | |
|  * 
 | |
|  * You should have received a copy of the GNU Lesser General Public License
 | |
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | |
|  * #L%
 | |
|  */
 | |
| package org.alfresco.repo.admin.patch;
 | |
| 
 | |
| import java.util.ArrayList;
 | |
| import java.util.Collections;
 | |
| import java.util.Comparator;
 | |
| import java.util.Date;
 | |
| import java.util.HashMap;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| 
 | |
| import org.alfresco.error.AlfrescoRuntimeException;
 | |
| import org.alfresco.repo.domain.patch.AppliedPatchDAO;
 | |
| import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
 | |
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
 | |
| import org.alfresco.repo.transaction.TransactionServiceImpl;
 | |
| import org.alfresco.service.cmr.admin.PatchException;
 | |
| import org.alfresco.service.cmr.rule.RuleService;
 | |
| import org.alfresco.service.descriptor.Descriptor;
 | |
| import org.alfresco.service.descriptor.DescriptorService;
 | |
| import org.alfresco.service.namespace.NamespaceService;
 | |
| import org.alfresco.service.namespace.QName;
 | |
| import org.alfresco.service.transaction.TransactionService;
 | |
| import org.alfresco.util.ParameterCheck;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| import org.springframework.extensions.surf.util.I18NUtil;
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Manages patches applied against the repository.
 | |
|  * <p>
 | |
|  * Patches are injected into this class and any attempted applications are recorded
 | |
|  * for later auditing.
 | |
|  * 
 | |
|  * @since 1.2
 | |
|  * @author Derek Hulley
 | |
|  */
 | |
| public class PatchServiceImpl implements PatchService
 | |
| {
 | |
|     private static final String MSG_NOT_RELEVANT = "patch.service.not_relevant";
 | |
|     private static final String MSG_PRECEEDED_BY_ALTERNATIVE = "patch.service.preceeded_by_alternative";
 | |
|     private static final String MSG_APPLYING_PATCH = "patch.service.applying_patch";
 | |
|     private static final String MSG_VALIDATION_FAILED = "patch.validation.failed";
 | |
|     
 | |
|     private static final Date ZERO_DATE = new Date(0L);
 | |
|     private static final Date INFINITE_DATE = new Date(Long.MAX_VALUE);
 | |
|     
 | |
|     private static Log logger = LogFactory.getLog(PatchExecuter.class);
 | |
|     
 | |
|     private DescriptorService descriptorService;
 | |
|     private TransactionServiceImpl transactionService;
 | |
|     private RuleService ruleService;
 | |
|     private AppliedPatchDAO appliedPatchDAO;
 | |
|     private List<Patch> patches;
 | |
| 
 | |
|     public PatchServiceImpl()
 | |
|     {
 | |
|         this.patches = new ArrayList<Patch>(10);
 | |
|     }
 | |
|     
 | |
|     public void setDescriptorService(DescriptorService descriptorService)
 | |
|     {
 | |
|         this.descriptorService = descriptorService;
 | |
|     }
 | |
| 
 | |
|     public void setTransactionService(TransactionServiceImpl transactionService)
 | |
|     {
 | |
|         this.transactionService = transactionService;
 | |
|     }
 | |
| 
 | |
|     public void setAppliedPatchDAO(AppliedPatchDAO appliedPatchDAO)
 | |
|     {
 | |
|         this.appliedPatchDAO = appliedPatchDAO;
 | |
|     }
 | |
|     
 | |
|     public void setRuleService(RuleService ruleService)
 | |
|     {
 | |
|         this.ruleService = ruleService;
 | |
|     }
 | |
| 
 | |
|     public void registerPatch(Patch patch)
 | |
|     {
 | |
|         patches.add(patch);
 | |
|     }
 | |
|     
 | |
|     private final QName vetoName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "PatchServiceImpl");
 | |
| 
 | |
|     /**
 | |
|      * {@inheritDoc}
 | |
|      */
 | |
|     public boolean validatePatches()
 | |
|     {
 | |
|         return validatePatchImpl(patches);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * {@inheritDoc}
 | |
|      */
 | |
|     public boolean validatePatch(Patch patch)
 | |
|     {
 | |
|         ParameterCheck.mandatory("patch", patch);
 | |
|         return validatePatchImpl(Collections.singletonList(patch));        
 | |
|     }
 | |
|     
 | |
|     private boolean validatePatchImpl(List<Patch> patches)
 | |
|     {
 | |
|         boolean success = true;
 | |
|         int serverSchemaVersion = descriptorService.getServerDescriptor().getSchema();
 | |
|         for (Patch patch : patches)
 | |
|         {
 | |
|             if (patch.getFixesToSchema() > serverSchemaVersion)
 | |
|             {
 | |
|                 logger.error(I18NUtil.getMessage(MSG_VALIDATION_FAILED, patch.getId(), serverSchemaVersion, patch
 | |
|                         .getFixesToSchema(), patch.getTargetSchema()));
 | |
|                 success = false;
 | |
|             }
 | |
|         }
 | |
|         if (!success)
 | |
|         {
 | |
|             this.transactionService.setAllowWrite(false, vetoName);
 | |
|         }
 | |
|         return success;
 | |
|     }
 | |
|     
 | |
|     public boolean applyOutstandingPatches()
 | |
|     {
 | |
|         boolean success = true;
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Disable rules whilst processing the patches
 | |
|             this.ruleService.disableRules();
 | |
|             try
 | |
|             {
 | |
|                 // Sort the patches
 | |
|                 List<Patch> sortedPatches = new ArrayList<Patch>(patches);
 | |
|                 Comparator<Patch> comparator = new PatchTargetSchemaComparator();
 | |
|                 Collections.sort(sortedPatches, comparator);
 | |
|                 
 | |
|                 // construct a list of executed patches by ID (also check the date)
 | |
|                 Map<String, AppliedPatch> appliedPatchesById = new HashMap<String, AppliedPatch>(250);
 | |
|                 List<AppliedPatch> appliedPatches = appliedPatchDAO.getAppliedPatches();
 | |
|                 for (final AppliedPatch appliedPatch : appliedPatches)
 | |
|                 {
 | |
|                     appliedPatchesById.put(appliedPatch.getId(), appliedPatch);
 | |
|                     // Update the time of execution if it is null.  This is to deal with
 | |
|                     // patches that get executed prior to server startup and need to have
 | |
|                     // an execution time assigned
 | |
|                     if (appliedPatch.getAppliedOnDate() == null)
 | |
|                     {
 | |
|                         RetryingTransactionCallback<Date> callback = new RetryingTransactionCallback<Date>()
 | |
|                         {
 | |
|                             public Date execute() throws Throwable
 | |
|                             {
 | |
|                                 Date now = new Date();
 | |
|                                 appliedPatch.setAppliedOnDate(now);
 | |
|                                 appliedPatchDAO.updateAppliedPatch(appliedPatch);
 | |
|                                 return now;
 | |
|                             }
 | |
|                         };
 | |
|                         transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);
 | |
|                     }
 | |
|                 }
 | |
|             
 | |
|                 // go through all the patches and apply them where necessary        
 | |
|                 for (Patch patch : sortedPatches)
 | |
|                 {
 | |
|                     if(patch.isDeferred())
 | |
|                     {
 | |
|                         continue;
 | |
|                     }
 | |
|                     // apply the patch
 | |
|                     success = applyPatchAndDependencies(patch, appliedPatchesById);
 | |
|                     if (!success)
 | |
|                     {
 | |
|                         // we failed to apply a patch or one of its dependencies - terminate
 | |
|                         break;
 | |
|                     }
 | |
|                 }        
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 this.ruleService.enableRules();
 | |
|             }
 | |
|         }
 | |
|         catch (Throwable exception)
 | |
|         {
 | |
|             exception.printStackTrace();
 | |
|             success = false;
 | |
|         }
 | |
|         
 | |
|         // done
 | |
|         return success;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * {@inheritDoc}
 | |
|      */
 | |
|     public boolean applyOutstandingPatch(Patch patch)
 | |
|     {
 | |
|         boolean success = true;
 | |
|         try
 | |
|         {
 | |
|             // Disable rules whilst processing the patches
 | |
|             this.ruleService.disableRules();
 | |
|             try
 | |
|             {
 | |
|                 Map<String, AppliedPatch> appliedPatchesById = null;
 | |
|                 if (patch.getDependsOn().isEmpty())
 | |
|                 {
 | |
|                     AppliedPatch appliedPatch = appliedPatchDAO.getAppliedPatch(patch.getId());
 | |
|                     appliedPatchesById = new HashMap<String, AppliedPatch>(1);
 | |
|                     if (appliedPatch != null)
 | |
|                     {
 | |
|                         appliedPatchesById.put(appliedPatch.getId(), appliedPatch);
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     appliedPatchesById = new HashMap<String, AppliedPatch>(250);
 | |
|                     List<AppliedPatch> appliedPatches = appliedPatchDAO.getAppliedPatches();
 | |
|                     for (final AppliedPatch appliedPatch : appliedPatches)
 | |
|                     {
 | |
|                         appliedPatchesById.put(appliedPatch.getId(), appliedPatch);
 | |
|                     }
 | |
|                 }
 | |
|                 // apply the patch
 | |
|                 success = applyPatchAndDependencies(patch, appliedPatchesById);
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 this.ruleService.enableRules();
 | |
|             }
 | |
|         }
 | |
|         catch (Throwable exception)
 | |
|         {
 | |
|             exception.printStackTrace();
 | |
|             success = false;
 | |
|         }
 | |
| 
 | |
|         // done
 | |
|         return success;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Reentrant method that ensures that a patch and all its dependencies get applied.
 | |
|      * The process terminates on the first failure.
 | |
|      * 
 | |
|      * @param patch the patch (containing dependencies) to apply
 | |
|      * @param appliedPatchesById already applied patches keyed by their ID
 | |
|      * @return Returns true if the patch and all its dependencies were successfully applied.
 | |
|      */
 | |
|     private boolean applyPatchAndDependencies(final Patch patch, Map<String, AppliedPatch> appliedPatchesById)
 | |
|     {
 | |
|         String id = patch.getId();
 | |
|         
 | |
|         // ensure that dependencies have been done
 | |
|         List<Patch> dependencies = patch.getDependsOn();
 | |
|         for (Patch dependencyPatch : dependencies)
 | |
|         {
 | |
|             boolean success = applyPatchAndDependencies(dependencyPatch, appliedPatchesById);
 | |
|             if (!success)
 | |
|             {
 | |
|                 // a patch failed to be applied
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // check if it has already been done
 | |
|         AppliedPatch appliedPatch = appliedPatchesById.get(id); 
 | |
|         if (appliedPatch != null && appliedPatch.getSucceeded())
 | |
|         {
 | |
|             if (appliedPatch.getWasExecuted())
 | |
|             {
 | |
|                 // It was successfully executed
 | |
|                 return true;
 | |
|             }
 | |
|             // We give the patch another chance
 | |
|         }
 | |
| 
 | |
|         
 | |
|         // all the dependencies were successful
 | |
|         
 | |
|         appliedPatch = applyPatch(patch);
 | |
| 
 | |
|         if (!appliedPatch.getSucceeded())
 | |
|         {
 | |
|             // this was a failure
 | |
|             return false;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // it was successful - add it to the map of successful patches
 | |
|             appliedPatchesById.put(id, appliedPatch);
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     private AppliedPatch applyPatch(Patch patch)
 | |
|     {
 | |
|         PatchWork work = new PatchWork(patch);
 | |
|         work.setAppliedPatchDAO(appliedPatchDAO);
 | |
|         work.setTransactionService(transactionService);
 | |
|         work.setAppliedPatchDAO(appliedPatchDAO);
 | |
|         work.setDescriptorService(descriptorService);
 | |
|         work.setLogger(logger);
 | |
|         work.execute();
 | |
| 
 | |
|         return work.getAppliedPatch();
 | |
|     }
 | |
| 
 | |
|     @SuppressWarnings("unchecked")
 | |
|     public List<AppliedPatch> getPatches(Date fromDate, Date toDate)
 | |
|     {
 | |
|         if (fromDate == null)
 | |
|         {
 | |
|             fromDate = ZERO_DATE;
 | |
|         }
 | |
|         if (toDate == null)
 | |
|         {
 | |
|             toDate = INFINITE_DATE;
 | |
|         }
 | |
|         List<? extends AppliedPatch> appliedPatches = appliedPatchDAO.getAppliedPatches(fromDate, toDate);
 | |
|         // done
 | |
|         return (List<AppliedPatch>) appliedPatches;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Executes a patch, ensuring that pre-conditions are met and patch information is saved.
 | |
|      * Introduced with fix for ALF-5621. 
 | |
|      * 
 | |
|      * This has been split out from applyPatch because it's easier to perform
 | |
|      * the transaction wrapping required when requiresTransaction == false
 | |
|      */
 | |
|     private static class PatchWork
 | |
|     {
 | |
|         private enum STATE
 | |
|         {
 | |
|             START, PRECEEDED, ALREADY_APPLIED, DOES_NOT_APPLY, APPLYING, NOT_APPLIED, APPLIED, FAILED;
 | |
|         };
 | |
|         
 | |
|         private Log logger;
 | |
|         private AppliedPatchDAO appliedPatchDAO;
 | |
|         private DescriptorService descriptorService;
 | |
|         private TransactionService transactionService;
 | |
| 
 | |
|         private STATE state = STATE.START;
 | |
|         
 | |
|         private Patch patch;
 | |
| 
 | |
|         private AppliedPatch appliedPatch;
 | |
|         private Descriptor repoDescriptor;
 | |
|         private String preceededByAlternative;
 | |
|         private boolean applies = false;
 | |
|         private String report = null;
 | |
|         
 | |
|         public PatchWork(Patch patch) {
 | |
|             super();
 | |
|             this.patch = patch;
 | |
|         }
 | |
|         
 | |
|         public void setLogger(Log logger)
 | |
|         {
 | |
|             this.logger = logger;
 | |
|         }
 | |
|         
 | |
|         public void setAppliedPatchDAO(AppliedPatchDAO appliedPatchDAO)
 | |
|         {
 | |
|             this.appliedPatchDAO = appliedPatchDAO;
 | |
|         }
 | |
|         
 | |
|         public void setDescriptorService(DescriptorService descriptorService)
 | |
|         {
 | |
|             this.descriptorService = descriptorService;
 | |
|         }
 | |
| 
 | |
|         public void setTransactionService(TransactionService transactionService)
 | |
|         {
 | |
|             this.transactionService = transactionService;
 | |
|         }
 | |
|         
 | |
|         /**
 | |
|          * Identifies if one of the alternative patches has already been executed.
 | |
|          * 
 | |
|          * @param patch             the patch to check
 | |
|          * @return                  Returns the ID of any successfully executed alternative patch
 | |
|          */
 | |
|         private String preceededByAlternative(Patch patch)
 | |
|         {
 | |
|             // If any alternatives were executed, then bypass this one
 | |
|             List<Patch> alternatives = patch.getAlternatives();
 | |
|             for (Patch alternative : alternatives)
 | |
|             {
 | |
|                 // If the patch was executed, then this one was effectively executed
 | |
|                 AppliedPatch appliedAlternative = appliedPatchDAO.getAppliedPatch(alternative.getId());
 | |
|                 if (appliedAlternative != null && appliedAlternative.getWasExecuted() && appliedAlternative.getSucceeded())
 | |
|                 {
 | |
|                     return alternative.getId();
 | |
|                 }
 | |
|             }
 | |
|             return null;
 | |
|         }
 | |
|         
 | |
|         /**
 | |
|          * Check whether the patch is applicable to the particular version of the repository. 
 | |
|          * 
 | |
|          * @param repoDescriptor contains the version details of the repository
 | |
|          * @param patch the patch whos version must be checked
 | |
|          * @return Returns true if the patch should be applied to the repository
 | |
|          */
 | |
|         private boolean applies(Descriptor repoDescriptor, Patch patch)
 | |
|         {
 | |
|             int repoSchema = repoDescriptor.getSchema();
 | |
|             // does the patch apply?
 | |
|             boolean apply = patch.applies(repoSchema);
 | |
|             
 | |
|             // done
 | |
|             if (logger.isDebugEnabled())
 | |
|             {
 | |
|                 logger.debug("Patch schema version number check against repo version: \n" +
 | |
|                         "   repo schema version: " + repoDescriptor.getVersion() + "\n" +
 | |
|                         "   patch: " + patch);
 | |
|             }
 | |
|             return apply;
 | |
|         }
 | |
| 
 | |
|         private boolean patchSucceeded()
 | |
|         {
 | |
|             return state == STATE.ALREADY_APPLIED || state == STATE.DOES_NOT_APPLY || state == STATE.APPLIED || state == STATE.PRECEEDED;
 | |
|         }
 | |
|         
 | |
|         private boolean savePatch()
 | |
|         {
 | |
|             return state == STATE.DOES_NOT_APPLY || state == STATE.APPLIED || state == STATE.PRECEEDED || state == STATE.FAILED;
 | |
|         }
 | |
| 
 | |
|         public void execute()
 | |
|         {
 | |
|             if(state != STATE.START)
 | |
|             {
 | |
|                 throw new IllegalStateException("Patch is already being applied");
 | |
|             }
 | |
| 
 | |
|             if(!patch.requiresTransaction() && AlfrescoTransactionSupport.isActualTransactionActive())
 | |
|             {
 | |
|                 throw new AlfrescoRuntimeException("Patch " +
 | |
|                         patch.getId() +
 | |
|                         " has been configured with requiresTransaction set to false but is being called in a transaction");
 | |
|             }
 | |
| 
 | |
|             setup();
 | |
|             applyPatch();
 | |
|             save();
 | |
|         }
 | |
|         
 | |
|         /**
 | |
|          * Perform some setup before applying the patch e.g. check whether the patch needs to be applied.
 | |
|          */
 | |
|         private void setup()
 | |
|         {
 | |
|             transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
 | |
|             {
 | |
|                 @Override
 | |
|                 public Object execute() throws Throwable
 | |
|                 {
 | |
|                     final boolean forcePatch = patch.isForce();
 | |
|                     if (forcePatch)
 | |
|                     {
 | |
|                         logger.warn(
 | |
|                                 "Patch will be forcefully executed: \n" +
 | |
|                                 "   Patch: " + patch);
 | |
|                     }
 | |
|                     
 | |
|                     // Check whether patch has been applied already
 | |
|                     // get the patch from the DAO
 | |
|                     appliedPatch = appliedPatchDAO.getAppliedPatch(patch.getId());
 | |
| 
 | |
|                     // We bypass the patch if it was executed successfully
 | |
|                     if (appliedPatch != null && !forcePatch)
 | |
|                     {
 | |
|                         if (appliedPatch.getSucceeded())
 | |
|                         {
 | |
|                             // It has already been successfully applied
 | |
|                             if (logger.isDebugEnabled())
 | |
|                             {
 | |
|                                 logger.debug(
 | |
|                                         "Patch was already successfully applied: \n" +
 | |
|                                         "   Patch: " + appliedPatch);
 | |
|                             }
 | |
|                             state = STATE.ALREADY_APPLIED;
 | |
|                             return null;
 | |
|                         }
 | |
|                     }
 | |
|                     
 | |
|                     // first check whether the patch is relevant to the repo
 | |
|                     repoDescriptor = descriptorService.getInstalledRepositoryDescriptor();
 | |
|                     applies = forcePatch || applies(repoDescriptor, patch);
 | |
|                     preceededByAlternative = forcePatch ? null : preceededByAlternative(patch);
 | |
|                     if (preceededByAlternative != null)
 | |
|                     {
 | |
|                         report = I18NUtil.getMessage(MSG_PRECEEDED_BY_ALTERNATIVE, preceededByAlternative);
 | |
|                         state = STATE.PRECEEDED;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         if(applies)
 | |
|                         {
 | |
|                             state = STATE.APPLYING;
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             report = I18NUtil.getMessage(MSG_NOT_RELEVANT, repoDescriptor.getSchema());
 | |
|                             state = STATE.DOES_NOT_APPLY;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     return null;
 | |
|                 }
 | |
|             }, false, true);
 | |
|         }
 | |
| 
 | |
|         private void applyPatch()
 | |
|         {
 | |
|             if (state != STATE.APPLYING)
 | |
|             {
 | |
|                 // nothing to do
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             // perform actual execution
 | |
|             try
 | |
|             {
 | |
|                 String msg = I18NUtil.getMessage(
 | |
|                         MSG_APPLYING_PATCH,
 | |
|                         patch.getId(),
 | |
|                         I18NUtil.getMessage(patch.getDescription()));
 | |
|                 logger.info(msg);
 | |
|                 // the patch is executed regardless of the deferred flag value
 | |
|                 report = (patch.isDeferred()) ? patch.applyAsync() : patch.apply();
 | |
|                 state = STATE.APPLIED;
 | |
|             }
 | |
|             catch (PatchException e)
 | |
|             {
 | |
|                 // failed
 | |
|                 report = e.getMessage();
 | |
|                 state = STATE.FAILED;
 | |
|                 // dump the report to log
 | |
|                 logger.error(report);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         private void save()
 | |
|         {
 | |
|             if(!savePatch())
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
 | |
|             {
 | |
|                 @Override
 | |
|                 public Object execute() throws Throwable {
 | |
|                     Descriptor serverDescriptor = descriptorService.getServerDescriptor();
 | |
|                     String server = (serverDescriptor.getVersion() + " - " + serverDescriptor.getEdition());
 | |
| 
 | |
|                     if (server.length() > 64)
 | |
|                     {
 | |
|                         logger.error("Server version '" + server + "' is too long for the 'applied_to_server' column therefore patch '" + 
 | |
|                                     patch.getId() + "' will not be registered.");
 | |
|                     }
 | |
|                     
 | |
|                     // create or update the record of execution
 | |
|                     boolean create = true;
 | |
|                     if (appliedPatch == null)
 | |
|                     {
 | |
|                         appliedPatch = new AppliedPatch();
 | |
|                         appliedPatch.setId(patch.getId());
 | |
|                         create = true;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         // Update it
 | |
|                         create = false;
 | |
|                     }
 | |
|                     // fill in the record's details
 | |
|                     String patchDescription = I18NUtil.getMessage(patch.getDescription());
 | |
|                     if (patchDescription == null)
 | |
|                     {
 | |
|                         logger.warn("Patch description is not available: " + patch);
 | |
|                         patchDescription = "No patch description available";
 | |
|                     }
 | |
|                     appliedPatch.setDescription(patchDescription);
 | |
|                     appliedPatch.setFixesFromSchema(patch.getFixesFromSchema());
 | |
|                     appliedPatch.setFixesToSchema(patch.getFixesToSchema());
 | |
|                     appliedPatch.setTargetSchema(patch.getTargetSchema());       // the schema the server is expecting
 | |
|                     appliedPatch.setAppliedToSchema(repoDescriptor.getSchema()); // the old schema of the repo
 | |
|                     appliedPatch.setAppliedToServer(server);                     // the current version and label of the server
 | |
|                     appliedPatch.setAppliedOnDate(new Date());                   // the date applied
 | |
|                     appliedPatch.setSucceeded(patchSucceeded());                 // whether or not the patch succeeded
 | |
|                     appliedPatch.setWasExecuted(applies);                        // whether or not the patch was executed
 | |
|                     appliedPatch.setReport(report);                              // additional, human-readable, status
 | |
|                     // Update or create the entry
 | |
|                     if (create)
 | |
|                     {
 | |
|                         appliedPatchDAO.createAppliedPatch(appliedPatch);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         appliedPatchDAO.updateAppliedPatch(appliedPatch);
 | |
|                     }
 | |
| 
 | |
|                     // done
 | |
|                     if (logger.isDebugEnabled())
 | |
|                     {
 | |
|                         logger.debug("Applied patch: \n" + appliedPatch);
 | |
|                     }
 | |
| 
 | |
|                     return null;
 | |
|                 }
 | |
|             }, false, true);
 | |
|         }
 | |
|         
 | |
|         public AppliedPatch getAppliedPatch()
 | |
|         {
 | |
|             return appliedPatch;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Compares patch target schemas.
 | |
|      * 
 | |
|      * @see Patch#getTargetSchema()
 | |
|      * @author Derek Hulley
 | |
|      */
 | |
|     private static class PatchTargetSchemaComparator implements Comparator<Patch>
 | |
|     {
 | |
|         public int compare(Patch p1, Patch p2)
 | |
|         {
 | |
|             Integer i1 = new Integer(p1.getTargetSchema());
 | |
|             Integer i2 = new Integer(p2.getTargetSchema());
 | |
|             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);
 | |
|     }
 | |
| }
 |