/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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.wso2.carbon.identity.workflow.impl;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.bpel.stub.upload.types.UploadedFileItem;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.workflow.impl.bean.BPSProfile;
import org.wso2.carbon.identity.workflow.impl.internal.WorkflowImplServiceDataHolder;
import org.wso2.carbon.identity.workflow.impl.util.WorkflowDeployerClient;
import org.wso2.carbon.identity.workflow.mgt.bean.Parameter;
import org.wso2.carbon.identity.workflow.mgt.exception.WorkflowRuntimeException;
import org.wso2.carbon.identity.workflow.mgt.util.WFConstant;
import org.wso2.carbon.identity.workflow.mgt.util.WorkflowManagementUtil;
import org.wso2.carbon.identity.workflow.mgt.workflow.TemplateInitializer;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class BPELDeployer implements TemplateInitializer {
private static Log log = LogFactory.getLog(BPELDeployer.class);
private static final String CLASSPATH_SEPARATOR = "/";
private BPSProfile bpsProfile = null;
private String processName;
private String htName;
private String role;
private String tenantContext = "" ;
@Override
public boolean initNeededAtStartUp() {
return false;
}
@Override
public void initialize(List<Parameter> parameterList) throws WorkflowImplException {
if (!validateParams(parameterList)) {
throw new WorkflowRuntimeException("Workflow initialization failed, required parameter is missing");
}
Parameter wfNameParameter = WorkflowManagementUtil
.getParameter(parameterList, WFConstant.ParameterName.WORKFLOW_NAME, WFConstant.ParameterHolder
.WORKFLOW_IMPL);
if (wfNameParameter != null) {
processName = StringUtils.deleteWhitespace(wfNameParameter.getParamValue());
role = WorkflowManagementUtil
.createWorkflowRoleName(StringUtils.deleteWhitespace(wfNameParameter.getParamValue()));
}
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain();
if(!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)){
tenantContext = "t/" + tenantDomain + "/";
}
Parameter bpsProfileParameter = WorkflowManagementUtil
.getParameter(parameterList, WFImplConstant.ParameterName.BPS_PROFILE, WFConstant.ParameterHolder
.WORKFLOW_IMPL);
if (bpsProfileParameter != null) {
String bpsProfileName = bpsProfileParameter.getParamValue();
bpsProfile = WorkflowImplServiceDataHolder.getInstance().getWorkflowImplService().getBPSProfile
(bpsProfileName, tenantId);
}
htName = processName + BPELDeployer.Constants.HT_SUFFIX;
generateAndDeployArtifacts();
}
private boolean validateParams(List<Parameter> parameterList) {
//todo: implement
return true;
}
/**
* Generate and deploy artifacts
*
* @throws WorkflowImplException
*/
public void generateAndDeployArtifacts() throws WorkflowImplException {
try {
generateProcessArtifact();
generateHTArtifact();
} catch (IOException e) {
throw new WorkflowImplException("Error when generating process artifacts");
}
try {
deployArtifacts();
} catch (RemoteException e) {
throw new WorkflowRuntimeException("Error occurred when deploying the BPEL");
}
}
private void deployArtifacts() throws RemoteException {
String bpelArchiveName = processName + BPELDeployer.Constants.ZIP_EXT;
String archiveHome = System.getProperty(BPELDeployer.Constants.TEMP_DIR_PROPERTY) + File.separator;
DataSource bpelDataSource = new FileDataSource(archiveHome + bpelArchiveName);
WorkflowDeployerClient workflowDeployerClient;
if (bpsProfile.getProfileName().equals(WFImplConstant.DEFAULT_BPS_PROFILE_NAME)) {
workflowDeployerClient = new WorkflowDeployerClient(bpsProfile.getManagerHostURL(),
bpsProfile.getUsername());
} else {
workflowDeployerClient = new WorkflowDeployerClient(bpsProfile.getManagerHostURL(),
bpsProfile.getUsername(), bpsProfile.getPassword());
}
workflowDeployerClient.uploadBPEL(getBPELUploadedFileItem(new DataHandler(bpelDataSource),
bpelArchiveName, BPELDeployer.Constants.ZIP_TYPE));
String htArchiveName = htName + BPELDeployer.Constants.ZIP_EXT;
DataSource htDataSource = new FileDataSource(archiveHome + htArchiveName);
workflowDeployerClient.uploadHumanTask(getHTUploadedFileItem(new DataHandler(htDataSource), htArchiveName,
BPELDeployer.Constants.ZIP_TYPE));
}
private UploadedFileItem[] getBPELUploadedFileItem(DataHandler dataHandler, String fileName,
String fileType) {
UploadedFileItem[] uploadedFileItems = new UploadedFileItem[1];
UploadedFileItem uploadedFileItem = new UploadedFileItem();
uploadedFileItem.setDataHandler(dataHandler);
uploadedFileItem.setFileName(fileName);
uploadedFileItem.setFileType(fileType);
uploadedFileItems[0] = uploadedFileItem;
return uploadedFileItems;
}
private org.wso2.carbon.humantask.stub.upload.types.UploadedFileItem[] getHTUploadedFileItem(
DataHandler dataHandler,
String fileName,
String fileType) {
org.wso2.carbon.humantask.stub.upload.types.UploadedFileItem[] uploadedFileItems = new org.wso2.carbon
.humantask.stub.upload.types.UploadedFileItem[1];
org.wso2.carbon.humantask.stub.upload.types.UploadedFileItem uploadedFileItem =
new org.wso2.carbon.humantask.stub.upload.types.UploadedFileItem();
uploadedFileItem.setDataHandler(dataHandler);
uploadedFileItem.setFileName(fileName);
uploadedFileItem.setFileType(fileType);
uploadedFileItems[0] = uploadedFileItem;
return uploadedFileItems;
}
private Map<String, String> getPlaceHolderValues() {
Map<String, String> placeHolderValues = new HashMap<>();
placeHolderValues.put(BPELDeployer.Constants.BPEL_PROCESS_NAME, processName);
placeHolderValues.put(BPELDeployer.Constants.HT_SERVICE_NAME, htName);
String url = bpsProfile.getWorkerHostURL() != null ? bpsProfile.getWorkerHostURL() : "" ;
if (url.endsWith("/")) {
url = url.substring(0,url.lastIndexOf("/"));
}
placeHolderValues.put(BPELDeployer.Constants.BPS_HOST_NAME, url);
placeHolderValues.put(Constants.URL_TENANT_CONTEXT, tenantContext);
placeHolderValues.put(BPELDeployer.Constants.CARBON_HOST_NAME, IdentityUtil.getServerURL("", true, true));
placeHolderValues.put(BPELDeployer.Constants.HT_OWNER_ROLE, role);
placeHolderValues.put(BPELDeployer.Constants.HT_ADMIN_ROLE, role);
return placeHolderValues;
}
private void removePlaceHolders(String relativeFilePath, String destination) throws IOException {
InputStream inputStream = getClass().getResourceAsStream(CLASSPATH_SEPARATOR + relativeFilePath);
String content = IOUtils.toString(inputStream);
for (Map.Entry<String, String> placeHolderEntry : getPlaceHolderValues().entrySet()) {
content = content.replaceAll(Pattern.quote(placeHolderEntry.getKey()), Matcher.quoteReplacement
(placeHolderEntry.getValue()));
}
File destinationParent = new File(destination).getParentFile();
if (!destinationParent.exists()) {
destinationParent.mkdirs();
}
FileOutputStream fileOutputStream = new FileOutputStream(new File(destination), false);
IOUtils.write(content, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
inputStream.close();
}
private void generateProcessArtifact() throws IOException {
Set<String> filesToAdd = new HashSet<>();
String taskWsdl, ws_humantask = null; //to keep without deleting for human task
//Not using File.separator because this will be loaded from java class path
//Java class path expects the "/" character as the separator
String resourceHomePath =
BPELDeployer.Constants.TEMPLATE_RESOURCE_LOCATION + CLASSPATH_SEPARATOR +
BPELDeployer.Constants.BPEL_RESOURCE_LOCATION + CLASSPATH_SEPARATOR + BPELDeployer.Constants
.APPROVAL_SERVICE_RESOURCE_LOCATION + CLASSPATH_SEPARATOR;
String outputPath = System.getProperty(BPELDeployer.Constants.TEMP_DIR_PROPERTY) + File.separator;
//process.wsdl
String outputFile = outputPath + processName + BPELDeployer.Constants.WSDL_EXT;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.PROCESS_WSDL_FILE, outputFile);
filesToAdd.add(outputFile);
//process.bpel
outputFile = outputPath + processName + BPELDeployer.Constants.BPEL_EXT;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.PROCESS_BPEL_FILE, outputFile);
filesToAdd.add(outputFile);
//callback.wsdl
outputFile = outputPath + BPELDeployer.Constants.CALLBACK_WSDL_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.CALLBACK_WSDL_FILE, outputFile);
filesToAdd.add(outputFile);
//task.wsdl
outputFile = outputPath + htName + BPELDeployer.Constants.SERVICE_TXT + BPELDeployer.Constants.WSDL_EXT;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.TASK_WSDL_FILE, outputFile);
filesToAdd.add(outputFile);
taskWsdl = outputFile;
//callback.epr
outputFile = outputPath + BPELDeployer.Constants.CALLBACK_EPR_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.CALLBACK_EPR_FILE, outputFile);
filesToAdd.add(outputFile);
//deploy.xml
outputFile = outputPath + BPELDeployer.Constants.DEPLOY_XML_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.DEPLOY_XML_FILE, outputFile);
filesToAdd.add(outputFile);
//taskService.epr
outputFile = outputPath + BPELDeployer.Constants.TASK_SERVICE_EPR_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.TASK_SERVICE_EPR_FILE, outputFile);
filesToAdd.add(outputFile);
//ws-humantask.xsd
outputFile = outputPath + BPELDeployer.Constants.WS_HUMAN_TASK_XSD_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.WS_HUMAN_TASK_XSD_FILE, outputFile);
filesToAdd.add(outputFile);
ws_humantask = outputFile;
//ws-humantask-types.xsd
outputFile = outputPath + BPELDeployer.Constants.WS_HUMAN_TASK_TYPE_XSD_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.WS_HUMAN_TASK_TYPE_XSD_FILE, outputFile);
filesToAdd.add(outputFile);
FileOutputStream zipFOS =
new FileOutputStream(outputPath + processName + BPELDeployer.Constants.ZIP_EXT);
ZipOutputStream zipOutputStream = new ZipOutputStream(zipFOS);
for (String fileName : filesToAdd) {
addToZipFile(fileName, zipOutputStream);
}
zipOutputStream.close();
zipFOS.close();
for (String fileName : filesToAdd) {
File file = new File(fileName);
if (file.exists() && file.isFile() && !fileName.equals(taskWsdl) && !fileName.equals(ws_humantask)) {
boolean deleteSuccess = file.delete();
if (!deleteSuccess) {
log.warn("Temporary file " + fileName + " deletion failed.");
}
}
}
}
private void generateHTArtifact() throws IOException {
Set<String> filesToAdd = new HashSet<>();
String resourceHomePath =
BPELDeployer.Constants.TEMPLATE_RESOURCE_LOCATION + CLASSPATH_SEPARATOR +
BPELDeployer.Constants.HT_RESOURCE_LOCATION + CLASSPATH_SEPARATOR + BPELDeployer.Constants
.APPROVAL_HT_RESOURCE_LOCATION + CLASSPATH_SEPARATOR;
String outputPath = System.getProperty(BPELDeployer.Constants.TEMP_DIR_PROPERTY) + File.separator;
//task.ht
String outputFile = outputPath + htName + BPELDeployer.Constants.HT_EXT;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.TASK_HT_FILE, outputFile);
filesToAdd.add(outputFile);
//htconfig.xml
outputFile = outputPath + BPELDeployer.Constants.HTCONFIG_XML_FILE;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.HTCONFIG_XML_FILE, outputFile);
filesToAdd.add(outputFile);
//task-input.jsp
outputFile =
outputPath + BPELDeployer.Constants.APPROVAL_JSP_LOCATION + File.separator + htName + BPELDeployer
.Constants.INPUT_JSP_SUFFIX;
File outputFileParent = new File(outputFile).getParentFile();
if (!outputFileParent.exists()) {
outputFileParent.mkdirs();
}
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.APPROVAL_JSP_LOCATION + CLASSPATH_SEPARATOR +
BPELDeployer.Constants.TASK_INPUT_JSP_FILE, outputFile);
filesToAdd.add(outputFile);
//task-output.jsp
outputFile =
outputPath + BPELDeployer.Constants.APPROVAL_JSP_LOCATION + File.separator + htName +
BPELDeployer.Constants.OUTPUT_JSP_SUFFIX;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.APPROVAL_JSP_LOCATION + CLASSPATH_SEPARATOR +
BPELDeployer.Constants.TASK_OUTPUT_JSP_FILE, outputFile);
filesToAdd.add(outputFile);
//task-response.jsp
outputFile =
outputPath + BPELDeployer.Constants.APPROVAL_JSP_LOCATION + File.separator + htName +
BPELDeployer.Constants.RESPONSE_JSP_SUFFIX;
removePlaceHolders(resourceHomePath + BPELDeployer.Constants.APPROVAL_JSP_LOCATION + CLASSPATH_SEPARATOR +
BPELDeployer.Constants.TASK_RESPONSE_JSP_FILE, outputFile);
filesToAdd.add(outputFile);
//created from process
filesToAdd.add(outputPath + htName + BPELDeployer.Constants.SERVICE_TXT + BPELDeployer.Constants.WSDL_EXT);
filesToAdd.add(outputPath + BPELDeployer.Constants.WS_HUMAN_TASK_XSD_FILE);
FileOutputStream zipFOS =
new FileOutputStream(outputPath + htName + BPELDeployer.Constants.ZIP_EXT);
ZipOutputStream zipOutputStream = new ZipOutputStream(zipFOS);
for (String fileName : filesToAdd) {
addToZipFile(fileName, zipOutputStream);
}
zipOutputStream.close();
zipFOS.close();
for (String fileName : filesToAdd) {
File file = new File(fileName);
if (file.exists() && file.isFile()) {
boolean deleteSuccess = file.delete();
if (!deleteSuccess) {
log.warn("Temporary file " + fileName + " deletion failed.");
}
}
}
}
private void addToZipFile(String fileName, ZipOutputStream zos) throws IOException {
File zipRootPath = new File(System.getProperty(BPELDeployer.Constants.TEMP_DIR_PROPERTY));
File file = new File(fileName);
String relativePath = zipRootPath.toURI().relativize(file.toURI()).getPath();
ZipEntry zipEntry = new ZipEntry(relativePath);
zos.putNextEntry(zipEntry);
FileInputStream fis = null;
try {
fis = new FileInputStream(fileName);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
} finally {
zos.closeEntry();
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
log.error("Error when closing the file input stream for " + fileName);
}
}
}
}
private static class Constants {
private static final String BPEL_PROCESS_NAME = "${bpelProcessName}";
private static final String HT_SERVICE_NAME = "${htServiceName}";
private static final String BPS_HOST_NAME = "${bpsURL}";
private static final String URL_TENANT_CONTEXT = "${tenantContext}";
private static final String CARBON_HOST_NAME = "${carbonHostName}";
private static final String HT_OWNER_ROLE = "${htOwnerRole}";
private static final String HT_ADMIN_ROLE = "${htAdminRole}";
private static final String PROCESS_BPEL_FILE = "ApprovalProcess.bpel";
private static final String PROCESS_WSDL_FILE = "ApprovalProcessArtifacts.wsdl";
private static final String TASK_WSDL_FILE = "ApprovalTaskService.wsdl";
private static final String CALLBACK_WSDL_FILE = "CallbackService.wsdl";
private static final String CALLBACK_EPR_FILE = "callbackService.epr";
private static final String DEPLOY_XML_FILE = "deploy.xml";
private static final String TASK_SERVICE_EPR_FILE = "taskService.epr";
private static final String WS_HUMAN_TASK_XSD_FILE = "ws-humantask.xsd";
private static final String WS_HUMAN_TASK_TYPE_XSD_FILE = "ws-humantask-types.xsd";
private static final String HTCONFIG_XML_FILE = "htconfig.xml";
private static final String TASK_HT_FILE = "ApprovalTask.ht";
private static final String TASK_INPUT_JSP_FILE = "ApprovalTask-input.jsp";
private static final String TASK_OUTPUT_JSP_FILE = "ApprovalTask-output.jsp";
private static final String TASK_RESPONSE_JSP_FILE = "ApprovalTask-response.jsp";
private static final String TEMPLATE_RESOURCE_LOCATION = "templates";
private static final String BPEL_RESOURCE_LOCATION = "bpel";
private static final String HT_RESOURCE_LOCATION = "humantask";
private static final String APPROVAL_SERVICE_RESOURCE_LOCATION = "MultiStepApprovalService";
private static final String APPROVAL_HT_RESOURCE_LOCATION = "MultiStepApprovalTask";
private static final String APPROVAL_JSP_LOCATION = "web";
private static final String SERVICE_TXT = "Service";
private static final String WSDL_EXT = ".wsdl";
private static final String BPEL_EXT = ".bpel";
private static final String ZIP_EXT = ".zip";
private static final String XSD_EXT = ".xsd";
private static final String HT_EXT = ".ht";
private static final String HT_SUFFIX = "Task";
private static final String INPUT_JSP_SUFFIX = "-input.jsp";
private static final String OUTPUT_JSP_SUFFIX = "-output.jsp";
private static final String RESPONSE_JSP_SUFFIX = "-response.jsp";
private static final String TEMP_DIR_PROPERTY = "java.io.tmpdir";
private static final String ZIP_TYPE = "zip";
}
}