sbt-native-packager icon indicating copy to clipboard operation
sbt-native-packager copied to clipboard

Option to set the JAVAHOME before its checked in the bash/bat script.

Open rehan814 opened this issue 8 years ago • 11 comments
trafficstars

Hi,

We want to ship our own jre with the application. The application runs through one of the bash/bat scripts generated by the sbt build tool using universal plugin as universal:package-zip-tarball and universal:packageBin.

Is there any option to check the JAVAHOME before it checks a systemwide JAVAHOME. I mean we want to set our own JAVAHOME through the build.sbt script but that option is added in the bash/bat scripts (specially bat) after it has already check for the systemwide java.

Problem is if the system doesn't have a java then it throws an error to download the java first. In bash script we have suppressed that error using -no-version-check option. But on the bat script there is no such option available.

Expected behaviour

Set JAVAHOME at the start of the bash/bat scripts.

Actual behaviour

javaoptions set through batFileExtraDefines are added at almost end of the batch script when javahome is already been checked.

Information

  • What sbt-native-packager are you using universal

  • What sbt version 0.13.15

  • What is your build system (e.g. Ubuntu, MacOS, Windows, Debian ) MAcOS

  • What package are you building (e.g. docker, rpm, ...) universal package-zip-tarball

  • What version has your build tool (find out with e.g. rpm --version)

  • What is your target system (e.g. Ubuntu 16.04, CentOS 7) windows, linux

rehan814 avatar Nov 22 '17 13:11 rehan814

Thanks for your feature request :) Let's see if we can solve this without complicating the bash script. It's already quite complicated for a simple java -cp ... call.

Copy & Alter

This first option is to override the bash script.

That is a very brutal, but simple solution. However it comes with the downside that possible bugfixes / features won't be directly available when you upgrade native-packager

Provide a second start script

Native-packager supports multiple entrypoints by default. However you can add your own bash scripts that init the application after a {{JAVA_HOME}} check.

The script template is straightfoward. Finding out the current script directory is only tricky when you want to be able to execute on MacOS.

muuki88 avatar Nov 24 '17 12:11 muuki88

I think we already support this in some other way: http://sbt-native-packager.readthedocs.io/en/stable/recipes/embedded-jvm.html#adding-the-jvm

muuki88 avatar Dec 10 '17 16:12 muuki88

Hi Mukki,

Thanks for the feedback. we already had implemented the solution (as mentioned in the last comment) to use our own JRE. However this solution doesn't work, when we tested our application on a solaris system because it didn't have JAVAHOME set on the environment variable. This is where we realise that the bash script checks for a JAVAHOME even if we set the JRE path through the build script.

This is what our code looked like.

mappings in Universal ++= {
  // Add logback config:
  file("src/main/resources/logback.xml") -> "conf/logback.xml"
  // Add JRE
  val jresDir = target.value / "java"
  // Map the location of jre files
  val jreName = "ibm-" + s"${buildEnv.value}" + "-jre8"
  val jreLocation = jresDir.toPath.resolve(jreName).resolve("jre")
  directory(jreLocation.toFile).map { j =>
    j._1 -> j._2.replace(jreName, "jre")
    }
}

javaOptions in Universal ++= Seq(
  "-java-home ${app_home}/../jre", ""
)

We got away with this problem on solaris by using -no-version-check. However as bat script didn't have the -no-version-check so we had to use our own bat template script.

I have implemented a small change in the bat script to read the -no-version-check option and will be happy to share or do the pull request. It also opens the door for bat script to read more arguments as done for the bash script.

rehan814 avatar Dec 11 '17 11:12 rehan814

Thanks for the detailed explanation.

I would love to merge a pull request for the windows bat script 😍

muuki88 avatar Dec 11 '17 22:12 muuki88

Hi Mukki,

So previously we were using 1.2.2 and while testing with the sbt 1.3.2, I have noticed a few changes done to the bat template file already. There is one issue with one of the change. The configuration file path is changed on the fly to be application.ini where previously we had APPNAME_config.txt. Problem is, we had configurations for bash script in the application.ini. and when batch script reads that configuration it throws an error.

Example: In our application's build file we have

javaOptions in Universal ++= Seq(
  "-java-home ${app_home}/../jre", ""
)

which creates a config file application.ini with this line

# options from build
-java-home ${app_home}/../jre

