()
+ {
+ 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;
+ }
+}