From e6f160ca9f99e12f8f27be2fb8e6fa7a857d6cab Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Fri, 20 Jan 2006 17:53:05 +0000 Subject: [PATCH] First real patch: Saved Searches folder fix. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2167 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/patch/patch-services-context.xml | 39 +++- config/alfresco/version.properties | 2 +- .../repo/admin/patch/PatchExecuter.java | 61 +++++ .../repo/admin/patch/PatchService.java | 7 - .../repo/admin/patch/PatchServiceImpl.java | 4 + .../patch/impl/SavedSearchFolderPatch.java | 210 ++++++++++++++++++ .../alfresco/repo/domain/AppliedPatch.java | 3 + .../domain/hibernate/AppliedPatch.hbm.xml | 1 + .../domain/hibernate/AppliedPatchImpl.java | 12 + 9 files changed, 330 insertions(+), 9 deletions(-) create mode 100644 source/java/org/alfresco/repo/admin/patch/PatchExecuter.java create mode 100644 source/java/org/alfresco/repo/admin/patch/impl/SavedSearchFolderPatch.java diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 5745e01e95..a815f61fac 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -2,6 +2,16 @@ + + + + + + + @@ -44,7 +54,12 @@ - + + + + + + @@ -67,5 +82,27 @@ + + + + patch.savedSearchesFolder + Ensures the existence of the 'Saved Searches' folder + 0 + 1 + 2 + + + + + + + + + + + + + + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 3098f4b4e5..512aa3b749 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -15,4 +15,4 @@ version.edition=Open Source # Schema number -version.schema=1 +version.schema=2 diff --git a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java new file mode 100644 index 0000000000..a07bddd725 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java @@ -0,0 +1,61 @@ +/* + * 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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This component is responsible for ensuring that patches are applied + * at the appropriate time. + * + * @author Derek Hulley + */ +public class PatchExecuter +{ + private static Log logger = LogFactory.getLog(PatchExecuter.class); + + private PatchService patchService; + + /** + * @param patchService the server that actually executes the patches + */ + public void setPatchService(PatchService patchService) + { + this.patchService = patchService; + } + + /** + * Ensures that all outstanding patches are applied. + */ + public void applyOutStandingPatches() + { + /* + * TODO: This is simplistic at the moment. It must do better reporting of failures. + */ + + boolean success = patchService.applyOutstandingPatches(); + if (!success) + { + logger.error("Not all patches could be applied"); + } + else + { + logger.info("Patches applied successfully"); + } + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/PatchService.java b/source/java/org/alfresco/repo/admin/patch/PatchService.java index 15a4ec6cea..59f3e5e90e 100644 --- a/source/java/org/alfresco/repo/admin/patch/PatchService.java +++ b/source/java/org/alfresco/repo/admin/patch/PatchService.java @@ -38,13 +38,6 @@ public interface PatchService * 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. - *

- * Patches have a version, e.g. 1.1.1, which is the version of the repo - * after which the patch should be applied. If the repository version is 1.1.2 - * then the patch will not be applied as the repository was created with a newer version - * of the codebase, thereby rendering the patch unecessary. If the repository is at - * version 1.1 then the patch needs to be applied as the codebase of the repository - * is newer than the repository creation. * * @return Returns true if all outstanding patches were applied, or false if the process * was termintated before all patches could be applied. diff --git a/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java b/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java index b33d70e0dc..9d87134f1e 100644 --- a/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java +++ b/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java @@ -188,6 +188,9 @@ public class PatchServiceImpl implements PatchService success = false; } } + + Descriptor serverDescriptor = descriptorService.getServerDescriptor(); + String server = (serverDescriptor.getVersion() + " - " + serverDescriptor.getEdition()); // create a record for the execution appliedPatch = patchDaoService.newAppliedPatch(patch.getId()); @@ -197,6 +200,7 @@ public class PatchServiceImpl implements PatchService 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(success); // whether or not the patch succeeded appliedPatch.setReport(report); // additional, human-readable, status diff --git a/source/java/org/alfresco/repo/admin/patch/impl/SavedSearchFolderPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/SavedSearchFolderPatch.java new file mode 100644 index 0000000000..978a8507bb --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/SavedSearchFolderPatch.java @@ -0,0 +1,210 @@ +/* + * 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.impl; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.importer.ImporterBootstrap; +import org.alfresco.service.cmr.admin.PatchException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Ensures that the savedsearches folder is present. + *

