Skip to content

Commit 45f2016

Browse files
committed
Custom JAVA_OPTS
Previously, the user had no way to specify custom JAVA_OPTS to be used when the application was running. This change adds the ability for the user to specify java.opts in system.properties and to have the value added to the JAVA_OPTS contributed by the buildpack components. [#49694697]
1 parent ff511e2 commit 45f2016

10 files changed

Lines changed: 283 additions & 59 deletions

File tree

README.md

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ The `java-buildpack` is a [Cloud Foundry][cf] buildpack for running Java applica
1212
* [Standard Components](#standard-components)
1313
* [OpenJDK JRE](#openjdk) ([Configuration](#openjdk-config))
1414
* [Java Main Class Container](#javamain) ([Configuration](#javamain-config))
15+
* [`JAVA_OPTS` Framework](#javaopts) ([Configuration](#javaopts-config))
1516
* [Extending](#extending)
1617
* [JREs](#extending-jres)
1718
* [Containers](#extending-containers)
19+
* [Frameworks](#extending-framework)
1820

1921
---
2022

@@ -58,7 +60,10 @@ The buildpack contributes a number of standard components that enable most Java
5860

5961
<a name='openjdk'></a>
6062
## OpenJDK JRE
61-
**Criteria:** `java.runtime.vendor` set to `openjdk`
63+
| | |
64+
| - | -
65+
| **Detection Criteria** | `java.runtime.vendor` set to `openjdk`
66+
| **Detection Tags** | `jre-openjdk-<version>`
6267

6368
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.
6469

@@ -106,11 +111,20 @@ The OpenJDK JRE allows the configuration of the version of Java to use as well a
106111

107112
If some memory sizes are not specified using the above properties, default values are provided. For maximum heap, Metaspace, or PermGen size, the default value is based on a proportion of the total memory specified when the application was pushed. For stack size, the default value is one megabyte.
108113

109-
If any memory sizes are specified which are not equal to the default value, the proportionate defaults are adjusted accordingly. The default stack size is never adjusted from the default value.
114+
If any memory sizes are specified which are not equal to the default value, the proportionate defaults are adjusted accordingly. The default stack size is never adjusted from the default value.
115+
116+
#### OpenJDK Memory Heuristics
117+
118+
The calculation of default memory sizes for OpenJDK is configured via YAML files in the buildpack's `config` directory.
119+
120+
The configuration contains a weighting between `0` and `1` corresponding to a proportion of the total memory specified when the application was pushed. The weightings should add up to `1`.
110121

111122
<a name='javamain'></a>
112123
## Java Main Class Container
113-
**Criteria:** `Main-Class` attribute set in `META-INF/MANIFEST.MF` or `java.main.class` set
124+
| | |
125+
| - | -
126+
| **Detection Criteria** | `Main-Class` attribute set in `META-INF/MANIFEST.MF` or `java.main.class` set
127+
| **Detection Tags** | `java-main`
114128

115129
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`.
116130

@@ -119,7 +133,25 @@ The Java Main Class Container allows applications that provide a class with a `m
119133

120134
| Name | Description
121135
| ---- | -----------
122-
| `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.
136+
| `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.
137+
138+
<a name="javaopts"></a>
139+
## `JAVA_OPTS` Framework
140+
| | |
141+
| - | -
142+
| **Detection Criteria** | `java.opts` set
143+
| **Detection Tags** | `java-opts`
144+
145+
The `JAVA_OPTS` Framework contributes arbitrary Java options to the application at runtime.
146+
147+
<a name="javaopts"></a>
148+
### Configuration
149+
150+
| Name | Description
151+
| ---- | -----------
152+
| `java.opts` | The Java options to use when running the application. All values are used without modification when invoking the JVM.
153+
154+
123155

124156

125157
# Extending
@@ -144,8 +176,8 @@ def initialize(context = {})
144176

145177
# Determines if the JRE can be used to run the application.
146178
#
147-
# @return [String, nil] If the JRE can be used to run the application, a +String+ that uniquely identifies the JRE
148-
# (e.g. +jre-openjdk-1.7.0_21+). Otherwise, +nil+.
179+
# @return [String, nil] If the JRE can be used to run the application, a +String+ that uniquely identifies the JRE
180+
# (e.g. +jre-openjdk-1.7.0_21+). Otherwise, +nil+.
149181
def detect
150182

151183
# Downloads and unpacks the JRE. The JRE is expected to be unpacked such that +JAVA_HOME+ is +.java+. Status output
@@ -178,8 +210,8 @@ def initialize(context = {})
178210

179211
# Determines if the container can be used to run the application.
180212
#
181-
# @return [String, nil] If the container can be used to run the application, a +String+ that uniquely identifies the
182-
# container (e.g. +tomcat-7.0.29+). Otherwise, +nil+.
213+
# @return [String, nil] If the container can be used to run the application, a +String+ that uniquely identifies the
214+
# container (e.g. +tomcat-7.0.29+). Otherwise, +nil+.
183215
def detect
184216

185217
# Downloads and unpacks the container. The container is expected to transform the application in whatever way
@@ -196,9 +228,35 @@ def compile
196228
def release
197229
```
198230

199-
## OpenJDK Memory Heuristics
231+
<a name='extending-frameworks'></a>
232+
## Frameworks
233+
To add a framework, the class file must be located in [`lib/java_buildpack/framework`][framework_dir]. The class must have the following methods:
200234

201-
The calculation of default memory sizes for OpenJDK is configured via YAML files in the buildpack's `config` directory.
235+
[framework_dir]: lib/java_buildpack/framework
236+
237+
```ruby
238+
# An initializer for the instance.
239+
#
240+
# @param [Hash<Symbol, String>] context A shared context provided to all components
241+
# @option context [String] :app_dir the directory that the application exists in
242+
# @option context [Array<String>] :java_opts an array that Java options can be added to
243+
# @option context [Hash] :system_properties the properties provided by the user
244+
def initialize(context = {})
245+
246+
# Determines if the framework can be applied to the application
247+
#
248+
# @return [String, nil] If the framework can be used to run the application, a +String+ that uniquely identifies the
249+
# framework (e.g. +java-opts+). Otherwise, +nil+.
250+
def detect
202251

203-
The configuration contains a weighting between 0 and 1 corresponding to a proportion of the total memory specified
204-
when the application was pushed. The weightings should add up to 1.
252+
# Transforms the application based on the framework. Status output written to +STDOUT+ is expected as part of this invocation.
253+
#
254+
# @return [void]
255+
def compile
256+
257+
# Adds any framework-specific options to +context[:java_opts]+. Typically this includes any JRE configuration required
258+
# by the framework, but could be anything that a framework needs to have configured.
259+
#
260+
# @return [void]
261+
def release
262+
```

config/components.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ containers:
1919
- "JavaBuildpack::Container::Main"
2020
jres:
2121
- "JavaBuildpack::Jre::OpenJdk"
22+
frameworks:
23+
- "JavaBuildpack::Framework::JavaOpts"

lib/java_buildpack/buildpack.rb

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,18 @@ def initialize(app_dir)
3939
Buildpack.require_component_files
4040
components = Buildpack.components
4141

42+
@jres = components['jres'].map do |jre|
43+
jre.constantize.new(context)
44+
end
45+
46+
@frameworks = components['frameworks'].map do |framework|
47+
framework.constantize.new(context)
48+
end
49+
4250
@containers = components['containers'].map do |container|
4351
container.constantize.new(context)
4452
end
4553

46-
@jres = components['jres'].map do |jre|
47-
jre.constantize.new(context)
48-
end
4954
end
5055

5156
# Iterates over all of the components to detect if this buildpack can be used to run an application
@@ -57,17 +62,20 @@ def detect
5762
jre_detections = @jres.map { |jre| jre.detect }.compact
5863
raise "Application can be run useing more than one JRE: #{jre_detections.join(', ')}" if jre_detections.size > 1
5964

65+
framework_detections = @frameworks.map { |framework| framework.detect }.compact
66+
6067
container_detections = @containers.map { |container| container.detect }.compact
6168
raise "Application can be run by more than one container: #{container_detections.join(', ')}" if container_detections.size > 1
6269

63-
container_detections.empty? ? [] : jre_detections.concat(container_detections).flatten.compact
70+
container_detections.empty? ? [] : jre_detections.concat(framework_detections).concat(container_detections).flatten.compact
6471
end
6572

6673
# Transforms the application directory such that the JRE, container, and frameworks can run the application
6774
#
6875
# @return [void]
6976
def compile
7077
jre.compile
78+
frameworks.each { |framework| framework.compile }
7179
container.compile
7280
end
7381

@@ -77,6 +85,7 @@ def compile
7785
# @return [String] The payload required to run the application.
7886
def release
7987
jre.release
88+
frameworks.each { |framework| framework.release }
8089
command = container.release
8190

8291
{
@@ -100,13 +109,18 @@ def self.container_directory
100109
Pathname.new(File.expand_path('container', File.dirname(__FILE__)))
101110
end
102111

112+
def self.framework_directory
113+
Pathname.new(File.expand_path('framework', File.dirname(__FILE__)))
114+
end
115+
103116
def self.jre_directory
104117
Pathname.new(File.expand_path('jre', File.dirname(__FILE__)))
105118
end
106119

107120
def self.require_component_files
108-
component_files = container_directory.children()
109-
component_files.concat jre_directory.children
121+
component_files = jre_directory.children()
122+
component_files.concat framework_directory.children
123+
component_files.concat container_directory.children
110124

111125
component_files.each do |file|
112126
require file.relative_path_from(root_directory) unless file.directory?
@@ -121,6 +135,10 @@ def container
121135
@containers.detect { |container| container.detect }
122136
end
123137

138+
def frameworks
139+
@frameworks.select { |framework| framework.detect }
140+
end
141+
124142
def jre
125143
@jres.detect { |jre| jre.detect }
126144
end

lib/java_buildpack/container/main.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ def release
5959

6060
private
6161

62+
CONFIGURATION_PROPERTY = 'java.main.class'.freeze
63+
6264
CONTAINER_NAME = 'java-main'.freeze
6365

6466
MANIFEST_PROPERTY = 'Main-Class'.freeze
6567

66-
SYSTEM_PROPERTY = 'java.main.class'.freeze
67-
6868
def java_opts
6969
@java_opts.compact.sort.join(' ')
7070
end
@@ -76,7 +76,7 @@ def manifest
7676
end
7777

7878
def main_class
79-
@configuration[SYSTEM_PROPERTY] || manifest[MANIFEST_PROPERTY]
79+
@configuration[CONFIGURATION_PROPERTY] || manifest[MANIFEST_PROPERTY]
8080
end
8181

8282
end

lib/java_buildpack/framework.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
require 'java_buildpack'
17+
18+
# A module encapsulating all of the framework components for the Java buildpack
19+
module JavaBuildpack::Framework
20+
end
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
require 'java_buildpack/framework'
17+
require 'shellwords'
18+
19+
module JavaBuildpack::Framework
20+
21+
# Encapsulates the detect, compile, and release functionality for contributing custom Java options to an application
22+
# at runtime.
23+
class JavaOpts
24+
25+
# Creates an instance, passing in an arbitrary collection of options.
26+
#
27+
# @param [Hash] context the context that is provided to the instance
28+
# @option context [Array<String>] :java_opts an array that Java options can be added to
29+
# @option context [Hash] :configuration the properties provided by the user
30+
def initialize(context = {})
31+
@java_opts = context[:java_opts]
32+
@configuration = context[:configuration]
33+
end
34+
35+
# Detects whether this application contributes Java options.
36+
#
37+
# @return [String] returns +java-opts+ if a +java.opts+ system property is set by the user
38+
def detect
39+
@configuration.has_key?(CONFIGURATION_PROPERTY) ? CONTAINER_NAME : nil
40+
end
41+
42+
# Does nothing as no transformations are required when contributing Java options
43+
#
44+
# @return [void]
45+
def compile
46+
end
47+
48+
# Adds the contents of +java.opts+ to the +context[:java_opts]+ for use when running the application
49+
#
50+
# @return [void]
51+
def release
52+
@configuration[CONFIGURATION_PROPERTY].shellsplit.map do |java_opt|
53+
@java_opts << java_opt.gsub(/([\s])/, '\\\\\1')
54+
end
55+
end
56+
57+
private
58+
59+
CONFIGURATION_PROPERTY = 'java.opts'.freeze
60+
61+
CONTAINER_NAME = 'java-opts'.freeze
62+
63+
end
64+
65+
end

lib/java_buildpack/jre.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515

1616
require 'java_buildpack'
1717

18-
# A module encapsulating all of the JRE selection code for the Java buildpack
18+
# A module encapsulating all of the JRE components for the Java buildpack
1919
module JavaBuildpack::Jre
2020
end

0 commit comments

Comments
 (0)