mizuno 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -11,27 +11,23 @@ container for your Rack application to run under JRuby, because Mizuno
11
11
  works just like Mongrel, WEBRick, Thin, or any other standard Rack
12
12
  handler.
13
13
 
14
- Mizuno is the fastest option for Rack applications on JRuby:
15
-
16
- Mizuno: 6106.66 req/s (mean)
17
- Jetty (via jruby-rack): 2011.67 req/s (mean)
18
- Mongrel: 1479.15 req/sec (mean)
19
-
20
14
  Mizuno also supports asynchronous request handling, via the Java Servlet
21
15
  3.0 asynchronous processing mechanism
22
16
 
23
- All the speed comes from Jetty 7; Mizuno just ties it to Rack through
17
+ All the speed comes from Jetty 8; Mizuno just ties it to Rack through
24
18
  JRuby's Ruby/Java integration layer.
25
19
 
26
20
  Note that Mizuno is NOT a direct replacement for jruby-rack or Warbler,
27
21
  because it doesn't produce WAR files or make any attempt to package a
28
22
  Rack application for installation in a Java web container.
29
23
 
30
- There's also a few features that I have yet to implement:
24
+ You can also run Mizuno via rackup:
25
+
26
+ rackup -s mizuno
27
+
28
+ Or with live reloading support:
31
29
 
32
- 1. Route Jetty's logs into Rack::Logger.
33
- 2. Add hooks for realtime monitoring of server performance.
34
- 3. Fast restarts of the Rack application.
30
+ mizuno --reloadable
35
31
 
36
32
  Mizuno is licensed under the Apache Public License, version 2.0; see
37
33
  the LICENSE file for details, and was developed on behalf of
data/Rakefile CHANGED
@@ -6,6 +6,8 @@ RSpec::Core::RakeTask.new(:spec)
6
6
 
7
7
  task :default => :spec
8
8
 
9
+ # http://rubydoc.info/gems/pompompom/1.1.3/frames
10
+
9
11
  namespace :jetty do
10
12
  desc "Grab the latest Jetty from its Maven repository."
11
13
  task :update do
@@ -40,15 +42,15 @@ namespace :jetty do
40
42
  # Inventory contents of the tarball we picked up.
41
43
  inventory = `tar tzf #{tempfile}`.split(/\n/)
42
44
 
43
- # Find replacements from our downloaded tarball for each of our jars.
45
+ # Figure out which JARs we should replce with tarball contents.
44
46
  replacements = {}
45
47
  Dir.entries(jar_path).each do |entry|
46
- next unless (entry =~ /^\w.*\d\.jar$/)
48
+ next unless ((entry =~ /^jetty-\w.*\d\.jar$/) \
49
+ or (entry =~ /^servlet-api.*\d\.jar$/))
47
50
  name = entry.sub(/\-\d.*$/, '')
48
51
  matcher = /\/#{name}\-[^\/]+\d\.jar$/
49
52
  archive_file = inventory.find { |i| i =~ matcher }
50
- raise("Archive missing replacement for #{entry}") \
51
- unless archive_file
53
+ next unless archive_file
52
54
  replacements[entry] = archive_file
53
55
  end
54
56
 
Binary file
Binary file
@@ -76,7 +76,7 @@ Choice.options do
76
76
  end
77
77
 
78
78
  separator ''
79
- separator 'Daemonization: '
79
+ separator 'Mizuno-specific options: '
80
80
 
81
81
  option :daemonize do
82
82
  short '-D'
@@ -105,7 +105,13 @@ Choice.options do
105
105
 
106
106
  option :reload do
107
107
  long '--reload'
108
- desc 'reload a running daemon'
108
+ desc 'reloads a running mizuno instance'
109
+ default false
110
+ end
111
+
112
+ option :reloadable do
113
+ long '--reloadable'
114
+ desc 'sets up live reloading via mizuno/reloader'
109
115
  default false
110
116
  end
111
117
 
@@ -1,5 +1,6 @@
1
1
  require 'mizuno/version'
2
2
  require 'mizuno/java_logger'
3
+ require 'mizuno/reloader'
3
4
 
4
5
  module Mizuno
5
6
  class HttpServer
@@ -73,6 +74,11 @@ module Mizuno
73
74
  app_handler = ServletContextHandler.new(nil, "/",
74
75
  ServletContextHandler::NO_SESSIONS)
75
76
 
