diff --git a/project/build.properties b/project/build.properties index 64abd373f..a6e117b61 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.6 +sbt.version=0.13.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index 249ff6a24..31df39d7f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -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 diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/application.ini-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/application.ini-template new file mode 100644 index 000000000..383b51947 --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/application.ini-template @@ -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 + +# Don't run the java version check +# -no-version-check \ No newline at end of file diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template index f2872ee9b..61a068d5b 100644 --- a/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template @@ -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" @@ -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 ;; @@ -274,7 +247,6 @@ run() { # run sbt execRunner "$java_cmd" \ - $(get_mem_opts $app_mem) \ ${java_opts[@]} \ "${java_args[@]}" \ -cp "$(fix_classpath "$app_classpath")" \ @@ -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 set memory options in MB (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) -main Define a custom main class -jvm-debug Turn on JVM debugging, open at the given port. diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/etc-default-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/etc-default-template index e411aabdc..786b94a53 100644 --- a/src/main/resources/com/typesafe/sbt/packager/archetypes/etc-default-template +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/etc-default-template @@ -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 # ------------------------------------------------ @@ -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 +# 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 \ No newline at end of file +# export env vars for 3rd party libs +# ---------------------------------- +# COMPANY_API_KEY=123abc +# export COMPANY_API_KEY \ No newline at end of file diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-debian-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-debian-template index cf5bcbaaa..f1092aa2f 100644 --- a/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-debian-template +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-debian-template @@ -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 diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-rpm-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-rpm-template index fd1b7296a..12fbc335b 100644 --- a/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-rpm-template +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/java_server/systemloader/systemv/start-rpm-template @@ -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 diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala index 873e84244..3ae1f196f 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala @@ -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 } @@ -39,6 +39,11 @@ 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._ @@ -46,6 +51,7 @@ object JavaAppPackaging extends AutoPlugin with JavaAppStartScript { 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, @@ -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 { @@ -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\\}/../", "") + } } /** diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala index 2d3ea764d..17b9a3154 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala @@ -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.") diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala index 5b80b8363..d3a0aa678 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -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 } @@ -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 @@ -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" @@ -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 } ) @@ -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), @@ -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) => @@ -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) } diff --git a/src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerHelper.scala b/src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerHelper.scala index 2071ccc0b..d86c28330 100644 --- a/src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerHelper.scala +++ b/src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerHelper.scala @@ -2,8 +2,8 @@ package com.typesafe.sbt.packager.jdkpackager import com.typesafe.sbt.packager.chmod import sbt._ - import scala.util.Try +import com.typesafe.sbt.packager.archetypes.JavaAppPackaging /** * Support/helper functions for interacting with the `javapackager` tool. * @author Simeon H.K. Fitch @@ -80,7 +80,7 @@ object JDKPackagerHelper { .getOrElse(Map.empty) // Make a setting? - val jvmOptsFile = (sourceDir ** "jvmopts").getPaths.headOption.map(file) + val jvmOptsFile = (sourceDir ** JavaAppPackaging.appIniLocation).getPaths.headOption.map(file) val jvmOptsArgs = jvmOptsFile.toSeq.flatMap { jvmopts ⇒ IO.readLines(jvmopts).map { diff --git a/src/sbt-test/bash/memory-settings/build.sbt b/src/sbt-test/bash/memory-settings/build.sbt new file mode 100644 index 000000000..8ddcc83ac --- /dev/null +++ b/src/sbt-test/bash/memory-settings/build.sbt @@ -0,0 +1,24 @@ +enablePlugins(JavaAppPackaging) + +name := "simple-app" + +version := "0.1.0" + +javaOptions in Universal ++= Seq( + "-J-Xmx64m", "-J-Xms64m" +) + +TaskKey[Unit]("jvmopts-check") := { + val jvmopts = (stagingDirectory in Universal).value / "conf" / "application.ini" + val options = IO.read(jvmopts) + assert(options contains "-J-Xmx64m", "Options don't contain xmx setting:\n" + options) + assert(options contains "-J-Xms64m", "Options don't contain xms setting:\n" + options) +} + +TaskKey[Unit]("run-check") := { + val cwd = (stagingDirectory in Universal).value + val cmd = Seq((cwd / "bin" / packageName.value).getAbsolutePath) + val memory = (Process(cmd, cwd).!!).replaceAll("\n", "") + assert(memory.toLong <= 64, "Runtime memory is bigger then 64m < " + memory + "m") +} + diff --git a/src/sbt-test/bash/memory-settings/project/plugins.sbt b/src/sbt-test/bash/memory-settings/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/bash/memory-settings/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/bash/memory-settings/src/main/scala/MainApp.scala b/src/sbt-test/bash/memory-settings/src/main/scala/MainApp.scala new file mode 100644 index 000000000..9f0c6bd99 --- /dev/null +++ b/src/sbt-test/bash/memory-settings/src/main/scala/MainApp.scala @@ -0,0 +1,4 @@ +object MainApp extends App { + val memory = Runtime.getRuntime.maxMemory() / (1024L * 1024L) + print(memory) +} diff --git a/src/sbt-test/bash/memory-settings/test b/src/sbt-test/bash/memory-settings/test new file mode 100644 index 000000000..a2fad512b --- /dev/null +++ b/src/sbt-test/bash/memory-settings/test @@ -0,0 +1,5 @@ +# Run the staging and check the script. +> stage +$ exists target/universal/stage/conf/application.ini +> jvmopts-check +> run-check \ No newline at end of file diff --git a/src/sbt-test/bash/simple-app/build.sbt b/src/sbt-test/bash/simple-app/build.sbt new file mode 100644 index 000000000..126282f43 --- /dev/null +++ b/src/sbt-test/bash/simple-app/build.sbt @@ -0,0 +1,12 @@ +enablePlugins(JavaAppPackaging) + +name := "simple-app" + +version := "0.1.0" + +TaskKey[Unit]("run-check") := { + val cwd = (stagingDirectory in Universal).value + val cmd = Seq((cwd / "bin" / packageName.value).getAbsolutePath) + val output = Process(cmd, cwd).!! + assert(output contains "SUCCESS!", "Output didn't contain success: " + output) +} diff --git a/src/sbt-test/bash/simple-app/project/plugins.sbt b/src/sbt-test/bash/simple-app/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/bash/simple-app/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/bash/simple-app/src/main/scala/MainApp.scala b/src/sbt-test/bash/simple-app/src/main/scala/MainApp.scala new file mode 100644 index 000000000..0325f9cb5 --- /dev/null +++ b/src/sbt-test/bash/simple-app/src/main/scala/MainApp.scala @@ -0,0 +1,3 @@ +object MainApp extends App { + println("SUCCESS!") +} diff --git a/src/sbt-test/bash/simple-app/test b/src/sbt-test/bash/simple-app/test new file mode 100644 index 000000000..877989c06 --- /dev/null +++ b/src/sbt-test/bash/simple-app/test @@ -0,0 +1,3 @@ +# Run the staging and check the script. +> stage +> run-check \ No newline at end of file diff --git a/src/sbt-test/jar/launcher-jar/build.sbt b/src/sbt-test/jar/launcher-jar/build.sbt index 1e2e7ea0c..f5d4bd402 100644 --- a/src/sbt-test/jar/launcher-jar/build.sbt +++ b/src/sbt-test/jar/launcher-jar/build.sbt @@ -12,15 +12,19 @@ libraryDependencies ++= Seq( TaskKey[Unit]("check-classpath") := { val dir = (stagingDirectory in Universal).value val bat = IO.read(dir / "bin" / "launcher-jar-test.bat") - assert(bat contains "set \"APP_CLASSPATH=\"") - assert(bat contains "set \"APP_MAIN_CLASS=-jar %APP_LIB_DIR%\\launcher-jar-test.launcher-jar-test-0.1.0-launcher.jar\"") + assert(bat contains "set \"APP_CLASSPATH=\"", "bat should set APP_CLASSPATH:\n" + bat) + assert(bat contains "set \"APP_MAIN_CLASS=-jar %APP_LIB_DIR%\\launcher-jar-test.launcher-jar-test-0.1.0-launcher.jar\"", + "bat should set APP_MAIN_CLASS:\n" + bat) val bash = IO.read(dir / "bin" / "launcher-jar-test") - assert(bash contains "declare -r app_classpath=\"\"") - assert(bash contains "declare -r app_mainclass=\"-jar $lib_dir/launcher-jar-test.launcher-jar-test-0.1.0-launcher.jar\"") + assert(bash contains "declare -r app_classpath=\"\"", "bash should declare app_classpath:\n" + bash) + assert(bash contains "declare -r app_mainclass=\"-jar $lib_dir/launcher-jar-test.launcher-jar-test-0.1.0-launcher.jar\"", + "bash should declare app_mainclass:\n" + bash) val jar = new java.util.jar.JarFile(dir / "lib" / "launcher-jar-test.launcher-jar-test-0.1.0-launcher.jar") val attributes = jar.getManifest().getMainAttributes() - assert(attributes.getValue("Class-Path").toString() contains "com.typesafe.akka.akka-actor") - assert(attributes.getValue("Main-Class").toString() contains "test.Test") + assert(attributes.getValue("Class-Path").toString() contains "com.typesafe.akka.akka-actor", + "MANIFEST Class-Path should contain akka-actor:\n" + attributes.getValue("Class-Path").toString()) + assert(attributes.getValue("Main-Class").toString() contains "test.Test", + "MANIFEST Main-Class should contain test.Test:\n" + attributes.getValue("Main-Class").toString()) jar close } diff --git a/src/sbt-test/universal/jvmopts-makeEtcDefault/build.sbt b/src/sbt-test/universal/jvmopts-makeEtcDefault/build.sbt new file mode 100644 index 000000000..367189dd9 --- /dev/null +++ b/src/sbt-test/universal/jvmopts-makeEtcDefault/build.sbt @@ -0,0 +1,18 @@ +enablePlugins(JavaServerAppPackaging) + +name := "simple-test" + +version := "0.1.0" + +javaOptions in Linux ++= Seq( + "-J-Xmx64m", "-J-Xms64m", "-Dproperty=true" +) + +TaskKey[Unit]("check") := { + val etc = (target in Universal).value / "tmp" / "etc" / "default" / name.value + val content = IO.read(etc) + val options = (javaOptions in Linux).value + options.foreach { opt => + assert(content.contains(opt), "Option [" + opt + "] is missing") + } +} diff --git a/src/sbt-test/universal/jvmopts-makeEtcDefault/project/plugins.sbt b/src/sbt-test/universal/jvmopts-makeEtcDefault/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/universal/jvmopts-makeEtcDefault/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/universal/jvmopts-makeEtcDefault/src/main/scala/Main.scala b/src/sbt-test/universal/jvmopts-makeEtcDefault/src/main/scala/Main.scala new file mode 100644 index 000000000..61471c658 --- /dev/null +++ b/src/sbt-test/universal/jvmopts-makeEtcDefault/src/main/scala/Main.scala @@ -0,0 +1,3 @@ +object Main extends App { + println("Hello world") +} diff --git a/src/sbt-test/universal/jvmopts-makeEtcDefault/test b/src/sbt-test/universal/jvmopts-makeEtcDefault/test new file mode 100644 index 000000000..23a7a0bb9 --- /dev/null +++ b/src/sbt-test/universal/jvmopts-makeEtcDefault/test @@ -0,0 +1,4 @@ +# Run the zip packaging. +> show makeEtcDefault +$ exists target/universal/tmp/etc/default/simple-test +> check \ No newline at end of file diff --git a/src/sphinx/archetypes/cheatsheet.rst b/src/sphinx/archetypes/cheatsheet.rst index c2d8d7c1b..dfe52d306 100644 --- a/src/sphinx/archetypes/cheatsheet.rst +++ b/src/sphinx/archetypes/cheatsheet.rst @@ -65,6 +65,90 @@ These settings configure the path behaviour Defaults to ``/var/log/``. Used to determine the log path for linux packages (rpm, debian). +JVM Options +=========== + +Jvm options can be added via different mechanisms. It depends on your use case which is most suitable. +The available options are + +- Adding via ``bashScriptExtraDefines`` and ``batScriptExtraDefines`` +- Providing a ``jvmopts`` (JavaApp) or ``etc-default`` (JavaServer) file +- Set ``javaOptions in Universal`` (JavaApp) or ``javaOptions in Linux`` (JavaServer, linux only) + +.. raw:: html + + + +Extra Defines +------------- + +With this approach you are altering the bash/bat script that gets executed. +Your configuration is literally woven into it, so it applies to any archetype +using this bashscript (app, akka app, server, ...). + +For a bash script this could look like this. + +.. code-block:: scala + + bashScriptExtraDefines += """addJava "-Dconfig.file=${app_home}/../conf/app.config"""" + + // or more. -X options don't need to be prefixed with -J + bashScriptExtraDefines ++= Seq( + "-Xms1024m", + "-Xmx2048m", + ) + +For information take a look at the :doc:` customize section for java apps ` + +File - jvmopts or etc-default +----------------------------------- + +Another approach would be to provide a file that is read by the bash script during execution. + +Java App +~~~~~~~~ + +Create a file ``src/universal/conf/jvmopts`` (gets automatically added to the package mappings) +and add this to your ``build.sbt`` inject the config location into the bashscript. + +.. code-block:: scala + + bashScriptConfigLocation := Some("${app_home}/../conf/jvmopts") + + +Java Server +~~~~~~~~~~~ + +See :ref:`server-app-config` + +Setting - javaOptions +--------------------- + +The last option to set your java options is using ``javaOptions in Universal`` (JavaApp and Server). +This will generate files according to your archetype. The following table gives you an overview what +you can use and how things will be behave if you mix different options. Options lower in the table +are more specific and will thus override the any previous settings (if allowed). + +======== ========= ======================== ========== ======== ======= +javaOpts Scope bashScriptConfigLocation Archetype mappings comment +======== ========= ======================== ========== ======== ======= +Nil Universal None JavaApp No jvm options +Nil Universal Some(jvmoptsLocation) JavaApp User provides the jvmopts file in ``src/universal/conf/jvmopts`` +opts Universal Some(_) JavaApp added creates ``jvmopts`` but leaves ``bashScriptConfigLocation`` unchanged +opts Universal None JavaApp added creates ``jvmopts`` and sets ``bashScriptConfigLocation``. If ``src/universal/conf/jvmopts`` is present it will be overridden +Nil Linux None JavaServer added creates ``etc-default`` and sets ``bashScriptConfigLocation`` +opts Linux None JavaServer added creates ``etc-default``, appends ``javaOptions in Linux`` and sets ``bashScriptConfigLocation`` +opts Linux Some(_) JavaServer added creates ``etc-default``, appends ``javaOptions in Linux`` and overrides ``bashScriptConfigLocation`` +======== ========= ======================== ========== ======== ======= + + Overriding Templates ==================== @@ -131,6 +215,8 @@ You can use ``${{variable_name}}`` to reference variables when writing your scri * ``app_classpath`` - The (ordered) classpath of the application. * ``daemon_user`` - The user that the server should run as. +.. _server-app-config: + Server App Config - ``src/templates/etc-default`` ------------------------------------------------- diff --git a/src/sphinx/archetypes/java_app/customize.rst b/src/sphinx/archetypes/java_app/customize.rst index c2fe8f892..94c354ff2 100644 --- a/src/sphinx/archetypes/java_app/customize.rst +++ b/src/sphinx/archetypes/java_app/customize.rst @@ -32,7 +32,7 @@ There are generally two types of configurations: * Configuring the Application itself. The native packager provides a direct hook into the generated scripts for JVM configuration. Let's make use of this. -First, add the following to the ``src/universal/conf/jvmopts`` file in the project :: +First, add the following to the ``src/universal/conf/application.ini`` file in the project :: -DsomeProperty=true @@ -44,16 +44,18 @@ Now, if we run the ``stage`` task, we'll see this file show up in the distributi conf/ lib/ $ ls target/universal/stage/conf - jvmopts + application.ini By default, any file in the ``src/universal`` directory is packaged. This is a convenient way to include things like licenses, and readmes. -Now, we need to modify the script templates to load this configuration. To do so, add the following +Now, we need to modify the script templates to load this configuration. The following is enabled by default, but you can change +it if you don't like ``application.ini`` as a name. To do so, add the following to ``build.sbt`` :: - bashScriptConfigLocation := Some("${app_home}/../conf/jvmopts") + bashScriptConfigLocation := Some("${app_home}/../conf/application.ini") -Here, we define the configuration location for the BASH script too look for the ``conf/jvmopts`` file. Now, let's run ``sbt stage`` and then execute the script in debug mode to see what command line it executes :: +Here, we define the configuration location for the BASH script too look for the ``conf/application.ini`` file. +Now, let's run ``sbt stage`` and then execute the script in debug mode to see what command line it executes :: ./target/universal/stage/bin/example-cli -d # Executing command line: @@ -116,7 +118,7 @@ The resulting structure is the following :: share/example-cli/ conf/ app.config - jvmopts + application.ini bin/ example-cli lib/ diff --git a/src/sphinx/archetypes/java_server/customize.rst b/src/sphinx/archetypes/java_server/customize.rst index 408361829..ae49204c7 100644 --- a/src/sphinx/archetypes/java_server/customize.rst +++ b/src/sphinx/archetypes/java_server/customize.rst @@ -18,7 +18,18 @@ As this file is OS dependent, each OS gets section. Linux Configuration ------------------- -Create ``src/templates/etc-default`` with the following template +You have three options. First, you can specify your options via the ``build.sbt``. + +.. code-block :: scala + + javaOptions in Linux ++= Seq( + "-J-Xmx64m", "-J-Xms64m", "-Dproperty=true", + ) + +For the ``-X`` settings you need to add a suffix ``-J`` so the start script will +recognize these as vm config parameters. + +The second option is to create ``src/templates/application.ini`` with the following template .. code-block :: bash @@ -34,18 +45,15 @@ Create ``src/templates/etc-default`` with the following template # ${{daemon_user}} daemon user # ------------------------------------------------- - # Setting -Xmx and -Xms in Megabyte - # -mem 1024 - # Setting -X directly (-J is stripped) # -J-X - # -J-Xmx1024 + -J-Xmx1024 # Add additional jvm parameters - # -Dkey=val + -Dkey=val # For play applications you may set - # -Dpidfile.path=/var/run/${{app_name}}/play.pid + -Dpidfile.path=/var/run/${{app_name}}/play.pid # Turn on JVM debugging, open at the given port # -jvm-debug @@ -58,8 +66,26 @@ Create ``src/templates/etc-default`` with the following template # using a reserved parameter. See #184 # -d -- -d -The file will be installed to ``/etc/default/`` and read from there -by the startscript. +The file will be installed to ``${app_home}/conf/application.ini`` and read from there +by the startscript. You can use ``#`` for comments and new lines as you like. The +available are listed in the template or you can display them via ``show linuxScriptReplacements``. + + +The last option is to provide a ``etc-default`` file placed in ``src/templates``, which currently +only works for **SystemV**. The file gets sourced before the actual startscript is executed. +The file will be installed to ``/etc/default/`` + +.. code-block :: bash + + # Setting JAVA_OPTS + # ----------------- + JAVA_OPTS="-Dpidfile.path=/var/run/${{app_name}}/play.pid $JAVA_OPTS" + + # export env vars for 3rd party libs + # ---------------------------------- + COMPANY_API_KEY=123abc + export COMPANY_API_KEY + Environment variables ~~~~~~~~~~~~~~~~~~~~~ diff --git a/test-project-simple/build.sbt b/test-project-simple/build.sbt index 1c6429a13..002dd646d 100644 --- a/test-project-simple/build.sbt +++ b/test-project-simple/build.sbt @@ -6,7 +6,7 @@ libraryDependencies ++= Seq( mainClass in Compile := Some("ExampleApp") -enablePlugins(JavaServerAppPackaging, JDebPackaging) +enablePlugins(JavaAppPackaging, JDebPackaging) maintainer := "Josh Suereth " packageSummary := "Minimal Native Packager" @@ -17,3 +17,10 @@ packageDescription := """A fun package description of our software, rpmVendor := "typesafe" rpmLicense := Some("BSD") rpmChangelogFile := Some("changelog.txt") + +// these settings are conflicting +javaOptions in Universal ++= Seq( + "-J-Xmx64m", "-J-Xms64m" +) + +//bashScriptConfigLocation := Some("${app_home}/../conf/jvmopts") \ No newline at end of file diff --git a/test-project-simple/src/main/scala/ExampleApp.scala b/test-project-simple/src/main/scala/ExampleApp.scala index afb108019..cf0548998 100644 --- a/test-project-simple/src/main/scala/ExampleApp.scala +++ b/test-project-simple/src/main/scala/ExampleApp.scala @@ -4,8 +4,11 @@ object ExampleApp extends App { val executorService = Executors newFixedThreadPool 2 + val memory = Runtime.getRuntime.maxMemory() / (1024L * 1024L) + println("Memory " + memory + "m") + while (true) { - for(i <- 0 to 5) executorService execute HelloWorld(i) + for (i <- 0 to 5) executorService execute HelloWorld(i) Thread sleep 5000 } diff --git a/test-project-simple/src/templates/etc-default b/test-project-simple/src/templates/etc-default.bak similarity index 100% rename from test-project-simple/src/templates/etc-default rename to test-project-simple/src/templates/etc-default.bak