diff --git a/.idea/dictionaries/cgfrost.xml b/.idea/dictionaries/cgfrost.xml new file mode 100644 index 0000000000..bed05d0733 --- /dev/null +++ b/.idea/dictionaries/cgfrost.xml @@ -0,0 +1,7 @@ + + + + cafebabe + + + \ No newline at end of file diff --git a/config/open_jdk_jre.yml b/config/open_jdk_jre.yml index 2a4feb2af6..2044fd5fda 100644 --- a/config/open_jdk_jre.yml +++ b/config/open_jdk_jre.yml @@ -14,16 +14,21 @@ # limitations under the License. # Configuration for JRE repositories keyed by vendor -# To go back to Java 7, permgen should be used instead of metaspace. Please see the documentation for more detail. +# Pre Java 1.8, permgen was used instead of metaspace, the buildpack will consume the configuration as appropriate. +# Please see the documentation for more detail. --- repository_root: "{default.repository.root}/openjdk/{platform}/{architecture}" -version: 1.8.0_+ +version: + detect_compiled: enabled + 8: 1.8.0_+ + 7: 1.7.0_+ + 6: 1.6.0_+ memory_sizes: metaspace: 64m.. - # permgen: 64m.. + permgen: 64m.. memory_heuristics: heap: 75 metaspace: 10 - # permgen: 10 + permgen: 10 stack: 5 native: 10 diff --git a/config/oracle_jre.yml b/config/oracle_jre.yml index cdb6226dc2..6ffe8bf3b8 100644 --- a/config/oracle_jre.yml +++ b/config/oracle_jre.yml @@ -14,19 +14,24 @@ # limitations under the License. # Configuration for JRE repositories keyed by vendor -# Pre Java 1.8, permgen was used instead of metaspace. Please see the documentation for more detail. +# Pre Java 1.8, permgen was used instead of metaspace, the buildpack will consume the configuration as appropriate. +# Please see the documentation for more detail. --- # You must specify a the repository root of an Oracle JRE repository. Please see the documentation for more detail. # e.g. repository_root: "http://example.com/oracle-jre/{platform}/{architecture}" repository_root: "" -version: 1.8.0_+ +version: + detect_compiled: enabled + 8: 1.8.0_+ + 7: 1.7.0_+ + 6: 1.6.0_+ memory_sizes: metaspace: 64m.. -# permgen: 64m.. + permgen: 64m.. memory_heuristics: heap: 75 metaspace: 10 -# permgen: 10 + permgen: 10 stack: 5 native: 10 diff --git a/java-buildpack.iml b/java-buildpack.iml index a0bcb4d298..85fb2c22ac 100644 --- a/java-buildpack.iml +++ b/java-buildpack.iml @@ -271,7 +271,7 @@ - + @@ -281,13 +281,13 @@ - + - + diff --git a/lib/java_buildpack/jre/open_jdk_like.rb b/lib/java_buildpack/jre/open_jdk_like.rb index 4b665c02b6..6592151c92 100644 --- a/lib/java_buildpack/jre/open_jdk_like.rb +++ b/lib/java_buildpack/jre/open_jdk_like.rb @@ -15,6 +15,8 @@ # limitations under the License. require 'fileutils' +require 'zip' +require 'find' require 'java_buildpack/component/versioned_dependency_component' require 'java_buildpack/jre' require 'java_buildpack/jre/memory/openjdk_memory_heuristic_factory' @@ -39,8 +41,12 @@ def initialize(context) # (see JavaBuildpack::Component::BaseComponent#detect) def detect + version = required_java_version + version_specific_configuration = { KEY_REPOSITORY_ROOT => @configuration[KEY_REPOSITORY_ROOT], + KEY_VERSION => version } + @version, @uri = JavaBuildpack::Repository::ConfiguredItem.find_item(@component_name, - @configuration) + version_specific_configuration) @droplet.java_home.version = @version super end @@ -65,15 +71,90 @@ def release KEY_MEMORY_SIZES = 'memory_sizes'.freeze - private_constant :KEY_MEMORY_HEURISTICS, :KEY_MEMORY_SIZES + KEY_DETECT_COMPILED_VERSION = 'detect_compiled'.freeze + + KEY_JAVA_EIGHT_VERSION = 8.freeze + + KEY_JAVA_SEVEN_VERSION = 7.freeze + + KEY_JAVA_SIX_VERSION = 6.freeze + + KEY_REPOSITORY_ROOT = 'repository_root'.freeze + + KEY_VERSION = 'version'.freeze + + CAFEBABE = 'cafebabe'.freeze + + private_constant :KEY_MEMORY_HEURISTICS, :KEY_MEMORY_SIZES, :KEY_DETECT_COMPILED_VERSION, + :KEY_JAVA_SEVEN_VERSION, :KEY_JAVA_SIX_VERSION, :KEY_VERSION, :CAFEBABE def killjava @droplet.sandbox + 'bin/killjava.sh' end + def java_6?(version_code) + version_code == 32 + end + + def java_7?(version_code) + version_code == 33 + end + + def java_6_or_java_7?(version_code) + java_6?(version_code) || java_7?(version_code) + end + + def required_java_version + version_configuration = @configuration[KEY_VERSION] + detected_version = resolved_version_code version_configuration + + if java_6? detected_version + version = version_configuration[KEY_JAVA_SIX_VERSION] + elsif java_7? detected_version + version = version_configuration[KEY_JAVA_SEVEN_VERSION] + else + version = version_configuration[KEY_JAVA_EIGHT_VERSION] + end + version + end + + def resolved_version_code(configuration) + configuration[KEY_DETECT_COMPILED_VERSION] == 'enabled' ? detect_compiled_version(@application.root) : 34 + end + + # If no valid files are found with 'cafebabe' and an expected version code then + # the code '34' for Java 8 will be returned. + def detect_compiled_version(application_root) + result = 0 + Find.find(application_root.to_path) do |sub_entry| + result = check_file(sub_entry, result) if sub_entry.end_with? '.class' unless FileTest.directory?(sub_entry) + end + result == 0 ? 34 : result + end + + def check_file(entry, result) + bits = File.open(entry).read.unpack('H*')[0] + logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger OpenJDKLike + logger.debug "Scanning '#{entry}', first 8 bits are '#{bits[0, 8]}' and the version code is '#{bits[14, 2]}'." + result = [result, Integer(bits[14, 2])].max if bits[0, 8] == CAFEBABE + result + end + def memory - sizes = @configuration[KEY_MEMORY_SIZES] || {} - heuristics = @configuration[KEY_MEMORY_HEURISTICS] || {} + sizes = @configuration[KEY_MEMORY_SIZES] ? @configuration[KEY_MEMORY_SIZES].clone : {} + heuristics = @configuration[KEY_MEMORY_HEURISTICS] ? @configuration[KEY_MEMORY_HEURISTICS].clone : {} + + version_configuration = @configuration[KEY_VERSION] + detected_version = resolved_version_code version_configuration + + if java_6_or_java_7? detected_version + heuristics.delete 'metaspace' + sizes.delete 'metaspace' + else + heuristics.delete 'permgen' + sizes.delete 'permgen' + end + OpenJDKMemoryHeuristicFactory.create_memory_heuristic(sizes, heuristics, @version).resolve end diff --git a/spec/fixtures/jre_java6_application/META-INF/MANIFEST.MF b/spec/fixtures/jre_java6_application/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..58630c02ef --- /dev/null +++ b/spec/fixtures/jre_java6_application/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/spec/fixtures/jre_java6_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationConfiguration.class b/spec/fixtures/jre_java6_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationConfiguration.class new file mode 100644 index 0000000000..70c48311b4 Binary files /dev/null and b/spec/fixtures/jre_java6_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationConfiguration.class differ diff --git a/spec/fixtures/jre_java6_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationInitializer.class b/spec/fixtures/jre_java6_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationInitializer.class new file mode 100644 index 0000000000..e9f0ceccd7 Binary files /dev/null and b/spec/fixtures/jre_java6_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationInitializer.class differ diff --git a/spec/fixtures/jre_java7_application/META-INF/MANIFEST.MF b/spec/fixtures/jre_java7_application/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..58630c02ef --- /dev/null +++ b/spec/fixtures/jre_java7_application/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/spec/fixtures/jre_java7_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationConfiguration.class b/spec/fixtures/jre_java7_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationConfiguration.class new file mode 100644 index 0000000000..41847273ee Binary files /dev/null and b/spec/fixtures/jre_java7_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationConfiguration.class differ diff --git a/spec/fixtures/jre_java7_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationInitializer.class b/spec/fixtures/jre_java7_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationInitializer.class new file mode 100644 index 0000000000..84967a6d3d Binary files /dev/null and b/spec/fixtures/jre_java7_application/WEB-INF/classes/com/gopivotal/cloudfoundry/test/ApplicationInitializer.class differ diff --git a/spec/java_buildpack/jre/open_jdk_jre_spec.rb b/spec/java_buildpack/jre/open_jdk_jre_spec.rb index 62208cbefc..f15f2048da 100644 --- a/spec/java_buildpack/jre/open_jdk_jre_spec.rb +++ b/spec/java_buildpack/jre/open_jdk_jre_spec.rb @@ -27,6 +27,20 @@ let(:memory_heuristic) { double('MemoryHeuristic', resolve: %w(opt-1 opt-2)) } + let(:configuration) do + { 'version' => { 'detect_compile' => 'disabled', + 8 => '1.8.0_+', + 7 => '1.7.0_+', + 6 => '1.6.0_+' }, + 'memory_sizes' => { 'metaspace' => '64m..', + 'permgen' => '64m..' }, + 'memory_heuristics' => { 'heap' => '75', + 'metaspace' => '10', + 'permgen' => '10', + 'stack' => '5', + 'native' => '10' } } + end + before do allow(JavaBuildpack::Jre::WeightBalancingMemoryHeuristic).to receive(:new).and_return(memory_heuristic) end @@ -81,4 +95,81 @@ expect(java_opts).to include('-Djava.io.tmpdir=$TMPDIR') end + context do + + let(:configuration) { super().merge 'version' => { 'detect_compile' => 'enabled' } } + + context do + + before do + allow_any_instance_of(described_class).to receive(:java_6?).and_return(true) + end + + it 'detects with id of openjdk_jre- for a java 6 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java6_application' do + + expect(component.detect).to eq("open-jdk-jre=#{version}") + end + + it 'adds memory options to java_opts for a java 6 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java6_application' do + + component.detect + component.release + + expect(java_opts).to include('opt-1') + expect(java_opts).to include('opt-2') + end + + end + + context do + + before do + allow_any_instance_of(described_class).to receive(:java_7?).and_return(true) + end + + it 'detects with id of openjdk_jre- for a java 7 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java7_application' do + + expect(component.detect).to eq("open-jdk-jre=#{version}") + end + + end + + context do + + let(:component) { StubOpenJdkJRE.new context } + + before do + allow_any_instance_of(StubOpenJdkJRE).to receive(:supports?).and_return(true) + end + + it 'detects the correct version for a java 7 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java7_application' do + + expect(component.detect_compiled_version Pathname.new('spec/fixtures/jre_java7_application')).to eq(33) + end + + it 'detects the correct version for a java 6 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java6_application' do + + expect(component.detect_compiled_version Pathname.new('spec/fixtures/jre_java6_application')).to eq(32) + end + + class StubOpenJdkJRE < JavaBuildpack::Jre::OpenJdkJRE + + public :detect_compiled_version + + end + + end + + end + end diff --git a/spec/java_buildpack/jre/oracle_jre_spec.rb b/spec/java_buildpack/jre/oracle_jre_spec.rb index efec872043..534d8b8d9f 100644 --- a/spec/java_buildpack/jre/oracle_jre_spec.rb +++ b/spec/java_buildpack/jre/oracle_jre_spec.rb @@ -27,6 +27,20 @@ let(:memory_heuristic) { double('MemoryHeuristic', resolve: %w(opt-1 opt-2)) } + let(:configuration) do + { 'version' => { 'detect_compile' => 'disabled', + 8 => '1.8.0_+', + 7 => '1.7.0_+', + 6 => '1.6.0_+' }, + 'memory_sizes' => { 'metaspace' => '64m..', + 'permgen' => '64m..' }, + 'memory_heuristics' => { 'heap' => '75', + 'metaspace' => '10', + 'permgen' => '10', + 'stack' => '5', + 'native' => '10' } } + end + before do allow(JavaBuildpack::Jre::WeightBalancingMemoryHeuristic).to receive(:new).and_return(memory_heuristic) end @@ -81,4 +95,81 @@ expect(java_opts).to include('-Djava.io.tmpdir=$TMPDIR') end + context do + + let(:configuration) { super().merge 'version' => { 'detect_compile' => 'enabled' } } + + context do + + before do + allow_any_instance_of(described_class).to receive(:java_6?).and_return(true) + end + + it 'detects with id of oracle_jre- for a java 6 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java6_application' do + + expect(component.detect).to eq("oracle-jre=#{version}") + end + + it 'adds memory options to java_opts for a java 6 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java6_application' do + + component.detect + component.release + + expect(java_opts).to include('opt-1') + expect(java_opts).to include('opt-2') + end + + end + + context do + + before do + allow_any_instance_of(described_class).to receive(:java_7?).and_return(true) + end + + it 'detects with id of oracle_jre- for a java 7 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java7_application' do + + expect(component.detect).to eq("oracle-jre=#{version}") + end + + end + + context do + + let(:component) { StubOracleJRE.new context } + + before do + allow_any_instance_of(StubOracleJRE).to receive(:supports?).and_return(true) + end + + it 'detects the correct version for a java 7 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java7_application' do + + expect(component.detect_compiled_version Pathname.new('spec/fixtures/jre_java7_application')).to eq(33) + end + + it 'detects the correct version for a java 6 app', + cache_fixture: 'stub-java.tar.gz', + app_fixture: 'jre_java6_application' do + + expect(component.detect_compiled_version Pathname.new('spec/fixtures/jre_java6_application')).to eq(32) + end + + class StubOracleJRE < JavaBuildpack::Jre::OracleJRE + + public :detect_compiled_version + + end + + end + + end + end