trinidad 1.3.5 → 1.4.0.RC
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +8 -2
- data/History.txt +43 -0
- data/LICENSE +3 -2
- data/README.md +121 -51
- data/Rakefile +2 -3
- data/lib/trinidad.rb +6 -10
- data/lib/trinidad/command_line_parser.rb +65 -65
- data/lib/trinidad/configuration.rb +103 -26
- data/lib/trinidad/lifecycle/base.rb +70 -0
- data/lib/trinidad/lifecycle/host.rb +87 -0
- data/lib/trinidad/lifecycle/host/restart_reload.rb +13 -0
- data/lib/trinidad/lifecycle/host/rolling_reload.rb +72 -0
- data/lib/trinidad/lifecycle/web_app/default.rb +94 -0
- data/lib/trinidad/lifecycle/web_app/shared.rb +53 -0
- data/lib/trinidad/lifecycle/web_app/war.rb +52 -0
- data/lib/trinidad/logging.rb +282 -0
- data/lib/trinidad/server.rb +142 -149
- data/lib/trinidad/version.rb +1 -1
- data/lib/trinidad/web_app.rb +278 -106
- data/rakelib/tomcat.rake +41 -10
- data/src/java/org/apache/juli/FileHandler.java +401 -0
- data/trinidad.gemspec +5 -9
- metadata +29 -19
- data/lib/trinidad/core_ext.rb +0 -42
- data/lib/trinidad/lifecycle/lifecycle_listener_base.rb +0 -88
- data/lib/trinidad/lifecycle/lifecycle_listener_default.rb +0 -84
- data/lib/trinidad/lifecycle/lifecycle_listener_host.rb +0 -81
- data/lib/trinidad/lifecycle/lifecycle_listener_war.rb +0 -43
- data/lib/trinidad/lifecycle/takeover.rb +0 -25
- data/lib/trinidad/log_formatter.rb +0 -18
- data/lib/trinidad/rackup_web_app.rb +0 -16
- data/lib/trinidad/rails_web_app.rb +0 -13
- data/lib/trinidad/war_web_app.rb +0 -19
@@ -3,45 +3,122 @@ module Trinidad
|
|
3
3
|
attr_accessor :configuration
|
4
4
|
end
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
self.configuration
|
6
|
+
# Creates a new global configuration (unless already exists) and
|
7
|
+
# (deep) merges the current values using the provided options.
|
8
|
+
def self.configure(*args)
|
9
|
+
config = ( self.configuration ||= Configuration.new )
|
10
|
+
args.compact!
|
11
|
+
if options = args.shift
|
12
|
+
options = Trinidad::Configuration.symbolize_options(options)
|
13
|
+
args.each do |opts|
|
14
|
+
opts = Trinidad::Configuration.symbolize_options(opts)
|
15
|
+
options = Trinidad::Configuration.merge_options(options, opts)
|
16
|
+
end
|
17
|
+
config.update!(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
yield config if block_given?
|
21
|
+
config
|
10
22
|
end
|
11
23
|
|
24
|
+
# Forces a new global configuration using default and the provided options.
|
25
|
+
def self.configure!(*args, &block)
|
26
|
+
self.configuration = Configuration.new
|
27
|
+
configure(*args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Trinidad's (global) configuration instance.
|
31
|
+
# Use Trinidad#configure to update and obtain the global instance or access
|
32
|
+
# the instance using Trinidad#configuration
|
12
33
|
class Configuration
|
13
|
-
|
14
|
-
:default_web_xml, :log, :jruby_min_runtimes, :jruby_max_runtimes,
|
15
|
-
:monitor, :http, :ajp, :ssl, :extensions, :apps_base, :web_apps, :web_app_dir,
|
16
|
-
:trap, :rackup, :servlet, :public, :hosts
|
17
|
-
|
34
|
+
|
18
35
|
def initialize(options = {})
|
19
|
-
@
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
@config = {
|
37
|
+
:port => 3000,
|
38
|
+
:address => 'localhost',
|
39
|
+
:environment => 'development',
|
40
|
+
:context_path => '/',
|
41
|
+
:libs_dir => 'lib',
|
42
|
+
:classes_dir => 'classes',
|
43
|
+
:default_web_xml => 'config/web.xml',
|
44
|
+
:jruby_min_runtimes => 1,
|
45
|
+
:jruby_max_runtimes => 5,
|
46
|
+
:log => 'INFO',
|
47
|
+
:trap => true
|
48
|
+
}
|
49
|
+
update!(options)
|
32
50
|
end
|
33
51
|
|
34
|
-
def [](name)
|
35
|
-
|
52
|
+
def [](name)
|
53
|
+
@config[name.to_sym]
|
36
54
|
end
|
37
55
|
|
38
56
|
def []=(name, value)
|
39
|
-
|
57
|
+
@config[name.to_sym] = value
|
40
58
|
end
|
41
59
|
|
42
60
|
def has_key?(name)
|
43
|
-
|
61
|
+
@config.has_key?(name.to_sym)
|
44
62
|
end
|
45
63
|
alias_method :key?, :has_key?
|
64
|
+
|
65
|
+
def keys
|
66
|
+
@config.keys
|
67
|
+
end
|
68
|
+
|
69
|
+
def each(&block)
|
70
|
+
@config.each(&block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def update!(options)
|
74
|
+
options.each do |key, value|
|
75
|
+
self[key] = value.respond_to?(:strip) ? value.strip : value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
%w{ port address environment context_path
|
80
|
+
libs_dir classes_dir default_web_xml
|
81
|
+
jruby_min_runtimes jruby_max_runtimes
|
82
|
+
rackup servlet public hosts
|
83
|
+
http ajp ssl extensions
|
84
|
+
apps_base web_apps web_app_dir
|
85
|
+
monitor log trap }.each do |method|
|
86
|
+
class_eval "def #{method}; self[:'#{method}']; end"
|
87
|
+
class_eval "def #{method}=(value); self[:'#{method}'] = value; end"
|
88
|
+
end
|
89
|
+
|
90
|
+
# a Hash like #symbolize helper
|
91
|
+
def self.symbolize_options(options, deep = true)
|
92
|
+
new_options = options.class.new
|
93
|
+
options.each do |key, value|
|
94
|
+
if deep && options_like?(value)
|
95
|
+
new_options[key.to_sym] = symbolize_options(value, deep)
|
96
|
+
else
|
97
|
+
new_options[key.to_sym] = value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
new_options
|
101
|
+
end
|
102
|
+
|
103
|
+
# a Hash like deep_merge helper
|
104
|
+
def self.merge_options(target, current, deep = true)
|
105
|
+
target_dup = target.dup
|
106
|
+
current.keys.each do |key|
|
107
|
+
target_dup[key] =
|
108
|
+
if deep && options_like?(target[key]) && options_like?(current[key])
|
109
|
+
merge_options(target[key], current[key], deep)
|
110
|
+
else
|
111
|
+
current[key]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
target_dup
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def self.options_like?(object)
|
119
|
+
object.is_a?(Hash) ||
|
120
|
+
( object.respond_to?(:keys) && object.respond_to?(:'[]') )
|
121
|
+
end
|
122
|
+
|
46
123
|
end
|
47
124
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Lifecycle
|
3
|
+
# Trinidad lifecycle listener (generic) base class.
|
4
|
+
# Allows hooking into the container's lifecycle using the provided methods.
|
5
|
+
class Base
|
6
|
+
|
7
|
+
include Trinidad::Tomcat::LifecycleListener
|
8
|
+
|
9
|
+
# The base implementation simply routes events to correspondig methods.
|
10
|
+
#
|
11
|
+
# http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/Lifecycle.html
|
12
|
+
# http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/LifecycleListener.html
|
13
|
+
def lifecycleEvent(event)
|
14
|
+
events = Trinidad::Tomcat::Lifecycle
|
15
|
+
case event.type
|
16
|
+
when events::BEFORE_INIT_EVENT then
|
17
|
+
before_init(event)
|
18
|
+
when events::AFTER_INIT_EVENT then
|
19
|
+
after_init(event)
|
20
|
+
when events::CONFIGURE_START_EVENT then
|
21
|
+
configure_start(event)
|
22
|
+
when events::CONFIGURE_STOP_EVENT then
|
23
|
+
configure_stop(event)
|
24
|
+
when events::BEFORE_START_EVENT then
|
25
|
+
before_start(event)
|
26
|
+
when events::START_EVENT then
|
27
|
+
start(event)
|
28
|
+
when events::AFTER_START_EVENT then
|
29
|
+
after_start(event)
|
30
|
+
when events::BEFORE_STOP_EVENT then
|
31
|
+
before_stop(event)
|
32
|
+
when events::STOP_EVENT then
|
33
|
+
stop(event)
|
34
|
+
when events::AFTER_STOP_EVENT then
|
35
|
+
after_stop(event)
|
36
|
+
when events::BEFORE_DESTROY_EVENT then
|
37
|
+
before_destroy(event)
|
38
|
+
when events::AFTER_DESTROY_EVENT then
|
39
|
+
after_destroy(event)
|
40
|
+
when events::PERIODIC_EVENT then
|
41
|
+
periodic(event)
|
42
|
+
else
|
43
|
+
raise "unsupported event.type = #{event.type}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Event hook methods for a more Ruby-ish API :
|
48
|
+
|
49
|
+
def before_init(event); end
|
50
|
+
def after_init(event); end
|
51
|
+
|
52
|
+
def configure_start(event); end
|
53
|
+
def configure_stop(event); end
|
54
|
+
|
55
|
+
def before_start(event); end
|
56
|
+
def start(event); end
|
57
|
+
def after_start(event); end
|
58
|
+
|
59
|
+
def before_stop(event); end
|
60
|
+
def stop(event); end
|
61
|
+
def after_stop(event); end
|
62
|
+
|
63
|
+
def before_destroy(event); end
|
64
|
+
def after_destroy(event); end
|
65
|
+
|
66
|
+
def periodic(event); end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Lifecycle
|
3
|
+
# A host lifecycle listener - monitors deployed web apps.
|
4
|
+
class Host < Base
|
5
|
+
|
6
|
+
attr_reader :server, :app_holders
|
7
|
+
# @deprecated (<= 1.3.5)
|
8
|
+
alias_method :contexts, :app_holders
|
9
|
+
|
10
|
+
# #server current server instance
|
11
|
+
# #app_holders deployed web application holders
|
12
|
+
def initialize(server, *app_holders)
|
13
|
+
app_holders.map! do |app_holder|
|
14
|
+
if app_holder.is_a?(Hash) # backwards compatibility
|
15
|
+
Trinidad::WebApp::Holder.new(app_holder[:app], app_holder[:context])
|
16
|
+
else
|
17
|
+
app_holder
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@server, @app_holders = server, app_holders
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Trinidad::Lifecycle::Base#before_start
|
24
|
+
def before_start(event)
|
25
|
+
init_monitors
|
26
|
+
end
|
27
|
+
|
28
|
+
# @see Trinidad::Lifecycle::Base#periodic
|
29
|
+
def periodic(event)
|
30
|
+
check_monitors
|
31
|
+
end
|
32
|
+
|
33
|
+
def tomcat; @server.tomcat; end # for backwards compatibility
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def init_monitors
|
38
|
+
app_holders.each do |app_holder|
|
39
|
+
monitor = app_holder.monitor
|
40
|
+
opts = 'w+'
|
41
|
+
if ! File.exist?(dir = File.dirname(monitor))
|
42
|
+
Dir.mkdir dir
|
43
|
+
elsif File.exist?(monitor)
|
44
|
+
opts = 'r'
|
45
|
+
end
|
46
|
+
File.open(monitor, opts) do |file|
|
47
|
+
app_holder.monitor_mtime = file.mtime
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_monitors
|
53
|
+
app_holders.each do |app_holder|
|
54
|
+
# double check monitor, capistrano removes it temporarily
|
55
|
+
unless File.exist?(monitor = app_holder.monitor)
|
56
|
+
sleep(0.5)
|
57
|
+
next unless File.exist?(monitor)
|
58
|
+
end
|
59
|
+
|
60
|
+
mtime = File.mtime(monitor)
|
61
|
+
if mtime > app_holder.monitor_mtime && app_holder.try_lock
|
62
|
+
app_holder.monitor_mtime = mtime
|
63
|
+
app_holder.unlock if reload_application!(app_holder)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
autoload :RestartReload, 'trinidad/lifecycle/host/restart_reload'
|
69
|
+
autoload :RollingReload, 'trinidad/lifecycle/host/rolling_reload'
|
70
|
+
|
71
|
+
RELOAD_STRATEGIES = {
|
72
|
+
:default => :RestartReload,
|
73
|
+
:restart => :RestartReload,
|
74
|
+
:rolling => :RollingReload,
|
75
|
+
}
|
76
|
+
|
77
|
+
def reload_application!(app_holder)
|
78
|
+
strategy = (app_holder.web_app.reload_strategy || :default).to_sym
|
79
|
+
strategy = RELOAD_STRATEGIES[ strategy ]
|
80
|
+
strategy = strategy ? self.class.const_get(strategy) : RestartReload
|
81
|
+
strategy.instance_method(:initialize).arity != 0 ?
|
82
|
+
strategy.new(server).reload!(app_holder) : strategy.new.reload!(app_holder)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Trinidad
|
4
|
+
module Lifecycle
|
5
|
+
# Rolls a new context that replaces the current one on reloads.
|
6
|
+
class Host::RollingReload
|
7
|
+
|
8
|
+
def initialize(server)
|
9
|
+
@server = server
|
10
|
+
end
|
11
|
+
|
12
|
+
def reload!(app_holder)
|
13
|
+
web_app, old_context = app_holder.web_app, app_holder.context
|
14
|
+
logger = self.class.logger
|
15
|
+
logger.info "Context with name [#{old_context.name}] has started rolling"
|
16
|
+
|
17
|
+
web_app.reset! # force a new class loader + re-read state (from config)
|
18
|
+
no_host = org.apache.catalina.Host.impl {} # do not add to parent yet
|
19
|
+
new_context = @server.add_web_app(web_app, no_host)
|
20
|
+
new_context.add_lifecycle_listener(Takeover.new(old_context))
|
21
|
+
# Tomcat requires us to have unique names for its containers :
|
22
|
+
new_context.name = "#{old_context.name}-#{java.lang.System.currentTimeMillis}"
|
23
|
+
|
24
|
+
app_holder.context = new_context
|
25
|
+
|
26
|
+
Thread.new do
|
27
|
+
begin
|
28
|
+
logger.debug "Starting a new Context for [#{new_context.path}]"
|
29
|
+
old_context.parent.add_child new_context # NOTE: likely starts!
|
30
|
+
new_context.start unless new_context.state_name =~ /START/i
|
31
|
+
logger.info "Context with name [#{old_context.name}] has completed rolling"
|
32
|
+
ensure
|
33
|
+
app_holder.unlock
|
34
|
+
end
|
35
|
+
end
|
36
|
+
false # not yet reloaded do not release lock
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.logger # log into the same location as context.reload does :
|
40
|
+
Trinidad::Logging::LogFactory.getLog('org.apache.catalina.core.StandardContext')
|
41
|
+
end
|
42
|
+
|
43
|
+
class Takeover < Trinidad::Lifecycle::Base # :nodoc
|
44
|
+
|
45
|
+
def initialize(context)
|
46
|
+
@old_context = context
|
47
|
+
end
|
48
|
+
|
49
|
+
def after_start(event)
|
50
|
+
new_context = event.lifecycle
|
51
|
+
new_context.remove_lifecycle_listener(self) # GC old context
|
52
|
+
|
53
|
+
logger.debug "Stoping the old Context for [#{@old_context.path}]"
|
54
|
+
|
55
|
+
@old_context.stop
|
56
|
+
@old_context.destroy
|
57
|
+
# NOTE: name might not be changed once added to a parent
|
58
|
+
new_context.name = @old_context.name
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def logger
|
65
|
+
Trinidad::Lifecycle::Host::RollingReload.logger
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Lifecycle
|
3
|
+
module WebApp
|
4
|
+
class Default < Lifecycle::Base
|
5
|
+
include Shared
|
6
|
+
|
7
|
+
def configure(context)
|
8
|
+
super
|
9
|
+
deployment_descriptor = configure_deployment_descriptor(context)
|
10
|
+
unless deployment_descriptor
|
11
|
+
configure_rack_servlet(context)
|
12
|
+
configure_rack_listener(context)
|
13
|
+
end
|
14
|
+
configure_context_params(context)
|
15
|
+
configure_context_loader(context)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def configure_deployment_descriptor(context)
|
21
|
+
if descriptor = web_app.deployment_descriptor
|
22
|
+
listeners = context.findLifecycleListeners
|
23
|
+
context_config = listeners && listeners.find do |listener|
|
24
|
+
listener.is_a?(Trinidad::Tomcat::ContextConfig)
|
25
|
+
end
|
26
|
+
|
27
|
+
unless context_config
|
28
|
+
context_config = Trinidad::Tomcat::ContextConfig.new
|
29
|
+
context.addLifecycleListener(context_config)
|
30
|
+
end
|
31
|
+
|
32
|
+
context_config.setDefaultWebXml(descriptor)
|
33
|
+
end
|
34
|
+
descriptor
|
35
|
+
end
|
36
|
+
|
37
|
+
def configure_rack_servlet(context)
|
38
|
+
wrapper = context.create_wrapper
|
39
|
+
if web_app.rack_servlet[:instance]
|
40
|
+
wrapper.servlet = web_app.rack_servlet[:instance]
|
41
|
+
else
|
42
|
+
wrapper.servlet_class = web_app.rack_servlet[:class]
|
43
|
+
wrapper.async_supported = web_app.rack_servlet[:async_supported]
|
44
|
+
end
|
45
|
+
wrapper.name = web_app.rack_servlet[:name]
|
46
|
+
|
47
|
+
context.add_child(wrapper)
|
48
|
+
context.add_servlet_mapping('/*', wrapper.name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure_rack_listener(context)
|
52
|
+
context.addApplicationListener(web_app.rack_listener) unless web_app.rack_servlet[:instance]
|
53
|
+
end
|
54
|
+
|
55
|
+
def configure_context_params(context)
|
56
|
+
web_app.context_params.each do |name, value|
|
57
|
+
context.addParameter(name, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# @deprecated use {#configure_context_params}
|
61
|
+
alias_method :configure_init_params, :configure_context_params
|
62
|
+
|
63
|
+
def configure_context_loader(context)
|
64
|
+
class_loader = web_app.class_loader
|
65
|
+
|
66
|
+
add_application_jars(class_loader)
|
67
|
+
add_application_java_classes(class_loader)
|
68
|
+
|
69
|
+
loader = Trinidad::Tomcat::WebappLoader.new(class_loader)
|
70
|
+
context.loader = loader # does loader.container = context
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_application_jars(class_loader)
|
74
|
+
return unless web_app.libs_dir
|
75
|
+
|
76
|
+
resources_dir = File.join(web_app.web_app_dir, web_app.libs_dir, '**', '*.jar')
|
77
|
+
|
78
|
+
Dir[resources_dir].each do |resource|
|
79
|
+
class_loader.addURL(java.io.File.new(resource).to_url)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_application_java_classes(class_loader)
|
84
|
+
return unless web_app.classes_dir
|
85
|
+
|
86
|
+
resources_dir = File.join(web_app.web_app_dir, web_app.classes_dir)
|
87
|
+
class_loader.addURL(java.io.File.new(resources_dir).to_url)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
Default = Trinidad::Lifecycle::WebApp::Default # backwards compatibility
|
93
|
+
end
|
94
|
+
end
|