All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jbpm.compiler.canonical.RuleSetNodeVisitor Maven / Gradle / Ivy

/*
 * 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.canonical;

import java.text.MessageFormat;

import org.drools.ruleunits.api.RuleUnitData;
import org.drools.ruleunits.api.SingletonStore;
import org.drools.ruleunits.impl.AssignableChecker;
import org.drools.ruleunits.impl.GeneratedRuleUnitDescription;
import org.drools.ruleunits.impl.ReflectiveRuleUnitDescription;
import org.drools.ruleunits.impl.factory.RuleUnitComponentFactoryImpl;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.context.variable.VariableScope;
import org.jbpm.ruleflow.core.factory.RuleSetNodeFactory;
import org.jbpm.workflow.core.node.RuleSetNode;
import org.jbpm.workflow.instance.rule.DecisionRuleType;
import org.jbpm.workflow.instance.rule.RuleType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.ruleunit.RuleUnitComponentFactory;
import org.kie.internal.ruleunit.RuleUnitDescription;
import org.kie.kogito.decision.DecisionModels;
import org.kie.kogito.rules.RuleConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.NullLiteralExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.UnknownType;

import static org.jbpm.ruleflow.core.factory.RuleSetNodeFactory.METHOD_DECISION;
import static org.jbpm.ruleflow.core.factory.RuleSetNodeFactory.METHOD_PARAMETER;

public class RuleSetNodeVisitor extends AbstractNodeVisitor {

    public static final Logger logger = LoggerFactory.getLogger(ProcessToExecModelGenerator.class);

    private final ClassLoader contextClassLoader;
    private final AssignableChecker assignableChecker;

    public RuleSetNodeVisitor(ClassLoader contextClassLoader) {
        this.contextClassLoader = contextClassLoader;
        this.assignableChecker = AssignableChecker.create(contextClassLoader);
    }

    @Override
    protected String getNodeKey() {
        return "ruleSetNode";
    }

    @Override
    public void visitNode(String factoryField, RuleSetNode node, BlockStmt body, VariableScope variableScope, ProcessMetaData metadata) {
        String nodeName = node.getName();

        body.addStatement(getAssignedFactoryMethod(factoryField, RuleSetNodeFactory.class, getNodeId(node), getNodeKey(), getWorkflowElementConstructor(node.getId())))
                .addStatement(getNameMethod(node, "Rule"));

        RuleType ruleType = node.getRuleType();
        if (ruleType.getName().isEmpty()) {
            throw new IllegalArgumentException(
                    MessageFormat.format(
                            "Rule task \"{0}\" is invalid: you did not set a unit name, a rule flow group or a decision model.", nodeName));
        }

        addParams(node, body, getNodeId(node));

        NameExpr methodScope = new NameExpr(getNodeId(node));
        MethodCallExpr m;
        if (ruleType.isRuleFlowGroup()) {
            m = handleRuleFlowGroup(ruleType);
        } else if (ruleType.isRuleUnit()) {
            m = handleRuleUnit(variableScope, metadata, node, nodeName, ruleType);
        } else if (ruleType.isDecision()) {
            m = handleDecision((DecisionRuleType) ruleType);
        } else {
            throw new IllegalArgumentException("Rule task " + nodeName + "is invalid: unsupported rule language " + node.getLanguage());
        }
        m.setScope(methodScope);
        body.addStatement(m);
        addNodeMappings(node, body, getNodeId(node));
        visitMetaData(node.getMetaData(), body, getNodeId(node));
        body.addStatement(getDoneMethod(getNodeId(node)));
    }

    private void addParams(RuleSetNode node, BlockStmt body, String nodeId) {
        node.getParameters()
                .forEach((k, v) -> body.addStatement(getFactoryMethod(nodeId, METHOD_PARAMETER, new StringLiteralExpr(k), new StringLiteralExpr(v.toString()))));
    }

    private MethodCallExpr handleDecision(DecisionRuleType ruleType) {

        StringLiteralExpr namespace = new StringLiteralExpr(ruleType.getNamespace());
        StringLiteralExpr model = new StringLiteralExpr(ruleType.getModel());
        Expression decision = ruleType.getDecision() == null ? new NullLiteralExpr() : new StringLiteralExpr(ruleType.getDecision());

        // app.get(org.kie.kogito.decision.DecisionModels.class).getDecisionModel(namespace, model)
        MethodCallExpr decisionModels =
                new MethodCallExpr(new NameExpr("app"), "get")
                        .addArgument(new ClassExpr().setType(DecisionModels.class.getCanonicalName()));
        MethodCallExpr decisionModel =
                new MethodCallExpr(decisionModels, "getDecisionModel")
                        .addArgument(namespace)
                        .addArgument(model);

        BlockStmt actionBody = new BlockStmt();
        LambdaExpr lambda = new LambdaExpr(new Parameter(new UnknownType(), "()"), actionBody);
        actionBody.addStatement(new ReturnStmt(decisionModel));

        return new MethodCallExpr(METHOD_DECISION)
                .addArgument(namespace)
                .addArgument(model)
                .addArgument(decision)
                .addArgument(lambda);
    }

    private MethodCallExpr handleRuleUnit(VariableScope variableScope, ProcessMetaData metadata, RuleSetNode ruleSetNode, String nodeName, RuleType ruleType) {
        String unitName = ruleType.getName();
        ProcessContextMetaModel processContext = new ProcessContextMetaModel(variableScope, contextClassLoader);
        RuleUnitDescription description;

        try {
            Class unitClass = loadUnitClass(unitName, metadata.getPackageName());
            description = new ReflectiveRuleUnitDescription((Class) unitClass);
        } catch (ClassNotFoundException e) {
            logger.warn("Rule task \"{}\": cannot load class {}. " +
                    "The unit data object will be generated.", nodeName, unitName);

            GeneratedRuleUnitDescription d = generateRuleUnitDescription(unitName, processContext);
            RuleUnitComponentFactoryImpl impl = (RuleUnitComponentFactoryImpl) RuleUnitComponentFactory.get();
            impl.registerRuleUnitDescription(d);
            description = d;
        }

        RuleUnitHandler handler = new RuleUnitHandler(description, processContext, ruleSetNode, assignableChecker);
        Expression ruleUnitFactory = handler.invoke();

        return new MethodCallExpr("ruleUnit")
                .addArgument(new StringLiteralExpr(ruleType.getName()))
                .addArgument(ruleUnitFactory);

    }

    private GeneratedRuleUnitDescription generateRuleUnitDescription(String unitName, ProcessContextMetaModel processContext) {
        GeneratedRuleUnitDescription d = new GeneratedRuleUnitDescription(unitName, contextClassLoader);
        for (Variable variable : processContext.getVariables()) {
            d.putDatasourceVar(
                    variable.getName(),
                    SingletonStore.class.getCanonicalName(),
                    variable.getType().getStringType());
        }
        return d;
    }

    private MethodCallExpr handleRuleFlowGroup(RuleType ruleType) {
        // build supplier for rule runtime
        BlockStmt actionBody = new BlockStmt();
        LambdaExpr lambda = new LambdaExpr(new Parameter(new UnknownType(), "()"), actionBody);

        // app.config().get(org.kie.kogito.rules.RuleConfig.class)
        MethodCallExpr ruleConfig = new MethodCallExpr(
                new MethodCallExpr(new NameExpr("app"), "config"), "get")
                        .addArgument(new ClassExpr().setType(RuleConfig.class.getCanonicalName()));

        VariableDeclarationExpr configVar = new VariableDeclarationExpr(new ClassOrInterfaceType(null, RuleConfig.class.getCanonicalName()), "ruleConfig");
        actionBody.addStatement(new AssignExpr(configVar, ruleConfig, AssignExpr.Operator.ASSIGN));

        MethodCallExpr ruleRuntimeSupplier = new MethodCallExpr(
                new NameExpr("org.drools.project.model.ProjectRuntime.INSTANCE"), "newKieSession",
                NodeList.nodeList(new StringLiteralExpr("defaultStatelessKieSession")));

        VariableDeclarationExpr sessionVar = new VariableDeclarationExpr(new ClassOrInterfaceType(null, KieSession.class.getCanonicalName()), "ksession");
        actionBody.addStatement(new AssignExpr(sessionVar, ruleRuntimeSupplier, AssignExpr.Operator.ASSIGN));

        actionBody.addStatement(new JavaParser().parseStatement("ruleConfig.ruleEventListeners().agendaListeners().forEach(ksession::addEventListener);").getResult().get());
        actionBody.addStatement(new JavaParser().parseStatement("ruleConfig.ruleEventListeners().ruleRuntimeListeners().forEach(ksession::addEventListener);").getResult().get());

        actionBody.addStatement(new ReturnStmt("ksession"));

        return new MethodCallExpr("ruleFlowGroup")
                .addArgument(new StringLiteralExpr(ruleType.getName()))
                .addArgument(lambda);

    }

    private Class loadUnitClass(String unitName, String packageName) throws ClassNotFoundException {
        ClassNotFoundException ex;
        try {
            return contextClassLoader.loadClass(unitName);
        } catch (ClassNotFoundException e) {
            ex = e;
        }
        if (packageName == null || packageName.isEmpty()) {
            throw ex;
        }
        // maybe the name is not qualified. Let's try with tacking the packageName at the front
        try {
            return contextClassLoader.loadClass(packageName + "." + unitName);
        } catch (ClassNotFoundException e) {
            // throw the original error
            throw ex;
        }
    }

    private boolean hasClass(String className) {
        try {
            loadUnitClass(className, null);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy