lib/trinidad/web_app.rb in trinidad-1.4.5.B1 vs lib/trinidad/web_app.rb in trinidad-1.4.5

- old
+ new

@@ -1,12 +1,12 @@ require 'trinidad/configuration' module Trinidad class WebApp - + @@defaults = Trinidad::Configuration::DEFAULTS - + attr_reader :config, :default_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) : @@ -21,118 +21,131 @@ def [](key) key = key.to_sym config.key?(key) ? config[key] : default_config[key] end - + def key?(key, use_default = true) key = key.to_sym return true if config.has_key?(key) use_default ? default_config.key?(key) : false end - + %w{ root_dir rackup async_supported reload_strategy host_name }.each do |method| class_eval "def #{method}; self[:'#{method}']; end" end - + alias_method :web_app_dir, :root_dir # is getting deprecated soon def app_root; root_dir; end - + # @deprecated use `self[:log]` instead def log; self[:log]; end - + def context_path path = self[:context_path] || self[:path] path ? path.to_s : path end - + def context_name name = self[:context_name] || self[:name] name ? name.to_s : name end - + # NOTE: should be set to application root (base) directory thus # JRuby-Rack correctly resolves relative paths for the context! def doc_base; self[:doc_base] || root_dir; end - + def jruby_min_runtimes if min = config[:jruby_min_runtimes] return min.to_i # min specified overrides :threadsafe else # but :threadsafe takes precendence over default : self[:threadsafe] ? 1 : fetch_default_config_value(:jruby_min_runtimes) end end - + def jruby_max_runtimes if max = config[:jruby_max_runtimes] return max.to_i # max specified overrides :threadsafe else # but :threadsafe takes precendence over default : self[:threadsafe] ? 1 : fetch_default_config_value(:jruby_max_runtimes) end end - + def jruby_initial_runtimes if ini = config[:jruby_initial_runtimes] return ini.to_i # min specified overrides :threadsafe else # but :threadsafe takes precendence over default : - self[:threadsafe] ? 1 : + self[:threadsafe] ? 1 : fetch_default_config_value(:jruby_initial_runtimes, jruby_min_runtimes) end end - + def jruby_runtime_acquire_timeout fetch_config_value(:jruby_runtime_acquire_timeout, 5.0) # default 10s seems too high end def jruby_compat_version fetch_config_value(:jruby_compat_version, RUBY_VERSION) end - - def environment; self[:environment] || @@defaults[:environment]; end # TODO check web.xml - + + def environment + @environment ||= begin + if env = web_xml_environment + if self[:environment] && env != self[:environment] + logger.info "Ignoring set :environment '#{self[:environment]}' for " << + "#{context_path} since it's configured in web.xml as '#{env}'" + end + else + env = self[:environment] || @@defaults[:environment] + env = env.to_s if env.is_a?(Symbol) # make sure it's a String + end + env + end + end + def public_dir @public_dir ||= ( public_root == '/' ? root_dir : expand_path(public_root) ) end - + # by (a "Rails") convention use '[RAILS_ROOT]/tmp' def work_dir @work_dir ||= self[:work_dir] || File.join(root_dir, 'tmp') end - + # by a "Rails" convention defaults to '[RAILS_ROOT]/log' def log_dir @log_dir ||= self[:log_dir] || File.join(root_dir, 'log') end - + def monitor File.expand_path(self[:monitor] || 'restart.txt', work_dir) end - + def context_xml; self[:context_xml] || self[:default_context_xml]; end def web_xml; self[:web_xml] || self[:default_web_xml]; end def default_web_xml; self[:default_web_xml]; end - + def java_lib # accepts #deprecated :libs_dir syntax self[:java_lib] || self[:libs_dir] || @@defaults[:java_lib] end - + def java_classes # accepts #deprecated :classes_dir syntax self[:java_classes] || self[:classes_dir] || File.join(java_lib, 'classes') end - + def java_lib_dir @java_lib_dir ||= self[:java_lib_dir] || expand_path(java_lib) end alias_method :libs_dir, :java_lib_dir # #deprecated - + def java_classes_dir @java_classes_dir ||= self[:java_classes_dir] || expand_path(java_classes) end alias_method :classes_dir, :java_classes_dir # #deprecated - + def extensions @extensions ||= begin extensions = default_config[:extensions] || {} extensions.merge(config[:extensions] || {}) end @@ -162,50 +175,50 @@ def logging @logging ||= begin defaults = { :level => log, # backwards compatibility - :use_parent_handlers => environment == 'development', + :use_parent_handlers => ( environment == 'development' ), :file => { :dir => log_dir, :prefix => environment, :suffix => '.log', :rotate => true } } Trinidad::Configuration.merge_options(defaults, self[:logging]) end end - + def deployment_descriptor return nil if @deployment_descriptor == false @deployment_descriptor ||= expand_path(web_xml) || false end - + # @deprecated use {#deployment_descriptor} def default_deployment_descriptor return nil if @default_deployment_descriptor == false @default_deployment_descriptor ||= expand_path(default_web_xml) || false end - + def public_root @public_root ||= ( public_config[:root] || @@defaults[:public] ) end alias_method :public, :public_root - + # we do support nested :public configuration e.g. : # public: # root: /assets # cache: true # cache_ttl: 60000 def public_config - @public_config ||= - self[:public].is_a?(String) ? - { :root => self[:public] } : + @public_config ||= + self[:public].is_a?(String) ? + { :root => self[:public] } : ( self[:public] || {} ) end - + def aliases # :public => { :aliases => ... } return nil unless aliases = ( self[:aliases] || public_config[:aliases] ) return aliases if aliases.is_a?(String) # "/aliasPath1=docBase1,/aliasPath2=docBase2" @aliases ||= aliases.map do |path, base| @@ -214,70 +227,68 @@ path = (root << path) end "#{path}=#{File.expand_path(base, root_dir)}" end.join(',') end - + def caching_allowed? # :public => { :cached => ... } # ((BaseDirContext) resources).setCached(isCachingAllowed()) return @caching_allowed unless @caching_allowed.nil? @caching_allowed = self[:caching_allowed] if @caching_allowed.nil? @caching_allowed = public_config[:cached] - if @caching_allowed.nil? - @caching_allowed = environment != 'development' - end + @caching_allowed = environment != 'development' if @caching_allowed.nil? end @caching_allowed = !! @caching_allowed end - + # The cache max size in kB def cache_max_size # :public => { :cache_max_size => ... } # ((BaseDirContext) resources).setCacheMaxSize self[:cache_max_size] || public_config[:cache_max_size] end - + # The max size for a cached object in kB def cache_object_max_size # :public => { :cache_object_max_size => ... } # ((BaseDirContext) resources).setCacheObjectMaxSize self[:cache_object_max_size] || public_config[:cache_object_max_size] end - + # Cache entry time-to-live in millis def cache_ttl # :public => { :cache_ttl => ... } # ((BaseDirContext) resources).setCacheTTL self[:cache_ttl] || public_config[:cache_ttl] end - + def class_loader - @class_loader ||= + @class_loader ||= org.jruby.util.JRubyClassLoader.new(JRuby.runtime.jruby_class_loader) end - + 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 # Reset the hold web application state so it gets re-initialized. # Please note that the configuration objects are not cleared. def reset! - vars = instance_variables.map(&:to_sym) + vars = instance_variables.map(&:to_sym) vars = vars - [ :'@config', :'@default_config' ] vars.each { |var| instance_variable_set(var, nil) } end - + DEFAULT_SERVLET_CLASS = nil # by default we resolve by it's name DEFAULT_SERVLET_NAME = 'default' - - # Returns a servlet config for the DefaultServlet. - # This servlet is setup for each and every Tomcat context and is named + + # Returns a servlet config for the DefaultServlet. + # This servlet is setup for each and every Tomcat context and is named # 'default' and mapped to '/' we allow fine tunning of this servlet. # Return values should be interpreted as follows : # true - do nothing leave the servlet as set-up (by default) # false - remove the set-up default (e.g. configured in web.xml) def default_servlet @@ -302,16 +313,16 @@ else false # configured in web.xml thus remove the (default) "default" end end end - + JSP_SERVLET_CLASS = nil # by default we resolve by it's name JSP_SERVLET_NAME = 'jsp' - - # Returns a servlet config for the JspServlet. - # This servlet is setup by default for every Tomcat context and is named + + # Returns a servlet config for the JspServlet. + # This servlet is setup by default for every Tomcat context and is named # 'jsp' with '*.jsp' and '*.jspx' mappings. # Return values should be interpreted as follows : # true - do nothing leave the servlet as set-up (by default) # false - remove the set-up servlet (by default we do not need jsp support) def jsp_servlet @@ -327,29 +338,29 @@ else false # configured in web.xml thus remove the default "jsp" end end end - + RACK_SERVLET_CLASS = 'org.jruby.rack.RackServlet' RACK_SERVLET_NAME = 'rack' # in-case of a "custom" rack servlet class RACK_FILTER_CLASS = 'org.jruby.rack.RackFilter' RACK_FILTER_NAME = 'rack' - + # Returns a config for the RackServlet or nil if no need to set-up one. # (to be used for dispatching to this Rack / Rails web application) def rack_servlet return nil if @rack_servlet == false @rack_servlet ||= begin rack_servlet = self[:rack_servlet] || self[:servlet] || {} - + if rack_servlet.is_a?(javax.servlet.Servlet) { :instance => rack_servlet, :name => RACK_SERVLET_NAME, :mapping => '/*' } else servlet_class = rack_servlet[:class] || RACK_SERVLET_CLASS servlet_name = rack_servlet[:name] || RACK_SERVLET_NAME - + if ! web_xml_servlet?(servlet_class, servlet_name) && ! web_xml_filter?(RACK_FILTER_CLASS, RACK_FILTER_NAME) { :instance => rack_servlet[:instance], :class => servlet_class, :name => servlet_name, @@ -359,54 +370,54 @@ :load_on_startup => ( rack_servlet[:load_on_startup] || 2 ).to_i, :mapping => rack_servlet[:mapping] || '/*' } else if ! rack_servlet.empty? - logger.info "ignoring :rack_servlet configuration for " + + logger.info "Ignoring :rack_servlet configuration for " << "#{context_path} due #{deployment_descriptor}" end false # no need to setup a rack servlet end end end || nil end # @deprecated use {#rack_servlet} instead def servlet; rack_servlet; end - + def rack_listener context_listener unless web_xml_listener?(context_listener) end - + def war?; self.class.war?(config); end def solo? ! is_a?(WarWebApp) && config[:solo] end - + def threadsafe? jruby_min_runtimes == 1 && jruby_max_runtimes == 1 end - + protected - + def context_listener raise NotImplementedError.new "context_listener expected to be redefined" end - + def layout_class 'JRuby::Rack::FileSystemLayout' # handles Rails as well as Rack end - + def complete_config! config[:root_dir] ||= self.class.root_dir(config, default_config) config[:root_dir] = File.expand_path(config[:root_dir]) config[:context_path] = self.class.context_path(config, default_config) end - + public - - # Returns true if there's a servlet with the given servlet-class name + + # Returns true if there's a servlet with the given servlet-class name # configured or if the optional name second argument is given it also # checks for a servlet with the given name. def web_xml_servlet?(servlet_class, servlet_name = nil) return nil unless web_xml_doc if servlet_class @@ -415,11 +426,11 @@ end if servlet_name servlet_xpath = "/web-app/servlet[servlet-name = '#{servlet_name}']" return !! web_xml_doc.root.elements[servlet_xpath] end - + return false if servlet_class || servlet_name raise ArgumentError, "nor servlet_class nor servlet_name given" end # Returns true if a filter definition with a given filter-class is found. @@ -431,46 +442,48 @@ end if filter_name filter_xpath = "/web-app/filter[filter-name = '#{filter_name}']" return !! web_xml_doc.root.elements[filter_xpath] end - + return false if filter_class || filter_name raise ArgumentError, "nor filter_class nor filter_name given" end - + # Returns true if a listener definition with a given listener-class is found. def web_xml_listener?(listener_class) return nil unless web_xml_doc !! web_xml_doc.root.elements["/web-app/listener[listener-class = '#{listener_class}']"] end - + # Returns a param-value for a context-param with a given param-name. def web_xml_context_param(name) return nil unless web_xml_doc if param = web_xml_doc.root.elements["/web-app/context-param[param-name = '#{name}']"] param.elements['param-value'].text end end - + + def web_xml_environment; nil; end + private - + def web_xml_doc return @web_xml_doc || nil unless @web_xml_doc.nil? descriptor = deployment_descriptor if descriptor && File.exist?(descriptor) begin require 'rexml/document' @web_xml_doc = REXML::Document.new(File.read(descriptor)) rescue REXML::ParseException => e - logger.warn "invalid deployment descriptor:[#{descriptor}]\n #{e.message}" + logger.warn "Invalid deployment descriptor:[#{descriptor}]\n #{e.message}" @web_xml_doc = false end @web_xml_doc || nil end end - + def expand_path(path) if path path_file = java.io.File.new(path) if path_file.absolute? path_file.absolute_path @@ -482,39 +495,39 @@ def fetch_config_value(name, default = nil) value = config[name] value.nil? ? fetch_default_config_value(name, default) : value end - + def fetch_default_config_value(name, default = nil) value = default_config[name] if value.nil? # JRuby-Rack names: jruby_min_runtimes -> jruby.min.runtimes : value = java.lang.System.getProperty(name.to_s.gsub('_', '.')) value ||= default end value end - + def logger @logger ||= Trinidad::Logging::LogFactory.getLog('') end - + protected - + def self.rackup?(config, default_config = nil) return true if config.has_key?(:rackup) root_dir = root_dir(config, default_config) 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(root_dir, config_ru)) && + if File.exists?(File.join(root_dir, config_ru)) && ! rails?(config, default_config) # do not :rackup a rails app config[:rackup] = config_ru end config[:rackup] || ! Dir[File.join(root_dir, 'WEB-INF/**/config.ru')].empty? end - + def self.rails?(config, default_config = nil) root_dir = root_dir(config, default_config) # standart Rails 3.x `class Application < Rails::Application` if File.exists?(application = File.join(root_dir, 'config/application.rb')) return true if file_line_match?(application, /^[^#]*Rails::Application/) @@ -526,72 +539,72 @@ line =~ /^[^#]*Rails::Application/ || line =~ /^[^#]*RAILS_GEM_VERSION/ end end false end - + def self.war?(config, default_config = nil) root_dir = root_dir(config, default_config) return true if root_dir && root_dir.to_s[-4..-1] == '.war' context_path = config[:context_path] # backwards-compatibility : context_path && context_path.to_s[-4..-1] == '.war' end - + private - + def self.root_dir(config, default_config, default_dir = Dir.pwd) # for backwards compatibility accepts the :web_app_dir "alias" - config[:root_dir] || config[:web_app_dir] || - ( default_config && + config[:root_dir] || config[:web_app_dir] || + ( default_config && ( default_config[:root_dir] || default_config[:web_app_dir] ) ) || default_dir end - + def self.context_path(config, default_config = nil) - path = config[:context_path] || + path = config[:context_path] || ( default_config && default_config[:context_path] ) unless path name = config[:context_name] || ( default_config && default_config[:context_name] ) path = name.to_s == 'default' ? '/' : "/#{name}" end path = "/#{path}" if path.to_s[0, 1] != '/' path.to_s end - + def self.file_line_match?(path, pattern = nil) File.open(path) do |file| if block_given? file.each_line { |line| return true if yield(line) } else file.each_line { |line| return true if line =~ pattern } end end false 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 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 @@ -617,15 +630,15 @@ when :mtime then self.monitor_mtime=(val) else raise NoMethodError, "#{key}=" end end - + end - + end - + # Rack web application (looks for a "rackup" *config.ru* file). class RackupWebApp < WebApp def context_params add_context_param 'app.root', app_root @@ -636,41 +649,45 @@ end super end def context_listener; 'org.jruby.rack.RackServletContextListener'; end - + + def web_xml_environment; web_xml_context_param('rack.env'); end + end - + # Rails web application specifics (supports same versions as JRuby-Rack). class RailsWebApp < WebApp def context_params add_context_param 'rails.root', app_root add_context_param 'rails.env', environment super end def context_listener; 'org.jruby.rack.rails.RailsServletContextListener'; end - + + def web_xml_environment; web_xml_context_param('rails.env'); end + protected - + def complete_config! super # detect threadsafe! in config/environments/environment.rb : if ! key?(:threadsafe) && self.class.threadsafe?(root_dir, environment) config[:jruby_min_runtimes] = 1 unless key?(:jruby_min_runtimes, false) config[:jruby_max_runtimes] = 1 unless key?(:jruby_max_runtimes, false) end end - + #def layout_class #'JRuby::Rack::RailsFileSystemLayout' #end - + private - + def self.threadsafe?(app_base, environment) threadsafe_match?("#{app_base}/config/environments/#{environment}.rb") || threadsafe_match?("#{app_base}/config/environment.rb") end @@ -680,16 +697,16 @@ file_line_match?(file, /^[^#]*config\.eager_load = true/) && file_line_match?(file, /^[^#]*config\.cache_classes = true/) ) ) end - + end - + # A web application for deploying (java) .war files. class WarWebApp < WebApp - + def root_dir @root_dir ||= ( config[:root_dir] || begin path = config[:context_path] path.to_s if path.to_s[-4..-1] == '.war' end || default_confit[:root_dir] ) @@ -706,11 +723,11 @@ def work_dir self[:work_dir] end def log_dir - @log_dir ||= self[:log_dir] || begin + @log_dir ||= self[:log_dir] || begin if work_dir then work_dir else if root_dir[-4..-1] == '.war' parent_dir = File.dirname(root_dir) expanded_dir = File.join(parent_dir, context_path) @@ -719,15 +736,15 @@ File.join(root_dir, 'log') end end end end - + def monitor root_dir ? File.expand_path(root_dir) : nil # the .war file itself end - + def class_loader @class_loader ||= nil # lifecycle will setup JRuby CL end def context_params @@ -735,17 +752,17 @@ end def layout_class 'JRuby::Rack::WebInfLayout' end - + def define_lifecycle Trinidad::Lifecycle::WebApp::War.new(self) end private def warbler?; nil; end # TODO detect warbler created .war ?! - + end - + end