[Open Source Project] Research on snakeflow process engine

Project address

https://gitee.com/yuqs/snakerflow

https://toscode.mulanos.cn/zc-libre/snakerflow-spring-boot-stater (recommended)

https://github.com/snakerflow-starter/snakerflow-spring-boot-starter

Common API

Deployment process

processId = engine.process().deploy(StreamHelper.
                getStreamFromClasspath("test/task/simple/leave.snaker"), null, 10001L);

Create process instance

Order order = engine.startInstanceById(processId, "1", args);

Perform tasks

 List<Task> tasks = engine.executeTask(activeTasks.get(0).getId(), "1");

Get a person’s tasks that require approval

 List<Task> activeTasks2 = engine.query().getActiveTasks(new QueryFilter().setOperator("admin"));

Leave process xml configuration

<process displayName="Leave process test" instanceUrl="/snaker/flow/all" name="leave">
<start displayName="start1" layout="24,124,-1,-1" name="start1" postInterceptors="test.task.interceptor.LocalTaskInterceptor" preInterceptors="test.task. interceptor.LocalTaskInterceptor">
<transition g="" name="transition1" offset="0,0" to="apply"/>
</start>
<end displayName="end1" layout="570,124,-1,-1" name="end1"/>
<task assignee="apply.operator" displayName="Leave application" form="/flow/leave/apply" layout="117,122,-1,-1" name="apply\ " performType="ANY">
<transition g="" name="transition2" offset="0,0" to="approveDept"/>
</task>
<task assignee="approveDept.operator" displayName="Department Manager Approval" form="/flow/leave/approveDept" layout="272,122,-1,-1" name="approveDept " performType="ANY">
<transition g="" name="transition3" offset="0,0" to="decision1"/>
</task>
<decision displayName="decision1" expr="#day "> & amp;gt; 2 ? 'transition5' : 'transition4'" layout="426,124,-1,-1\ " name="decision1">
<transition displayName=" &lt;=2 days" g="" name="transition4" offset="0,0" to="end1"/>
<transition displayName=""> & amp;gt;2 days" g="" name="transition5" offset="0,0" to="approveBoss"/>
</decision>
<task assignee="approveBoss.operator" displayName="General Manager Approval" form="/flow/leave/approveBoss" layout="404,231,-1,-1" name="approveBoss " performType="ANY">
<transition g="" name="transition6" offset="0,0" to="end1"/>
</task>
</process>

Process analysis

Create process instance

SnakerEngineImpl#startInstanceById(), obtains the StartModel object and executes the execute method.

 public Order startInstanceById(String id, String operator, Map<String, Object> args) {<!-- -->
if(args == null) args = new HashMap<String, Object>();
Process process = process().getProcessById(id);
process().check(process, id);
return startProcess(process, operator, args);
}

private Order startProcess(Process process, String operator, Map<String, Object> args) {<!-- -->
Execution execution = execute(process, operator, args, null, null);
if(process.getModel() != null) {<!-- -->
StartModel start = process.getModel().getStart();
AssertHelper.notNull(start, "Process definition [name=" + process.getName() + ", version=" + process.getVersion() + "] has no start node");
start.execute(execution);
}

return execution.getOrder();
}

NodeModel#execute, execute interceptor and exec method.

 public void execute(Execution execution) {<!-- -->
intercept(preInterceptorList, execution);
exec(execution);
intercept(postInterceptorList, execution);
}

StartModel#exec

 protected void exec(Execution execution) {<!-- -->
runOutTransition(execution);
}

NodeModel#runOutTransition, obtains the TransitionModel object and executes the TransitionModel#execute method.

 /**
* Run the transition to continue execution
* @param execution execution object
*/
protected void runOutTransition(Execution execution) {<!-- -->
for (TransitionModel tm : getOutputs()) {<!-- -->
tm.setEnabled(true);
tm.execute(execution);
}
}

