
org.jbpm.compiler.ProcessBuilderImpl Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jbpm.compiler;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.FactoryConfigurationError;
import org.drools.base.definitions.InternalKnowledgePackage;
import org.drools.base.definitions.ProcessPackage;
import org.drools.base.definitions.ResourceTypePackageRegistry;
import org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl;
import org.drools.compiler.builder.impl.KnowledgeBuilderImpl;
import org.drools.compiler.compiler.Dialect;
import org.drools.compiler.compiler.DialectCompiletimeRegistry;
import org.drools.compiler.compiler.PackageRegistry;
import org.drools.drl.ast.descr.ActionDescr;
import org.drools.drl.ast.descr.PackageDescr;
import org.drools.drl.ast.descr.ProcessDescr;
import org.drools.drl.parser.DroolsParserException;
import org.drools.drl.parser.ParserError;
import org.drools.mvel.java.JavaDialect;
import org.jbpm.assembler.DuplicateProcess;
import org.jbpm.compiler.xml.XmlProcessReader;
import org.jbpm.compiler.xml.compiler.SemanticKnowledgeBuilderConfigurationImpl;
import org.jbpm.process.builder.ProcessBuildContext;
import org.jbpm.process.builder.ProcessNodeBuilder;
import org.jbpm.process.builder.ProcessNodeBuilderRegistry;
import org.jbpm.process.builder.dialect.ProcessDialect;
import org.jbpm.process.builder.dialect.ProcessDialectRegistry;
import org.jbpm.process.core.Context;
import org.jbpm.process.core.ContextContainer;
import org.jbpm.process.core.context.exception.ActionExceptionHandler;
import org.jbpm.process.core.context.exception.ExceptionHandler;
import org.jbpm.process.core.context.exception.ExceptionScope;
import org.jbpm.process.core.impl.ProcessImpl;
import org.jbpm.process.core.validation.ProcessValidationError;
import org.jbpm.process.core.validation.ProcessValidator;
import org.jbpm.process.core.validation.ProcessValidatorRegistry;
import org.jbpm.workflow.core.Constraint;
import org.jbpm.workflow.core.Node;
import org.jbpm.workflow.core.impl.ConnectionRef;
import org.jbpm.workflow.core.impl.DroolsConsequenceAction;
import org.jbpm.workflow.core.impl.NodeImpl;
import org.jbpm.workflow.core.impl.WorkflowProcessImpl;
import org.jbpm.workflow.core.node.CompositeNode;
import org.jbpm.workflow.core.node.ConstraintTrigger;
import org.jbpm.workflow.core.node.EventNode;
import org.jbpm.workflow.core.node.EventSubProcessNode;
import org.jbpm.workflow.core.node.Split;
import org.jbpm.workflow.core.node.StartNode;
import org.jbpm.workflow.core.node.StateNode;
import org.jbpm.workflow.core.node.Trigger;
import org.kie.api.definition.process.Connection;
import org.kie.api.definition.process.NodeContainer;
import org.kie.api.definition.process.Process;
import org.kie.api.definition.process.WorkflowProcess;
import org.kie.api.io.Resource;
import org.kie.internal.builder.KnowledgeBuilderResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A ProcessBuilder can be used to build processes based on XML files
* containing a process definition.
*/
public class ProcessBuilderImpl implements org.drools.compiler.compiler.ProcessBuilder {
private static final Logger logger = LoggerFactory.getLogger(ProcessBuilderImpl.class);
private KnowledgeBuilderImpl knowledgeBuilder;
private final List errors = new ArrayList<>();
public ProcessBuilderImpl(KnowledgeBuilderImpl packageBuilder) {
this.knowledgeBuilder = packageBuilder;
}
public List getErrors() {
return errors;
}
public void buildProcess(final Process process, Resource resource) {
if (resource != null) {
process.setResource(resource);
}
boolean hasErrors = false;
ProcessValidator validator = ProcessValidatorRegistry.getInstance().getValidator(process, resource);
if (validator == null) {
logger.warn("Could not find validator for process {}.", process.getType());
logger.warn("Continuing without validation of the process {} [{}]", process.getName(), process.getId());
} else {
ProcessValidationError[] errors = validator.validateProcess(process);
if (errors.length != 0) {
hasErrors = true;
for (int i = 0; i < errors.length; i++) {
this.errors.add(new ParserError(resource,
errors[i].toString(),
-1,
-1));
}
}
}
if (!hasErrors) {
// generate and add rule for process
String rules = "package " + process.getPackageName() + "\n";
// NPE for validator
if (validator != null && validator.compilationSupported()) {
rules = generateRules(process);
}
try {
knowledgeBuilder.addPackageFromDrl(new StringReader(rules), resource);
} catch (IOException e) {
// should never occur
logger.error("IOException during addPackageFromDRL", e);
} catch (DroolsParserException e) {
// should never occur
logger.error("DroolsParserException during addPackageFromDRL", e);
}
PackageRegistry pkgRegistry = this.knowledgeBuilder.getOrCreatePackageRegistry(new PackageDescr(process.getPackageName()));
if (pkgRegistry != null) {
InternalKnowledgePackage p = pkgRegistry.getPackage();
ResourceTypePackageRegistry resourceTypePackages = p.getResourceTypePackages();
ProcessPackage rpkg = ProcessPackage.getOrCreate(resourceTypePackages);
if (validator != null) {
// NPE for validator
if (validator.compilationSupported()) {
ProcessDescr processDescr = new ProcessDescr();
processDescr.setName(process.getPackageName() + "." + process.getName());
processDescr.setResource(resource);
processDescr.setProcessId(process.getId());
DialectCompiletimeRegistry dialectRegistry = pkgRegistry.getDialectCompiletimeRegistry();
Dialect dialect = dialectRegistry.getDialect("java");
dialect.init(processDescr);
ProcessBuildContext buildContext = new ProcessBuildContext(
this.knowledgeBuilder,
p,
process,
processDescr,
dialectRegistry,
dialect);
buildContexts((ContextContainer) process, buildContext);
if (process instanceof WorkflowProcess) {
buildNodes((WorkflowProcess) process, buildContext);
}
}
Process duplicateProcess = rpkg.getRuleFlows().get(process.getId());
if (duplicateProcess != null) {
Resource duplicatedResource = duplicateProcess.getResource();
if (resource == null || duplicatedResource == null || duplicatedResource.getSourcePath() == null ||
duplicatedResource.getSourcePath().equals(resource.getSourcePath())) {
this.errors.add(new DuplicateProcess(process,
this.knowledgeBuilder.getBuilderConfiguration()));
} else {
this.errors.add(new ParserError(resource,
"Process with same id already exists: " + process.getId(),
-1,
-1));
}
}
rpkg.add(process);
// NPE for validator
if (validator.compilationSupported()) {
pkgRegistry.compileAll();
pkgRegistry.getDialectRuntimeRegistry().onBeforeExecute();
}
}
} else {
// invalid package registry..there is an issue with the package
// name of the process
throw new RuntimeException("invalid package name");
}
}
}
public void buildContexts(ContextContainer contextContainer, ProcessBuildContext buildContext) {
List exceptionScopes = contextContainer.getContexts(ExceptionScope.EXCEPTION_SCOPE);
if (exceptionScopes != null) {
for (Context context : exceptionScopes) {
// TODO: OCRAM: add compensation scope to process builder????
ExceptionScope exceptionScope = (ExceptionScope) context;
for (ExceptionHandler exceptionHandler : exceptionScope.getExceptionHandlers().values()) {
if (exceptionHandler instanceof ActionExceptionHandler) {
DroolsConsequenceAction action = (DroolsConsequenceAction) ((ActionExceptionHandler) exceptionHandler).getAction();
ActionDescr actionDescr = new ActionDescr();
actionDescr.setText(action.getConsequence());
actionDescr.setResource(buildContext.getProcessDescr().getResource());
ProcessDialect dialect = ProcessDialectRegistry.getDialect(action.getDialect());
dialect.getActionBuilder().build(buildContext, action, actionDescr, (ProcessImpl) buildContext.getProcess());
}
}
}
}
}
public void buildNodes(WorkflowProcess process, ProcessBuildContext context) {
processNodes(process.getNodes(), process, context.getProcessDescr(), context);
if (!context.getErrors().isEmpty()) {
this.errors.addAll(context.getErrors());
}
ProcessDialectRegistry.getDialect(JavaDialect.ID).addProcess(context);
}
private void processNodes(
org.kie.api.definition.process.Node[] nodes, Process process, ProcessDescr processDescr,
ProcessBuildContext context) {
for (org.kie.api.definition.process.Node node : nodes) {
ProcessNodeBuilder builder = ProcessNodeBuilderRegistry.INSTANCE.getNodeBuilder(node);
if (builder != null) {
// only build if there is a registered builder for this node type
builder.build(process,
processDescr,
context,
node);
}
if (node instanceof NodeContainer) {
processNodes(((NodeContainer) node).getNodes(),
process,
processDescr,
context);
}
if (node instanceof ContextContainer) {
buildContexts((ContextContainer) node,
context);
}
if ("true".equals(System.getProperty("jbpm.enable.multi.con"))) {
builder = ProcessNodeBuilderRegistry.INSTANCE.getNodeBuilder(NodeImpl.class);
if (builder != null) {
builder.build(process, processDescr, context, node);
}
}
}
}
public List addProcessFromXml(final Resource resource) throws IOException {
Reader reader = resource.getReader();
KnowledgeBuilderConfigurationImpl configuration = knowledgeBuilder.getBuilderConfiguration();
XmlProcessReader xmlReader = new XmlProcessReader(((SemanticKnowledgeBuilderConfigurationImpl) configuration).getSemanticModules(), knowledgeBuilder.getRootClassLoader());
List processes = null;
try {
processes = xmlReader.read(reader);
if (processes != null) {
// it is possible an xml file could not be parsed, so we need to
// stop null pointers
for (Process process : processes) {
buildProcess(process, resource);
xmlReader.getProcessBuildData().onBuildComplete(process);
}
} else {
// @TODO could we maybe add something a bit more informative about what is wrong with the XML ?
this.errors.add(new ProcessLoadError(resource, "unable to parse xml", null));
}
} catch (FactoryConfigurationError e1) {
this.errors.add(new ProcessLoadError(resource, "FactoryConfigurationError ", e1.getException()));
} catch (Exception e2) {
e2.printStackTrace();
this.errors.add(new ProcessLoadError(resource, "unable to parse xml", e2));
} finally {
reader.close();
}
return processes;
}
private String generateRules(final Process process) {
StringBuffer builder = new StringBuffer();
if (process instanceof WorkflowProcessImpl) {
WorkflowProcessImpl ruleFlow = (WorkflowProcessImpl) process;
builder.append("package " + ruleFlow.getPackageName() + "\n");
Set imports = ruleFlow.getImports();
if (imports != null) {
for (String importString : imports) {
builder.append("import " + importString + ";\n");
}
}
List functionImports = ruleFlow.getFunctionImports();
if (functionImports != null) {
for (String importString : functionImports) {
builder.append("import function " + importString + ";\n");
}
}
Map globals = ruleFlow.getGlobals();
if (globals != null) {
for (Map.Entry entry : globals.entrySet()) {
builder.append("global " + entry.getValue() + " " + entry.getKey() + ";\n");
}
}
org.kie.api.definition.process.Node[] nodes = ruleFlow.getNodes();
generateRules(nodes, process, builder);
}
return builder.toString();
}
private void generateRules(org.kie.api.definition.process.Node[] nodes, Process process, StringBuffer builder) {
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] instanceof Split) {
Split split = (Split) nodes[i];
if (split.getType() == Split.TYPE_XOR || split.getType() == Split.TYPE_OR) {
for (Connection connection : split.getDefaultOutgoingConnections()) {
Collection constraints = split.getConstraints(connection);
if (constraints != null) {
for (Constraint constraint : constraints)
if (constraint != null && "rule".equals(constraint.getType())) {
builder.append(createSplitRule(process,
connection,
constraint.getConstraint()));
}
}
}
}
} else if (nodes[i] instanceof StateNode) {
StateNode state = (StateNode) nodes[i];
builder.append(createStateRules(process, state));
} else if (nodes[i] instanceof StartNode) {
StartNode startNode = (StartNode) nodes[i];
List triggers = startNode.getTriggers();
if (triggers != null) {
for (Trigger trigger : triggers) {
if (trigger instanceof ConstraintTrigger) {
builder.append(createStartConstraintRule(process, startNode.getParentContainer(),
(ConstraintTrigger) trigger));
}
}
}
} else if (nodes[i] instanceof NodeContainer) {
generateRules(((NodeContainer) nodes[i]).getNodes(), process, builder);
} else if (nodes[i] instanceof EventNode) {
EventNode state = (EventNode) nodes[i];
builder.append(createEventStateRule(process, state));
}
}
}
private String createSplitRule(Process process,
Connection connection,
String constraint) {
return "rule \"RuleFlow-Split-" + process.getId() + "-" +
((Node) connection.getFrom()).getUniqueId() + "-" +
((Node) connection.getTo()).getUniqueId() + "-" +
connection.getToType() + "\" @Propagation(EAGER) \n" +
" ruleflow-group \"DROOLS_SYSTEM\" \n" +
" when \n" +
" " + constraint + "\n" +
" then \n" +
"end \n\n";
}
private String createStateRule(Process process, StateNode state, ConnectionRef key, Constraint constraint) {
if (constraint.getConstraint() == null
|| constraint.getConstraint().trim().length() == 0) {
return "";
} else {
return "rule \"RuleFlowStateNode-" + process.getId() + "-" + state.getUniqueId() + "-" +
key.getNodeId().toExternalFormat() + "-" + key.getToType() + "\" @Propagation(EAGER) \n" +
" ruleflow-group \"DROOLS_SYSTEM\" \n" +
" when \n" +
" " + state.internalGetConstraint(key).getConstraint() + "\n" +
" then \n" +
"end \n\n";
}
}
private String createEventStateRule(Process process, EventNode event) {
String condition = (String) event.getMetaData("Condition");
String attachedTo = (String) event.getMetaData("AttachedTo");
if (condition == null
|| condition.trim().length() == 0) {
return "";
} else {
return "rule \"RuleFlowStateEvent-" + process.getId() + "-" + event.getUniqueId() + "-" +
attachedTo + "\" @Propagation(EAGER) \n" +
" ruleflow-group \"DROOLS_SYSTEM\" \n" +
" when \n" +
" " + condition + "\n" +
" then \n" +
"end \n\n";
}
}
private String createEventSubprocessStateRule(Process process, CompositeNode compositeNode,
ConstraintTrigger trigger) {
String condition = trigger.getConstraint();
if (condition == null
|| condition.trim().length() == 0) {
return "";
} else {
return "rule \"RuleFlowStateEventSubProcess-Event-" + process.getId() + "-" + compositeNode.getUniqueId() + "\" @Propagation(EAGER) \n" +
" ruleflow-group \"DROOLS_SYSTEM\" \n" +
" when \n" +
" " + condition + "\n" +
" then \n" +
"end \n\n";
}
}
private String createStateRules(Process process, StateNode state) {
StringBuilder result = new StringBuilder();
for (Map.Entry> entry : state.getConstraints().entrySet()) {
for (Constraint constraint : entry.getValue()) {
result.append(createStateRule(process, state, entry.getKey(), constraint));
}
}
return result.toString();
}
private String createStartConstraintRule(Process process, NodeContainer nodeContainer,
ConstraintTrigger trigger) {
if (nodeContainer instanceof EventSubProcessNode) {
return createEventSubprocessStateRule(process, (EventSubProcessNode) nodeContainer, trigger);
}
StringBuilder result =
new StringBuilder("rule \"RuleFlow-Start-" + process.getId() + "\" @Propagation(EAGER) \n" +
(trigger.getHeader() == null ? "" : " " + trigger.getHeader() + " \n") +
" when\n" +
" " + trigger.getConstraint() + "\n" +
" then\n");
Map inMappings = trigger.getInMappings();
if (inMappings != null && !inMappings.isEmpty()) {
result.append(" java.util.Map params = new java.util.HashMap();\n");
for (Map.Entry entry : inMappings.entrySet()) {
result.append(" params.put(\"").append(entry.getValue()).append("\", ").append(entry.getKey()).append(");\n");
}
result.append(" ((org.jbpm.process.instance.ProcessRuntimeImpl)((org.drools.core.common.InternalWorkingMemory)kcontext.getKieRuntime()).getProcessRuntime()).startProcess(\"")
.append(process.getId()).append("\", params, \"conditional\");\n").append("end\n\n");
} else {
result.append(" ((org.jbpm.process.instance.ProcessRuntimeImpl)((org.drools.core.common.InternalWorkingMemory)kcontext.getKieRuntime()).getProcessRuntime()).startProcess(\"")
.append(process.getId()).append("\", null, \"conditional\");\n").append("end\n\n");
}
return result.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy