/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.activiti.workflow.simple.alfresco.export; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import org.activiti.bpmn.converter.BpmnXMLConverter; import org.activiti.bpmn.model.BpmnModel; import org.activiti.workflow.simple.alfresco.conversion.AlfrescoConversionUtil; import org.activiti.workflow.simple.alfresco.conversion.exception.AlfrescoSimpleWorkflowException; import org.activiti.workflow.simple.alfresco.model.M2Model; import org.activiti.workflow.simple.alfresco.model.beans.Bean; import org.activiti.workflow.simple.alfresco.model.beans.BeanProperty; import org.activiti.workflow.simple.alfresco.model.beans.BeanPropertyProp; import org.activiti.workflow.simple.alfresco.model.beans.Beans; import org.activiti.workflow.simple.alfresco.model.config.AlfrescoConfiguration; import org.activiti.workflow.simple.alfresco.model.config.Extension; import org.activiti.workflow.simple.alfresco.model.config.Module; import org.activiti.workflow.simple.alfresco.model.config.ModuleDeployment; import org.activiti.workflow.simple.converter.WorkflowDefinitionConversion; /** * Class capable of exporting Alfresco artifacts that are part of a * {@link WorkflowDefinitionConversion}, together with the required * configuration files in order to make the artifacts available in an Alfresco * installation. * * @author Frederik Heremans */ public class AlfrescoArtifactExporter { protected static final String PROCESS_FILE_SUFFIX = ".bpmn20.xml"; protected static final String MODEL_FILE_SUFFIX = "-model.xml"; protected static final String SPRING_CONTEXT_FILE_SUFFIX = "-context.xml"; protected static final String SHARE_CONFIG_FILE_SUFFIX = "-config-custom.xml"; protected static final String SHARE_CONFIG_MODULE_DEPLOYMENT_SUFFIX = "-module-deployment.xml"; protected static final String SHARE_CONTEXT_FILE_SUFFIX = "-share-context.xml"; protected static final String WORKFLOW_DEPLOYER_PARENT = "workflowDeployer"; protected static final String WORKFLOW_DEPLOYER_NAME_SUFFIX = ".workflowBootstrap"; protected static final String PROPERTY_MODELS = "models"; protected static final String PROPERTY_WORKFLOW_DEFINITIONS = "workflowDefinitions"; protected static final String PROP_KEY_ENGINE_ID = "engineId"; protected static final String PROP_KEY_LOCATION = "location"; protected static final String PROP_KEY_MIME_TYPE = "mimeType"; protected static final String PROP_KEY_REDEPLOY = "redeploy"; protected static final String PROP_ENGINE_ID = "activiti"; protected static final String PROP_MIME_TYPE = "text/xml"; protected static final String PROPERTY_CONFIGS = "configs"; protected static final String PROPERTY_CONFIG_SERVICE = "configService"; protected static final String PROPERTY_CONFIG_SERVICE_REF = "web.config"; protected static final String EXTENSIONS_PATH_PREFIX = "alfresco/extension/"; protected static final String WEB_EXTENSIONS_PATH_PREFIX = "classpath:alfresco/web-extension/"; protected static final String SHARE_CONFIG_BOOTSTRAP_BEAN_CLASS = "org.springframework.extensions.config.ConfigBootstrap"; protected static final String SHARE_CONFIG_BOOTSTRAP_BEAN_INIT_METHOD = "register"; protected BpmnXMLConverter bpmnConverter; protected JAXBContext modelJaxbContext; protected JAXBContext beansJaxbContext; protected JAXBContext moduleJaxbContext; public AlfrescoArtifactExporter() { // Initialize writers for artifacts bpmnConverter = new BpmnXMLConverter(); try { modelJaxbContext = JAXBContext.newInstance(M2Model.class); moduleJaxbContext = JAXBContext.newInstance(Extension.class, Module.class, AlfrescoConfiguration.class, ModuleDeployment.class); beansJaxbContext = JAXBContext.newInstance(Beans.class); } catch (JAXBException jaxbe) { throw new AlfrescoSimpleWorkflowException("Error while building JAXB-context for exporting content-model", jaxbe); } } /** * Export all artifacts and configuration-files needed to run the process that is converted in the given * {@link WorkflowDefinitionConversion}. * * @param conversion the conversion object to be exported * @param repositoryFolder the folder where all repository-artifacts and configurations are exported to * @param shareFolder the folder where all share-artifacts and configurations are exported to */ public void exportArtifacts(WorkflowDefinitionConversion conversion, File repositoryFolder, File shareFolder, boolean asExtension) { validateArtifactTargets(repositoryFolder, shareFolder); String processId = conversion.getProcess().getId(); try { // Export process BPMN File processFile = new File(repositoryFolder, getBpmnFileName(conversion)); processFile.createNewFile(); FileOutputStream processStream = new FileOutputStream(processFile); writeBpmnModel(processStream, conversion); processStream.close(); // Export content model File contentModelFile = new File(repositoryFolder, getContentModelFileName(conversion)); contentModelFile.createNewFile(); FileOutputStream modelStream = new FileOutputStream(contentModelFile); writeContentModel(modelStream, conversion); modelStream.close(); // Export workflow deployer context XML File springContextFile = new File(repositoryFolder, processId + SPRING_CONTEXT_FILE_SUFFIX); springContextFile.createNewFile(); FileOutputStream springContextStream = new FileOutputStream(springContextFile); writeWorkflowDeployerBean(springContextStream, processId, processFile.getName(), contentModelFile.getName()); springContextStream.close(); // Export share config File shareConfigFile = new File(shareFolder, getShareConfigFileName(conversion)); shareConfigFile.createNewFile(); FileOutputStream shareConfigStream = new FileOutputStream(shareConfigFile); writeShareConfig(shareConfigStream, conversion, asExtension); shareConfigStream.close(); if(asExtension) { File shareModuleDeploymentFile = new File(shareFolder, getShareModuleDeploymentFileName(conversion)); shareModuleDeploymentFile.createNewFile(); FileOutputStream shareModuleStream = new FileOutputStream(shareModuleDeploymentFile); writeShareExtensionModule(shareModuleStream, conversion); shareModuleStream.close(); } // Export share custom context File shareContextFile = new File(shareFolder, processId + SHARE_CONTEXT_FILE_SUFFIX); shareContextFile.createNewFile(); FileOutputStream shareContextStream = new FileOutputStream(shareContextFile); writeShareCustomConfigBean(shareContextStream, processId, shareConfigFile.getName()); shareContextStream.close(); } catch (IOException ioe) { throw new AlfrescoSimpleWorkflowException("Error while exporting artifacts", ioe); } } public String getBpmnFileName(WorkflowDefinitionConversion conversion) { String processId = conversion.getProcess().getId(); return processId + PROCESS_FILE_SUFFIX; } public String getContentModelFileName(WorkflowDefinitionConversion conversion) { String processId = conversion.getProcess().getId(); return processId + MODEL_FILE_SUFFIX; } public String getShareConfigFileName(WorkflowDefinitionConversion conversion) { String processId = conversion.getProcess().getId(); return processId + SHARE_CONFIG_FILE_SUFFIX; } public String getShareModuleDeploymentFileName(WorkflowDefinitionConversion conversion) { String processId = conversion.getProcess().getId(); return processId + SHARE_CONFIG_MODULE_DEPLOYMENT_SUFFIX; } /** * Write the BPMN-model in the given conversion to the given stream. */ public void writeBpmnModel(OutputStream out, WorkflowDefinitionConversion conversion) throws IOException { BpmnModel model = conversion.getBpmnModel(); byte[] xmlContent = bpmnConverter.convertToXML(model, "UTF-8"); out.write(xmlContent); } /** * Write the Share module XML in the given conversion to the given stream. */ public void writeShareConfig(OutputStream out, WorkflowDefinitionConversion conversion, boolean asExtension) throws IOException { Extension extension = AlfrescoConversionUtil.getExtension(conversion); try { Object toMarshall = extension; // In case the configuration should NOT be exported as a module, wrap the configurations // in a "alfresco-configuration" element instead if(!asExtension) { toMarshall = new AlfrescoConfiguration(); ((AlfrescoConfiguration) toMarshall).setConfigurations(extension.getModules().get(0).getConfigurations()); } Marshaller marshaller = moduleJaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(toMarshall, out); } catch (JAXBException jaxbe) { throw new IOException(jaxbe); } } /** * Write the Share module XML in the given conversion to the given stream. */ public void writeShareExtensionModule(OutputStream out, WorkflowDefinitionConversion conversion) throws IOException { Extension extension = AlfrescoConversionUtil.getExtension(conversion); try { ModuleDeployment toMarshall = new ModuleDeployment(); toMarshall.setModule(extension.getModules().get(0).getId()); // In case the configuration should NOT be exported as a module, wrap the configurations // in a "alfresco-configuration" element instead Marshaller marshaller = moduleJaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(toMarshall, out); } catch (JAXBException jaxbe) { throw new IOException(jaxbe); } } /** * Write the content model XML in the given conversion to the given stream. */ public void writeContentModel(OutputStream out, WorkflowDefinitionConversion conversion) throws IOException { M2Model model = AlfrescoConversionUtil.getContentModel(conversion); try { Marshaller marshaller = modelJaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(model, out); } catch (JAXBException jaxbe) { throw new IOException(jaxbe); } } protected void writeWorkflowDeployerBean(OutputStream out, String processId, String processFileName, String contentModelFileName) throws IOException { // Create root "beans" element Beans beans = new Beans(); Bean workflowDeployer = new Bean(processId + WORKFLOW_DEPLOYER_NAME_SUFFIX, WORKFLOW_DEPLOYER_PARENT); beans.getBeans().add(workflowDeployer); // Add pointer to model BeanProperty modelsProperty = new BeanProperty(PROPERTY_MODELS); modelsProperty.addListItem(EXTENSIONS_PATH_PREFIX + contentModelFileName); workflowDeployer.getProperties().add(modelsProperty); // Add pointer to process definition BeanProperty workflowProperty = new BeanProperty(PROPERTY_WORKFLOW_DEFINITIONS); workflowProperty.addProp(new BeanPropertyProp(PROP_KEY_ENGINE_ID, PROP_ENGINE_ID)); workflowProperty.addProp(new BeanPropertyProp(PROP_KEY_MIME_TYPE, PROP_MIME_TYPE)); workflowProperty.addProp(new BeanPropertyProp(PROP_KEY_LOCATION, EXTENSIONS_PATH_PREFIX + processFileName)); // TODO: set back to false or make configurable workflowProperty.addProp(new BeanPropertyProp(PROP_KEY_REDEPLOY, Boolean.TRUE.toString())); workflowDeployer.getProperties().add(workflowProperty); writeBeans(out, beans); } protected void writeShareCustomConfigBean(OutputStream out, String processId, String configFileName) throws IOException { Beans beans = new Beans(); Bean shareCustomConfigBean = new Bean(processId + WORKFLOW_DEPLOYER_NAME_SUFFIX, null); shareCustomConfigBean.setBeanClass(SHARE_CONFIG_BOOTSTRAP_BEAN_CLASS); shareCustomConfigBean.setInitMethod(SHARE_CONFIG_BOOTSTRAP_BEAN_INIT_METHOD); BeanProperty configsProperty = new BeanProperty(PROPERTY_CONFIGS); configsProperty.addListItem(WEB_EXTENSIONS_PATH_PREFIX + configFileName); BeanProperty configServiceProperty = new BeanProperty(PROPERTY_CONFIG_SERVICE); configServiceProperty.setRef(PROPERTY_CONFIG_SERVICE_REF); shareCustomConfigBean.getProperties().add(configServiceProperty); shareCustomConfigBean.getProperties().add(configsProperty); beans.getBeans().add(shareCustomConfigBean); writeBeans(out, beans); } protected void writeBeans(OutputStream out, Beans beans) throws IOException { try { Marshaller marshaller = beansJaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"); marshaller.marshal(beans, out); } catch (JAXBException jaxbe) { throw new IOException(jaxbe); } } protected void validateArtifactTargets(File repositoryFolder, File shareFolder) { if(repositoryFolder == null || shareFolder == null) { throw new AlfrescoSimpleWorkflowException("Both repositoryFolder and shareFolder are required."); } if(!repositoryFolder.exists()) { throw new AlfrescoSimpleWorkflowException("The repository target folder does not exist: " + repositoryFolder.getAbsolutePath()); } if(!shareFolder.exists()) { throw new AlfrescoSimpleWorkflowException("The share target folder does not exist: " + shareFolder.getAbsolutePath()); } } }