• drools动态规则之Maven解析


    drools实现动态规则的两种形式:

    1.将规则文件存储到数据库中,按照一定的规则去加载更新规则文件。

    2.通过动态加载 jar(Maven) 的方式来实现资源加载和动态更新(官方推荐)

    我们这里说的就是第二种形式,这里源码说明的drools版本为:7.73.0.Final。

    动态加载规则,不得不提到一个类 KieScanner

    使用方法:

    1. KieServices kieServices = KieServices.Factory.get();
    2. ReleaseIdImpl releaseId = new ReleaseIdImpl("com.myspace","stream_test3","1.0.0");
    3. kieContainer = kieServices.newKieContainer(releaseId);
    4. KieScanner kieScanner = kieServices.newKieScanner(kieContainer);
    5. // 现在开始扫描
    6. kieScanner.scanNow();
    7. KieSession kieSession = kieContainer.newKieSession();

    仅仅如此使用KieScanner是远远不够的,还需要添加依赖:

    1. <dependency>
    2. <groupId>org.kiegroupId>
    3. <artifactId>kie-ciartifactId>
    4. <version>7.73.0.Finalversion>
    5. dependency>

    既然说到了跟maven有关,那么kie-ci又是如何获取maven的本地及远程仓库呢,那我们就看看kie-ci的pom.xml文件中有什么吧:

    1. <dependency>
    2. <groupId>org.apache.mavengroupId>
    3. <artifactId>maven-coreartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.apache.mavengroupId>
    7. <artifactId>maven-modelartifactId>
    8. dependency>
    9. <dependency>
    10. <groupId>org.apache.mavengroupId>
    11. <artifactId>maven-model-builderartifactId>
    12. dependency>
    13. <dependency>
    14. <groupId>org.apache.mavengroupId>
    15. <artifactId>maven-settingsartifactId>
    16. dependency>
    17. <dependency>
    18. <groupId>org.apache.mavengroupId>
    19. <artifactId>maven-settings-builderartifactId>
    20. dependency>
    21. <dependency>
    22. <groupId>org.apache.mavengroupId>
    23. <artifactId>maven-compatartifactId>
    24. dependency>
    25. <dependency>
    26. <groupId>org.apache.mavengroupId>
    27. <artifactId>maven-aether-providerartifactId>
    28. dependency>
    29. <dependency>
    30. <groupId>org.apache.maven.wagongroupId>
    31. <artifactId>wagon-provider-apiartifactId>
    32. <exclusions>
    33. <exclusion>
    34. <groupId>org.codehaus.plexusgroupId>
    35. <artifactId>plexus-utilsartifactId>
    36. exclusion>
    37. exclusions>
    38. dependency>

    上面的依赖是kie-ci依赖的一部分,但是可以看出,kie-ci是依赖了maven的一些类库,以此类库为基础,实现动态加载jar。

    以下是代码部分:

    KieServicesImpl类
    1. public KieContainer newKieContainer(String containerId, ReleaseId releaseId, ClassLoader classLoader) {
    2. // 从仓库获取kieModule,我们接着看getRepository()方法,可以看到此处的仓库跟maven没啥关系,是kieModule的仓库,直接看getKieModule()方法
    3. InternalKieModule kieModule = (InternalKieModule) getRepository().getKieModule(releaseId);
    4. if (kieModule == null) {
    5. throw new RuntimeException("Cannot find KieModule: " + releaseId);
    6. }
    7. if (classLoader == null) {
    8. classLoader = kieModule.getModuleClassLoader();
    9. }
    10. KieProject kProject = new KieModuleKieProject( kieModule, classLoader );
    11. if (classLoader != kProject.getClassLoader()) {
    12. // if the new kproject has a different classloader than the original one it has to be initialized
    13. kProject.init();
    14. }
    15. if (containerId == null) {
    16. KieContainerImpl newContainer = new KieContainerImpl( UUID.randomUUID().toString(), kProject, getRepository(), releaseId );
    17. return newContainer;
    18. }
    19. if ( kContainers.get(containerId) == null ) {
    20. KieContainerImpl newContainer = new KieContainerImpl( containerId, kProject, getRepository(), releaseId );
    21. KieContainer check = kContainers.putIfAbsent(containerId, newContainer);
    22. if (check == null) {
    23. return newContainer;
    24. } else {
    25. newContainer.dispose();
    26. throw new IllegalStateException("There's already another KieContainer created with the id "+containerId);
    27. }
    28. } else {
    29. throw new IllegalStateException("There's already another KieContainer created with the id "+containerId);
    30. }
    31. }

    KieRepositoryImpl类
    1. public KieModule getKieModule(ReleaseId releaseId, PomModel pomModel) {
    2. //从kieModuleRepo加载
    3. KieModule kieModule = kieModuleRepo.load( KieScannerHolder.kieScanner, releaseId );
    4. if (kieModule == null) {
    5. log.debug("KieModule Lookup. ReleaseId {} was not in cache, checking classpath",
    6. releaseId.toExternalForm());
    7. //如果加载不到从ClassPath加载
    8. kieModule = checkClasspathForKieModule(releaseId);
    9. }
    10. if (kieModule == null) {
    11. log.debug("KieModule Lookup. ReleaseId {} was not in cache, checking maven repository",
    12. releaseId.toExternalForm());
    13. //都加载不到则从Maven仓库加载,这正是我们要找的
    14. kieModule = loadKieModuleFromMavenRepo(releaseId, pomModel);
    15. }
    16. return kieModule;
    17. }

    从上面的代码可以看出,加载KieModule的过程是分阶段的,都找不到了,再从Maven仓库加载。

    KieRepositoryScannerImpl类
    1. public synchronized KieModule loadArtifact(ReleaseId releaseId, PomModel pomModel) {
    2. // 这里先创建了一个ArtifactResolver类 , 我们重点看 getArtifactResolver()方法
    3. ArtifactResolver resolver = pomModel != null ?
    4. ArtifactResolver.getResolverFor(pomModel) :
    5. getArtifactResolver();
    6. // 当得到ArtifactResolver实例后,我们在看该方法
    7. return loadArtifact( releaseId, resolver );
    8. }

    DefaultArtifactResolver类
    1. DefaultArtifactResolver() {
    2. // 这一步是解析pom.xml文件,当我们打开EmbeddedPomParser的定义时,发现其中只有一个成员变量为MavenProject(这个变量是maven类中的类),同时需要注意EmbeddedPomParser还实现了获取依赖的方法,也跟MavenProject类有关。
    3. this.pomParser = new EmbeddedPomParser();
    4. // 获取maven远程仓库的方法
    5. this.mavenRepository = MavenRepository.getMavenRepository();
    6. }

    那么我们看看MavenProject类是如何初始化的吧。

    MavenProjectLoader类
    1. public static MavenProject parseMavenPom(File pomFile, boolean offline) {
    2. boolean hasPom = pomFile.exists();
    3. //重点,下面会贴代码
    4. MavenRequest mavenRequest = createMavenRequest(offline);
    5. if (hasPom) {
    6. mavenRequest.setPom(pomFile.getAbsolutePath());
    7. }
    8. MavenEmbedder mavenEmbedder = null;
    9. try {
    10. mavenEmbedder = new MavenEmbedder(mavenRequest);
    11. return hasPom ?
    12. mavenEmbedder.readProject(pomFile) :
    13. mavenEmbedder.readProject(new ByteArrayInputStream(DUMMY_POM.getBytes(StandardCharsets.UTF_8)));
    14. } catch (Exception e) {
    15. throw new RuntimeException(e);
    16. } finally {
    17. if (mavenEmbedder != null) {
    18. mavenEmbedder.dispose();
    19. }
    20. }
    21. }
    1. public static MavenRequest createMavenRequest(boolean _offline) {
    2. MavenRequest mavenRequest = new MavenRequest();
    3. // 这里设置了本地maven仓库的地址,我们可以看看这个本地地址是如何确定的,重点在MavenSettings.getSettings(),而且该方法返回Settings类,该类是maven类库中的类
    4. mavenRequest.setLocalRepositoryPath(MavenSettings.getSettings().getLocalRepository());
    5. // 这里设定了一些用户的个性配置(主要跟kie相关的),MavenSettings.getUserSettingsSource() 返回的是SettingsSource的子类,不过在当前版本SettingsSource接口已过时
    6. mavenRequest.setUserSettingsSource(MavenSettings.getUserSettingsSource());
    7. final boolean offline = IS_FORCE_OFFLINE || _offline;
    8. // BZ-1007894: If dependency is not resolvable and maven project builder does not complain about it,
    9. // then a java.lang.NullPointerException is thrown to the client.
    10. // So, the user will se an exception message "null", not descriptive about the real error.
    11. mavenRequest.setResolveDependencies(!offline);
    12. //设置离线是否是模式
    13. mavenRequest.setOffline(offline);
    14. return mavenRequest;
    15. }
    MavenSettings类
    1. private static Settings initSettings(SettingsSource userSettingsSource) {
    2. SettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();
    3. DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
    4. if (userSettingsSource != null) {
    5. request.setUserSettingsSource( userSettingsSource );
    6. }
    7. String mavenHome = System.getenv( "M2_HOME" );
    8. if (mavenHome != null) {
    9. File globalSettingsFile = new File( mavenHome + "/conf/settings.xml" );
    10. if (globalSettingsFile.exists()) {
    11. request.setGlobalSettingsFile( globalSettingsFile );
    12. }
    13. } else {
    14. log.warn("Environment variable M2_HOME is not set");
    15. }
    16. request.setSystemProperties( System.getProperties() );
    17. Settings settings = null;
    18. try {
    19. settings = settingsBuilder.build( request ).getEffectiveSettings();
    20. } catch ( SettingsBuildingException e ) {
    21. throw new RuntimeException(e);
    22. }
    23. if (settings.getLocalRepository() == null) {
    24. String userHome = System.getProperty( "user.home" );
    25. if (userHome != null) {
    26. settings.setLocalRepository( userHome + "/.m2/repository" );
    27. } else {
    28. log.error("Cannot find maven local repository");
    29. }
    30. }
    31. return settings;
    32. }

    上图代码初始化了Setting类(maven类库中的类),是如何初始化的,其实就是读取了maven的环境变量或者默认配置,从而找到了setting.xml,此时已经知道了maven的设置,那么一切就变得清晰起来,此时回来填坑。有了本地仓库就一定有远程仓库。远程仓库的设置已经标识在从当前向上第四份代码处,那我们就看看远程仓库是如何设置的。

    MavenRepository类
    1. public static synchronized MavenRepository getMavenRepository() {
    2. if ( defaultMavenRepository == null ) {
    3. // 查看了这个方法,里面又出现了我们熟悉的Settings类,即Maven的setting.xml的信息
    4. Aether defaultAether = Aether.getAether();
    5. // 重点看这个
    6. defaultMavenRepository = new MavenRepository( defaultAether );
    7. }
    8. return defaultMavenRepository;
    9. }

    1. private Collection initRemoteRepositoriesForRequest() {
    2. final MavenRepositoryConfiguration repositoryUtils = getMavenRepositoryConfiguration();
    3. Collection remoteRepos = new HashSet();
    4. remoteRepos.addAll( repositoryUtils.getRemoteRepositoriesForRequest() );
    5. //aether对象,即上述包含了Settings类的对象
    6. for ( RemoteRepository repo : aether.getRepositories() ) {
    7. //resolveMirroredRepo方法是做了一些远程仓库的初始化
    8. remoteRepos.add( repositoryUtils.resolveMirroredRepo( repo ) );
    9. }
    10. //终于返回了远程仓库的集合
    11. return remoteRepos;
    12. }
    MavenRepositoryConfiguration类
    1. // maven的相关知识,不再赘述
    2. public RemoteRepository resolveMirroredRepo( RemoteRepository repo ) {
    3. for ( Mirror mirror : settings.getMirrors() ) {
    4. if ( isMirror( repo, mirror.getMirrorOf() ) ) {
    5. return toRemoteRepositoryBuilder( settings,
    6. mirror.getId(),
    7. mirror.getLayout(),
    8. mirror.getUrl() ).build();
    9. }
    10. }
    11. return repo;
    12. }

    上面终于讲完了maven仓库的相关配置,接下来终于可以开始获取需要的依赖了。

    KieRepositoryScannerImpl类
    1. private KieModule loadArtifact( ReleaseId releaseId, ArtifactResolver resolver ) {
    2. //resolver对象中包含了maven的配置信息,但是该方法还不是获取依赖,二是配置依赖的信息
    3. Artifact artifact = resolver.resolveArtifact(releaseId);
    4. // buildArtifact() /loadPomArtifact() 后面再看
    5. return artifact != null ? buildArtifact(artifact, resolver) : loadPomArtifact(releaseId);
    6. }
    MavenRepository类
    1. public Artifact resolveArtifact( AFReleaseId releaseId ) {
    2. String artifactName = releaseId.toString();
    3. // 判断依赖版本是否是范围设置,这里的判断也很简单,就是判断version中是否包含中括号和括号
    4. if ( DependencyDescriptor.isRangedVersion( releaseId.getVersion() ) ) {
    5. Version v = resolveVersion( artifactName );
    6. if ( v == null ) {
    7. return null;
    8. }
    9. artifactName = releaseId.getGroupId() + ":" + releaseId.getArtifactId() + ":" + v;
    10. }
    11. //获取依赖在这一步
    12. return resolveArtifact( artifactName );
    13. }

     

    1. public Artifact resolveArtifact( String artifactName,
    2. boolean logUnresolvedArtifact ) {
    3. Artifact artifact = new DefaultArtifact( artifactName );
    4. ArtifactRequest artifactRequest = new ArtifactRequest();
    5. artifactRequest.setArtifact( artifact );
    6. for ( RemoteRepository repo : remoteRepositoriesForRequest ) {
    7. artifactRequest.addRepository( repo );
    8. }
    9. RepositorySystemSession session = aether.getSession();
    10. Object sessionChecks = null;
    11. boolean isSnapshot = artifactName.endsWith( "-SNAPSHOT" );
    12. if (artifactName.endsWith( "-SNAPSHOT" )) {
    13. // ensure to always update snapshots
    14. sessionChecks = session.getData().get( SESSION_CHECKS );
    15. session.getData().set( SESSION_CHECKS, null );
    16. }
    17. try {
    18. // 获取依赖
    19. ArtifactResult artifactResult = aether.getSystem().resolveArtifact( session, artifactRequest );
    20. return artifactResult.getArtifact();
    21. } catch ( ArtifactResolutionException e ) {
    22. if ( logUnresolvedArtifact ) {
    23. if ( log.isDebugEnabled() ) {
    24. log.debug( "Unable to resolve artifact: " + artifactName, e );
    25. } else {
    26. log.warn( "Unable to resolve artifact: " + artifactName );
    27. }
    28. }
    29. return null;
    30. } finally {
    31. if (sessionChecks != null) {
    32. session.getData().set( SESSION_CHECKS, sessionChecks );
    33. }
    34. }
    35. }

    构建完了Artifact类就开始构建KieModule了。

    KieRepositoryScannerImpl类
    1. private InternalKieModule buildArtifact(Artifact artifact, ArtifactResolver resolver) {
    2. DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(artifact);
    3. ReleaseId releaseId = adapt( dependencyDescriptor.getReleaseId() );
    4. InternalKieModule kieModule = createKieModule(releaseId, artifact.getFile());
    5. if (kieModule != null) {
    6. addDependencies(kieModule, resolver, resolver.getArtifactDependecies(dependencyDescriptor.toString()));
    7. }
    8. return kieModule;
    9. }
    10. private void addDependencies(InternalKieModule kieModule, ArtifactResolver resolver, List dependencies) {
    11. for (DependencyDescriptor dep : dependencies) {
    12. InternalKieModule dependency = (InternalKieModule) KieServices.Factory.get().getRepository().getKieModule(adapt( dep.getReleaseId() ));
    13. if (dependency != null) {
    14. kieModule.addKieDependency(dependency);
    15. } else {
    16. Artifact depArtifact = resolver.resolveArtifact(dep.getReleaseId());
    17. if (depArtifact != null && isKJar(depArtifact.getFile())) {
    18. ReleaseId depReleaseId = adapt( new DependencyDescriptor(depArtifact).getReleaseId() );
    19. InternalKieModule zipKieModule = createKieModule(depReleaseId, depArtifact.getFile());
    20. if (zipKieModule != null) {
    21. kieModule.addKieDependency(zipKieModule);
    22. }
    23. }
    24. }
    25. }
    26. }
    1. private KieModule loadPomArtifact(ReleaseId releaseId) {
    2. ArtifactResolver resolver = ArtifactResolver.getResolverFor(releaseId, false);
    3. if (resolver == null) {
    4. return null;
    5. }
    6. MemoryKieModule kieModule = new MemoryKieModule(releaseId);
    7. addDependencies(kieModule, resolver, resolver.getPomDirectDependencies( DependencyFilter.COMPILE_FILTER ));
    8. return kieModule;
    9. }

  • 相关阅读:
    昇思MindSpore开源社区算力使能,快来SIG申请你的专属算力
    基于人脸识别的情绪社区(Python+Django+Mysql+Keras,tensorflow)
    1.3 c++虚基类的用途以及内存模型
    OpenGL进阶(二)之像素缓冲PixelBuffer
    vue学习-07TodoList本地存储以及TodoList的自定义事件
    盲盒电商模式:神秘感+趣味性,轻松实现社交购物
    SpringBoot集成JSR并使用
    webpack5零基础入门-8清空前次打包文件与处理图标字体资源
    程序设计与算法(三)C++面向对象程序设计笔记 第九周 标准模板库STL(二)
    可以直接调用 Thread 类的 run 方法吗?
  • 原文地址:https://blog.csdn.net/kanyun123/article/details/127101000