Unable to package spring boot with cloud config jar
Hi, I am trying to assemble spring boot with cloud config client jar but it not assembling properly due to wrong spring.factories file.
No existing merge strategy properly merge spring.factories files
I am facing the same issue. can anyone help?
@jackyin5918 by using https://www.scala-sbt.org/sbt-native-packager/gettingstarted.html I resolve this. I created jar file of my project without dependency and exported all project dependency jars in different folder using sbt native packaging and run the project jar with added dependency in class path.
@AjitTK Thank you for sharing. I just saw your comment. and I was trying this https://github.com/xerial/sbt-pack, looks like the same approach to solve this issue.
just: assemblyMergeStrategy in assembly := { case PathList("META-INF", ps @ _) if ps.contains("MANIFEST.MF") => MergeStrategy.discard case PathList("META-INF", ps @ _) if ps.contains("spring.factories") || ps.contains("spring.handlers") || ps.contains("spring.schemas") => MergeStrategy.concat }
@KhankaLake this strategy is not working. multiple entries of same loader class give you error. Validate this by implementation.
This works for me:
case PathList("META-INF", "spring.factories") => new MergeStrategy {
import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets
import java.util.Properties
import scala.jdk.CollectionConverters.*
override val name: String = "merge-spring.factories"
override def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
val allFactories = files.foldLeft(Map.empty[String, List[String]]) { (acc, file) =>
val props = new Properties()
IO.load(props, file)
val factories = props.asScala.map { case (name, classes) => name.trim -> classes.split(',').map(_.trim).toList }
acc ++ factories.map { case (name, classes) => name -> (classes ++ acc.getOrElse(name, List.empty)) }
}
// use custom properties formatting because 'Properties.store' appends date to result
// (we hope that nobody will use non-ASCII characters in class names)
val file = MergeStrategy.createMergeTarget(tempDir, path)
val lines = "# merged spring.factories\n\n" + allFactories.map {
case (name, classes) => s"$name=${classes.mkString("\\\n", ",\\\n", "\n")}"
}.mkString("\n")
IO.write(file, lines.getBytes(StandardCharsets.ISO_8859_1))
Right(Seq(file -> path))
}
}
A few notes:
- it uses unix newlines to generate the same results on Windows
-
Properties.storeis not used because it- always adds comment with current date (making build non-repeatable)
- escapes newlines, making results less readable
- I'm using old API