TransitionModel#execute, if the next node is a TaskModel object, execute CreateTaskHandler

 public void execute(Execution execution) {<!-- -->
if(!enabled) return;
if(target instanceof TaskModel) {<!-- -->
//If the target node model is TaskModel, create the task
fire(new CreateTaskHandler((TaskModel)target), execution);
} else if(target instanceof SubProcessModel) {<!-- -->
//If the target node model is SubProcessModel, start the subprocess
fire(new StartSubProcessHandler((SubProcessModel)target), execution);
} else {<!-- -->
//If the target node model is other control types, continue to be executed by the target node
target.execute(execution);
}
}

Create tasks

CreateTaskHandler#handle

 /**
* Based on the task model and execution object, create the next task and add it to the tasks collection of the execution object
*/
public void handle(Execution execution) {<!-- -->
List<Task> tasks = execution.getEngine().task().createTask(model, execution);
execution.addTasks(tasks);
/**
* Find the task interceptor list from the service context and intercept the task collection in sequence.
*/
List<SnakerInterceptor> interceptors = ServiceContext.getContext().findList(SnakerInterceptor.class);
try {<!-- -->
for(SnakerInterceptor interceptor : interceptors) {<!-- -->
interceptor.intercept(execution);
}
} catch(Exception e) {<!-- -->
log.error("Interceptor execution failed=" + e.getMessage());
throw new SnakerException(e);
}
}

TaskService#createTask, obtains the executor of the task, and generates multiple tasks or a single task according to performType.

 public List<Task> createTask(TaskModel taskModel, Execution execution) {<!-- -->
List<Task> tasks = new ArrayList<Task>();
\t\t
Map<String, Object> args = execution.getArgs();
if(args == null) args = new HashMap<String, Object>();
Date expireDate = DateHelper.processTime(args, taskModel.getExpireTime());
Date remindDate = DateHelper.processTime(args, taskModel.getReminderTime());
String form = (String)args.get(taskModel.getForm());
String actionUrl = StringHelper.isEmpty(form) ? taskModel.getForm() : form;
\t\t
String[] actors = getTaskActors(taskModel, execution);
args.put(Task.KEY_ACTOR, StringHelper.getStringByArray(actors));
Task task = createTaskBase(taskModel, execution);
task.setActionUrl(actionUrl);
task.setExpireDate(expireDate);
task.setExpireTime(DateHelper.parseTime(expireDate));
        task.setVariable(JsonHelper.toJson(args));
\t\t
if(taskModel.isPerformAny()) {<!-- -->
//The task execution method is that any one of the participants can drive the process to continue flowing. This method only generates one task.
task = saveTask(task, actors);
task.setRemindDate(remindDate);
tasks.add(task);
} else if(taskModel.isPerformAll()){<!-- -->
//The task execution method is that each participant must be executed before the process can continue to flow. This method generates the corresponding number of tasks based on the number of participants.
for(String actor : actors) {<!-- -->
                Task singleTask;
                try {<!-- -->
                    singleTask = (Task) task.clone();
                } catch (CloneNotSupportedException e) {<!-- -->
                    singleTask = task;
                }
                singleTask = saveTask(singleTask, actor);
                singleTask.setRemindDate(remindDate);
                tasks.add(singleTask);
}
}
return tasks;
}

TaskService#getTaskActors(), get the assignee in the configuration, if the parameter is defined args.put("apply.operator", "1\ ");, is the value corresponding to the key.

 private String[] getTaskActors(TaskModel model, Execution execution) {<!-- -->
Object assigneeObject = null;
        AssignmentHandler handler = model.getAssignmentHandlerObject();
if(StringHelper.isNotEmpty(model.getAssignee())) {<!-- -->
assigneeObject = execution.getArgs().get(model.getAssignee());
} else if(handler != null) {<!-- -->
            if(handler instanceof Assignment) {<!-- -->
                assigneeObject = ((Assignment)handler).assign(model, execution);
            } else {<!-- -->
                assigneeObject = handler.assign(execution);
            }
}
return getTaskActors(assigneeObject == null ? model.getAssignee() : assigneeObject);
}

