Skip to content

Commit 6c4c030

Browse files
committed
FIX #82 adding the ability to specify jvm options via sbt
1 parent 3a2a576 commit 6c4c030

File tree

30 files changed

+381
-103
lines changed

30 files changed

+381
-103
lines changed

project/build.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=0.13.6
1+
sbt.version=0.13.8

project/plugins.sbt

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ resolvers += Resolver.url("sbt-plugin-releases", new URL("http://scalasbt.artifa
22

33
resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven"
44

5-
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.2")
5+
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3")
66

7-
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.6.2")
7+
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.0")
88

9-
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.7.2")
9+
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1")
1010

1111
libraryDependencies <+= (sbtVersion) { sv =>
1212
"org.scala-sbt" % "scripted-plugin" % sv
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# #################################
2+
# ##### Default configuration #####
3+
# #################################
4+
5+
# Available replacements
6+
# ------------------------------------------------
7+
# ${{author}} debian author
8+
# ${{descr}} debian package description
9+
# ${{exec}} startup script name
10+
# ${{chdir}} app directory
11+
# ${{retries}} retries for startup
12+
# ${{retryTimeout}} retry timeout
13+
# ${{app_name}} normalized app name
14+
# ${{daemon_user}} daemon user
15+
# -------------------------------------------------
16+
17+
# DEPRECATED, use -J-Xmx1024m instead
18+
# -mem 1024
19+
20+
# Setting -X directly (-J is stripped)
21+
# -J-X
22+
# -J-Xmx1024
23+
24+
# Add additional jvm parameters
25+
# -Dkey=val
26+
27+
# For play applications you may set
28+
# -Dpidfile.path=/var/run/${{app_name}}/play.pid
29+
30+
# Turn on JVM debugging, open at the given port
31+
# -jvm-debug <port>
32+
33+
# Don't run the java version check
34+
# -no-version-check

src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template

+2-31
Original file line numberDiff line numberDiff line change
@@ -150,34 +150,7 @@ addResidual () {
150150
addDebugger () {
151151
addJava "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$1"
152152
}
153-
# a ham-fisted attempt to move some memory settings in concert
154-
# so they need not be messed around with individually.
155-
get_mem_opts () {
156-
local mem=${1:-1024}
157-
local perm=$(( $mem / 4 ))
158-
(( $perm > 256 )) || perm=256
159-
(( $perm < 1024 )) || perm=1024
160-
local codecache=$(( $perm / 2 ))
161-
162-
# if we detect any of these settings in ${java_opts} we need to NOT output our settings.
163-
# The reason is the Xms/Xmx, if they don't line up, cause errors.
164-
if [[ "${java_opts}" == *-Xmx* ]] ||
165-
[[ "${java_opts}" == *-Xms* ]] ||
166-
[[ "${java_opts}" == *-XX:MaxPermSize* ]] ||
167-
[[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] ||
168-
# check java arguments for settings, too
169-
[[ "${java_args[@]}" == *-Xmx* ]] ||
170-
[[ "${java_args[@]}" == *-Xms* ]] ||
171-
[[ "${java_args[@]}" == *-XX:MaxPermSize* ]] ||
172-
[[ "${java_args[@]}" == *-XX:ReservedCodeCacheSize* ]];
173-
then
174-
echo ""
175-
elif [[ !$no_version_check ]] && [[ "$java_version" > "1.8" ]]; then
176-
echo "-Xms${mem}m -Xmx${mem}m -XX:ReservedCodeCacheSize=${codecache}m"
177-
else
178-
echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
179-
fi
180-
}
153+
181154
require_arg () {
182155
local type="$1"
183156
local opt="$2"
@@ -214,7 +187,7 @@ process_args () {
214187

215188
-no-version-check) no_version_check=1 && shift ;;
216189

217-
-mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;;
190+
-mem) echo "!! WARNING !! -mem option is ignored. Please use -J-Xmx and -J-Xms" && shift 2 ;;
218191
-jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
219192

220193
-main) custom_mainclass="$2" && shift 2 ;;
@@ -274,7 +247,6 @@ run() {
274247

275248
# run sbt
276249
execRunner "$java_cmd" \
277-
$(get_mem_opts $app_mem) \
278250
${java_opts[@]} \
279251
"${java_args[@]}" \
280252
-cp "$(fix_classpath "$app_classpath")" \
@@ -328,7 +300,6 @@ Usage: $script_name [options]
328300
-v | -verbose this runner is chattier
329301
-d | -debug set sbt log level to debug
330302
-no-version-check Don't run the java version check.
331-
-mem <integer> set memory options in MB (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
332303
-main <classname> Define a custom main class
333304
-jvm-debug <port> Turn on JVM debugging, open at the given port.
334305
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
# #################################
2-
# ##### Default configuration #####
3-
# #################################
1+
# #####################################
2+
# ##### Environment Configuration #####
3+
# #####################################
4+
5+
# This file gets sourced before the actual bashscript
6+
# gets executed. You can use this file to provide
7+
# environment variables
48

59
# Available replacements
610
# ------------------------------------------------
@@ -14,21 +18,11 @@
1418
# ${{daemon_user}} daemon user
1519
# -------------------------------------------------
1620

17-
# Setting -Xmx and -Xms in Megabyte
18-
# -mem 1024
19-
20-
# Setting -X directly (-J is stripped)
21-
# -J-X
22-
# -J-Xmx1024
23-
24-
# Add additional jvm parameters
25-
# -Dkey=val
26-
27-
# For play applications you may set
28-
# -Dpidfile.path=/var/run/${{app_name}}/play.pid
29-
30-
# Turn on JVM debugging, open at the given port
31-
# -jvm-debug <port>
21+
# Setting JAVA_OPTS
22+
# -----------------
23+
# JAVA_OPTS="-Dpidfile.path=/var/run/${{app_name}}/play.pid $JAVA_OPTS"
3224

33-
# Don't run the java version check
34-
# -no-version-check
25+
# export env vars for 3rd party libs
26+
# ----------------------------------
27+
# COMPANY_API_KEY=123abc
28+
# export COMPANY_API_KEY

src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-debian-template

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
source /lib/init/vars.sh
1313
source /lib/lsb/init-functions
1414

15+
# adding bashScriptEnvConfigLocation
16+
[[ -f ${{env_config}} ]] && . ${{env_config}}
17+
18+
# $JAVA_OPTS used in $RUN_CMD wrapper
19+
export JAVA_OPTS
1520

1621
PIDFILE=/var/run/${{app_name}}/running.pid
1722

src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-rpm-template

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
# Source function library.
2929
. /etc/rc.d/init.d/functions
3030

31+
# adding bashScriptEnvConfigLocation
32+
[[ -f ${{env_config}} ]] && . ${{env_config}}
33+
34+
# $JAVA_OPTS used in $RUN_CMD wrapper
35+
export JAVA_OPTS
36+
3137
prog="${{exec}}"
3238

3339
# FIXME The pid file should be handled by the executed script

src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala

+48-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package packager
33
package archetypes
44

55
import sbt._
6-
import sbt.Keys.{ mappings, target, name, mainClass, sourceDirectory }
6+
import sbt.Keys.{ mappings, target, name, mainClass, sourceDirectory, javaOptions, streams }
77
import packager.Keys.{ packageName, executableScriptName }
88
import linux.{ LinuxFileMetaData, LinuxPackageMapping }
99
import linux.LinuxPlugin.autoImport.{ linuxPackageMappings, defaultLinuxInstallLocation }
@@ -39,13 +39,19 @@ object JavaAppPackaging extends AutoPlugin with JavaAppStartScript {
3939
*/
4040
val batTemplate = "bat-template"
4141

42+
/**
43+
* Location for the application.ini file used by the bash script to load initialization parameters for jvm and app
44+
*/
45+
val appIniLocation = "${app_home}/../conf/application.ini"
46+
4247
object autoImport extends JavaAppKeys
4348

4449
import JavaAppPackaging.autoImport._
4550

4651
override def requires = debian.DebianPlugin && rpm.RpmPlugin && docker.DockerPlugin && windows.WindowsPlugin
4752

4853
override def projectSettings = Seq(
54+
javaOptions in Universal := Nil,
4955
// Here we record the classpath as it's added to the mappings separately, so
5056
// we can use its order to generate the bash/bat scripts.
5157
scriptClasspathOrdering := Nil,
@@ -61,11 +67,34 @@ object JavaAppPackaging extends AutoPlugin with JavaAppStartScript {
6167
mappings in Universal <++= scriptClasspathOrdering,
6268
scriptClasspath <<= scriptClasspathOrdering map makeRelativeClasspathNames,
6369
bashScriptExtraDefines := Nil,
64-
bashScriptConfigLocation <<= bashScriptConfigLocation ?? None,
70+
// Create a bashConfigLocation if options are set in build.sbt
71+
bashScriptConfigLocation <<= bashScriptConfigLocation ?? Some(appIniLocation),
6572
bashScriptEnvConfigLocation <<= bashScriptEnvConfigLocation ?? None,
66-
bashScriptExtraDefines <++= (bashScriptEnvConfigLocation in Universal) map { _.map { config =>
67-
"[[ -f '" + config +"' ]] && source " + config
68-
}.toSeq },
73+
mappings in Universal := {
74+
val log = streams.value.log
75+
val universalMappings = (mappings in Universal).value
76+
val dir = (target in Universal).value
77+
val options = (javaOptions in Universal).value
78+
79+
bashScriptConfigLocation.value.collect {
80+
case location if options.nonEmpty =>
81+
val configFile = dir / "tmp" / "conf" / "application.ini"
82+
IO.writeLines(configFile, options)
83+
val filteredMappings = universalMappings.filter {
84+
case (file, path) => path != appIniLocation
85+
}
86+
// Warn the user if he tries to specify options
87+
if (filteredMappings.size < universalMappings.size) {
88+
log.warn("--------!!! JVM Options are defined twice !!!-----------")
89+
log.warn("application.ini is already present in output package. Will be overriden by 'javaOptions in Universal'")
90+
}
91+
(configFile -> cleanApplicationIniPath(location)) +: filteredMappings
92+
93+
}.getOrElse(universalMappings)
94+
95+
},
96+
97+
// ---
6998
bashScriptDefines <<= (Keys.mainClass in (Compile, bashScriptDefines), scriptClasspath in bashScriptDefines, bashScriptExtraDefines, bashScriptConfigLocation) map { (mainClass, cp, extras, config) =>
7099
val hasMain =
71100
for {
@@ -208,6 +237,20 @@ object JavaAppPackaging extends AutoPlugin with JavaAppStartScript {
208237
dep <- deps
209238
realDep <- findRealDep(dep, projectArts)
210239
} yield realDep.data -> ("lib/" + getJarFullFilename(realDep))
240+
241+
/**
242+
* Currently unused.
243+
* TODO figure out a proper way to ship default `application.ini` if necessary
244+
*/
245+
protected def applicationIniTemplateSource: java.net.URL = getClass.getResource("application.ini-template")
246+
247+
/**
248+
* @param path that could be relative to app_home
249+
* @return path relative to app_home
250+
*/
251+
private def cleanApplicationIniPath(path: String): String = {
252+
path.replaceFirst("\\$\\{app_home\\}/../", "")
253+
}
211254
}
212255

213256
/**

src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ trait JavaAppKeys {
1212
val bashScriptDefines = TaskKey[Seq[String]]("bashScriptDefines", "A list of definitions that should be written to the bash file template.")
1313
val bashScriptExtraDefines = TaskKey[Seq[String]]("bashScriptExtraDefines", "A list of extra definitions that should be written to the bash file template.")
1414
val bashScriptConfigLocation = TaskKey[Option[String]]("bashScriptConfigLocation", "The location where the bash script will load default argument configuration from.")
15-
val bashScriptEnvConfigLocation = TaskKey[Option[String]]("bashScriptEnvConfigLocation", "The location of a bash script that will be sourced before running the app.")
15+
val bashScriptEnvConfigLocation = SettingKey[Option[String]]("bashScriptEnvConfigLocation", "The location of a bash script that will be sourced before running the app.")
1616
val batScriptExtraDefines = TaskKey[Seq[String]]("batScriptExtraDefines", "A list of extra definitions that should be written to the bat file template.")
1717
val scriptClasspathOrdering = TaskKey[Seq[(File, String)]]("scriptClasspathOrdering", "The order of the classpath used at runtime for the bat/bash scripts.")
1818
val projectDependencyArtifacts = TaskKey[Seq[Attributed[File]]]("projectDependencyArtifacts", "The set of exported artifacts from our dependent projects.")

src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala

+35-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package packager
33
package archetypes
44

55
import sbt._
6-
import sbt.Keys.{ target, mainClass, sourceDirectory, streams }
6+
import sbt.Keys.{ target, mainClass, sourceDirectory, streams, javaOptions, run }
77
import SbtNativePackager.{ Debian, Rpm, Universal }
88
import packager.Keys.{ packageName }
99
import linux.{ LinuxFileMetaData, LinuxPackageMapping, LinuxSymlink, LinuxPlugin }
@@ -32,11 +32,13 @@ object JavaServerAppPackaging extends AutoPlugin {
3232
override def projectSettings = javaServerSettings
3333

3434
val ARCHETYPE = "java_server"
35+
val ENV_CONFIG_REPLACEMENT = "env_config"
36+
val ETC_DEFAULT = "etc-default"
3537

3638
/** These settings will be provided by this archetype*/
3739
def javaServerSettings: Seq[Setting[_]] = linuxSettings ++ debianSettings ++ rpmSettings
3840

39-
protected def etcDefaultTemplateSource: java.net.URL = getClass.getResource("etc-default-template")
41+
protected def etcDefaultTemplateSource: java.net.URL = getClass.getResource(ETC_DEFAULT + "-template")
4042

4143
/**
4244
* general settings which apply to all linux server archetypes
@@ -46,6 +48,7 @@ object JavaServerAppPackaging extends AutoPlugin {
4648
* - config directory
4749
*/
4850
def linuxSettings: Seq[Setting[_]] = Seq(
51+
javaOptions in Linux <<= javaOptions in Universal,
4952
// === logging directory mapping ===
5053
linuxPackageMappings <+= (packageName in Linux, defaultLinuxLogsLocation, daemonUser in Linux, daemonGroup in Linux) map {
5154
(name, logsDir, user, group) => packageTemplateMapping(logsDir + "/" + name)() withUser user withGroup group withPerms "755"
@@ -54,25 +57,22 @@ object JavaServerAppPackaging extends AutoPlugin {
5457
(name, install, logsDir) => LinuxSymlink(install + "/" + name + "/logs", logsDir + "/" + name)
5558
},
5659
// === etc config mapping ===
57-
bashScriptConfigLocation <<= (packageName in Linux) map (name => Some("/etc/default/" + name)),
58-
bashScriptEnvConfigLocation <<= (bashScriptConfigLocation, bashScriptEnvConfigLocation) map { (configLocation, envConfigLocation) =>
59-
envConfigLocation orElse (configLocation map (_ + "_env"))
60-
},
60+
bashScriptConfigLocation := Some(JavaAppPackaging.appIniLocation),
61+
bashScriptEnvConfigLocation := Some("/etc/default/" + (packageName in Linux).value),
6162
linuxEtcDefaultTemplate <<= sourceDirectory map { dir =>
62-
val overrideScript = dir / "templates" / "etc-default"
63+
val overrideScript = dir / "templates" / ETC_DEFAULT
6364
if (overrideScript.exists) overrideScript.toURI.toURL
6465
else etcDefaultTemplateSource
6566
},
66-
makeEtcDefault <<= (packageName in Linux, target in Universal, linuxEtcDefaultTemplate, linuxScriptReplacements)
67+
makeEtcDefault <<= (packageName in Linux, target in Universal, linuxEtcDefaultTemplate, linuxScriptReplacements, javaOptions in Linux)
6768
map makeEtcDefaultScript,
68-
linuxPackageMappings <++= (makeEtcDefault, bashScriptConfigLocation) map { (conf, configLocation) =>
69-
configLocation.flatMap { path =>
70-
// TODO this is ugly. Create a better solution
71-
// check that path doesn't contain relative elements
72-
val relativePaths = "\\$\\{app_home\\}|/[\\.]{1,2}".r.findFirstIn(path)
73-
if (relativePaths.isDefined) None // cannot create the file, mapping provided by user
74-
else conf.map(c => LinuxPackageMapping(Seq(c -> path), LinuxFileMetaData(Users.Root, Users.Root, "644")).withConfig())
75-
}.toSeq
69+
linuxPackageMappings <++= (makeEtcDefault, bashScriptEnvConfigLocation) map { (conf, envLocation) =>
70+
val mapping = for (
71+
path <- envLocation;
72+
c <- conf
73+
) yield LinuxPackageMapping(Seq(c -> path), LinuxFileMetaData(Users.Root, Users.Root, "644")).withConfig()
74+
75+
mapping.toSeq
7676
}
7777

7878
)
@@ -89,6 +89,7 @@ object JavaServerAppPackaging extends AutoPlugin {
8989
linuxScriptReplacements <++= (requiredStartFacilities, requiredStopFacilities, startRunlevels, stopRunlevels, serverLoading) apply
9090
makeStartScriptReplacements,
9191
linuxScriptReplacements += JavaServerLoaderScript.loaderFunctionsReplacement(serverLoading.value, ARCHETYPE),
92+
linuxScriptReplacements ++= bashScriptEnvConfigLocation.value.map(ENV_CONFIG_REPLACEMENT -> _).toSeq,
9293

9394
linuxStartScriptTemplate := JavaServerLoaderScript(
9495
script = startScriptName(serverLoading.value, Debian),
@@ -127,6 +128,7 @@ object JavaServerAppPackaging extends AutoPlugin {
127128
linuxScriptReplacements <++= (requiredStartFacilities, requiredStopFacilities, startRunlevels, stopRunlevels, serverLoading) apply
128129
makeStartScriptReplacements,
129130
linuxScriptReplacements += JavaServerLoaderScript.loaderFunctionsReplacement(serverLoading.value, ARCHETYPE),
131+
linuxScriptReplacements ++= bashScriptEnvConfigLocation.value.map(ENV_CONFIG_REPLACEMENT -> _).toSeq,
130132

131133
// === /var/run/app pid folder ===
132134
linuxPackageMappings <+= (packageName, daemonUser, daemonGroup) map { (name, user, group) =>
@@ -262,10 +264,26 @@ object JavaServerAppPackaging extends AutoPlugin {
262264
Some(script)
263265
}
264266

265-
protected def makeEtcDefaultScript(name: String, tmpDir: File, source: java.net.URL, replacements: Seq[(String, String)]): Option[File] = {
267+
/**
268+
* Creates the etc-default file, which will contain the basic configuration
269+
* for an app.
270+
*
271+
* @param name of the etc-default config file
272+
* @param tmpDir to store the resulting file in (e.g. target in Universal)
273+
* @param source of etc-default script
274+
* @param replacements for placeholders in etc-default script
275+
* @param javaOptions that get appended to the etc-default script
276+
*
277+
* @return Some(file: File)
278+
*/
279+
protected def makeEtcDefaultScript(name: String, tmpDir: File, source: java.net.URL,
280+
replacements: Seq[(String, String)], javaOptions: Seq[String]): Option[File] = {
266281
val scriptBits = TemplateWriter.generateScript(source, replacements)
267282
val script = tmpDir / "tmp" / "etc" / "default" / name
268283
IO.write(script, scriptBits)
284+
if (javaOptions.nonEmpty) {
285+
IO.writeLines(script, "# java options from build" +: javaOptions, append = true)
286+
}
269287
Some(script)
270288
}
271289

0 commit comments

Comments
 (0)