0

I'm generating a fat jar of my project using sbt assembly. Then, when trying to run my jar file, i get a nullpointer on this line:

val kieServices: KieServices = KieServices.Factory.get
val kieContainer: KieContainer = kieServices.getKieClasspathContainer

I've already tried adding a kie.conf, but this does not help. I am not using maven or a pom file etc. And am using scala sbt.

Running drools' latest version.

build.sbt:

ThisBuild / version := "0.1.0-SNAPSHOT"

ThisBuild / scalaVersion := "2.13.10"

lazy val root = (project in file("."))
  .settings(
    name := "untitled",
    libraryDependencies ++= Seq(
      "org.drools" % "drools-core" % "8.31.1.Final",
      "org.drools" % "drools-compiler" % "8.31.1.Final",
      "org.drools" % "drools-decisiontables" % "8.31.1.Final",
      "org.drools" % "drools-mvel" % "8.31.1.Final",
      "org.drools" % "drools-model-compiler" % "8.31.1.Final",
      "org.kie" % "kie-api" % "8.31.1.Final"
    ),
    resolvers in Global ++= Seq(
      "Sbt plugins" at "https://dl.bintray.com/sbt/sbt-plugin-releases",
    ),
    Compile / packageBin / mainClass := Some("src.Main"),
    Compile / run / mainClass := Some("src.Main")
  )
  .settings(
    assembly / assemblyJarName := "myJar.jar",
    assembly / assemblyMergeStrategy := {
      case PathList("META-INF", xs@_*) => MergeStrategy.discard
      case _ => MergeStrategy.first
    },
  )

project/plugins.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")

src/main/scala/src/Main.scala (sorry not sorry):

package src

import org.kie.api.KieServices
import org.kie.api.runtime.KieContainer

object Main extends App {
  val kieServices: KieServices = KieServices.Factory.get
  val kieContainer: KieContainer = kieServices.getKieClasspathContainer
}
GSerum_
  • 57
  • 8
  • 1
    [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) – Dmytro Mitin Dec 14 '22 at 15:33
  • *"Then, when trying to run my jar file, i get a nullpointer on this line"* This not a line, this is two lines. Did you try to debug what exactly is null? – Dmytro Mitin Dec 14 '22 at 15:34
  • 1
    KieServices is null, kieContainer causes the error because of a call to the null object @DmytroMitin – GSerum_ Dec 14 '22 at 18:40
  • Would you provide MCVE? For example your `build.sbt` – Dmytro Mitin Dec 14 '22 at 18:42
  • 1
    I have added a MCVE @DmytroMitin – GSerum_ Dec 14 '22 at 21:20
  • Managed to reproduce NPE (had to use Java 11+ to avoid `java.lang.UnsupportedClassVersionError: org/kie/api/runtime/KieContainer has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0`). – Dmytro Mitin Dec 15 '22 at 05:22
  • Hm that is a different error though – GSerum_ Dec 15 '22 at 05:37
  • That was because while working with Scala I normally use Java 8. With Java 11+ (actually, 17) I managed to reproduce NPE. Probably you use Java 11+. – Dmytro Mitin Dec 15 '22 at 05:45
  • 1
    Correct. With Java 11, you get the following: `Exception in thread "main" java.lang.NullPointerException at Main$.main(Main.scala:5)` Which is the `getKieClasspathContainer` line – GSerum_ Dec 15 '22 at 06:07

1 Answers1

1

Debugging shows that

import scala.jdk.CollectionConverters._

ServiceLoader.load(classOf[KieServices], classOf[KieServices].getClassLoader)
 .asScala.size

is 1 when I do sbt run but 0 when I do java -jar myJar.jar.

In the former case KieServices.Factory.get returns org.drools.compiler.kie.builder.impl.KieServicesImpl@...some...hashcode... but in the latter case it returns null.

Try to add a file src/main/resources/META-INF/services/org.kie.api.KieServices with content