TaskService#getTaskActors(java.lang.Object), if the data obtained is a string with English commas, it will be cut according to the commas.

 private String[] getTaskActors(Object actors) {<!-- -->
if(actors == null) return null;
String[] results;
if(actors instanceof String) {<!-- -->
//If the value is a string type, use commas to separate it
return ((String)actors).split(",");
        } else if(actors instanceof List){<!-- -->
            //jackson will convert stirng[] into arraylist, and add the logical judgment of arraylist here, by Red Bean Smoothie 2014.11.21
List<?> list = (List)actors;
results = new String[list.size()];
for(int i = 0; i < list.size(); i + + ) {<!-- -->
results[i] = (String)list.get(i);
}
            return results;
} else if(actors instanceof Long) {<!-- -->
//If it is a Long type, return a String[] of 1 element
results = new String[1];
results[0] = String.valueOf((Long)actors);
return results;
} else if(actors instanceof Integer) {<!-- -->
//If it is an Integer type, return a String[] of 1 element
results = new String[1];
results[0] = String.valueOf((Integer)actors);
return results;
} else if(actors instanceof String[]) {<!-- -->
//If it is String[] type, return directly
return (String[])actors;
} else {<!-- -->
//Other types, throw an unsupported type exception
throw new SnakerException("Task participant object [" + actors + "] type is not supported."
+ "Examples of legal parameters: Long,Integer,new String[]{},'10000,20000',List<String>");
}
}

Judgment model

DecisionModel#exec, obtains the expr attribute in the model and executes the judgment. Determine which TransitionModel to execute based on the execution result

 public void exec(Execution execution) {<!-- -->
log.info(execution.getOrder().getId() + "->decision execution.getArgs():" + execution.getArgs());
if(expression == null) {<!-- -->
expression = ServiceContext.getContext().find(Expression.class);
}
log.info("expression is " + expression);
if(expression == null) throw new SnakerException("The expression parser is empty, please check the configuration.");
String next = null;
if(StringHelper.isNotEmpty(expr)) {<!-- -->
next = expression.eval(String.class, expr, execution.getArgs());
} else if(decide != null) {<!-- -->
next = decide.decide(execution);
}
log.info(execution.getOrder().getId() + "->decision expression[expr=" + expr + "] return result:" + next);
boolean isfound = false;
for(TransitionModel tm : getOutputs()) {<!-- -->
if(StringHelper.isEmpty(next)) {<!-- -->
String expr = tm.getExpr();
if(StringHelper.isNotEmpty(expr) & amp; & amp; expression.eval(Boolean.class, expr, execution.getArgs())) {<!-- -->
tm.setEnabled(true);
tm.execute(execution);
isfound = true;
}
} else {<!-- -->
if(tm.getName().equals(next)) {<!-- -->
tm.setEnabled(true);
tm.execute(execution);
isfound = true;
}
}
}
if(!isfound) throw new SnakerException(execution.getOrder().getId() + "->decision node cannot determine the next execution route");
}

Parse xml into ProcessModel object

ProcessService#deploy(), parses according to ModelParser, and uses cache to avoid wasting time caused by secondary parsing.

 public String deploy(InputStream input, String creator, Long tenantId) {<!-- -->
        AssertHelper.notNull(input);
        try {<!-- -->
            byte[] bytes = StreamHelper.readBytes(input);
            ProcessModel model = ModelParser.parse(bytes);
            Integer version = access().getLatestProcessVersion(model.getName());
            Process entity = new Process();
            entity.setId(StringHelper.getPrimaryKey());
            if (version == null || version < 0) {<!-- -->
                entity.setVersion(0);
            } else {<!-- -->
                entity.setVersion(version + 1);
            }
            entity.setState(STATE_ACTIVE);
            entity.setModel(model);
            entity.setBytes(bytes);
            entity.setCreateTime(DateHelper.getTime());
            entity.setCreator(creator);
            entity.setTenantId(tenantId);
            saveProcess(entity);
            cache(entity);
            return entity.getId();
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
            log.error(e.getMessage());
            throw new SnakerException(e.getMessage(), e.getCause());
        }
    }