As you can see this configuration is for bash script. Now when the batch script reads the application.ini it throws Error: Unknown option 'j'

Error happens when it reaches the the run command.

Any workaround in your mind for this issue?

An obvious one is to put a condition in batch file that when we are parsing the config file, if there is -java-home parameter then just ignore it. But this will only work for the -java-home.

Another general solution could be to have an identifier in front of parameter's name for bash script and batch script. for example -bash-java-home meant for bash and -batch-java-home meant for batch script. And then add if statements in both bash and bat script templates when they parse the config file to check if the parameter it has read is meant for batch or bash script.

Another option could be to put some identifiers in the config file such as

# options from build 
 
/*bash options
-java-home ${app_home}/../jre
*/

/*batch options
JAVA_HOME @@APP_HOME@@
*/

So each script will look for its own options line (/*bash options, /batch options) and will read the config file until it sees '/'. This seems to me a very nice solution but it needs two separate setting keys for javaOptions i.e. javaOptionsBat, javaOptionsBash

rehan814 avatar Dec 12 '17 14:12 rehan814

I would prefer to keep platform-specific options out of the universal java options. Maybe you could use bashScriptExtraDefines and batScriptExtraDefines instead.

dwickern avatar Dec 12 '17 17:12 dwickern

Hi Dwickern,

Actually the issue I mentioned above is making the universal package less of a universal and more of a platform specific thing. I guess what you are suggesting is to completely remove javaOption setting key? In that case, its usage should be discouraged and there should be no config file.

rehan814 avatar Dec 18 '17 15:12 rehan814

Both are valid points. I'm with @dwickern on keeping the javaOptions in Universal platform agnostic. IMHO the javaOptions setting should always be favoured over the application.ini as it is way more powerful and "sbt like".

Working with comments or magic syntax is too error prone and we already have to much (the -J prefix for example). I would like to scope the generation of the application.ini in some way, e.g.

// applicable to unix and windows
javaOptions in Universal ++= Seq(
 "-J-Xms128mb", "-J-Xmx512mb"
)

// bash script specific
javaOptions in (Universal, makeBashScripts) ++= Seq(

)
// bat script specific
javaOptions in (Universal, makeBatScripts) ++= Seq(

)

The responsible line for the bash script is here. The change should be straightforward

// not sure if this is required though :/
javaOptions in (Universal, makeBashScript) := (javaOptions in Universal).value,

mappings in Universal := generateApplicationIni(
   (mappings in Universal).value
   // this allows to customise the application.ini within the makeBashscriptTask
   (javaOptions in (Universal, makeBashScript)).value,
   bashScriptConfigLocation.value,
   (target in Universal).value,
   streams.value.log
)

WDYT?

muuki88 avatar Dec 30 '17 13:12 muuki88

I think we would need different defaults then for bashScriptConfigLocation / batScriptConfigLocation since currently they point to the same conf/application.ini.

dwickern avatar Dec 30 '17 19:12 dwickern

Good point. This is also way more explicit and easier to follow 👍

muuki88 avatar Dec 31 '17 10:12 muuki88

On a slightly related note, the -java-home option doesn't really seem to fly when working with batch, so I applied the following trick instead:

// force write java home in script
makeBatScripts := {
  val s: TaskStreams = streams.value
  val bats = makeBatScripts.value
  
  bats.map(_._1).foreach{bat => 
    s.log.info(s"tweaking $bat")
  
    val batOut = IO.readLines(bat).flatMap{
      case s @ "set \"APP_LIB_DIR=%APP_HOME%\\lib\\\"" => 
        Seq(s,"set \"JAVA_HOME=%APP_HOME%\\jre\"")
      case s => Seq(s)
    }
  
    IO.writeLines(bat,batOut)
    s.log.info(s"wrote $bat")
  }
  
  bats
}

The java options also didn't seem to be well behaved either, so became:

// set some java options
batScriptExtraDefines ++= Seq(
  "-server",
  "-Xms128m",
  "-Xmx2048m",
  "-Xss1m",
  "-XX:+UseG1GC",
  "-XX:MaxDirectMemorySize=1024m",
  "-Dcom.sun.management.jmxremote",
).map{p => 
  s"""call :add_java $p"""
}

Sorry for slightly bombing the issue. But given that I landed here when looking for a solution for my problem I thought I might leave a trace for others

spangaer avatar May 14 '19 10:05 spangaer