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

org.drools.core.definitions.impl.KnowledgePackageImpl Maven / Gradle / Ivy

There is a newer version: 9.44.0.Final
Show newest version
/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed 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.drools.core.definitions.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import org.drools.core.base.ClassFieldAccessorCache;
import org.drools.core.base.ClassFieldAccessorStore;
import org.drools.core.common.DroolsObjectInputStream;
import org.drools.core.common.DroolsObjectOutputStream;
import org.drools.core.common.ProjectClassLoader;
import org.drools.core.definitions.InternalKnowledgePackage;
import org.drools.core.definitions.rule.impl.GlobalImpl;
import org.drools.core.definitions.rule.impl.RuleImpl;
import org.drools.core.factmodel.traits.TraitRegistry;
import org.drools.core.facttemplates.FactTemplate;
import org.drools.core.rule.DialectRuntimeRegistry;
import org.drools.core.rule.Function;
import org.drools.core.rule.ImportDeclaration;
import org.drools.core.rule.InvalidRulePackage;
import org.drools.core.rule.JavaDialectRuntimeData;
import org.drools.core.rule.TypeDeclaration;
import org.drools.core.rule.WindowDeclaration;
import org.drools.core.ruleunit.RuleUnitRegistry;
import org.drools.core.util.ClassUtils;
import org.kie.api.definition.process.Process;
import org.kie.api.definition.rule.Global;
import org.kie.api.definition.rule.Query;
import org.kie.api.definition.rule.Rule;
import org.kie.api.definition.type.FactType;
import org.kie.api.internal.io.ResourceTypePackage;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.rule.AccumulateFunction;
import org.kie.soup.project.datamodel.commons.types.TypeResolver;

