Skip to content

Commit

Permalink
Merge pull request #25 from uschi2000/feature/windows-classpath
Browse files Browse the repository at this point in the history
Windows start scripts use pathing jar instead of explicit classpath
  • Loading branch information
markelliot committed Nov 10, 2015
2 parents 66b02cc + c79ec68 commit 0cca9ac
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 8 deletions.
13 changes: 9 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ content of the package. The package will follow this structure:

[service-name]-[service-version]/
deployment/
manifest.yaml # simple package manifest
manifest.yaml # simple package manifest
service/
bin/
[service-name] # start script
init.sh # daemonizing script
[service-name] # start script
[service-name.bat] # Windows start script
init.sh # daemonizing script
lib/
[jars]
var/
Expand All @@ -41,13 +42,17 @@ program for a default run configuration:
mainClass 'com.palantir.foo.bar.MyServiceMainClass'
args 'server', 'var/conf/my-service.yml'
}

The `distribution` block offers the following options:

* `serviceName` the name of this service, used to construct the final artifact's file name.
* `mainClass` class containing the entry point to start the program.
* (optional) `args` a list of arguments to supply when running `start`.
* (optional) `defaultJvmOpts` a list of default JVM options to set on the program.
* (optional) `enableManifestClasspath` a boolean flag; if set to true, then the explicit Java
classpath is omitted from the generated Windows start script and instead infered
from a JAR file whose MANIFEST contains the classpath entries


Packaging
---------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ class DistTarTask extends Tar {
from(project.configurations.runtime)
}

if (ext.isEnableManifestClasspath()) {
into("${archiveRootDir}/service/lib") {
from(project.tasks.getByName("manifestClasspathJar"))
}
}

