lib/trinidad/server.rb in trinidad-1.4.4 vs lib/trinidad/server.rb in trinidad-1.4.5.B1

- old
+ new

@@ -1,245 +1,485 @@ require 'trinidad/configuration' require 'trinidad/web_app' module Trinidad class Server - attr_reader :config, :tomcat, :web_apps + attr_reader :config def initialize(config = Trinidad.configuration) - load_config(config) - configure_logging(@config[:log]) - load_tomcat_server - @web_apps = create_web_apps - load_host_monitor(@web_apps) + configure(config) end - def load_config(config) - add_default_web_app! config + def configure(config = Trinidad.configuration) + configure_logging config[:log] @config = config.freeze end + # @deprecated replaced with {#configure} + def load_config(config); configure(config); end - def load_tomcat_server - load_default_system_properties - - @tomcat = Trinidad::Tomcat::Tomcat.new - @tomcat.base_dir = Dir.pwd - @tomcat.hostname = @config[:address] || 'localhost' - @tomcat.server.address = @config[:address] - @tomcat.port = @config[:port].to_i - create_hosts - @tomcat.enable_naming - - add_http_connector if http_configured? - add_ssl_connector if ssl_enabled? - add_ajp_connector if ajp_enabled? - - @tomcat = Trinidad::Extensions.configure_server_extensions(@config[:extensions], @tomcat) + def hosts + @hosts ||= @config[:hosts] end + attr_writer :hosts + + def app_base + @app_base ||= @config[:app_base] || @config[:apps_base] + end + attr_writer :app_base - def load_host_monitor(apps) - @tomcat.engine.find_children.each do |host| - host.add_lifecycle_listener(Trinidad::Lifecycle::Host.new(self, *apps)) - end + def web_apps + @web_apps ||= @config[:web_apps] || @config[:webapps] end + attr_writer :web_apps + + def trap? + @trap ||= @config[:trap] if ! defined?(@trap) || @trap.nil? + @trap + end + attr_writer :trap def ssl_enabled? - !! @config[:ssl] && ! @config[:ssl].empty? + if ! defined?(@ssl_enabled) || @ssl_enabled.nil? + @ssl_enabled ||= ( !! @config[:ssl] && ! @config[:ssl].empty? ) + end + @ssl_enabled end + attr_writer :ssl_enabled def ajp_enabled? - !! @config[:ajp] && ! @config[:ajp].empty? + if ! defined?(@ajp_enabled) || @ajp_enabled.nil? + @ajp_enabled ||= ( !! @config[:ajp] && ! @config[:ajp].empty? ) + end + @ajp_enabled end + attr_writer :ajp_enabled def http_configured? - (!! @config[:http] && ! @config[:http].empty?) || @config[:address] != 'localhost' + if ! defined?(@http_configured) || @http_configured.nil? + @http_configured ||= + ( ( !! @config[:http] && ! @config[:http].empty? ) || @config[:address] != 'localhost' ) + end + @http_configured end + attr_writer :http_configured - def add_ajp_connector - add_service_connector(@config[:ajp], 'AJP/1.3') + def tomcat; @tomcat ||= initialize_tomcat; end + + def initialize_tomcat + set_system_properties + + tomcat = Trinidad::Tomcat::Tomcat.new + tomcat.base_dir = config[:base_dir] || Dir.pwd + tomcat.hostname = config[:address] || 'localhost' + tomcat.server.address = config[:address] + tomcat.port = config[:port].to_i + default_host(tomcat) + create_hosts(tomcat) + tomcat.enable_naming + + add_http_connector(tomcat) if http_configured? + add_ssl_connector(tomcat) if ssl_enabled? + add_ajp_connector(tomcat) if ajp_enabled? + + Trinidad::Extensions.configure_server_extensions(config[:extensions], tomcat) end + protected :initialize_tomcat + # #deprecated renamed to {#initialize_tomcat} + def load_tomcat_server; initialize_tomcat; end - def add_http_connector - options = @config[:http] || {} + def add_host_monitor(app_holders) + for host in tomcat.engine.find_children + host_apps = select_host_apps(app_holders, host) + host.add_lifecycle_listener(Trinidad::Lifecycle::Host.new(self, *host_apps)) + end + end + protected :add_host_monitor + # @deprecated replaced with {#setup_host_monitor} + def load_host_monitor(web_apps); add_host_monitor(web_apps); end + + def add_ajp_connector(tomcat = @tomcat) + add_service_connector(@config[:ajp], 'AJP/1.3', tomcat) + end + + def add_http_connector(tomcat = @tomcat) + options = config[:http] || {} options[:address] ||= @config[:address] if @config[:address] != 'localhost' options[:port] = @config[:port] options[:protocol_handler] = 'org.apache.coyote.http11.Http11NioProtocol' if options[:nio] if options[:apr] - @tomcat.server.add_lifecycle_listener(Trinidad::Tomcat::AprLifecycleListener.new) + tomcat.server.add_lifecycle_listener(Trinidad::Tomcat::AprLifecycleListener.new) end - connector = add_service_connector(options, options[:protocol_handler] || 'HTTP/1.1') - @tomcat.connector = connector + connector = add_service_connector(options, options[:protocol_handler] || 'HTTP/1.1', tomcat) + tomcat.connector = connector end - def add_ssl_connector - options = @config[:ssl].merge({ + def add_ssl_connector(tomcat = @tomcat) + options = config[:ssl].merge({ :scheme => 'https', :secure => true, :SSLEnabled => 'true' }) options[:keystoreFile] ||= options.delete(:keystore) - if !options[:keystoreFile] && !options[:SSLCertificateFile] + if ! options[:keystoreFile] && ! options[:SSLCertificateFile] options[:keystoreFile] = 'ssl/keystore' options[:keystorePass] = 'waduswadus42' generate_default_keystore(options) end - add_service_connector(options) + add_service_connector(options, nil, tomcat) end - def add_service_connector(options, protocol = nil) - connector = Trinidad::Tomcat::Connector.new(protocol) + def add_service_connector(options, protocol = nil, tomcat = @tomcat) opts = options.dup + connector = Trinidad::Tomcat::Connector.new(protocol) connector.scheme = opts.delete(:scheme) if opts[:scheme] connector.secure = opts.delete(:secure) || false connector.port = opts.delete(:port).to_i connector.protocol_handler_class_name = opts.delete(:protocol_handler) if opts[:protocol_handler] - opts.each do |key, value| - connector.setProperty(key.to_s, value.to_s) - end + opts.each { |key, value| connector.setProperty(key.to_s, value.to_s) } - @tomcat.service.add_connector(connector) + tomcat.service.add_connector(connector) connector end + private :add_service_connector - def add_web_app(web_app, host = nil) - host ||= web_app.config[:host] || @tomcat.host - context = @tomcat.addWebapp(host, web_app.context_path, web_app.root_dir) - Trinidad::Extensions.configure_webapp_extensions(web_app.extensions, @tomcat, context) - context.add_lifecycle_listener(web_app.define_lifecycle) + def add_web_app(web_app, host = nil, start = nil) + host ||= begin + name = web_app.host_name + name ? find_host(name, tomcat) : tomcat.host + end + prev_start = host.start_children + context = begin + host.start_children = start unless start.nil? + # public Context addWebapp(Host host, String url, String name, String docBase) + tomcat.addWebapp(host, web_app.context_path, web_app.context_name, web_app.root_dir) + rescue java.lang.IllegalArgumentException => e + if e.message =~ /addChild\:/ + context_name = web_app.context_name + logger.error "could not add application #{context_name.inspect} from #{web_app.root_dir}\n" << + " (same context name is used for #{host.find_child(context_name).doc_base})" + raise "there's already an application named #{context_name.inspect} for host #{host.name.inspect}" + end + raise e + ensure + host.start_children = prev_start unless start.nil? + end + Trinidad::Extensions.configure_webapp_extensions(web_app.extensions, tomcat, context) + if lifecycle = web_app.define_lifecycle + context.add_lifecycle_listener(lifecycle) + end context end + def deploy_web_apps(tomcat = self.tomcat) + add_host_monitor web_apps = create_web_apps + web_apps + end + def start + deploy_web_apps(tomcat) + trap_signals if trap? - @tomcat.start - @tomcat.server.await + tomcat.start + tomcat.server.await end + def start! + if defined?(@tomcat) && @tomcat + @tomcat.destroy; @tomcat = nil + end + start + end + def stop - @tomcat.stop + if defined?(@tomcat) && @tomcat + @tomcat.stop; true + end end def stop! - stop - @tomcat.destroy + (@tomcat.destroy; true) if stop end protected - - def trap? - !!@config[:trap] - end - + def create_web_apps - apps = [ create_from_web_apps ] - apps << create_from_apps_base - apps.flatten!; apps.compact! - apps - end - - def create_from_web_apps - @config[:web_apps].map do |name, app_config| - app_config[:context_name] ||= name - create_web_app(app_config) - end if @config[:web_apps] - end + # add default web app if needed : + if ! web_apps && ! app_base && ! hosts + default_app = { :context_path => config[:context_path] } + root_dir = web_app_root_dir(config) || Dir.pwd + default_app[:root_dir] = root_dir if root_dir != false + default_app[:rackup] = config[:rackup] if config[:rackup] - def create_from_apps_base - if @config[:apps_base] || @config[:hosts] - @tomcat.engine.find_children.map do |host| - apps_base = host.app_base + self.web_apps = { :default => default_app } + end - apps_path = Dir.glob(File.join(apps_base, '*')). - select { |path| !(path =~ /tomcat\.\d+$/) } + apps = [] - apps_path.reject! { |path| apps_path.include?(path + '.war') } + # configured :web_apps + web_apps.each do |name, app_config| + app_config[:context_name] ||= name + apps << ( app_holder = create_web_app(app_config) ); app = app_holder.web_app + logger.info "Deploying from #{app.root_dir} as #{app.context_path}" + end if web_apps - apps_path.map do |path| - if File.directory?(path) || path =~ /\.war$/ - create_web_app({ - :context_name => File.basename(path), - :root_dir => File.expand_path(path), - :host => host - }) + # configured :app_base or :hosts - scan for applications in host's app_base directory : + tomcat.engine.find_children.each do |host| + apps_path = java.io.File.new(host.app_base).list.to_a + if host.deploy_ignore # respect deploy ignore pattern (even if not deploying on startup) + deploy_ignore_pattern = Regexp.new(host.deploy_ignore) + apps_path.reject! { |path| path =~ deploy_ignore_pattern } + end + # we do a bit of "default" filtering for hosts of our own : + work_dir = host.work_dir + apps_path.reject! do |path| + if path[0, 1] == '.' then true # ignore "hidden" files + elsif work_dir && work_dir == path then true + elsif ! work_dir && path =~ /tomcat\.\d+$/ then true # [host_base]/tomcat.8080 + elsif path[-4..-1] == '.war' && apps_path.include?(path[0...-4]) # only keep expanded .war + logger.info "Expanded .war at #{path} - only deploying directory (.war ignored)" + true + end + end + + apps_path.each do |path| # host web apps (from dir or .war files) + app_root = File.expand_path(path, host.app_base) + if File.directory?(app_root) || ( app_root[-4..-1] == '.war' ) + app_base_name = File.basename(app_root) + deployed = apps.find do |app_holder|; web_app = app_holder.web_app + web_app.root_dir == app_root || + web_app.context_path == Trinidad::Tomcat::ContextName.new(app_base_name).path end + if deployed + logger.debug "Skipping auto-deploy from #{app_root} (already deployed)" + else + apps << ( app_holder = create_web_app({ + :context_name => path, :root_dir => app_root, :host_name => host.name + }) ); app = app_holder.web_app + logger.info "Auto-Deploying from #{app.root_dir} as #{app.context_path}" + end end - end.flatten - end + end + end if app_base || hosts + + apps end def create_web_app(app_config) + host_name = app_config[:host_name] || 'localhost' + host = tomcat.engine.find_child(host_name) + app_config[:root_dir] = web_app_root_dir(app_config, host) + web_app = WebApp.create(app_config, config) WebApp::Holder.new(web_app, add_web_app(web_app)) end - - def create_hosts - if @config[:hosts] - @config[:hosts].each do |apps_base, names| - create_host(apps_base, names) + + def create_hosts(tomcat = @tomcat) + hosts.each do |app_base, host_config| + next if app_base == :default # @see #default_host + if host = find_host(app_base, host_config, tomcat) + setup_host(app_base, host_config, host) + else + create_host(app_base, host_config, tomcat) end - elsif @config[:web_apps] - # create the hosts when they are specified for each app into web_apps. - # We must create them before creating the applications. - @config[:web_apps].each do |_, app_config| - if host_names = app_config.delete(:hosts) - dir = web_app_root_dir(app_config) - apps_base = File.dirname(dir) == '.' ? dir : File.dirname(dir) - app_config[:host] = create_host(apps_base, host_names) + end if hosts + + default_host = tomcat.host + default_app_base = ( default_host.app_base == DEFAULT_HOST_APP_BASE ) + if self.app_base || + ( default_app_base && ! File.exists?(DEFAULT_HOST_APP_BASE) ) + tomcat.host.app_base = self.app_base || Dir.pwd + end + + web_app_hosts = [] + # create hosts as they are specified for each app in :web_apps : + # e.g. :app1 => { :root_dir => 'app1', :host => 'virtual.host' } + web_apps.each do |_, app_config| + if host_names = app_config[:hosts] || app_config[:host] + if host = find_host(host_names, tomcat) + app_root = web_app_root_dir(app_config, host) + set_host_app_base(app_root, host, default_host, web_app_hosts) + else + app_root = web_app_root_dir(app_config) + raise "no root for app #{app_config.inspect}" unless app_root + app_root = File.expand_path(app_root) + # for created hosts -> web-app per host by default + # thus new host's app_base will point to root_dir : + host = create_host(app_root, host_names, tomcat) + web_app_hosts << host end + app_config[:host_name] = host.name end - else - @tomcat.host.app_base = @config[:apps_base] || Dir.pwd - end + end if web_apps end - - def create_host(apps_base, names) - host_names = Array(names) - host_name = host_names.shift - unless host = @tomcat.engine.find_child(host_name) - host = Trinidad::Tomcat::StandardHost.new - host.name = host_name - host.app_base = apps_base || Dir.pwd - host_names.each { |name| host.add_alias(name) } - @tomcat.engine.add_child host - end + def create_host(app_base, host_config, tomcat = @tomcat) + host = Trinidad::Tomcat::StandardHost.new + host.app_base = nil # reset default app_base + host.deployXML = false # disabled by default + setup_host(app_base, host_config, host) + tomcat.engine.add_child host if tomcat host end - def load_default_system_properties - java.lang.System.set_property("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", 'true') + def setup_host(app_base, host_config, host) + if host_config.is_a?(Array) + name = host_config.shift + host_config = { :name => name, :aliases => host_config } + elsif host_config.is_a?(String) || host_config.is_a?(Symbol) + host_config = { :name => host_config } + else + host_config[:name] ||= app_base + end + host_config[:app_base] ||= app_base if app_base.is_a?(String) + + host_config.each do |name, value| + case (name = name.to_sym) + when :app_base + host.app_base = value if default_host_base?(host) + when :aliases + aliases = host.find_aliases || [] + value.each do |name| + next if (name = name.to_s) == host.name + host.add_alias(name) unless aliases.include?(name) + end if host_config[:aliases] + else + value = value.to_s if value.is_a?(Symbol) + host.send("#{name}=", value) # e.g. host.name = value + end + end end + + def set_system_properties(system = Java::JavaLang::System) + system.set_property("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", 'true') + end + # @deprecated renamed to {#set_system_properties} + def load_default_system_properties; set_system_properties; end def configure_logging(log_level) Trinidad::Logging.configure(log_level) end - + + def logger; @logger ||= self.class.logger; end + + def self.logger + Logging::LogFactory.getLog('org.apache.catalina.startup.Tomcat') + end + private - - def add_default_web_app!(config) - if ! config[:web_apps] && ! config[:apps_base] && ! config[:hosts] - default_app = { - :context_path => config[:context_path], - :root_dir => web_app_root_dir(config), - :log => config[:log] - } - default_app[:rackup] = config[:rackup] if config[:rackup] - config[:web_apps] = { :default => default_app } + def default_host(tomcat = @tomcat) + host = tomcat.host # make sure we initialize default host + host.deployXML = false + host_config = @config[:host] || ( @config[:hosts] && @config[:hosts][:default] ) + host_config.each { |name, value| host.send("#{name}=", value) } if host_config + host + end + + DEFAULT_HOST_APP_BASE = 'webapps' # :nodoc: + + def default_host_base?(host) + host.app_base.nil? || ( host.app_base == DEFAULT_HOST_APP_BASE && host.name == 'localhost' ) + end + + def set_host_app_base(app_root, host, default_host, web_app_hosts) + if host.app_base # we'll try setting a common parent : + require 'pathname'; app_path = Pathname.new(app_root) + base_path = Pathname.new(host.app_base) + unless app_path.exist? + app_path = app_path.relative_path_from(base_path) rescue app_path + end + app_real_path = begin; app_path.realpath.to_s; rescue + logger.warn "Application root #{app_root} does not exist !" + return + end + base_parent = false + 2.times do + begin + break if base_parent = ( app_real_path.index(base_path.realpath.to_s) == 0 ) + rescue => e + logger.warn "Host #{host.name.inspect} app_base does not exist," << + " try configuring an absolute path or create it\n (#{e.message})" + return + end + base_path = base_path.parent + end + if base_parent + return if base_path.to_s == host.app_base + host.app_base = base_path.realpath.to_s + unless web_app_hosts.include?(host) + logger.info "Changing (configured) app_base for host #{host.name.inspect}" << + " (#{host.app_base}) to include application root: #{app_path}" + end + else + logger.warn "Host #{host.name.inspect} app_base #{host.app_base.inspect}" << + " is not a parent directory for application root: #{app_path}" + end + else + host.app_base = app_path.parent.realpath.to_s end end - - def web_app_root_dir(config, default = Dir.pwd) - config[:root_dir] || config[:web_app_dir] || default + + def select_host_apps(app_holders, host) + app_holders.select do |app_holder| + host_name = app_holder.web_app.host_name + ( host_name || 'localhost' ) == host.name + end + end + + def find_host(name, host_config, tomcat = nil) + if tomcat.nil? # assume 2 args (host_config, tomcat) + tomcat = host_config; host_config = name + end + + if host_config.is_a?(Array) + names = host_config + elsif host_config.is_a?(String) || host_config.is_a?(Symbol) + names = [ host_config ] + elsif host_config # :localhost => { :aliases => 'local,127.0.0.1' ... } + names = [ host_config[:name] ||= name ] + aliases = host_config[:aliases] + if aliases && ! aliases.is_a?(Array) + aliases = aliases.split(',').each(&:strip!) + host_config[:aliases] = aliases + end + else # only name passed : + return tomcat.engine.find_child(name.to_s) + end + + hosts = tomcat.engine.find_children + for name in names # host_names + host = hosts.find do |host| + host.name == name || (host.aliases || []).include?(name) + end + return host if host + end + nil + end + + def web_app_root_dir(config, host = nil) + path = config[:root_dir] || config[:web_app_dir] || begin + path = config[:context_path] + ( path && path[0, 1] == '/' ) ? path[1..-1] : path + end || ( config[:context_name] ? config[:context_name].to_s : nil ) + + return nil if path.nil? + return File.expand_path(path) if File.exist?(path) + + if host + base = host.app_base + ( path && base ) ? File.join(base, path) : path + else + path + end end def generate_default_keystore(config) keystore_file = java.io.File.new(config[:keystoreFile])