I am trying to run a drools based (and KieServices based) project from a fat jar that has been generated using sbt assembly.
[main] INFO org.drools.compiler.kie.builder.impl.ClasspathKieProject - Found kmodule: jar:file:/.../myJar.jar!/META-INF/kmodule.xml
[main] ERROR org.drools.compiler.kie.builder.impl.ClasspathKieProject - Unable to build index of kmodule.xml url=jar:file:/.../myJar.jar/META-INF/kmodule.xml
You're trying to perform a xml related operation without the necessary xml support for drools. Please add the module org.drools:drools-xml-support to your classpath.
[main] ERROR org.drools.compiler.kie.builder.impl.KieContainerImpl - Unknown KieSession name: DroolDummyKS
This error is triggered by the following code, last line to be specific:
val kieServices: KieServices = KieServices.Factory.get
val kieContainer: KieContainer = kieServices.getKieClasspathContainer
// Apply the correct kie session from the ./resources/META-INF/kmodule.xml configuration
val kieSession: KieSession = kieContainer.newKieSession("DroolDummyKS")
Project is a Scala SBT project (Java 11 as compiler)
I couldn't reproduce Unable to build index of kmodule.xml. Just kieContainer.newKieSession("DroolDummyKS") returns null for assembly jar (java -jar myJar.jar), on contrary to sbt run, where it returns KieSession[0].
Notice that it's written in your error:
Please add the module org.drools:drools-xml-support to your classpath
Do two things in build.sbt:
add to libraryDependencies
"org.drools" % "drools-xml-support" % "8.31.1.Final"
and
unignore kmodule.xml in assembly strategy (so that kmodule.xml is included into assembly jar), for example with singleOrError (not sure that concatenation makes sense for xml on contrary to service files, we're making sure that it's your kmodule.xml being included, otherwise it throws)
assembly / assemblyMergeStrategy := {
case PathList("META-INF", "services", xs#_*) => MergeStrategy.concat
case PathList("META-INF", "kmodule.xml") => MergeStrategy.singleOrError
case PathList("META-INF", xs#_*) => MergeStrategy.discard
case _ => MergeStrategy.first
}
Update. With
assembly / assemblyMergeStrategy := {
case _ => MergeStrategy.singleOrError
}
you can check what files have duplicates.
You should add the dependency "org.drools" % "drools-xml-support" % "8.31.1.Final" as I adviced earlier. Otherwise there is NPE even for sbt run. I see that you added it in update.
You should remove the file src/main/resources/META-INF/services/org.kie.api.KieServices. Anyway it's present in the dependency drools-compiler-8.31.1.Final.jar.
Try the strategy ignoring as less as possible
assembly / assemblyMergeStrategy := {
case xs if xs.endsWith("LICENSE") => MergeStrategy.discard
case xs if xs.endsWith("LICENSE.txt") => MergeStrategy.discard
case xs if xs.endsWith("INDEX.LIST") => MergeStrategy.discard
case xs if xs.endsWith("MANIFEST.MF") => MergeStrategy.discard
case xs if xs.endsWith("NOTICE") => MergeStrategy.discard
case xs if xs.endsWith("NOTICE.txt") => MergeStrategy.discard
case xs if xs.endsWith("module-info.class") => MergeStrategy.discard
case PathList("META-INF", "services", "org.apache.poi.sl.draw.ImageRenderer") => MergeStrategy.filterDistinctLines
case PathList("META-INF", "services", "org.apache.poi.ss.usermodel.WorkbookProvider") => MergeStrategy.filterDistinctLines
case PathList("META-INF", "services", "org.apache.poi.extractor.ExtractorProvider") => MergeStrategy.filterDistinctLines
case PathList("META-INF", "services", "org.drools.wiring.api.ComponentsSupplier") => MergeStrategy.filterDistinctLines
case _ => MergeStrategy.singleOrError
}
Duplicates should be resolved then.
filterDistinctLines is similar to concat, just not adding the same lines.
The strategy can be simplified
assembly / assemblyMergeStrategy := {
case xs if Seq(
"LICENSE",
"LICENSE.txt",
"INDEX.LIST",
"MANIFEST.MF",
"NOTICE",
"NOTICE.txt",
"module-info.class"
).exists(xs.endsWith) => MergeStrategy.discard
case PathList("META-INF", "services", xs#_*) => MergeStrategy.filterDistinctLines
case _ => MergeStrategy.singleOrError
}
Update 2. I looked for reasons of the latest NPE
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke
"org.drools.compiler.compiler.Dialect.getId()"
because the return value of
"org.drools.compiler.rule.builder.RuleBuildContext.getDialect()"
is null
It turns out that the thing was in the file META-INF/kie.default.properties.conf of one of dependencies. So it was enough to additionally unignore it
assembly / assemblyMergeStrategy := {
case x if x.endsWith("module-info.class") => MergeStrategy.discard
case PathList("META-INF", "services", xs#_*) => MergeStrategy.concat
case PathList("META-INF", "kmodule.xml") => MergeStrategy.singleOrError
case PathList("META-INF", "kie.default.properties.conf") => MergeStrategy.singleOrError
case PathList("META-INF", xs#_*) => MergeStrategy.discard
case _ => MergeStrategy.first
}
But the main conclusion for us should be that ignoring the whole META-INF can be dangerous. There can be some other files there that are important for some of dependencies. Maybe now there is no NPE but some issues can be later.
It turns out that the default assembly strategy
assembly / assemblyMergeStrategy := MergeStrategy.defaultMergeStrategy
or
assembly / assemblyMergeStrategy := {
case x =>
val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
oldStrategy(x)
}
or (see here)
val defaultMergeStrategy: String => MergeStrategy = {
case x if Assembly.isConfigFile(x) =>
MergeStrategy.concat
case PathList(ps # _*) if Assembly.isReadme(ps.last) || Assembly.isLicenseFile(ps.last) =>
MergeStrategy.rename
case PathList("META-INF", xs # _*) =>
(xs map {_.toLowerCase}) match {
case ("manifest.mf" :: Nil) | ("index.list" :: Nil) | ("dependencies" :: Nil) =>
MergeStrategy.discard
case ps # (x :: xs) if ps.last.endsWith(".sf") || ps.last.endsWith(".dsa") =>
MergeStrategy.discard
case "plexus" :: xs =>
MergeStrategy.discard
case "services" :: xs =>
MergeStrategy.filterDistinctLines
case ("spring.schemas" :: Nil) | ("spring.handlers" :: Nil) =>
MergeStrategy.filterDistinctLines
case _ => MergeStrategy.deduplicate
}
case _ => MergeStrategy.deduplicate
}
does the work pretty well. You just had to ignore additionally files module-info.class. So you can prefer
assembly / assemblyMergeStrategy := {
case x if x.endsWith("module-info.class") => MergeStrategy.discard
case x =>
val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
oldStrategy(x)
}
https://github.com/ThijmenL98/DroolsMCVE/pull/1
Related
I have a scala code.
val lines = Source
.fromResource("doc-topics-new.txt")
.getLines
.toList
.drop(1) match {
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
when I run the code it is working how ever there is a warning message
Warning:(81, 14) match may not be exhaustive.
It would fail on the following input: Nil
.drop(1) match {
Please suggest how to remove this warning.
Just add a case of Nil:
val lines = Source
.fromResource("doc-topics-new.txt")
.getLines
.toList
.drop(1) match {
case Nil => List.empty // Add this line
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
you can just add the Nil case to your pattern matching.
if you really wanna suppress the warning you could use the unchecked annotation:
val lines = (Source
.fromResource("doc-topics-new.txt")
.getLines
.toList
.drop(1): #unchecked) match {
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
How do packaging a jar by selected scala classes using SBT assembly. While using SBT full project is created as jar. Is there a way to exclude the classes? Please help .
You can use assemblyMergeStrategy with MergeStrategy.discard to discard to the classes that you want, like:
assemblyMergeStrategy in assembly := {
case PathList("javax", "servlet", xs # _*) => MergeStrategy.first
case PathList(ps # _*) if ps.last endsWith ".html" => MergeStrategy.first
case "application.conf" => MergeStrategy.concat
case "unwanted.txt" => MergeStrategy.discard
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
Reference:
https://github.com/sbt/sbt-assembly#merge-strategy
What these phrases mean:
xs # _*
ps # _*
Copied from documentation:
assemblyMergeStrategy in assembly := {
case PathList("javax", "servlet", xs # _*) => MergeStrategy.first
case PathList(ps # _*) if ps.last endsWith ".html" => MergeStrategy.first
case "application.conf" => MergeStrategy.concat
case "unwanted.txt" => MergeStrategy.discard
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
In your example _* means everything, # for matching
xs # _* is case pattern means pick every thing that matched for first case /javax/servlet/*
ps # _* means pick up all that match as /* and with html extension
'#' operator is used to bind to variables in pattern matching.
<somevar> : _* is used to unpack varargs as sequence of appropriate type.
In this example ps # _* tells pattern matching to retrieve varargs from PathList as Sequence of paths.
I am trying to use the spark-avro library to process avro files. I am using SBT:
build.sbt:
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-sql" % "1.3.0",
"com.databricks" %% "spark-avro" % "1.0.0")
tester.scala:
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
import org.apache.spark.sql._
import com.databricks.spark.avro._
object tester {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("SimpleApplication").setMaster("local")
val sc = new SparkContext(conf)
// Creates a DataFrame from a specified file
val df = sqlContext.load("episodes.avro", "com.databricks.spark.avro")
}
}
when I run tester in IntelliJ IDE, I get the following stack trace:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/avro/mapred/FsInput
at com.databricks.spark.avro.AvroRelation.newReader(AvroRelation.scala:111)
at com.databricks.spark.avro.AvroRelation.<init>(AvroRelation.scala:53)
at com.databricks.spark.avro.DefaultSource.createRelation(DefaultSource.scala:41)
at org.apache.spark.sql.sources.ResolvedDataSource$.apply(ddl.scala:290)
when I run:
$ sbt package
$ ~/spark-1.3.1/bin/spark-submit --class "tester" target/scala-2.10/project_2.10-0.1-SNAPSHOT.jar
I get the following stack trace:
Exception in thread "main" java.lang.RuntimeException: Failed to load class for data source: com.databricks.spark.avro
at scala.sys.package$.error(package.scala:27)
at org.apache.spark.sql.sources.ResolvedDataSource$.lookupDataSource(ddl.scala:194)
at org.apache.spark.sql.sources.ResolvedDataSource$.apply(ddl.scala:205)
at org.apache.spark.sql.SQLContext.load(SQLContext.scala:697)
What can I do to resolve this error? Any help is greatly appreciated. Thanks!!
"sbt package" will not include your dependencies, try sbt-assembly instead.
I changed the build.sbt file to:
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-sql" % "1.3.0",
"com.databricks" %% "spark-avro" % "1.0.0",
"org.apache.avro" % "avro" % "1.7.7",
"org.apache.avro" % "avro-mapred" % "1.7.7")
assemblyMergeStrategy in assembly := {
case PathList("org", "slf4j", xs#_*) => MergeStrategy.first
case PathList("org", "apache", "spark", xs #_*) => MergeStrategy.first
case PathList("com", "esotericsoftware", "minlog", xs #_*) => MergeStrategy.first
case PathList("javax", "activation", xs #_*) => MergeStrategy.first
case PathList("javax", "servlet", xs #_*) => MergeStrategy.first
case PathList("javax", "xml", "stream", xs #_*) => MergeStrategy.first
case PathList("org", "apache", "commons", xs #_*) => MergeStrategy.first
case PathList("com", "google", "common", xs #_*) => MergeStrategy.first
case "org/apache/hadoop/yarn/factories/package-info.class" => MergeStrategy.first
case "org/apache/hadoop/yarn/factory/providers/package-info.class" => MergeStrategy.first
case "org/apache/hadoop/yarn/util/package-info.class" => MergeStrategy.first
case x if x.startsWith("META-INF") => MergeStrategy.discard
case x if x.startsWith("plugin.properties") => MergeStrategy.discard
case x => {
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
}
and used the command
$ sbt assembly
to build the jar. Everything works now.
I have an automatically generated client for an web service. I has many complicated classes and I have to do a pattern matching against it. For now I have a structure looking like this:
val response = client.getResponse
response match {
case Left(_) => None
case Right(a: SomeClass) => a match {
case SomeClass2(b: Option[SomeClass3]) => b match {
case None => None
case Some(c: SomeClass3) => c match {
case SomeClass4(_, _, _, _, d: Seq[SomeClass4]) => d match {
case Nil => None
case seq: Seq[SomeClass5] => seq match {
case Nil => None
case Seq(xs#_*) => xs map { x =>
x match {
case Nil => None
case SomeClass6(e: SomeClass7) => e match {
case Nil => None
case SomeClass8(f, _, _, _, _) => f match {
case Nil => None
case Seq(xs#_*) => xs map { x =>
x match {
case Nil => None
case SomeClass9(g: Seq[SomeClass9], _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
}
}
}
}
}
}
}
}
}
}
}
where SomeClass1 - SomeClass9 are case classes.
As you can see, it seems frightening. What do I do about it? What's the standard way to make it look nicer?
I guess there should be not only refactoring but rather another approach.
Assuming that a should be SomeClass2, but not SomeClass (same with b, c, d).
You could use alternative patterns like case A | B => ... and structural patterns like Some(MyClass(f)).
Also you could use partial function in map like map { case ... } instead of map { x => x match {...} }.
And I guess there is a error in your code: there is check for case Nil => ...; case SomeClass8(...) => ....
You could replace Seq(xs #_*) with xs. If you need entire collection you don't need to extract elements.
Your code:
response match {
case Left(_) | Right(SomeClass2(None)) | Right(SomeClass2(Some(SomeClass3(_, _, _, _, Nil))) => None
case Right(SomeClass2(Some(SomeClass3(_, _, _, _, xs))) =>
xs map {
case SomeClass6(None) | SomeClass6(Some(SomeClass8(Nil, _, _, _, _))) => None
case SomeClass6(Some(SomeClass8(xs, _, _, _, _))) =>
xs map {
case Nil => None
case SomeClass9(g, _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
}
}
}
You should also extract nested matches to separate methods.
Pattern matching is not the only solution. You could use methods of Either and Option:
response.right.toOption.collect {
// No need for the first part.
case SomeClass2(Some(SomeClass3(_, _, _, _, xs)) if xs.nonEmpty => ...
}
You might find extractors useful. It might also be worth flattening some of the cases so you have
case Right(SomeClass(SomeClass2(Some(SomeClass3(value))))) => value ...
case _ => None
rather than having a None case explicitly defined at each level.
You can probably reduce a lot of this complexity by using for comprehensions rather than pattern matching.
One simple opportunity is where you are mapping a sequence to yet another pattern match:
case seq: Seq[SomeClass5] => seq match {
case Nil => None
case Seq(xs#_*) => xs map { x =>
x match {
...
}
}
}
This is very ugly because you have used match to eliminate the Nil case and then matched seq again. Two levels of match to deal with one object. This could become
case seq: Seq[SomeClass5] => for (x <- seq) yield {
x match {
...
}
}
This eliminates the Nil case check and removes a couple of layers of nesting, which is a big win. And you do this in at least two layers, so that's an even bigger win. Of course, this returns a (possibly Nil) sequence rather than f(x) or None, but you can easily convert that. One way to do this, without adding another of nesting, would be this:
case seq: Seq[SomeClass5] => (for (x <- seq) yield {
x match {
...
}
}) match {
case Nil => None
case Seq(i) => Some(i)
case ...
}
Or, if (as I suspect) you expect these sequences only to have one element in them...
case seq: Seq[SomeClass5] => (for (x <- seq) yield {
x match {
...
}
}) match {
case Seq(i) => Some(i)
case _ => None
}