drools实现动态规则的两种形式:
1.将规则文件存储到数据库中,按照一定的规则去加载更新规则文件。
2.通过动态加载 jar(Maven) 的方式来实现资源加载和动态更新(官方推荐)
我们这里说的就是第二种形式,这里源码说明的drools版本为:7.73.0.Final。
动态加载规则,不得不提到一个类 KieScanner
使用方法:
KieServices kieServices = KieServices.Factory.get();
ReleaseIdImpl releaseId = new ReleaseIdImpl("com.myspace","stream_test3","1.0.0");
kieContainer = kieServices.newKieContainer(releaseId);
KieScanner kieScanner = kieServices.newKieScanner(kieContainer);
// 现在开始扫描
kieScanner.scanNow();
KieSession kieSession = kieContainer.newKieSession();
仅仅如此使用KieScanner是远远不够的,还需要添加依赖:
org.kie
kie-ci
7.73.0.Final
既然说到了跟maven有关,那么kie-ci又是如何获取maven的本地及远程仓库呢,那我们就看看kie-ci的pom.xml文件中有什么吧:
org.apache.maven
maven-core
org.apache.maven
maven-model
org.apache.maven
maven-model-builder
org.apache.maven
maven-settings
org.apache.maven
maven-settings-builder
org.apache.maven
maven-compat
org.apache.maven
maven-aether-provider
org.apache.maven.wagon
wagon-provider-api
org.codehaus.plexus
plexus-utils
上面的依赖是kie-ci依赖的一部分,但是可以看出,kie-ci是依赖了maven的一些类库,以此类库为基础,实现动态加载jar。
以下是代码部分:
KieServicesImpl类
public KieContainer newKieContainer(String containerId, ReleaseId releaseId, ClassLoader classLoader) {
// 从仓库获取kieModule,我们接着看getRepository()方法,可以看到此处的仓库跟maven没啥关系,是kieModule的仓库,直接看getKieModule()方法
InternalKieModule kieModule = (InternalKieModule) getRepository().getKieModule(releaseId);
if (kieModule == null) {
throw new RuntimeException("Cannot find KieModule: " + releaseId);
}
if (classLoader == null) {
classLoader = kieModule.getModuleClassLoader();
}
KieProject kProject = new KieModuleKieProject( kieModule, classLoader );
if (classLoader != kProject.getClassLoader()) {
// if the new kproject has a different classloader than the original one it has to be initialized
kProject.init();
}
if (containerId == null) {
KieContainerImpl newContainer = new KieContainerImpl( UUID.randomUUID().toString(), kProject, getRepository(), releaseId );
return newContainer;
}
if ( kContainers.get(containerId) == null ) {
KieContainerImpl newContainer = new KieContainerImpl( containerId, kProject, getRepository(), releaseId );
KieContainer check = kContainers.putIfAbsent(containerId, newContainer);
if (check == null) {
return newContainer;
} else {
newContainer.dispose();
throw new IllegalStateException("There's already another KieContainer created with the id "+containerId);
}
} else {
throw new IllegalStateException("There's already another KieContainer created with the id "+containerId);
}
}
KieRepositoryImpl类
public KieModule getKieModule(ReleaseId releaseId, PomModel pomModel) {
//从kieModuleRepo加载
KieModule kieModule = kieModuleRepo.load( KieScannerHolder.kieScanner, releaseId );
if (kieModule == null) {
log.debug("KieModule Lookup. ReleaseId {} was not in cache, checking classpath",
releaseId.toExternalForm());
//如果加载不到从ClassPath加载
kieModule = checkClasspathForKieModule(releaseId);
}
if (kieModule == null) {
log.debug("KieModule Lookup. ReleaseId {} was not in cache, checking maven repository",
releaseId.toExternalForm());
//都加载不到则从Maven仓库加载,这正是我们要找的
kieModule = loadKieModuleFromMavenRepo(releaseId, pomModel);
}
return kieModule;
}
从上面的代码可以看出,加载KieModule的过程是分阶段的,都找不到了,再从Maven仓库加载。
KieRepositoryScannerImpl类
public synchronized KieModule loadArtifact(ReleaseId releaseId, PomModel pomModel) {
// 这里先创建了一个ArtifactResolver类 , 我们重点看 getArtifactResolver()方法
ArtifactResolver resolver = pomModel != null ?
ArtifactResolver.getResolverFor(pomModel) :
getArtifactResolver();
// 当得到ArtifactResolver实例后,我们在看该方法
return loadArtifact( releaseId, resolver );
}
DefaultArtifactResolver类
DefaultArtifactResolver() {
// 这一步是解析pom.xml文件,当我们打开EmbeddedPomParser的定义时,发现其中只有一个成员变量为MavenProject(这个变量是maven类中的类),同时需要注意EmbeddedPomParser还实现了获取依赖的方法,也跟MavenProject类有关。
this.pomParser = new EmbeddedPomParser();
// 获取maven远程仓库的方法
this.mavenRepository = MavenRepository.getMavenRepository();
}
那么我们看看MavenProject类是如何初始化的吧。
MavenProjectLoader类
public static MavenProject parseMavenPom(File pomFile, boolean offline) {
boolean hasPom = pomFile.exists();
//重点,下面会贴代码
MavenRequest mavenRequest = createMavenRequest(offline);
if (hasPom) {
mavenRequest.setPom(pomFile.getAbsolutePath());
}
MavenEmbedder mavenEmbedder = null;
try {
mavenEmbedder = new MavenEmbedder(mavenRequest);
return hasPom ?
mavenEmbedder.readProject(pomFile) :
mavenEmbedder.readProject(new ByteArrayInputStream(DUMMY_POM.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (mavenEmbedder != null) {
mavenEmbedder.dispose();
}
}
}
public static MavenRequest createMavenRequest(boolean _offline) {
MavenRequest mavenRequest = new MavenRequest();
// 这里设置了本地maven仓库的地址,我们可以看看这个本地地址是如何确定的,重点在MavenSettings.getSettings(),而且该方法返回Settings类,该类是maven类库中的类
mavenRequest.setLocalRepositoryPath(MavenSettings.getSettings().getLocalRepository());
// 这里设定了一些用户的个性配置(主要跟kie相关的),MavenSettings.getUserSettingsSource() 返回的是SettingsSource的子类,不过在当前版本SettingsSource接口已过时
mavenRequest.setUserSettingsSource(MavenSettings.getUserSettingsSource());
final boolean offline = IS_FORCE_OFFLINE || _offline;
// BZ-1007894: If dependency is not resolvable and maven project builder does not complain about it,
// then a java.lang.NullPointerException
is thrown to the client.
// So, the user will se an exception message "null", not descriptive about the real error.
mavenRequest.setResolveDependencies(!offline);
//设置离线是否是模式
mavenRequest.setOffline(offline);
return mavenRequest;
}
MavenSettings类
private static Settings initSettings(SettingsSource userSettingsSource) {
SettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();
DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
if (userSettingsSource != null) {
request.setUserSettingsSource( userSettingsSource );
}
String mavenHome = System.getenv( "M2_HOME" );
if (mavenHome != null) {
File globalSettingsFile = new File( mavenHome + "/conf/settings.xml" );
if (globalSettingsFile.exists()) {
request.setGlobalSettingsFile( globalSettingsFile );
}
} else {
log.warn("Environment variable M2_HOME is not set");
}
request.setSystemProperties( System.getProperties() );
Settings settings = null;
try {
settings = settingsBuilder.build( request ).getEffectiveSettings();
} catch ( SettingsBuildingException e ) {
throw new RuntimeException(e);
}
if (settings.getLocalRepository() == null) {
String userHome = System.getProperty( "user.home" );
if (userHome != null) {
settings.setLocalRepository( userHome + "/.m2/repository" );
} else {
log.error("Cannot find maven local repository");
}
}
return settings;
}
上图代码初始化了Setting类(maven类库中的类),是如何初始化的,其实就是读取了maven的环境变量或者默认配置,从而找到了setting.xml,此时已经知道了maven的设置,那么一切就变得清晰起来,此时回来填坑。有了本地仓库就一定有远程仓库。远程仓库的设置已经标识在从当前向上第四份代码处,那我们就看看远程仓库是如何设置的。
MavenRepository类
public static synchronized MavenRepository getMavenRepository() {
if ( defaultMavenRepository == null ) {
// 查看了这个方法,里面又出现了我们熟悉的Settings类,即Maven的setting.xml的信息
Aether defaultAether = Aether.getAether();
// 重点看这个
defaultMavenRepository = new MavenRepository( defaultAether );
}
return defaultMavenRepository;
}
private Collection initRemoteRepositoriesForRequest() {
final MavenRepositoryConfiguration repositoryUtils = getMavenRepositoryConfiguration();
Collection remoteRepos = new HashSet();
remoteRepos.addAll( repositoryUtils.getRemoteRepositoriesForRequest() );
//aether对象,即上述包含了Settings类的对象
for ( RemoteRepository repo : aether.getRepositories() ) {
//resolveMirroredRepo方法是做了一些远程仓库的初始化
remoteRepos.add( repositoryUtils.resolveMirroredRepo( repo ) );
}
//终于返回了远程仓库的集合
return remoteRepos;
}
MavenRepositoryConfiguration类
// maven的相关知识,不再赘述
public RemoteRepository resolveMirroredRepo( RemoteRepository repo ) {
for ( Mirror mirror : settings.getMirrors() ) {
if ( isMirror( repo, mirror.getMirrorOf() ) ) {
return toRemoteRepositoryBuilder( settings,
mirror.getId(),
mirror.getLayout(),
mirror.getUrl() ).build();
}
}
return repo;
}
上面终于讲完了maven仓库的相关配置,接下来终于可以开始获取需要的依赖了。
KieRepositoryScannerImpl类
private KieModule loadArtifact( ReleaseId releaseId, ArtifactResolver resolver ) {
//resolver对象中包含了maven的配置信息,但是该方法还不是获取依赖,二是配置依赖的信息
Artifact artifact = resolver.resolveArtifact(releaseId);
// buildArtifact() /loadPomArtifact() 后面再看
return artifact != null ? buildArtifact(artifact, resolver) : loadPomArtifact(releaseId);
}
MavenRepository类
public Artifact resolveArtifact( AFReleaseId releaseId ) {
String artifactName = releaseId.toString();
// 判断依赖版本是否是范围设置,这里的判断也很简单,就是判断version中是否包含中括号和括号
if ( DependencyDescriptor.isRangedVersion( releaseId.getVersion() ) ) {
Version v = resolveVersion( artifactName );
if ( v == null ) {
return null;
}
artifactName = releaseId.getGroupId() + ":" + releaseId.getArtifactId() + ":" + v;
}
//获取依赖在这一步
return resolveArtifact( artifactName );
}
public Artifact resolveArtifact( String artifactName,
boolean logUnresolvedArtifact ) {
Artifact artifact = new DefaultArtifact( artifactName );
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact( artifact );
for ( RemoteRepository repo : remoteRepositoriesForRequest ) {
artifactRequest.addRepository( repo );
}
RepositorySystemSession session = aether.getSession();
Object sessionChecks = null;
boolean isSnapshot = artifactName.endsWith( "-SNAPSHOT" );
if (artifactName.endsWith( "-SNAPSHOT" )) {
// ensure to always update snapshots
sessionChecks = session.getData().get( SESSION_CHECKS );
session.getData().set( SESSION_CHECKS, null );
}
try {
// 获取依赖
ArtifactResult artifactResult = aether.getSystem().resolveArtifact( session, artifactRequest );
return artifactResult.getArtifact();
} catch ( ArtifactResolutionException e ) {
if ( logUnresolvedArtifact ) {
if ( log.isDebugEnabled() ) {
log.debug( "Unable to resolve artifact: " + artifactName, e );
} else {
log.warn( "Unable to resolve artifact: " + artifactName );
}
}
return null;
} finally {
if (sessionChecks != null) {
session.getData().set( SESSION_CHECKS, sessionChecks );
}
}
}
构建完了Artifact类就开始构建KieModule了。
KieRepositoryScannerImpl类
private InternalKieModule buildArtifact(Artifact artifact, ArtifactResolver resolver) {
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(artifact);
ReleaseId releaseId = adapt( dependencyDescriptor.getReleaseId() );
InternalKieModule kieModule = createKieModule(releaseId, artifact.getFile());
if (kieModule != null) {
addDependencies(kieModule, resolver, resolver.getArtifactDependecies(dependencyDescriptor.toString()));
}
return kieModule;
}
private void addDependencies(InternalKieModule kieModule, ArtifactResolver resolver, List dependencies) {
for (DependencyDescriptor dep : dependencies) {
InternalKieModule dependency = (InternalKieModule) KieServices.Factory.get().getRepository().getKieModule(adapt( dep.getReleaseId() ));
if (dependency != null) {
kieModule.addKieDependency(dependency);
} else {
Artifact depArtifact = resolver.resolveArtifact(dep.getReleaseId());
if (depArtifact != null && isKJar(depArtifact.getFile())) {
ReleaseId depReleaseId = adapt( new DependencyDescriptor(depArtifact).getReleaseId() );
InternalKieModule zipKieModule = createKieModule(depReleaseId, depArtifact.getFile());
if (zipKieModule != null) {
kieModule.addKieDependency(zipKieModule);
}
}
}
}
}
private KieModule loadPomArtifact(ReleaseId releaseId) {
ArtifactResolver resolver = ArtifactResolver.getResolverFor(releaseId, false);
if (resolver == null) {
return null;
}
MemoryKieModule kieModule = new MemoryKieModule(releaseId);
addDependencies(kieModule, resolver, resolver.getPomDirectDependencies( DependencyFilter.COMPILE_FILTER ));
return kieModule;
}