77
+ # Optionally wrap with Mizuno::Reloader.
78
+ threshold = (ENV['RACK_ENV'] == 'production' ? 10 : 1)
79
+ app = Mizuno::Reloader.new(app, threshold) \
80
+ if options[:reloadable]
81
+
76
82
  # The servlet itself.
77
83
  rack_servlet = RackServlet.new
78
84
  rack_servlet.rackup(app)
@@ -175,8 +181,5 @@ module Mizuno
175
181
  end
176
182
  end
177
183
 
178
- # Register ourselves with Rack when this file gets loaded.
179
- Rack::Handler.register 'mizuno', 'Mizuno::HttpServer'
180
-
181
184
  # Ensure that we shutdown the server on exit.
182
185
  at_exit { Mizuno::HttpServer.stop }
@@ -0,0 +1,147 @@
1
+ require 'pathname'
2
+ require 'thread'
3
+
4
+ #:nodoc:
5
+ module Mizuno
6
+ #
7
+ # Middleware for reloading production applications; works exactly
8
+ # like Rack::Reloader, but rather than checking for any changed
9
+ # file, only looks at one specific file.
10
+ #
11
+ # Also allows for explicit reloading via a class method, as well as
12
+ # by sending a SIGHUP to the process.
13
+ #
14
+ class Reloader
15
+ class << self
16
+ attr_accessor :logger, :trigger, :reloaders
17
+ end
18
+
19
+ def Reloader.reload!
20
+ reloaders.each { |r| r.reload!(true) }
21
+ end
22
+
23
+ def initialize(app, interval = 1)
24
+ Thread.exclusive do
25
+ self.class.reloaders ||= []
26
+ self.class.reloaders << self
27
+ self.class.logger ||= Mizuno::HttpServer.logger
28
+ self.class.trigger ||= "tmp/restart.txt"
29
+ end
30
+
31
+ @app = app
32
+ @interval = interval
33
+ @trigger = self.class.trigger
34
+ @logger = self.class.logger
35
+ @updated = @threshold = Time.now.to_i
36
+ end
37
+
38
+ #
39
+ # Reload @app on request.
40
+ #
41
+ def call(env)
42
+ Thread.exclusive { reload! }
43
+ @app.call(env)
44
+ end
45
+
46
+ #
47
+ # Reloads the application if (a) we haven't reloaded in
48
+ # @interval seconds, (b) the trigger file has been touched
49
+ # since our last check, and (c) some other thread hasn't handled
50
+ # the update.
51
+ #
52
+ def reload!(force = false)
53
+ return unless (Time.now.to_i > @threshold)
54
+ @threshold = Time.now.to_i + @interval
55
+ return unless (force or \
56
+ ((timestamp = mtime(@trigger)).to_i > @updated))
57
+ timestamp ||= Time.now.to_i
58
+
59
+ # Check updated files to ensure they're loadable.
60
+ missing, errors = 0, 0
61
+ files = find_files_for_reload do |file, file_mtime|
62
+ next(missing += 1 and nil) unless file_mtime
63
+ next unless (file_mtime >= @updated)
64
+ next(errors += 1 and nil) unless verify(file)
65
+ file
66
+ end
67
+
68
+ # Cowardly fail if we can't load something.
69
+ @logger.debug("#{missing} files missing during reload.") \
70
+ if (missing > 0)
71
+ return(@logger.error("#{errors} errors, not reloading.")) \
72
+ if (errors > 0)
73
+
74
+ # Reload everything that's changed.
75
+ files.each do |file|
76
+ next unless file
77
+ @logger.info("Reloading #{file}")
78
+ load(file)
79
+ end
80
+ @updated = timestamp
81
+ end
82
+
83
+ #
84
+ # Walk through the list of every file we've loaded.
85
+ #
86
+ def find_files_for_reload
87
+ paths = [ './', *$LOAD_PATH ].uniq
88
+ [ $0, *$LOADED_FEATURES ].uniq.map do |file|
89
+ next if file =~ /\.(so|bundle)$/
90
+ yield(find(file, paths))
91
+ end
92
+ end
93
+
94
+ #
95
+ # Returns true if the file is loadable; uses the wrapper
96
+ # functionality of Kernel#load to protect the global namespace.
97
+ #
98
+ def verify(file)
99
+ begin
100
+ @logger.debug("Verifying #{file}")
101
+ load(file, true)
102
+ return(true)
103
+ rescue => error
104
+ @logger.error("Failed to verify #{file}: #{error.to_s}")
105
+ error.backtrace.each { |l| @logger.error(" #{l}") }
106
+ end
107
+ end
108
+
109
+ #
110
+ # Takes a relative or absolute +file+ name, a couple possible
111
+ # +paths+ that the +file+ might reside in. Returns a tuple of
112
+ # the full path where the file was found and its modification
113
+ # time, or nil if not found.
114
+ #
115
+ def find(file, paths)
116
+ if(Pathname.new(file).absolute?)
117
+ return unless (timestamp = mtime(file))
118
+ @logger.debug("Found #{file}")
119
+ [ file, timestamp ]
120
+ else
121
+ paths.each do |path|
122
+ fullpath = File.expand_path((File.join(path, file)))
123
+ next unless (timestamp = mtime(fullpath))
124
+ @logger.debug("Found #{file} in #{fullpath}")
125
+ return([ fullpath, timestamp ])
126
+ end
127
+ return(nil)
128
+ end
129
+ end
130
+
131
+ #
132
+ # Returns the modification time of _file_.
133
+ #
134
+ def mtime(file)
135
+ begin
136
+ return unless file
137
+ stat = File.stat(file)
138
+ stat.file? ? stat.mtime.to_i : nil
139
+ rescue Errno::ENOENT, Errno::ENOTDIR
140
+ nil
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ # Reload on SIGHUP.
147
+ trap("SIGHUP") { Mizuno::Reloader.reload! }
data/lib/mizuno/runner.rb CHANGED
@@ -8,6 +8,59 @@ require 'etc'
8
8
  require 'rack'
