mizuno-aspace 9.4.44

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,292 @@
1
+ require 'ffi'
2
+ require 'net/http'
3
+ require 'choice'
4
+ require 'childprocess'
5
+ require 'fileutils'
6
+ require 'etc'
7
+ require 'rack'
8
+ require 'mizuno'
9
+ require 'mizuno/choices'
10
+ require 'mizuno/server'
11
+ require 'rack/handler/mizuno'
12
+
13
+ module Mizuno
14
+ require 'rbconfig'
15
+
16
+ module Daemonizer
17
+ def self.included?(base)
18
+ if(Config::CONFIG['host_os'] =~ /mswin|mingw/)
19
+ base.send(:extend, StubbedClassMethods)
20
+ else
21
+ base.send(:extend, UnixClassMethods)
22
+ base.send(:class_eval) do
23
+ extend FFI::Library
24
+ ffi_lib 'c'
25
+ attach_function :_setuid, :setuid, [ :uint ], :int
26
+ attach_function :_setgid, :setgid, [ :uint ], :int
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ module UnixClassMethods
33
+ #
34
+ # Switch the process over to a new user id; will abort the
35
+ # process if it fails. _options_ is the full list of options
36
+ # passed to a server.
37
+ #
38
+ def setuid(options)
39
+ entry = Etc.getpwnam(options[:user])
40
+ die("Can't find --user named '#{options[:user]}'") \
41
+ unless entry
42
+ return unless (_setuid(entry.uid) != 0)
43
+ die("Can't switch to user '#{options[:user]}'")
44
+ end
45
+
46
+ #
47
+ # Like setuid, but for groups.
48
+ #
49
+ def setgid(options)
50
+ entry = Etc.getgrnam(options[:group])
51
+ die("Can't find --group named '#{options[:group]}'") \
52
+ unless entry
53
+ return unless (_setgid(entry.gid) != 0)
54
+ die("Can't switch to group '#{options[:group]}'")
55
+ end
56
+ end
57
+
58
+ module StubbedClassMethods
59
+ def setuid(options)
60
+ end
61
+
62
+ def setgid(options)
63
+ end
64
+ end
65
+ end
66
+
67
+ #
68
+ # Launches Mizuno when called from the command-line, and handles
69
+ # damonization via FFI.
70
+ #
71
+ # Daemonization code based on Spoon.
72
+ #
73
+ class Runner
74
+ include Daemonizer
75
+
76
+ #
77
+ # Launch Jetty, optionally as a daemon.
78
+ #
79
+ def Runner.start!
80
+ # Default rackup is in config.ru
81
+ config = (Choice.rest.first or "config.ru")
82
+
83
+ # Create an options hash with only symbols.
84
+ choices = Choice.choices.merge(:config => config)
85
+ options = Hash[choices.map { |k, v| [ k.to_sym, v ] }]
86
+
87
+ # Resolve relative paths to the logfile, etc.
88
+ root = options[:root]
89
+ options[:pidfile] = Runner.resolve_path(root, options[:pidfile])
90
+ options[:log] = Runner.resolve_path(root, options[:log])
91
+ options[:public] = Runner.resolve_path(root, options[:public])
92
+
93
+ # Require multiple libraries.
94
+ options.delete(:require).each { |r| require r }
95
+
96
+ # Handle daemon-related commands.
97
+ Runner.status(options) if options.delete(:status)
98
+ Runner.reload(options) if options.delete(:reload)
99
+ Runner.stop(options) if options.delete(:stop)
100
+ Runner.kill(options) if options.delete(:kill)
101
+ Runner.daemonize(options) if options.delete(:daemonize)
102
+
103
+ # Fire up Mizuno as if it was called from Rackup.
104
+ Runner.start(options)
105
+ end
106
+
107
+ def Runner.start(options)
108
+ Dir.chdir(options[:root])
109
+ Logger.configure(options)
110
+ ENV['RACK_ENV'] = options[:env]
111
+ server = Rack::Server.new
112
+ server.options = options.merge(:server => 'mizuno',
113
+ :environment => options[:env])
114
+ server.start
115
+ end
116
+
117
+ #
118
+ # Relaunch as a daemon.
119
+ #
120
+ def Runner.daemonize(options)
121
+ # Ensure that Mizuno isn't running.
122
+ Runner.pid(options) and die("Mizuno is already running.")
123
+
124
+ # Build a command line that should launch JRuby with the
125
+ # appropriate options; this depends on the proper jruby
126
+ # being in the $PATH
127
+ config = options.delete(:config)
128
+ args = Mizuno::LAUNCH_ENV.concat(options.map { |k, v|
129
+ (v.to_s.empty?) ? nil : [ "--#{k}", v.to_s ] }.compact.flatten)
130
+ args.push(config)
131
+ args.unshift('jruby')
132
+
133
+ # Launch a detached child process.
134
+ child = ChildProcess.build(*args)
135
+ child.io.inherit!
136
+ child.detach = true
137
+ child.start
138
+ File.open(options[:pidfile], 'w') { |f| f.puts(child.pid) }
139
+
140
+ # Wait until the server starts or we time out waiting for it.
141
+ exit if wait_for_server(options, 60)
142
+ child.stop
143
+ die("Failed to start Mizuno.")
144
+ end
145
+
146
+ #
147
+ # Return the status of a running daemon.
148
+ #
149
+ def Runner.status(options)
150
+ die("Mizuno doesn't appear to be running.") \
151
+ unless (pid = Runner.pid(options))
152
+ die("Mizuno is running, but not online.") \
153
+ unless(wait_for_server(options))
154
+ die("Mizuno is running.", true)
155
+ end
156
+
157
+ #
158
+ # Reload a running daemon by SIGHUPing it.
159
+ #
160
+ def Runner.reload(options)
161
+ pid = Runner.pid(options)
162
+ return(Runner.daemonize(options)) \
163
+ if(pid.nil? and options.delete(:restart))
164
+ die("Mizuno is currently not running.") unless pid
165
+ Process.kill("HUP", pid)
166
+ die("Mizuno signaled to reload app.", true)
167
+ end
168
+
169
+ #
170
+ # Stop a running daemon (SIGKILL)
171
+ #
172
+ def Runner.stop(options)
173
+ pid = Runner.pid(options) or die("Mizuno isn't running.")
174
+ print "Stopping Mizuno..."
175
+ Process.kill("KILL", pid)
176
+ die("failed") unless wait_for_server_to_die(options)
177
+ FileUtils.rm(options[:pidfile])
178
+ die("stopped", true)
179
+ end
180
+
181
+ #
182
+ # Really stop a running daemon (SIGTERM)
183
+ #
184
+ def Runner.kill(options)
185
+ pid = Runner.pid(options) or die("Mizuno isn't running.")
186
+ $stderr.puts "Terminating Mizuno with extreme prejudice..."
187
+ Process.kill("TERM", pid)
188
+ die("failed") unless wait_for_server_to_die(options)
189
+ FileUtils.rm(options[:pidfile])
190
+ die("stopped", true)
191
+ end
192
+
193
+ #
194
+ # Transform a relative path to an absolute path.
195
+ #
196
+ def Runner.resolve_path(root, path)
197
+ return(path) unless path.is_a?(String)
198
+ return(path) if (path =~ /^\//)
199
+ File.expand_path(File.join(root, path))
200
+ end
201
+
202
+ #
203
+ # Fetches the PID from the :pidfile.
204
+ #
205
+ def Runner.pid(options)
206
+ options[:pidfile] or die("Speficy a --pidfile to daemonize.")
207
+ return unless File.exists?(options[:pidfile])
208
+ pid = File.read(options[:pidfile]).to_i
209
+
210
+ # FIXME: This is a hacky way to get the process list, but I
211
+ # haven't found a good cross-platform solution yet; this
212
+ # should work on MacOS and Linux, possibly Solaris and BSD,
213
+ # and almost definitely not on Windows.
214
+ process = `ps ax`.lines.select { |l| l =~ /^\s*#{pid}\s*/ }
215
+ return(pid) if (process.join =~ /\bmizuno\b/)
216
+
217
+ # Stale pidfile; remove.
218
+ $stderr.puts("Removing stale pidfile '#{options[:pidfile]}'")
219
+ FileUtils.rm(options[:pidfile])
220
+ return(nil)
221
+ end
222
+
223
+ #
224
+ # Wait until _timeout_ seconds for a successful http connection;
225
+ # returns true if we could connect and didn't get a server
226
+ # error, false otherwise.
227
+ #
228
+ def Runner.wait_for_server(options, timeout = 120)
229
+ force_time_out_at = Time.now + timeout
230
+ sleep_interval_for_next_retry = 0.1
231
+
232
+ begin
233
+ response = connect_to_server_as_client(options, timeout)
234
+ return(response.code.to_i < 500)
235
+ rescue Errno::ECONNREFUSED => error
236
+ return(false) if (Time.now > force_time_out_at)
237
+ sleep(sleep_interval_for_next_retry)
238
+ sleep_interval_for_next_retry *= 2
239
+ retry
240
+ rescue => error
241
+ puts "HTTP Error '#{error}'"
242
+ return(false)
243
+ end
244
+ end
245
+
246
+ #
247
+ # Like wait_for_server, but returns true when the server goes
248
+ # offline. If we hit _timeout_ seconds and the server is still
249
+ # responding, returns false.
250
+ #
251
+ def Runner.wait_for_server_to_die(options, timeout = 120)
252
+ force_time_out_at = Time.now + timeout
253
+ sleep_interval_for_next_retry = 0.1
254
+
255
+ begin
256
+ while (Time.now < force_time_out_at)
257
+ connect_to_server_as_client(options, timeout)
258
+ sleep(sleep_interval_for_next_retry)
259
+ sleep_interval_for_next_retry *= 2
260
+ end
261
+ return(false)
262
+ rescue Errno::ECONNREFUSED => error
263
+ return(true)
264
+ rescue => error
265
+ puts "**** http error: #{error}"
266
+ return(true)
267
+ end
268
+ end
269
+
270
+ def Runner.connect_to_server_as_client(server_options, timeout)
271
+ options = server_options.dup
272
+ options[:host] = '127.0.0.1' if options[:host] == "0.0.0.0"
273
+ Net::HTTP.start(options[:host], options[:port]) do |http|
274
+ http.read_timeout = timeout
275
+ http.get("/")
276
+ end
277
+ end
278
+
279
+ #
280
+ # Exit with a message and a status value.
281
+ #
282
+ # FIXME: Dump these in the logfile if called from Server?
283
+ #
284
+ def Runner.die(message, success = false)
285
+ $stderr.puts(message)
286
+ exit(success ? 0 : 1)
287
+ end
288
+ end
289
+ end
290
+
291
+ # Ensure that we shutdown the server on exit.
292
+ at_exit { Mizuno::Server.stop }
@@ -0,0 +1,167 @@
1
+ # FIXME: mizuno/http_server needs to still work, but we will throw out a
2
+ # deprecation notice and remove it in later versions.
3
+
4
+ require 'rack'
5
+ require 'rack/rewindable_input'
6
+ require 'mizuno'
7
+ Mizuno.require_jars(%w(jetty-continuation jetty-http jetty-io jetty-jmx
8
+ jetty-security jetty-server jetty-util servlet-api
9
+ rewindable-input-stream))
10
+ require 'mizuno/version'
11
+ require 'mizuno/rack/chunked'
12
+ require 'mizuno/rack_handler'
13
+ #require 'mizuno/logger'
14
+ require 'mizuno/reloader'
15
+
16
+ module Mizuno
17
+ class Server
18
+ java_import 'org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector'
19
+ java_import 'org.eclipse.jetty.util.thread.QueuedThreadPool'
20
+ java_import 'org.jruby.rack.servlet.RewindableInputStream'
21
+
22
+ # java_import "org.eclipse.jetty.server.ssl.SslSelectChannelConnector"
23
+
24
+ # attr_accessor :logger
25
+
26
+ @lock ||= Mutex.new
27
+
28
+ def Server.run(app, options = {})
29
+ @lock.synchronize do
30
+ return if @server
31
+ @server = new
32
+ @server.run(app, options)
33
+ end
34
+ end
35
+
36
+ def Server.stop
37
+ @lock.synchronize do
38
+ return unless @server
39
+ @server.stop
40
+ @server = nil
41
+ end
42
+ end
43
+
44
+ def Server.logger
45
+ Logger.logger
46
+ end
47
+
48
+ #
49
+ # Start up an instance of Jetty, running a Rack application.
50
+ # Options can be any of the follwing, and are not
51
+ # case-sensitive:
52
+ #
53
+ # :host::
54
+ # String specifying the IP address to bind to; defaults
55
+ # to 0.0.0.0.
56
+ #
57
+ # :port::
58
+ # String or integer with the port to bind to; defaults
59
+ # to 9292.
60
+ #
61
+ def run(app, options = {})
62
+ # Symbolize and downcase keys.
63
+ @options = options = Hash[options.map { |k, v|
64
+ [ k.to_s.downcase.to_sym, v ] }]
65
+ options[:quiet] ||= true if options[:embedded]
66
+
67
+ # Thread pool
68
+ threads = options[:threads] || 50
69
+ thread_pool = QueuedThreadPool.new
70
+ thread_pool.min_threads = options.fetch(:min_threads,
71
+ [ threads.to_i / 10, 5 ].max)
72
+ thread_pool.max_threads = [ threads.to_i, 10 ].max
73
+
74
+ # The Jetty server
75
+ # Logger.configure(options)
76
+ # @logger = Logger.logger
77
+ @server = Java.org.eclipse.jetty.server.Server.new(thread_pool)
78
+ # @server.setSendServerVersion(false)
79
+
80
+ # @server.set_thread_pool(thread_pool)
81
+
82
+ # Connector
83
+ connector = Java.org.eclipse.jetty.server.ServerConnector.new(@server)
84
+ connector.setReuseAddress(options.fetch(:reuse_address, false))
85
+ connector.setPort(options[:port].to_i)
86
+ connector.setHost(options[:host])
87
+ # max_header_size = options.fetch(:max_header_size, 32768)
88
+ # connector.setRequestHeaderSize(max_header_size)
89
+
90
+ @server.addConnector(connector)
91
+
92
+ # SSL Connector
93
+ #configure_https(options) if options[:ssl_port]
94
+
95
+ # Switch to a different user or group if we were asked to.
96
+ Runner.setgid(options) if options[:group]
97
+ Runner.setuid(options) if options[:user]
98
+
99
+ # Optionally wrap with Mizuno::Reloader.
100
+ threshold = (ENV['RACK_ENV'] == 'production' ? 10 : 1)
101
+ app = Mizuno::Reloader.new(app, threshold) \
102
+ if options[:reloadable]
103
+
104
+ # The servlet itself.
105
+ rack_handler = RackHandler.new(self)
106
+ rack_handler.rackup(app)
107
+
108
+ # Add the context to the server and start.
109
+ @server.set_handler(rack_handler)
110
+ @server.start
111
+ $stderr.printf("%s listening on %s:%s\n", version,
112
+ connector.host, connector.port) unless options[:quiet]
113
+
114
+ # If we're embedded, we're done.
115
+ return if options[:embedded]
116
+
117
+ # Stop the server when we get The Signal.
118
+ trap("SIGTERM") { @server.stop and exit }
119
+
120
+ # Join with the server thread, so that currently open file
121
+ # descriptors don't get closed by accident.
122
+ # http://www.ruby-forum.com/topic/209252
123
+ @server.join
124
+ end
125
+
126
+ #
127
+ # Shuts down an embedded Jetty instance.
128
+ #
129
+ def stop
130
+ return unless @server
131
+ $stderr.print "Stopping Jetty..." unless @options[:quiet]
132
+ @server.stop
133
+ $stderr.puts "done." unless @options[:quiet]
134
+ end
135
+
136
+ #
137
+ # Returns the full version string.
138
+ #
139
+ def version
140
+ "Mizuno #{Mizuno::VERSION} (Jetty #{Java.org.eclipse.jetty.server.Server.getVersion})"
141
+ end
142
+
143
+ #
144
+ # Wraps the Java InputStream for the level of Rack compliance
145
+ # desired.
146
+ #
147
+ def rewindable(request)
148
+ input = request.getInputStream
149
+
150
+ @options[:rewindable] ?
151
+ Rack::RewindableInput.new(input.to_io.binmode) :
152
+ RewindableInputStream.new(input).to_io.binmode
153
+ end
154
+
155
+ private
156
+
157
+ # def configure_https(options)
158
+ # connector = SslSelectChannelConnector.new
159
+ # connector.setPort(options[:ssl_port])
160
+ # factory = connector.getSslContextFactory
161
+ # factory.setKeyStore(options[:keystore] || "keystore")
162
+ # factory.setKeyStorePassword(options[:keystore_password])
163
+ # @server.addConnector(connector)
164
+ # self
165
+ # end
166
+ end
167
+ end
@@ -0,0 +1,3 @@
1
+ module Mizuno
2
+ VERSION = "9.4.44"
3
+ end
data/lib/mizuno.rb ADDED
@@ -0,0 +1,39 @@
1
+ #
2
+ # A Rack handler for Jetty 8.
3
+ #
4
+ # Written by Don Werve <don@madwombat.com>
5
+ #
6
+
7
+ require 'java'
8
+
9
+ # Save our launch environment for spawning children later.
10
+ module Mizuno
11
+ LAUNCH_ENV = $LOAD_PATH.map { |i| "-I#{i}" }.push($0)
12
+
13
+ HOME = File.expand_path(File.dirname(__FILE__))
14
+
15
+ #
16
+ # Tell log4j not to complain to the console about a missing
17
+ # log4j.properties file, as we configure it programmatically in
18
+ # Mizuno::Server (http://stackoverflow.com/questions/6849887)
19
+ #
20
+ # def Mizuno.initialize_logger
21
+ # require_jars(%w(log4j slf4j-api slf4j-log4j12))
22
+ # Java.org.apache.log4j.Logger.getRootLogger.setLevel( \
23
+ # Java.org.apache.log4j.Level::INFO)
24
+ # end
25
+
26
+ #
27
+ # Loads jarfiles independent of versions.
28
+ #
29
+ def Mizuno.require_jars(*names)
30
+ names.flatten.each do |name|
31
+ file = Dir[File.join(HOME, 'java', "#{name}-*.jar")].first
32
+ file ||= Dir[File.join(HOME, 'java', "#{name}.jar")].first
33
+ raise("Unknown or missing jar: #{name}") unless file
34
+ require file
35
+ end
36
+ end
37
+ end
38
+
39
+ #Mizuno.initialize_logger
@@ -0,0 +1,4 @@
1
+ require 'mizuno/server'
2
+
3
+ # Register ourselves with Rack when this file gets loaded.
4
+ Rack::Handler.register 'mizuno', 'Mizuno::Server'
data/mizuno.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ $:.push File.expand_path("../lib/mizuno", __FILE__)
2
+ require 'version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "mizuno-aspace"
6
+ spec.version = Mizuno::VERSION
7
+ spec.required_rubygems_version = Gem::Requirement.new(">= 1.2") \
8
+ if spec.respond_to?(:required_rubygems_version=)
9
+ spec.authors = [ "Don Werve", "ArchivesSpace Program Team" ]
10
+ spec.description = 'Jetty-powered running shoes for JRuby/Rack.'
11
+ spec.summary = 'Rack handler for Jetty 8 on JRuby. Features multithreading, event-driven I/O, and async support.'
12
+ spec.email = 'ArchivesSpaceHome@lyrasis.org'
13
+ spec.executables = [ "mizuno" ]
14
+ # FIXME: Use Dir.glob for this
15
+ spec.files = %w(.gitignore
16
+ README.markdown
17
+ LICENSE
18
+ Rakefile
19
+ Gemfile
20
+ mizuno.gemspec)
21
+ spec.files.concat(Dir['lib/**/*.rb'])
22
+ spec.files.concat(Dir['bin/*'])
23
+ spec.files.concat(Dir['lib/**/*.jar'])
24
+ # jars = Dir.entries("lib/java").grep(/\.jar$/)
25
+ # spec.files.concat(jars.map { |j| "lib/java/#{j}" })
26
+ spec.homepage = 'http://github.com/archivesspace/mizuno'
27
+ spec.has_rdoc = false
28
+ spec.require_paths = [ "lib" ]
29
+ spec.rubygems_version = '1.3.6'
30
+ spec.add_dependency('rack', '>= 1.0.0')
31
+ spec.add_dependency('ffi', '>= 1.0.0')
32
+ spec.add_dependency('choice', '>= 0.1.0')
33
+ spec.add_dependency('childprocess', '>= 0.2.6')
34
+ spec.add_development_dependency('rake')
35
+ spec.add_development_dependency('rspec', '>= 2.7.0')
36
+ spec.add_development_dependency('rspec-core', '>= 2.7.0')
37
+ spec.add_development_dependency('json_pure', '>= 1.6.0')
38
+ spec.add_development_dependency('nokogiri')
39
+ end