into("${archiveRootDir}/service/bin") {
from("${project.buildDir}/scripts")
fileMode = 0755
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class DistributionExtension {
private String mainClass
private List<String> args = []
private List<String> defaultJvmOpts = []
private boolean enableManifestClasspath = false

public void serviceName(String serviceName) {
this.serviceName = serviceName
Expand All @@ -38,6 +39,10 @@ class DistributionExtension {
this.defaultJvmOpts = Arrays.asList(defaultJvmOpts)
}

public void enableManifestClasspath(boolean enableManifestClasspath) {
this.enableManifestClasspath = enableManifestClasspath
}

public String getServiceName() {
return serviceName;
}
Expand All @@ -54,4 +59,8 @@ class DistributionExtension {
return defaultJvmOpts;
}

public boolean isEnableManifestClasspath() {
return enableManifestClasspath;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
*/
package com.palantir.gradle.javadist

import java.nio.file.Paths

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task

import java.nio.file.Paths

class JavaDistributionPlugin implements Plugin<Project> {

void apply(Project project) {
Expand All @@ -29,9 +29,32 @@ class JavaDistributionPlugin implements Plugin<Project> {

DistributionExtension ext = project.extensions.create('distribution', DistributionExtension)

// Specify classpath using pathing jar rather than command line argument on Windows, since
// Windows path sizes are limited.
ManifestClasspathJarTask manifestClasspathJar =
project.tasks.create("manifestClasspathJar", ManifestClasspathJarTask)
manifestClasspathJar.setOnlyIf {
ext.isEnableManifestClasspath()
}

Task startScripts = project.tasks.create('createStartScripts', DistributionCreateStartScriptsTask, {
description = "Generates standard Java start scripts."
})
}) << {
if (ext.isEnableManifestClasspath()) {
// Replace standard classpath with pathing jar in order to circumnavigate length limits:
// https://issues.gradle.org/browse/GRADLE-2992
def winScriptFile = project.file getWindowsScript()
def winFileText = winScriptFile.text

// Remove too-long-classpath and use pathing jar instead
winFileText = winFileText.replaceAll('set CLASSPATH=.*', 'rem CLASSPATH declaration removed.')
winFileText = winFileText.replaceAll(
'("%JAVA_EXE%" .* -classpath ")%CLASSPATH%(" .*)',
'$1%APP_HOME%\\\\lib\\\\' + manifestClasspathJar.archiveName + '$2')

winScriptFile.text = winFileText
}
}

Task initScript = project.tasks.create('createInitScript', {
description = "Generates daemonizing init.sh script."
Expand Down Expand Up @@ -59,14 +82,15 @@ class JavaDistributionPlugin implements Plugin<Project> {

DistTarTask distTar = project.tasks.create('distTar', DistTarTask, {
description = "Creates a compressed, gzipped tar file that contains required runtime resources."
dependsOn startScripts, initScript, manifest
dependsOn startScripts, initScript, manifest, manifestClasspathJar
})

RunTask run = project.tasks.create('run', RunTask, {
description = "Runs the specified project using configured mainClass and with default args."
})

project.afterEvaluate {
manifestClasspathJar.configure(ext)
startScripts.configure(ext)
distTar.configure(ext)
run.configure(ext)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2015 Palantir Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* <http://www.apache.org/licenses/LICENSE-2.0>
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palantir.gradle.javadist

import org.gradle.api.tasks.bundling.Jar

/**
* Produces a JAR whose manifest's {@code Class-Path} entry lists exactly the JARs produced by the project's runtime
* configuration.
*/
class ManifestClasspathJarTask extends Jar {

public ManifestClasspathJarTask() {
appendix = 'manifest-classpath'
}

public void configure(DistributionExtension ext) {
manifest.attributes("Class-Path": project.files(project.configurations.runtime)
.collect { it.getName() }
.join(' ') + ' ' + project.tasks.jar.archiveName
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,51 @@ class JavaDistributionPluginTests extends Specification {
startScript.contains('DEFAULT_JVM_OPTS=\'"-Xmx4M" "-Djavax.net.ssl.trustStore=truststore.jks"\'')
}

def 'produces manifest-classpath jar and windows start script with no classpath length limitations' () {
given:
createUntarBuildFile(buildFile)
buildFile << '''
distribution {
enableManifestClasspath true
}
'''.stripIndent()

when:
BuildResult buildResult = run('build', 'distTar', 'untar').build()

then:
buildResult.task(':build').outcome == TaskOutcome.SUCCESS
buildResult.task(':distTar').outcome == TaskOutcome.SUCCESS
buildResult.task(':untar').outcome == TaskOutcome.SUCCESS

new File(projectDir, 'dist/service-name-0.1/service/bin/service-name.bat').exists()
String startScript = readFully('dist/service-name-0.1/service/bin/service-name.bat')
startScript.contains("-manifest-classpath-0.1.jar")
!startScript.contains("-classpath \"%CLASSPATH%\"")
new File(projectDir, 'dist/service-name-0.1/service/lib/').listFiles()
.find({it.name.endsWith("-manifest-classpath-0.1.jar")})
}

def 'does not produce manifest-classpath jar when disabled in extension'() {
given:
createUntarBuildFile(buildFile)

when:
BuildResult buildResult = run('build', 'distTar', 'untar').build()

then:
buildResult.task(':build').outcome == TaskOutcome.SUCCESS
buildResult.task(':distTar').outcome == TaskOutcome.SUCCESS
buildResult.task(':untar').outcome == TaskOutcome.SUCCESS

new File(projectDir, 'dist/service-name-0.1/service/bin/service-name.bat').exists()
String startScript = readFully('dist/service-name-0.1/service/bin/service-name.bat')
!startScript.contains("-manifest-classpath-0.1.jar")
startScript.contains("-classpath \"%CLASSPATH%\"")
!new File(projectDir, 'dist/service-name-0.1/service/lib/').listFiles()
.find({it.name.endsWith("-manifest-classpath-0.1.jar")})
}

private def createUntarBuildFile(buildFile) {
buildFile << '''
plugins {
Expand Down

0 comments on commit 0cca9ac

Please sign in to comment.