Skip to content

Commit 664f2cc

Browse files
committed
Tomcat container
Previously, web applications had no container to run in. This change adds a Tomcat container to run web applications in. The container detects the presence of a WEB-INF directory in the application, downloads and configures Tomcat during the compile phase, and returns a Tomcat starting command in the release phase. [#49694799]
1 parent d22a90c commit 664f2cc

13 files changed

Lines changed: 529 additions & 103 deletions

File tree

README.md

Lines changed: 199 additions & 39 deletions
Large diffs are not rendered by default.

lib/java_buildpack/buildpack.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ def initialize(app_dir)
3535
Buildpack.require_component_files
3636
components = Buildpack.components
3737

38-
java_opts = []
38+
java_home = String.new
39+
java_opts = Array.new
3940

4041
@jres = components['jres'].map do |jre|
4142
jre.constantize.new({
4243
:app_dir => app_dir,
44+
:java_home => java_home,
4345
:java_opts => java_opts,
4446
:configuration => Buildpack.configuration(app_dir, jre)
4547
})
@@ -48,6 +50,7 @@ def initialize(app_dir)
4850
@frameworks = components['frameworks'].map do |framework|
4951
framework.constantize.new({
5052
:app_dir => app_dir,
53+
:java_home => java_home,
5154
:java_opts => java_opts,
5255
:configuration => Buildpack.configuration(app_dir, framework)
5356
})
@@ -56,6 +59,7 @@ def initialize(app_dir)
5659
@containers = components['containers'].map do |container|
5760
container.constantize.new({
5861
:app_dir => app_dir,
62+
:java_home => java_home,
5963
:java_opts => java_opts,
6064
:configuration => Buildpack.configuration(app_dir, container)
6165
})

lib/java_buildpack/container/main.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ class Main
2727
#
2828
# @param [Hash] context the context that is provided to the instance
2929
# @option context [String] :app_dir the directory that the application exists in
30+
# @option context [String] :java_home the directory that acts as +JAVA_HOME+
3031
# @option context [Array<String>] :java_opts an array that Java options can be added to
3132
# @option context [Hash] :configuration the properties provided by the user
3233
def initialize(context = {})
3334
@app_dir = context[:app_dir]
35+
@java_home = context[:java_home]
3436
@java_opts = context[:java_opts]
3537
@configuration = context[:configuration]
3638
end
@@ -54,7 +56,7 @@ def compile
5456
#
5557
# @return [String] the command to run the application.
5658
def release
57-
".java/bin/java -cp . #{java_opts} #{main_class}"
59+
"#{@java_home}/bin/java -cp . #{java_opts} #{main_class}"
5860
end
5961

6062
private

lib/java_buildpack/container/tomcat.rb

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
require 'java_buildpack/container'
1717
require 'java_buildpack/repository/configured_item'
18-
require 'java_buildpack/util/properties'
18+
require 'java_buildpack/util/application_cache'
19+
require 'java_buildpack/util/format_duration'
1920

2021
module JavaBuildpack::Container
2122

@@ -26,63 +27,120 @@ class Tomcat
2627
#
2728
# @param [Hash] context the context that is provided to the instance
2829
# @option context [String] :app_dir the directory that the application exists in
30+
# @option context [String] :java_home the directory that acts as +JAVA_HOME+
2931
# @option context [Array<String>] :java_opts an array that Java options can be added to
30-
# @option context [Hash] :configuration the properties
32+
# @option context [Hash] :configuration the properties provided by the user
3133
def initialize(context)
3234
@app_dir = context[:app_dir]
35+
@java_home = context[:java_home]
3336
@java_opts = context[:java_opts]
34-
@configuration = context[:configuration]
37+
@version, @uri = Tomcat.find_tomcat(@app_dir, context[:configuration])
38+
@tomcat_home = Tomcat.tomcat_home @app_dir
3539
end
3640

3741
# Detects whether this application is a Tomcat application.
3842
#
3943
# @return [String] returns +tomcat-<version>+ if and only if the application has a +WEB-INF+ directory, otherwise
4044
# returns +nil+
4145
def detect
42-
if web_inf?
43-
tomcat_version, tomcat_uri = find_tomcat
44-
id tomcat_version
45-
else
46-
nil
47-
end
46+
@version ? id(@version) : nil
4847
end
4948

50-
# Does nothing as no transformations are currently performed for Tomcat applications.
49+
# Downlaods and unpacks a Tomcat instance
5150
#
5251
# @return [void]
5352
def compile
53+
download_start_time = Time.now
54+
print "-----> Downloading Tomcat #{@version} from #{@uri} "
55+
56+
JavaBuildpack::Util::ApplicationCache.new.get(@uri) do |file| # TODO Use global cache #50175265
57+
puts "(#{(Time.now - download_start_time).duration})"
58+
expand file
59+
end
60+
61+
link_application
5462
end
5563

5664
# Creates the command to run the Tomcat application.
5765
#
5866
# @return [String] the command to run the application.
5967
def release
68+
@java_opts << "-D#{KEY_HTTP_PORT}=$PORT"
69+
70+
"JAVA_HOME=#{@java_home} JAVA_OPTS=\"#{java_opts}\" #{@tomcat_home}/bin/catalina.sh run"
6071
end
6172

6273
private
6374

75+
KEY_HTTP_PORT = 'http.port'.freeze
76+
77+
RESOURCES = '../../../resources/tomcat'
78+
79+
TOMCAT_HOME = '.tomcat'.freeze
80+
6481
WEB_INF_DIRECTORY = 'WEB-INF'.freeze
6582

66-
def web_inf?
67-
File.exists? File.join(@app_dir, WEB_INF_DIRECTORY)
83+
def self.check_version_format(version)
84+
raise "Malformed Tomcat version #{version}: too many version components" if version[3]
6885
end
6986

70-
def find_tomcat
71-
JavaBuildpack::Repository::ConfiguredItem.find_item(@configuration) do |version|
72-
check_version_format version
87+
def copy_resources(tomcat_home)
88+
resources = File.expand_path(RESOURCES, File.dirname(__FILE__))
89+
system "cp -r #{resources}/* #{tomcat_home}"
90+
end
91+
92+
def expand(file)
93+
expand_start_time = Time.now
94+
print "-----> Expanding Tomcat to #{TOMCAT_HOME} "
95+
96+
tomcat_home = File.join @app_dir, TOMCAT_HOME
97+
system "rm -rf #{@tomcat_home}"
98+
system "mkdir -p #{@tomcat_home}"
99+
system "tar xzf #{file.path} -C #{@tomcat_home} --strip 1 --exclude webapps --exclude conf/server.xml --exclude conf/context.xml 2>&1"
100+
101+
copy_resources @tomcat_home
102+
103+
puts "(#{(Time.now - expand_start_time).duration})"
104+
end
105+
106+
def self.find_tomcat(app_dir, configuration)
107+
if web_inf? app_dir
108+
version, uri = JavaBuildpack::Repository::ConfiguredItem.find_item(configuration) do |version|
109+
check_version_format version
110+
end
111+
else
112+
version = nil
113+
uri = nil
73114
end
115+
116+
return version, uri
74117
rescue => e
75118
raise RuntimeError, "Tomcat container error: #{e.message}", e.backtrace
76119
end
77120

78-
private
121+
def id(version)
122+
"tomcat-#{version}"
123+
end
79124

80-
def check_version_format(version)
81-
raise "Malformed Tomcat version #{version}: too many version components" if version[3]
125+
def java_opts
126+
@java_opts.compact.sort.join(' ')
127+
end
128+
129+
def link_application
130+
webapps = "#{@tomcat_home}/webapps"
131+
root = "#{webapps}/ROOT"
132+
133+
system "rm -rf #{root}"
134+
system "mkdir -p #{webapps}"
135+
system "ln -s #{File.expand_path @app_dir} #{root}"
136+
end
137+
138+
def self.tomcat_home(app_dir)
139+
File.join app_dir, TOMCAT_HOME
82140
end
83141

84-
def id(tomcat_version)
85-
"tomcat-#{tomcat_version}"
142+
def self.web_inf?(app_dir)
143+
File.exists? File.join(app_dir, WEB_INF_DIRECTORY)
86144
end
87145

88146
end

lib/java_buildpack/jre/openjdk.rb

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ class OpenJdk
3030
#
3131
# @param [Hash] context the context that is provided to the instance
3232
# @option context [String] :app_dir the directory that the application exists in
33+
# @option context [String] :java_home the directory that acts as +JAVA_HOME+
3334
# @option context [Array<String>] :java_opts an array that Java options can be added to
3435
# @option context [Hash] :configuration the properties provided by the user
3536
def initialize(context)
3637
@app_dir = context[:app_dir]
38+
@java_home = context[:java_home].concat OpenJdk.java_home(@app_dir)
3739
@java_opts = context[:java_opts]
3840
@configuration = context[:configuration]
3941
@version, @uri = OpenJdk.find_openjdk(@configuration)
@@ -93,37 +95,30 @@ def release
9395
}
9496
}
9597

