/* 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());
}
}
}