Search code examples
javajarnullpointerexceptionoptaplanner

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?


Solution

  • 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