sbt-assembly icon indicating copy to clipboard operation
sbt-assembly copied to clipboard

Unable to package spring boot with cloud config jar

Open AjitTK opened this issue 5 years ago • 6 comments

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

AjitTK avatar Jul 01 '20 08:07 AjitTK

I am facing the same issue. can anyone help?

jackyin5918 avatar Dec 23 '20 09:12 jackyin5918

@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 avatar Dec 23 '20 18:12 AjitTK

@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.

jackyin5918 avatar Dec 28 '20 02:12 jackyin5918

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 avatar Jan 21 '21 06:01 KhankaLake

@KhankaLake this strategy is not working. multiple entries of same loader class give you error. Validate this by implementation.

AjitTK avatar Jan 21 '21 07:01 AjitTK

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.store is 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

piotrp avatar Jan 29 '24 10:01 piotrp