Google Groups

Re: [akka-user] reference.conf conflicts in fat jar (I think)


cessationoftime Feb 4, 2012 6:58 AM
Posted in group: Akka User List
> After sinking 2 days into this, I have reached the conclusion that 1)
> the sbt assembly plugin, in its current state, is incapable of
> including a generated fat reference.conf file in the fat jar, and 2)
> sbt may not even be capable of generating that fat reference.conf
> using the jars in the project's dependencies.
>
> I would love more than anything for someone to prove me wrong by
> posting code samples to the mailing list. :) #hinthint

Here to prove you wrong. (At least on point #2)


I am not familiar with the assembly plugin, but I have this working using a modified version of the proguard plugin.  https://github.com/cessationoftime/xsbt-proguard-plugin 
I want to do a little more work on it before I do a pull request to siasia/xsbt-proguard-plugin. The config isn't as pretty as it could be, I want to split up my Tuple Injar\Outjar filter setting.  But it works well.

I feel the best solution would ultimately be a fork of the proguard plugin which handles akka config issues.  Or a plugin that simply uses the sbt-proguard-plugin as a dependency and handles akka config issues.

The config works like this:

plugins.sbt: 
list the akka JARs which contain reference.conf files so that they may be merged at compile time

Commands.scala: 
implements finding and merging of the reference.conf files at compile time. Also strips comments from the .conf files.
All reference.conf files merge with myapp.conf and common.conf and become application.conf. But I ONLY merge in the "node" configuration of myapp.conf

Build.scala :  (resourceGenerators in Compile <+= Commands.AkkaConfig.akkaMergedRefConfigKey)
this causes me to generate the merged akka config file when I compile, the file ends up in sbt's managed resource folder, which is merged into the final jar.

proguardCfg.sbt:
in this file I have the standard proguard config for xsbt-proguard-plugin and InJar\OutJar filters. The config for this is a little ugly, I have a single Tuple setting for the Injar\Outjar filter. The normal siasia/xsbt-proguard-plugin is currently missing an outJar filter. Injar filter removes the reference.conf files from the akka Jars prior to proguard. Outjar filter removes common.conf and myapp.conf from my build since they are also now part of the merged config.

I also included a command which takes the output of proguard and copies\renames it to signed-min.jar.  In the next step I would then sign the proguarded jar (signed-min.jar) with the xsbt-webstart plugin.

and the configuration looks (approximately) like this:

proguardCfg.sbt
---------------------------------------
//filter resource files from JARs
makeJarFilter <<= (makeJarFilter) {
  (makeFilter) => 
  val (makeInJarFilter,makeOutJarFilter) = makeFilter; 
  val InFilter =
  (file:String) => file match {
      case "akka-remote-2.0-M3.jar" => makeInJarFilter(file) + ",!reference.conf"
      case "akka-actor-2.0-M3.jar" => makeInJarFilter(file) + ",!reference.conf"
      case "akka-agent-2.0-M3.jar" => makeInJarFilter(file) + ",!reference.conf"
      case "application.conf" => makeInJarFilter(file)
      case _ => makeInJarFilter(file)
    };
     val OutFilter =
  (file:String) => file match {
      case "myjar_2.9.1-0.0.1-SNAPSHOT.min.jar" => "!myapp.conf,!common.conf"
      case _ => makeOutJarFilter(file)
    };
  (InFilter,OutFilter)  
}

Commands.proguardOutputRename := "signed-min.jar"

project/plugins.sbt
-----------------------
//akka JARs which will have reference.conf files merged into the proguard output reference.conf file
libraryDependencies ++= Seq(
    "com.typesafe.akka" % "akka-actor" % "2.0-M3",
    "com.typesafe.akka" % "akka-remote" % "2.0-M3",
    "com.typesafe.akka" % "akka-agent" % "2.0-M3",
    "com.typesafe.akka" % "akka-slf4j" % "2.0-M3"    
)

project/Build.scala
-----------------------------------------------------------------------
lazy val aProject: Project = Project("project-name", 
      file("project-name"),
      settings = commonSettings ++ Commands.tasks ++ (libraryDependencies ++=  aProjectDependencies) :+ (resourceGenerators in Compile <+= Commands.AkkaConfig.akkaMergedRefConfigKey),
      dependencies = Seq(dependency1, dependency2))

project/Commands.scala
---------------------------------------------------------------
import sbt._;
import Keys._
import Keys.Classpath
import Keys.TaskStreams
import Project.Initialize
import classpath.ClasspathUtilities
import ProguardPlugin._
import akka.actor.ActorSystem
import scala.collection.immutable.StringLike
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigResolveOptions
object Commands extends Build {
  
   object AkkaConfig {
    
    def stripComments(mergedConfigString : StringLike[_]) : String = {
      val Rege = """^\s*#.*""".r
     val nc : Iterator[String]= mergedConfigString.lines;
     val noComments = nc.flatMap (_ match {
        case Rege() => None
        case x => Some(x)
      })
          noComments.mkString("\n") 
    }
    
   /**
     * get the merged version of the default settings (from the reference.conf files in the JARs on the classpath)
     */
   def mergedConfig(configName: String) : String = {
     
     //load application.conf and then use  .withFallback() on the reference.conf object       
     val referenceConfig = ConfigFactory.parseResourcesAnySyntax(getClass,"/reference", ConfigParseOptions.defaults)
     val applicationConfig = ConfigFactory.parseFile(new java.io.File("src/main/resources/myapp.conf"), ConfigParseOptions.defaults).getConfig(configName)
     val mergedConfig = applicationConfig.withFallback(referenceConfig)
     val mergedConfigString :StringLike[_]  = mergedConfig.root.render;
            
     stripComments(mergedConfigString)
    }
  
    lazy val akkaMergedRefConfigKey = TaskKey[Seq[java.io.File]]("merge-akka-refconfig","pulls the reference.conf files from akka jars and merges them into a new reference.conf file for inclusion later in the proguard output")   
    lazy val akkaMergedRefConfigTask = akkaMergedRefConfigKey <<= (resourceManaged) map { (managedResourceDir : File)=>
   val targetFile = managedResourceDir / "application.conf";
      IO write (targetFile, mergedConfig("node"))
   Seq(targetFile.asFile)
    }  
   
  }
import AkkaConfig._
  
  val proguardOutputRename = SettingKey[String]("proguard-output-rename","final name of the proguarded jar")

 val runMinjarKey = TaskKey[Unit]("run-minjar","executes the mainClass of the proguarded .min.jar file");

lazy val renameProguardOutputKey = TaskKey[Unit]("rename-proguard-output","copied the proguarded jar with a new name")

lazy val renameProguardOutputTask = renameProguardOutputKey <<= (proguard,minJarPath,crossTarget,proguardOutputRename) map { (doProguard,minJar:File,crossDir,newName)=>
    List("cp", minJar.toString, crossDir.toString + "/" + newName) ! ; //execute console command to launch jar
 }

 //execute the mainClass of the proguarded Jar (the .min.jar)
 lazy val runMinjarTask = runMinjarKey <<= (minJarPath,proguard) map { (minJar:File,p)=>
    List("java", "-jar", minJar.toString) ! ; //execute console command to launch jar
  }

val tasks = Seq(runMinjarTask,renameProguardOutputTask, akkaMergedRefConfigTask)


-Chris