AnsweredAssumed Answered

custom bootstrap code not running on startup

Question asked by cdn_alfresco_developer on Nov 4, 2015
Latest reply on Nov 10, 2015 by cdn_alfresco_developer
Platform: Alfresco 4.2.1 Enterprise Edition
OS: Windows 7
Database: Oracle Express: 11g

We are in the process of upgrading our CMS the enterprise edition. Our bootstrap code that runs once on startup is not running and I am having difficulty debugging it. (note: I have removed some of the code details for accounts and folder structure for security)

We are going to 4.2.4  so that we can because it still has the explorer interface which we have customised (removed functionality) quite a bit for our client. For the time being, they still want to use the customised version they are familiar with. Long term, we want to get them on the latest enterprise version.

I haven't been able to find a solution from the forum or a general web search as of you. Any help would be appreciated.



Here is our xml configuration (nhpols-bootstrap-context.xml). It resides in the /alfresco/extensions folder.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<!–
    NHP OnLine System Bootstrap Sequence.
   
    This file specifies the initialisation (and order of initialisation) to
    perform during system startup. The pattern for adding new initialisation to
    the bootstrap sequence is as follows:
   
    1) Develop a bean that implements the Spring interface ApplicationListener
    2) Place the initialisation logic in the method onApplicationEvent
       (ApplicationEvent event)…
   
       public void onApplicationEvent(ApplicationEvent event)
       {
          if (event instanceof ContextRefreshedEvent)
          {
             // initialisation logic here
          }
       }
      
    3) Add the bean definition to this file - Note: the beans are initialised
       in the order they are specified.
–>



<beans>
    <bean id="CMSBasePatch" class="ca.gc.hc.nhpd.repo.structure.patch.BasePatch"/>
    <bean id="CMSMinorPatch" class="ca.gc.hc.nhpd.repo.structure.patch.MinorPatch"/>

    <!– Need to do this first so the app version number can be determined –>
    <bean id="nhpolsStructureBootstrap" class="ca.gc.hc.nhpd.repo.structure.StructureBootstrap" >
        <property name="executeOnceOnly" value="false"/>
      <property name="patches">
        <set> <!– These will be run in the order they appear here –>
          <ref bean="CMSBasePatch"/>
          <ref bean="CMSMinorPatch"/>
        </set>
      </property>
    </bean>

    <bean id="nhpolsSchemaBootstrap" class="ca.gc.hc.nhpd.domain.schema.SchemaBootstrap" >
    </bean>
</beans>


Here is StructureBootstrap.java:

package ca.gc.hc.nhpd.repo.structure;

import ca.gc.hc.nhpd.configuration.NhpolsFiles;
import ca.gc.hc.nhpd.repo.NhpolsModel;
import ca.gc.hc.nhpd.repo.RepositoryHelper;
import ca.gc.hc.nhpd.repo.structure.patch.Patch;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import javax.jcr.Session;
import org.alfresco.service.cmr.action.ActionServiceException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;

/*******************************************************************************
* This is used to create/update the structure part of the application design
* that is persisted in the Alfresco repository. The items that it manages
* are: Categories, User Groups, the Application "home" Space structure and
* Space Templates.
* Note that error messages are sent to the log as well as directly to standard
* out, since the log doesn't seem to be displaying them in the console at this
* point.
*/
public class StructureBootstrap extends AbstractLifecycleBean {
    public static final DateFormat DATE_FORMATTER = new SimpleDateFormat(
                                                        "yyyy/MM/dd");
    private static final Log LOG = LogFactory.getLog(StructureBootstrap.class);
    private static final String NHPOLS_HOME_FOLDER_KEY = "nhpolsHome";
    private LinkedHashSet<Patch> patches; // Preserve the original order

    /***************************************************************************
     * Gets the system patches that have been registered.
     */
    public LinkedHashSet<Patch> getPatches() {
        return patches;
    }
   
    /***************************************************************************
     * Sets the patches to be applied to the system. Should only be called by
     * Swing injection.
     */
    public void setPatches(LinkedHashSet<Patch> aSet) {
        patches = aSet;
    }