ModelParser#parse, obtain node data in xml

 public static ProcessModel parse(byte[] bytes) {<!-- -->
DocumentBuilder documentBuilder = XmlHelper.createDocumentBuilder();
if(documentBuilder != null) {<!-- -->
Document doc = null;
try {<!-- -->
doc = documentBuilder.parse(new ByteArrayInputStream(bytes));
Element processE = doc.getDocumentElement();
ProcessModel process = new ProcessModel();
process.setName(processE.getAttribute(NodeParser.ATTR_NAME));
process.setDisplayName(processE.getAttribute(NodeParser.ATTR_DISPLAYNAME));
process.setExpireTime(processE.getAttribute(NodeParser.ATTR_EXPIRETIME));
process.setInstanceUrl(processE.getAttribute(NodeParser.ATTR_INSTANCEURL));
process.setInstanceNoClass(processE.getAttribute(NodeParser.ATTR_INSTANCENOCLASS));
NodeList nodeList = processE.getChildNodes();
int nodeSize = nodeList.getLength();
for(int i = 0; i < nodeSize; i + + ) {<!-- -->
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {<!-- -->
NodeModel model = parseModel(node);
process.getNodes().add(model);
}
}
\t\t\t\t
//Loop node model, construct source and target of transition input and output
for(NodeModel node : process.getNodes()) {<!-- -->
for(TransitionModel transition : node.getOutputs()) {<!-- -->
String to = transition.getTo();
for(NodeModel node2 : process.getNodes()) {<!-- -->
if(to.equalsIgnoreCase(node2.getName())) {<!-- -->
node2.getInputs().add(transition);
transition.setTarget(node2);
}
}
}
}
return process;
} catch (SAXException e) {<!-- -->
e.printStackTrace();
throw new SnakerException(e);
} catch (IOException e) {<!-- -->
throw new SnakerException(e);
}
} else {<!-- -->
throw new SnakerException("documentBuilder is null");
}
}

Supplement

Data operations

DBAccess Database operation interface class, used to save tasks, processes and process instances. AbstractDBAccess implements DBAccess and defines the sql for modifying and inserting data. If you want to do tenant customization, you can make modifications in AbstractDBAccess.

/**
 * Database access interface
 * Mainly provides tables related to saving, updating, and querying processes
 * @author yuqs
 * @since 1.0
 */
