Skip to content

Commit

Permalink
distZip-style applications
Browse files Browse the repository at this point in the history
This change adds support for distZip-style applications[1].  This
means applications that are packaged as a start script in bin/ and
classpath JARs in lib/ can be run.  This package can be created with
the Gradle application plugin, but also by the sbt-native-package
plugin.

[1]: http://www.gradle.org/docs/current/userguide/application_plugin.html

[#69255612]
  • Loading branch information
nebhale committed Apr 10, 2014
1 parent 5fc79a0 commit 3d833c2
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .idea/.rakeTasks

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ To learn how to configure various properties of the buildpack, follow the "Confi
* [Design](docs/design.md)
* [Security](docs/security.md)
* Standard Containers
* [Dist ZIP](docs/container-dist_zip.md)
* [Groovy](docs/container-groovy.md) ([Configuration](docs/container-groovy.md#configuration))
* [Java Main](docs/container-java_main.md) ([Configuration](docs/container-java_main.md#configuration))
* [Play Framework](docs/container-play_framework.md)
Expand Down
1 change: 1 addition & 0 deletions config/components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Configuration for components to use in the buildpack
---
containers:
- "JavaBuildpack::Container::DistZip"
- "JavaBuildpack::Container::Groovy"
- "JavaBuildpack::Container::JavaMain"
- "JavaBuildpack::Container::PlayFramework"
Expand Down
24 changes: 24 additions & 0 deletions docs/container-dist_zip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Dist Zip Container
The Dist Zip Container allows applications packaged in [`distZip`-style][] to be run.

<table>
<tr>
<td><strong>Detection Criteria</strong></td>
<td><ul>
<li>A start script in the <tt>bin/</tt> subdirectory of the application directory or one of its immediate subdirectories (but not in both), and</li>
<li>A JAR file in the <tt>lib/</tt> subdirectory of the application directory or one of its immediate subdirectories (but not in both), and</li>
<li>Not a Play Framework application</li>
</ul></td>
</tr>
<tr>
<td><strong>Tags</strong></td>
<td><tt>dist-zip</tt></td>
</tr>
</table>
Tags are printed to standard output by the buildpack detect script

## Configuration
The Dist Zip Container cannot be configured.


[`distZip`-style]: http://www.gradle.org/docs/current/userguide/application_plugin.html
3 changes: 2 additions & 1 deletion docs/container-play_framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ The Play Framework Container allows Play Framework applications to be run.

<table>
<tr>
<td><strong>Detection Criteria</strong></td><td>The Play Framework start script and the Play Framework runtime JAR exist in the appropriate subdirectories of the application directory or one of its immediate subdirectories (but not in both)</td>
<td><strong>Detection Criteria</strong></td>
<td>The Play Framework start script and the Play Framework runtime JAR exist in the appropriate subdirectories of the application directory or one of its immediate subdirectories (but not in both)</td>
</tr>
<tr>
<td><strong>Tags</strong></td>
Expand Down
116 changes: 116 additions & 0 deletions lib/java_buildpack/container/dist_zip.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013 the original author or authors.
#
# 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.

require 'java_buildpack/component/base_component'
require 'java_buildpack/container'
require 'java_buildpack/util/dash_case'
require 'java_buildpack/util/find_single_directory'
require 'java_buildpack/util/play/factory'
require 'java_buildpack/util/qualify_path'
require 'java_buildpack/util/start_script'

module JavaBuildpack
module Container

# Encapsulates the detect, compile, and release functionality for +distZip+ style applications.
class DistZip < JavaBuildpack::Component::BaseComponent
include JavaBuildpack::Util

# Creates an instance
#
# @param [Hash] context a collection of utilities used the component
def initialize(context)
super(context)
end

# (see JavaBuildpack::Component::BaseComponent#detect)
def detect
supports? ? DistZip.to_s.dash_case : nil
end

# (see JavaBuildpack::Component::BaseComponent#compile)
def compile
start_script.chmod 0755
augment_classpath
end

# (see JavaBuildpack::Component::BaseComponent#release)
def release
[
@droplet.java_home.as_env_var,
@droplet.java_opts.as_env_var,
'SERVER_PORT=$PORT',
qualify_path(start_script, @droplet.root)
].flatten.compact.join(' ')
end

private

PATTERN_APP_CLASSPATH = /^declare -r app_classpath=\"(.*)\"$/

PATTERN_CLASSPATH = /^CLASSPATH=(.*)$/.freeze

def augment_classpath
content = start_script.read

if content =~ PATTERN_CLASSPATH
additional_classpath = @droplet.additional_libraries.sort.map do |additional_library|
"$APP_HOME/#{additional_library.relative_path_from(root)}"
end

update_file start_script, content,
PATTERN_CLASSPATH, "CLASSPATH=#{additional_classpath.join(':')}:\\1"
elsif content =~ PATTERN_APP_CLASSPATH
additional_classpath = @droplet.additional_libraries.sort.map do |additional_library|
"$app_home/#{additional_library.relative_path_from(start_script.dirname)}"
end

update_file start_script, content,
PATTERN_APP_CLASSPATH, "declare -r app_classpath=\"#{additional_classpath.join(':')}:\\1\""
end
end

def jars?
(lib_dir + '*.jar').glob.any?
end

def lib_dir
root + 'lib'
end

def root
find_single_directory || @droplet.root
end

def start_script
JavaBuildpack::Util.start_script root
end

def supports?
start_script && start_script.exist? && jars? && !JavaBuildpack::Util::Play::Factory.create(@droplet)
end

def update_file(path, content, pattern, replacement)
path.open('w') do |f|
f.write content.gsub pattern, replacement
f.fsync
end
end

end

end
end
6 changes: 3 additions & 3 deletions lib/java_buildpack/container/java_main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ def release
manifest_class_path.each { |path| @droplet.additional_libraries << path }

[
port,
"#{@droplet.java_home.root}/bin/java",
@droplet.additional_libraries.as_classpath,
@droplet.java_opts.join(' '),
main_class,
arguments,
port
arguments
].flatten.compact.join(' ')
end

Expand All @@ -73,7 +73,7 @@ def manifest_class_path
end

def port
main_class =~ /^org\.springframework\.boot\.loader\.(?:[JW]ar|Properties)Launcher$/ ? '--server.port=$PORT' : nil
main_class =~ /^org\.springframework\.boot\.loader\.(?:[JW]ar|Properties)Launcher$/ ? 'SERVER_PORT=$PORT' : nil
end

end
Expand Down
5 changes: 2 additions & 3 deletions lib/java_buildpack/container/spring_boot_cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@ def release
[
@droplet.java_home.as_env_var,
@droplet.java_opts.as_env_var,
'SERVER_PORT=$PORT',
qualify_path(@droplet.sandbox + 'bin/spring', @droplet.root),
'run',
relative_groovy_files,
'--',
'--server.port=$PORT'
relative_groovy_files
].flatten.compact.join(' ')
end

Expand Down
33 changes: 33 additions & 0 deletions lib/java_buildpack/util/find_single_directory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013 the original author or authors.
#
# 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.

require 'java_buildpack/util'

module JavaBuildpack
module Util

# Find the single directory in the root of the droplet
#
# @return [Pathname, nil] the single directory in the root of the droplet, otherwise +nil+
def find_single_directory
roots = (@droplet.root + '*').glob.select { |child| child.directory? }
roots.size == 1 ? roots.first : nil
end

module_function :find_single_directory

end
end
9 changes: 1 addition & 8 deletions lib/java_buildpack/util/play/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

require 'java_buildpack/util/play'
require 'java_buildpack/util/find_single_directory'
require 'java_buildpack/util/qualify_path'

module JavaBuildpack
Expand Down Expand Up @@ -80,14 +81,6 @@ def augment_classpath
fail "Method 'augment_classpath' must be defined"
end

# Find the single directory in the root of the droplet
#
# @return [Pathname, nil] the single directory in the root of the droplet, otherwise +nil+
def find_single_directory
roots = (@droplet.root + '*').glob.select { |child| child.directory? }
roots.size == 1 ? roots.first : nil
end

# Returns the +JAVA_OPTS+ in the form that they need to be added to the command line
#
# @return [Array<String>] the +JAVA_OPTS+ in the form that they need to be added to the command line
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CLASSPATH=$APP_HOME/lib/dist-zip-application-1.0.0.BUILD-SNAPSHOT.jar:$APP_HOME/lib/h2-1.3.174.jar:$APP_HOME/lib/spring-boot-starter-jdbc-1.0.0.RELEASE.jar:$APP_HOME/lib/spring-boot-starter-web-1.0.0.RELEASE.jar:$APP_HOME/lib/spring-common-1.0.0.BUILD-SNAPSHOT.jar:$APP_HOME/lib/mysql-connector-java-5.1.28.jar:$APP_HOME/lib/spring-boot-starter-1.0.0.RELEASE.jar:$APP_HOME/lib/spring-jdbc-4.0.3.RELEASE.jar:$APP_HOME/lib/tomcat-jdbc-7.0.52.jar:$APP_HOME/lib/spring-tx-4.0.3.RELEASE.jar:$APP_HOME/lib/spring-boot-starter-tomcat-1.0.0.RELEASE.jar:$APP_HOME/lib/jackson-databind-2.3.2.jar:$APP_HOME/lib/spring-web-4.0.3.RELEASE.jar:$APP_HOME/lib/spring-webmvc-4.0.3.RELEASE.jar:$APP_HOME/lib/core-1.0.0.BUILD-SNAPSHOT.jar:$APP_HOME/lib/spring-boot-1.0.0.RELEASE.jar:$APP_HOME/lib/spring-boot-autoconfigure-1.0.0.RELEASE.jar:$APP_HOME/lib/spring-boot-starter-logging-1.0.0.RELEASE.jar:$APP_HOME/lib/snakeyaml-1.13.jar:$APP_HOME/lib/tomcat-juli-7.0.52.jar:$APP_HOME/lib/spring-beans-4.0.3.RELEASE.jar:$APP_HOME/lib/spring-core-4.0.3.RELEASE.jar:$APP_HOME/lib/tomcat-embed-core-7.0.52.jar:$APP_HOME/lib/tomcat-embed-el-7.0.52.jar:$APP_HOME/lib/tomcat-embed-logging-juli-7.0.52.jar:$APP_HOME/lib/jackson-annotations-2.3.0.jar:$APP_HOME/lib/jackson-core-2.3.2.jar:$APP_HOME/lib/spring-data-redis-1.1.1.RELEASE.jar:$APP_HOME/lib/spring-data-mongodb-1.4.0.RELEASE.jar:$APP_HOME/lib/spring-rabbit-1.2.1.RELEASE.jar:$APP_HOME/lib/jedis-2.1.0.jar:$APP_HOME/lib/jcl-over-slf4j-1.7.6.jar:$APP_HOME/lib/jul-to-slf4j-1.7.6.jar:$APP_HOME/lib/log4j-over-slf4j-1.7.6.jar:$APP_HOME/lib/logback-classic-1.1.1.jar:$APP_HOME/lib/spring-context-support-3.1.4.RELEASE.jar:$APP_HOME/lib/spring-data-commons-1.7.0.RELEASE.jar:$APP_HOME/lib/mongo-java-driver-2.11.4.jar:$APP_HOME/lib/amqp-client-3.1.3.jar:$APP_HOME/lib/spring-amqp-1.2.1.RELEASE.jar:$APP_HOME/lib/commons-pool-1.5.5.jar:$APP_HOME/lib/logback-core-1.1.1.jar:$APP_HOME/lib/commons-logging-1.1.3.jar:$APP_HOME/lib/spring-context-4.0.3.RELEASE.jar:$APP_HOME/lib/slf4j-api-1.7.6.jar:$APP_HOME/lib/spring-expression-4.0.3.RELEASE.jar:$APP_HOME/lib/spring-aop-4.0.3.RELEASE.jar:$APP_HOME/lib/aopalliance-1.0.jar
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare -r app_classpath="$lib_dir/com.wmg.eventprocessor-0.0.1-SNAPSHOT.jar:$lib_dir/org.scala-lang.scala-library-2.10.3.jar:$lib_dir/com.typesafe.akka.akka-actor_2.10-2.2.3.jar:$lib_dir/com.typesafe.config-1.0.2.jar:$lib_dir/com.rabbitmq.amqp-client-3.2.3.jar:$lib_dir/org.xerial.snappy.snappy-java-1.0.5.jar:$lib_dir/com.datastax.cassandra.cassandra-driver-core-1.0.0.jar:$lib_dir/io.netty.netty-3.6.3.Final.jar:$lib_dir/org.apache.cassandra.cassandra-thrift-1.2.3.jar:$lib_dir/commons-lang.commons-lang-2.4.jar:$lib_dir/org.slf4j.slf4j-api-1.7.2.jar:$lib_dir/org.apache.thrift.libthrift-0.7.0.jar:$lib_dir/javax.servlet.servlet-api-2.5.jar:$lib_dir/org.apache.httpcomponents.httpclient-4.0.1.jar:$lib_dir/org.apache.httpcomponents.httpcore-4.0.1.jar:$lib_dir/commons-logging.commons-logging-1.1.1.jar:$lib_dir/commons-codec.commons-codec-1.2.jar:$lib_dir/org.codehaus.jackson.jackson-core-asl-1.9.2.jar:$lib_dir/org.apache.cassandra.cassandra-all-1.2.3.jar:$lib_dir/net.jpountz.lz4.lz4-1.1.0.jar:$lib_dir/com.ning.compress-lzf-0.8.4.jar:$lib_dir/commons-cli.commons-cli-1.1.jar:$lib_dir/com.googlecode.concurrentlinkedhashmap.concurrentlinkedhashmap-lru-1.3.jar:$lib_dir/org.antlr.antlr-3.2.jar:$lib_dir/org.antlr.antlr-runtime-3.2.jar:$lib_dir/org.antlr.stringtemplate-3.2.1.jar:$lib_dir/antlr.antlr-2.7.7.jar:$lib_dir/org.apache.cassandra.deps.avro-1.4.0-cassandra-1.jar:$lib_dir/org.codehaus.jackson.jackson-mapper-asl-1.9.2.jar:$lib_dir/org.mortbay.jetty.jetty-6.1.22.jar:$lib_dir/org.mortbay.jetty.jetty-util-6.1.22.jar:$lib_dir/org.mortbay.jetty.servlet-api-2.5-20081211.jar:$lib_dir/jline.jline-1.0.jar:$lib_dir/com.googlecode.json-simple.json-simple-1.1.jar:$lib_dir/com.github.stephenc.high-scale-lib.high-scale-lib-1.1.2.jar:$lib_dir/org.yaml.snakeyaml-1.6.jar:$lib_dir/edu.stanford.ppl.snaptree-0.1.jar:$lib_dir/org.mindrot.jbcrypt-0.3m.jar:$lib_dir/com.yammer.metrics.metrics-core-2.0.3.jar:$lib_dir/log4j.log4j-1.2.16.jar:$lib_dir/com.github.stephenc.jamm-0.2.5.jar:$lib_dir/org.slf4j.slf4j-log4j12-1.7.2.jar:$lib_dir/com.fasterxml.jackson.module.jackson-module-scala_2.10-2.3.2.jar:$lib_dir/com.fasterxml.jackson.core.jackson-core-2.3.2.jar:$lib_dir/com.fasterxml.jackson.core.jackson-annotations-2.3.2.jar:$lib_dir/com.fasterxml.jackson.core.jackson-databind-2.3.2.jar:$lib_dir/com.thoughtworks.paranamer.paranamer-2.6.jar:$lib_dir/com.google.code.findbugs.jsr305-2.0.1.jar:$lib_dir/com.google.guava.guava-13.0.1.jar"
Empty file.
71 changes: 71 additions & 0 deletions spec/java_buildpack/container/dist_zip_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013 the original author or authors.
#
# 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.

require 'spec_helper'
require 'component_helper'
require 'java_buildpack/container/dist_zip'

describe JavaBuildpack::Container::DistZip do
include_context 'component_helper'

it 'should not recognize non-applications' do
expect(component.detect).not_to be
end

it 'should not recognize Play dist applications',
app_fixture: 'container_play_2.2_dist' do

expect(component.detect).not_to be
end

it 'should not recognize Play dist applications',
app_fixture: 'container_play_2.2_staged' do

expect(component.detect).not_to be
end

it 'should recognize distZip applications',
app_fixture: 'container_dist_zip' do

expect(component.detect).to eq('dist-zip')
end

it 'should correctly extend the CLASSPATH-style classpath',
app_fixture: 'container_dist_zip' do

component.compile

expect((app_dir + 'dist-zip-application/bin/dist-zip-application').read)
.to match 'CLASSPATH=\$APP_HOME/../.additional_libs/test-jar-1.jar:\$APP_HOME/../.additional_libs/test-jar-2.jar:'
end

it 'should correctly extend the app_classpath-style classpath',
app_fixture: 'container_dist_zip_app_classpath' do

component.compile

expect((app_dir + 'dist-zip-application/bin/dist-zip-application').read)
.to match 'declare -r app_classpath="\$app_home/../../.additional_libs/test-jar-1.jar:\$app_home/../../.additional_libs/test-jar-2.jar:'
end

it 'should return command',
app_fixture: 'container_dist_zip' do

expect(component.release).to eq("#{java_home.as_env_var} JAVA_OPTS=\"test-opt-2 test-opt-1\" SERVER_PORT=$PORT " \
'$PWD/dist-zip-application/bin/dist-zip-application')
end

end
Loading

0 comments on commit 3d833c2

Please sign in to comment.