    /***************************************************************************
     * Updates the existing system's version number and release date.
     */
    public void updateVersionInformation(String version, Date releaseDate) {
        RepositoryHelper helper = RepositoryHelper.instance();
        NodeService ns = helper.getNodeService();
       
        try {
            NodeRef home = NhpolsFiles.getPathAsNodeRef(NHPOLS_HOME_FOLDER_KEY);
   
            if (home != null) {
                Map<QName, Serializable> nodePropertiesMap =
                                         ns.getProperties(home);
               
                if (version != null) {
                    nodePropertiesMap.put(NhpolsModel.PROP_VERSION_NUMBER,
                                          version);
                    ns.removeProperty(home, NhpolsModel.PROP_VERSION_NUMBER);                   
                }
                if (releaseDate != null) {
                    nodePropertiesMap.put(NhpolsModel.PROP_RELEASE_DATE,
                                          releaseDate);
                    ns.removeProperty(home, NhpolsModel.PROP_RELEASE_DATE);
                }
                ns.setProperties(home, nodePropertiesMap);
            }

        } catch (ActionServiceException e) {
            LOG.error("Encountered a problem updating version information", e);
        }
    }
   
    /***************************************************************************
     * Callback for initialising Component on first startup of Alfresco Server
     * @param event
     */
    protected void onBootstrap(ApplicationEvent event) {
        long startTime = System.currentTimeMillis();
        RepositoryHelper helper = RepositoryHelper.instance();
        int patchCounter = 0;
        SimpleDateFormat timeFormatter = new SimpleDateFormat("mm:ss.SSS");

        LOG.info("Applying NHPOLS patches…");
        timeFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
        try {
            // Need to log into a session to access the repository:
            Session session = helper.getJcrSession();

            doPreSystemSetup(helper);

            // Patches are applied in the order that they appear in the Set
            for (Patch patch : patches) {
                if (patch.update(NhpolsModel.getNhpolsVersion())) {
                    LOG.info("Applied NHPOLS patch " + patch.getName());
                    patchCounter++;
                    if (patch.affectsVersion()) {
                        updateVersionInformation(patch.getVersion(),
                                                 patch.getReleaseDate());
                        NhpolsModel.resetNhpolsVersion();
                    }
                }
            }

            session.save(); // Ends the transaction
            session.logout();

            String elapsedTime = timeFormatter.format(new Date(
                                 System.currentTimeMillis() - startTime));
            LOG.info("Installed " + patchCounter + " patches in " + elapsedTime);
           
        } catch (Exception e) {
            //Not sure whether it was a JCR Problem
            LOG.error("StructureBootstrap failed ", e);
        }
    }
   
    /***************************************************************************
     * Callback for terminating Component on shutdown of Alfresco Server
     * @param event
     */
    protected void onShutdown(ApplicationEvent event) {
        // Nothing to do here…
    }
   
    /***************************************************************************
     * These are system setup steps that should be done before any patches are
     * applied. Some of the system setup requires administrative privileges, so
     * they have to be done here.
     */
    private void doPreSystemSetup(RepositoryHelper helper) {
        // Otherwise if a person (user) is asked for that doesn't exist, a new
        // one would be created automatically:
        helper.getPersonService().setCreateMissingPeople(true);
    }
}


BasePatch:

package ca.gc.hc.nhpd.repo.structure.patch;

