launcher.rb
1 # Drop all environment variables so they don't interfere with our war file gem loading. 2 java.lang.System.set_property("aspace.launcher.base", ENV['ASPACE_LAUNCHER_BASE']) 3 4 require 'jruby' 5 require 'aspace_gems' 6 ASpaceGems.setup 7 8 require_relative 'launcher_init' 9 require 'asutils' 10 require 'fileutils' 11 require 'securerandom' 12 require 'uri' 13 require 'ashttp' 14 15 $server_prepare_hooks = [] 16 17 18 # Add a new hook that will be called as a Jetty server is prepared. 19 # Each hook will be called with: 20 # 21 # hook.call(server, port, [{:war => '/path/to/app.war', :path => '/uri'}, ...]) 22 # 23 # Nothing uses this feature by default, but you could use it for performing 24 # further configuration on the Jetty server object (such as sizing thread pools) 25 # by adding a hook from code in your ASPACE_LAUNCHER_BASE/launcher_rc.rb file. 26 # 27 def add_server_prepare_hook(callback) 28 $server_prepare_hooks << callback 29 end 30 31 32 def start_server(port, *webapps) 33 server = org.eclipse.jetty.server.Server.new 34 35 configuration = org.eclipse.jetty.server.HttpConfiguration.new 36 configuration.request_header_size = AppConfig[:jetty_request_buffer_size_bytes] || 64 * 1024 37 configuration.response_header_size = AppConfig[:jetty_response_buffer_size_bytes] || 64 * 1024 38 configuration.send_date_header 39 configuration.send_server_version 40 configuration.send_xpowered_by 41 42 http = org.eclipse.jetty.server.HttpConnectionFactory.new(configuration) 43 connector = org.eclipse.jetty.server.ServerConnector.new(server, http) 44 connector.port = port 45 server.add_connector(connector) 46 47 contexts = webapps.map do |webapp| 48 if webapp[:war] 49 context = org.eclipse.jetty.webapp.WebAppContext.new 50 context.server = server 51 context.context_path = webapp[:path] 52 context.war = webapp[:war] 53 context.class_loader = org.eclipse.jetty.webapp.WebAppClassLoader.new(JRuby.runtime.jruby_class_loader, context) 54 55 context 56 elsif webapp[:static_dirs] 57 handlers = org.eclipse.jetty.server.handler.HandlerList.new 58 59 Array(webapp[:static_dirs]).each do |static_dir| 60 handler = org.eclipse.jetty.server.handler.ResourceHandler.new 61 handler.set_resource_base(static_dir) 62 63 handlers.add_handler(handler) 64 end 65 66 ctx = org.eclipse.jetty.server.handler.ContextHandler.new(webapp[:path]) 67 ctx.set_handler(handlers) 68 69 ctx 70 else 71 raise "Unrecognised webapp definition: #{webapp.inspect}" 72 end 73 end 74 75 # this establishes a shutdown port on jetty. use the context xkcd so there is 76 # little change of this overlapping on a server 77 # posting to /xkcd/shutdown?password= will stop that jetty instance 78 if AppConfig[:use_jetty_shutdown_handler] 79 shtctx = org.eclipse.jetty.server.handler.ContextHandler.new(AppConfig[:jetty_shutdown_path]) 80 shtctx.set_handler(org.eclipse.jetty.server.handler.ShutdownHandler.new(server, generate_secret_for("jetty_shutdown"))) 81 contexts << shtctx 82 end 83 collection = org.eclipse.jetty.server.handler.ContextHandlerCollection.new 84 collection.handlers = contexts 85 server.handler = collection 86 87 $server_prepare_hooks.each do |hook| 88 hook.call(server, port, webapps) 89 end 90 91 server.start 92 93 server 94 end 95 96 97 def generate_secret_for(secret) 98 file = File.join(AppConfig[:data_directory], "#{secret}.dat") 99 100 if !File.exist?(file) 101 File.write(file, SecureRandom.hex) 102 103 puts "****" 104 puts "**** INFO: Generated a secret key for AppConfig[:#{secret}]" 105 puts "**** and stored it in #{file}." 106 puts "****" 107 unless secret == "shutdown" 108 puts "**** If you're running ArchivesSpace in a clustered setup, you will" 109 puts "**** need to make sure that all instances share the same value for this" 110 puts "**** setting. You can do that by setting a value for AppConfig[:#{secret}]" 111 puts "**** in your config.rb file." 112 puts "****" 113 end 114 puts "" 115 end 116 117 File.read(file) 118 end 119 120 121 def main 122 java.lang.System.set_property("org.eclipse.jetty.webapp.LEVEL", "WARN") 123 java.lang.System.set_property("org.eclipse.jetty.server.handler.LEVEL", "WARN") 124 125 126 String tempdir = File.join(AppConfig[:data_directory], "tmp") 127 128 FileUtils.mkdir_p(tempdir) 129 130 java.lang.System.set_property("java.io.tmpdir", tempdir) 131 132 [:search_user_secret, :public_user_secret, :staff_user_secret].each do |property| 133 if !AppConfig.has_key?(property) 134 java.lang.System.set_property("aspace.config.#{property}", SecureRandom.hex) 135 end 136 end 137 138 cookie_secrets = [:frontend_cookie_secret, :public_cookie_secret].each do |secret| 139 if !AppConfig.has_key?("#{secret}".intern) 140 java.lang.System.set_property("aspace.config.#{secret}", 141 generate_secret_for(secret)) 142 end 143 end 144 145 servers = [] 146 147 begin 148 aspace_base = java.lang.System.get_property("ASPACE_LAUNCHER_BASE") 149 servers << start_server(URI(AppConfig[:backend_url]).port, {:war => File.join(aspace_base, 'wars', 'backend.war'), :path => '/'}) if AppConfig[:enable_backend] 150 151 servers << start_server(URI(AppConfig[:indexer_url]).port, 152 {:war => File.join(aspace_base, 'wars', 'indexer.war'), :path => '/aspace-indexer'}) if AppConfig[:enable_indexer] 153 154 servers << start_server(URI(AppConfig[:frontend_url]).port, 155 {:war => File.join(aspace_base, 'wars', 'frontend.war'), :path => '/'}, 156 {:static_dirs => ASUtils.find_local_directories("frontend/assets"), 157 :path => "#{AppConfig[:frontend_proxy_prefix]}assets"}) if AppConfig[:enable_frontend] 158 159 servers << start_server(URI(AppConfig[:public_url]).port, 160 {:war => File.join(aspace_base, 'wars', 'public.war'), :path => '/'}, 161 {:static_dirs => ASUtils.find_local_directories("public/assets"), 162 :path => "#{AppConfig[:public_proxy_prefix]}assets"}) if AppConfig[:enable_public] 163 164 servers << start_server(URI(AppConfig[:docs_url]).port, 165 {:static_dirs => File.join(aspace_base, "docs", "_site"), :path => '/archivesspace'}) if AppConfig[:enable_docs] 166 167 servers << start_server(URI(AppConfig[:oai_url]).port, 168 {:war => File.join(aspace_base, 'wars', 'oai.war'), :path => '/'}) if AppConfig[:enable_oai] 169 170 171 rescue 172 # If anything fails on startup, dump a diagnostic file. 173 ASUtils.dump_diagnostics($!) 174 end 175 176 puts <<~EOF 177 ************************************************************ 178 Welcome to ArchivesSpace! 179 You can now point your browser to #{AppConfig[:frontend_url]} 180 ************************************************************ 181 EOF 182 183 servers.first&.join 184 end 185 186 187 def stop_server(uri) 188 puts "Stopping : #{uri.to_s}" 189 190 shutdown_uri = uri.clone 191 shutdown_uri.path = "/xkcd/shutdown" 192 response = ASHTTP.post_form(shutdown_uri, 'token' => generate_secret_for("jetty_shutdown")) 193 194 if response.code != 404 195 #now we check to see if indeed the server has shutdown. should return an 196 #connection error. 197 ASHTTP.get(uri) 198 199 puts "Jetty Shutdown error on #{uri.to_s}" 200 puts "Shutdown returned: #{response.code}" 201 puts "#{response.body}" 202 else 203 puts "Jetty Shutdown handler does not exist" 204 end 205 rescue Errno::ECONNREFUSED, SocketError, EOFError => se 206 # A little odd, but when jetty shutdowns it just shutsdown and no response is 207 # sent. Some jrubys handle this differently, but most raise either a 208 # Connection, socket, or a rbuff_fill execption. When this happens, we can 209 # assume the shutdown has worked. 210 puts "#{uri.to_s} not running" 211 rescue Exception => e 212 # Server is possibly still running so overall shutdown may fail 213 puts "Unexpected shutdown error" 214 puts e.inspect 215 end 216 217 218 def stop 219 if AppConfig[:use_jetty_shutdown_handler] 220 stop_server(URI(AppConfig[:frontend_url])) if AppConfig[:enable_frontend] 221 stop_server(URI(AppConfig[:public_url])) if AppConfig[:enable_public] 222 stop_server(URI(AppConfig[:docs_url])) if AppConfig[:enable_docs] 223 stop_server(URI(AppConfig[:indexer_url])) if AppConfig[:enable_indexer] 224 stop_server(URI(AppConfig[:backend_url])) if AppConfig[:enable_backend] 225 pid_file = File.join(AppConfig[:data_directory], ".archivesspace.pid" ) 226 FileUtils.rm(pid_file) if File.exist?(pid_file) 227 java.lang.System.exit(0) 228 else 229 puts "****" 230 puts "AppConfig[:use_jetty_shutdown_handler] has not been set. " 231 puts "To use this shutdown command, you must update the config.rb file." 232 puts "****" 233 end 234 rescue => e 235 puts "There has been an error issueing shutdown to Jetty." 236 puts e.inspect 237 end 238 239 launcher_rc = File.join(java.lang.System.get_property("ASPACE_LAUNCHER_BASE"), "launcher_rc.rb") 240 241 if java.lang.System.get_property("ASPACE_LAUNCHER_BASE") && File.exist?(launcher_rc) 242 load File.absolute_path(launcher_rc) 243 end 244 245 246 if ARGV[0] == 'stop' 247 stop 248 else 249 main 250 end