From 47449b155549178040a40894b2be0a1122636fd1 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Mon, 16 Jan 2006 19:49:34 +0000 Subject: [PATCH] Checkpoint checkin of patch code. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2119 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/application-context.xml | 1 + config/alfresco/hibernate-context.xml | 2 + config/alfresco/patch-services-context.xml | 69 ++++++ config/ehcache.xml | 9 + .../repo/admin/patch/AbstractPatch.java | 232 ++++++++++++++++++ .../org/alfresco/repo/admin/patch/Patch.java | 58 +++++ .../repo/admin/patch/PatchDaoService.java | 54 ++++ .../repo/admin/patch/PatchService.java | 58 +++++ .../repo/admin/patch/PatchServiceImpl.java | 230 +++++++++++++++++ .../alfresco/repo/admin/patch/PatchTest.java | 142 +++++++++++ .../repo/admin/patch/SamplePatch.java | 66 +++++ .../HibernatePatchDaoServiceImpl.java | 83 +++++++ .../alfresco/repo/domain/AppliedPatch.java | 48 ++++ .../domain/hibernate/AppliedPatch.hbm.xml | 33 +++ .../domain/hibernate/AppliedPatchImpl.java | 110 +++++++++ .../repo/domain/hibernate/NodeImpl.java | 7 +- .../service/cmr/admin/AdminService.java | 30 +++ .../service/cmr/admin/PatchException.java | 47 ++++ .../alfresco/service/cmr/admin/PatchInfo.java | 123 ++++++++++ 19 files changed, 1401 insertions(+), 1 deletion(-) create mode 100644 config/alfresco/patch-services-context.xml create mode 100644 source/java/org/alfresco/repo/admin/patch/AbstractPatch.java create mode 100644 source/java/org/alfresco/repo/admin/patch/Patch.java create mode 100644 source/java/org/alfresco/repo/admin/patch/PatchDaoService.java create mode 100644 source/java/org/alfresco/repo/admin/patch/PatchService.java create mode 100644 source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java create mode 100644 source/java/org/alfresco/repo/admin/patch/PatchTest.java create mode 100644 source/java/org/alfresco/repo/admin/patch/SamplePatch.java create mode 100644 source/java/org/alfresco/repo/admin/patch/hibernate/HibernatePatchDaoServiceImpl.java create mode 100644 source/java/org/alfresco/repo/domain/AppliedPatch.java create mode 100644 source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml create mode 100644 source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java create mode 100644 source/java/org/alfresco/service/cmr/admin/AdminService.java create mode 100644 source/java/org/alfresco/service/cmr/admin/PatchException.java create mode 100644 source/java/org/alfresco/service/cmr/admin/PatchInfo.java diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index bc6f5d03e6..661faa9532 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -20,6 +20,7 @@ + + + + + + + + + + + Sample01 + A NO-OP sample patch + 0.0 + + + + Sample02 + A NO-OP sample patch + 0.0 + + + + + + + + diff --git a/config/ehcache.xml b/config/ehcache.xml index 6a7df93a63..6dc991d364 100644 --- a/config/ehcache.xml +++ b/config/ehcache.xml @@ -116,6 +116,15 @@ timeToLiveSeconds="0" overflowToDisk="false" /> + + dependsOn; + /** flag indicating if the patch was successfully applied */ + private boolean applied; + private TransactionService transactionService; + + public AbstractPatch() + { + this.applied = false; + this.dependsOn = Collections.emptyList(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(56); + sb.append("Patch") + .append("[id=").append(getId()) + .append(", after=").append(getApplyAfterVersion()) + .append(", description=").append(getDescription()) + .append("]"); + return sb.toString(); + } + + /** + * Set the transaction provider so that each execution can be performed within a transaction + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public String getId() + { + return id; + } + + /** + * + * @param id the unique ID of the patch. This dictates the order in which patches are applied. + */ + public void setId(String id) + { + this.id = id; + } + + public String getApplyAfterVersion() + { + return applyAfterVersion; + } + + /** + * + * @param applyAfterVersion the version of the repository after which this patch must be applied. + */ + public void setApplyAfterVersion(String applyAfterVersion) + { + this.applyAfterVersion = applyAfterVersion; + } + + public String getDescription() + { + return description; + } + + /** + * @param description a thorough description of the patch + */ + public void setDescription(String description) + { + this.description = description; + } + + public List getDependsOn() + { + return this.dependsOn; + } + /** + * 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 + */ + public void setDependsOn(List dependsOn) + { + this.dependsOn = dependsOn; + } + + /** + * Sets up the transaction and ensures thread-safety. + * + * @see #applyInternal() + */ + 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); + } + // check that the necessary properties have been set + if (id == null || applyAfterVersion == null || description == null) + { + throw new AlfrescoRuntimeException( + "Patch properties 'id', 'applyAfterVersion' and 'description' have not all been set on this patch: \n" + + " patch: " + this); + } + else if (transactionService == null) + { + throw new AlfrescoRuntimeException("'transactionService' property has not been set: \n" + + " patch: " + this); + } + // execute in a transaction + try + { + TransactionWork patchWork = new TransactionWork() + { + public String doWork() throws Exception + { + String report = applyInternal(); + // done + return report; + }; + }; + String report = TransactionUtil.executeInUserTransaction(transactionService, patchWork); + // the patch was successfully applied + applied = true; + // done + if (logger.isDebugEnabled()) + { + logger.debug("Patch successfully applied: \n" + + " patch: " + this + "\n" + + " report: " + report); + } + return report; + } + catch (PatchException e) + { + // no need to extract the exception + throw e; + } + catch (Throwable e) + { + // check whether there is an embedded patch exception + Throwable cause = e.getCause(); + if (cause != null && cause instanceof PatchException) + { + throw (PatchException) cause; + } + // need to generate a message from the exception + String report = makeReport(e); + // generate the correct exception + throw new PatchException(report); + } + } + + /** + * Dumps the error's full message and trace to the String + * + * @param e the throwable + * @return Returns a String representative of the printStackTrace method + */ + private String makeReport(Throwable e) + { + StringWriter stringWriter = new StringWriter(1024); + PrintWriter printWriter = new PrintWriter(stringWriter, true); + try + { + e.printStackTrace(printWriter); + return stringWriter.toString(); + } + finally + { + printWriter.close(); + } + } + + /** + * 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. + * + * @return Returns the report (only success messages). + * @see #apply() + * @throws Exception anything can be thrown. This must be used for all failures. + */ + protected abstract String applyInternal() throws Exception; +} diff --git a/source/java/org/alfresco/repo/admin/patch/Patch.java b/source/java/org/alfresco/repo/admin/patch/Patch.java new file mode 100644 index 0000000000..579fdb6feb --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/Patch.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch; + +import java.util.List; + +import org.alfresco.service.cmr.admin.PatchException; + +/** + * A patch is an executable class that makes a change to persisted data. + *