import ca.gc.hc.nhpd.action.executer.CopyToFileSystemActionExecuter;
import ca.gc.hc.nhpd.action.executer.DeleteFromFileSystemActionExecuter;
import ca.gc.hc.nhpd.action.executer.ValidateSubmissionActionExecuter;
import ca.gc.hc.nhpd.configuration.NhpolsFiles;
import ca.gc.hc.nhpd.repo.NhpolsModel;
import ca.gc.hc.nhpd.repo.RepositoryHelper;
import ca.gc.hc.nhpd.repo.structure.StructureHelper;
import ca.gc.hc.nhpd.repo.structure.StructureSynchronizer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.action.executer.AddFeaturesActionExecuter;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/*******************************************************************************
* This is a patch used to install the basic NHPOLS structures.
* It only runs if no installed version number is detected, which indicates that
* the structure used to persist that information hasn't been created.
*/
public class BasePatch extends Patch {
    public static final String DOSSIER_ICON = "space-icon-doc";
    public static final String NHPOLS_HOME_DESCRIPTION = "System Home";
    public static final String NHPOLS_HOME_ICON = "space-icon-nhpols";
    // Keys to NhpolsFiles structures
    public static final String DATA_DICTIONARY_PATH_KEY = "dataDictionary";
//define dossiers and folders
…………
………..
.
    private static final String ALFRESCO_LANGUAGES_CATEGORY = "Languages";
    private static final String ALFRESCO_REGIONS_CATEGORY = "Regions";
    private static final String ALFRESCO_SOFTWARE_CLASSIFICATION_CATEGORY =
                                "Software Document Classification";
    private static final Log LOG = LogFactory.getLog(BasePatch.class);

    /***************************************************************************
     * Checks the passed version information and adds the required structures to
     * the system if required.
     * @param currentSystemVersion the version of the system before the patch
     *        is applied.
     * @return true if the system was updated, false if it was un-necessary.
     * @throws Exception if there was a problem.
     */
    @Override
    public boolean update(String currentSystemVersion) throws Exception {
       LOG.info("BasePatch: Begin");
        if ((currentSystemVersion == null) || (currentSystemVersion.equals(""))) {
           LOG.info("Creating Admin Account");
            createAdminAccount();
            deleteAlfrescoCategories();
            createCategories();
            createUserGroups();
            createApplicationHome();
            createDossierTemplate();
            createTestHome();
            //2009-12-01 BD: Removal of TPP Tables and TPP Home
            //createTPPHome();
            //This does NOT overwrite existing files, so that previous
            //configuration settings are not lost:
            StructureSynchronizer.instance().copySystemFilesToRepository(false,
                                                                         false);
            return true;
        }
        return false;
    }

    /***************************************************************************
     * This adds the system administrator user account to the system.
     */
    private void createAdminAccount() {
        PersonService personService = RepositoryHelper.instance()
                                      .getPersonService();
        StructureHelper sp = StructureHelper.instance();

        if (!personService.personExists("username")) {
            sp.addUser("username", "System", "username", "",
                       "password");
        }
    }

    /***************************************************************************
     * This deletes the default alfresco Categories from the system.
     */
    public void deleteAlfrescoCategories() {
        StructureHelper sHelper = StructureHelper.instance();
       
        try {
            sHelper.deleteCategory(ALFRESCO_LANGUAGES_CATEGORY);
            sHelper.deleteCategory(ALFRESCO_REGIONS_CATEGORY);
            sHelper.deleteCategory(ALFRESCO_SOFTWARE_CLASSIFICATION_CATEGORY);
       
        } catch (Exception e) {
            System.err.println("Problem deleting Alfresco Categories: " + e);
            LOG.error("Problem deleting Alfresco Categories: " + e);
        }
    }

