#-- # Copyright (c) 2010 Engine Yard, Inc. # Copyright (c) 2007-2009 Sun Microsystems, Inc. # This source code is available under the MIT license. # See the file LICENSE.txt for details. #++ require 'ostruct' module Warbler # Warbler war file assembly configuration class. class Config TOP_DIRS = %w(app config lib log vendor) FILE = "config/warble.rb" DEFAULT_GEM_PATH = '/WEB-INF/gems' BUILD_GEMS = %w(warbler rake rcov) # Deprecated: No longer has any effect. attr_accessor :staging_dir # A hash of files that Warbler will build into the .war file. Keys # to the hash are filenames in the .war, values are either nil # (for a directory entry), the source filenames or IO objects for # temporary file contents. This will be filled with files by the # various Warbler tasks as they run. You can add arbitrary # filenames to this hash if you wish. attr_accessor :files # Directory where the war file will be written. Can be used to direct # Warbler to place your war file directly in your application server's # autodeploy directory. Defaults to the root of the Rails directory. attr_accessor :autodeploy_dir # Top-level directories to be copied into WEB-INF. Defaults to # names in TOP_DIRS attr_accessor :dirs # Additional files beyond the top-level directories to include in the # WEB-INF directory attr_accessor :includes # Files to exclude from the WEB-INF directory attr_accessor :excludes # Java classes and other files to copy to WEB-INF/classes attr_accessor :java_classes # Java libraries to copy to WEB-INF/lib attr_accessor :java_libs # Rubygems to install into the webapp. attr_accessor :gems # Whether to include dependent gems (default true) attr_accessor :gem_dependencies # Whether to exclude **/*.log files (default is true) attr_accessor :exclude_logs # Public HTML directory file list, to be copied into the root of the war attr_accessor :public_html # Container of pathmaps used for specifying source-to-destination transformations # under various situations (public_html and java_classes are two # entries in this structure). attr_accessor :pathmaps # Name of war file (without the .war), defaults to the directory name containing # the Rails application attr_accessor :war_name # Name of a MANIFEST.MF template to use. attr_accessor :manifest_file # Files for WEB-INF directory (next to web.xml). Contains web.xml by default. # If there are .erb files they will be processed with webxml config. attr_accessor :webinf_files # Use Bundler to locate gems if Gemfile is found. Default is true. attr_accessor :bundler # Path to the pre-bundled gem directory inside the war file. Default is '/WEB-INF/gems'. # This also sets 'gem.path' inside web.xml. attr_accessor :gem_path # Extra configuration for web.xml. Controls how the dynamically-generated web.xml # file is generated. # # * webxml.jndi -- the name of one or more JNDI data sources name to be # available to the application. Places appropriate <resource-ref> entries # in the file. # * webxml.ignored -- array of key names that will be not used to # generate a context param. Defaults to ['jndi', 'booter'] # # Any other key/value pair placed in the open structure will be dumped as a # context parameter in the web.xml file. Some of the recognized values are: # # * webxml.rails.env -- the Rails environment to use for the # running application, usually either development or production (the # default). # * webxml.gem.path -- the path to your bundled gem directory # * webxml.jruby.min.runtimes -- minimum number of pooled runtimes to # keep around during idle time # * webxml.jruby.max.runtimes -- maximum number of pooled Rails # application runtimes # # Note that if you attempt to access webxml configuration keys in a conditional, # you might not obtain the result you want. For example: # <%= webxml.maybe.present.key || 'default' %> # doesn't yield the right result. Instead, you need to generate the context parameters: # <%= webxml.context_params['maybe.present.key'] || 'default' %> attr_accessor :webxml def initialize(warbler_home = WARBLER_HOME) @warbler_home = warbler_home @dirs = TOP_DIRS.select {|d| File.directory?(d)} @includes = FileList[] @excludes = FileList[] @java_libs = default_jar_files @java_classes = FileList[] @gems = Warbler::Gems.new @gem_path = DEFAULT_GEM_PATH @gem_dependencies = true @exclude_logs = true @public_html = FileList["public/**/*"] @pathmaps = default_pathmaps @webxml = default_webxml_config @rails_root = File.expand_path(defined?(RAILS_ROOT) ? RAILS_ROOT : Dir.getwd) @war_name = File.basename(@rails_root) @bundler = true @webinf_files = default_webinf_files auto_detect_frameworks yield self if block_given? update_gem_path detect_bundler_gems @excludes += warbler_vendor_excludes(warbler_home) @excludes += FileList["**/*.log"] if @exclude_logs end def gems=(value) @gems = Warbler::Gems.new(value) end def relative_gem_path @gem_path[1..-1] end private def warbler_vendor_excludes(warbler_home) warbler = File.expand_path(warbler_home) if warbler =~ %r{^#{@rails_root}/(.*)} FileList["#{$1}"] else [] end end def default_pathmaps p = OpenStruct.new p.public_html = ["%{public/,}p"] p.java_libs = ["WEB-INF/lib/%f"] p.java_classes = ["WEB-INF/classes/%p"] p.application = ["WEB-INF/%p"] p.webinf = ["WEB-INF/%{.erb$,}f"] p.gemspecs = ["#{relative_gem_path}/specifications/%f"] p.gems = ["#{relative_gem_path}/gems/%p"] p end def default_webxml_config c = WebxmlOpenStruct.new c.rails.env = ENV['RAILS_ENV'] || 'production' c.public.root = '/' c.jndi = nil c.ignored = %w(jndi booter) c end def default_webinf_files webxml = if File.exist?("config/web.xml") "config/web.xml" elsif File.exist?("config/web.xml.erb") "config/web.xml.erb" else "#{WARBLER_HOME}/web.xml.erb" end FileList[webxml] end def update_gem_path if @gem_path != DEFAULT_GEM_PATH @gem_path = "/#{@gem_path}" unless @gem_path =~ %r{^/} sub_gem_path = @gem_path[1..-1] @pathmaps.gemspecs.each {|p| p.sub!(DEFAULT_GEM_PATH[1..-1], sub_gem_path)} @pathmaps.gems.each {|p| p.sub!(DEFAULT_GEM_PATH[1..-1], sub_gem_path)} @webxml["gem"]["path"] = @gem_path end end def detect_bundler_gems if @bundler && File.exist?("Gemfile") @gems.clear @gem_dependencies = false # Bundler takes care of these require 'bundler' env = Bundler.respond_to?(:runtime) ? Bundler.runtime : Bundler.load def Bundler.env_file; root.join(::Warbler::Runtime::WAR_ENV); end env.extend Warbler::Runtime env.gem_path = @gem_path env.write_war_environment env.war_specs.each {|spec| @gems << spec } else @bundler = false end end def default_jar_files require 'jruby-jars' require 'jruby-rack' FileList[JRubyJars.core_jar_path, JRubyJars.stdlib_jar_path, JRubyJars.jruby_rack_jar_path] end def auto_detect_frameworks return unless Warbler.framework_detection if File.exist?(".bundle/environment.rb") begin # Don't want Bundler to load from .bundle/environment mv(".bundle/environment.rb",".bundle/environment-save.rb", :verbose => false) auto_detect_rails || auto_detect_merb || auto_detect_rackup ensure mv(".bundle/environment-save.rb",".bundle/environment.rb", :verbose => false) end else auto_detect_rails || auto_detect_merb || auto_detect_rackup end end def auto_detect_rails return false unless task = Warbler.project_application.lookup("environment") task.invoke rescue nil return false unless defined?(::Rails) @dirs << "tmp" if File.directory?("tmp") @webxml.booter = :rails unless (defined?(Rails.vendor_rails?) && Rails.vendor_rails?) || File.directory?("vendor/rails") @gems["rails"] = Rails::VERSION::STRING end if defined?(Rails.configuration.gems) Rails.configuration.gems.each do |g| @gems << Gem::Dependency.new(g.name, g.requirement) if Dir["vendor/gems/#{g.name}*"].empty? end end if defined?(Rails.configuration.threadsafe!) && (defined?(Rails.configuration.allow_concurrency) && # Rails 3 Rails.configuration.allow_concurrency && Rails.configuration.preload_frameworks) || (defined?(Rails.configuration.action_controller.allow_concurrency) && # Rails 2 Rails.configuration.action_controller.allow_concurrency && Rails.configuration.action_controller.preload_frameworks) @webxml.jruby.max.runtimes = 1 end true end def auto_detect_merb return false unless task = Warbler.project_application.lookup("merb_env") task.invoke rescue nil return false unless defined?(::Merb) @webxml.booter = :merb if defined?(Merb::BootLoader::Dependencies.dependencies) Merb::BootLoader::Dependencies.dependencies.each {|g| @gems << g } else warn "unable to auto-detect Merb dependencies; upgrade to Merb 1.0 or greater" end true end def auto_detect_rackup return false unless File.exist?("config.ru") @webxml.booter = :rack @webxml.rackup = File.read("config.ru") true end end # Helper class for holding arbitrary config.webxml values for injecting into +web.xml+. class WebxmlOpenStruct < OpenStruct %w(java com org javax).each {|name| undef_method name if Object.methods.include?(name) } def initialize(key = 'webxml') @key = key @table = Hash.new {|h,k| h[k] = WebxmlOpenStruct.new(k) } end def servlet_context_listener case self.booter when :rack "org.jruby.rack.RackServletContextListener" when :merb "org.jruby.rack.merb.MerbServletContextListener" else # :rails, default "org.jruby.rack.rails.RailsServletContextListener" end end def [](key) new_ostruct_member(key) send(key) end def []=(key, value) new_ostruct_member(key) send("#{key}=", value) end def context_params require 'cgi' params = {} @table.each do |k,v| case v when WebxmlOpenStruct nested_params = v.context_params nested_params.each do |nk,nv| params["#{CGI::escapeHTML(k.to_s)}.#{nk}"] = nv end else params[CGI::escapeHTML(k.to_s)] = CGI::escapeHTML(v.to_s) end end params.delete_if {|k,v| ['ignored', *ignored].include?(k.to_s) } params end def to_s "No value for '#@key' found" end end end