异常启动Optaplanner应用程序时,子服务没有父服务

时间:2017-10-11 13:58:23

标签: drools optaplanner kie

我有一个使用optaplanner的测试程序。没有直接使用KIE A​​PI,但看起来它们是在幕后调用的。这可能与我使用DROOLS进行分数计算的事实有关。该程序可以在IDE或maven中运行,但我想创建一个不需要maven的独立jar。 我使用maven程序集插件来构建一个包含所有依赖项的胖jar,以便独立运行。

当我运行logwatch时,我得到:

java -jar target/OptaPlannerTest-1.4-SNAPSHOT-jar-with-dependencies.jar

Main.java的第38行只是应用程序的两行,所以它所做的只是加载配置文件并尝试构建解算器。

Exception in thread "main" java.lang.ExceptionInInitializerError
        at org.kie.api.internal.utils.ServiceRegistry.getInstance(ServiceRegistry.java:27)
        at org.kie.api.KieServices$Factory$LazyHolder.<clinit>(KieServices.java:332)
        at org.kie.api.KieServices$Factory.get(KieServices.java:339)
        at org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildDroolsScoreDirectorFactory(ScoreDirectorFactoryConfig.java:460)
        at org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildScoreDirectorFactory(ScoreDirectorFactoryConfig.java:331)
        at org.optaplanner.core.config.solver.SolverConfig.buildSolver(SolverConfig.java:220)
        at org.optaplanner.core.impl.solver.AbstractSolverFactory.buildSolver(AbstractSolverFactory.java:61)
        at com.github.wshackle.optaplannertest.Main.main(Main.java:38)
Caused by: java.lang.RuntimeException: Child services [org.kie.api.internal.assembler.KieAssemblers] have no parent
        at org.kie.api.internal.utils.ServiceDiscoveryImpl.buildMap(ServiceDiscoveryImpl.java:186)
        at org.kie.api.internal.utils.ServiceDiscoveryImpl.getServices(ServiceDiscoveryImpl.java:97)
        at org.kie.api.internal.utils.ServiceRegistryImpl.<init>(ServiceRegistryImpl.java:36)
        at org.kie.api.internal.utils.ServiceRegistryImpl$LazyHolder.<clinit>(ServiceRegistryImpl.java:32)

solverConfig.xml是:

    SolverFactory<Plan> solverFactory = SolverFactory.createFromXmlResource(
            "com/github/wshackle/optaplannertest/solverConfig.xml");
    Solver<Plan> solver = solverFactory.buildSolver();

在演员阵容中,我的pom是相关的:

<solver>
  <!-- Domain model configuration -->
   <scanAnnotatedClasses>
    <packageInclude>com.github.wshackle.optaplannertest.model</packageInclude>
  </scanAnnotatedClasses>


  <!-- Score configuration -->
  <scoreDirectorFactory>
      <scoreDrl>com/github/wshackle/optaplannertest/scoreRules.drl</scoreDrl>
  </scoreDirectorFactory>

  <!-- Optimization algorithms configuration -->
  <termination>
    <secondsSpentLimit>5</secondsSpentLimit>
  </termination>
</solver>

jar中的完整文件列表如下所示 https://gist.github.com/wshackle/8887aac8a10e8c4b1f862a4bda288e41