    /***************************************************************************
     * This adds the Categories to the system.
     */
    private void createCategories() {
        NodeRef firstLevel; // reused
        //NodeRef secondLevel; // reused
        StructureHelper sp = StructureHelper.instance();
       
        try {
        //creat and
       
        } catch (Exception e) {
    }

    /***************************************************************************
     * This adds the UserGroups to the system.
     */
    private void createUserGroups() {
        StructureHelper sp = StructureHelper.instance();
             
        try {
      //create and add user groups
            System.err.println("Problem adding NHPOLS UserGroups: " + e);
            LOG.error("Problem adding NHPOLS UserGroups: " + e);
        }
    }

    /***************************************************************************
     * This creates the main application space structure.
     */
    private NodeRef createApplicationHome() {
        StructureHelper sp = StructureHelper.instance();
        RepositoryHelper helper = RepositoryHelper.instance();
        String spaceName = NhpolsFiles.getFolderName(NHPOLS_HOME_FOLDER_KEY);
        Map<QName, Serializable> additionalProperties =
                                 new HashMap<QName, Serializable>();
        List<QName> aspects = new ArrayList<QName>();
        Map<String, List<String>> permissions =
                                  new HashMap<String, List<String>>();
        List<String> adminPermissions = new ArrayList<String>();
        List<String> userPermissions = new ArrayList<String>();
       
        try {
            additionalProperties.put(NhpolsModel.PROP_VERSION_NUMBER,
                                     getVersion());
            additionalProperties.put(ApplicationModel.PROP_ICON,
                                     NHPOLS_HOME_ICON);
            additionalProperties.put(NhpolsModel.PROP_RELEASE_DATE,
                                     getReleaseDate());
   
            aspects.add(ApplicationModel.ASPECT_UIFACETS);
   
            adminPermissions.add(PermissionService.COORDINATOR);
            LOG.info("Assigning permissions to users to the home node");
            permissions.put(RepositoryHelper.ADMIN_ACCOUNT, adminPermissions);
            userPermissions.add(PermissionService.CONSUMER);
            permissions.put(NhpolsModel.LNAME_NHPOLS_USERS, userPermissions);
            NodeRef home = sp.addContentNode(helper.getCompanyHomeRef(),
                spaceName, spaceName, NHPOLS_HOME_DESCRIPTION,
                NhpolsModel.TYPE_NHPOLS_HOME, additionalProperties, aspects,
                permissions, false);

            if (home != null) {
                createInternalDocumentsSpace(home, sp);
                createSystemFilesSpace(home, sp);
                for (int i=1; i<=6; i++) {
                    createDropBoxSpace(i, home, sp);
                }
                createSubmissionsSpace(home, sp);
            }
            return home;
           
        } catch (Exception e) {
            System.err.println("Problem creating NHPOLS ApplicationHome: " + e);
            LOG.error("Problem creating NHPOLS ApplicationHome: " + e);
        }
        return null;
    }

    /***************************************************************************
     * This creates the test documents space structure.
     */
    private NodeRef createTestHome() {
        NodeRef dataDictionaryRef;
        StructureHelper sp = StructureHelper.instance();
        String spaceName = NhpolsFiles.getFolderName(TEST_DOCUMENTS_FOLDER_KEY);
        Map<String, List<String>> permissions =
                                  new HashMap<String, List<String>>();
        List<String> adminPermissions = new ArrayList<String>();
       
        try {   
            dataDictionaryRef = NhpolsFiles.getPathAsNodeRef(
                                DATA_DICTIONARY_PATH_KEY);
           
            adminPermissions.add(PermissionService.COORDINATOR);
            permissions.put(RepositoryHelper.ADMIN_ACCOUNT, adminPermissions);
       
            NodeRef home = sp.addContentNode(dataDictionaryRef, spaceName,
                spaceName, spaceName, ContentModel.TYPE_FOLDER,
                null, null, permissions, false);
            return home;
           
        } catch (RepositoryException e) {
            System.err.println("Problem creating Test Home Space: " + e);
            LOG.error("Problem creating Test Home Space: " + e);
        }
        return null;
    }

    /***************************************************************************
     * This creates the template for the SubmissionDossier Space.
     */
    private NodeRef createDossierTemplate() {
        StructureHelper sp = StructureHelper.instance();
        String spaceName = NhpolsFiles.getFolderName(PLA_DOSSIER_TEMPLATE_FOLDER_KEY);
        Map<QName, Serializable> additionalProperties =
                                 new HashMap<QName, Serializable>();
        List<String> adminPermissions = new ArrayList<String>();
        Map<String, List<String>> permissions =
                                  new HashMap<String, List<String>>();

        additionalProperties.put(ApplicationModel.PROP_ICON, DOSSIER_ICON);
        adminPermissions.add(PermissionService.COORDINATOR);
        permissions.put(NhpolsModel.LNAME_SYS_ADMIN, adminPermissions);
        permissions.put(NhpolsModel.LNAME_SUBMISSION_ADMIN_OFFICERS, adminPermissions);

        try {
            NodeRef dossier = sp.addContentNode(NhpolsFiles.getPathAsNodeRef(
                                                SPACE_TEMPLATES_PATH_KEY),
                    spaceName, spaceName, spaceName, NhpolsModel.TYPE_PLA_DOSSIER,
                    additionalProperties, null, permissions, true);

            if (dossier != null) {
                // Correspondence General
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_CORRESPONDENCE_GENERAL_FOLDER_KEY));
                // Correspondence Assessment
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_CORRESPONDENCE_ASSESSMENT_FOLDER_KEY));
                // ePLA & label
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_EPLA_AND_LABEL_FOLDER_KEY));
                // S&E Evidence
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_S_AND_E_EVIDENCE_FOLDER_KEY));
                // Evidence Summary
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_EVIDENCE_SUMMARY_FOLDER_KEY));
                // Assessment
                NodeRef assessmentNode = createGenericSpace(dossier, sp,
                        NhpolsFiles.getFolderName(PLA_DOSSIER_ASSESSMENT_FOLDER_KEY));
                // Assessment / SEAR
                createGenericSpace(assessmentNode, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_ASSESSMENT_SEAR_FOLDER_KEY));
               
                // Assessment / QAR
                createGenericSpace(assessmentNode, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_ASSESSMENT_QAR_FOLDER_KEY));
               
                // Quality Evidence
                NodeRef qualityEvidenceNode = createGenericSpace(dossier, sp,
                        NhpolsFiles.getFolderName(PLA_DOSSIER_QUALITY_EVIDENCE_FOLDER_KEY));
               
                // Quality Evidence / ATF(s)
                createGenericSpace(qualityEvidenceNode, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_ATF_FOLDER_KEY));
               
                // Decision
                NodeRef decisionNode = createGenericSpace(dossier, sp,
                        NhpolsFiles.getFolderName(PLA_DOSSIER_DECISION_FOLDER_KEY));
               
                // Decision / Reconsideration
                createGenericSpace(decisionNode, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_RECONSIDERATION_FOLDER_KEY));
               
                // Post License
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_POST_LICENCE_FOLDER_KEY));
               
                // Original Submission
                createGenericSpace(dossier, sp, NhpolsFiles.getFolderName(
                                   PLA_DOSSIER_ORIGINAL_SUBMISSION_FOLDER_KEY));
               
            }
            return dossier;
           
        } catch (Exception e) {
            System.err.println("Problem creating SubmissionDossier template: " + e);
            LOG.error("Problem creating SubmissionDossier template: " + e);
        }
        return null;
    }
   
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    /***************************************************************************
     * This creates the DropBox spaces under the Application Home Space.
     */
    private NodeRef createDropBoxSpace(int index, NodeRef parent,
                    StructureHelper sp) throws RepositoryException {
        String spaceName = NhpolsFiles.getFolderName(DROP_BOX_FOLDER_KEY)
                           + index;
        Map<String, List<String>> permissions =
                                  new HashMap<String, List<String>>();
        List<String> coordPermissions = new ArrayList<String>();
        NodeRef dropBox;

        coordPermissions.add(PermissionService.COORDINATOR);
        permissions.put(NhpolsModel.LNAME_SYS_ADMIN, coordPermissions);
   
        dropBox = sp.addContentNode(parent, spaceName, spaceName, spaceName,
                  NhpolsModel.TYPE_DROP_BOX, null, null, permissions, false);
        addValidateSubmissionRule(dropBox, sp);
       
        return dropBox;
    }

    /***************************************************************************
     * This adds the ValidateSubmission action as a Business Rule to the passed
     * node (a DropBox).
     */
    private void addValidateSubmissionRule(NodeRef dropBox, StructureHelper sp) {
        ActionService as = RepositoryHelper.instance().getActionService();
        List<ActionCondition> conditions = new ArrayList<ActionCondition>();
        Map<String, Serializable> conditionParameters =
                                  new HashMap<String, Serializable>();
        Action validateAction = as.createAction(
                                   ValidateSubmissionActionExecuter.NAME);
       
        conditionParameters.put(ComparePropertyValueEvaluator.PARAM_VALUE,
                                "*.hcs");
        conditions.add(as.createActionCondition(
                   ComparePropertyValueEvaluator.NAME, conditionParameters));
        sp.addRule(dropBox, "Validate Submission", "Validate Submission",
                   RuleType.INBOUND, conditions, validateAction);
    }

    /***************************************************************************
     * This creates the InternalDocuments space under the Application Home Space.
     */
    private NodeRef createInternalDocumentsSpace(NodeRef parent,
                           StructureHelper sp) throws RepositoryException {
        String spaceName = NhpolsFiles.getFolderName(
                           INTERNAL_DOCUMENTS_FOLDER_KEY);
        NodeRef internalDocuments;
        Map<String, List<String>> permissions =
                                  new HashMap<String, List<String>>();
        List<String> coordPermissions = new ArrayList<String>();

        coordPermissions.add(PermissionService.COORDINATOR);
        permissions.put(NhpolsModel.LNAME_SYS_ADMIN, coordPermissions);

        internalDocuments = sp.addContentNode(parent, spaceName, spaceName,
                            spaceName, ContentModel.TYPE_FOLDER, null, null,
                            permissions, true);

        addVersionableRule(internalDocuments, sp);
   
        return internalDocuments;
    }

    /***************************************************************************
     * This creates the Submissions space under the Application Home Space.
     */
    private NodeRef createSubmissionsSpace(NodeRef parent, StructureHelper sp)
                                           throws RepositoryException {
        String spaceName = NhpolsFiles.getFolderName(SUBMISSIONS_FOLDER_KEY);
        NodeRef submissionsSpace;
        Map<String, List<String>> permissions =
            new HashMap<String, List<String>>();
        List<String> collabPermissions = new ArrayList<String>();
        List<String> adminPermissions = new ArrayList<String>();
       
        //2009-10-26 Bruce Dempsey: set the permissions for the group
        //NHPOnLineSystemUsers to collaborator on the parent submission folder
        collabPermissions.add(RepositoryHelper.COLLABORATOR);
        adminPermissions.add(PermissionService.COORDINATOR);
        permissions.put(RepositoryHelper.ADMIN_ACCOUNT, adminPermissions);
        permissions.put(NhpolsModel.LNAME_NHPOLS_USERS, collabPermissions );
        //        PermissionService.CONSUMER);
      /*  permissions.put(NhpolsModel.LNAME_QUEUE_TEAM_MEMBERS,
                        coordPermissions);
        permissions.put(NhpolsModel.LNAME_QUEUE_TEAM_MASTERS,
                        coordPermissions);
        permissions.put(NhpolsModel.LNAME_SUBMISSION_ADMIN_OFFICERS,
                        coordPermissions);
        */
       
        submissionsSpace = sp.addContentNode(parent, spaceName, spaceName,
                           spaceName, ContentModel.TYPE_FOLDER, null, null,
                           permissions, false);
       
        addVersionableRule(submissionsSpace, sp);

        return submissionsSpace;
    }

    /***************************************************************************
     * This adds the versionable AddFeatures action as a Business Rule to the
     * passed node (internalDocuments and submissions).
     */
    private void addVersionableRule(NodeRef node,
                                    StructureHelper sp) {
        List<Action> actions = new ArrayList<Action>();
        ActionService as = RepositoryHelper.instance().getActionService();
        List<ActionCondition> conditions = new ArrayList<ActionCondition>();
        List<String> types = new ArrayList<String>();
        Action addFeaturesAction = as.createAction(
                                      AddFeaturesActionExecuter.NAME);

        conditions.add(as.createActionCondition(NoConditionEvaluator.NAME));
        addFeaturesAction.setParameterValue(
                          AddFeaturesActionExecuter.PARAM_ASPECT_NAME,
                          ContentModel.ASPECT_VERSIONABLE);
        actions.add(addFeaturesAction);
        types.add(RuleType.INBOUND);

        sp.addRule(node, "Make all files Versionable",
                   "Make all files Versionable", types, conditions, actions,
                   false, true, false);
    }

    /***************************************************************************
     * This creates the System Files space under the Application Home Space.
     */
    private NodeRef createSystemFilesSpace(NodeRef parent, StructureHelper sp)
                                           throws RepositoryException {
        String spaceName = NhpolsFiles.getFolderName(SYSTEM_FILES_FOLDER_KEY);
        Map<String, List<String>> permissions =
                                  new HashMap<String, List<String>>();
        List<String> coordPermissions = new ArrayList<String>();
        NodeRef systemFiles;

        coordPermissions.add(PermissionService.COORDINATOR);
        permissions.put(NhpolsModel.LNAME_SYS_ADMIN, coordPermissions);
   
        systemFiles = sp.addContentNode(parent, spaceName, spaceName, spaceName,
                      ContentModel.TYPE_FOLDER, null, null, permissions, true);
        addSystemFilesRules(systemFiles, sp);
       
        return systemFiles;
    }

    /***************************************************************************
     * This adds the actions that sync this directory with the file system, as
     * well as one that makes its content versionable, all as Business Rules to
     * the passed node (systemFiles).
     */
    private void addSystemFilesRules(NodeRef systemFiles, StructureHelper sp) {
        List<Action> actions = new ArrayList<Action>();
        ActionService as = RepositoryHelper.instance().getActionService();
        List<ActionCondition> conditions = new ArrayList<ActionCondition>();
        List<String> types = new ArrayList<String>();

        Action addFeaturesAction = as.createAction(
                                      AddFeaturesActionExecuter.NAME);
        addFeaturesAction.setParameterValue(
                          AddFeaturesActionExecuter.PARAM_ASPECT_NAME,
                          ContentModel.ASPECT_VERSIONABLE);

        // Note that each rule needs its own unique set of collections
        actions.add(addFeaturesAction);
        conditions.add(as.createActionCondition(NoConditionEvaluator.NAME));
        types.add(RuleType.INBOUND);
        sp.addRule(systemFiles, "Make all files Versionable",
                   "Make all files Versionable", types, conditions, actions,
                   false, true, false);

        actions = new ArrayList<Action>();
        actions.add(as.createAction(CopyToFileSystemActionExecuter.NAME));
        conditions = new ArrayList<ActionCondition>();
        conditions.add(as.createActionCondition(NoConditionEvaluator.NAME));
        types = new ArrayList<String>();
        types.add(RuleType.UPDATE); // Also appears to do inbound
        sp.addRule(systemFiles, "Update files in File System",
                   "Update files in File System", types, conditions,
                   actions, false, true, false);

        actions = new ArrayList<Action>();
        actions.add(as.createAction(DeleteFromFileSystemActionExecuter.NAME));
        conditions = new ArrayList<ActionCondition>();
        conditions.add(as.createActionCondition(NoConditionEvaluator.NAME));
        types = new ArrayList<String>();
        types.add(RuleType.OUTBOUND);
        sp.addRule(systemFiles, "Remove existing files from File System",
                   "Remove existing files from File System", types, conditions,
                   actions, false, true, false);
    }

    /***************************************************************************
     * This creates a spaces under the passed parent.
     */
    private NodeRef createGenericSpace(NodeRef parentSpace, StructureHelper sp,
                                       String name) throws RepositoryException {
        return sp.addContentNode(parentSpace, name, name, name,
            ContentModel.TYPE_FOLDER, null, null, null, true);
    }
}


