Exception Child services have no parent when starting Optaplanner application

旧时模样 提交于 2019-12-22 18:14:14

问题


I have a test program that uses optaplanner. There is no direct use of KIE API's but it looks like they are being invoked behind the scenes. This may be related to the fact that I am using DROOLS for the score calculation. The program works from the IDE or through maven, but I want to create a standalone jar that will not require maven. I used the maven assembly plugin to build a fat jar with all dependencies included to be run standalone.

When I run java -jar target/OptaPlannerTest-1.4-SNAPSHOT-jar-with-dependencies.jar I get :

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)

Line 38 of Main.java is only two lines into the application, so all it has done is load the config file and try to build the solver.

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

solverConfig.xml is:

<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>

In cast it is relevant my pom is this:

<?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>

The complete list of files in the jar is shown at https://gist.github.com/wshackle/8887aac8a10e8c4b1f862a4bda288e41

I used grep to verify they seem to include the expected classes for each jar dependancy:

> grep -c org/kie/api jarlisting.txt 
391
> grep -c org/kie/internal jarlisting.txt 
364
> grep -c org/optaplanner/core jarlisting.txt 
841
> grep -c org/drools/core jarlisting.txt 
2175
> grep -c org/drools/compiler jarlisting.txt 
832

回答1:


The problem is that the following jar files all contain different versions of 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

When the maven-assembly-plugin puts them together only one version of the META-INF/kie.conf can be included. When building the solver the Optaplanner library will indirectly call getResources("META-INF/kie.conf") on the current Thread context classloader. If there are multiple jars on the classpath then all of them will be found and the resulting configuration will be the product of parsing all of them. In order to make this work in a single fat uber jar the kie.conf files need to be moved to different file names and a classloader overloaded to direct the library to use them at the new names. (It might also be possible to combine them into a single kie.conf file)

Extract and move the kie.conf files:

 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

Then overload and set the thread context loader.

    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);



回答2:


I agree that the problem is as @WillSchackleford describes:

The problem is that the following jar files all contain different versions of 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

When the maven-assembly-plugin puts them together only one version of the META-INF/kie.conf can be included.

The best trick to combine all of these kie.conf files is to use a transformer in your maven config, like so:

<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>

This creates one META-INF/kie.conf file with the content of any of the kie.conf files it finds.




回答3:


Run "mvn dependency:tree" and you'll see that optaplanner-core depends on kie-api, kie-internal-api, drools-core and drools-compiler. One of those will be missing in your fat jar.




回答4:


I was having the same problem with the following 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>

Removing

<index>true</index>

solved my problem. Hope this helps others as well.




回答5:


For people using maven-shade-plugin, here is a proposed fix that is going to merge the META-INF/kie.conf that are duplicated into a single file using the AppendingTransformer https://stackoverflow.com/a/53273253/5903731



来源:https://stackoverflow.com/questions/46690139/exception-child-services-have-no-parent-when-starting-optaplanner-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!