我使用grep来验证它们似乎包含每个jar依赖的预期类:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.github.wshackle</groupId>
    <artifactId>OptaPlannerTest</artifactId>
    <version>1.4-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <optiplanner.version>7.3.0.Final</optiplanner.version>
        <main.class>com.github.wshackle.optaplannertest.Main</main.class>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.optaplanner</groupId>
            <artifactId>optaplanner-core</artifactId>
            <version>${optiplanner.version}</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-api</artifactId>
            <version>${optiplanner.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <scope>runtime</scope>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.5.5</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>${main.class}</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
                        <phase>package</phase> <!-- bind to the packaging phase -->
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

6 个答案:

答案 0 :(得分:3)

问题是以下jar文件都包含META-INF/kie.conf的不同版本:

optaplanner-core-7.3.0.Final.jar
kie-internal/7.3.0.Final/kie-internal-7.3.0.Final.jar 
drools-core-7.3.0.Final.jar
drools-compiler-7.3.0.Final.jar

当maven-assembly-plugin将它们组合在一起时,只能包含META-INF/kie.conf的一个版本。构建解算器时,Optaplanner库将间接调用当前Thread上下文类加载器上的getResources("META-INF/kie.conf")。如果类路径上有多个jar,那么将找到所有这些jar,结果配置将是解析所有这些jar的产物。为了使这个工作在一个胖的超级jar中,需要将kie.conf文件移动到不同的文件名,并重载类加载器以指示库以新名称使用它们。 (也可以将它们组合成一个kie.conf文件)

提取并移动kie.conf文件:

 jar -xf ~/.m2/repository/org/optaplanner/optaplanner-core/7.3.0.Final/optaplanner-core-7.3.0.Final.jar META-INF/kie.conf
 mv META-INF/kie.conf src/main/resources/optaplanner-core-kie.conf
 jar -xf ~/.m2/repository/org/kie/kie-internal/7.3.0.Final/kie-internal-7.3.0.Final.jar META-INF/kie.conf
 mv META-INF/kie.conf src/main/resources/kie-internal-kie.conf
 jar -xf ~/.m2/repository/org/drools/drools-core/7.3.0.Final/drools-core-7.3.0.Final.jar META-INF/kie.conf
 mv META-INF/kie.conf src/main/resources/drools-core-kie.conf
 jar -xf ~/.m2/repository/org/drools/drools-compiler/7.3.0.Final/drools-compiler-7.3.0.Final.jar META-INF/kie.conf
 mv META-INF/kie.conf src/main/resources/drools-compiler-kie.conf

然后重载并设置线程上下文加载器。

    ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
    URL[] localKieConfUrls = new URL[]{
        ClassLoader.getSystemResource("optaplanner-core-kie.conf"),
        ClassLoader.getSystemResource("kie-internal-kie.conf"),
        ClassLoader.getSystemResource("drools-core-kie.conf"),
        ClassLoader.getSystemResource("drools-compiler-kie.conf")
    };
    ClassLoader newClassLoader = new ClassLoader(oldClassLoader) {

        private final URL[] kieConfUrls = localKieConfUrls;
        @Override
        public Enumeration<URL> getResources(String name) throws IOException {
            if ("META-INF/kie.conf".equals(name)) {
                return new Enumeration<URL>() {
                    int index;
                    @Override
                    public boolean hasMoreElements() {
                        return index < kieConfUrls.length;
                    }

                    @Override
                    public URL nextElement() {
                        return kieConfUrls[index++];
                    }
                };
            }
            return super.getResources(name);
        }

    };
    Thread.currentThread().setContextClassLoader(newClassLoader);

答案 1 :(得分:2)

我同意问题出在@WillSchackleford所述:

  

问题是以下jar文件都包含不同版本的META-INF / kie.conf:

   optaplanner-core-7.3.0.Final.jar
   kie-internal/7.3.0.Final/kie-internal-7.3.0.Final.jar 
   drools-core-7.3.0.Final.jar
   drools-compiler-7.3.0.Final.jar
     

当maven-assembly-plugin将它们放在一起时,只能包含一个版本的META-INF / kie.conf。

结合所有这些kie.conf文件的最佳技巧是在您的maven配置中使用transformer,如下所示:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <!-- get all project dependencies -->
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <!-- MainClass in mainfest make a executable jar -->
        <archive>
            <manifest>
                <mainClass>com.paconsulting.powerpeers.PowerPeersDemo</mainClass>
            </manifest>
        </archive>
        <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/kie.conf</resource>
            </transformer>
        </transformers>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <!-- bind to the packaging phase -->
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

这会创建一个META-INF/kie.conf文件,其中包含找到的任何kie.conf文件的内容。

答案 2 :(得分:1)

运行&#34; mvn依赖:树&#34;而且你会发现optaplanner-core依赖于kie-api,kie-internal-api,drools-core和drools-compiler。其中一个将在你的胖罐中丢失。

答案 3 :(得分:1)

我遇到了与以下pom相同的问题

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
..
    <dependencies>
        <dependency>
            <groupId>org.optaplanner</groupId>
            <artifactId>optaplanner-core</artifactId>
            <version>${optaPlanner.version}</version>
        </dependency>    
...
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${jar.plugin.version}</version>
                <configuration>
                    <archive>
                        <addMavenDescriptor>false</addMavenDescriptor>
                        <compress>false</compress>                
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>libs/</classpathPrefix>
                            <mainClass>${mainClass}</mainClass>
                        </manifest> 
                        <index>true</index>
                        <manifestEntries>
                            <impl-version>${project.version}</impl-version>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
           <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>${dependency.plugin.version}</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/libs</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>${assembly.plugin.version}</version>
                <configuration>
                    <descriptors>
                        <descriptor>assembly/release.xml</descriptor>
                    </descriptors>
                    <finalName>${distribution.file.name}</finalName>
                    <outputDirectory>${project.build.directory}/dist</outputDirectory>
                    <workDirectory>${project.build.directory}/assembly/work</workDirectory>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

删除

<index>true</index>

解决了我的问题。希望这也有助于其他人。

答案 4 :(得分:0)

对于使用maven-shade-plugin的用户,这是一个建议的修复程序,它将使用AppendingTransformer https://stackoverflow.com/a/53273253/5903731

将复制到单个文件中的META-INF / kie.conf合并。

答案 5 :(得分:0)

我也遇到了这个错误,但是我找到了一种不同的解决方案。与其生成具有所有依赖关系的jar文件,不如生成具有依赖关系的库文件夹的jar文件是获取可运行jar文件的更简单方法。要使用lib文件夹生成jar文件,请按照以下说明修改pom.xml文件。

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-dependency-plugin</artifactId>
 <executions>
   <execution>
    <id>copy-dependencies</id>
    <phase>prepare-package</phase>
    <goals>
     <goal>copy-dependencies</goal>
    </goals>
    <configuration>
     <outputDirectory>${project.build.directory}/lib</outputDirectory>
     <overWriteReleases>false</overWriteReleases>
     <overWriteSnapshots>false</overWriteSnapshots>
     <overWriteIfNewer>true</overWriteIfNewer>
    </configuration>
   </execution>
  </executions>
 </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
  <configuration>
   <archive>
    <manifest>
     <addClasspath>true</addClasspath>
     <classpathPrefix>lib/</classpathPrefix>
     <mainClassMain-Class</mainClass>
    </manifest>
   </archive>
  </configuration>
</plugin>