public interface DBAccess {<!-- -->
/**
* Set the specific implementation class according to the access object
* @param accessObject database access object (Connection, etc.)
*/
public void initialize(Object accessObject);
/**
* Save task object
* @param task task object
*/
public void saveTask(Task task);
\t
/**
* Save the process instance object
* @param order process instance object
*/
public void saveOrder(Order order);
\t
/**
* Save CC instance
* @param ccorder CC entity
* @since 1.5
*/
public void saveCCOrder(CCOrder ccorder);
\t
/**
* Save the process definition object
* @param process process definition object
*/
public void saveProcess(Process process);
\t
/**
* Save task participant object
* @param taskActor task actor object
*/
public void saveTaskActor(TaskActor taskActor);
\t
/**
* Update task object
* @param task task object
*/
public void updateTask(Task task);
\t
/**
* Update process instance object
* @param order process instance object
*/
public void updateOrder(Order order);
\t
/**
* Update CC status
* @param ccorder CC entity object
*/
public void updateCCOrder(CCOrder ccorder);
\t
/**
* Update process definition object
* @param process process definition object
*/
public void updateProcess(Process process);

/**
* Delete process definition object
* @param process process definition object
*/
public void deleteProcess(Process process);
\t
/**
* Update process definition categories
* @param type category
* @since 1.5
*/
public void updateProcessType(String id, String type);
\t
/**
* Delete tasks and task participant objects
* @param task task object
*/
public void deleteTask(Task task);
\t
/**
* Delete process instance object
* @param order process instance object
*/
public void deleteOrder(Order order);
\t
/**
* Delete CC record
* @param ccorder CC entity object
*/
public void deleteCCOrder(CCOrder ccorder);
\t
/**
* Delete participants
* @param taskId task id
* @param actors participant collection
*/
public void removeTaskActor(String taskId, String... actors);
\t
/**
* Migration activity instance
* @param order historical process instance object
*/
public void saveHistory(HistoryOrder order);
\t
/**
* Update historical process instance status
* @param order historical process instance object
*/
public void updateHistory(HistoryOrder order);
\t
/**
* Migration activity tasks
* @param task historical task object
*/
public void saveHistory(HistoryTask task);

/**
* Delete historical instance records
* @param historyOrder historical instance
*/
public void deleteHistoryOrder(HistoryOrder historyOrder);

/**
* Delete historical task records
* @param historyTask historical task
*/
public void deleteHistoryTask(HistoryTask historyTask);

