Skip to content

Commit

Permalink
FIX #82 adding the ability to specify jvm options via sbt
Browse files Browse the repository at this point in the history
  • Loading branch information
muuki88 committed Mar 22, 2015
1 parent 3a2a576 commit 6c4c030
Show file tree
Hide file tree
Showing 30 changed files with 381 additions and 103 deletions.
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.6
sbt.version=0.13.8
6 changes: 3 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ resolvers += Resolver.url("sbt-plugin-releases", new URL("http://scalasbt.artifa

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

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

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

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

libraryDependencies <+= (sbtVersion) { sv =>
"org.scala-sbt" % "scripted-plugin" % sv
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# #################################
# ##### Default configuration #####
# #################################

# Available replacements
# ------------------------------------------------
# ${{author}} debian author
# ${{descr}} debian package description
# ${{exec}} startup script name
# ${{chdir}} app directory
# ${{retries}} retries for startup
# ${{retryTimeout}} retry timeout
# ${{app_name}} normalized app name
# ${{daemon_user}} daemon user
# -------------------------------------------------

# DEPRECATED, use -J-Xmx1024m instead
# -mem 1024

# Setting -X directly (-J is stripped)
# -J-X
# -J-Xmx1024

# Add additional jvm parameters
# -Dkey=val

# For play applications you may set
# -Dpidfile.path=/var/run/${{app_name}}/play.pid

# Turn on JVM debugging, open at the given port
# -jvm-debug <port>

# Don't run the java version check
# -no-version-check
Original file line number Diff line number Diff line change
Expand Up @@ -150,34 +150,7 @@ addResidual () {
addDebugger () {
addJava "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$1"
}
# a ham-fisted attempt to move some memory settings in concert
# so they need not be messed around with individually.
get_mem_opts () {
local mem=${1:-1024}
local perm=$(( $mem / 4 ))
(( $perm > 256 )) || perm=256
(( $perm < 1024 )) || perm=1024
local codecache=$(( $perm / 2 ))

# if we detect any of these settings in ${java_opts} we need to NOT output our settings.
# The reason is the Xms/Xmx, if they don't line up, cause errors.
if [[ "${java_opts}" == *-Xmx* ]] ||
[[ "${java_opts}" == *-Xms* ]] ||
[[ "${java_opts}" == *-XX:MaxPermSize* ]] ||
[[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] ||
# check java arguments for settings, too
[[ "${java_args[@]}" == *-Xmx* ]] ||
[[ "${java_args[@]}" == *-Xms* ]] ||
[[ "${java_args[@]}" == *-XX:MaxPermSize* ]] ||
[[ "${java_args[@]}" == *-XX:ReservedCodeCacheSize* ]];
then
echo ""
elif [[ !$no_version_check ]] && [[ "$java_version" > "1.8" ]]; then
echo "-Xms${mem}m -Xmx${mem}m -XX:ReservedCodeCacheSize=${codecache}m"
else
echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
fi
}

require_arg () {
local type="$1"
local opt="$2"
Expand Down Expand Up @@ -214,7 +187,7 @@ process_args () {

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

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

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

# run sbt
execRunner "$java_cmd" \
$(get_mem_opts $app_mem) \
${java_opts[@]} \
"${java_args[@]}" \
-cp "$(fix_classpath "$app_classpath")" \
Expand Down Expand Up @@ -328,7 +300,6 @@ Usage: $script_name [options]
-v | -verbose this runner is chattier
-d | -debug set sbt log level to debug
-no-version-check Don't run the java version check.
-mem <integer> set memory options in MB (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
-main <classname> Define a custom main class
-jvm-debug <port> Turn on JVM debugging, open at the given port.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# #################################
# ##### Default configuration #####
# #################################
# #####################################
# ##### Environment Configuration #####
# #####################################

# This file gets sourced before the actual bashscript
# gets executed. You can use this file to provide
# environment variables

# Available replacements
# ------------------------------------------------
Expand All @@ -14,21 +18,11 @@
# ${{daemon_user}} daemon user
# -------------------------------------------------

# Setting -Xmx and -Xms in Megabyte
# -mem 1024

# Setting -X directly (-J is stripped)
# -J-X
# -J-Xmx1024

# Add additional jvm parameters
# -Dkey=val

# For play applications you may set
# -Dpidfile.path=/var/run/${{app_name}}/play.pid

# Turn on JVM debugging, open at the given port
# -jvm-debug <port>
# Setting JAVA_OPTS
# -----------------
# JAVA_OPTS="-Dpidfile.path=/var/run/${{app_name}}/play.pid $JAVA_OPTS"

# Don't run the java version check
# -no-version-check
# export env vars for 3rd party libs
# ----------------------------------
# COMPANY_API_KEY=123abc
# export COMPANY_API_KEY
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
source /lib/init/vars.sh
source /lib/lsb/init-functions

# adding bashScriptEnvConfigLocation
[[ -f ${{env_config}} ]] && . ${{env_config}}

# $JAVA_OPTS used in $RUN_CMD wrapper
export JAVA_OPTS

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
# Source function library.
. /etc/rc.d/init.d/functions

# adding bashScriptEnvConfigLocation
[[ -f ${{env_config}} ]] && . ${{env_config}}

# $JAVA_OPTS used in $RUN_CMD wrapper
export JAVA_OPTS

prog="${{exec}}"

# FIXME The pid file should be handled by the executed script
Expand Down
53 changes: 48 additions & 5 deletions src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package packager
package archetypes

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

/**
* Location for the application.ini file used by the bash script to load initialization parameters for jvm and app
*/
val appIniLocation = "${app_home}/../conf/application.ini"

object autoImport extends JavaAppKeys

import JavaAppPackaging.autoImport._

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

override def projectSettings = Seq(
javaOptions in Universal := Nil,
// Here we record the classpath as it's added to the mappings separately, so
// we can use its order to generate the bash/bat scripts.
scriptClasspathOrdering := Nil,
Expand All @@ -61,11 +67,34 @@ object JavaAppPackaging extends AutoPlugin with JavaAppStartScript {
mappings in Universal <++= scriptClasspathOrdering,
scriptClasspath <<= scriptClasspathOrdering map makeRelativeClasspathNames,
bashScriptExtraDefines := Nil,
bashScriptConfigLocation <<= bashScriptConfigLocation ?? None,
// Create a bashConfigLocation if options are set in build.sbt
bashScriptConfigLocation <<= bashScriptConfigLocation ?? Some(appIniLocation),
bashScriptEnvConfigLocation <<= bashScriptEnvConfigLocation ?? None,
bashScriptExtraDefines <++= (bashScriptEnvConfigLocation in Universal) map { _.map { config =>
"[[ -f '" + config +"' ]] && source " + config
}.toSeq },
mappings in Universal := {
val log = streams.value.log
val universalMappings = (mappings in Universal).value
val dir = (target in Universal).value
val options = (javaOptions in Universal).value

bashScriptConfigLocation.value.collect {
case location if options.nonEmpty =>
val configFile = dir / "tmp" / "conf" / "application.ini"
IO.writeLines(configFile, options)
val filteredMappings = universalMappings.filter {
case (file, path) => path != appIniLocation
}
// Warn the user if he tries to specify options
if (filteredMappings.size < universalMappings.size) {
log.warn("--------!!! JVM Options are defined twice !!!-----------")
log.warn("application.ini is already present in output package. Will be overriden by 'javaOptions in Universal'")
}
(configFile -> cleanApplicationIniPath(location)) +: filteredMappings

}.getOrElse(universalMappings)

},

// ---
bashScriptDefines <<= (Keys.mainClass in (Compile, bashScriptDefines), scriptClasspath in bashScriptDefines, bashScriptExtraDefines, bashScriptConfigLocation) map { (mainClass, cp, extras, config) =>
val hasMain =
for {
Expand Down Expand Up @@ -208,6 +237,20 @@ object JavaAppPackaging extends AutoPlugin with JavaAppStartScript {
dep <- deps
realDep <- findRealDep(dep, projectArts)
} yield realDep.data -> ("lib/" + getJarFullFilename(realDep))

/**
* Currently unused.
* TODO figure out a proper way to ship default `application.ini` if necessary
*/
protected def applicationIniTemplateSource: java.net.URL = getClass.getResource("application.ini-template")

/**
* @param path that could be relative to app_home
* @return path relative to app_home
*/
private def cleanApplicationIniPath(path: String): String = {
path.replaceFirst("\\$\\{app_home\\}/../", "")
}
}

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

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

val ARCHETYPE = "java_server"
val ENV_CONFIG_REPLACEMENT = "env_config"
val ETC_DEFAULT = "etc-default"

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

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

/**
* general settings which apply to all linux server archetypes
Expand All @@ -46,6 +48,7 @@ object JavaServerAppPackaging extends AutoPlugin {
* - config directory
*/
def linuxSettings: Seq[Setting[_]] = Seq(
javaOptions in Linux <<= javaOptions in Universal,
// === logging directory mapping ===
linuxPackageMappings <+= (packageName in Linux, defaultLinuxLogsLocation, daemonUser in Linux, daemonGroup in Linux) map {
(name, logsDir, user, group) => packageTemplateMapping(logsDir + "/" + name)() withUser user withGroup group withPerms "755"
Expand All @@ -54,25 +57,22 @@ object JavaServerAppPackaging extends AutoPlugin {
(name, install, logsDir) => LinuxSymlink(install + "/" + name + "/logs", logsDir + "/" + name)
},
// === etc config mapping ===
bashScriptConfigLocation <<= (packageName in Linux) map (name => Some("/etc/default/" + name)),
bashScriptEnvConfigLocation <<= (bashScriptConfigLocation, bashScriptEnvConfigLocation) map { (configLocation, envConfigLocation) =>
envConfigLocation orElse (configLocation map (_ + "_env"))
},
bashScriptConfigLocation := Some(JavaAppPackaging.appIniLocation),
bashScriptEnvConfigLocation := Some("/etc/default/" + (packageName in Linux).value),
linuxEtcDefaultTemplate <<= sourceDirectory map { dir =>
val overrideScript = dir / "templates" / "etc-default"
val overrideScript = dir / "templates" / ETC_DEFAULT
if (overrideScript.exists) overrideScript.toURI.toURL
else etcDefaultTemplateSource
},
makeEtcDefault <<= (packageName in Linux, target in Universal, linuxEtcDefaultTemplate, linuxScriptReplacements)
makeEtcDefault <<= (packageName in Linux, target in Universal, linuxEtcDefaultTemplate, linuxScriptReplacements, javaOptions in Linux)
map makeEtcDefaultScript,
linuxPackageMappings <++= (makeEtcDefault, bashScriptConfigLocation) map { (conf, configLocation) =>
configLocation.flatMap { path =>
// TODO this is ugly. Create a better solution
// check that path doesn't contain relative elements
val relativePaths = "\\$\\{app_home\\}|/[\\.]{1,2}".r.findFirstIn(path)
if (relativePaths.isDefined) None // cannot create the file, mapping provided by user
else conf.map(c => LinuxPackageMapping(Seq(c -> path), LinuxFileMetaData(Users.Root, Users.Root, "644")).withConfig())
}.toSeq
linuxPackageMappings <++= (makeEtcDefault, bashScriptEnvConfigLocation) map { (conf, envLocation) =>
val mapping = for (
path <- envLocation;
c <- conf
) yield LinuxPackageMapping(Seq(c -> path), LinuxFileMetaData(Users.Root, Users.Root, "644")).withConfig()

mapping.toSeq
}

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

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

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

protected def makeEtcDefaultScript(name: String, tmpDir: File, source: java.net.URL, replacements: Seq[(String, String)]): Option[File] = {
/**
* Creates the etc-default file, which will contain the basic configuration
* for an app.
*
* @param name of the etc-default config file
* @param tmpDir to store the resulting file in (e.g. target in Universal)
* @param source of etc-default script
* @param replacements for placeholders in etc-default script
* @param javaOptions that get appended to the etc-default script
*
* @return Some(file: File)
*/
protected def makeEtcDefaultScript(name: String, tmpDir: File, source: java.net.URL,
replacements: Seq[(String, String)], javaOptions: Seq[String]): Option[File] = {
val scriptBits = TemplateWriter.generateScript(source, replacements)
val script = tmpDir / "tmp" / "etc" / "default" / name
IO.write(script, scriptBits)
if (javaOptions.nonEmpty) {
IO.writeLines(script, "# java options from build" +: javaOptions, append = true)
}
Some(script)
}

Expand Down
Loading

0 comments on commit 6c4c030

Please sign in to comment.