diff --git a/.idea/dictionaries/bhale.xml b/.idea/dictionaries/bhale.xml
index 7af11507f1..e3ff95b37b 100644
--- a/.idea/dictionaries/bhale.xml
+++ b/.idea/dictionaries/bhale.xml
@@ -6,7 +6,6 @@
appdynamics
applicationid
argv
- aspectsecurity
atpack
bootclasspath
buildpack
@@ -100,7 +99,6 @@
stubjre
submodules
tasklib
- teamserver
tenanttoken
tmpdir
tokenized
diff --git a/.idea/runConfigurations/versions__JSON_.xml b/.idea/runConfigurations/versions__JSON_.xml
new file mode 100644
index 0000000000..1d3402880a
--- /dev/null
+++ b/.idea/runConfigurations/versions__JSON_.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/tomcat.yml b/config/tomcat.yml
index e1ff5a78a9..fa12afd1fb 100644
--- a/config/tomcat.yml
+++ b/config/tomcat.yml
@@ -39,3 +39,6 @@ redis_store:
database: 0
timeout: 2000
connection_pool_size: 2
+geode_store:
+ version: 0.+
+ repository_root: "https://tomcat-javabuildpack-spike.s3.amazonaws.com/geode-store"
\ No newline at end of file
diff --git a/lib/java_buildpack/container/tomcat.rb b/lib/java_buildpack/container/tomcat.rb
index e81475b89a..8138fe6864 100644
--- a/lib/java_buildpack/container/tomcat.rb
+++ b/lib/java_buildpack/container/tomcat.rb
@@ -15,12 +15,13 @@
require 'java_buildpack/component/modular_component'
require 'java_buildpack/container'
+require 'java_buildpack/container/tomcat/tomcat_access_logging_support'
+require 'java_buildpack/container/tomcat/tomcat_external_configuration'
+require 'java_buildpack/container/tomcat/tomcat_geode_store'
require 'java_buildpack/container/tomcat/tomcat_insight_support'
require 'java_buildpack/container/tomcat/tomcat_instance'
-require 'java_buildpack/container/tomcat/tomcat_external_configuration'
require 'java_buildpack/container/tomcat/tomcat_lifecycle_support'
require 'java_buildpack/container/tomcat/tomcat_logging_support'
-require 'java_buildpack/container/tomcat/tomcat_access_logging_support'
require 'java_buildpack/container/tomcat/tomcat_redis_store'
require 'java_buildpack/util/java_main_utils'
@@ -50,9 +51,10 @@ def command
def sub_components(context)
components = [
TomcatInstance.new(sub_configuration_context(context, 'tomcat')),
+ TomcatAccessLoggingSupport.new(sub_configuration_context(context, 'access_logging_support')),
+ TomcatGeodeStore.new(sub_configuration_context(context, 'geode_store')),
TomcatLifecycleSupport.new(sub_configuration_context(context, 'lifecycle_support')),
TomcatLoggingSupport.new(sub_configuration_context(context, 'logging_support')),
- TomcatAccessLoggingSupport.new(sub_configuration_context(context, 'access_logging_support')),
TomcatRedisStore.new(sub_configuration_context(context, 'redis_store')),
TomcatInsightSupport.new(context)
]
diff --git a/lib/java_buildpack/container/tomcat/tomcat_geode_store.rb b/lib/java_buildpack/container/tomcat/tomcat_geode_store.rb
new file mode 100644
index 0000000000..b42fab975f
--- /dev/null
+++ b/lib/java_buildpack/container/tomcat/tomcat_geode_store.rb
@@ -0,0 +1,176 @@
+# Cloud Foundry Java Buildpack
+# Copyright 2013-2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'java_buildpack/component/versioned_dependency_component'
+require 'java_buildpack/container'
+require 'java_buildpack/container/tomcat/tomcat_utils'
+require 'java_buildpack/logging/logger_factory'
+
+module JavaBuildpack
+ module Container
+
+ # Encapsulates the detect, compile, and release functionality for Tomcat Redis support.
+ class TomcatGeodeStore < JavaBuildpack::Component::VersionedDependencyComponent
+ include JavaBuildpack::Container
+
+ # (see JavaBuildpack::Component::BaseComponent#compile)
+ def compile
+ return unless supports?
+ download_tar(false, tomcat_lib, tar_name)
+ mutate_context
+ mutate_server
+ create_cache_client_xml
+ end
+
+ # (see JavaBuildpack::Component::BaseComponent#release)
+ def release
+ return unless supports?
+ credentials = @application.services.find_service(FILTER)['credentials']
+ user = credentials[KEY_USERS].find { |u| u['username'] == 'cluster_operator' }
+
+ @droplet.java_opts.add_system_property 'gemfire.security-username', 'cluster_operator'
+ @droplet.java_opts.add_system_property 'gemfire.security-password', user['password']
+ @droplet.java_opts.add_system_property 'gemfire.security-client-auth-init',
+ 'io.pivotal.cloudcache.ClientAuthInitialize.create'
+ end
+
+ protected
+
+ # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?)
+ def supports?
+ @application.services.one_service? FILTER, KEY_LOCATORS, KEY_USERS
+ end
+
+ private
+
+ FILTER = /session-replication/
+ KEY_LOCATORS = 'locators'.freeze
+ KEY_USERS = 'users'.freeze
+
+ SESSION_MANAGER_CLASS_NAME = 'org.apache.geode.modules.session.catalina.Tomcat8DeltaSessionManager'.freeze
+ REGION_ATTRIBUTES_ID = 'PARTITION_REDUNDANT_HEAP_LRU'.freeze
+ CACHE_CLIENT_LISTENER_CLASS_NAME =
+ 'org.apache.geode.modules.session.catalina.ClientServerCacheLifecycleListener'.freeze
+ SCHEMA_URL = 'http://geode.apache.org/schema/cache'.freeze
+ SCHEMA_INSTANCE_URL = 'http://www.w3.org/2001/XMLSchema-instance'.freeze
+ SCHEMA_LOCATION = 'http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd'.freeze
+ LOCATOR_REGEXP = Regexp.new('([^\\[]+)\\[([^\\]]+)\\]').freeze
+ FUNCTION_SERVICE_CLASS_NAMES = [
+ 'org.apache.geode.modules.util.CreateRegionFunction',
+ 'org.apache.geode.modules.util.TouchPartitionedRegionEntriesFunction',
+ 'org.apache.geode.modules.util.TouchReplicatedRegionEntriesFunction',
+ 'org.apache.geode.modules.util.RegionSizeFunction'
+ ].freeze
+
+ private_constant :FILTER, :KEY_LOCATORS, :KEY_USERS, :SESSION_MANAGER_CLASS_NAME, :REGION_ATTRIBUTES_ID,
+ :CACHE_CLIENT_LISTENER_CLASS_NAME, :SCHEMA_URL, :SCHEMA_INSTANCE_URL, :SCHEMA_LOCATION,
+ :LOCATOR_REGEXP, :FUNCTION_SERVICE_CLASS_NAMES
+
+ def add_client_cache(document)
+ client_cache = document.add_element 'client-cache',
+ 'xmlns' => SCHEMA_URL,
+ 'xmlns:xsi' => SCHEMA_INSTANCE_URL,
+ 'xsi:schemaLocation' => SCHEMA_LOCATION,
+ 'version' => '1.0'
+
+ add_pool client_cache
+ add_function_service client_cache
+ end
+
+ def add_functions(function_service)
+ FUNCTION_SERVICE_CLASS_NAMES.each do |function_class_name|
+ function = function_service.add_element 'function'
+ class_name = function.add_element 'class-name'
+ class_name.add_text(function_class_name)
+ end
+ end
+
+ def add_function_service(client_cache)
+ function_service = client_cache.add_element 'function-service'
+ add_functions function_service
+ end
+
+ def add_listener(server)
+ server.add_element 'Listener',
+ 'className' => CACHE_CLIENT_LISTENER_CLASS_NAME
+ end
+
+ def add_locators(pool)
+ service = @application.services.find_service FILTER
+ service['credentials']['locators'].each do |locator|
+ match_info = LOCATOR_REGEXP.match(locator)
+ pool.add_element 'locator',
+ 'host' => match_info[1],
+ 'port' => match_info[2]
+ end
+ end
+
+ def add_manager(context)
+ context.add_element 'Manager',
+ 'className' => SESSION_MANAGER_CLASS_NAME,
+ 'enableLocalCache' => 'true',
+ 'regionAttributesId' => REGION_ATTRIBUTES_ID
+ end
+
+ def add_pool(client_cache)
+ pool = client_cache.add_element 'pool',
+ 'name' => 'sessions',
+ 'subscription-enabled' => 'true'
+ add_locators pool
+ end
+
+ def cache_client_xml
+ 'cache-client.xml'
+ end
+
+ def cache_client_xml_path
+ @droplet.sandbox + 'conf' + cache_client_xml
+ end
+
+ def create_cache_client_xml
+ document = REXML::Document.new('')
+ add_client_cache document
+ write_xml cache_client_xml_path, document
+ end
+
+ def mutate_context
+ puts ' Adding Geode-based Session Replication'
+
+ document = read_xml context_xml
+ context = REXML::XPath.match(document, '/Context').first
+
+ add_manager context
+
+ write_xml context_xml, document
+ end
+
+ def mutate_server
+ document = read_xml server_xml
+
+ server = REXML::XPath.match(document, '/Server').first
+
+ add_listener server
+
+ write_xml server_xml, document
+ end
+
+ def tar_name
+ "geode-store-#{@version}.tar.gz"
+ end
+
+ end
+
+ end
+end
diff --git a/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/context.xml b/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/context.xml
new file mode 100644
index 0000000000..7f96549265
--- /dev/null
+++ b/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/context.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/server.xml b/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/server.xml
new file mode 100644
index 0000000000..925aa05504
--- /dev/null
+++ b/spec/fixtures/container_tomcat_geode_store/.java-buildpack/tomcat/conf/server.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spec/fixtures/container_tomcat_geode_store/WEB-INF/.gitkeep b/spec/fixtures/container_tomcat_geode_store/WEB-INF/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/spec/fixtures/container_tomcat_geode_store_cache_client_after.xml b/spec/fixtures/container_tomcat_geode_store_cache_client_after.xml
new file mode 100644
index 0000000000..b68c118145
--- /dev/null
+++ b/spec/fixtures/container_tomcat_geode_store_cache_client_after.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+ org.apache.geode.modules.util.CreateRegionFunction
+
+
+ org.apache.geode.modules.util.TouchPartitionedRegionEntriesFunction
+
+
+ org.apache.geode.modules.util.TouchReplicatedRegionEntriesFunction
+
+
+ org.apache.geode.modules.util.RegionSizeFunction
+
+
+
diff --git a/spec/fixtures/container_tomcat_geode_store_context_after.xml b/spec/fixtures/container_tomcat_geode_store_context_after.xml
new file mode 100644
index 0000000000..412053d479
--- /dev/null
+++ b/spec/fixtures/container_tomcat_geode_store_context_after.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/spec/fixtures/container_tomcat_geode_store_server_after.xml b/spec/fixtures/container_tomcat_geode_store_server_after.xml
new file mode 100644
index 0000000000..56b8728e04
--- /dev/null
+++ b/spec/fixtures/container_tomcat_geode_store_server_after.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spec/fixtures/stub-geode-store.jar b/spec/fixtures/stub-geode-store.jar
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/spec/fixtures/stub-geode-store.tar b/spec/fixtures/stub-geode-store.tar
new file mode 100644
index 0000000000..816deb94d1
Binary files /dev/null and b/spec/fixtures/stub-geode-store.tar differ
diff --git a/spec/java_buildpack/container/tomcat/tomcat_geode_store_spec.rb b/spec/java_buildpack/container/tomcat/tomcat_geode_store_spec.rb
new file mode 100644
index 0000000000..26dab5a30d
--- /dev/null
+++ b/spec/java_buildpack/container/tomcat/tomcat_geode_store_spec.rb
@@ -0,0 +1,110 @@
+# Cloud Foundry Java Buildpack
+# Copyright 2013-2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'spec_helper'
+require 'component_helper'
+require 'java_buildpack/container/tomcat/tomcat_geode_store'
+
+describe JavaBuildpack::Container::TomcatGeodeStore do
+ include_context 'component_helper'
+
+ let(:component_id) { 'tomcat' }
+
+ let(:configuration) do
+ { 'database' => 'test-database',
+ 'timeout' => 'test-timeout',
+ 'connection_pool_size' => 'test-connection-pool-size' }
+ end
+
+ it 'does not detect without a session-replication service' do
+ expect(component.detect).to be_nil
+ end
+
+ context 'when there is a session-replication service' do
+ before do
+ allow(services).to receive(:one_service?).with(/session-replication/, 'locators', 'users')
+ .and_return(true)
+ allow(services).to receive(:find_service).and_return(
+ 'credentials' => {
+ 'locators' => ['some-locator[some-port]', 'some-other-locator[some-other-port]'],
+ 'users' =>
+ [
+ { 'password' => 'fake-password',
+ 'username' => 'cluster_operator' }
+ ]
+ }
+ )
+
+ end
+
+ it 'detect with a session-replication service' do
+ expect(component.detect).to eq("tomcat-geode-store=#{version}")
+ end
+
+ it 'copies resources',
+ app_fixture: 'container_tomcat_geode_store',
+ cache_fixture: 'stub-geode-store.tar' do
+
+ component.compile
+
+ expect(sandbox + 'lib/stub-geode-store/stub-jar-1.jar').to exist
+ expect(sandbox + 'lib/stub-geode-store/stub-jar-2.jar').to exist
+ end
+
+ it 'mutates context.xml',
+ app_fixture: 'container_tomcat_geode_store',
+ cache_fixture: 'stub-geode-store.tar' do
+
+ component.compile
+
+ expect((sandbox + 'conf/context.xml').read)
+ .to eq(Pathname.new('spec/fixtures/container_tomcat_geode_store_context_after.xml').read)
+ end
+
+ it 'mutates server.xml',
+ app_fixture: 'container_tomcat_geode_store',
+ cache_fixture: 'stub-geode-store.tar' do
+
+ component.compile
+
+ expect((sandbox + 'conf/server.xml').read)
+ .to eq(Pathname.new('spec/fixtures/container_tomcat_geode_store_server_after.xml').read)
+ end
+
+ it 'adds a cache-client.xml',
+ app_fixture: 'container_tomcat_geode_store',
+ cache_fixture: 'stub-geode-store.tar' do
+
+ component.compile
+
+ expect((sandbox + 'conf/cache-client.xml').read)
+ .to eq(Pathname.new('spec/fixtures/container_tomcat_geode_store_cache_client_after.xml').read)
+ end
+
+ it 'passes security properties to the release',
+ app_fixture: 'container_tomcat_geode_store',
+ cache_fixture: 'stub-geode-store.tar' do
+
+ component.release
+
+ expect(java_opts).to include(
+ '-Dgemfire.security-client-auth-init=io.pivotal.cloudcache.ClientAuthInitialize.create'
+ )
+ expect(java_opts).to include('-Dgemfire.security-username=cluster_operator')
+ expect(java_opts).to include('-Dgemfire.security-password=fake-password')
+ end
+
+ end
+end
diff --git a/spec/java_buildpack/container/tomcat_spec.rb b/spec/java_buildpack/container/tomcat_spec.rb
index ee65087d6c..281fab0b86 100644
--- a/spec/java_buildpack/container/tomcat_spec.rb
+++ b/spec/java_buildpack/container/tomcat_spec.rb
@@ -17,11 +17,12 @@
require 'component_helper'
require 'fileutils'
require 'java_buildpack/container/tomcat'
+require 'java_buildpack/container/tomcat/tomcat_access_logging_support'
+require 'java_buildpack/container/tomcat/tomcat_geode_store'
require 'java_buildpack/container/tomcat/tomcat_insight_support'
require 'java_buildpack/container/tomcat/tomcat_instance'
require 'java_buildpack/container/tomcat/tomcat_lifecycle_support'
require 'java_buildpack/container/tomcat/tomcat_logging_support'
-require 'java_buildpack/container/tomcat/tomcat_access_logging_support'
require 'java_buildpack/container/tomcat/tomcat_redis_store'
describe JavaBuildpack::Container::Tomcat do
@@ -30,24 +31,27 @@
let(:component) { StubTomcat.new context }
let(:configuration) do
- { 'tomcat' => tomcat_configuration,
+ { 'access_logging_support' => access_logging_support_configuration,
+ 'external_configuration' => tomcat_external_configuration,
+ 'geode_store' => geode_store_configuration,
'lifecycle_support' => lifecycle_support_configuration,
'logging_support' => logging_support_configuration,
- 'access_logging_support' => access_logging_support_configuration,
'redis_store' => redis_store_configuration,
- 'external_configuration' => tomcat_external_configuration }
+ 'tomcat' => tomcat_configuration }
end
- let(:tomcat_configuration) { { 'external_configuration_enabled' => false } }
+ let(:access_logging_support_configuration) { instance_double('logging-support-configuration') }
let(:lifecycle_support_configuration) { instance_double('lifecycle-support-configuration') }
let(:logging_support_configuration) { instance_double('logging-support-configuration') }
- let(:access_logging_support_configuration) { instance_double('logging-support-configuration') }
+ let(:geode_store_configuration) { instance_double('geode_store_configuration') }
let(:redis_store_configuration) { instance_double('redis-store-configuration') }
+ let(:tomcat_configuration) { { 'external_configuration_enabled' => false } }
+
let(:tomcat_external_configuration) { instance_double('tomcat_external_configuration') }
it 'detects WEB-INF',
@@ -69,17 +73,19 @@
end
it 'creates submodules' do
+ allow(JavaBuildpack::Container::TomcatAccessLoggingSupport)
+ .to receive(:new).with(sub_configuration_context(access_logging_support_configuration))
+ allow(JavaBuildpack::Container::TomcatGeodeStore)
+ .to receive(:new).with(sub_configuration_context(geode_store_configuration))
allow(JavaBuildpack::Container::TomcatInstance)
.to receive(:new).with(sub_configuration_context(tomcat_configuration))
+ allow(JavaBuildpack::Container::TomcatInsightSupport).to receive(:new).with(context)
allow(JavaBuildpack::Container::TomcatLifecycleSupport)
.to receive(:new).with(sub_configuration_context(lifecycle_support_configuration))
allow(JavaBuildpack::Container::TomcatLoggingSupport)
.to receive(:new).with(sub_configuration_context(logging_support_configuration))
- allow(JavaBuildpack::Container::TomcatAccessLoggingSupport)
- .to receive(:new).with(sub_configuration_context(access_logging_support_configuration))
allow(JavaBuildpack::Container::TomcatRedisStore)
.to receive(:new).with(sub_configuration_context(redis_store_configuration))
- allow(JavaBuildpack::Container::TomcatInsightSupport).to receive(:new).with(context)
component.sub_components context
end