org.drools.compiler.kie.builder.impl.AbstractKieModule Maven / Gradle / Ivy
/*
* Copyright 2015 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.
*
* 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.compiler.kie.builder.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import com.google.protobuf.ExtensionRegistry;
import org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl;
import org.drools.compiler.kie.builder.impl.KieModuleCache.CompDataEntry;
import org.drools.compiler.kie.builder.impl.KieModuleCache.CompilationData;
import org.drools.compiler.kie.builder.impl.KieModuleCache.Header;
import org.drools.compiler.kie.builder.impl.KieModuleCache.KModuleCache;
import org.drools.compiler.kproject.ReleaseIdImpl;
import org.drools.compiler.kproject.models.KieBaseModelImpl;
import org.drools.compiler.kproject.models.KieModuleModelImpl;
import org.drools.compiler.kproject.xml.DependencyFilter;
import org.drools.compiler.kproject.xml.PomModel;
import org.drools.core.RuleBaseConfiguration;
import org.drools.core.builder.conf.impl.DecisionTableConfigurationImpl;
import org.drools.core.builder.conf.impl.ResourceConfigurationImpl;
import org.drools.core.common.ResourceProvider;
import org.drools.core.definitions.impl.KnowledgePackageImpl;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.rule.KieModuleMetaInfo;
import org.drools.core.rule.TypeMetaInfo;
import org.drools.core.util.Drools;
import org.drools.core.util.IoUtils;
import org.drools.core.util.StringUtils;
import org.kie.api.KieBaseConfiguration;
import org.kie.api.builder.Message;
import org.kie.api.builder.ReleaseId;
import org.kie.api.builder.Results;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.builder.model.RuleTemplateModel;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceConfiguration;
import org.kie.api.io.ResourceType;
import org.kie.internal.KnowledgeBaseFactory;
import org.kie.internal.builder.CompositeKnowledgeBuilder;
import org.kie.internal.builder.DecisionTableConfiguration;
import org.kie.internal.builder.DecisionTableInputType;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderConfiguration;
import org.kie.internal.builder.KnowledgeBuilderError;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.builder.KnowledgeBuilderResult;
import org.kie.internal.builder.ResourceChangeSet;
import org.kie.internal.builder.ResultSeverity;
import org.kie.internal.definition.KnowledgePackage;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.io.ResourceTypeImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.drools.compiler.kie.builder.impl.KieBuilderImpl.filterFileInKBase;
import static org.drools.core.util.ClassUtils.convertResourceToClassName;
public abstract class AbstractKieModule
implements
InternalKieModule {
private static final Logger log = LoggerFactory.getLogger(AbstractKieModule.class);
private final Map kBuilders = new HashMap();
private final Map resultsCache = new HashMap();
protected final ReleaseId releaseId;
private final KieModuleModel kModuleModel;
private Map kieDependencies;
// Map< KBaseName, CompilationCache>
protected Map compilationCache = new HashMap();
private Map typesMetaInfo;
private Map resourceConfigurationCache = new HashMap();
protected PomModel pomModel;
private Collection unresolvedDependencies;
public AbstractKieModule(ReleaseId releaseId, KieModuleModel kModuleModel) {
this.releaseId = releaseId;
this.kModuleModel = kModuleModel;
}
public KieModuleModel getKieModuleModel() {
return this.kModuleModel;
}
public Map getKieDependencies() {
return kieDependencies == null ? Collections. emptyMap() : kieDependencies;
}
public void addKieDependency(InternalKieModule dependency) {
if (kieDependencies == null) {
kieDependencies = new HashMap();
}
kieDependencies.put(dependency.getReleaseId(), dependency);
}
public Collection getJarDependencies(DependencyFilter filter) {
if( pomModel == null ) {
getPomModel();
}
Collection deps = null;
if( pomModel != null ) {
deps = pomModel.getDependencies(filter);
}
return deps == null ? Collections. emptyList() : deps;
}
public Collection getUnresolvedDependencies() {
return unresolvedDependencies == null ? Collections. emptyList() : unresolvedDependencies;
}
public void setUnresolvedDependencies(Collection unresolvedDependencies) {
this.unresolvedDependencies = unresolvedDependencies;
}
public ReleaseId getReleaseId() {
return releaseId;
}
public KnowledgeBuilder getKnowledgeBuilderForKieBase(String kieBaseName) {
return kBuilders.get(kieBaseName);
}
public Collection getKnowledgePackagesForKieBase(String kieBaseName) {
KnowledgeBuilder kbuilder = kBuilders.get(kieBaseName);
return kbuilder != null ? kbuilder.getKnowledgePackages() : null;
}
public void cacheKnowledgeBuilderForKieBase(String kieBaseName, KnowledgeBuilder kbuilder) {
kBuilders.put(kieBaseName, kbuilder);
}
public Map getKnowledgeResultsCache() {
return resultsCache;
}
public void cacheResultsForKieBase(String kieBaseName, Results results) {
resultsCache.put(kieBaseName, results);
}
public Map getClassesMap(boolean includeTypeDeclarations) {
Map classes = new HashMap();
for (String fileName : getFileNames()) {
if (fileName.endsWith(".class")) {
if (includeTypeDeclarations || !isTypeDeclaration(fileName)) {
classes.put(fileName, getBytes(fileName));
}
}
}
return classes;
}
private boolean isTypeDeclaration(String fileName) {
Map info = getTypesMetaInfo();
TypeMetaInfo typeInfo = info == null ? null : info.get(convertResourceToClassName(fileName));
return typeInfo != null && typeInfo.isDeclaredType();
}
private Map getTypesMetaInfo() {
if (typesMetaInfo == null) {
byte[] bytes = getBytes(KieModuleModelImpl.KMODULE_INFO_JAR_PATH);
if (bytes != null) {
typesMetaInfo = KieModuleMetaInfo.unmarshallMetaInfos(new String(bytes, IoUtils.UTF8_CHARSET)).getTypeMetaInfos();
}
}
return typesMetaInfo;
}
public InternalKnowledgeBase createKieBase( KieBaseModelImpl kBaseModel, KieProject kieProject, ResultsImpl messages, KieBaseConfiguration conf ) {
Collection pkgs = getKnowledgePackagesForKieBase(kBaseModel.getName());
if ( pkgs == null ) {
KnowledgeBuilder kbuilder = buildKnowledgePackages(kBaseModel, kieProject, messages);
if ( kbuilder.hasErrors() ) {
// Messages already populated by the buildKnowlegePackages
return null;
}
}
// if we get to here, then we know the pkgs is now cached
pkgs = getKnowledgePackagesForKieBase(kBaseModel.getName());
if ( kBaseModel.getEventProcessingMode() == EventProcessingOption.CLOUD &&
(conf == null || conf.getOption(EventProcessingOption.class) == EventProcessingOption.CLOUD ) ) {
for (KnowledgePackage kpkg : pkgs) {
if ( ((KnowledgePackageImpl) kpkg).needsStreamMode() ) {
throw new RuntimeException( "The requested KieBase \"" + kBaseModel.getName() + "\" has been set to run in CLOUD mode but requires features only available in STREAM mode" );
}
}
}
ClassLoader cl = kieProject.getClassLoader();
if (conf == null) {
conf = getKnowledgeBaseConfiguration(kBaseModel, cl);
} else if (conf instanceof RuleBaseConfiguration ) {
((RuleBaseConfiguration)conf).setClassLoader(cl);
}
InternalKnowledgeBase kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase( kBaseModel.getName(), conf );
kBase.addKnowledgePackages( pkgs );
return kBase;
}
private KieBaseConfiguration getKnowledgeBaseConfiguration(KieBaseModelImpl kBaseModel, ClassLoader cl) {
KieBaseConfiguration kbConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, cl);
kbConf.setOption(kBaseModel.getEqualsBehavior());
kbConf.setOption(kBaseModel.getEventProcessingMode());
kbConf.setOption(kBaseModel.getDeclarativeAgenda());
return kbConf;
}
@SuppressWarnings("deprecation")
static KnowledgeBuilder buildKnowledgePackages( KieBaseModelImpl kBaseModel,
KieProject kieProject,
ResultsImpl messages ) {
AbstractKieModule kModule = (AbstractKieModule) kieProject.getKieModuleForKBase(kBaseModel.getName());
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(getBuilderConfiguration(kBaseModel, kieProject, kModule));
CompositeKnowledgeBuilder ckbuilder = kbuilder.batch();
Set assets = new HashSet();
boolean allIncludesAreValid = true;
for (String include : kieProject.getTransitiveIncludes(kBaseModel)) {
if (StringUtils.isEmpty(include)) {
continue;
}
InternalKieModule includeModule = kieProject.getKieModuleForKBase(include);
if (includeModule == null) {
String text = "Unable to build KieBase, could not find include: " + include;
log.error(text);
messages.addMessage(Message.Level.ERROR, KieModuleModelImpl.KMODULE_SRC_PATH, text).setKieBaseName( kBaseModel.getName() );
allIncludesAreValid = false;
continue;
}
addFiles( assets, kieProject.getKieBaseModel(include), includeModule );
}
if (!allIncludesAreValid) {
return null;
}
addFiles( assets, kBaseModel, kModule );
if (assets.isEmpty()) {
if (kModule instanceof FileKieModule) {
log.warn("No files found for KieBase " + kBaseModel.getName() + ", searching folder " + kModule.getFile());
} else {
log.warn("No files found for KieBase " + kBaseModel.getName());
}
} else {
for (Asset asset : assets) {
asset.kmodule.addResourceToCompiler(ckbuilder, kBaseModel, asset.name);
}
}
ckbuilder.build();
if ( kbuilder.hasErrors() ) {
for ( KnowledgeBuilderError error : kbuilder.getErrors() ) {
messages.addMessage( error ).setKieBaseName( kBaseModel.getName() );
}
log.error("Unable to build KieBaseModel:" + kBaseModel.getName() + "\n" + kbuilder.getErrors().toString());
}
if ( kbuilder.hasResults( ResultSeverity.WARNING ) ) {
for ( KnowledgeBuilderResult warn : kbuilder.getResults( ResultSeverity.WARNING ) ) {
messages.addMessage( warn ).setKieBaseName( kBaseModel.getName() );
}
log.warn( "Warning : " + kBaseModel.getName() + "\n" + kbuilder.getResults( ResultSeverity.WARNING ).toString() );
}
// cache KnowledgeBuilder and results
kModule.cacheKnowledgeBuilderForKieBase(kBaseModel.getName(), kbuilder);
kModule.cacheResultsForKieBase(kBaseModel.getName(), messages);
return kbuilder;
}
private static class Asset {
private final InternalKieModule kmodule;
private final String name;
private Asset( InternalKieModule kmodule, String name ) {
this.kmodule = kmodule;
this.name = name;
}
@Override
public boolean equals( Object o ) {
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Asset asset = (Asset) o;
return kmodule.equals( asset.kmodule ) && name.equals( asset.name );
}
@Override
public int hashCode() {
int result = kmodule.hashCode();
result = 31 * result + name.hashCode();
return result;
}
}
private static KnowledgeBuilderConfigurationImpl getBuilderConfiguration(KieBaseModelImpl kBaseModel, KieProject kieProject, AbstractKieModule kModule) {
KnowledgeBuilderConfigurationImpl pconf = new KnowledgeBuilderConfigurationImpl(kieProject.getClonedClassLoader());
pconf.setCompilationCache(kModule.getCompilationCache(kBaseModel.getName()));
setModelPropsOnConf( kBaseModel, pconf );
return pconf;
}
private static void setModelPropsOnConf( KieBaseModelImpl kBaseModel, KnowledgeBuilderConfigurationImpl pconf ) {
KieModuleModel kModuleModel = kBaseModel.getKModule();
for (Map.Entry entry : kModuleModel.getConfigurationProperties().entrySet()) {
pconf.setProperty(entry.getKey(), entry.getValue());
}
}
public KnowledgeBuilderConfiguration getBuilderConfiguration(KieBaseModel kBaseModel) {
KnowledgeBuilderConfigurationImpl pconf = new KnowledgeBuilderConfigurationImpl();
setModelPropsOnConf( (KieBaseModelImpl) kBaseModel, pconf );
return pconf;
}
private static void addFiles(Set assets,
KieBaseModel kieBaseModel,
InternalKieModule kieModule) {
for (String fileName : kieModule.getFileNames()) {
if (!fileName.startsWith(".") && !fileName.endsWith(".properties") && filterFileInKBase(kieModule, kieBaseModel, fileName)) {
assets.add(new Asset( kieModule, fileName ));
}
}
}
public final boolean addResourceToCompiler(CompositeKnowledgeBuilder ckbuilder, KieBaseModel kieBaseModel, String fileName) {
return addResourceToCompiler(ckbuilder, kieBaseModel, fileName, null);
}
public final boolean addResourceToCompiler(CompositeKnowledgeBuilder ckbuilder, KieBaseModel kieBaseModel, String fileName, ResourceChangeSet rcs) {
ResourceConfiguration conf = getResourceConfiguration(fileName);
Resource resource = getResource(fileName);
if (resource != null) {
ResourceType resourceType = conf instanceof ResourceConfigurationImpl && ((ResourceConfigurationImpl)conf).getResourceType() != null ?
((ResourceConfigurationImpl)conf).getResourceType() :
ResourceType.determineResourceType(fileName);
if (resourceType == ResourceType.DTABLE && conf instanceof DecisionTableConfiguration) {
for (RuleTemplateModel template : kieBaseModel.getRuleTemplates()) {
if (template.getDtable().equals( fileName )) {
Resource templateResource = getResource( template.getTemplate() );
if ( templateResource != null ) {
( (DecisionTableConfiguration) conf ).addRuleTemplateConfiguration( templateResource, template.getRow(), template.getCol() );
}
}
}
}
if (conf == null) {
ckbuilder.add(resource, resourceType, rcs);
} else {
ckbuilder.add(resource, resourceType, conf, rcs);
}
return true;
}
return false;
}
public boolean hasResource(String fileName) {
byte[] bytes = getBytes(fileName);
return bytes != null && bytes.length > 0;
}
public Resource getResource(String fileName) {
byte[] bytes = getBytes(fileName);
if (bytes != null && bytes.length > 0) {
return ResourceFactory.newByteArrayResource(bytes).setSourcePath(fileName);
}
return null;
}
public ResourceConfiguration getResourceConfiguration(String fileName) {
ResourceConfiguration conf = resourceConfigurationCache.get(fileName);
if (conf != null) {
return conf;
}
if (isAvailable(fileName + ".properties")) {
// configuration file available
Properties prop = new Properties();
try {
prop.load(new ByteArrayInputStream(getBytes(fileName + ".properties")));
} catch (IOException e) {
log.error("Error loading resource configuration from file: " + fileName + ".properties");
}
conf = ResourceTypeImpl.fromProperties(prop);
} else if (ResourceType.DTABLE.matchesExtension(fileName)) {
int lastDot = fileName.lastIndexOf( '.' );
if (lastDot >= 0 && fileName.length() > lastDot+1) {
String extension = fileName.substring( lastDot+1 );
Properties prop = new Properties();
prop.setProperty(ResourceTypeImpl.KIE_RESOURCE_CONF_CLASS, DecisionTableConfigurationImpl.class.getName());
prop.setProperty(DecisionTableConfigurationImpl.DROOLS_DT_TYPE, DecisionTableInputType.valueOf( extension.toUpperCase() ).toString());
conf = ResourceTypeImpl.fromProperties(prop);
}
}
resourceConfigurationCache.put(fileName, conf);
return conf;
}
protected CompilationCache getCompilationCache(String kbaseName) {
// Map< DIALECT, Map< RESOURCE, List > >
CompilationCache cache = compilationCache.get(kbaseName);
if (cache == null) {
byte[] fileContents = getBytes(KieBuilderImpl.getCompilationCachePath(releaseId, kbaseName));
if (fileContents != null) {
ExtensionRegistry registry = KieModuleCacheHelper.buildRegistry();
try {
Header _header = KieModuleCacheHelper.readFromStreamWithHeaderPreloaded(new ByteArrayInputStream(fileContents), registry);
if (!Drools.isCompatible(_header.getVersion().getVersionMajor(),
_header.getVersion().getVersionMinor(),
_header.getVersion().getVersionRevision())) {
// if cache has been built with an incompatible version avoid to use it
log.warn("The compilation cache has been built with an incompatible version. " +
"You should recompile your project in order to use it with current release.");
return null;
}
KModuleCache _cache = KModuleCache.parseFrom(_header.getPayload());
cache = new CompilationCache();
for (CompilationData _data : _cache.getCompilationDataList()) {
for (CompDataEntry _entry : _data.getEntryList()) {
cache.addEntry(_data.getDialect(), _entry.getId(), _entry.getData().toByteArray());
}
}
compilationCache.put(kbaseName, cache);
} catch (Exception e) {
log.error("Unable to load compilation cache... ", e);
}
}
}
return cache;
}
public PomModel getPomModel() {
if (pomModel == null) {
try {
byte[] pomXml = getPomXml();
if( pomXml != null ) {
PomModel tempPomModel = PomModel.Parser.parse("pom.xml", new ByteArrayInputStream(pomXml));
validatePomModel(tempPomModel); // throws an exception if invalid
pomModel = tempPomModel;
}
} catch( Exception e ) {
// nothing to do as it was not possible to retrieve pom.xml
}
}
return pomModel;
}
public void setPomModel(PomModel pomModel) {
this.pomModel = pomModel;
}
private void validatePomModel(PomModel pomModel) {
ReleaseId pomReleaseId = pomModel.getReleaseId();
if (StringUtils.isEmpty(pomReleaseId.getGroupId()) || StringUtils.isEmpty(pomReleaseId.getArtifactId()) || StringUtils.isEmpty(pomReleaseId.getVersion())) {
throw new RuntimeException("Maven pom.properties exists but ReleaseId content is malformed");
}
}
private byte[] getPomXml() {
return getBytes(((ReleaseIdImpl)releaseId).getPomXmlPath());
}
public InputStream getPomAsStream() {
byte[] pom = getBytes(((ReleaseIdImpl)releaseId).getPomXmlPath());
return pom != null ? new ByteArrayInputStream(pom) : null;
}
public static boolean updateResource(CompositeKnowledgeBuilder ckbuilder,
InternalKieModule kieModule,
String resourceName,
ResourceChangeSet changes) {
ResourceConfiguration conf = kieModule.getResourceConfiguration(resourceName);
Resource resource = kieModule.getResource(resourceName);
if (resource != null) {
if (conf == null) {
ckbuilder.add(resource,
ResourceType.determineResourceType(resourceName),
changes );
} else {
ckbuilder.add(resource,
ResourceType.determineResourceType(resourceName),
conf,
changes );
}
return true;
}
return false;
}
public static class CompilationCache implements Serializable {
private static final long serialVersionUID = 3812243055974412935L;
// this is a { DIALECT -> ( RESOURCE, List ) } cache
protected final Map>> compilationCache = new HashMap>>();
public void addEntry(String dialect, String className, byte[] bytecode) {
Map> resourceEntries = compilationCache.get(dialect);
if( resourceEntries == null ) {
resourceEntries = new HashMap>();
compilationCache.put(dialect, resourceEntries);
}
String key = className.contains("$") ? className.substring(0, className.indexOf('$') ) + ".class" : className;
List bytes = resourceEntries.get(key);
if( bytes == null ) {
bytes = new ArrayList();
resourceEntries.put(key, bytes);
}
//System.out.println(String.format("Adding to in-memory cache: %s %s", key, className ));
bytes.add(new CompilationCacheEntry(className, bytecode));
}
public Map> getCacheForDialect(String dialect) {
return compilationCache.get(dialect);
}
}
public static class CompilationCacheEntry implements Serializable {
private static final long serialVersionUID = 1423987159014688588L;
public final String className;
public final byte[] bytecode;
public CompilationCacheEntry( String className, byte[] bytecode) {
this.className = className;
this.bytecode = bytecode;
}
}
@Override
public ResourceProvider createResourceProvider() {
try {
return new KieModuleResourceProvider(this, getFile().toURI().toURL());
} catch (Exception e) {
return null;
}
}
private static class KieModuleResourceProvider implements ResourceProvider {
private final InternalKieModule kieModule;
private final URL kieModuleUrl;
private KieModuleResourceProvider(InternalKieModule kieModule, URL kieModuleUrl) {
this.kieModule = kieModule;
this.kieModuleUrl = kieModuleUrl;
}
@Override
public InputStream getResourceAsStream(String name) throws IOException {
if (name.endsWith( "/" )) {
name = name.substring( 0, name.length()-1 );
}
Resource resource = kieModule.getResource(name);
return resource != null ? resource.getInputStream() : null;
}
@Override
public URL getResource(String name) {
if (name.endsWith( "/" )) {
name = name.substring( 0, name.length()-1 );
}
return kieModule.hasResource(name) ? createURLForResource(name) : null;
}
private URL createURLForResource(String name) {
try {
if (kieModule instanceof ZipKieModule) {
return new URL("jar", "", kieModuleUrl + "!/" + name);
} else {
return new URL(kieModuleUrl, name);
}
} catch (MalformedURLException e) {
return null;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy