sbt-native-packager
sbt-native-packager copied to clipboard
Option to set the JAVAHOME before its checked in the bash/bat script.
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
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.
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
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.
Thanks for the detailed explanation.
I would love to merge a pull request for the windows bat script 😍
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
I would prefer to keep platform-specific options out of the universal java options. Maybe you could use bashScriptExtraDefines and batScriptExtraDefines instead.
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.
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?
I think we would need different defaults then for bashScriptConfigLocation / batScriptConfigLocation since currently they point to the same conf/application.ini.
Good point. This is also way more explicit and easier to follow 👍
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