+ * Auditing information is not maintained by the patch - rather it is solely + * responsible for the execution of the processes necessary to apply the patch. + *

+ * Patches must not be reappliable. It is up to the patch management systems + * to ensure that patches are never reapplied. + * + * @since 1.2 + * @author Derek Hulley + */ +public interface Patch +{ + public String getId(); + + public String getDescription(); + + public String getApplyAfterVersion(); + + /** + * Get patches that this patch depends on + * + * @return Returns a list of patches + */ + public List getDependsOn(); + + /** + * Applies the patch. Typically this will be within the bounds of a new + * transaction. + * + * @return Returns the patch execution report + * @throws PatchException if the patch failed to be applied + */ + public String apply() throws PatchException; +} diff --git a/source/java/org/alfresco/repo/admin/patch/PatchDaoService.java b/source/java/org/alfresco/repo/admin/patch/PatchDaoService.java new file mode 100644 index 0000000000..a280753f0a --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/PatchDaoService.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch; + +import java.util.List; + +import org.alfresco.repo.domain.AppliedPatch; + +/** + * Provides data access support for patch persistence. + * + * @since 1.2 + * @author Derek Hulley + */ +public interface PatchDaoService +{ + /** + * Creates and saves a new instance of the patch. This will not have all the mandatory + * properties set - only the ID. + * + * @param id the unique key + * @return Returns a new instance that can be manipulated + */ + public AppliedPatch newAppliedPatch(String id); + + /** + * Retrieve an existing patch + * + * @param id the patch unique ID + * @return Returns the patch instance or null if one has not been persisted + */ + public AppliedPatch getAppliedPatch(String id); + + /** + * Get a list of all applied patches + * + * @return Returns a list of all applied patches + */ + public List getAppliedPatches(); +} diff --git a/source/java/org/alfresco/repo/admin/patch/PatchService.java b/source/java/org/alfresco/repo/admin/patch/PatchService.java new file mode 100644 index 0000000000..35b3ad5c3a --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/PatchService.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch; + +import java.util.List; + +import org.alfresco.service.cmr.admin.PatchInfo; + +/** + * Manages patches applied against the repository. + *

+ * Patches are injected into this class and any attempted applications are recorded + * for later auditing. + * + * @since 1.2 + * @author Derek Hulley + */ +public interface PatchService +{ + /** + * Set the complete list of patches. All patch IDs must remain static for the duration their + * existence. This allows us to recognise the + * + * @param patches the complete list of patches (either applied or not) + */ + public void setPatches(List patches); + + /** + * Get a list of all previously applied patches + * + * @return Returns a list of patch application information + */ + public List getAppliedPatches(); + + /** + * Apply all outstanding patches that are relevant to the repo. + * If there is a failure, then the patches that were applied will remain so, + * but the process will not attempt to apply any further patches. + * + * @return Returns true if all outstanding patches were applied, or false if the process + * was termintated before all patches could be applied. + */ + public boolean applyOutstandingPatches(); +} diff --git a/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java b/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java new file mode 100644 index 0000000000..2a22f62f41 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.domain.AppliedPatch; +import org.alfresco.service.cmr.admin.PatchException; +import org.alfresco.service.cmr.admin.PatchInfo; +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.descriptor.DescriptorService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Manages patches applied against the repository. + *