MinorPatch:

package ca.gc.hc.nhpd.repo.structure.patch;

import ca.gc.hc.nhpd.repo.structure.StructureSynchronizer;
import ca.gc.hc.nhpd.util.ApplicationDetails;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/*******************************************************************************
* This is a generic minor patch used on all updates that don't require specific
* structure upgrades. It runs if the installed version number is different than
* the version number of this release.
*
* This updates the version number and date as well as all system (configuration)
* files that are in the deployment War.
*/
public class MinorPatch extends Patch {
    private static final Log LOG = LogFactory.getLog(MinorPatch.class);
              
    /***************************************************************************
     * Gets the system release date that this patch will change the system to.
     * @return the system release date that this patch will change the system to.
     */
    @Override
    public Date getReleaseDate() {
        return ApplicationDetails.instance().getApplicationBuildDate();
    }

    /***************************************************************************
     * Gets the system version that this patch will change the system to.
     * @return the system version that this patch will change the system to.
     */
    @Override
    public String getVersion() {
        return ApplicationDetails.instance().getApplicationVersion();
    }
   
    /***************************************************************************
     * Checks the passed version information and updates the system if required.
     * @param currentSystemVersion the version of the system before the patch
     *        is applied.
     * @return true if the system was updated, false if it was un-necessary.
     * @throws Exception if there was a problem.
     */
    @Override
    public boolean update(String currentSystemVersion) throws Exception {
        if (currentSystemVersion == null
            || !currentSystemVersion.equals(getVersion())) {
            StructureSynchronizer.instance().copySystemFilesToRepository(true,
                                                                         true);
            return true;
        }
        return false;
    }
}