org.drools.compiler.kie.builder.impl.KieServicesImpl

One more issue is that, on contrary to sbt package, sbt assembly is missing this file from the assembly jar. So try to unpack myJar.jar, put manually this file as myJar.jar/META-INF/services/org.kie.api.KieServices and zip the jar back (myJar.jar/META-INF/MANIFEST.MF should exist but myJar.jar/META-INF/services is probably missing: https://github.com/sbt/sbt-assembly/issues/11)

java.util.ServiceLoader.load() function is useless and only returns empty result

How to include a config file in the "META-INF/services" folder of a JAR using Maven


It's missing from assembly jar because of

assembly / assemblyMergeStrategy := {
  case PathList("META-INF", xs@_*) => MergeStrategy.discard

in your build.sbt. So modify the strategy:

assembly / assemblyMergeStrategy := {
  case PathList("META-INF", "services", "org.kie.api.KieServices") => MergeStrategy.concat
  case PathList("META-INF", xs@_*) => MergeStrategy.discard
  case _ => MergeStrategy.first
}

and service file will be included into assembly jar.


Actually, the service file already exists in the dependency: ~/.cache/coursier/v1/https/repo1.maven.org/maven2/org/drools/drools-compiler/8.31.1.Final/drools-compiler-8.31.1.Final.jar:META-INF/services/org.kie.api.KieServices

So you shouldn't add it manually, just do case PathList("META-INF", "services", "org.kie.api.KieServices") => MergeStrategy.concat.

There are different service files in drools-compiler-8.31.1.Final.jar:META-INF/services/ so be careful with case PathList("META-INF", xs@_*) => MergeStrategy.discard, it's possible you'll have more problems later if you ignore service files.


Try

assembly / assemblyMergeStrategy := {
  case PathList("META-INF", "services", xs@_*) => MergeStrategy.concat
  case PathList("META-INF", xs@_*) => MergeStrategy.discard
  case _ => MergeStrategy.first
}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Hm, whilst this does solve the nullpointer, a new error has appeared: `Exception in thread "main" java.lang.NullPointerException at org.drools.wiring.api.ComponentsFactory.createProjectClassLoader(ComponentsFactory.java:34) at org.drools.wiring.api.classloader.ProjectClassLoader.createProjectClassLoader(ProjectClassLoader.java:99) at org.drools.wiring.api.classloader.ProjectClassLoader.createProjectClassLoader(ProjectClassLoader.java:92) at org.drools.compiler.kie.builder.impl.ClasspathKieProject.init(ClasspathKieProject.java:96) ` – GSerum_ Dec 15 '22 at 07:18
  • @GSerum_ Why are you ignoring the files from `META-INF`? – Dmytro Mitin Dec 15 '22 at 07:31
  • 1
    I don't know, that's just the code that was already there (company code), but your new case worked, thanks! – GSerum_ Dec 15 '22 at 07:35
  • Certainly, thank you @dmytromitin I do have a final question, unrelated probably. It now no longer finds the KieSession name, probably due to the ksession.xml not being copied over correctly? I tried to add it similarly using `case PathList("META-INF", "kmodule.xml") => MergeStrategy.concat`, but that does not work... Line is `val kieSession: KieSession = kieContainer.newKieSession("DroolDummyKS")`. It works when running normally (not via jar) – GSerum_ Dec 15 '22 at 08:37
  • @GSerum_ It's hard to guess without reproduction. You should start a new question providing the content of `kmodule.xml`, where you put it, current `build.sbt`, what error you have (*"that does not work"* is a bad description). Is it possible that you have several `kmodule.xml`? Would `concat` make sense then? You can check with `singleOrError`. – Dmytro Mitin Dec 15 '22 at 08:49
  • @GSerum_ Please start a new question with a proper description. – Dmytro Mitin Dec 15 '22 at 08:53
  • https://stackoverflow.com/questions/25144484/sbt-assembly-deduplication-found-error – Dmytro Mitin Jan 14 '23 at 03:44