Thursday, August 31, 2017

Alfresco 4 - Importing ACP with UUID Binding with existing "Import" functionality

One of my projects uses Alfresco 4.1, and I recently needed to import data from another environment and keep the folder and content node references intact while doing so.  These below articles were extremely helpful, but neither of them talked about overriding the existing "Import" functionality when inside Alfresco Explorer.

https://community.alfresco.com/thread/178902-acp-import-with-uuid-binding-specified
http://ecmarchitect.com/archives/2008/10/27/859


To preserve UUID binding during existing ACP import:
  1. Followed the steps from the first article above, creating a custom class  that extended the ImporterActionExecuter.  cherryshoe-alfresco-amp\src\main\java\com\cherryshoe\action\executer\CustomAcpImporterActionExecuter.java

    package com.cherryshoe.action.executer;
    
    import java.io.File;
    
    import org.alfresco.model.ContentModel;
    import org.alfresco.repo.action.executer.ImporterActionExecuter;
    import org.alfresco.repo.content.MimetypeMap;
    import org.alfresco.repo.importer.ACPImportPackageHandler;
    import org.alfresco.service.cmr.action.Action;
    import org.alfresco.service.cmr.repository.ContentReader;
    import org.alfresco.service.cmr.repository.ContentService;
    import org.alfresco.service.cmr.repository.NodeRef;
    import org.alfresco.service.cmr.repository.NodeService;
    import org.alfresco.service.cmr.view.ImporterBinding;
    import org.alfresco.service.cmr.view.ImporterService;
    import org.alfresco.service.cmr.view.Location;
    import org.alfresco.service.namespace.QName;
    import org.alfresco.util.TempFileProvider;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class CustomAcpImporterActionExecuter extends ImporterActionExecuter {
     private static final Log log = LogFactory.getLog(CustomAcpImporterActionExecuter.class);
     public static final String NAME = "custom-import";
     public static final String PARAM_ENCODING = "encoding";
     public static final String PARAM_DESTINATION_FOLDER = "destination";
     private static final String TEMP_FILE_PREFIX = "alf";
     private static final String TEMP_FILE_SUFFIX_ACP = ".acp";
     private ImporterBinding.UUID_BINDING uuidBinding;
     private ImporterService importerService;
     private NodeService nodeService;
     private ContentService contentService;
    
     public void setImporterService(ImporterService importerService) {
      this.importerService = importerService;
     }
    
     public void setNodeService(NodeService nodeService) {
      this.nodeService = nodeService;
     }
    
     public void setContentService(ContentService contentService) {
      this.contentService = contentService;
     }
    
     public CustomAcpImporterActionExecuter() {
      super();
      log.info(
        "*****************************CustomAcpImporterActionExecuter constructor*************************************");
     }
    
     /**
      * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.repo.ref.NodeRef,
      *      org.alfresco.repo.ref.NodeRef)
      */
     @Override
     public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) {
      log.info(
        "************************actionedUponNodeRef:**********************************" + actionedUponNodeRef);
      if (this.nodeService.exists(actionedUponNodeRef) == true) {
       // The node being passed in should be an Alfresco content package
       ContentReader reader = this.contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT);
       if (reader != null) {
        NodeRef importDest = (NodeRef) ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER);
        log.info("*******************************importDest:" + importDest + " actionedUponNodeRef:"
          + actionedUponNodeRef);
        if (MimetypeMap.MIMETYPE_ACP.equals(reader.getMimetype())) {
         // perform an import of an Alfresco ACP file (special format
         // ZIP structure)
         File zipFile = null;
         try {
          // unfortunately a ZIP file can not be read directly
          // from an input stream so we have to create
          // a temporary file first
          zipFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX_ACP);
          reader.getContent(zipFile);
    
          ACPImportPackageHandler importHandler = new ACPImportPackageHandler(zipFile,
            (String) ruleAction.getParameterValue(PARAM_ENCODING));
    
          log.debug(
            "************************************Starting import*****************************************");
          this.importerService.importView(importHandler, new Location(importDest), getBinding(), null);
          log.debug(
            "************************************Stopping import*****************************************");
         } finally {
          // now the import is done, delete the temporary file
          if (zipFile != null) {
           zipFile.delete();
          }
         }
        }
       }
      }
     }
    
     private ImporterBinding getBinding() {
      ImporterBinding binding = new ImporterBinding() {
       public boolean allowReferenceWithinTransaction() {
        return false;
       }
    
       public QName[] getExcludedClasses() {
        return null;
       }
    
       public UUID_BINDING getUUIDBinding() {
        return uuidBinding;
       }
    
       public String getValue(String key) {
        return null;
       }
      };
    
      // Uses UPDATE_EXISTING as usage of REPLACE_EXISTING removes child nodes
      // if these are not sent over in the export.
      uuidBinding = ImporterBinding.UUID_BINDING.UPDATE_EXISTING;
      return binding;
     }
    }
    
  2. Copied the existing alfresco\WEB-INF\classes\alfresco\action-services-context.xml file to my maven alfresco sdk folder cherryshoe-alfresco-amp\src\main\amp\config\alfresco\extension folder and named it action-services-cherryshoe-override-context.xml.  I removed everything in the file except the spring bean with id "import", and updated the class attribute with the package location of my custom class com.cherryshoe.action.executer.CustomAcpImporterActionExecuter.  The extension folder gets loaded after the out-of-the-box alfresco folders, so this should be overriding the WEB-INF\classes\alfresco\action-services-context.xml with just this one bean to use our custom one (If you know the classloading order of resources, Spring does allow overriding beans of the same id).
    cherryshoe-alfresco-amp\src\main\amp\config\alfresco\extension\action-services-cherryshoe-override-context.xml

    <?xml version='1.0' encoding='UTF-8'?>
    <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
    
    <beans>
    
     <!-- The extension folder gets loaded after the OOB alfresco folders, so this should be overriding the 
     WEB-INF\classes\alfresco\action-services-context.xml with just this one bean to use our custom one -->
        <bean id="import" class="com.cherryshoe.action.executer.CustomAcpImporterActionExecuter" parent="action-executer">
            <property name="importerService">
                <ref bean="ImporterService"/>
            </property>
            <property name="nodeService">
                <ref bean="NodeService"></ref>
            </property>
            <property name="contentService">
                <ref bean="ContentService" />
            </property>
            <property name="fileFolderService">
                <ref bean="FileFolderService"/>
            </property>
        </bean>
        
    </beans>
    
  3. Updated cherryshoe-alfresco-amp\src\test\resources\alfresco\extension\custom-log4j.properties with:
    # custom import action executer
    log4j.logger.com.cherryshoe.action.executor=info

  4. After navigating to <servername>:<port>/alfresco -> Folder to import ACP -> Import ACP -> then searching no a known node reference UUID using the Node Browser, I can see that the node was binded with the original UUID!
    1. NOTE: Since ACP files can be very large, using this default behavior could hang/fail, since the ACP first has to be uploaded, then imported.  Instead you can write a javascript script, implement a rule on a folder to call that script, and import an ACP that already exists on the repository (this article was helpful and worked great for me).

No comments:

Post a Comment

I appreciate your time in leaving a comment!