9
9
 
10
10
  module Mizuno
11
+ require 'rbconfig'
12
+
13
+ module Daemonizer
14
+ def self.included?(base)
15
+ if(Config::CONFIG['host_os'] =~ /mswin|mingw/)
16
+ base.send(:extend, StubbedClassMethods)
17
+ else
18
+ base.send(:extend, UnixClassMethods)
19
+ base.send(:class_eval) do
20
+ extend FFI::Library
21
+ ffi_lib 'c'
22
+ attach_function :_setuid, :setuid, [ :uint ], :int
23
+ attach_function :_setgid, :setgid, [ :uint ], :int
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ module UnixClassMethods
30
+ #
31
+ # Switch the process over to a new user id; will abort the
32
+ # process if it fails. _options_ is the full list of options
33
+ # passed to a server.
34
+ #
35
+ def setuid(options)
36
+ entry = Etc.getpwnam(options[:user])
37
+ die("Can't find --user named '#{options[:user]}'") \
38
+ unless entry
39
+ return unless (_setuid(entry.uid) != 0)
40
+ die("Can't switch to user '#{options[:user]}'")
41
+ end
42
+
43
+ #
44
+ # Like setuid, but for groups.
45
+ #
46
+ def setgid(options)
47
+ entry = Etc.getgrnam(options[:group])
48
+ die("Can't find --group named '#{options[:group]}'") \
49
+ unless entry
50
+ return unless (_setgid(entry.gid) != 0)
51
+ die("Can't switch to group '#{options[:group]}'")
52
+ end
53
+ end
54
+
55
+ module StubbedClassMethods
56
+ def setuid(options)
57
+ end
58
+
59
+ def setgid(options)
60
+ end
61
+ end
62
+ end
63
+
11
64
  #
12
65
  # Launches Mizuno when called from the command-line, and handles
13
66
  # damonization via FFI.
@@ -15,35 +68,7 @@ module Mizuno
15
68
  # Daemonization code based on Spoon.
16
69
  #
17
70
  class Runner
18
- extend FFI::Library
19
-
20
- ffi_lib 'c'
21
-
22
- attach_function :_setuid, :setuid, [ :uint ], :int
23
-
24
- attach_function :_setgid, :setgid, [ :uint ], :int
25
-
26
- #
27
- # Switch the process over to a new user id; will abort the
28
- # process if it fails. _options_ is the full list of options
29
- # passed to a server.
30
- #
31
- def Runner.setuid(options)
32
- entry = Etc.getpwnam(options[:user])
33
- die("Can't find --user named '#{options[:user]}'") unless entry
34
- return unless (_setuid(entry.uid) != 0)
35
- die("Can't switch to user '#{options[:user]}'")
36
- end
37
-
38
- #
39
- # Like setuid, but for groups.
40
- #
41
- def Runner.setgid(options)
42
- entry = Etc.getgrnam(options[:group])
43
- die("Can't find --group named '#{options[:group]}'") unless entry
44
- return unless (_setgid(entry.gid) != 0)
45
- die("Can't switch to group '#{options[:group]}'")
46
- end
71
+ include Daemonizer
47
72
 
