Skip to content

Commit ffeefb9

Browse files
committed
Merge branch 'multi-buildpack'
2 parents b788680 + 635a4eb commit ffeefb9

11 files changed

Lines changed: 698 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ To learn how to configure various properties of the buildpack, follow the "Confi
9090
* [JMX](docs/framework-jmx.md) ([Configuration](docs/framework-jmx.md#configuration))
9191
* [Luna Security Provider](docs/framework-luna_security_provider.md) ([Configuration](docs/framework-luna_security_provider.md#configuration))
9292
* [MariaDB JDBC](docs/framework-maria_db_jdbc.md) ([Configuration](docs/framework-maria_db_jdbc.md#configuration)) (also supports MySQL)
93+
* [Multiple Buildpack](docs/framework-multi_buildpack.md)
9394
* [Metric Writer](docs/framework-metric_writer.md) ([Configuration](docs/framework-metric_writer.md#configuration))
9495
* [New Relic Agent](docs/framework-new_relic_agent.md) ([Configuration](docs/framework-new_relic_agent.md#configuration))
9596
* [Play Framework Auto Reconfiguration](docs/framework-play_framework_auto_reconfiguration.md) ([Configuration](docs/framework-play_framework_auto_reconfiguration.md#configuration))

bin/finalize

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env ruby
2+
# Cloud Foundry Java Buildpack
3+
# Copyright 2013-2017 the original author or authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
$stdout.sync = true
18+
$stderr.sync = true
19+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
20+
21+
require 'java_buildpack/buildpack'
22+
23+
app_dir = ARGV[0]
24+
25+
JavaBuildpack::Buildpack.with_buildpack(app_dir, 'Finalize failed with exception %s', &:compile)

config/components.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ jres:
3434
# - "JavaBuildpack::Jre::OracleJRE"
3535
# - "JavaBuildpack::Jre::ZuluJRE"
3636

37-
# Frameworks are processed in order. Any Java Opts added by the JavaOpts framework will be specified in the start
38-
# command after any Java Opts added by previous frameworks.
37+
# Frameworks are processed in order.
38+
# The MultiBuildpack framework is first in order to allow any framework to override contributions from earlier buildpacks
39+
# The JavaOpts is last in order to allow any user-defined JAVA_OPTS to override contributions from earlier frameworks
3940
frameworks:
41+
- "JavaBuildpack::Framework::MultiBuildpack"
4042
- "JavaBuildpack::Framework::AppDynamicsAgent"
4143
- "JavaBuildpack::Framework::AspectjWeaverAgent"
4244
- "JavaBuildpack::Framework::ClientCertificateMapper"

docs/framework-multi_buildpack.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Multiple Buildpack Framework
2+
The Multiple Buildpack Framework enables the Java Buildpack to act as the final buildpack in a multiple buildpack deployment. It reads the contributions of other, earlier buildpacks and incorporates them into its standard staging.
3+
4+
The Java Options Framework contributes arbitrary Java options to the application at runtime.
5+
6+
<table>
7+
<tr>
8+
<td><strong>Detection Criterion</strong></td>
9+
<td>Existence of buildpack contribution directories (typically <tt>/tmp/&lt;RANDOM&gt;/deps/&lt;INDEX&gt;</tt> containing a <tt>config.yml</tt> file.</td>
10+
</tr>
11+
<tr>
12+
<td><strong>Tags</strong></td>
13+
<td><tt>multi-buildpack=&lt;BUILDPACK_NAME&gt;,...</tt></td>
14+
</tr>
15+
</table>
16+
Tags are printed to standard output by the buildpack detect script
17+
18+
## Multiple Buildpack Integration API
19+
When the Java Buildpack acts as the final buildpack in a multiple buildpack deployment it honors the following core contract integration points.
20+
21+
| Integration Point | Buildpack Usage
22+
| ----------------- | ---------------
23+
| `/bin` | An existing `/bin` directory contributed by a non-final buildpack will be added to the `$PATH` of the application as it executes
24+
| `/lib` | An existing `/lib` directory contributed by a non-final buildpack will be added to the `$LD_LIBRARY_PATH` of the application as it executes
25+
26+
In addition to the core contract, the Java Buildpack defines the following keys in `config.yml` as extension points for contributing to the application. **All keys are optional, and all paths are absolute.**
27+
28+
| Key | Type | Description
29+
| --- | ---- | -----------
30+
| `additional_libraries` | `[ path ]` | An array of absolute paths to libraries will be added to the application's classpath
31+
| `environment_variables` | `{ string, ( path \| string ) }` | A hash of string keys to absolute path or string values that will be added as environment variables
32+
| `extension_directories` | `[ path ]` | An array of absolute paths to directories containing JRE extensions
33+
| `java_opts.agentpaths` | `[ path ]` | An array of absolute paths to libraries that will be added as agents
34+
| `java_opts.agentpaths_with_props` | `{ path, { string, string } }` | A nested hash with absolute paths keys and hashes of string keys and string values as a value that will be added as agents with properties
35+
| `java_opts.bootclasspath_ps` | `[ path ]` | An array of absolute paths that will be added to the application's bootclasspath
36+
| `java_opts.javaagents` | `[ path ]` | An array of absolute paths that will be added as javaagents
37+
| `java_opts.preformatted_options` | `[ string ]` | An array of strings that will be added as options without modification
38+
| `java_opts.options` | `{ string, ( path \| string ) }` | A hash of string keys to absolute path or string values that will be added as options
39+
| `java_opts.system_properties` | `{ string , ( path \| string ) }` | A hash of string keys to absolute path or string values that will be added as system properties
40+
| `security_providers` | `[ string ]` | An array of strings to be added to list of security providers
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
# Cloud Foundry Java Buildpack
2+
# Copyright 2013-2017 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 'fileutils'
17+
require 'pathname'
18+
require 'java_buildpack/component/base_component'
19+
require 'java_buildpack/framework'
20+
require 'java_buildpack/logging/logger_factory'
21+
require 'java_buildpack/util/qualify_path'
22+
require 'yaml'
23+
24+
module JavaBuildpack
25+
module Framework
26+
27+
# Encapsulates the functionality for multi buildpack support.
28+
class MultiBuildpack < JavaBuildpack::Component::BaseComponent
29+
include JavaBuildpack::Util
30+
31+
# (see JavaBuildpack::Component::BaseComponent#initialize)
32+
def initialize(context)
33+
super(context)
34+
35+
@logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger MultiBuildpack
36+
@logger.debug { "Dependencies Directory: #{ARGV[3]}" }
37+
end
38+
39+
# (see JavaBuildpack::Component::BaseComponent#detect)
40+
def detect
41+
!dep_directories.empty? ? "multi-buildpack=#{names(dep_directories).join(',')}" : nil
42+
end
43+
44+
# (see JavaBuildpack::Component::BaseComponent#compile)
45+
def compile
46+
puts "#{'----->'.red.bold} #{'Multiple Buildpacks'.blue.bold} detected"
47+
48+
dep_directories.each do |dep_directory|
49+
config = config(config_file(dep_directory))
50+
name = name(config)
51+
52+
log_configuration config
53+
log_dep_contents dep_directory
54+
55+
contributions = [
56+
add_bin(dep_directory),
57+
add_lib(dep_directory),
58+
add_additional_libraries(config),
59+
add_environment_variables(config),
60+
add_extension_directories(config),
61+
add_java_opts(config),
62+
add_security_providers(config)
63+
]
64+
65+
puts " #{name}#{contributions_message(contributions)}"
66+
end
67+
end
68+
69+
# (see JavaBuildpack::Component::BaseComponent#release)
70+
def release
71+
dep_directories.each do |dep_directory|
72+
config = config(config_file(dep_directory))
73+
74+
add_bin(dep_directory)
75+
add_lib(dep_directory)
76+
add_additional_libraries(config)
77+
add_environment_variables(config)
78+
add_extension_directories(config)
79+
add_java_opts(config)
80+
add_security_providers(config)
81+
end
82+
end
83+
84+
private
85+
86+
def add_additional_libraries(config)
87+
additional_libraries = config['config']['additional_libraries']
88+
return unless additional_libraries
89+
90+
additional_libraries.each do |additional_library|
91+
@droplet.additional_libraries << Pathname.new(additional_library)
92+
end
93+
94+
'Additional Libraries'
95+
end
96+
97+
def add_agentpaths(java_opts)
98+
agentpaths = java_opts['agentpaths']
99+
return unless agentpaths
100+
101+
agentpaths.each do |agentpath|
102+
@droplet.java_opts.add_agentpath Pathname.new(agentpath)
103+
end
104+
105+
'Agents'
106+
end
107+
108+
def add_agentpaths_with_props(java_opts)
109+
agentpaths = java_opts['agentpaths_with_props']
110+
return unless agentpaths
111+
112+
agentpaths.each do |agentpath, props|
113+
@droplet.java_opts.add_agentpath_with_props Pathname.new(agentpath), props
114+
end
115+
116+
'Agent with Properties'
117+
end
118+
119+
def add_bin(dep_directory)
120+
bin_directory = dep_directory + 'bin'
121+
return unless bin_directory.exist?
122+
123+
@droplet.environment_variables
124+
.add_environment_variable('PATH', "$PATH:#{qualify_path(bin_directory, @droplet.root)}")
125+
126+
'$PATH'
127+
end
128+
129+
def add_bootclasspath_ps(java_opts)
130+
bootclasspath_ps = java_opts['bootclasspath_ps']
131+
return unless bootclasspath_ps
132+
133+
bootclasspath_ps.each do |bootclasspath_p|
134+
@droplet.java_opts.add_bootclasspath_p Pathname.new(bootclasspath_p)
135+
end
136+
137+
'Boot Classpaths'
138+
end
139+
140+
def add_environment_variables(config)
141+
environment_variables = config['config']['environment_variables']
142+
return unless environment_variables
143+
144+
environment_variables.each do |key, value|
145+
path = Pathname.new(value)
146+
147+
if path.exist?
148+
@droplet.environment_variables.add_environment_variable key, path
149+
else
150+
@droplet.environment_variables.add_environment_variable key, value
151+
end
152+
end
153+
154+
'Environment Variables'
155+
end
156+
157+
def add_extension_directories(config)
158+
extension_directories = config['config']['extension_directories']
159+
return unless extension_directories
160+
161+
extension_directories.each do |extension_directory|
162+
@droplet.extension_directories << Pathname.new(extension_directory)
163+
end
164+
165+
'Extension Directories'
166+
end
167+
168+
def add_javaagent(java_opts)
169+
javaagents = java_opts['javaagents']
170+
return unless javaagents
171+
172+
javaagents.each do |javaagent|
173+
@droplet.java_opts.add_javaagent Pathname.new(javaagent)
174+
end
175+
176+
'Java Agents'
177+
end
178+
179+
def add_java_opts(config)
180+
java_opts = config['config']['java_opts']
181+
return unless java_opts
182+
183+
[
184+
add_agentpaths(java_opts),
185+
add_agentpaths_with_props(java_opts),
186+
add_bootclasspath_ps(java_opts),
187+
add_javaagent(java_opts),
188+
add_options(java_opts),
189+
add_preformatted_options(java_opts),
190+
add_system_properties(java_opts)
191+
]
192+
end
193+
194+
def add_lib(dep_directory)
195+
lib_directory = dep_directory + 'lib'
196+
return unless lib_directory.exist?
197+
198+
@droplet.environment_variables
199+
.add_environment_variable('LD_LIBRARY_PATH',
200+
"$LD_LIBRARY_PATH:#{qualify_path(lib_directory, @droplet.root)}")
201+
202+
'$LD_LIBRARY_PATH'
203+
end
204+
205+
def add_options(java_opts)
206+
options = java_opts['options']
207+
return unless options
208+
209+
options.each do |key, value|
210+
path = Pathname.new(value)
211+
212+
if path.exist?
213+
@droplet.java_opts.add_option key, path
214+
else
215+
@droplet.java_opts.add_option key, value
216+
end
217+
end
218+
219+
'Options'
220+
end
221+
222+
def add_preformatted_options(java_opts)
223+
preformatted_options = java_opts['preformatted_options']
224+
return unless preformatted_options
225+
226+
preformatted_options.each do |preformatted_option|
227+
@droplet.java_opts.add_preformatted_options preformatted_option
228+
end
229+
230+
'Preformatted Options'
231+
end
232+
233+
def add_security_providers(config)
234+
security_providers = config['config']['security_providers']
235+
return unless security_providers
236+
237+
security_providers.each do |security_provider|
238+
@droplet.security_providers << security_provider
239+
end
240+
241+
'Security Providers'
242+
end
243+
244+
def add_system_properties(java_opts)
245+
system_properties = java_opts['system_properties']
246+
return unless system_properties
247+
248+
system_properties.each do |key, value|
249+
path = Pathname.new(value)
250+
251+
if path.exist?
252+
@droplet.java_opts.add_system_property key, path
253+
else
254+
@droplet.java_opts.add_system_property key, value
255+
end
256+
end
257+
258+
'System Properties'
259+
end
260+
261+
def config(config_file)
262+
YAML.load_file(config_file)
263+
end
264+
265+
def config_file(dep_directory)
266+
dep_directory + 'config.yml'
267+
end
268+
269+
def contributions_message(contributions)
270+
return if contributions.compact.empty?
271+
" contributed to: #{contributions.flatten.compact.sort.join(', ')}"
272+
end
273+
274+
def dep_directories
275+
deps = Pathname.glob('/tmp/*/deps').first
276+
return [] unless deps
277+
278+
deps
279+
.children
280+
.select { |dep_directory| config_file(dep_directory).exist? }
281+
.sort_by(&:basename)
282+
end
283+
284+
def log_configuration(config)
285+
@logger.debug { "Configuration: #{config}" }
286+
end
287+
288+
def log_dep_contents(dep_directory)
289+
@logger.debug do
290+
paths = []
291+
dep_directory.find { |f| paths << f.relative_path_from(dep_directory).to_s }
292+
293+
"Application Contents (#{dep_directory}): #{paths}"
294+
end
295+
end
296+
297+
def name(config)
298+
config['name']
299+
end
300+
301+
def names(dep_directories)
302+
dep_directories.map { |dep_directory| name(config(config_file(dep_directory))) }
303+
end
304+
305+
end
306+
307+
end
308+
end

spec/fixtures/framework_multi_buildpack_deps/0/bin/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)