public class KnowledgePackageImpl
        implements
        InternalKnowledgePackage,
        Externalizable {

    private static final long serialVersionUID = 510l;

    /**
     * Name of the pkg.
     */
    private String name;

    /**
     * Set of all rule-names in this Package.
     */
    private Map rules = new LinkedHashMap<>();

    private Map imports = new HashMap<>();

    private Map functions;

    private Map accumulateFunctions;

    private Set staticImports;

    private Map globals;

    private Map factTemplates;

    private Map ruleFlows;

    // private JavaDialectData packageCompilationData;
    private DialectRuntimeRegistry dialectRuntimeRegistry;

    private Map typeDeclarations = new ConcurrentHashMap<>();

    private Set entryPointsIds = Collections.emptySet();

    private Map windowDeclarations;

    private ClassFieldAccessorStore classFieldAccessorStore;

    private TraitRegistry traitRegistry;

    private Map resourceTypePackages;

    private Map cloningResources = new HashMap<>();

    /**
     * This is to indicate the the package has no errors during the
     * compilation/building phase
     */
    private boolean valid = true;

    private boolean needStreamMode = false;

    /**
     * This will keep a summary error message as to why this package is not
     * valid
     */
    private String errorSummary;

    private transient TypeResolver typeResolver;

    private transient RuleUnitRegistry ruleUnitRegistry;

    private transient AtomicBoolean inUse = new AtomicBoolean(false);

    public KnowledgePackageImpl() {
        this(null);
    }

    /**
     * Construct.
     *
     * @param name The name of this Package.
     */
    public KnowledgePackageImpl(final String name) {
        this.name = name;
        this.accumulateFunctions = Collections.emptyMap();
        this.staticImports = Collections.emptySet();
        this.ruleFlows = Collections.emptyMap();
        this.globals = Collections.emptyMap();
        this.factTemplates = Collections.emptyMap();
        this.functions = Collections.emptyMap();
        this.dialectRuntimeRegistry = new DialectRuntimeRegistry();
        this.classFieldAccessorStore = new ClassFieldAccessorStore();
        this.entryPointsIds = Collections.emptySet();
        this.windowDeclarations = Collections.emptyMap();
        this.resourceTypePackages = Collections.emptyMap();
    }

    public Map getResourceTypePackages() {
        if (resourceTypePackages == Collections.EMPTY_MAP) {
            resourceTypePackages = new HashMap();
        }
        return resourceTypePackages;
    }

    public Collection getRules() {
        return Collections.unmodifiableCollection(rules.values());
    }

    public Function getFunction(String name) {
        return functions.getOrDefault(name, null);
    }

    public Collection getProcesses() {
        Collection processes = getRuleFlows().values();
        List list = new ArrayList(processes.size());
        for (org.kie.api.definition.process.Process process : processes) {
            list.add(process);
        }
        return Collections.unmodifiableCollection(list);
    }

    public Collection getFactTypes() {
        if (typeDeclarations.isEmpty()) {
            return Collections.emptyList();
        }
        List list = new ArrayList();
        for (TypeDeclaration typeDeclaration : typeDeclarations.values()) {
            list.add(typeDeclaration.getTypeClassDef());
        }
        return Collections.unmodifiableCollection(list);
    }

    public Map getFactTypesMap() {
        Map types = new HashMap();
        for (Map.Entry entry : typeDeclarations.entrySet()) {
            types.put(entry.getKey(), entry.getValue().getTypeClassDef());
        }
        return types;
    }

    public Collection getQueries() {
        List list = new ArrayList(rules.size());
        for (RuleImpl rule : rules.values()) {
            if (rule.isQuery()) {
                list.add(rule);
            }
        }
        return Collections.unmodifiableCollection(list);
    }

    public Collection getFunctionNames() {
        return Collections.unmodifiableCollection(functions.keySet());
    }

    public Collection getGlobalVariables() {
        List list = new ArrayList(getGlobals().size());
        for (Map.Entry global : getGlobals().entrySet()) {
            list.add(new GlobalImpl(global.getKey(), global.getValue()));
        }
        return Collections.unmodifiableCollection(list);
    }

    /**
     * Handles the write serialization of the Package. Patterns in Rules may
     * reference generated data which cannot be serialized by default methods.
     * The Package uses PackageCompilationData to hold a reference to the
     * generated bytecode. The generated bytecode must be restored before any
     * Rules.
     *
     * @param stream out the stream to write the object to; should be an instance
     *               of DroolsObjectOutputStream or OutputStream
     */
    public void writeExternal(ObjectOutput stream) throws IOException {
        boolean isDroolsStream = stream instanceof DroolsObjectOutputStream;
        ByteArrayOutputStream bytes = null;
        ObjectOutput out;

        if (isDroolsStream) {
            out = stream;
        } else {
            bytes = new ByteArrayOutputStream();
            out = new DroolsObjectOutputStream(bytes);
        }

        out.writeObject(this.name);
        out.writeObject(this.classFieldAccessorStore);
        out.writeObject(this.dialectRuntimeRegistry);
        out.writeObject(this.typeDeclarations);
        out.writeObject(this.imports);
        out.writeObject(this.staticImports);
        out.writeObject(this.functions);
        out.writeObject(this.accumulateFunctions);
        out.writeObject(this.factTemplates);
        out.writeObject(this.ruleFlows);
        out.writeObject(this.globals);
        out.writeBoolean(this.valid);
        out.writeBoolean(this.needStreamMode);
        out.writeObject(this.rules);
        out.writeObject(this.entryPointsIds);
        out.writeObject(this.windowDeclarations);
        out.writeObject(this.traitRegistry);
        out.writeObject(this.resourceTypePackages);
        // writing the whole stream as a byte array
        if (!isDroolsStream) {
            bytes.flush();
            bytes.close();
            stream.writeObject(bytes.toByteArray());
        }
    }

    /**
     * Handles the read serialization of the Package. Patterns in Rules may
     * reference generated data which cannot be serialized by default methods.
     * The Package uses PackageCompilationData to hold a reference to the
     * generated bytecode; which must be restored before any Rules. A custom
     * ObjectInputStream, able to resolve classes against the bytecode in the
     * PackageCompilationData, is used to restore the Rules.
     *
     * @param stream, the stream to read data from in order to restore the object;
     *                should be an instance of DroolsObjectInputStream or
     *                InputStream
     */
    public void readExternal(ObjectInput stream) throws IOException,
            ClassNotFoundException {
        boolean isDroolsStream = stream instanceof DroolsObjectInputStream;
        DroolsObjectInputStream in = isDroolsStream ? (DroolsObjectInputStream) stream
                : new DroolsObjectInputStream(
                new ByteArrayInputStream(
                        (byte[]) stream.readObject()));

        this.name = (String) in.readObject();
        this.classFieldAccessorStore = (ClassFieldAccessorStore) in.readObject();
        in.setStore(this.classFieldAccessorStore);

        this.dialectRuntimeRegistry = (DialectRuntimeRegistry) in.readObject();
        this.typeDeclarations = (Map) in.readObject();
        this.imports = (Map) in.readObject();
        this.staticImports = (Set) in.readObject();
        this.functions = (Map) in.readObject();
        this.accumulateFunctions = (Map) in.readObject();
        this.factTemplates = (Map) in.readObject();
        this.ruleFlows = (Map) in.readObject();
        this.globals = (Map) in.readObject();
        this.valid = in.readBoolean();
        this.needStreamMode = in.readBoolean();
        this.rules = (Map) in.readObject();
        this.entryPointsIds = (Set) in.readObject();
        this.windowDeclarations = (Map) in.readObject();
        this.traitRegistry = (TraitRegistry) in.readObject();
        this.resourceTypePackages = (Map) in.readObject();

        in.setStore(null);

        if (!isDroolsStream) {
            in.close();
        }
    }

    // ------------------------------------------------------------
    // Instance methods
    // ------------------------------------------------------------

    /**
     * Retrieve the name of this Package.
     *
     * @return The name of this Package.
     */
    public String getName() {
        return this.name;
    }

    public ClassLoader getPackageClassLoader() {
        JavaDialectRuntimeData javaRuntime = (JavaDialectRuntimeData) getDialectRuntimeRegistry().getDialectData("java");
        return javaRuntime.getClassLoader();
    }

    public DialectRuntimeRegistry getDialectRuntimeRegistry() {
        return this.dialectRuntimeRegistry;
    }

    public void setDialectRuntimeRegistry(DialectRuntimeRegistry dialectRuntimeRegistry) {
        this.dialectRuntimeRegistry = dialectRuntimeRegistry;
    }

    public void addImport(final ImportDeclaration importDecl) {
        this.imports.put(importDecl.getTarget(),
                         importDecl);
    }

    public Map getImports() {
        return this.imports;
    }

    public void addTypeDeclaration(final TypeDeclaration typeDecl) {
        this.typeDeclarations.put(typeDecl.getTypeName(),
                                  typeDecl);
    }

    public void removeTypeDeclaration(final String type) {
        this.typeDeclarations.remove(type);
    }

    public Map getTypeDeclarations() {
        return this.typeDeclarations;
    }

    public TypeDeclaration getTypeDeclaration(Class clazz) {
        if (clazz == null) {
            return null;
        }
        TypeDeclaration typeDeclaration = getTypeDeclaration(ClassUtils.getSimpleName(clazz));
        if (typeDeclaration == null) {
            // check if clazz is resolved by any of the type declarations
            for (TypeDeclaration type : this.typeDeclarations.values()) {
                if (type.isValid() && type.matches(clazz)) {
                    typeDeclaration = type;
                    break;
                }
            }
        }
        return typeDeclaration;
    }

    public TypeDeclaration getTypeDeclaration(String type) {
        return this.typeDeclarations.get(type);
    }

    public void addStaticImport(final String functionImport) {
        if (this.staticImports == Collections.EMPTY_SET) {
            this.staticImports = new HashSet(2);
        }
        this.staticImports.add(functionImport);
    }

    public void addFunction(final Function function) {
        if (this.functions == Collections.EMPTY_MAP) {
            this.functions = new HashMap(1);
        }

        this.functions.put(function.getName(),
                           function);
        dialectRuntimeRegistry.getDialectData(function.getDialect()).setDirty(true);
    }

    public Map getFunctions() {
        return this.functions;
    }

    public void addAccumulateFunction(final String name, final AccumulateFunction function) {
        if (this.accumulateFunctions == Collections.EMPTY_MAP) {
            this.accumulateFunctions = new HashMap(1);
        }

        this.accumulateFunctions.put(name,
                                     function);
    }

    public Map getAccumulateFunctions() {
        return this.accumulateFunctions;
    }

    public void removeFunctionImport(final String functionImport) {
        this.staticImports.remove(functionImport);
    }

    public Set getStaticImports() {
        return this.staticImports;
    }

    public void addGlobal(final String identifier,
                          final Class clazz) {
        if (this.globals == Collections.EMPTY_MAP) {
            this.globals = new HashMap(1);
        }
        this.globals.put(identifier,
                         clazz.getName());
    }

    public void removeGlobal(final String identifier) {
        this.globals.remove(identifier);
    }

    public Map getGlobals() {
        return this.globals;
    }

    public void removeFunction(final String functionName) {
        Function function = this.functions.remove(functionName);
        if (function != null) {
            this.dialectRuntimeRegistry.removeFunction(this,
                                                       function);
        }
    }

    public FactTemplate getFactTemplate(final String name) {
        return this.factTemplates.get(name);
    }

    public void addFactTemplate(final FactTemplate factTemplate) {
        if (this.factTemplates == Collections.EMPTY_MAP) {
            this.factTemplates = new HashMap<>(1);
        }
        this.factTemplates.put(factTemplate.getName(),
                               factTemplate);
    }

    /**
     * Add a Rule to this Package.
     *
     * @param rule The rule to add.
     * @throws org.drools.core.rule.DuplicateRuleNameException If the Rule attempting to be added has the
     *                                                         same name as another previously added Rule.
     * @throws org.drools.core.rule.InvalidRuleException       If the Rule is not valid.
     */
    public void addRule(RuleImpl rule) {
        this.rules.put(rule.getName(),
                       rule);
    }

    /**
     * Add a rule flow to this package.
     */
    public void addProcess(Process process) {
        if (this.ruleFlows == Collections.EMPTY_MAP) {
            this.ruleFlows = new HashMap();
        }
        this.ruleFlows.put(process.getId(),
                           process);
    }

    /**
     * Get the rule flows for this package. The key is the ruleflow id. It will
     * be Collections.EMPTY_MAP if none have been added.
     */
    public Map getRuleFlows() {
        return this.ruleFlows;
    }

    /**
     * Rule flows can be removed by ID.
     */
    public void removeRuleFlow(String id) {
        if (!this.ruleFlows.containsKey(id)) {
            throw new IllegalArgumentException("The rule flow with id [" + id + "] is not part of this package.");
        }
        this.ruleFlows.remove(id);
    }

    public void removeRule(RuleImpl rule) {
        this.rules.remove(rule.getName());
        this.dialectRuntimeRegistry.removeRule(this,
                                               rule);
    }

    /**
     * Retrieve a Rule by name.
     *
     * @param name The name of the Rule to retrieve.
     * @return The named Rule, or null if not
     * such Rule has been added to this
     * Package.
     */
    public RuleImpl getRule(final String name) {
        return this.rules.get(name);
    }

    // public JavaDialectData getPackageCompilationData() {
    // return this.packageCompilationData;
    // }

    public String toString() {
        return "[Package name=" + this.name + "]";
    }

    /**
     * Once this is called, the package will be marked as invalid
     */
    public void setError(final String summary) {
        this.errorSummary = summary;
        this.valid = false;
    }

    /**
     * Once this is called, the package will be marked as invalid
     */
    public void resetErrors() {
        this.errorSummary = "";
        this.valid = true;
    }

    /**
     * @return true (default) if there are no build/structural problems.
     */
    public boolean isValid() {
        return this.valid;
    }

    /**
     * This will throw an exception if the package is not valid
     */
    public void checkValidity() {
        if (!isValid()) {
            throw new InvalidRulePackage(this.getErrorSummary());
        }
    }

    /**
     * This will return the error summary (if any) if the package is invalid.
     */
    public String getErrorSummary() {
        return this.errorSummary;
    }

    public boolean equals(final Object object) {
        if (this == object) {
            return true;
        }

        if (object == null || !(object instanceof KnowledgePackageImpl)) {
            return false;
        }

        KnowledgePackageImpl other = (KnowledgePackageImpl) object;

        return this.name.equals(other.name);
    }

    public int hashCode() {
        return this.name.hashCode();
    }

    public void clear() {
        this.rules.clear();
        this.dialectRuntimeRegistry.clear();
        this.ruleFlows.clear();
        this.imports.clear();
        this.functions.clear();
        this.accumulateFunctions.clear();
        this.staticImports.clear();
        this.globals.clear();
        this.factTemplates.clear();
        this.typeDeclarations.clear();
        this.windowDeclarations.clear();
    }

    public FactType getFactType(final String typeName) {
        if (typeName == null || (this.name != null && !typeName.startsWith(this.name + "."))) {
            return null;
        }
        // in case the package name is != null, remove the package name from the
        // beginning of the type name
        String key = this.name == null ? typeName : typeName.substring(this.name.length() + 1);
        TypeDeclaration decl = this.typeDeclarations.get(key);
        if (decl == null) {
            return null;
        } else {
            if (decl.isDefinition()) {
                return decl.getTypeClassDef();
            } else {
                throw new UnsupportedOperationException("KieBase.getFactType should only be used to retrieve declared beans. Class " + typeName + " exists outside DRL ");
            }
        }
    }

    public ClassFieldAccessorStore getClassFieldAccessorStore() {
        return classFieldAccessorStore;
    }

    public void setClassFieldAccessorCache(ClassFieldAccessorCache classFieldAccessorCache) {
        this.classFieldAccessorStore.setClassFieldAccessorCache(classFieldAccessorCache);
    }

    public Set getEntryPointIds() {
        return entryPointsIds;
    }

    public void addEntryPointId(String id) {
        if (entryPointsIds == Collections.EMPTY_SET) {
            entryPointsIds = new HashSet();
        }
        entryPointsIds.add(id);
    }

    public TypeResolver getTypeResolver() {
        return typeResolver;
    }

    public void setTypeResolver(TypeResolver typeResolver) {
        this.typeResolver = typeResolver;
        this.ruleUnitRegistry = new RuleUnitRegistry(typeResolver);
    }

    public RuleUnitRegistry getRuleUnitRegistry() {
        return ruleUnitRegistry;
    }

    public void addWindowDeclaration(WindowDeclaration window) {
        if (windowDeclarations == Collections.EMPTY_MAP) {
            windowDeclarations = new HashMap();
        }
        this.windowDeclarations.put(window.getName(), window);
    }

    public Map getWindowDeclarations() {
        return windowDeclarations;
    }

    public boolean hasTraitRegistry() {
        return traitRegistry != null;
    }

    public TraitRegistry getTraitRegistry() {
        if (traitRegistry == null) {
            traitRegistry = new TraitRegistry();
        }
        return traitRegistry;
    }

    public boolean removeObjectsGeneratedFromResource(Resource resource) {
        List rulesToBeRemoved = removeRulesGeneratedFromResource(resource);
        List typesToBeRemoved = removeTypesGeneratedFromResource(resource);
        List functionsToBeRemoved = removeFunctionsGeneratedFromResource(resource);
        List processesToBeRemoved = removeProcessesGeneratedFromResource(resource);
        boolean resourceTypePackageSomethingRemoved = removeFromResourceTypePackageGeneratedFromResource(resource);
        return !rulesToBeRemoved.isEmpty()
                || !typesToBeRemoved.isEmpty()
                || !functionsToBeRemoved.isEmpty()
                || !processesToBeRemoved.isEmpty()
                || resourceTypePackageSomethingRemoved;
    }

    @Override
    public boolean removeFromResourceTypePackageGeneratedFromResource(Resource resource) {
        boolean somethingWasRemoved = false;
        for (ResourceTypePackage rtp : resourceTypePackages.values()) {
            somethingWasRemoved = rtp.removeResource(resource) || somethingWasRemoved;
        }
        return somethingWasRemoved;
    }

    public List removeTypesGeneratedFromResource(Resource resource) {
        List typesToBeRemoved = getTypesGeneratedFromResource(resource);
        if (!typesToBeRemoved.isEmpty()) {
            JavaDialectRuntimeData dialect = (JavaDialectRuntimeData) getDialectRuntimeRegistry().getDialectData("java");
            for (TypeDeclaration type : typesToBeRemoved) {
                if (type.getTypeClassName() != null) {
                    // the type declaration might not have been built up to actual class, if an error was found first
                    // in this case, no accessor would have been wired
                    classFieldAccessorStore.removeType(type);
                    dialect.remove(type.getTypeClassName());
                }
                removeTypeDeclaration(type.getTypeName());
            }
            dialect.reload();
        }
        return typesToBeRemoved;
    }

    public List removeRulesGeneratedFromResource(Resource resource) {
        List rulesToBeRemoved = getRulesGeneratedFromResource(resource);
        for (RuleImpl rule : rulesToBeRemoved) {
            removeRule(rule);
        }
        return rulesToBeRemoved;
    }

    private List getRulesGeneratedFromResource(Resource resource) {
        List rulesFromResource = new ArrayList();
        for (RuleImpl rule : rules.values()) {
            if (resource.equals(rule.getResource())) {
                rulesFromResource.add(rule);
            }
        }
        return rulesFromResource;
    }

    private List getTypesGeneratedFromResource(Resource resource) {
        List typesFromResource = new ArrayList();
        for (TypeDeclaration type : typeDeclarations.values()) {
            if (resource.equals(type.getResource())) {
                typesFromResource.add(type);
            }
        }
        return typesFromResource;
    }

    public List removeFunctionsGeneratedFromResource(Resource resource) {
        List functionsToBeRemoved = getFunctionsGeneratedFromResource(resource);
        for (Function function : functionsToBeRemoved) {
            removeFunction(function.getName());
        }
        return functionsToBeRemoved;
    }

    private List getFunctionsGeneratedFromResource(Resource resource) {
        List functionsFromResource = new ArrayList();
        for (Function function : functions.values()) {
            if (resource.equals(function.getResource())) {
                functionsFromResource.add(function);
            }
        }
        return functionsFromResource;
    }

    public List removeProcessesGeneratedFromResource(Resource resource) {
        List processesToBeRemoved = getProcessesGeneratedFromResource(resource);
        for (Process process : processesToBeRemoved) {
            removeProcess(process);
        }
        return processesToBeRemoved;
    }

    private void removeProcess(Process process) {
        ruleFlows.remove(process.getId());
    }

    private List getProcessesGeneratedFromResource(Resource resource) {
        List processesFromResource = new ArrayList();
        for (Process process : ruleFlows.values()) {
            if (resource.equals(process.getResource())) {
                processesFromResource.add(process);
            }
        }
        return processesFromResource;
    }

    public boolean needsStreamMode() {
        return needStreamMode;
    }

    public void setNeedStreamMode() {
        this.needStreamMode = true;
    }

    public KnowledgePackageImpl deepCloneIfAlreadyInUse(ClassLoader classLoader) {
        if (inUse.compareAndSet(false, true)) {
            return this;
        }

        if (classLoader instanceof ProjectClassLoader) {
            ClassLoader originalClassLoader = ((JavaDialectRuntimeData) dialectRuntimeRegistry.getDialectData("java")).getRootClassLoader();
            if (originalClassLoader instanceof ProjectClassLoader) {
                ((ProjectClassLoader) classLoader).initFrom((ProjectClassLoader) originalClassLoader);
            }
        }

        return ClassUtils.deepClone(this, classLoader, cloningResources);
    }

    @Override
    public void addCloningResource(String key, Object resource) {
        this.cloningResources.put(key, resource);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy