Skip to content

Optaplanner – NullPointerException when creating jar file

My program works fine from my IDE (IntelliJ) but for some reason, when I try to create a jar file I get following error when I run the program from a terminal:

Exception in thread “main” java.lang.NullPointerException at org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildDroolsScoreDirectorFactory(ScoreDirectorFactoryConfig.java:461)

org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildScoreDirectorFactory(ScoreDirectorFactoryConfig.java:331)

org.optaplanner.core.config.solver.SolverConfig.buildSolver(SolverConfig.java:220)

org.optaplanner.core.impl.solver.AbstractSolverFactory.buildSolver(AbstractSolverFactory.java:57)

org.optaplanner.EmployeeRoster.main(EmployeeRoster.java:31)

This is my line 31 in EmployeeRoster:

Solver solver = SolverFactory.createFromXmlResource(SOLVER_CONFIG_XML).buildSolver();

SOLVER_CONFIG_XML is a String containing my path for my XML solver-config, it looks like this

<?xml version="1.0" encoding="UTF-8"?>
<solver>
	<solutionClass>org.optaplanner.solver.Roster</solutionClass>
	<entityClass>org.optaplanner.domain.Assignment</entityClass>
	<scoreDirectorFactory>
		<scoreDrl>org/optaplanner/solver/employeeShiftsScoreRules.drl</scoreDrl>
	</scoreDirectorFactory>
	<localSearch>
		<termination>
			<secondsSpentLimit>5</secondsSpentLimit>
			<bestScoreLimit>0hard/0medium/0soft</bestScoreLimit>
		</termination>
		<!--<termination>
			<unimprovedStepCountLimit>5</unimprovedStepCountLimit>
		</termination>-->
		<acceptor>
			<entityTabuSize>7</entityTabuSize>
		</acceptor>
		<forager>
			<acceptedCountLimit>1000</acceptedCountLimit>
		</forager>
	</localSearch>
</solver>

Also here’s my pom.xml file if that should be relevant:

<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>org.btrg.dfb</groupId>
<artifactId>optaplanner</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
    <dependency>
        <groupId>org.optaplanner</groupId>
        <artifactId>optaplanner-core</artifactId>
        <version>7.3.0.Final</version>
    </dependency>

    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.4.8</version>
    </dependency>

    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.1</version>
    </dependency>

    <dependency>
        <groupId>com.jolira</groupId>
        <artifactId>onejar-maven-plugin</artifactId>
        <version>1.4.4</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>

        <plugin>
            <!-- Build an executable JAR -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <mainClass>org.avalin.optaplanner.EmployeeRoster</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-my-jar-with-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>com.jolira</groupId>
            <artifactId>onejar-maven-plugin</artifactId>
            <version>1.4.4</version>
            <executions>
                <execution>
                    <goals>
                        <goal>one-jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

What might I be doing wrong?

Answer

For me, the issue had to deal with how I wanted to run the jar (java -jar), and consequently how I built the jar. The NullPointerException arose when I upgraded to optaplanner-core/7.4.1 from 6.4.0. This issue was not present back when I was still using 6.4.0.

Exception:

java.lang.NullPointerException at org.kie.internal.io.ResourceFactory.newByteArrayResource(ResourceFactory.java:66) at org.drools.compiler.kie.builder.impl.AbstractKieModule.getResource(AbstractKieModule.java:299) at org.drools.compiler.kie.builder.impl.AbstractKieModule.addResourceToCompiler(AbstractKieModule.java:264) at org.drools.compiler.kie.builder.impl.AbstractKieModule.addResourceToCompiler(AbstractKieModule.java:259) at org.drools.compiler.kie.builder.impl.AbstractKieProject.buildKnowledgePackages(AbstractKieProject.java:243) at org.drools.compiler.kie.builder.impl.AbstractKieProject.verify(AbstractKieProject.java:74) at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildKieProject(KieBuilderImpl.java:250) at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:218) at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:176) at org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildDroolsScoreDirectorFactory(ScoreDirectorFactoryConfig.java:503)

The following is a temporary workaround I did to resolve the NullPointerException and just get the application to run.

  1. Use IntelliJ’s gradle to build one big jar with all dependencies.
  2. Unzip the jar, for example, to unzippedJar/ directory.
  3. Modify unzippedJar/META-INF/kie.conf
  4. Rejar the files.
  5. Run the jar with java.

Step 1. Building the jar

dependencies {
    ...
    compile group: 'org.optaplanner', name: 'optaplanner-core', version:'7.4.1.Final'
    compile group: 'org.optaplanner', name: 'optaplanner-benchmark', version:'7.4.1.Final'
    ...
}
task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'Self contained jar with all dependencies',
        'Implementation-Version': version,
        'Main-Class': 'path.to.class.with.main.method'
    }
    baseName = 'fatJar'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

Step 2. Unzip

unzip fatJar.jar -d unzippedJar/

Step 3. Modify kie.conf

The “fatJar” task flattened the dependencies – duplicate file names are allowed in the jar. This resulted in four unzippedJar/META-INF/kie.conf files, only one of which was used. Regardless of whichever kie.conf file was used, this was my final kie.conf.

    org.kie.api.internal.assembler.KieAssemblers = +org.optaplanner.core.impl.solver.kie.KieSolverAssemblerService
    org.kie.api.internal.assembler.KieAssemblers = org.kie.internal.services.KieAssemblersImpl
    org.kie.api.internal.runtime.KieRuntimes = org.kie.internal.services.KieRuntimesImpl
    org.kie.api.internal.weaver.KieWeavers = org.kie.internal.services.KieWeaversImpl
    org.kie.api.internal.runtime.beliefs.KieBeliefs = org.kie.internal.services.KieBeliefsImpl
    org.kie.api.io.KieResources = org.drools.core.io.impl.ResourceFactoryServiceImpl
    org.kie.api.marshalling.KieMarshallers = org.drools.core.marshalling.impl.MarshallerProviderImpl
    org.kie.api.concurrent.KieExecutors = org.drools.core.concurrent.ExecutorProviderImpl
    org.kie.api.KieServices = org.drools.compiler.kie.builder.impl.KieServicesImpl
    org.kie.internal.builder.KnowledgeBuilderFactoryService = org.drools.compiler.builder.impl.KnowledgeBuilderFactoryServiceImpl

Step 4. Rejar

For whatever reason, specifying the MANIFEST.MF file did nothing for me, hence I left it out.

jar cf rejard.jar .

Step 5. Run the jar

java -cp rejard.jar path.to.class.with.main.method