    /**
     * Update instance variables (including historical instance table)
     * @param order instance object
     */
    public void updateOrderVariable(Order order);
\t
/**
* Save the delegate object
* @param surrogate delegate proxy object
*/
public void saveSurrogate(Surrogate surrogate);
\t
/**
* Update the delegate object
* @param surrogate delegate proxy object
*/
public void updateSurrogate(Surrogate surrogate);
\t
/**
* Delete the delegate object
* @param surrogate delegate proxy object
*/
public void deleteSurrogate(Surrogate surrogate);
\t
/**
* Query the delegated proxy object based on the primary key id
* @param id primary key id
* @return surrogate delegate proxy object
*/
public Surrogate getSurrogate(String id);
\t
/**
* Query the entrusted agent object based on the authorizer and process name
* @param page paging object
* @param filter query filter
* @return List<Surrogate> Collection of delegate proxy objects
*/
public List<Surrogate> getSurrogate(Page<Surrogate> page, QueryFilter filter);
\t
/**
* Query task objects based on task id
* @param taskId task id
* @return Task task object
*/
public Task getTask(String taskId);
\t
/**
* Get historical task objects based on task ID
* @param taskId historical task id
* @return historical task object
*/
HistoryTask getHistTask(String taskId);
\t
/**
* Query all subtasks based on parent task id
* @param parentTaskId parent task id
* @return List<Task> Active task collection
*/
public List<Task> getNextActiveTasks(String parentTaskId);
\t
/**
* Obtained based on process instance id and task name
* @param orderId process instance id
* @param taskName task name
* @param parentTaskId parent task id
* @return List<Task> Active task collection
*/
public List<Task> getNextActiveTasks(String orderId, String taskName, String parentTaskId);
\t
/**
* Query the collection of all active task participants based on task id
* @param taskId active task id
* @return List<TaskActor> Collection of active task participants
*/
public List<TaskActor> getTaskActorsByTaskId(String taskId);
\t
/**
* Query all historical task participant collections based on task id
* @param taskId historical task id
* @return List<HistoryTaskActor> Collection of historical task participants
*/
public List<HistoryTaskActor> getHistTaskActorsByTaskId(String taskId);
\t
/**
* Query the instance object based on the process instance id
* @param orderId activity process instance id
* @return Order activity process instance object
*/
public Order getOrder(String orderId);
\t
/**
* Get CC records based on process instance id and participant id
* @param orderId activity process instance id
* @param actorIds actor id
* @return Transmission record list
*/
public List<CCOrder> getCCOrder(String orderId, String... actorIds);
\t
/**
* Get the historical process instance object based on the process instance ID
* @param orderId historical process instance id
* @return HistoryOrder historical process instance object
*/
HistoryOrder getHistOrder(String orderId);
\t
/**
* Query the process definition object based on the process definition id
* @param id process definition id
* @return Process process definition object
*/
public Process getProcess(String id);
\t
/**
* Query the latest version number based on the process name
* @param name process name
* @return Integer process definition version number
*/
public Integer getLatestProcessVersion(String name);
\t
/**
* According to the query parameters and paging object, return the paging query results
* @param page paging object
* @param filter query filter
* @return List<Process> Process definition collection
*/
public List<Process> getProcesss(Page<Process> page, QueryFilter filter);
\t
/**
* Paging query process example
* @param page paging object
* @param filter query filter
* @return List<Order> Collection of active process instances
*/
public List<Order> getActiveOrders(Page<Order> page, QueryFilter filter);
\t
/**
* Query active task list by page
* @param page paging object
* @param filter query filter
* @return List<Task> Active task collection
*/
public List<Task> getActiveTasks(Page<Task> page, QueryFilter filter);
\t
/**
* Paging query historical process examples
* @param page paging object
* @param filter query filter
* @return List<HistoryOrder> Collection of historical process instances
*/
public List<HistoryOrder> getHistoryOrders(Page<HistoryOrder> page, QueryFilter filter);
\t
/**
* Paging query of completed historical tasks based on participants
* @param page paging object
* @param filter query filter
* @return List<HistoryTask> historical task collection
*/
public List<HistoryTask> getHistoryTasks(Page<HistoryTask> page, QueryFilter filter);
\t
/**
* According to the query parameters and paging object, return the active work items after paging
* @param page paging object
* @param filter query filter
* @return List<WorkItem> active work items
*/
public List<WorkItem> getWorkItems(Page<WorkItem> page, QueryFilter filter);
\t
/**
* According to the query parameters and paging object, return the paged carbon copy task items
* @param page paging object
* @param filter query filter
* @return List<WorkItem> active work items
*/
public List<HistoryOrder> getCCWorks(Page<HistoryOrder> page, QueryFilter filter);
\t
/**
* Query completed historical task items in paging based on process definition ID and participants
* @param page paging object
* @param filter query filter
* @return List<WorkItem> historical work items
*/
public List<WorkItem> getHistoryWorkItems(Page<WorkItem> page, QueryFilter filter);
\t
/**
* Query a single object based on type clazz, Sql statement, and parameters
* @param clazz type
* @param sql sql statement
* @param args parameter list
* @return result object
*/
public <T> T queryObject(Class<T> clazz, String sql, Object... args);
\t
/**
* Query list objects based on type clazz, Sql statement, and parameters
* @param clazz type
* @param sql sql statement
* @param args parameter list
* @return result object list
*/
public <T> List<T> queryList(Class<T> clazz, String sql, Object... args);
\t
/**
* Paging query list objects based on type clazz, Sql statement, and parameters
* @param page paging object
     * @param filter query filter
* @param clazz type
* @param sql sql statement
* @param args parameter list
* @return result object list
*/
public <T> List<T> queryList(Page<T> page, QueryFilter filter, Class<T> clazz, String sql, Object... args);

    /**
     * Run script file
     */
    public void runScript();
}

Context

public interface Context {<!-- -->
/**
* Register with the service factory based on service name and instance
* @param name service name
* @param object service instance
*/
void put(String name, Object object);
\t
/**
* Register with the service factory according to service name and type
* @param name service name
* @param clazz type
*/
void put(String name, Class<?> clazz);
\t
/**
* Determine whether the given service name exists
* @param name service name
* @return
*/
boolean exist(String name);
\t
/**
* Find service instances based on the given type
* @param clazz type
* @return
*/
<T> T find(Class<T> clazz);
\t
/**
* Find all service instances of this type based on a given type
* @param clazz type
* @return
*/
<T> List<T> findList(Class<T> clazz);
\t
/**
* Find service instances based on given service name and type
* @param name service name
* @param clazz type
* @return
*/
<T> T findByName(String name, Class<T> clazz);
}