+ * This uses the bootstrap importer to get the paths to look for. If not present, + * the required structures are created. + *

+ * This class should be replaced with a more generic ImporterPatch + * that can do conditional importing into given locations. + * + * @author Derek Hulley + */ +public class SavedSearchFolderPatch extends AbstractPatch +{ + private static final String MSG_EXISTS = "The saved searches folder already exists: %s."; + private static final String MSG_CREATED = "The saved searches folder successfully created: %s."; + + private static final String PROPERTY_COMPANY_HOME_CHILDNAME = "spaces.company_home.childname"; + private static final String PROPERTY_DICTIONARY_CHILDNAME = "spaces.dictionary.childname"; + private static final String PROPERTY_SAVED_SEARCHES_FOLDER_CHILDNAME = "spaces.savedsearches.childname"; + private static final String PROPERTY_SAVED_SEARCHES_FOLDER_NAME = "spaces.savedsearches.name"; + private static final String PROPERTY_SAVED_SEARCHES_FOLDER_DESCRIPTION = "spaces.savedsearches.description"; + private static final String PROPERTY_ICON = "space-icon-default"; + + private ImporterBootstrap importerBootstrap; + private NamespaceService namespaceService; + private SearchService searchService; + private NodeService nodeService; + + public void setImporterBootstrap(ImporterBootstrap importerBootstrap) + { + this.importerBootstrap = importerBootstrap; + } + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + @Override + protected String applyInternal() throws Exception + { + if (importerBootstrap == null) + { + throw new PatchException("'importerBootstrap' property has not been set"); + } + else if (namespaceService == null) + { + throw new PatchException("'namespaceService' property has not been set"); + } + else if (searchService == null) + { + throw new PatchException("'searchService' property has not been set"); + } + else if (nodeService == null) + { + throw new PatchException("'nodeService' property has not been set"); + } + + // get the node store that we must work against + StoreRef storeRef = importerBootstrap.getStoreRef(); + if (storeRef == null) + { + throw new PatchException("Bootstrap store has not been set"); + } + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + + Properties configuration = importerBootstrap.getConfiguration(); + // get the association names that form the path + String companyHomeChildName = configuration.getProperty(PROPERTY_COMPANY_HOME_CHILDNAME); + if (companyHomeChildName == null || companyHomeChildName.length() == 0) + { + throw new PatchException("Bootstrap property '" + PROPERTY_COMPANY_HOME_CHILDNAME + "' is not present"); + } + String dictionaryChildName = configuration.getProperty(PROPERTY_DICTIONARY_CHILDNAME); + if (dictionaryChildName == null || dictionaryChildName.length() == 0) + { + throw new PatchException("Bootstrap property '" + PROPERTY_DICTIONARY_CHILDNAME + "' is not present"); + } + String savedSearchesChildName = configuration.getProperty(PROPERTY_SAVED_SEARCHES_FOLDER_CHILDNAME); + if (savedSearchesChildName == null || savedSearchesChildName.length() == 0) + { + throw new PatchException("Bootstrap property '" + PROPERTY_SAVED_SEARCHES_FOLDER_CHILDNAME + "' is not present"); + } + + // build the search string to get the dictionary node + StringBuilder sb = new StringBuilder(512); + sb.append("/").append(companyHomeChildName) + .append("/").append(dictionaryChildName); + String xpath = sb.toString(); + // get the dictionary node + List nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false); + if (nodeRefs.size() == 0) + { + throw new PatchException("XPath didn't return any results: \n" + + " root: " + storeRootNodeRef + "\n" + + " xpath: " + xpath); + } + else if (nodeRefs.size() > 1) + { + throw new PatchException("XPath returned too many results: \n" + + " root: " + storeRootNodeRef + "\n" + + " xpath: " + xpath + "\n" + + " results: " + nodeRefs); + } + NodeRef dictionaryNodeRef = nodeRefs.get(0); + + // Now we have the optional part. Check for the existence of the saved searches folder + xpath = savedSearchesChildName; + nodeRefs = searchService.selectNodes(dictionaryNodeRef, xpath, null, namespaceService, false); + if (nodeRefs.size() > 1) + { + throw new PatchException("XPath returned too many results: \n" + + " dictionary node: " + dictionaryNodeRef + "\n" + + " xpath: " + xpath + "\n" + + " results: " + nodeRefs); + } + String msg = null; + if (nodeRefs.size() == 1) + { + // it already exists + msg = String.format(MSG_EXISTS, new Object[] {nodeRefs.get(0)}); + } + else + { + // create it + NodeRef savedSearchesFolderNodeRef = createFolder(dictionaryNodeRef, configuration); + msg = String.format(MSG_CREATED, new Object[] {savedSearchesFolderNodeRef}); + } + // done + return msg; + } + + private NodeRef createFolder(NodeRef dictionaryNodeRef, Properties configuration) + { + // get required properties + String savedSearchesChildName = configuration.getProperty( + PROPERTY_SAVED_SEARCHES_FOLDER_CHILDNAME, + "app:saved_searches"); + String savedSearchesName = configuration.getProperty( + PROPERTY_SAVED_SEARCHES_FOLDER_NAME, + "Saved Searches"); + String savedSearchesDescription = configuration.getProperty( + PROPERTY_SAVED_SEARCHES_FOLDER_DESCRIPTION, + "Saved searches"); + Map properties = new HashMap(7); + properties.put(ContentModel.PROP_NAME, savedSearchesName); + properties.put(ContentModel.PROP_TITLE, savedSearchesName); + properties.put(ContentModel.PROP_DESCRIPTION, savedSearchesDescription); + properties.put(ContentModel.PROP_ICON, PROPERTY_ICON); + // create the node + ChildAssociationRef childAssocRef = nodeService.createNode( + dictionaryNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.resolveToQName(namespaceService, savedSearchesChildName), + ContentModel.TYPE_FOLDER, + properties); + NodeRef nodeRef = childAssocRef.getChildRef(); + // add the required aspects + nodeService.addAspect(nodeRef, ContentModel.ASPECT_UIFACETS, null); + + // done + return nodeRef; + } +} diff --git a/source/java/org/alfresco/repo/domain/AppliedPatch.java b/source/java/org/alfresco/repo/domain/AppliedPatch.java index eaa3dcf583..a65623a97e 100644 --- a/source/java/org/alfresco/repo/domain/AppliedPatch.java +++ b/source/java/org/alfresco/repo/domain/AppliedPatch.java @@ -43,6 +43,9 @@ public interface AppliedPatch public int getAppliedToSchema(); public void setAppliedToSchema(int version); + public String getAppliedToServer(); + public void setAppliedToServer(String server); + public Date getAppliedOnDate(); public void setAppliedOnDate(Date date); diff --git a/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml index 6337ad1fcd..c18a655168 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml @@ -21,6 +21,7 @@ + diff --git a/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java index 64c7426c36..15c2d5cac3 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/AppliedPatchImpl.java @@ -34,6 +34,7 @@ public class AppliedPatchImpl implements AppliedPatch private int targetSchema; private int appliedToSchema; + private String appliedToServer; private Date appliedOnDate; private boolean succeeded; private String report; @@ -53,6 +54,7 @@ public class AppliedPatchImpl implements AppliedPatch .append(", fixesToSchema=").append(fixesToSchema) .append(", targetSchema=").append(targetSchema) .append(", appliedToSchema=").append(appliedToSchema) + .append(", appliedToServer=").append(appliedToServer) .append(", appliedOnDate=").append(appliedOnDate) .append(", succeeded=").append(succeeded) .append(", report=").append(report) @@ -119,6 +121,16 @@ public class AppliedPatchImpl implements AppliedPatch this.appliedToSchema = version; } + public String getAppliedToServer() + { + return appliedToServer; + } + + public void setAppliedToServer(String appliedToServer) + { + this.appliedToServer = appliedToServer; + } + public Date getAppliedOnDate() { return appliedOnDate;