Patch:

package ca.gc.hc.nhpd.repo.structure.patch;

import ca.gc.hc.nhpd.configuration.NhpolsFiles;
import ca.gc.hc.nhpd.repo.RepositoryHelper;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/*******************************************************************************
* A base class for system patches.
*/
public abstract class Patch {
    private static final Log LOG = LogFactory.getLog(Patch.class);
    // Keys to NhpolsFiles structures
    public static final String DROP_BOX_PATH_KEY = "dropBox";

    /***************************************************************************
     * Checks the passed version information and updates the system if required.
     * @param currentSystemVersion the version of the system before the patch
     *        is applied.
     * @return true if the system was updated, false if it was un-necessary.
     * @throws Exception if there was a problem.
     */
    public abstract boolean update(String currentSystemVersion) throws Exception;

    /***************************************************************************
     * Gets whether this patch will change the system version.
     * @return true if this patch will change the system version.
     */
    public boolean affectsVersion() {
        return getVersion() != null;
    }

    /***************************************************************************
     * Gets the name that should be displayed in the log when this patch is
     * applied.
     * @return this patch's display name.
     *         This default implementation returns the version. If the version
     *         is null, it returns the class name.
     */
    public String getName() {
        if (getVersion() != null) {
            return getVersion();
        }
        return getClass().getSimpleName();
    }

