JBPM深入解析之变量设计

JBPM深入解析之变量设计

      在流程的流转的过程中,很多时候我们需要根据不同的实际情况传入一些初始化数据,以便完成我们个性化的业务需求;同时很多时候我们需要在不同的节点之间共享一些业务数据,特别是一些节点要以前一节点的输出作为输入等;变量对于流程引擎来说很重要,可以说没有变量,那么我们就不能运行时动态的设置和传入一些数据,这将极大的限制流程的灵活性!
      变量类型
       全局变量,所有的节点都可以获取并设置该变量的值
       局部变量,只在该节点及其子节点可以获取并设置该变量的值
      变量的传入
       在流程定义中进行变量的定义

      

复制代码
<?xml version="1.0" encoding="UTF-8"?>

<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>
复制代码


       在启动流程流程实例的时候传入全局变量

    Map<String, Object> variables = new HashMap<String, Object>();
    variables.put(
"category""big");
    variables.put(
"dollars"100000);
    Execution execution 
= executionService.startProcessInstanceByKey("TaskVariables", variables);

       在唤醒那些可外部唤醒类型的节点时候传入局部变量

       

    variables = new HashMap<String, Object>();
    variables.put(
"category""small");
    variables.put(
"lires"923874893);
    taskService.completeTask(taskId, variables)

       在任务存在的情况下,可以在任务等待外部唤醒时进行局部变量的设置

    variables = new HashMap<String, Object>();
    variables.put(
"category""small");
    variables.put(
"lires"923874893);    
    taskService.setVariables(taskId, variables);

       在任何时候都可以通过执行服务传入设置全局变量

      

    variables = new HashMap<String, Object>();
    variables.put(
"category""small");
    variables.put(
"lires"923874893);    
    executionService.setVariable(execution.getId(),variables)

 

    variables = new HashMap<String, Object>();
    variables.put(
"category""small");
    variables.put(
"lires"923874893);    
    executionService.signalExecutionById(execution.getId(),variables);

      变量架构设计  

      
      TypeSet、DefaultTypeSet
      TypeSet定义了流程引擎中通过变量类型和matcher两种方式查找变量类型的接口;DefaultTypeSet对TypeSet的接口进行了实现,并定义了一个List<TypeMapping>来装载所有的变量类型的映射变量实例。
      TypeSet

     

复制代码
package org.jbpm.pvm.internal.type;


/**
 * 
@author Tom Baeyens
 
*/
public interface TypeSet {

  Type findTypeByMatch(String key, Object value);
  Type findTypeByName(String typeName);

}
复制代码


      DefaultTypeSet

     

复制代码
package org.jbpm.pvm.internal.type;

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引入变量类型配置的文件路径

复制代码
<process-engine-context>
 
   
<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>

  
<!-- 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 class TypesBinding extends WireDescriptorBinding {

  
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
        

复制代码
public class TypesDescriptor extends AbstractDescriptor {

  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
      

复制代码
public class TypeMapping implements Serializable {

  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对应的类的变量类型相同的。
     

复制代码
public class ClassNameMatcher implements 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

        

复制代码
public class Type implements Serializable {

  
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,负责变量运行时的表现形式和持久化形式进行转化
      

复制代码
public class SerializableToBytesConverter implements Converter {

  
private static final long serialVersionUID = 1L;
  
  
public boolean supports(Object value, ScopeInstanceImpl scopeInstance, Variable variable) {
    
if (value==nullreturn 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.classfalse);
      
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.classfalse);
      
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,定义具体的变量以及一些持久化需要的属性

       

复制代码
public class StringVariable extends Variable {
  
  
private static final long serialVersionUID = 1L;
  
  
protected String string = null;

  
public boolean isStorable(Object value) {
    
if (value==nullreturn 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;
    }
  }
}
复制代码


 

posted @   无风听海  阅读(3025)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示