+ * 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 Log logger = LogFactory.getLog(PatchServiceImpl.class); + + private DescriptorService descriptorService; + private PatchDaoService patchDaoService; + private List patches; + + public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } + + public void setPatchDaoService(PatchDaoService patchDaoService) + { + this.patchDaoService = patchDaoService; + } + + public void setPatches(List patches) + { + this.patches = patches; + } + + public List getAppliedPatches() + { + // get all the persisted patches + List appliedPatches = patchDaoService.getAppliedPatches(); + List patchInfos = new ArrayList(appliedPatches.size()); + for (AppliedPatch patch : appliedPatches) + { + PatchInfo patchInfo = new PatchInfo(patch); + patchInfos.add(patchInfo); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved list of " + patchInfos.size() + " applied patches: \n"); + } + return patchInfos; + } + + public boolean applyOutstandingPatches() + { + // construct a map of all known patches by ID + Map allPatchesById = new HashMap(23); + for (Patch patch : patches) + { + allPatchesById.put(patch.getId(), patch); + } + // construct a list of executed patches by ID + Map appliedPatchInfosById = new HashMap(23); + List appliedPatches = getAppliedPatches(); + for (PatchInfo patchInfo : appliedPatches) + { + // ignore unsuccessful attempts - we need to try them again + if (!patchInfo.getSucceeded()) + { + continue; + } + appliedPatchInfosById.put(patchInfo.getId(), patchInfo); + } + // go through all the patches and apply them where necessary + boolean success = true; + for (Patch patch : allPatchesById.values()) + { + // apply the patch + success = applyPatchAndDependencies(patch, appliedPatchInfosById); + if (!success) + { + // we failed to apply a patch or one of its dependencies - terminate + break; + } + } + // done + return success; + } + + /** + * Reentrant method that ensures that a patch and all its dependencies get applied. + * The process terminates on the first failure. + * + * @param patchInfos all the executed patch data. If there was a failure, then this + * is the list of successful executions only. + * @param patch the patch (containing dependencies) to apply + * @param appliedPatchInfosById already applied patches + * @return Returns true if the patch and all its dependencies were successfully applied. + */ + private boolean applyPatchAndDependencies(Patch patch, Map appliedPatchInfosById) + { + String id = patch.getId(); + // check if it has already been done + PatchInfo patchInfo = appliedPatchInfosById.get(id); + if (patchInfo != null && patchInfo.getSucceeded()) + { + // this has already been done + return true; + } + + // ensure that dependencies have been done + List dependencies = patch.getDependsOn(); + for (Patch dependencyPatch : dependencies) + { + boolean success = applyPatchAndDependencies(dependencyPatch, appliedPatchInfosById); + if (!success) + { + // a patch failed to be applied + return false; + } + } + // all the dependencies were successful + patchInfo = applyPatch(patch); + if (!patchInfo.getSucceeded()) + { + // this was a failure + return false; + } + else + { + // it was successful - add it to the map of successful patches + appliedPatchInfosById.put(id, patchInfo); + return true; + } + } + + private PatchInfo applyPatch(Patch patch) + { + // get the patch from the DAO + AppliedPatch appliedPatch = patchDaoService.getAppliedPatch(patch.getId()); + if (appliedPatch != null && appliedPatch.getSucceeded()) + { + // it has already been applied + PatchInfo patchInfo = new PatchInfo(appliedPatch); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Patch was already successfully applied: \n" + + " patch: " + patchInfo); + } + return patchInfo; + } + // the execution report + String report = null; + boolean success = false; + // first check whether the patch is relevant to the repo + Descriptor repo = descriptorService.getRepositoryDescriptor(); + String versionLabel = repo.getVersionLabel(); + if (versionLabel.compareTo(patch.getApplyAfterVersion()) >= 0) + { + // create a dummy report + StringBuilder sb = new StringBuilder(128); + sb.append("Patch ").append(patch.getId()).append(" was not relevant."); + report = sb.toString(); + success = true; // this succeeded because it didn't need to be applied + } + else + { + // perform actual execution + try + { + report = patch.apply(); + success = true; + } + catch (PatchException e) + { + // failed + report = e.getMessage(); + success = false; + } + } + // create a record for the execution + appliedPatch = patchDaoService.newAppliedPatch(patch.getId()); + // fill in the record's details + appliedPatch.setDescription(patch.getDescription()); + appliedPatch.setApplyAfterVersion(patch.getApplyAfterVersion()); + appliedPatch.setSucceeded(success); + appliedPatch.setAppliedOnVersion(versionLabel); + appliedPatch.setAppliedOnDate(new Date()); + appliedPatch.setReport(report); + // create the info for returning + PatchInfo patchInfo = new PatchInfo(appliedPatch); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Applied patch: \n" + patchInfo); + } + return patchInfo; + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/PatchTest.java b/source/java/org/alfresco/repo/admin/patch/PatchTest.java new file mode 100644 index 0000000000..29d95e5a5b --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/PatchTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.admin.PatchException; +import org.alfresco.service.cmr.admin.PatchInfo; +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 PatchService patchService; + private List patches; + + public PatchTest(String name) + { + super(name); + } + + public void setUp() throws Exception + { + transactionService = (TransactionService) ctx.getBean("transactionComponent"); + patchService = (PatchService) ctx.getBean("PatchService"); + + // get the patches to play with + patches = new ArrayList(2); + patches.add((Patch)ctx.getBean("patch.sample.02")); + patches.add((Patch)ctx.getBean("patch.sample.01")); + patchService.setPatches(patches); + } + + public void testSetup() throws Exception + { + assertNotNull(transactionService); + assertNotNull(patchService); + } + + public void testSimplePatchSuccess() throws Exception + { + Patch patch = new SamplePatch(false, transactionService); + 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 = new SamplePatch(false, transactionService); + 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 = new SamplePatch(true, transactionService); + 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 patchInfos = patchService.getAppliedPatches(); + // check that the patch application was recorded + boolean found01 = false; + boolean found02 = false; + for (PatchInfo patchInfo : patchInfos) + { + if (patchInfo.getId().equals("Sample01")) + { + found01 = true; + assertTrue("Patch info didn't indicate success: " + patchInfo, patchInfo.getSucceeded()); + } + else if (patchInfo.getId().equals("Sample02")) + { + found02 = true; + assertTrue("Patch info didn't indicate success: " + patchInfo, patchInfo.getSucceeded()); + } + } + assertTrue("Sample 01 not in list of applied patches", found01); + assertTrue("Sample 02 not in list of applied patches", found02); + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/SamplePatch.java b/source/java/org/alfresco/repo/admin/patch/SamplePatch.java new file mode 100644 index 0000000000..049ebe9f36 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/SamplePatch.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch; + +import org.alfresco.service.transaction.TransactionService; + +public class SamplePatch extends AbstractPatch +{ + public static final String MSG_SUCCESS = "SamplePatch applied successfully"; + public static final String MSG_FAILURE = "SamplePatch failed to apply"; + + private boolean mustFail; + + /** + * Default constructor for Spring config + */ + public SamplePatch() + { + } + + /** + * Helper constructor for some tests. Default properties are set automatically. + * + * @param mustFail true if this instance must always fail to apply + */ + /* protected */ SamplePatch(boolean mustFail, TransactionService transactionService) + { + this.mustFail = mustFail; + setTransactionService(transactionService); + setId("SamplePatch"); + setDescription("This is a sample patch"); + setApplyAfterVersion("1.0.0"); + } + + /** + * Does nothing + * + * @return Returns a success or failure message dependent on the constructor used + */ + @Override + protected String applyInternal() throws Exception + { + if (mustFail) + { + throw new Exception(MSG_FAILURE); + } + else + { + return MSG_SUCCESS; + } + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/hibernate/HibernatePatchDaoServiceImpl.java b/source/java/org/alfresco/repo/admin/patch/hibernate/HibernatePatchDaoServiceImpl.java new file mode 100644 index 0000000000..bf560bdd72 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/hibernate/HibernatePatchDaoServiceImpl.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.admin.patch.hibernate; + +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.admin.patch.PatchDaoService; +import org.alfresco.repo.domain.AppliedPatch; +import org.alfresco.repo.domain.hibernate.AppliedPatchImpl; +import org.hibernate.Query; +import org.hibernate.Session; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * Hibernate-specific implementation for managing patch persistence. + * + * @since 1.2 + * @author Derek Hulley + */ +public class HibernatePatchDaoServiceImpl extends HibernateDaoSupport implements PatchDaoService +{ + public static final String QUERY_GET_ALL_APPLIED_PATCHES = "patch.GetAllAppliedPatches"; + + public AppliedPatch newAppliedPatch(String id) + { + // check for existence + AppliedPatch existing = getAppliedPatch(id); + if (existing != null) + { + throw new AlfrescoRuntimeException("An applied patch already exists: \n" + + " id: " + id); + } + // construct a new one + AppliedPatchImpl patch = new AppliedPatchImpl(); + patch.setId(id); + // save this in hibernate + getHibernateTemplate().save(patch); + // done + return patch; + } + + public AppliedPatch getAppliedPatch(String id) + { + AppliedPatch patch = (AppliedPatch) getHibernateTemplate().get(AppliedPatchImpl.class, id); + // done + return patch; + } + + /** + * @see #QUERY_GET_ALL_APPLIED_PATCHES + */ + @SuppressWarnings("unchecked") + public List getAppliedPatches() + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(HibernatePatchDaoServiceImpl.QUERY_GET_ALL_APPLIED_PATCHES); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + // done + return queryResults; + } +} diff --git a/source/java/org/alfresco/repo/domain/AppliedPatch.java b/source/java/org/alfresco/repo/domain/AppliedPatch.java new file mode 100644 index 0000000000..3cba265d80 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/AppliedPatch.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain; + +import java.util.Date; + +/** + * Interface for persistent patch application information. + * + * @author Derek Hulley + */ +public interface AppliedPatch +{ + public String getId(); + public void setId(String id); + + public String getDescription(); + public void setDescription(String description); + + public String getApplyAfterVersion(); + public void setApplyAfterVersion(String version); + + public boolean getSucceeded(); + public void setSucceeded(boolean succeeded); + + public String getAppliedOnVersion(); + public void setAppliedOnVersion(String version); + + public Date getAppliedOnDate(); + public void setAppliedOnDate(Date date); + + public String getReport(); + public void setReport(String report); +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml new file mode 100644 index 0000000000..7ec8bfa9fe --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + select + appliedPatch + from + org.alfresco.repo.domain.hibernate.AppliedPatchImpl as appliedPatch + + + diff --git a/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java new file mode 100644 index 0000000000..837542d17b --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain.hibernate; + +import java.util.Date; + +import org.alfresco.repo.domain.AppliedPatch; + +/** + * Hibernate-specific implementation of the persistent object. + * + * @author Derek Hulley + */ +public class AppliedPatchImpl implements AppliedPatch +{ + private String id; + private String description; + private String applyAfterVersion; + private boolean succeeded; + private String appliedOnVersion; + private Date appliedOnDate; + private String report; + + public String getId() + { + return id; + } + public void setId(String id) + { + this.id = id; + } + + public String getDescription() + { + return description; + } + public void setDescription(String description) + { + if (description.length() > 1024) + { + // truncate as necessary + description = (description.substring(0, 1020) + "..."); + } + this.description = description; + } + + public String getAppliedOnVersion() + { + return appliedOnVersion; + } + public void setAppliedOnVersion(String appliedOnVersion) + { + this.appliedOnVersion = appliedOnVersion; + } + + public boolean getSucceeded() + { + return succeeded; + } + public void setSucceeded(boolean succeeded) + { + this.succeeded = succeeded; + } + + public String getApplyAfterVersion() + { + return applyAfterVersion; + } + public void setApplyAfterVersion(String applyAfterVersion) + { + this.applyAfterVersion = applyAfterVersion; + } + + public Date getAppliedOnDate() + { + return appliedOnDate; + } + public void setAppliedOnDate(Date appliedOnDate) + { + this.appliedOnDate = appliedOnDate; + } + + public String getReport() + { + return report; + } + public void setReport(String report) + { + if (report.length() > 1024) + { + // truncate as necessary + report = (report.substring(0, 1020) + "..."); + } + this.report = report; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java index cfee421637..5a5e5271f4 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java @@ -32,7 +32,6 @@ import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.Store; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; -import org.hibernate.mapping.Bag; /** * Bean containing all the persistence data representing a node. @@ -136,6 +135,7 @@ public class NodeImpl implements Node /** * For Hibernate use */ + @SuppressWarnings("unused") private void setAspects(Set aspects) { this.aspects = aspects; @@ -149,6 +149,7 @@ public class NodeImpl implements Node /** * For Hibernate use */ + @SuppressWarnings("unused") private void setSourceNodeAssocs(Collection sourceNodeAssocs) { this.sourceNodeAssocs = sourceNodeAssocs; @@ -162,6 +163,7 @@ public class NodeImpl implements Node /** * For Hibernate use */ + @SuppressWarnings("unused") private void setTargetNodeAssocs(Collection targetNodeAssocs) { this.targetNodeAssocs = targetNodeAssocs; @@ -175,6 +177,7 @@ public class NodeImpl implements Node /** * For Hibernate use */ + @SuppressWarnings("unused") private void setParentAssocs(Collection parentAssocs) { this.parentAssocs = parentAssocs; @@ -188,6 +191,7 @@ public class NodeImpl implements Node /** * For Hibernate use */ + @SuppressWarnings("unused") private void setChildAssocs(Collection childAssocs) { this.childAssocs = childAssocs; @@ -201,6 +205,7 @@ public class NodeImpl implements Node /** * For Hibernate use */ + @SuppressWarnings("unused") private void setProperties(Map properties) { this.properties = properties; diff --git a/source/java/org/alfresco/service/cmr/admin/AdminService.java b/source/java/org/alfresco/service/cmr/admin/AdminService.java new file mode 100644 index 0000000000..9085804e13 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/admin/AdminService.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service.cmr.admin; + +import java.util.List; + +/** + * General administration tasks and system information. + * + * @since 1.2 + * @author Derek Hulley + */ +public interface AdminService +{ + public List getPatches(); +} diff --git a/source/java/org/alfresco/service/cmr/admin/PatchException.java b/source/java/org/alfresco/service/cmr/admin/PatchException.java new file mode 100644 index 0000000000..bcf1f78a17 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/admin/PatchException.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service.cmr.admin; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Thrown when a patch fails to execute successfully. + * + * @author Derek Hulley + */ +public class PatchException extends AlfrescoRuntimeException +{ + private static final long serialVersionUID = 7022368915143884315L; + + private String report; + + /** + * @param report the patch failure report + */ + public PatchException(String report) + { + super(report); + } + + /** + * @return Returns the patch failure report + */ + public String getReport() + { + return report; + } +} diff --git a/source/java/org/alfresco/service/cmr/admin/PatchInfo.java b/source/java/org/alfresco/service/cmr/admin/PatchInfo.java new file mode 100644 index 0000000000..0309a4ef05 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/admin/PatchInfo.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service.cmr.admin; + +import java.io.Serializable; +import java.util.Date; + +import org.alfresco.repo.domain.AppliedPatch; + +/** + * Provides information regarding an individual patch. + * + * @since 1.2 + * @author Derek Hulley + */ +public class PatchInfo implements Serializable +{ + private static final long serialVersionUID = -8288217080763245510L; + + private String id; + private String description; + private String applyAfterVersion; + private boolean succeeded; + private String appliedOnVersion; + private Date appliedOnDate; + private String report; + + public PatchInfo(String id, String description, String applyAfterVersion) + { + this.id = id; + this.description = description; + this.applyAfterVersion = applyAfterVersion; + this.succeeded = false; + } + + public PatchInfo(AppliedPatch appliedPatch) + { + this.id = appliedPatch.getId(); + this.description = appliedPatch.getDescription(); + this.applyAfterVersion = appliedPatch.getApplyAfterVersion(); + + this.succeeded = appliedPatch.getSucceeded(); + this.appliedOnVersion = appliedPatch.getAppliedOnVersion(); + this.appliedOnDate = appliedPatch.getAppliedOnDate(); + this.report = appliedPatch.getReport(); + } + + /** + * @return Returns the unique patch identifier + */ + public String getId() + { + return id; + } + + /** + * @return Returns a description of the patch + */ + public String getDescription() + { + return description; + } + + /** + * @return Returns the version of the repository after which this patch must be applied + */ + public String getApplyAfterVersion() + { + return applyAfterVersion; + } + + /** + * @return Returns true if the patch has been successfully applied + */ + public boolean getSucceeded() + { + return succeeded; + } + + /** + * @return Returns the repository version that the patch was applied on, or null if the patch + * has not been applied + */ + public String getAppliedOnVersion() + { + return appliedOnVersion; + } + + /** + * @return Returns the date that the patch was applied, or null if the patch has not been applied + */ + public Date getAppliedOnDate() + { + return appliedOnDate; + } + + /** + * Get a report generated during the last attempted application. This will be an error report if + * the last attempt failed. If the application of the patch was successful + * ({@link #getAppliedOnDate() applied date} is not null) the it will be a message saying that it worked. + * + * @return Returns a report generated during application. This is only null if no attempt has been + * made to apply the patch. + */ + public String getReport() + { + return report; + } +}