96-
def self.find_openjdk(configuration)
97-
JavaBuildpack::Repository::ConfiguredItem.find_item(configuration)
98-
rescue => e
99-
raise RuntimeError, "OpenJDK JRE error: #{e.message}", e.backtrace
100-
end
101-
10298
def expand(file)
10399
expand_start_time = Time.now
104100
print "-----> Expanding JRE to #{JAVA_HOME} "
105101

106-
java_home = File.join @app_dir, JAVA_HOME
107-
system "rm -rf #{java_home}"
108-
system "mkdir -p #{java_home}"
109-
system "tar xzf #{file.path} -C #{java_home} --strip 1 2>&1"
102+
java_home =
103+
system "rm -rf #{@java_home}"
104+
system "mkdir -p #{@java_home}"
105+
system "tar xzf #{file.path} -C #{@java_home} --strip 1 2>&1"
110106

111107
puts "(#{(Time.now - expand_start_time).duration})"
112108
end
113109

110+
def self.find_openjdk(configuration)
111+
JavaBuildpack::Repository::ConfiguredItem.find_item(configuration)
112+
rescue => e
113+
raise RuntimeError, "OpenJDK JRE error: #{e.message}", e.backtrace
114+
end
115+
114116
def id(version)
115117
"openjdk-#{version}"
116118
end
117119

