【转】JBPM深入解析之变量设计
在流程的流转的过程中,很多时候我们需要根据不同的实际情况传入一些初始化数据,以便完成我们个性化的业务需求;同时很多时候我们需要在不同的节点
之间共享一些业务数据,特别是一些节点要以前一节点的输出作为输入等;变量对于流程引擎来说很重要,可以说没有变量,那么我们就不能运行时动态的设置和传
入一些数据,这将极大的限制流程的灵活性!
变量类型
全局变量,所有的节点都可以获取并设置该变量的值
局部变量,只在该节点及其子节点可以获取并设置该变量的值
变量的传入
在流程定义中进行变量的定义
< process name ="EndMultiple" xmlns ="http://jbpm.org/4.4/jpdl" >
< start g ="16,96,48,48" >
< transition to ="get return code" name ="" />
</ start >
< state g ="96,94,111,52" name ="get return code" >
< transition g ="151,60:-36,11" name ="200" to ="ok" />
< transition g =":-16,-27" name ="400" to ="bad request" />
< transition g ="151,183:-33,-32" name ="500" to ="internal server error" />
</ state >
< end g ="238,37,48,48" name ="ok" />
< end g ="238,98,48,48" name ="bad request" />
< end g ="240,160,48,48" name ="internal server error" />
< variable name ="msg" init-expr ="jbpm" type ="string" >
</ variable >
< null ></ null >
</ variable >
</ process >
在启动流程流程实例的时候传入全局变量
variables.put( " category " , " big " );
variables.put( " dollars " , 100000 );
Execution execution = executionService.startProcessInstanceByKey( " TaskVariables " , variables);
在唤醒那些可外部唤醒类型的节点时候传入局部变量
variables.put( " category " , " small " );
variables.put( " lires " , 923874893 );
taskService.completeTask(taskId, variables)
在任务存在的情况下,可以在任务等待外部唤醒时进行局部变量的设置
variables.put( " category " , " small " );
variables.put( " lires " , 923874893 );
taskService.setVariables(taskId, variables);
在任何时候都可以通过执行服务传入设置全局变量
variables.put( " category " , " small " );
variables.put( " lires " , 923874893 );
executionService.setVariable(execution.getId(),variables)
variables.put( " category " , " small " );
variables.put( " lires " , 923874893 );
executionService.signalExecutionById(execution.getId(),variables);
变量架构设计
TypeSet、DefaultTypeSet
TypeSet定义了流程引擎中通过变量类型和matcher两种方式查找变量类型的接口;DefaultTypeSet对TypeSet的接口进行了实现,并定义了一个List<TypeMapping>来装载所有的变量类型的映射变量实例。
TypeSet
/**
* @author Tom Baeyens
*/
public interface TypeSet {
Type findTypeByMatch(String key, Object value);
Type findTypeByName(String typeName);
}
DefaultTypeSet
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Tom Baeyens
*/
public class DefaultTypeSet implements TypeSet, Serializable {
private static final long serialVersionUID = 1L ;
protected List < TypeMapping > typeMappings;
public Type findTypeByMatch(String key, Object value) {
if (typeMappings != null ) {
for (TypeMapping typeMapping: typeMappings) {
if (typeMapping.matches(key, value)) {
return typeMapping.getType();
}
}
}
return null ;
}
public Type findTypeByName(String typeName) {
if ( (typeMappings != null )
&& (typeName != null )
) {
for (TypeMapping typeMapping: typeMappings) {
Type type = typeMapping.getType();
if (typeName.equals(type.getName())) {
return type;
}
}
}
return null ;
}
public void addTypeMapping(TypeMapping typeMapping) {
if (typeMappings == null ) {
typeMappings = new ArrayList < TypeMapping > ();
}
typeMappings.add(typeMapping);
}
}
TypeSet的初始化 在配置文件中配置流程引擎中需要使用的变量的类型
Jbpm.defaut.cfg.xml引入变量类型配置的文件路径
< repository-service />
< repository-cache />
< execution-service />
< history-service />
< management-service />
< identity-service />
< task-service />
< object class ="org.jbpm.pvm.internal.id.DatabaseDbidGenerator" >
< field name ="commandService" >< ref object ="newTxRequiredCommandService" /></ field >
</ object >
< object class ="org.jbpm.pvm.internal.id.DatabaseIdComposer" init ="eager" />
< object class ="org.jbpm.pvm.internal.el.JbpmElFactoryImpl" />
<!-- 定义变量配置文件的路径 -->
< types resource ="jbpm.variable.types.xml" />
< address-resolver />
</ process-engine-context >
Jbpm.variable.types.xml定义了变量的类型、使用的转换器等信息
<!-- types stored in a native column -->
< type name ="string" class ="java.lang.String" variable-class ="org.jbpm.pvm.internal.type.variable.StringVariable" />
< type name ="long" class ="java.lang.Long" variable-class ="org.jbpm.pvm.internal.type.variable.LongVariable" />
< type name ="double" class ="java.lang.Double" variable-class ="org.jbpm.pvm.internal.type.variable.DoubleVariable" />
<!-- types converted to a string -->
< type name ="date" class ="java.util.Date" converter ="org.jbpm.pvm.internal.type.converter.DateToStringConverter" variable-class ="org.jbpm.pvm.internal.type.variable.StringVariable" />
< type name ="boolean" class ="java.lang.Boolean" converter ="org.jbpm.pvm.internal.type.converter.BooleanToStringConverter" variable-class ="org.jbpm.pvm.internal.type.variable.StringVariable" />
< type name ="char" class ="java.lang.Character" converter ="org.jbpm.pvm.internal.type.converter.CharacterToStringConverter" variable-class ="org.jbpm.pvm.internal.type.variable.StringVariable" />
<!-- types converted to a long -->
< type name ="byte" class ="java.lang.Byte" converter ="org.jbpm.pvm.internal.type.converter.ByteToLongConverter" variable-class ="org.jbpm.pvm.internal.type.variable.LongVariable" />
< type name ="short" class ="java.lang.Short" converter ="org.jbpm.pvm.internal.type.converter.ShortToLongConverter" variable-class ="org.jbpm.pvm.internal.type.variable.LongVariable" />
< type name ="integer" class ="java.lang.Integer" converter ="org.jbpm.pvm.internal.type.converter.IntegerToLongConverter" variable-class ="org.jbpm.pvm.internal.type.variable.LongVariable" />
<!-- types converted to a double -->
< type name ="float" class ="java.lang.Float" converter ="org.jbpm.pvm.internal.type.converter.FloatToDoubleConverter" variable-class ="org.jbpm.pvm.internal.type.variable.DoubleVariable" />
<!-- byte[] and char[] -->
< type name ="byte[]" class ="[B" variable-class ="org.jbpm.pvm.internal.type.variable.BlobVariable" />
< type name ="char[]" class ="[C" variable-class ="org.jbpm.pvm.internal.type.variable.TextVariable" />
< type name ="hibernate-long-id" class ="hibernatable" id-type ="long" variable-class ="org.jbpm.pvm.internal.type.variable.HibernateLongVariable" />
< type name ="hibernate-string-id" class ="hibernatable" id-type ="string" variable-class ="org.jbpm.pvm.internal.type.variable.HibernateStringVariable" />
< type name ="serializable" class ="serializable" converter ="org.jbpm.pvm.internal.type.converter.SerializableToBytesConverter" variable-class ="org.jbpm.pvm.internal.type.variable.BlobVariable" />
<!-- TODO: add ejb3 entity bean support -->
<!-- TODO: add JCR activity support -->
<!-- TODO: add collection support -->
</ types >
TypesBinding解析jbpm.variable.types.xml中的变量类型定义
public TypesBinding() {
super ( " types " );
}
public Object parse(Element element, Parse parse, Parser parser) {
StreamInput streamSource = null ;
// 查找type文件
if (element.hasAttribute( " file " )) {
String fileName = element.getAttribute( " file " );
File file = new File(fileName);
if (file.exists() && file.isFile()) {
streamSource = new FileStreamInput(file);
parser.importStream(streamSource, element, parse);
} else {
parse.addProblem( " file " + fileName + " isn't a file " , element);
}
}
if (element.hasAttribute( " resource " )) {
String resource = element.getAttribute( " resource " );
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
streamSource = new ResourceStreamInput(resource, classLoader);
parser.importStream(streamSource, element, parse);
}
if (element.hasAttribute( " url " )) {
String urlText = element.getAttribute( " url " );
try {
URL url = new URL(urlText);
streamSource = new UrlStreamInput(url);
parser.importStream(streamSource, element, parse);
} catch (Exception e) {
parse.addProblem( " couldn't open url " + urlText, e);
}
}
TypesDescriptor typesDescriptor = new TypesDescriptor();
List < Element > typeElements = XmlUtil.elements(element, " type " );
for (Element typeElement: typeElements) {
TypeMapping typeMapping = parseTypeMapping(typeElement, parse, parser);
typesDescriptor.addTypeMapping(typeMapping);
}
return typesDescriptor;
}
protected TypeMapping parseTypeMapping(Element element, Parse parse, Parser parser) {
TypeMapping typeMapping = new TypeMapping();
Type type = new Type();
typeMapping.setType(type);
// type name
// 类型名称
if (element.hasAttribute( " name " )) {
type.setName(element.getAttribute( " name " ));
}
String hibernateSessionFactoryName = XmlUtil.attribute(element, " hibernate-session-factory " );
// first we get the matcher
Matcher matcher = null ;
if (element.hasAttribute( " class " )) {
String className = element.getAttribute( " class " );
// if type="serializable"
if ( " serializable " .equals(className)) {
matcher = new SerializableMatcher();
// if type="hibernatable"
} else if ( " hibernatable " .equals(className)) {
if (element.hasAttribute( " id-type " )) {
String idType = element.getAttribute( " id-type " );
if ( " long " .equalsIgnoreCase(idType)) {
matcher = new HibernateLongIdMatcher(hibernateSessionFactoryName);
} else if ( " string " .equalsIgnoreCase(idType)) {
matcher = new HibernateStringIdMatcher(hibernateSessionFactoryName);
} else {
parse.addProblem( " id-type was not 'long' or 'string': " + idType, element);
}
} else {
parse.addProblem( " id-type is required in a persistable type " , element);
}
// otherwise, we expect type="some.java.ClassName"
} else {
matcher = new ClassNameMatcher(className);
}
} else {
// look for the matcher element
Element matcherElement = XmlUtil.element(element, " matcher " );
Element matcherObjectElement = XmlUtil.element(matcherElement);
if (matcherObjectElement != null ) {
try {
Descriptor descriptor = (Descriptor) parser.parseElement(matcherObjectElement, parse);
matcher = (Matcher) WireContext.create(descriptor);
} catch (ClassCastException e) {
parse.addProblem( " matcher is not a " + Matcher. class .getName() + " : " + (matcher != null ? matcher.getClass().getName() : " null " ), element);
}
} else {
parse.addProblem( " no matcher specified in " + XmlUtil.toString(element), element);
}
}
typeMapping.setMatcher(matcher);
// parsing the converter
Converter converter = null ;
if (element.hasAttribute( " converter " )) {
String converterClassName = element.getAttribute( " converter " );
try {
Class <?> converterClass = ReflectUtil.classForName(converterClassName);
converter = (Converter) converterClass.newInstance();
} catch (Exception e) {
parse.addProblem( " couldn't instantiate converter " + converterClassName, element);
}
} else {
// look for the matcher element
Element converterElement = XmlUtil.element(element, " converter " );
Element converterObjectElement = XmlUtil.element(converterElement);
if (converterObjectElement != null ) {
try {
converter = (Converter) parser.parseElement(converterObjectElement, parse);
} catch (ClassCastException e) {
parse.addProblem( " converter is not a " + Converter. class .getName() + " : " + (converter != null ? converter.getClass().getName() : " null " ), element);
}
}
}
type.setConverter(converter);
// parsing the variable class
Class <?> variableClass = null ;
if (element.hasAttribute( " variable-class " )) {
String variableClassName = element.getAttribute( " variable-class " );
try {
variableClass = ReflectUtil.classForName(variableClassName);
} catch (Exception e) {
parse.addProblem( " couldn't instantiate variable-class " + variableClassName, e);
}
} else {
parse.addProblem( " variable-class is required on a type: " + XmlUtil.toString(element), element);
}
type.setVariableClass(variableClass);
return typeMapping;
}
}
TypeDescriptor用于运行时生成DefaultTypeSet
private static final long serialVersionUID = 1L;
DefaultTypeSet defaultTypeSet = new DefaultTypeSet();
public Object construct(WireContext wireContext) {
return defaultTypeSet;
}
public Class < ? > getType(WireDefinition wireDefinition) {
return DefaultTypeSet.class;
}
public void addTypeMapping(TypeMapping typeMapping) {
defaultTypeSet.addTypeMapping(typeMapping);
}
public DefaultTypeSet getDefaultTypeSet() {
return defaultTypeSet;
}
}
TypeMapping作为映射器,负责承载变量的类型和matcher
Matcher matcher;
Type type;
private static final long serialVersionUID = 1L ;
public boolean matches(String name, Object value) {
return matcher.matches(name, value);
}
public String toString() {
return " ( " + matcher + " --> " + type + " ) " ;
}
public void setMatcher(Matcher matcher) {
this .matcher = matcher;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this .type = type;
}
public Matcher getMatcher() {
return matcher;
}
}
Matcher,递归查询给定变量的类型以及所有的基类中是否有与该Matcher对应的类的变量类型相同的。
private static final long serialVersionUID = 1L ;
String className = null ;
public ClassNameMatcher(String className) {
this .className = className;
}
public String toString() {
return className;
}
public boolean matches(String name, Object value) {
if (value == null ) {
return true ;
}
Class <?> valueClass = value.getClass();
while (valueClass != null ) {
if (className.equals(value.getClass().getName())) {
return true ;
} else {
Class <?> [] interfaces = valueClass.getInterfaces();
for ( int i = 0 ; i < interfaces.length; i ++ ) {
if (className.equals(interfaces[i].getName())) {
return true ;
}
}
valueClass = valueClass.getSuperclass();
}
}
return false ;
}
}
Type 定义变量类型的名称、转换器和对应的Variable
private static final long serialVersionUID = 1L ;
protected String name;
protected Converter converter;
protected Class <?> variableClass;
public String toString() {
if (name != null ) {
return name;
}
StringBuilder text = new StringBuilder();
if (converter != null ) {
text.append(converter.toString());
text.append( " --> " );
}
if (variableClass != null ) {
text.append(variableClass.getSimpleName());
} else {
text.append( " undefined " );
}
return text.toString();
}
public Converter getConverter() {
return converter;
}
public void setConverter(Converter converter) {
this .converter = converter;
}
public Class < ? > getVariableClass() {
return variableClass;
}
public void setVariableClass(Class < ? > variableClass) {
this .variableClass = variableClass;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
}
Converter,负责变量运行时的表现形式和持久化形式进行转化
private static final long serialVersionUID = 1L ;
public boolean supports(Object value, ScopeInstanceImpl scopeInstance, Variable variable) {
if (value == null ) return true ;
return Serializable. class .isAssignableFrom(value.getClass());
}
public Object convert(Object o, ScopeInstanceImpl scopeInstance, Variable variable) {
byte [] bytes = null ;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.flush();
bytes = baos.toByteArray();
Transaction transaction = EnvironmentImpl.getFromCurrent(Transaction. class , false );
if (transaction != null ) {
transaction.registerDeserializedObject( new DeserializedObject(o, scopeInstance, (BlobVariable) variable));
}
} catch (IOException e) {
throw new JbpmException( " couldn't serialize ' " + o + " ' " , e);
}
return bytes;
}
public Object revert(Object o, ScopeInstanceImpl scopeInstance, Variable variable) {
byte [] bytes = ( byte []) o;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = null ;
ois = new DeploymentObjectInputStream(bais, scopeInstance.getExecution().getProcessDefinition().getDeploymentId());
Object object = ois.readObject();
Transaction transaction = EnvironmentImpl.getFromCurrent(Transaction. class , false );
if (transaction != null ) {
transaction.registerDeserializedObject( new DeserializedObject(object, scopeInstance, (BlobVariable) variable));
}
return object;
} catch (Exception e) {
throw new JbpmException( " couldn't deserialize object " , e);
}
}
}
Variable,定义具体的变量以及一些持久化需要的属性
private static final long serialVersionUID = 1L ;
protected String string = null ;
public boolean isStorable(Object value) {
if (value == null ) return true ;
return (String. class == value.getClass());
}
public Object getObject() {
return string;
}
public void setObject(Object value) {
this .string = (String) value;
if (value != null ) {
this .textValue = string;
} else {
this .textValue = null ;
}
}
}