Service container class

The commonly used form is DBAccess access = ServiceContext.find(DBAccess.class);,

public interface Context {<!-- -->
/**
* Register with the service factory based on service name and instance
* @param name service name
* @param object service instance
*/
void put(String name, Object object);
\t
/**
* Register with the service factory according to service name and type
* @param name service name
* @param clazz type
*/
void put(String name, Class<?> clazz);
\t
/**
* Determine whether the given service name exists
* @param name service name
* @return
*/
boolean exist(String name);
\t
/**
* Find service instances based on the given type
* @param clazz type
* @return
*/
<T> T find(Class<T> clazz);
\t
/**
* Find all service instances of this type based on a given type
* @param clazz type
* @return
*/
<T> List<T> findList(Class<T> clazz);
\t
/**
* Find service instances based on given service name and type
* @param name service name
* @param clazz type
* @return
*/
<T> T findByName(String name, Class<T> clazz);
}

Configuration#parser(), parses snaker.xml and base.config.xml

 protected void parser() {<!-- -->
if(log.isDebugEnabled()) {<!-- -->
log.debug("Service parsing start......");
}

//Use snaker.xml to configure custom beans by default
String config = ConfigHelper.getProperty("config");
if (StringHelper.isEmpty(config)) {<!-- -->
config = USER_CONFIG_FILE;
}
parser(config);
parser(BASE_CONFIG_FILE);
if (!isCMB()) {<!-- -->
parser(EXT_CONFIG_FILE);
for(Entry<String, Class<?>> entry : txClass.entrySet()) {<!-- -->
if(interceptor != null) {<!-- -->
                    Object instance = interceptor.getProxy(entry.getValue());
                    ServiceContext.put(entry.getKey(), instance);
} else {<!-- -->
                    ServiceContext.put(entry.getKey(), entry.getValue());
}
}
}
\t\t
if(log.isDebugEnabled()) {<!-- -->
log.debug("Service parsing finish......");
}
}

Configuration#parser(java.lang.String), parses the xml file and loads the xml configured class object into the container.

 private void parser(String resource) {<!-- -->
//Parse all configuration nodes and instantiate the class specified by class
DocumentBuilder documentBuilder = XmlHelper.createDocumentBuilder();
try {<!-- -->
if (documentBuilder != null) {<!-- -->
InputStream input = StreamHelper.openStream(resource);
if(input == null) return;
Document doc = documentBuilder.parse(input);
Element configElement = doc.getDocumentElement();
NodeList nodeList = configElement.getChildNodes();
int nodeSize = nodeList.getLength();
for(int i = 0; i < nodeSize; i + + ) {<!-- -->
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {<!-- -->
Element element = (Element)node;
String name = element.getAttribute("name");
String className = element.getAttribute("class");
String proxy = element.getAttribute("proxy");
if(StringHelper.isEmpty(name)) {<!-- -->
name = className;
}
if(ServiceContext.exist(name)) {<!-- -->
log.warn("Duplicate name is:" + name);
continue;
}
Class<?> clazz = ClassHelper.loadClass(className);
if(TransactionInterceptor.class.isAssignableFrom(clazz)) {<!-- -->
interceptor = (TransactionInterceptor)ClassHelper.instantiate(clazz);
ServiceContext.put(name, interceptor);
continue;
}
if(proxy != null & amp; & amp; proxy.equalsIgnoreCase("transaction")) {<!-- -->
txClass.put(name, clazz);
} else {<!-- -->
ServiceContext.put(name, clazz);
}
}
}
}
} catch (Exception e) {<!-- -->
e.printStackTrace();
throw new SnakerException("Resource parsing failed, please check the configuration file [" + resource + "]", e.getCause());
}
}