/**
* Copyright 2007-2010 非也
* All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License v3 as published by the Free Software
* Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, see http://www.gnu.org/licenses/lgpl.html.
*
*/
package org.fireflow.service.callback;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.script.ScriptException;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceProvider;
import org.apache.commons.lang.StringUtils;
import org.fireflow.client.WorkflowQuery;
import org.fireflow.client.WorkflowSession;
import org.fireflow.client.WorkflowSessionFactory;
import org.fireflow.client.WorkflowStatement;
import org.fireflow.client.impl.WorkflowSessionLocalImpl;
import org.fireflow.client.query.Restrictions;
import org.fireflow.engine.context.RuntimeContext;
import org.fireflow.engine.entity.runtime.ActivityInstance;
import org.fireflow.engine.entity.runtime.ActivityInstanceProperty;
import org.fireflow.engine.entity.runtime.ActivityInstanceState;
import org.fireflow.engine.entity.runtime.ProcessInstance;
import org.fireflow.engine.exception.InvalidOperationException;
import org.fireflow.engine.exception.WorkflowProcessNotFoundException;
import org.fireflow.engine.modules.instancemanager.ActivityInstanceManager;
import org.fireflow.engine.modules.ousystem.impl.FireWorkflowSystem;
import org.fireflow.engine.modules.script.ScriptContextVariableNames;
import org.fireflow.engine.modules.script.ScriptEngineHelper;
import org.fireflow.model.InvalidModelException;
import org.fireflow.model.binding.Assignment;
import org.fireflow.model.binding.ServiceBinding;
import org.fireflow.model.data.Expression;
import org.firesoa.common.schema.DOMInitializer;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.w3c.dom.Document;
/**
* 响应WebService的回调请求
* @author 非也 nychen2000@163.com
* Fire Workflow 官方网站:www.firesoa.com 或者 www.fireflow.org
*
*/
@WebServiceProvider()
@ServiceMode(value=Service.Mode.PAYLOAD)
public class ProcessServiceProvider implements Provider<Source>{
protected static final TransformerFactory transformerFactory = TransformerFactory
.newInstance();
@Resource
protected WebServiceContext wsContext;
protected RuntimeContext workflowRuntimeContext = null;
protected CallbackService callbackService = null;
protected ServiceBinding serviceBinding = null;
protected TransactionTemplate transactionTemplate = null;
protected boolean startNewProcess = false;
protected String processId = null;
protected String processType = null;
// protected Integer processVersion = null;
/**
* @return the transactionTemplate
*/
public TransactionTemplate getTransactionTemplate() {
return transactionTemplate;
}
/**
* @param transactionTemplate the transactionTemplate to set
*/
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public ServiceBinding getServiceBinding() {
return serviceBinding;
}
public void setServiceBinding(ServiceBinding serviceBinding) {
this.serviceBinding = serviceBinding;
}
public void setWorkflowRuntimeContext(RuntimeContext ctx){
this.workflowRuntimeContext = ctx;
}
public RuntimeContext getWorkflowRuntimeContext(){
return this.workflowRuntimeContext;
}
public CallbackService getCallbackService() {
return callbackService;
}
public void setCallbackService(CallbackService callbackService) {
this.callbackService = callbackService;
}
public ProcessServiceProvider(){
}
public boolean isStartNewProcess() {
return startNewProcess;
}
public void setStartNewProcess(boolean startNewProcess) {
this.startNewProcess = startNewProcess;
}
public String getProcessId() {
return processId;
}
public void setProcessId(String processId) {
this.processId = processId;
}
public String getProcessType() {
return processType;
}
public void setProcessType(String processType) {
this.processType = processType;
}
// public Integer getProcessVersion() {
// return processVersion;
// }
// public void setProcessVersion(Integer processVersion) {
// this.processVersion = processVersion;
// }
public Source invoke(Source request) {
Expression correlation = callbackService.getCorrelation();
final QName responseRootElementQName = new QName(callbackService.getTargetNamespaceUri(),this.serviceBinding.getOperationName()+"Response");
//下面是测试代码
/*
try{
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "2");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// transformer.transform()方法 将 XML Source转换为 Result
transformer.transform(request, new StreamResult(
outputStream));
System.out.println( outputStream.toString());
}catch(Exception e){
e.printStackTrace();
}
*/
final Document requestDOM ;
try{
Transformer transformer = transformerFactory.newTransformer();
DOMResult domResult = new DOMResult();
transformer.transform(request, domResult);
requestDOM = (Document)domResult.getNode();//从reuqest中获得DOM对象
}catch(TransformerException e){
throw new WebServiceException("Can NOT transform request to DOM.",e);
}
final WorkflowSession session = WorkflowSessionFactory.createWorkflowSession(workflowRuntimeContext, FireWorkflowSystem.getInstance());
if (!startNewProcess){//调用某个中间节点
if (correlation==null || StringUtils.isEmpty(correlation.getBody())){
throw new WebServiceException("The correlation can NOT be empty; the callbackservice is "+callbackService.getName());
}
//1、通过serviceId和service version获得candidate activityInstance
WorkflowQuery<ActivityInstance> query = session.createWorkflowQuery(ActivityInstance.class);
List<ActivityInstance> candidates = query.add(Restrictions.eq(ActivityInstanceProperty.SERVICE_ID, callbackService.getId()))
.add(Restrictions.eq(ActivityInstanceProperty.SERVICE_VERSION, callbackService.getVersion()))
.add(Restrictions.eq(ActivityInstanceProperty.STATE, ActivityInstanceState.RUNNING))
.list();
//2、匹配correlation
//correlation是一个bool表达式,如:processVars.var1==xpath(requestDom,'method1Request/id')
ProcessInstance theProcessInstance = null;
ActivityInstance theActivityInstance = null;
if (candidates!=null && candidates.size()>0){
for (ActivityInstance activityInstance : candidates){
ProcessInstance processInstance = activityInstance.getProcessInstance(session);
((WorkflowSessionLocalImpl)session).setCurrentProcessInstance(processInstance);
((WorkflowSessionLocalImpl)session).setCurrentActivityInstance(activityInstance);
Map<String, Object> varContext = ScriptEngineHelper
.fulfillScriptContext(session,
workflowRuntimeContext, processInstance,
activityInstance);
varContext.put(ScriptContextVariableNames.INPUTS,
requestDOM);
Object result = ScriptEngineHelper.evaluateExpression(
workflowRuntimeContext, correlation, varContext);
if (result != null && (result instanceof Boolean)) {
if ((Boolean) result) {
theActivityInstance = activityInstance;
theProcessInstance = processInstance;
break;
}
}
}
}
final ActivityInstance theMatchedActivityInstance = theActivityInstance;//匹配上的activityInstance
final ProcessInstance theMatchedProcessInstance = theProcessInstance;
if (theMatchedActivityInstance!=null){
//1、首先设置currentProcessInstance和CurrentActivityInstance
((WorkflowSessionLocalImpl)session).setCurrentActivityInstance(theMatchedActivityInstance);
((WorkflowSessionLocalImpl)session).setCurrentProcessInstance(theMatchedProcessInstance);
try{
this.transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
//2、设置流程变量
List<Assignment> inputAssignments_ = serviceBinding.getInputAssignments();
Map<String,Object> scriptContext = new HashMap<String,Object>();
scriptContext.put(ScriptContextVariableNames.INPUTS,
requestDOM);
try {
ScriptEngineHelper.assignOutputToVariable(session,workflowRuntimeContext,
theMatchedProcessInstance,theMatchedActivityInstance, inputAssignments_,
scriptContext);
} catch (ScriptException e) {
throw new RuntimeException("Can NOT assign inputs to process instance varialbes,the callback service is "+callbackService.getName(),e);
}
//3、执行closeActivity的操作
ActivityInstanceManager actInstMgr = workflowRuntimeContext.getEngineModule(ActivityInstanceManager.class, theMatchedProcessInstance.getProcessType());
actInstMgr.onServiceCompleted(session, theMatchedActivityInstance);
return null;
}
});
}catch(TransactionException e){
throw new WebServiceException(e);
}
//4、返回结果
try{
Map<String, Object> allTheVars = ScriptEngineHelper.fulfillScriptContext(session,workflowRuntimeContext, theMatchedProcessInstance, theMatchedActivityInstance);
List<Assignment> outputAssignments = serviceBinding.getOutputAssignments();
Document doc = DOMInitializer.generateDocument(callbackService.getXmlSchemaCollection(),responseRootElementQName);
allTheVars.put(ScriptContextVariableNames.OUTPUTS, doc);
Map<String, Object> tmp = ScriptEngineHelper.resolveAssignments(workflowRuntimeContext, outputAssignments, allTheVars);
Document resultDOM = (Document)tmp.get(ScriptContextVariableNames.OUTPUTS);
return new DOMSource(resultDOM);
}catch(ScriptException e){
throw new WebServiceException("Can NOT assign process instance varialbes to output,the callback service is "+callbackService.getName(),e);
} catch (ParserConfigurationException e) {
throw new WebServiceException("Can NOT init output DOM,the callback service is "+callbackService.getName(),e);
}
}else{
throw new WebServiceException("Process instance NOT found for the conditions as follows,service id='"+callbackService.getId()+"' and service version='"+callbackService.getVersion()+"' and correlation='"+correlation.getBody()+"'");
}
}
else{//启动新的流程实例
//1、获得输入变量,解析bizId
final Map<String,Object> processVars;
final String bizId ;
try{
List<Assignment> inputAssignments_ = serviceBinding.getInputAssignments();
Map<String,Object> scriptContext = new HashMap<String,Object>();
scriptContext.put(ScriptContextVariableNames.INPUTS,
requestDOM);
Map<String, Object> temp = ScriptEngineHelper.resolveAssignments(workflowRuntimeContext, inputAssignments_, scriptContext);
processVars = (Map<String, Object>)temp.get(ScriptContextVariableNames.PROCESS_VARIABLES);
Map<String, Object> varContext = new HashMap<String,Object>();
varContext.put(ScriptContextVariableNames.INPUTS, requestDOM);
Object result = ScriptEngineHelper.evaluateExpression(workflowRuntimeContext, correlation, varContext);
bizId=result==null?null:result.toString();
}catch(ScriptException e){
throw new WebServiceException("Can NOT assign inputs to process instance varialbes,the callback service is "+callbackService.getName(),e);
}
//2、启动流程
ProcessInstance processInstance = null;
try {
processInstance = (ProcessInstance)transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
WorkflowStatement stmt = session.createWorkflowStatement(processType);
ProcessInstance procInst = null;
try {
procInst = stmt.startProcess(processId,bizId, processVars);
} catch (InvalidModelException e1) {
throw new RuntimeException("Start process instance error! The callback service is "+callbackService.getName()+"; the process is "+processId, e1);
} catch (WorkflowProcessNotFoundException e1) {
throw new RuntimeException("Start process instance error! The callback service is "+callbackService.getName()+"; the process is "+processId, e1);
} catch (InvalidOperationException e1) {
throw new RuntimeException("Start process instance error! The callback service is "+callbackService.getName()+"; the process is "+processId, e1);
}
return procInst;
}
});
} catch (TransactionException e) {
throw new WebServiceException(e);
}
//3、返回结果
try{
Map<String, Object> allTheVars = ScriptEngineHelper.fulfillScriptContext(session,workflowRuntimeContext, processInstance, null);
List<Assignment> outputAssignments = serviceBinding.getOutputAssignments();
// 首先初始化返回的DOM对象
Document doc = DOMInitializer.generateDocument(callbackService.getXmlSchemaCollection(),responseRootElementQName);
allTheVars.put(ScriptContextVariableNames.OUTPUTS, doc);
Map<String, Object> tmp = ScriptEngineHelper.resolveAssignments(workflowRuntimeContext, outputAssignments, allTheVars);
Document resultDOM = (Document)tmp.get(ScriptContextVariableNames.OUTPUTS);
return new DOMSource(resultDOM);
}catch(ScriptException e){
throw new WebServiceException("Can NOT assign process instance varialbes to output,the callback service is "+callbackService.getName(),e);
} catch (ParserConfigurationException e) {
throw new WebServiceException("Can NOT init output DOM,the callback service is "+callbackService.getName(),e);
}
}
}
}