Skip to content

Commit

Permalink
Redis-based Session Replication
Browse files Browse the repository at this point in the history
Previously, all Tomcat Sessions existed only in memory.  In the case
that a specific instance failed, the data in the Sessions hosted by
that instance would be lost.  In some circumstances this isn't ideal,
so Sessions should be replicated to an external repository.

This change adds support for persisting Sessions to a Redis instance.
It does this by using the RedisStore Tomcat PersistentManager Store
implementation.

As part of this effort the Tomcat container was refactored (it was
already too complicated) and a ModularComponent base type was created.
The ModularComponent allows enables a component to be composed of
multiple "sub-components" and coordinates the component lifecycle
across all of them.

[#66942528]
  • Loading branch information
nebhale committed Mar 6, 2014
1 parent 46989e8 commit f00b00c
Show file tree
Hide file tree
Showing 40 changed files with 1,261 additions and 442 deletions.
3 changes: 3 additions & 0 deletions .idea/dictionaries/bhale.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .idea/runConfigurations/Without_Integration_Tests.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ GEM
diff-lcs (1.2.5)
docile (1.1.3)
json (1.8.1)
multi_json (1.8.4)
parser (2.1.5)
multi_json (1.9.0)
parser (2.1.7)
ast (~> 1.1)
slop (~> 3.4, >= 3.4.5)
powerpack (0.0.9)
Expand Down
11 changes: 9 additions & 2 deletions config/tomcat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@

# Configuration for the Tomcat container
---
version: 7.0.+
repository_root: "{default.repository.root}/tomcat"
tomcat:
version: 7.0.+
repository_root: "{default.repository.root}/tomcat"
lifecycle_support:
version: 2.+
repository_root: "{default.repository.root}/tomcat-lifecycle-support"
logging_support:
version: 2.+
repository_root: "{default.repository.root}/tomcat-logging-support"
redis_store:
version: 1.+
repository_root: "{default.repository.root}/redis-store"
database: 0
timeout: 2000
connection_pool_size: 2
23 changes: 19 additions & 4 deletions docs/container-tomcat.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The Tomcat Container allows servlet 2 and 3 web applications to be run. These a
</tr>
<tr>
<td><strong>Tags</strong></td>
<td><tt>tomcat=&lang;version&rang;</tt>, <tt>tomcat-buildpack-support=&lang;version&rang;</tt></td>
<td><tt>tomcat-instance=&lang;version&rang;</tt>, <tt>tomcat-lifecycle-support=&lang;version&rang;</tt>, <tt>tomcat-logging-support=&lang;version&rang;</tt> <tt>tomcat-redis-store=&lang;version&rang;</tt> <i>(optional)</i></td>
</tr>
</table>
Tags are printed to standard output by the buildpack detect script
Expand All @@ -21,8 +21,24 @@ The container can be configured by modifying the [`config/tomcat.yml`][] file.

| Name | Description
| ---- | -----------
| `repository_root` | The URL of the Tomcat repository index ([details][repositories]).
| `version` | The version of Tomcat to use. Candidate versions can be found in [this listing][].
| `tomcat.repository_root` | The URL of the Tomcat repository index ([details][repositories]).
| `tomcat.version` | The version of Tomcat to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat/index.yml).
| `lifecycle_support.repository_root` | The URL of the Tomcat Lifecycle Support repository index ([details][repositories]).
| `lifecycle_support.version` | The version of Tomcat Lifecycle Support to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat-lifecycle-support/index.yml).
| `logging_support.repository_root` | The URL of the Tomcat Logging Support repository index ([details][repositories]).
| `logging_support.version` | The version of Tomcat Logging Support to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/tomcat-logging-support/index.yml).
| `redis_store.repository_root` | The URL of the Redis Store repository index ([details][repositories]).
| `redis_store.version` | The version of Redis Store to use. Candidate versions can be found in [this listing](http://download.pivotal.io.s3.amazonaws.com/redis-store/index.yml).
| `redis_store.database` | The Redis database to connect to.
| `redis_store.timeout` | The Redis connection timeout (in milliseconds).
| `redis_store.connection_pool_size` | The Redis connection pool size. Note that this is per-instance, not per-application.

## Session Replication
By default, the Tomcat instance is configured to store all Sessions and their data in memory. Under certain cirmcumstances it my be appropriate to persist the Sessions and their data to a repository. When this is the case (small amounts of data that should survive the failure of any individual instance), the buildpack can automatically configure Tomcat to do so.

### Redis
To enable Redis-based session replication, simply bind a Redis service containing a name, label, or tag that has `session-replication` as a substring.


## Supporting Functionality
Additional supporting functionality can be found in the [`java-buildpack-support`][] Git repository.
Expand All @@ -33,5 +49,4 @@ Additional supporting functionality can be found in the [`java-buildpack-support
[repositories]: extending-repositories.md
[Spring profiles]:http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/
[`SPRING_PROFILES_ACTIVE`]: http://docs.spring.io/spring/docs/4.0.0.RELEASE/javadoc-api/org/springframework/core/env/AbstractEnvironment.html#ACTIVE_PROFILES_PROPERTY_NAME
[this listing]: http://download.pivotal.io.s3.amazonaws.com/tomcat/index.yml
[version syntax]: extending-repositories.md#version-syntax-and-ordering
43 changes: 43 additions & 0 deletions docs/extending-modular_component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# `JavaBuildpack::Component::ModularComponent`
This base class is recommended for use by any component that is sufficiently complex to need modularization. It enables a component to be composed of multiple "sub-components" and coordinates the component lifecycle across all of them.

## Required Method Implementations

```ruby
# The command for this component
#
# @return [void, String] components other than containers are not expected to return any value. Container
# components are expected to return the command required to run the application.
def command

# The modules that make up this component
#
# @param [Hash] context the context of the component
# @return [Array<BaseComponent>] a collection of +BaseComponent+s that make up the modules of this component
def modules(context)

# Whether or not this component supports this application
#
# @return [Boolean] whether or not this component supports this application
def supports?
```

## Exposed Instance Variables

| Name | Type
| ---- | ----
| `@modules` | [`Array<JavaBuildpack::Component::BaseComponent>`][]


## Helper Methods

```ruby
# Returns a copy of the context, but with a subset of the original configuration
#
# @param [Hash] context the original context of the component
# @param [String] key the key to get a subset of the context from
# @return [Hash] context a copy of the original context, but with a subset of the original configuration
def sub_configuration_context(context, key)
```

[`Array<JavaBuildpack::Component::BaseComponent>`]: extending-base_component.md
7 changes: 7 additions & 0 deletions docs/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ The buildpack provides a collection of base classes that may help you implement
### [`JavaBuildpack::Component::BaseComponent`][]
This base class is recommended for use by all components. It ensures that each component has a name, and that the contents of the context are exposed as instance variables (e.g. `context[:application]` is available as `@application`). In addition it provides two helper methods for downloading files as part of the component's operation.

### [`JavaBuildpack::Component::ModularComponent`][]
This base class is recommended for use by any component that is sufficiently complex to need modularization. It enables a component to be composed of multiple "sub-components" and coordinates the component lifecycle across all of them.

### [`JavaBuildpack::Component::VersionedDependencyComponent`][]
This base class is recommended for use by any component that uses the buildpack [repository support][] to download a dependency. It ensures that each component has a `@version` and `@uri` that were resolved from the repository specified in the component's configuration. It also implements the `detect` method with a standard implementation.

Expand All @@ -63,13 +66,17 @@ The following example components are relatively simple and good for copying as t
### Java Main Class Container
The [Java Main Class Container](container-java_main.md) ([`lib/java_buildpack/container/java_main.rb`](../lib/java_buildpack/container/main.rb)) extends the [`JavaBuildpack::Component::BaseComponent`](../lib/java_buildpack/component/base_component.rb) base class described above.

### Tomcat Container
The [Tomcat Container](container-tomcat.md) ([`lib/java_buildpack/container/tomcat.rb`](../lib/java_buildpack/container/tomcat.rb)) extends the [`JavaBuildpack::Component::ModularComponent`](../lib/java_buildpack/component/modular_component.rb) base class described above.

### Spring Boot CLI Container
The [Spring Boot CLI Container](container-spring_boot_cli.md) ([`lib/java_buildpack/container/spring_boot_cli.rb`](../lib/java_buildpack/container/spring_boot_cli.rb)) extends the [`JavaBuildpack::Component::VersionedDependencyComponent`](../lib/java_buildpack/component/versioned_dependency_component.rb) base class described above.

[`config/components.yml`]: ../config/components.yml
[`JavaBuildpack::Component::Application`]: extending-application.md
[`JavaBuildpack::Component::BaseComponent`]: extending-base_component.md
[`JavaBuildpack::Component::Droplet`]: extending-droplet.md
[`JavaBuildpack::Component::ModularComponent`]: extending-modular_component.md
[`JavaBuildpack::Component::VersionedDependencyComponent`]: extending-versioned_dependency_component.md
[`lib/java_buildpack/container`]: ../lib/java_buildpack/container
[`lib/java_buildpack/framework`]: ../lib/java_buildpack/framework
Expand Down
4 changes: 2 additions & 2 deletions java-buildpack.iml
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.2.5, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="docile (v1.1.3, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="json (v1.8.1, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="multi_json (v1.8.4, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="parser (v2.1.5, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="multi_json (v1.9.0, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="parser (v2.1.7, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="powerpack (v0.0.9, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="rainbow (v2.0.0, rbenv: 1.9.3-p545) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="rake (v10.1.1, rbenv: 1.9.3-p545) [gem]" level="application" />
Expand Down
99 changes: 99 additions & 0 deletions lib/java_buildpack/component/modular_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013 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 'fileutils'
require 'java_buildpack/component'
require 'java_buildpack/component/base_component'
require 'java_buildpack/repository/configured_item'
require 'java_buildpack/util/dash_case'
require 'tmpdir'

module JavaBuildpack::Component

# A convenience base class for all components that are built modularly. In addition to the functionality inherited
# from +BaseComponent+ this class also ensures that the collection of modules are iterated over for each lifecycle
# event.
class ModularComponent < BaseComponent

# Creates an instance. In addition to the functionality inherited from +BaseComponent+, a +@sub_components+
# instance variable is exposed.
#
# @param [Hash] context a collection of utilities used by components
# @param [Block, nil] version_validator an optional version validation block
def initialize(context, &version_validator)
super(context)
@sub_components = supports? ? sub_components(context) : []
end

# @macro base_component_detect
def detect
supports? ? @sub_components.map { |m| m.detect }.flatten.compact : nil
end

# @macro base_component_compile
def compile
@sub_components.each { |m| m.compile }
end

# @macro base_component_release
def release
@sub_components.map { |m| m.release }
command
end

protected

# @!macro [new] modular_component_command
# The command for this component
#
# @return [void, String] components other than containers are not expected to return any value. Container
# components are expected to return the command required to run the application.
def command
fail "Method 'command' must be defined"
end

# @!macro [new] modular_component_sub_components
# The sub_components that make up this component
#
# @param [Hash] context the context of the component
# @return [Array<BaseComponent>] a collection of +BaseComponent+s that make up the sub_components of this
# component
def sub_components(context)
fail "Method 'sub_components' must be defined"
end

# Returns a copy of the context, but with a subset of the original configuration
#
# @param [Hash] context the original context of the component
# @param [String] key the key to get a subset of the context from
# @return [Hash] context a copy of the original context, but with a subset of the original configuration
def sub_configuration_context(context, key)
c = context.clone
c[:configuration] = context[:configuration][key]
c
end

# @!macro [new] modular_component_supports
# Whether or not this component supports this application
#
# @return [Boolean] whether or not this component supports this application
def supports?
fail "Method 'supports?' must be defined"
end

end

end
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ def initialize(context, &version_validator)
end
end

# If the component should be used when staging an application
#
# @return [Array<String>, String, nil] If the component should be used when staging the application, a +String+ or
# an +Array<String>+ that uniquely identifies the component (e.g.
# +openjdk=1.7.0_40+). Otherwise, +nil+.
# @macro base_component_detect
def detect
@version ? id(@version) : nil
end
Expand Down
Loading

0 comments on commit f00b00c

Please sign in to comment.