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.
@@ -3,45 +3,122 @@ module Trinidad
3
3
  attr_accessor :configuration
4
4
  end
5
5
 
6
- def self.configure(options = {})
7
- self.configuration ||= Configuration.new(options)
8
- yield self.configuration if block_given?
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
- attr_accessor :port, :address, :environment, :context_path, :libs_dir, :classes_dir,
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
- @environment = 'development'
20
- @context_path = '/'
21
- @libs_dir = 'lib'
22
- @classes_dir = 'classes'
23
- @default_web_xml = 'config/web.xml'
24
- @port = 3000
25
- @jruby_min_runtimes = 1
26
- @jruby_max_runtimes = 5
27
- @address = 'localhost'
28
- @log = 'INFO'
29
- @trap = true
30
-
31
- options.symbolize!.each {|k, v| self[k] = v}
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
- respond_to?(name) ? send(name) : nil
52
+ def [](name)
53
+ @config[name.to_sym]
36
54
  end
37
55
 
38
56
  def []=(name, value)
39
- send :"#{name}=", value if respond_to?(:"#{name}=")
57
+ @config[name.to_sym] = value
40
58
  end
41
59
 
42
60
  def has_key?(name)
43
- instance_variable_defined?(name) rescue false
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,13 @@
1
+ module Trinidad
2
+ module Lifecycle
3
+ # Restarts the very same context on reloads, request processing pauses.
4
+ class Host::RestartReload
5
+
6
+ def reload!(app_holder)
7
+ app_holder.context.reload
8
+ true # release the lock
9
+ end
10
+
11
+ end
12
+ end
13
+ 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