我有一个使用optaplanner的测试程序。没有直接使用KIE API,但看起来它们是在幕后调用的。这可能与我使用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>
答案 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>