-
Notifications
You must be signed in to change notification settings - Fork 10
[Core] SubProcess and Multiple Instances
서브프로세스는 프로세스 내의 어떤 구역을 정해서 구역 내의 액티비티들을 멀티플인스턴스로 여러번 실행할 수 있도록 해준다. 멀티플 인스턴스가 발생한 구역내에서의 변수값은 쪼개져서 내부의 영역에 전달 될 수 있다.
protected void transferValues(ProcessInstance instance, ProcessInstance subProcessInstance, RoleMapping currentRoleMapping, Serializable currentVariableValue, int mappingIndex) throws Exception{
//transfer the values of variables
if(variableBindings !=null)
for(int i=0; i<variableBindings.size(); i++){
ParameterContext pvpc = variableBindings.get(i);
if(pvpc.getVariable()==null || pvpc.getArgument()==null
|| (pvpc.getDirection()!=null && pvpc.getDirection().equals(ParameterContext.DIRECTION_OUT))
)
continue;
boolean split = ((SubProcessParameterContext)pvpc).isSplit();
Object val;
if(getForEachVariable()!=null && getForEachVariable() == pvpc.getVariable()){
split = true;
}
String currExecutionScope = instance.getExecutionScopeContext().getExecutionScope();
instance.setExecutionScope(instance.getMainExecutionScope());
ProcessVariableValue pvv = pvpc.getVariable().getMultiple(instance, "");
instance.setExecutionScope(currExecutionScope);
if(pvv==null)
val=null;
else{
if((pvpc instanceof SubProcessParameterContext) && split){
if(pvv.size() <= mappingIndex){
mappingIndex = pvv.size() - 1; //move to the last element
}
VariablePointer vp = new VariablePointer();
vp.setKey(pvv.getName());
vp.setIndex(mappingIndex);
val = vp;
}else{
VariablePointer vp = new VariablePointer();
vp.setKey(pvv.getName());
val = vp;
}
}
instance.addDebugInfo("[SubProcessActivity] transferring main process' variable: ", pvpc.getVariable());
instance.addDebugInfo(" to sub process' variable: ", pvpc.getArgument().getText());
subProcessInstance.set("", pvpc.getVariable().getName(), (Serializable)val);
}
SubProcess 내에 HumanActivity 를 넣고, SubProcess 설정의 forEachVariable 을 배열 값을 가진 변수를 입력해주면, 다중값의 각 값들에 대하여 HumanActivity 를 실행하게 된다.
+------- Sub -------+
HumanActivity(1) ---> | HumanActivity(2) |
+-------------------+
- parameters
-- variable: var1
-- direction: out
- forEachVariable: var1
- variableBindings:
-- parameterContext
---- variable: var1
---- split: true
- parameters
-- variable: var1
-- direction: in
- var1: String
SubProcess 의 분배에 의한 각 HumanActivity(2) 의 input 값의 배분 결과
그리고 그 중 하나의 HumanActivity(2) 를 선택했을때 해당 ExecutionScope 의 값만을 얻어온 결과
bpm_worklist. execscope 에 SubActivity 내에 발생한 HumanActivity 들이 있다면, 워크아이템을 열거나 완료시에 어떤 ExecutionScope 에서 이들을 실행해야 할지를 저장해 놓는다.
저장해 놓은 ExecutionScope 값을 기반으로 워크아이템을 열기 위하여 워크아이템 핸들러 UI 객체 (mw3 객체)에서는 executionScope 값을 property 값으로 갖는다):
WorkItemHandler.java : setExecutionScope
해당 값은 WorkItem.java 의 detail 에서 값을 얻어온다.
public void detail() throws Exception {
IWorkItem workItem = databaseMe();
Long instId = workItem.getInstId(); //since it knows metaworks IDAO will load all the field members from the table beyond the listed by setter/getter.
String tracingTag = (String) workItem.get("trcTag"); //since it knows metaworks IDAO will load all the field members from the table beyond the listed by setter/getter.
//WorkItemHandler workItemHandler;
String tool = workItem.getTool();
if (tool != null && tool.startsWith("mw3.")) {
String metaworksHandler =
tool.substring(4);
workItemHandler = (WorkItemHandler) Thread.currentThread().getContextClassLoader().loadClass(metaworksHandler).newInstance();
} else {
if (workItemHandler != null && workItemHandler.getTaskId() != null) {
workItemHandler = null;
} else {
workItemHandler = MetaworksRemoteService.getComponent(WorkItemHandler.class);
}
}
if (workItemHandler != null) {
workItemHandler.processManager = codiPmSVC;
workItemHandler.setInstanceId(instId.toString());
workItemHandler.setTaskId(getTaskId());
workItemHandler.setTracingTag(tracingTag);
workItemHandler.setRootInstId(this.getRootInstId());
workItemHandler.setExecutionScope(workItem.getExecScope());
workItemHandler.setMetaworksContext(new MetaworksContext());
boolean editable = false;
boolean existRoleMapping = false;
if (DefaultWorkList.WORKITEM_STATUS_NEW.equals(getStatus()) ||
DefaultWorkList.WORKITEM_STATUS_DRAFT.equals(getStatus()) ||
DefaultWorkList.WORKITEM_STATUS_CONFIRMED.equals(getStatus())) {
ProcessInstance instance = processManager.getProcessInstance(this.getInstId().toString());
HumanActivity humanActivity = (HumanActivity) instance.getProcessDefinition().getActivity(tracingTag);
RoleMapping roleMapping = humanActivity.getRole().getMapping(instance, tracingTag);
if (roleMapping != null) {
existRoleMapping = true;
roleMapping.beforeFirst();
do {
if (roleMapping.getEndpoint().equals(session.getEmployee().getEmpCode())) {
editable = true;
break;
}
} while (roleMapping.next());
}
}
if (editable) {
if (this.getDispatchOption() == 1) {
workItemHandler.getMetaworksContext().setWhen("compete");
} else {
workItemHandler.getMetaworksContext().setWhen(WHEN_EDIT);
}
} else if (!existRoleMapping && this.getDispatchOption() == 1) {
workItemHandler.getMetaworksContext().setWhen("compete");
} else {
workItemHandler.getMetaworksContext().setWhen(WHEN_VIEW);
}
workItemHandler.session = session;
workItemHandler.load();
}
}
ProcessVariableValue processVariableValue = pc.getVariable().getMultiple(instance, "”);
에서의 값이 여러값이면 안되고, 쪼개진 각자의 값이어야 한다.
instance.variables hashtable 내의 값을 디버거로 찍어보면:
DefaultProcessInstance.java 의 getSourceValue 가 종국의 변수값을 얻어내는 기능:
public Serializable getSourceValue(String scopeByTracingTag, String key) throws Exception{
ExecutionScopeContext originalScope = getExecutionScopeContext();;
ExecutionScopeContext scope;
Serializable value = null;
do {
scope = getExecutionScopeContext();
String fullKey = createFullKey(scopeByTracingTag, key, false);
if(variables.containsKey(fullKey)){
value = (Serializable) variables.get(fullKey);
break;
}else {
setExecutionScope(getMainExecutionScope());
}
}while(scope != null); //if value is not found in the scope, finding values to the parent scopes as well.
setExecutionScopeContext(originalScope);
return value;
}
createFullKey 에서 얻은 값이 executionScope 에 따라 variable ht key를 정확히 요청해야 한다. 없다면, 상위의 값을 얻어온다. 즉, variable binding 을 해준적이 없다면, 해당 키에 값을 없을 것이므로, slipt 이 발생하지 않겠지… 처음 loop 의 fullKey는 ':1:var1' 이었다. 없다.. 없으면 상위 executionscope 의 값을 찾아본다.
기존 uEngine 3.x 와의 큰 차이점 중 하나는 SubProcess 와 CallActivity 의 구분이다. 기존에는 멀티플 인스턴스를 구성하기 위해서는 필수적으로 프로세스 정의를 쪼개어 나누어야만 했다. 즉, SubProcess와 CallActivity 는 하나였다. uEngine5 는 BPMN 2.0 을 지원하게 되면서 이 둘을 분리하여 하나의 프로세스 정의 내에 여러개의 멀티플 인스턴스 블록을 구성할 수 있게 되었다. 그로인해 오해려 불편해진 점도 있는데, CallActivity (uEngine 3.x 의 SubProcessActivity) 기능과 Multiple Instance 를 한번에 구성하고 싶다면 둘을 조합하여 구성해야 한다는 점이다. 아래의 예제는 이러한 구성을 보여준다.
+------ Sub -----+
| |
HumanActivity(1) ---> | CallActivity |
| |
+----------------+
아래 그림은 실행한 결과 시뮬레이션 화면이다
앞서의 예제는 HumanActivity 를 통하여 멀티플 값을 입력 받고, 이를 HumanActivity 각자가 받아들여 반영할 수 있도록 된 예제이다. 이를 커스텀 액티비티를 통하여 값을 입력, 배분, 반영 하는 예제를 구현한 경우의 예제는 아래와 같다.
+-------- Sub --------+
| |
CreateMultipleValueActivity ---> | DataInputActivity |
| |
+---------------------+
- 변수선언:
var1: String
- CreateMultipleValueActivity 의 선언
outValue: var1
- DataInputActivity 의 선언
outValue: var1
- Sub 의 선언
HumanActivity 를 통한 예제와 동일한 설정
public class CreateMultipleValueActivity extends DefaultActivity {
ProcessVariable outValue;
public ProcessVariable getOutValue() {
return outValue;
}
public void setOutValue(ProcessVariable outValue) {
this.outValue = outValue;
}
@Override
protected void executeActivity(final ProcessInstance instance) throws Exception {
ProcessVariableValue pvv = new ProcessVariableValue();
pvv.setValue("a");
pvv.setValue("b");
pvv.setValue("c");
getOutValue().set(instance, "", pvv);
super.executeActivity(instance);
}
}
public class DataInputActivity extends DefaultActivity {
ProcessVariable outValue;
public ProcessVariable getOutValue() {
return outValue;
}
public void setOutValue(ProcessVariable outValue) {
this.outValue = outValue;
}
@Override
protected void executeActivity(final ProcessInstance instance) throws Exception {
getOutValue().set(instance, "", "output of " + getInputValue());
super.executeActivity(instance);
}
}
이렇게 하면 멀티플로 발생한 각 브랜치의 변경값이 메인프로세스 변수값에도 반영되어, main 의 process 변수값은 아래와 같이 된다:
output of a
output of b
output of c