    /***************************************************************************
     * Gets the system release date that this patch will change the system to.
     * @return the system release date that this patch will change the system to.
     *         Null if this does not affect the version.
     *         This default implementation returns Null.
     */
    public Date getReleaseDate() {
        return null;
    }

    /***************************************************************************
     * Gets the system version that this patch will change the system to.
     * @return the system version that this patch will change the system to.
     *         Null if this does not affect the version.
     *         This default implementation returns Null.
     */
    public String getVersion() {
        return null;
    }

    /***************************************************************************
     * Gives Coordinator permissions for the Drop Box with the passed index
     * (they are numbered) to the passed user account.
     */
    protected void makeUserDropBoxCoordinator(String account, int index) {
        NodeRef dropBox = getDropBox(index);
        PermissionService permService = RepositoryHelper.instance()
                                        .getPermissionService();
       
        if (dropBox != null) {
            permService.setPermission(dropBox, account,
                                      PermissionService.COORDINATOR, true);
        }
    }

    /***************************************************************************
     * Get the drop box with the passed index (they are numbered). Returns null
     * if there was a problem.
     */
    private NodeRef getDropBox(int index) {
        FileFolderService fileFolderService = RepositoryHelper.instance()
                                              .getFileFolderService();
       
        List<String> namePath = NhpolsFiles.getPathAsList(DROP_BOX_PATH_KEY);

        try {
            NodeRef companyHome = RepositoryHelper.instance().getCompanyHomeRef();

            if (namePath == null || namePath.isEmpty()) {
                throw new Exception("NhpolsFiles entry not found.");
            }

            // The folder name doesn't include the index:
            namePath = new ArrayList<String>(namePath); // Make it mutable
            String folderName = namePath.remove(namePath.size() - 1);
            namePath.add(folderName + index);
           
            return fileFolderService.resolveNamePath(companyHome, namePath)
                   .getNodeRef();
           
        } catch (Exception e) {
            System.err.println("Couldn't get DropBox" + index + ": " + e);
            LOG.error("Couldn't get DropBox" + index + ": ", e);
        }
        return null;
    }
}

Outcomes