/**
* Copyright 2007-2010 非也
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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 General Public License
* along with this program. If not, see http://www.gnu.org/licenses. *
*/
package org.fireflow.engine.modules.script;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import javax.xml.namespace.QName;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.UnifiedJEXL;
import org.apache.commons.jxpath.JXPathContext;
import org.fireflow.client.WorkflowSession;
import org.fireflow.client.impl.WorkflowSessionLocalImpl;
import org.fireflow.engine.context.RuntimeContext;
import org.fireflow.engine.entity.runtime.ActivityInstance;
import org.fireflow.engine.entity.runtime.ProcessInstance;
import org.fireflow.engine.exception.InvalidOperationException;
import org.fireflow.engine.invocation.Message;
import org.fireflow.engine.modules.persistence.PersistenceService;
import org.fireflow.engine.modules.script.functions.DateTimeUtil;
import org.fireflow.engine.modules.script.functions.JexlContext4Fireflow;
import org.fireflow.engine.modules.script.functions.XPath;
import org.fireflow.model.binding.Assignment;
import org.fireflow.model.data.Expression;
import org.firesoa.common.jxpath.model.dom.W3CDomFactory;
import org.firesoa.common.schema.NameSpaces;
import org.firesoa.common.util.JavaDataTypeConvertor;
import org.firesoa.common.util.ScriptLanguages;
/**
*
*
* @author 非也
* @version 2.0
*/
public class ScriptEngineHelper {
// private static final Dom4JFactory dom4JFactory = new Dom4JFactory();
private static final W3CDomFactory w3cDomFactory = new W3CDomFactory();
// static{
// JXPathContextReferenceImpl.addNodePointerFactory(new Dom4JPointerFactory());
// }
private static final Map<String,Object> jexlFunctionsMap = new HashMap<String,Object>();
private static final JexlEngine jexlEngine = new JexlEngine();
static{
//TODO 所有的function应该写在一个xml注册文件中,便于扩展
jexlFunctionsMap.put("DateUtil", DateTimeUtil.getInstance());
jexlFunctionsMap.put("Xpath", XPath.class);
jexlEngine.setFunctions(jexlFunctionsMap);
}
//所有的表达式解析,均通过该方法。
public static Object evaluateExpression(RuntimeContext rtCtx,Expression fireflowExpression,Map<String,Object> contextObjects){
if (fireflowExpression==null) return null;
if (fireflowExpression.getNamespaceMap()!=null && fireflowExpression.getNamespaceMap().size()>0){
contextObjects.put(XPath.NAMESPACE_PREFIX_URI_MAP, fireflowExpression.getNamespaceMap());
}
if (ScriptLanguages.JEXL.name().equalsIgnoreCase(fireflowExpression.getLanguage())){
return evaluateJexlExpression(fireflowExpression,contextObjects);
}else if (ScriptLanguages.UNIFIEDJEXL.name().equalsIgnoreCase(fireflowExpression.getLanguage())){
return evaluateUnifiedJexlExpression(fireflowExpression,contextObjects);
}else if (ScriptLanguages.XPATH.name().equalsIgnoreCase(fireflowExpression.getLanguage())){
return evaluateXpathExpression(fireflowExpression,contextObjects);
}else{
return evaluateJSR233Expression(rtCtx,fireflowExpression,contextObjects);
}
}
private static Object evaluateJSR233Expression(RuntimeContext rtCtx,Expression fireflowExpression,Map<String,Object> contextObjects){
ScriptEngine scriptEngine = rtCtx.getScriptEngine(fireflowExpression.getLanguage());
ScriptContext scriptContext = new SimpleScriptContext();
Bindings engineScope = scriptContext
.getBindings(ScriptContext.ENGINE_SCOPE);
engineScope.putAll(contextObjects);
try {
Object result = scriptEngine.eval(fireflowExpression.getBody(), scriptContext);
return result;
} catch (ScriptException e) {
throw new RuntimeException("Can NOT evaluate the expression. ",e);
}
}
private static Object evaluateJexlExpression(Expression fireflowExpression,Map<String,Object> contextObjects){
JexlContext4Fireflow context = new JexlContext4Fireflow();
context.setAllContextObject(contextObjects);
org.apache.commons.jexl2.Expression jexlExp = jexlEngine.createExpression(fireflowExpression.getBody());
return jexlExp.evaluate(context);
}
private static Object evaluateUnifiedJexlExpression(Expression fireflowExpression,Map<String,Object> contextObjects){
JexlContext4Fireflow context = new JexlContext4Fireflow();
context.setAllContextObject(contextObjects);
UnifiedJEXL ujexl = new UnifiedJEXL(jexlEngine);
UnifiedJEXL.Expression jexlExp = ujexl.parse(fireflowExpression.getBody());
Object result = jexlExp.evaluate(context);
return result==null?null:result.toString();
}
private static Object evaluateXpathExpression(Expression fireflowExpression,Map<String,Object> contextObjects){
Map<String,String> namespacePrefixUriMap = fireflowExpression.getNamespaceMap();
JXPathContext jxpathContext = JXPathContext.newContext(contextObjects);
jxpathContext.setFactory(w3cDomFactory );
if (namespacePrefixUriMap!=null){
Iterator<String> prefixIterator = namespacePrefixUriMap.keySet().iterator();
while(prefixIterator.hasNext()){
String prefix = prefixIterator.next();
String nsUri = namespacePrefixUriMap.get(prefix);
jxpathContext.registerNamespace(prefix, nsUri);
}
}
//仅对W3C DOM进行了判断
//TODO 需要时情况增加对dom4j document值以及jdom document值的判断
Object obj = null;
Object _node = jxpathContext.selectSingleNode(fireflowExpression.getBody());
if (_node instanceof org.w3c.dom.Node){
if (_node instanceof org.w3c.dom.Document){
obj = _node;
}else{
obj = ((org.w3c.dom.Node)_node).getTextContent();
}
}else{
obj = _node;
}
return obj;
}
/**
* 构建脚本引擎的Context,
*
* @param session
* @param processInstance
* @param activityInstance
* @return
*/
public static Map<String, Object> fulfillScriptContext(
WorkflowSession session, RuntimeContext runtimeContext,
ProcessInstance processInstance, ActivityInstance activityInstance) {
WorkflowSessionLocalImpl localSession = (WorkflowSessionLocalImpl)session;
Map<String, Object> engineScope = new HashMap<String, Object>();
engineScope.put(ScriptContextVariableNames.CURRENT_PROCESS_INSTANCE,
processInstance);
engineScope.put(ScriptContextVariableNames.CURRENT_ACTIVITY_INSTANCE,
activityInstance);
Map<String, Object> varValues = processInstance
.getVariableValues(session);
PersistenceService p = runtimeContext.getDefaultEngineModule(PersistenceService.class);
Iterator<String> keyIt = varValues.keySet().iterator();
while(keyIt.hasNext()){
String key = keyIt.next();
Object value = varValues.get(key);
if (value!=null && (value instanceof BusinessObjectWrapper)){
BusinessObjectWrapper wrapper = (BusinessObjectWrapper)value;
try{
Object v = wrapper.resolveBusinessObject(runtimeContext, processInstance, p);
varValues.put(key, v);
}catch(Exception e){
e.printStackTrace();
}
}
}
engineScope
.put(ScriptContextVariableNames.PROCESS_VARIABLES, varValues);
if (activityInstance != null) {
Map<String, Object> varValues2 = activityInstance
.getVariableValues(session);
engineScope.put(ScriptContextVariableNames.ACTIVITY_VARIABLES,
varValues2);
Iterator<String> keyIt2 = varValues2.keySet().iterator();
while(keyIt2.hasNext()){
String key = keyIt2.next();
Object value = varValues2.get(key);
if (value!=null && (value instanceof BusinessObjectWrapper)){
BusinessObjectWrapper wrapper = (BusinessObjectWrapper)value;
try{
Object v = wrapper.resolveBusinessObject(runtimeContext, processInstance, p);
varValues2.put(key, v);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
engineScope.put(ScriptContextVariableNames.SESSION_ATTRIBUTES,
localSession.getAllAttributes());
engineScope.put(ScriptContextVariableNames.WORKFLOW_SESSION, session);
engineScope.put(ScriptContextVariableNames.RUNTIME_CONTEXT, runtimeContext);
return engineScope;
}
public static Map<String, Object> resolveAssignments(
RuntimeContext runtimeContext, List<Assignment> assignments,
Map<String, Object> contextVars) throws ScriptException {
if (assignments == null || assignments.size() == 0) {
return null;
}
Map<String, Object> jxpathRoot = new HashMap<String, Object>();
if (contextVars.get(ScriptContextVariableNames.INPUTS)!=null){
jxpathRoot.put(ScriptContextVariableNames.INPUTS,
contextVars.get(ScriptContextVariableNames.INPUTS));
}else{
jxpathRoot.put(ScriptContextVariableNames.INPUTS,
new HashMap<String, Object>());
}
if (contextVars.get(ScriptContextVariableNames.OUTPUTS)!=null){
jxpathRoot.put(ScriptContextVariableNames.OUTPUTS, contextVars.get(ScriptContextVariableNames.OUTPUTS));
}
else{
jxpathRoot.put(ScriptContextVariableNames.OUTPUTS,
new HashMap<String, Object>());
}
jxpathRoot.put(ScriptContextVariableNames.PROCESS_VARIABLES,
new HashMap<String, Object>());
jxpathRoot.put(ScriptContextVariableNames.ACTIVITY_VARIABLES,
new HashMap<String, Object>());
jxpathRoot.put(ScriptContextVariableNames.SESSION_ATTRIBUTES,
new HashMap<String, Object>());
for (Assignment assignment : assignments) {
Expression fromExpression = assignment.getFrom();
Object obj = evaluateExpression(runtimeContext,fromExpression,contextVars);
if (fromExpression.getDataType() != null && obj!=null) {
try {
obj = JavaDataTypeConvertor.dataTypeConvert(
fromExpression.getDataType(), obj, null);
} catch (ClassCastException e) {
throw new ScriptException(e);
} catch (ClassNotFoundException e) {
throw new ScriptException(e);
}
}
// TODO 所有的To 表达式都用JXpath处理器处理,不使用脚本 引擎
Expression toExpression = assignment.getTo();
QName dataType = toExpression.getDataType();
if (dataType != null
&& dataType.getNamespaceURI().equals(
NameSpaces.JAVA.getUri()) && obj!=null) {// 进行类型验证和转换
try {
obj = JavaDataTypeConvertor.dataTypeConvert(dataType, obj, null);
} catch (ClassCastException e) {
throw new ScriptException(e);
} catch (ClassNotFoundException e) {
throw new ScriptException(e);
}
} else {
// XSD数据类型待处理
//TODO (临时性代码)将date转为string
if (obj instanceof java.util.Date){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
obj = df.format((java.util.Date)obj);
}
}
JXPathContext jxpathContext = JXPathContext.newContext(jxpathRoot);
jxpathContext.setFactory(w3cDomFactory );
Map<String,String> nsMap = toExpression.getNamespaceMap();
Iterator<String> prefixIterator = nsMap.keySet().iterator();
while(prefixIterator.hasNext()){
String prefix = prefixIterator.next();
String nsUri = nsMap.get(prefix);
jxpathContext.registerNamespace(prefix, nsUri);
}
//赋值处理
jxpathContext.createPathAndSetValue(toExpression.getBody(), obj);
}
return jxpathRoot;
}
/**
* 解析输入参数的值
*
* @return
*/
@SuppressWarnings({ "rawtypes", "restriction", "unchecked" })
public static Map<String, Object> resolveInputParameters(
RuntimeContext runtimeContext, List<Assignment> inputAssignments,
Map<String, Object> contextVars) throws ScriptException {
Map<String, Object> result = resolveAssignments(
runtimeContext, inputAssignments, contextVars);
if (result != null) {
Map tmp = (Map) result.get(ScriptContextVariableNames.INPUTS);
return tmp;
} else {
return null;
}
}
@SuppressWarnings({ "restriction", "rawtypes", "unchecked" })
public static void assignOutputToVariable(WorkflowSession session,
RuntimeContext runtimeContext, ProcessInstance processInstance,
ActivityInstance activityInstance,
List<Assignment> assignmentsList, Map<String, Object> contextVars)
throws ScriptException {
WorkflowSessionLocalImpl localSession = (WorkflowSessionLocalImpl)session;
if (assignmentsList == null || assignmentsList.size()==0)
return;
Map<String, Object> result = resolveAssignments(
runtimeContext, assignmentsList, contextVars);
//System.out.println("===解析后的Assignment is "+result);
if (result==null)return;
Map<String,Object> procInstVars = (Map<String, Object>)result.get(ScriptContextVariableNames.PROCESS_VARIABLES);
if (procInstVars!=null && procInstVars.size()>0){
Iterator<String> keys = procInstVars.keySet().iterator();
while(keys.hasNext()){
String key = keys.next();
try {
Object value = procInstVars.get(key);
if (value instanceof Message){
processInstance.setVariableValue(session, key, ((Message)value).getPayload(),((Message)value).getHeaders());
}else{
processInstance.setVariableValue(session, key, value);
}
} catch (InvalidOperationException e) {
throw new ScriptException(e);
}
}
}
Map<String,Object> actInstVars = (Map<String, Object>)result.get(ScriptContextVariableNames.ACTIVITY_VARIABLES);
if (actInstVars!=null && actInstVars.size()>0){
Iterator<String> keys = actInstVars.keySet().iterator();
while(keys.hasNext()){
String key = keys.next();
try {
Object value = actInstVars.get(key);
if (value instanceof Message){
activityInstance.setVariableValue(session, key, ((Message)value).getPayload(),((Message)value).getHeaders());
}else{
activityInstance.setVariableValue(session, key, value);
}
} catch (InvalidOperationException e) {
throw new ScriptException(e);
}
}
}
Map<String,Object> sessionAttrs = (Map<String, Object>)result.get(ScriptContextVariableNames.SESSION_ATTRIBUTES);
if (sessionAttrs!=null && sessionAttrs.size()>0){
Iterator<String> keys = sessionAttrs.keySet().iterator();
while(keys.hasNext()){
String key = keys.next();
localSession.setAttribute(key, sessionAttrs.get(key));
}
}
}
}