lib/trinidad/web_app.rb in trinidad-1.3.5 vs lib/trinidad/web_app.rb in trinidad-1.4.0.RC
- old
+ new
@@ -1,168 +1,340 @@
module Trinidad
class WebApp
- attr_reader :config, :app_config, :class_loader, :servlet
+
+ attr_reader :config, :default_config
- def self.create(config, app_config)
- autodetect_configuration(config, app_config)
-
- war?(app_config) ? WarWebApp.new(config, app_config) :
- rackup?(app_config) ? RackupWebApp.new(config, app_config) : RailsWebApp.new(config, app_config)
+ def self.create(config, default_config = Trinidad.configuration)
+ war?(config, default_config) ? WarWebApp.new(config, default_config) :
+ rackup?(config, default_config) ? RackupWebApp.new(config, default_config) :
+ RailsWebApp.new(config, default_config)
end
- def self.rackup?(app_config)
- app_config.has_key?(:rackup) || !Dir['WEB-INF/**/config.ru'].empty?
+ def initialize(config, default_config)
+ @config, @default_config = config, default_config || {}
+ complete_config!
+ # NOTE: we should maybe @config.freeze here ?!
end
- def self.war?(app_config)
- app_config[:context_path] =~ /\.war$/
+ def [](key)
+ key = key.to_sym
+ config.has_key?(key) ? config[key] : default_config[key]
end
-
- def initialize(config, app_config, servlet_class = 'org.jruby.rack.RackServlet', servlet_name = 'RackServlet')
- @config = config
- @app_config = app_config
-
- generate_class_loader
-
- configure_rack_servlet(servlet_class, servlet_name) unless rack_servlet_configured?
+
+ %w{ context_path web_app_dir libs_dir classes_dir async_supported
+ jruby_min_runtimes jruby_max_runtimes jruby_compat_version rackup log
+ reload_strategy }.each do |method|
+ class_eval "def #{method}; self[:'#{method}']; end"
end
+
+ def web_xml; self[:web_xml] || self[:default_web_xml]; end
+ def default_web_xml; self[:default_web_xml]; end
+
+ def public_root; self[:public] || 'public'; end
+ def environment; self[:environment] || 'development'; end
+ def work_dir; self[:work_dir] || web_app_dir; end
+ def log_dir; self[:log_dir] || File.join(work_dir, 'log'); end
+
+ def extensions
+ @extensions ||= begin
+ extensions = default_config[:extensions] || {}
+ extensions.merge(config[:extensions] || {})
+ end
+ end
+
+ def monitor
+ File.expand_path(self[:monitor] || 'tmp/restart.txt', work_dir)
+ end
- def rack_listener
- context_listener unless rack_listener_configured?
+ def context_params
+ @context_params ||= {}
+ add_context_param 'jruby.min.runtimes', jruby_min_runtimes
+ add_context_param 'jruby.max.runtimes', jruby_max_runtimes
+ add_context_param 'jruby.initial.runtimes', jruby_min_runtimes
+ add_context_param 'jruby.compat.version', jruby_compat_version || RUBY_VERSION
+ add_context_param 'public.root', File.join('/', public_root)
+ @context_params
end
+ # @deprecated replaced with {#context_params}
+ def init_params; context_params; end
- def init_params
- @params ||= {}
- add_parameter_unless_exist 'jruby.min.runtimes', jruby_min_runtimes.to_s
- add_parameter_unless_exist 'jruby.max.runtimes', jruby_max_runtimes.to_s
- add_parameter_unless_exist 'jruby.initial.runtimes', jruby_min_runtimes.to_s
- add_parameter_unless_exist 'public.root', File.join('/', public_root)
- add_parameter_unless_exist 'jruby.compat.version', RUBY_VERSION
- @params
+ def add_context_param(param_name, param_value)
+ @context_params ||= {}
+ if ! param_value.nil? && ! web_xml_context_param(param_name)
+ @context_params[param_name] = param_value.to_s
+ end
end
+ def deployment_descriptor
+ @deployment_descriptor ||= if web_xml
+ file = File.expand_path(File.join(work_dir, web_xml))
+ File.exist?(file) ? file : nil
+ end
+ end
+
+ # @deprecated use {#deployment_descriptor}
def default_deployment_descriptor
- @deployment_descriptor ||= if default_web_xml
+ @default_deployment_descriptor ||= if default_web_xml
file = File.expand_path(File.join(work_dir, default_web_xml))
File.exist?(file) ? file : nil
end
end
-
- def rack_servlet_configured?
- !!( web_xml && (
- web_xml.root.elements["/web-app/servlet[contains(servlet-class, 'org.jruby.rack.RackServlet')]"] ||
- web_xml.root.elements["/web-app/filter[contains(filter-class, 'org.jruby.rack.RackFilter')]"]
- )
- )
+
+ def class_loader
+ @class_loader ||= org.jruby.util.JRubyClassLoader.new(JRuby.runtime.jruby_class_loader)
end
-
- def rack_listener_configured?
- !!( web_xml &&
- web_xml.root.elements["/web-app/listener[contains(listener-class, '#{context_listener}')]"]
- )
+
+ def class_loader!
+ ( @class_loader = nil ) || class_loader
end
+ # @deprecated replaced with {#class_loader!}
+ def generate_class_loader; class_loader!; end
+
+ def define_lifecycle
+ Trinidad::Lifecycle::WebApp::Default.new(self)
+ end
- %w{ context_path web_app_dir libs_dir classes_dir default_web_xml
- jruby_min_runtimes jruby_max_runtimes rackup log }.each do |method_name|
- define_method method_name do
- sym = method_name.to_sym
- @app_config[sym] || @config[sym]
- end
+ # Reset the hold web application state so it gets re-initialized.
+ # Please note that the received configuration object are not cleared.
+ def reset!
+ vars = instance_variables.map(&:to_sym)
+ vars = vars - [ :'@config', :'@default_config' ]
+ vars.each { |var| instance_variable_set(var, nil) }
end
+
+ def rack_servlet
+ return nil if @rack_servlet == false
+ @rack_servlet ||= begin
+ servlet_config = self[:servlet] || {}
+ servlet_class = servlet_config[:class] || 'org.jruby.rack.RackServlet'
+ servlet_name = servlet_config[:name] || 'RackServlet'
- def public_root; @app_config[:public] || @config[:public] || 'public'; end
- def environment; @app_config[:environment] || @config[:environment] || 'development'; end
- def work_dir; @app_config[:work_dir] || @config[:work_dir] || web_app_dir; end
+ if ! web_xml_servlet?(servlet_class, servlet_name) &&
+ ! web_xml_filter?('org.jruby.rack.RackFilter')
+ {
+ :class => servlet_class, :name => servlet_name,
+ :async_supported => !! servlet_config[:async_supported],
+ :instance => servlet_config[:instance]
+ }
+ else
+ false # no need to setup a rack servlet
+ end
+ end || nil
+ end
+ # @deprecated use {#rack_servlet} instead
+ def servlet; rack_servlet; end
- def extensions
- @extensions ||= begin
- extensions = @config[:extensions] || {}
- extensions.merge!(@app_config[:extensions]) if @app_config[:extensions]
- extensions
- end
+ def rack_listener
+ context_listener unless web_xml_listener?(context_listener)
end
+
+ def war?; self.class.war?(config); end
- def war?; WebApp.war?(app_config); end
-
def solo?
- !self.is_a?(WarWebApp) && @app_config[:solo]
+ ! is_a?(WarWebApp) && config[:solo]
end
def threadsafe?
jruby_min_runtimes.to_i == 1 && jruby_max_runtimes.to_i == 1
end
-
- def monitor
- m_file = @app_config[:monitor] || @config[:monitor] || 'tmp/restart.txt'
- File.expand_path(m_file, work_dir)
+
+ protected
+
+ def context_listener
+ raise NotImplementedError.new "context_listener expected to be redefined"
end
-
- def define_lifecycle
- Trinidad::Lifecycle::Default.new(self)
+
+ def complete_config!
+ config[:web_app_dir] ||= default_config[:web_app_dir] || Dir.pwd
end
-
- def generate_class_loader
- @class_loader = org.jruby.util.JRubyClassLoader.new(JRuby.runtime.jruby_class_loader)
+
+ def web_xml_servlet?(servlet_class, servlet_name = nil)
+ !!( web_xml_doc && (
+ web_xml_doc.root.elements["/web-app/servlet[contains(servlet-class, '#{servlet_class}')]"]
+ )
+ )
end
- protected
- def add_parameter_unless_exist(param_name, param_value)
- @params[param_name] = param_value unless web_context_param(param_name)
+ def web_xml_filter?(filter_class)
+ !!( web_xml_doc && (
+ web_xml_doc.root.elements["/web-app/filter[contains(filter-class, '#{filter_class}')]"]
+ )
+ )
end
-
+
+ def web_xml_listener?(listener_class)
+ !!( web_xml_doc &&
+ web_xml_doc.root.elements["/web-app/listener[contains(listener-class, '#{listener_class}')]"]
+ )
+ end
+
+ def web_xml_context_param(name)
+ if web_xml_doc &&
+ param = web_xml_doc.root.elements["/web-app/context-param[contains(param-name, '#{name}')]"]
+ param.elements['param-value'].text
+ end
+ end
+
private
- def web_xml
- return nil if @web_xml == false
- @web_xml ||=
+
+ def web_xml_doc
+ return @web_xml_doc || nil if ! @web_xml_doc.nil?
+ if deployment_descriptor
begin
require 'rexml/document'
- REXML::Document.new(File.read(default_deployment_descriptor))
+ @web_xml_doc = REXML::Document.new(File.read(deployment_descriptor))
rescue REXML::ParseException => e
- puts "WARNING: invalid deployment descriptor:[#{default_deployment_descriptor}]"
- puts e.message
- false
- end unless default_deployment_descriptor.nil?
+ log = Trinidad::Logging::LogFactory.getLog('')
+ log.warn "invalid deployment descriptor:[#{deployment_descriptor}]\n #{e.message}"
+ @web_xml_doc = false
+ end
+ @web_xml_doc || nil
+ end
end
-
- def web_context_param(param)
- if web_xml && param = web_xml.root.elements["/web-app/context-param[contains(param-name, '#{param}')]"]
- param.elements['param-value'].text
+
+ protected
+
+ def self.rackup?(config, default_config = nil)
+ return true if config.has_key?(:rackup)
+ web_app_dir = config[:web_app_dir] ||
+ (default_config && default_config[:web_app_dir]) || Dir.pwd
+ config_ru = (default_config && default_config[:rackup]) || 'config.ru'
+ # Check for rackup (but still use config/environment.rb for Rails 3)
+ if File.exists?(File.join(web_app_dir, config_ru)) &&
+ ! File.exists?(File.join(web_app_dir, 'config/environment.rb'))
+ config[:rackup] = config_ru
end
+ config[:rackup] || ! Dir[File.join(web_app_dir, 'WEB-INF/**/config.ru')].empty?
end
- def configure_rack_servlet(servlet_class, servlet_name)
- servlet_config = @config[:servlet] || @app_config[:servlet] || {}
- @servlet = {
- :class => servlet_config[:class] || servlet_class,
- :name => servlet_config[:name] || servlet_name,
- :instance => servlet_config[:instance]
- }
+ def self.war?(config, default_config = nil)
+ config[:context_path] && config[:context_path][-4..-1] == '.war'
end
+
+ class Holder
+
+ def initialize(web_app, context)
+ @web_app, @context = web_app, context
+ end
+
+ attr_reader :web_app
+ attr_accessor :context
+
+ def monitor; web_app.monitor; end
+
+ attr_accessor :monitor_mtime
+
+ def try_lock
+ locked? ? false : lock
+ end
- def self.autodetect_configuration(config, app_config)
- # Check for Rails threadsafe mode
- environment = app_config[:environment] || config[:environment]
- if threadsafe_instance?(app_config[:web_app_dir], environment)
- app_config[:jruby_min_runtimes] = 1
- app_config[:jruby_max_runtimes] = 1
+ def locked?; !!@lock; end
+ def lock; @lock = true; end
+ def unlock; @lock = false; end
+
+ # @deprecated behaves Hash like for (<= 1.3.5) compatibility
+ def [](key)
+ case key.to_sym
+ when :app then
+ web_app
+ when :context then
+ context
+ when :lock then
+ @lock
+ when :monitor then
+ monitor
+ when :mtime then
+ monitor_mtime
+ else raise NoMethodError, key.to_s
+ end
end
+
+ # @deprecated behaves Hash like for (<= 1.3.5) compatibility
+ def []=(key, val)
+ case key.to_sym
+ when :context then
+ self.context=(val)
+ when :lock then
+ @lock = val
+ when :mtime then
+ self.monitor_mtime=(val)
+ else raise NoMethodError, "#{key}="
+ end
+ end
- rackup = config[:rackup] || 'config.ru'
- app_config[:web_app_dir] ||= config[:web_app_dir] || Dir.pwd
- # Check for rackup (but still use config/environment.rb for Rails 3)
- if !app_config[:rackup] &&
- File.exists?(File.join(app_config[:web_app_dir], rackup)) &&
- !File.exists?(File.join(app_config[:web_app_dir], 'config/environment.rb'))
- app_config[:rackup] = rackup
+ end
+
+ end
+
+ # Rack web application (looks for a rackup config.ru file).
+ class RackupWebApp < WebApp
+
+ def context_params
+ add_context_param 'rack.env', environment
+ if rackup = self.rackup
+ rackup = File.join(rackup, 'config.ru') if File.directory?(rackup)
+ add_context_param 'rackup.path', rackup
end
+ super
end
- def self.threadsafe_instance?(app_base, environment)
+ def context_listener; 'org.jruby.rack.RackServletContextListener'; end
+
+ end
+
+ # Rails web app specifics. Supports the same versions as jruby-rack !
+ class RailsWebApp < WebApp
+
+ def context_params
+ add_context_param 'rails.env', environment
+ add_context_param 'rails.root', '/'
+ super
+ end
+
+ def context_listener; 'org.jruby.rack.rails.RailsServletContextListener'; end
+
+ protected
+
+ def complete_config!
+ super
+ # detect threadsafe! in config/environments/environment.rb :
+ if self.class.threadsafe?(web_app_dir, environment)
+ config[:jruby_min_runtimes] = 1
+ config[:jruby_max_runtimes] = 1
+ end
+ end
+
+ private
+
+ def self.threadsafe?(app_base, environment)
threadsafe_match?("#{app_base}/config/environments/#{environment}.rb") ||
threadsafe_match?("#{app_base}/config/environment.rb")
end
def self.threadsafe_match?(file)
File.exist?(file) && File.readlines(file).any? { |l| l =~ /^[^#]*threadsafe!/ }
end
+
end
+
+ # A web application for deploying (java) .war files.
+ class WarWebApp < WebApp
+
+ def context_path
+ super.gsub(/\.war$/, '')
+ end
+
+ def work_dir
+ File.join(web_app_dir.gsub(/\.war$/, ''), 'WEB-INF')
+ end
+
+ def monitor
+ File.expand_path(web_app_dir)
+ end
+
+ def define_lifecycle
+ Trinidad::Lifecycle::WebApp::War.new(self)
+ end
+
+ end
+
end