48
73
  #
49
74
  # Launch Jetty, optionally as a daemon.
@@ -75,6 +100,7 @@ module Mizuno
75
100
  # Fire up Mizuno as if it was called from Rackup.
76
101
  Dir.chdir(options[:root])
77
102
  HttpServer.configure_logging(options)
103
+ ENV['RACK_ENV'] = options[:env]
78
104
  server = Rack::Server.new
79
105
  server.options = options.merge(:server => 'mizuno',
80
106
  :environment => options[:env])
@@ -105,7 +131,7 @@ module Mizuno
105
131
  File.open(options[:pidfile], 'w') { |f| f.puts(child.pid) }
106
132
 
107
133
  # Wait until the server starts or we time out waiting for it.
108
- exit if wait_for_server(options)
134
+ exit if wait_for_server(options, 60)
109
135
  child.stop
110
136
  die("Failed to start Mizuno.")
111
137
  end
@@ -1,3 +1,3 @@
1
1
  module Mizuno
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -0,0 +1,4 @@
1
+ require 'mizuno'
2
+
3
+ # Register ourselves with Rack when this file gets loaded.
4
+ Rack::Handler.register 'mizuno', 'Mizuno::HttpServer'
data/mizuno.gemspec CHANGED
@@ -23,9 +23,11 @@ Gem::Specification.new do |spec|
23
23
  lib/mizuno/java_logger.rb
24
24
  lib/mizuno/rack/chunked.rb
25
25
  lib/mizuno/rack_servlet.rb
26
+ lib/mizuno/reloader.rb
26
27
  lib/mizuno/runner.rb
27
28
  lib/mizuno/version.rb
28
29
  lib/mizuno.rb
30
+ lib/rack/handler/mizuno.rb
29
31
  bin/mizuno)
30
32
  jars = Dir.entries("lib/java").grep(/\.jar$/)
31
33
  spec.files.concat(jars.map { |j| "lib/java/#{j}" })
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: mizuno
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.5.1
5
+ version: 0.6.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Don Werve
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-02-17 00:00:00 Z
13
+ date: 2012-04-09 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -120,19 +120,21 @@ files:
120
120
  - lib/mizuno/java_logger.rb
121
121
  - lib/mizuno/rack/chunked.rb
122
122
  - lib/mizuno/rack_servlet.rb
123
+ - lib/mizuno/reloader.rb
123
124
  - lib/mizuno/runner.rb
124
125
  - lib/mizuno/version.rb
125
126
  - lib/mizuno.rb
127
+ - lib/rack/handler/mizuno.rb
126
128
  - bin/mizuno
127
- - lib/java/jetty-continuation-8.0.4.v20111024.jar
128
- - lib/java/jetty-http-8.0.4.v20111024.jar
129
- - lib/java/jetty-io-8.0.4.v20111024.jar
130
- - lib/java/jetty-jmx-8.0.4.v20111024.jar
131
- - lib/java/jetty-security-8.0.4.v20111024.jar
132
- - lib/java/jetty-server-8.0.4.v20111024.jar
133
- - lib/java/jetty-servlet-8.0.4.v20111024.jar
134
- - lib/java/jetty-servlets-8.0.4.v20111024.jar
135
- - lib/java/jetty-util-8.0.4.v20111024.jar
129
+ - lib/java/jetty-continuation-8.1.2.v20120308.jar
130
+ - lib/java/jetty-http-8.1.2.v20120308.jar
131
+ - lib/java/jetty-io-8.1.2.v20120308.jar
132
+ - lib/java/jetty-jmx-8.1.2.v20120308.jar
133
+ - lib/java/jetty-security-8.1.2.v20120308.jar
134
+ - lib/java/jetty-server-8.1.2.v20120308.jar
135
+ - lib/java/jetty-servlet-8.1.2.v20120308.jar
136
+ - lib/java/jetty-servlets-8.1.2.v20120308.jar
137
+ - lib/java/jetty-util-8.1.2.v20120308.jar
136
138
  - lib/java/log4j-1.2.16.jar
137
139
  - lib/java/rewindable-input-stream.jar
138
140
  - lib/java/servlet-api-3.0.jar
Binary file
Binary file