Skip to content

Commit 9459728

Browse files
committed
Support main-class applications
Previously, there was no support for Java main() class applications. This change adds the functionality required to run these kinds of applications. In addition to the work required for that, the opportunity was taken to refactor the code base to be more composable. Specifically this includes the breaking out of JRE, container, and framework components which are dynamically loaded and called based on configuration. In addition to this, the documentation was dramatically overhauled to describe how to configure and extend the buildpack using the new structure. [#49693913]
1 parent 97f884e commit 9459728

68 files changed

Lines changed: 1734 additions & 1399 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 152 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,76 @@
33
[![Dependency Status](https://gemnasium.com/cloudfoundry/java-buildpack.png)](http://gemnasium.com/cloudfoundry/java-buildpack)
44
[![Code Climate](https://codeclimate.com/github/cloudfoundry/java-buildpack.png)](https://codeclimate.com/github/cloudfoundry/java-buildpack)
55

6-
`java-buildpack` is a [Cloud Foundry][cf] buildpack for running Java applications
6+
The `java-buildpack` is a [Cloud Foundry][cf] buildpack for running Java applications. It is designed to run most Java applications with no additional configuration, but supports configuration of the standard components, and extension to add custom components.
77

88
[cf]: http://www.cloudfoundry.com
99

10-
# Buildpack Users
11-
The buildpack allows you to configure the both the vendor and version of the Java runtime your application should use. To configure these, you can put a `system.properties` file into your pushed artifact.
10+
* [Usage](#usage) ([Configuration](#config))
11+
* [Design](#design)
12+
* [Standard Components](#standard-components)
13+
* [OpenJDK JRE](#openjdk) ([Configuration](#openjdk-config))
14+
* [Java Main Class Container](#javamain) ([Configuration](#javamain-config))
15+
* [Extending](#extending)
16+
* [JREs](#extending-jres)
17+
* [Containers](#extending-containers)
1218

13-
## `system.properties`
14-
If a `system.properties` file exists anywhere within your artifact's filesystem and the following properties have been set, they will be read and used to select the Java runtime for your application:
19+
---
1520

16-
| Name | Description
17-
| ---- | -----------
18-
| `java.runtime.vendor` | The vendor of the Java runtime to use. The legal values are defined by the keys in [`config/jres.yml`][jres_yml].
19-
| `java.runtime.version` | The version of the Java runtime to use. The legal values are defined by the keys in [`index.yml`][index_yml]
20-
| `java.runtime.heap.size.maximum` | The Java maximum heap size to use. For example, a value of `64m` will result in the java command line option `-Xmx64m`. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line appended to `-Xmx`.
21-
| `java.runtime.perm.gen.size.maximum` | The Java maximum PermGen size to use. For example, a value of `128m` will result in the java command line option `-XX:MaxPermSize=128m`. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line appended to `-XX:MaxPermSize=`.
22-
| `java.runtime.stack.size` | The Java stack size to use. For example, a value of `256k` will result in the java command line option `-Xss256k`. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line appended to `-Xss`.
23-
24-
An example `system.properties` file would to contain the following:
25-
```java
26-
java.runtime.vendor=openjdk
27-
java.runtime.version=1.7.0_21
21+
# Usage
22+
To use this buildpack specify the URI of the repository when pushing an application to Cloud Foundry.
23+
24+
```bash
25+
cf push --buildpack https://github.com/cloudfoundry/java-buildpack
2826
```
29-
## JRE Version Syntax and Ordering
27+
28+
<a name='config'></a>
29+
## Configuration and Extension
30+
The buildpack supports configuration and extension through the use of Git repository forking. The easiest way to accomplish this is to use [GitHub's forking functionality][fork] to create a copy of this repository. In that copy of the repository, make the required configuration and extension changes. Then when pushing a Cloud Foundry application, use the URL of the new repository. If the modifications are applicable to the Cloud Foundry community, please submit a [pull request][pull-request] with the changes.
31+
32+
[fork]: https://help.github.com/articles/fork-a-repo
33+
[pull-request]: https://help.github.com/articles/using-pull-requests
34+
35+
### `system.properties`
36+
Components are configured by setting key-value pairs in a `system.properties` file. The `system.properties` file can exist anywhere within the pushed artifact's file system.
37+
38+
# Design
39+
The buildpack is designed as a collection of components. These components are divided into three types; _JREs_, _Containers_, and _Frameworks_.
40+
41+
### JRE Components
42+
JRE components represent the JRE that will be used when running an application. This type of component is responsible for determining which JRE should be used, downloading and unpacking that JRE, and resolving any JRE-specific options that should be used at runtime.
43+
44+
Only a single JRE component can be used to run an application. If more than one JRE can be used, an error will be raised and application deployment will fail. In this case, the `java.runtime.vendor` property in `system.properties` must be set to a value that will cause a single JRE component to be used.
45+
46+
### Container Components
47+
Container components represent the way that an application will be run. Container types range from traditional application servers and servlet containers to simple Java `main()` method execution. This type of component is responsible for determining which container should be used, downloading and unpacking that container, and producing the command that will be executed by Cloud Foundry at runtime.
48+
49+
Only a single container component can run an application. If more than one container can be used, an error will be raised and application deployment will fail.
50+
51+
### Framework Components
52+
Framework components represent additional behavior or transformations used when an application is run. Framework types include the downloading of JDBC JARs for bound services and automatic reconfiguration of `DataSource`s in Spring configuration to match bound services. This type of component is responsible for determining which frameworks are required, transforming the application, and contributing any additional options that should be used at runtime.
53+
54+
Any number of framework components can be used when running an application.
55+
56+
# Standard Components
57+
The buildpack contributes a number of standard components that enable most Java applications to run.
58+
59+
<a name='openjdk'></a>
60+
## OpenJDK JRE
61+
**Criteria:** `java.runtime.vendor` set to `openjdk`
62+
63+
The OpenJDK JRE provides Java runtimes from the [OpenJDK][openjdk] project. Versions of Java from the 1.6, 1.7, and 1.8 lines are available. If the version to use is not configured in `system.properties`, the latest version from the `1.7.0` line is chosen.
64+
65+
[openjdk]: http://openjdk.java.net
66+
67+
### JRE Version Syntax and Ordering
3068
JREs versions are composed of major, minor, micro, and optional qualifier parts (`<major>.<minor>.<micro>[_<qualifier>]`). The major, minor, and micro parts must be numeric. The qualifier part is composed of letters, digits, and hyphens. The lexical ordering of the qualifier is:
3169

3270
1. hyphen
3371
2. lowercase letters
3472
3. uppercase letters
3573
4. digits
3674

37-
## JRE Version Wildcards
75+
### JRE Version Wildcards
3876
In addition to declaring a specific version of JRE to use, you can also specify a bounded range of JRES to use. Appending the `+` symbol to a version prefix chooses the latest JRE that begins with the prefix.
3977

4078
| Example | Description
@@ -43,51 +81,110 @@ In addition to declaring a specific version of JRE to use, you can also specify
4381
| `1.7.+` | Selects the greatest available version less than `1.8.0`.
4482
| `1.7.0_+` | Selects the greatest available version less than `1.7.1`. Use this syntax to stay up to date with the latest security releases in a particular version.
4583

46-
## Default JRE
47-
If the user does not specify a JRE vendor and version, a JRE is selected automatically. The selection algorithm is as follows:
84+
<a name='openjdk-config'></a>
85+
### Configuration
86+
The OpenJDK JRE allows the configuration of the version of Java to use as well as the allocation of memory at runtime.
4887

49-
1. If a single vendor is available, it is selected. If zero or more than one vendor is available, the buildpack will fail.
50-
2. The latest version of JRE for the selected vendor is chosen.
51-
52-
[jres_yml]: config/jres.yml
53-
[index_yml]: http://jres.gopivotal.com.s3.amazonaws.com/lucid/x86_64/openjdk/index.yml
88+
#### Version
5489

90+
| Name | Description
91+
| ---- | -----------
92+
| `java.runtime.version` | The version of Java runtime to use. This value can either be an explicit version as found in [this listing][index_yml] or by using wildcards.
5593

56-
# Buildpack Developers
57-
This buildpacks is designed to be extensible by other developers. To this end, various bits of configuration are exposed that make it simple to add functionality.
94+
[index_yml]: http://jres.gopivotal.com.s3.amazonaws.com/lucid/x86_64/openjdk/index.yml
5895

59-
## Adding JRES
60-
By default, this buildpack only allows users to choose from [OpenJDK][openjdk] JREs. To allow users to choose a JRE from other vendors, these vendors must be specified in [`config/jres.yml`][jres_yml]. The file is [YAML][yaml] formatted and in the simplest case is a mapping from a vendor name to a `String` repository root.
96+
#### Memory
6197

62-
```yaml
63-
<vendor name>: <JRE repository root URI>
64-
```
98+
| Name | Description
99+
| ---- | -----------
100+
| `java.heap.size` | The Java maximum heap size to use. For example, a value of `64m` will result in the java command line option `-Xmx64m`. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line appended to `-Xmx`.
101+
| `java.permgen.size` | The Java maximum PermGen size to use. For example, a value of `128m` will result in the java command line option `-XX:MaxPermSize=128m`. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line appended to `-XX:MaxPermSize=`.
102+
| `java.stack.size` | The Java stack size to use. For example, a value of `256k` will result in the java command line option `-Xss256k`. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line appended to `-Xss`.
65103

66-
When configured like this, if the user does not specify a version of the JRE to use, the latest possible version will be selected. If a particular JRE should use a default that is not the latest (e.g. using `1.7.0_21` instead of `1.8.0_M7`), the default version can be specified by using a `Hash` instead of a `String` as the value.
104+
<a name='javamain'></a>
105+
## Java Main Class Container
106+
**Criteria:** `Main-Class` attribute set in `META-INF/MANIFEST.MF` or `java.main.class` set
67107

68-
```yaml
69-
<vendor name>:
70-
default_version: <default version pattern>
71-
repository_root: <JRE repository root URI>
72-
```
108+
The Java Main Class Container allows applications that provide a class with a `main()` method in it to be run. These applications are run with a command that looks like `./java/bin/java -cp . com.gopivotal.SampleClass`.
73109

74-
The JRE repository root must contain a `/index.yml` file ([example][index_yml]). This file is also [YAML][yaml] formatted with the following syntax:
110+
<a name='javamain-config'></a>
111+
### Configuration
75112

76-
```yaml
77-
<JRE version>: <path relative to JRE repository root>
113+
| Name | Description
114+
| ---- | -----------
115+
| `java.main.class` | The Java class name to run. Values containing whitespace are rejected with an error, but all others values appear without modification on the java command line. If not specified, the Java Manifest value of `Main-Class` is used.
116+
117+
118+
# Extending
119+
The buildpack is designed to be extended by specifying components in [`config/components.yml`][components_yml]. The values listed in this file correspond to Ruby class names that will be instantiated and called. In order for these classes to be instantiated, the files containing them must be located in specific directories in the repository.
120+
121+
[components_yml]: config/components.yml
122+
123+
<a name='extending-jres'></a>
124+
## JREs
125+
To add a JRE, the class file must be located in [`lib/java_buildpack/jre`][jre_dir]. The class must have the following methods:
126+
127+
[jre_dir]: lib/java_buildpack/jre
128+
129+
```ruby
130+
# An initializer for the instance.
131+
#
132+
# @param [Hash<Symbol, String>] context A shared context provided to all components
133+
# @option context [String] :app_dir the directory that the application exists in
134+
# @option context [Array<String>] :java_opts an array that Java options can be added to
135+
# @option context [Hash] :system_properties the properties provided by the user
136+
def initialize(context = {})
137+
138+
# Determines if the JRE can be used to run the application.
139+
#
140+
# @return [String, nil] If the JRE can be used to run the application, a +String+ that uniquely identifies the JRE
141+
# (e.g. +jre-openjdk-1.7.0_21+). Otherwise, +nil+.
142+
def detect
143+
144+
# Downloads and unpacks the JRE. The JRE is expected to be unpacked such that +JAVA_HOME+ is +.java+. Status output
145+
# written to +STDOUT+ is expected as part of this invocation.
146+
#
147+
# @return [void]
148+
def compile
149+
150+
# Adds any JRE-specific options to +context[:java_opts]+. Typically this includes memory configuration (heap, perm gen,
151+
# etc.) but could be anything that a JRE needs to have configured.
152+
#
153+
# @return [void]
154+
def release
78155
```
79156

80-
The JRES uploaded to the repository must be gzipped TAR files and have no top-level directory ([example][example_jre]).
81-
82-
An example filesystem might look like:
83-
84-
```plain
85-
/index.yml
86-
/openjdk-1.6.0_27.tar.gz
87-
/openjdk-1.7.0_21.tar.gz
88-
/openjdk-1.8.0_M7.tar.gz
157+
<a name='extending-containers'></a>
158+
## Containers
159+
To add a container, the class file must be located in [`lib/java_buildpack/container`][container_dir]. The class must have the following methods
160+
161+
[container_dir]: lib/java_buildpack/container
162+
163+
```ruby
164+
# An initializer for the instance.
165+
#
166+
# @param [Hash<Symbol, String>] context A shared context provided to all components
167+
# @option context [String] :app_dir the directory that the application exists in
168+
# @option context [Array<String>] :java_opts an array that Java options can be added to
169+
# @option context [Hash] :system_properties the properties provided by the user
170+
def initialize(context = {})
171+
172+
# Determines if the container can be used to run the application.
173+
#
174+
# @return [String, nil] If the container can be used to run the application, a +String+ that uniquely identifies the
175+
# container (e.g. +tomcat-7.0.29+). Otherwise, +nil+.
176+
def detect
177+
178+
# Downloads and unpacks the container. The container is expected to transform the application in whatever way
179+
# necessary (e.g. moving files or creating symbolic links) to run it. Status output written to +STDOUT+ is expected as
180+
# part of this invocation.
181+
#
182+
# @return [void]
183+
def compile
184+
185+
# Creates the command to run the application with. The container is expected to read +context[:java_opts]+ and take
186+
# those values into account when creating the command.
187+
#
188+
# @return [String] the command to run the application with
189+
def release
89190
```
90-
91-
[openjdk]: http://openjdk.java.net
92-
[yaml]: http://www.yaml.org
93-
[example_jre]: http://jres.gopivotal.com.s3.amazonaws.com/lucid/x86_64/openjdk/openjdk-1.8.0_M7.tar.gz

bin/compile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
$stdout.sync = true
1818
$:.unshift File.expand_path("../../lib", __FILE__)
1919

20-
require 'java_buildpack/compile'
20+
require 'java_buildpack/buildpack'
2121

2222
build_dir = ARGV[0]
2323

2424
begin
25-
JavaBuildpack::Compile.new(build_dir).run
25+
JavaBuildpack::Buildpack.new(build_dir).compile
2626
rescue => e
2727
abort e.message
2828
end

bin/detect

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
$stdout.sync = true
1818
$:.unshift File.expand_path("../../lib", __FILE__)
1919

20-
require 'java_buildpack/detect'
20+
require 'java_buildpack/buildpack'
2121

2222
build_dir = ARGV[0]
2323

2424
begin
25-
components = JavaBuildpack::Detect.new(build_dir).run
25+
components = JavaBuildpack::Buildpack.new(build_dir).detect().compact
2626
unless components.empty?
2727
puts components
2828
else

bin/release

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
$stdout.sync = true
1818
$:.unshift File.expand_path("../../lib", __FILE__)
1919

20-
require 'java_buildpack/release'
20+
require 'java_buildpack/buildpack'
2121

2222
build_dir = ARGV[0]
2323

2424
begin
25-
puts JavaBuildpack::Release.new(build_dir).run
25+
puts JavaBuildpack::Buildpack.new(build_dir).release
2626
rescue => e
2727
abort e.message
2828
end

config/components.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Cloud Foundry Java Buildpack
2+
# Copyright (c) 2013 the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Configuration for components to use in the buildpack
17+
---
18+
containers:
19+
- "JavaBuildpack::Container::Main"
20+
jres:
21+
- "JavaBuildpack::Jre::OpenJdk"

lib/java_buildpack.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Cloud Foundry Java Buildpack
2+
# Copyright (c) 2013 the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# A module encapsulating all of the code for the Java buildpack
17+
module JavaBuildpack
18+
end

0 commit comments

Comments
 (0)