118-
def to_java_opts(memory_values)
119-
java_opts = []
120-
121-
memory_values.each_pair do |key, memory_value|
122-
mapping = MAPPINGS[key]
123-
java_opts << "#{mapping[:switch]}#{memory_value}" if mapping
124-
end
125-
126-
java_opts
120+
def self.java_home(app_dir)
121+
File.join app_dir, JAVA_HOME
127122
end
128123

129124
def memory_sizes(configuration)
@@ -149,6 +144,17 @@ def specified_sizes(configuration)
149144
specified_sizes
150145
end
151146

147+
def to_java_opts(memory_values)
148+
java_opts = []
149+
150+
memory_values.each_pair do |key, memory_value|
151+
mapping = MAPPINGS[key]
152+
java_opts << "#{mapping[:switch]}#{memory_value}" if mapping
153+
end
154+
155+
java_opts
156+
end
157+
152158
end
153159

154160
end

lib/java_buildpack/repository/configured_item.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ class ConfiguredItem
2525
# Finds an instance of the file based on the configuration.
2626
#
2727
# @param [Hash] configuration the configuration
28+
# @option configuration [String] :repository_root the root directory of the repository
29+
# @option configuration [String] :version the version of the file to resolve
2830
# @param [Block, nil] version_validator an optional version validation block
29-
# @return [TokenizedVersion] the chosen version of the file
30-
# @return [String] the download URI of the chosen version of the file
31+
# @return [JavaBuildpack::Util::TokenizedVersion] the chosen version of the file
32+
# @return [String] the URI of the chosen version of the file
3133
def self.find_item(configuration, &version_validator)
3234
repository_root = ConfiguredItem.repository_root(configuration)
3335
version = ConfiguredItem.version(configuration)
@@ -39,6 +41,7 @@ def self.find_item(configuration, &version_validator)
3941
private
4042

4143
KEY_REPOSITORY_ROOT = 'repository_root'.freeze
44+
4245
KEY_VERSION = 'version'.freeze
4346

4447
def self.index(repository_root)
@@ -56,4 +59,4 @@ def self.version(configuration)
5659

5760
end
5861

59-
end
62+
end

lib/java_buildpack/util/download_cache.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def initialize(cache_root = Dir.tmpdir)
3838
# 1. Obtain an exclusive lock based on the URI of the item. This allows concurrency for different items, but not for
3939
# the same item.
4040
# 2. If the the cached item does not exist, download from +uri+ and cache it, its +Etag+, and its +Last-Modified+
41-
# values if they exist
41+
# values if they exist.
4242
# 3. If the cached file does exist, and the original download had an +Etag+ or a +Last-Modified+ value, attempt to
4343
# download from +uri+ again. If the result is +304+ (+Not-Modified+), then proceed without changing the cached
4444
# item. If it is anything else, overwrite the cached file and its +Etag+ and +Last-Modified+ values if they exist.

resources/tomcat/conf/context.xml

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

resources/tomcat/conf/server.xml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version='1.0' encoding='utf-8'?>
2+
<!--
3+
Cloud Foundry Java Buildpack
4+
Copyright (c) 2013 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
-->
18+
19+
<Server port='-1'>
20+
21+
<Listener className='org.apache.catalina.core.JasperListener'/>
22+
23+
<Service name='Catalina'>
24+
<Connector port='${http.port}'/>
25+
26+
<Engine defaultHost='localhost' name='Catalina'>
27+
<Host name='localhost'/>
28+
</Engine>
29+
</Service>
30+
31+
</Server>

spec/fixtures/stub-tomcat.tar.gz

172 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)