# Drop all environment variables so they don't interfere with our war file gem loading. java.lang.System.set_property("aspace.launcher.base", ENV['ASPACE_LAUNCHER_BASE']) require 'jruby' require 'aspace_gems' ASpaceGems.setup require_relative 'launcher_init' require 'asutils' require 'fileutils' require 'securerandom' require 'uri' require 'ashttp' $server_prepare_hooks = [] # Add a new hook that will be called as a Jetty server is prepared. # Each hook will be called with: # # hook.call(server, port, [{:war => '/path/to/app.war', :path => '/uri'}, ...]) # # Nothing uses this feature by default, but you could use it for performing # further configuration on the Jetty server object (such as sizing thread pools) # by adding a hook from code in your ASPACE_LAUNCHER_BASE/launcher_rc.rb file. # def add_server_prepare_hook(callback) $server_prepare_hooks << callback end def start_server(port, *webapps) server = org.eclipse.jetty.server.Server.new configuration = org.eclipse.jetty.server.HttpConfiguration.new configuration.request_header_size = AppConfig[:jetty_request_buffer_size_bytes] || 64 * 1024 configuration.response_header_size = AppConfig[:jetty_response_buffer_size_bytes] || 64 * 1024 configuration.send_date_header configuration.send_server_version configuration.send_xpowered_by http = org.eclipse.jetty.server.HttpConnectionFactory.new(configuration) connector = org.eclipse.jetty.server.ServerConnector.new(server, http) connector.port = port server.add_connector(connector) contexts = webapps.map do |webapp| if webapp[:war] context = org.eclipse.jetty.webapp.WebAppContext.new context.server = server context.context_path = webapp[:path] context.war = webapp[:war] context.class_loader = org.eclipse.jetty.webapp.WebAppClassLoader.new(JRuby.runtime.jruby_class_loader, context) context elsif webapp[:static_dirs] handlers = org.eclipse.jetty.server.handler.HandlerList.new Array(webapp[:static_dirs]).each do |static_dir| handler = org.eclipse.jetty.server.handler.ResourceHandler.new handler.set_resource_base(static_dir) handlers.add_handler(handler) end ctx = org.eclipse.jetty.server.handler.ContextHandler.new(webapp[:path]) ctx.set_handler(handlers) ctx else raise "Unrecognised webapp definition: #{webapp.inspect}" end end # this establishes a shutdown port on jetty. use the context xkcd so there is # little change of this overlapping on a server # posting to /xkcd/shutdown?password= will stop that jetty instance if AppConfig[:use_jetty_shutdown_handler] shtctx = org.eclipse.jetty.server.handler.ContextHandler.new(AppConfig[:jetty_shutdown_path]) shtctx.set_handler(org.eclipse.jetty.server.handler.ShutdownHandler.new(server, generate_secret_for("jetty_shutdown"))) contexts << shtctx end collection = org.eclipse.jetty.server.handler.ContextHandlerCollection.new collection.handlers = contexts server.handler = collection $server_prepare_hooks.each do |hook| hook.call(server, port, webapps) end server.start server end def generate_secret_for(secret) file = File.join(AppConfig[:data_directory], "#{secret}.dat") if !File.exist?(file) File.write(file, SecureRandom.hex) puts "****" puts "**** INFO: Generated a secret key for AppConfig[:#{secret}]" puts "**** and stored it in #{file}." puts "****" unless secret == "shutdown" puts "**** If you're running ArchivesSpace in a clustered setup, you will" puts "**** need to make sure that all instances share the same value for this" puts "**** setting. You can do that by setting a value for AppConfig[:#{secret}]" puts "**** in your config.rb file." puts "****" end puts "" end File.read(file) end def main java.lang.System.set_property("org.eclipse.jetty.webapp.LEVEL", "WARN") java.lang.System.set_property("org.eclipse.jetty.server.handler.LEVEL", "WARN") String tempdir = File.join(AppConfig[:data_directory], "tmp") FileUtils.mkdir_p(tempdir) java.lang.System.set_property("java.io.tmpdir", tempdir) [:search_user_secret, :public_user_secret, :staff_user_secret].each do |property| if !AppConfig.has_key?(property) java.lang.System.set_property("aspace.config.#{property}", SecureRandom.hex) end end cookie_secrets = [:frontend_cookie_secret, :public_cookie_secret].each do |secret| if !AppConfig.has_key?("#{secret}".intern) java.lang.System.set_property("aspace.config.#{secret}", generate_secret_for(secret)) end end servers = [] begin aspace_base = java.lang.System.get_property("ASPACE_LAUNCHER_BASE") servers << start_server(URI(AppConfig[:backend_url]).port, {:war => File.join(aspace_base, 'wars', 'backend.war'), :path => '/'}) if AppConfig[:enable_backend] servers << start_server(URI(AppConfig[:indexer_url]).port, {:war => File.join(aspace_base, 'wars', 'indexer.war'), :path => '/aspace-indexer'}) if AppConfig[:enable_indexer] servers << start_server(URI(AppConfig[:frontend_url]).port, {:war => File.join(aspace_base, 'wars', 'frontend.war'), :path => '/'}, {:static_dirs => ASUtils.find_local_directories("frontend/assets"), :path => "#{AppConfig[:frontend_proxy_prefix]}assets"}) if AppConfig[:enable_frontend] servers << start_server(URI(AppConfig[:public_url]).port, {:war => File.join(aspace_base, 'wars', 'public.war'), :path => '/'}, {:static_dirs => ASUtils.find_local_directories("public/assets"), :path => "#{AppConfig[:public_proxy_prefix]}assets"}) if AppConfig[:enable_public] servers << start_server(URI(AppConfig[:docs_url]).port, {:static_dirs => File.join(aspace_base, "docs", "_site"), :path => '/archivesspace'}) if AppConfig[:enable_docs] servers << start_server(URI(AppConfig[:oai_url]).port, {:war => File.join(aspace_base, 'wars', 'oai.war'), :path => '/'}) if AppConfig[:enable_oai] rescue # If anything fails on startup, dump a diagnostic file. ASUtils.dump_diagnostics($!) end puts <<~EOF ************************************************************ Welcome to ArchivesSpace! You can now point your browser to #{AppConfig[:frontend_url]} ************************************************************ EOF servers.first&.join end def stop_server(uri) puts "Stopping : #{uri.to_s}" shutdown_uri = uri.clone shutdown_uri.path = "/xkcd/shutdown" response = ASHTTP.post_form(shutdown_uri, 'token' => generate_secret_for("jetty_shutdown")) if response.code != 404 #now we check to see if indeed the server has shutdown. should return an #connection error. ASHTTP.get(uri) puts "Jetty Shutdown error on #{uri.to_s}" puts "Shutdown returned: #{response.code}" puts "#{response.body}" else puts "Jetty Shutdown handler does not exist" end rescue Errno::ECONNREFUSED, SocketError, EOFError => se # A little odd, but when jetty shutdowns it just shutsdown and no response is # sent. Some jrubys handle this differently, but most raise either a # Connection, socket, or a rbuff_fill execption. When this happens, we can # assume the shutdown has worked. puts "#{uri.to_s} not running" rescue Exception => e # Server is possibly still running so overall shutdown may fail puts "Unexpected shutdown error" puts e.inspect end def stop if AppConfig[:use_jetty_shutdown_handler] stop_server(URI(AppConfig[:frontend_url])) if AppConfig[:enable_frontend] stop_server(URI(AppConfig[:public_url])) if AppConfig[:enable_public] stop_server(URI(AppConfig[:docs_url])) if AppConfig[:enable_docs] stop_server(URI(AppConfig[:indexer_url])) if AppConfig[:enable_indexer] stop_server(URI(AppConfig[:backend_url])) if AppConfig[:enable_backend] pid_file = File.join(AppConfig[:data_directory], ".archivesspace.pid" ) FileUtils.rm(pid_file) if File.exist?(pid_file) java.lang.System.exit(0) else puts "****" puts "AppConfig[:use_jetty_shutdown_handler] has not been set. " puts "To use this shutdown command, you must update the config.rb file." puts "****" end rescue => e puts "There has been an error issueing shutdown to Jetty." puts e.inspect end launcher_rc = File.join(java.lang.System.get_property("ASPACE_LAUNCHER_BASE"), "launcher_rc.rb") if java.lang.System.get_property("ASPACE_LAUNCHER_BASE") && File.exist?(launcher_rc) load File.absolute_path(launcher_rc) end if ARGV[0] == 'stop' stop else main end