jubilee 1.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG +32 -0
  5. data/Gemfile +35 -0
  6. data/Gemfile.lock +244 -0
  7. data/Guardfile +24 -0
  8. data/KNOWN_ISSUES +6 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +147 -0
  11. data/ROADMAP +5 -0
  12. data/Rakefile +98 -0
  13. data/bin/jubilee +6 -0
  14. data/bin/jubilee_d +13 -0
  15. data/examples/chatapp/Gemfile +5 -0
  16. data/examples/chatapp/Gemfile.lock +27 -0
  17. data/examples/chatapp/README.md +17 -0
  18. data/examples/chatapp/app.rb +57 -0
  19. data/examples/chatapp/config.ru +3 -0
  20. data/examples/chatapp/public/assets/javascripts/application.js +67 -0
  21. data/examples/chatapp/public/assets/javascripts/jquery.js +5 -0
  22. data/examples/chatapp/public/assets/javascripts/sockjs-0.3.4.min.js +27 -0
  23. data/examples/chatapp/public/assets/javascripts/vertxbus.js +216 -0
  24. data/examples/chatapp/public/assets/stylesheets/application.css +29 -0
  25. data/examples/client/sockjs-0.3.4.min.js +27 -0
  26. data/examples/client/vertxbus.js +216 -0
  27. data/examples/jubilee.conf.rb +12 -0
  28. data/examples/jubilee/keystore.jks +0 -0
  29. data/examples/jubilee/server-keystore.jks +0 -0
  30. data/jars/hazelcast-2.6.3.jar +0 -0
  31. data/jars/jackson-annotations-2.2.2.jar +0 -0
  32. data/jars/jackson-core-2.2.2.jar +0 -0
  33. data/jars/jackson-databind-2.2.2.jar +0 -0
  34. data/jars/netty-all-4.0.13.Final.jar +0 -0
  35. data/jars/vertx-core-2.1M3-SNAPSHOT.jar +0 -0
  36. data/jars/vertx-hazelcast-2.1M3-SNAPSHOT.jar +0 -0
  37. data/java/src/jubilee/JubileeService.java +20 -0
  38. data/java/src/org/jruby/jubilee/Const.java +32 -0
  39. data/java/src/org/jruby/jubilee/RackApplication.java +103 -0
  40. data/java/src/org/jruby/jubilee/RackEnvironment.java +150 -0
  41. data/java/src/org/jruby/jubilee/RackEnvironmentHash.java +449 -0
  42. data/java/src/org/jruby/jubilee/RackInput.java +62 -0
  43. data/java/src/org/jruby/jubilee/RackResponse.java +11 -0
  44. data/java/src/org/jruby/jubilee/RubyHttpServerResponse.java +88 -0
  45. data/java/src/org/jruby/jubilee/RubyServer.java +171 -0
  46. data/java/src/org/jruby/jubilee/deploy/Starter.java +26 -0
  47. data/java/src/org/jruby/jubilee/impl/RubyIORackInput.java +201 -0
  48. data/java/src/org/jruby/jubilee/impl/RubyNullIO.java +107 -0
  49. data/java/src/org/jruby/jubilee/utils/RubyHelper.java +37 -0
  50. data/java/src/org/jruby/jubilee/vertx/JubileeVertx.java +38 -0
  51. data/jubilee.gemspec +201 -0
  52. data/lib/jubilee.rb +17 -0
  53. data/lib/jubilee/application.rb +13 -0
  54. data/lib/jubilee/cli.rb +127 -0
  55. data/lib/jubilee/configuration.rb +177 -0
  56. data/lib/jubilee/const.rb +40 -0
  57. data/lib/jubilee/jubilee.jar +0 -0
  58. data/lib/jubilee/response.rb +69 -0
  59. data/lib/jubilee/server.rb +12 -0
  60. data/lib/jubilee/version.rb +10 -0
  61. data/lib/rack/chunked.rb +38 -0
  62. data/lib/rack/handler/jubilee.rb +44 -0
  63. data/lib/vertx.rb +26 -0
  64. data/lib/vertx/README.md +7 -0
  65. data/lib/vertx/buffer.rb +251 -0
  66. data/lib/vertx/event_bus.rb +206 -0
  67. data/lib/vertx/shared_data.rb +214 -0
  68. data/spec/apps/rack/basic/config.ru +50 -0
  69. data/spec/apps/rails4/basic/.gitignore +16 -0
  70. data/spec/apps/rails4/basic/Gemfile +41 -0
  71. data/spec/apps/rails4/basic/Gemfile.lock +127 -0
  72. data/spec/apps/rails4/basic/README.rdoc +28 -0
  73. data/spec/apps/rails4/basic/Rakefile +6 -0
  74. data/spec/apps/rails4/basic/app/assets/images/.keep +0 -0
  75. data/spec/apps/rails4/basic/app/assets/images/rails.png +0 -0
  76. data/spec/apps/rails4/basic/app/assets/javascripts/application.js +12 -0
  77. data/spec/apps/rails4/basic/app/assets/stylesheets/application.css +13 -0
  78. data/spec/apps/rails4/basic/app/controllers/application_controller.rb +5 -0
  79. data/spec/apps/rails4/basic/app/controllers/concerns/.keep +0 -0
  80. data/spec/apps/rails4/basic/app/controllers/reloader_controller.rb +11 -0
  81. data/spec/apps/rails4/basic/app/controllers/reloader_controller.rb.erb +11 -0
  82. data/spec/apps/rails4/basic/app/controllers/root_controller.rb +14 -0
  83. data/spec/apps/rails4/basic/app/helpers/application_helper.rb +2 -0
  84. data/spec/apps/rails4/basic/app/mailers/.keep +0 -0
  85. data/spec/apps/rails4/basic/app/models/.keep +0 -0
  86. data/spec/apps/rails4/basic/app/models/concerns/.keep +0 -0
  87. data/spec/apps/rails4/basic/app/views/layouts/application.html.erb +14 -0
  88. data/spec/apps/rails4/basic/app/views/reloader/index.html.erb +1 -0
  89. data/spec/apps/rails4/basic/app/views/root/index.html.erb +8 -0
  90. data/spec/apps/rails4/basic/app/views/root/streaming.html.erb +6 -0
  91. data/spec/apps/rails4/basic/bin/bundle +3 -0
  92. data/spec/apps/rails4/basic/bin/rails +4 -0
  93. data/spec/apps/rails4/basic/bin/rake +4 -0
  94. data/spec/apps/rails4/basic/config.ru +4 -0
  95. data/spec/apps/rails4/basic/config/application.rb +23 -0
  96. data/spec/apps/rails4/basic/config/boot.rb +4 -0
  97. data/spec/apps/rails4/basic/config/database.yml +20 -0
  98. data/spec/apps/rails4/basic/config/environment.rb +5 -0
  99. data/spec/apps/rails4/basic/config/environments/development.rb +29 -0
  100. data/spec/apps/rails4/basic/config/environments/production.rb +80 -0
  101. data/spec/apps/rails4/basic/config/environments/test.rb +36 -0
  102. data/spec/apps/rails4/basic/config/initializers/backtrace_silencers.rb +7 -0
  103. data/spec/apps/rails4/basic/config/initializers/filter_parameter_logging.rb +4 -0
  104. data/spec/apps/rails4/basic/config/initializers/inflections.rb +16 -0
  105. data/spec/apps/rails4/basic/config/initializers/mime_types.rb +5 -0
  106. data/spec/apps/rails4/basic/config/initializers/secret_token.rb +12 -0
  107. data/spec/apps/rails4/basic/config/initializers/session_store.rb +2 -0
  108. data/spec/apps/rails4/basic/config/initializers/wrap_parameters.rb +14 -0
  109. data/spec/apps/rails4/basic/config/locales/en.yml +23 -0
  110. data/spec/apps/rails4/basic/config/routes.rb +5 -0
  111. data/spec/apps/rails4/basic/db/seeds.rb +7 -0
  112. data/spec/apps/rails4/basic/lib/assets/.keep +0 -0
  113. data/spec/apps/rails4/basic/lib/tasks/.keep +0 -0
  114. data/spec/apps/rails4/basic/public/404.html +58 -0
  115. data/spec/apps/rails4/basic/public/422.html +58 -0
  116. data/spec/apps/rails4/basic/public/500.html +57 -0
  117. data/spec/apps/rails4/basic/public/favicon.ico +0 -0
  118. data/spec/apps/rails4/basic/public/robots.txt +5 -0
  119. data/spec/apps/rails4/basic/public/some_page.html +7 -0
  120. data/spec/apps/rails4/basic/test/controllers/.keep +0 -0
  121. data/spec/apps/rails4/basic/test/fixtures/.keep +0 -0
  122. data/spec/apps/rails4/basic/test/helpers/.keep +0 -0
  123. data/spec/apps/rails4/basic/test/integration/.keep +0 -0
  124. data/spec/apps/rails4/basic/test/mailers/.keep +0 -0
  125. data/spec/apps/rails4/basic/test/models/.keep +0 -0
  126. data/spec/apps/rails4/basic/test/test_helper.rb +15 -0
  127. data/spec/apps/rails4/basic/vendor/assets/javascripts/.keep +0 -0
  128. data/spec/apps/rails4/basic/vendor/assets/stylesheets/.keep +0 -0
  129. data/spec/apps/sinatra/basic/Gemfile +4 -0
  130. data/spec/apps/sinatra/basic/Gemfile.lock +20 -0
  131. data/spec/apps/sinatra/basic/basic.rb +27 -0
  132. data/spec/apps/sinatra/basic/config.ru +7 -0
  133. data/spec/apps/sinatra/basic/public/some_page.html +7 -0
  134. data/spec/apps/sinatra/basic/views/index.erb +4 -0
  135. data/spec/apps/sinatra/basic/views/posted.haml +2 -0
  136. data/spec/apps/sinatra/basic/views/poster.haml +4 -0
  137. data/spec/apps/sinatra/basic/views/request_mapping.haml +4 -0
  138. data/spec/integration/basic_rack_spec.rb +89 -0
  139. data/spec/integration/basic_rails4_spec.rb +64 -0
  140. data/spec/integration/basic_sinatra_spec.rb +80 -0
  141. data/spec/spec_helper.rb +13 -0
  142. data/test/.ruby-version +1 -0
  143. data/test/config/app.rb +5 -0
  144. data/test/jubilee/test_cli.rb +11 -0
  145. data/test/jubilee/test_configuration.rb +31 -0
  146. data/test/jubilee/test_rack_server.rb +137 -0
  147. data/test/jubilee/test_response.rb +272 -0
  148. data/test/jubilee/test_server.rb +72 -0
  149. data/test/jubilee/test_upload.rb +301 -0
  150. data/test/sinatra_app/app.rb +31 -0
  151. data/test/sinatra_app/config.ru +6 -0
  152. data/test/sinatra_app/public/test.html +10 -0
  153. data/test/sinatra_app/unicorn.conf.rb +29 -0
  154. data/test/test_helper.rb +93 -0
  155. metadata +242 -0
@@ -0,0 +1,62 @@
1
+ package org.jruby.jubilee;
2
+
3
+ import org.jruby.runtime.Block;
4
+ import org.jruby.runtime.ThreadContext;
5
+ import org.jruby.runtime.builtin.IRubyObject;
6
+
7
+ /**
8
+ * @author isaiah
9
+ * @since Nov 26, 2012
10
+ *
11
+ * Specification for Rack input, translated to a Java interface.
12
+ * @author nicksieger
13
+ */
14
+ public interface RackInput
15
+ {
16
+ /**
17
+ * gets must be called without arguments and return a string, or nil on EOF.
18
+ * @param context it's a JRuby thing
19
+ * @return a string, or nil on EOF
20
+ */
21
+ IRubyObject gets( ThreadContext context );
22
+
23
+ /**
24
+ * read behaves like IO#read. Its signature is read([length, [buffer]]). If given,
25
+ * length must be an non-negative Integer (>= 0) or nil, and buffer must be a
26
+ * String and may not be nil. If length is given and not nil, then this method
27
+ * reads at most length bytes from the input stream. If length is not given or
28
+ * nil, then this method reads all data until EOF. When EOF is reached, this
29
+ * method returns nil if length is given and not nil, or "" if length is not
30
+ * given or is nil. If buffer is given, then the read data will be placed into
31
+ * buffer instead of a newly created String object.
32
+ * @param context it's a JRuby thing
33
+ * @param args [length, [buffer]]
34
+ * @return nil if length is given and not nil, or "" if length is not given or nil
35
+ */
36
+ IRubyObject read( ThreadContext context, IRubyObject[] args );
37
+
38
+ /**
39
+ * each must be called without arguments and only yield Strings.
40
+ * @param context it's a JRuby thing
41
+ * @param block that receives yield of Strings
42
+ * @return pretty much nil
43
+ */
44
+ public IRubyObject each( ThreadContext context, Block block );
45
+
46
+ /**
47
+ * rewind must be called without arguments. It rewinds the input stream back
48
+ * to the beginning. It must not raise Errno::ESPIPE: that is, it may not be
49
+ * a pipe or a socket. Therefore, handler developers must buffer the input
50
+ * data into some rewindable object if the underlying input stream is not rewindable.
51
+ * @param context it's a JRuby thing
52
+ * @return pretty much nil
53
+ */
54
+ public IRubyObject rewind( ThreadContext context );
55
+
56
+ /**
57
+ * Close the input. Exposed only to the Java side because the Rack spec says
58
+ * that application code must not call close, so we don't expose a close method to Ruby.
59
+ */
60
+ public IRubyObject close(ThreadContext context);
61
+ }
62
+
@@ -0,0 +1,11 @@
1
+ package org.jruby.jubilee;
2
+
3
+ /**
4
+ * Created with IntelliJ IDEA.
5
+ * User: isaiah
6
+ * Date: 11/29/12
7
+ * Time: 5:39 PM
8
+ */
9
+ public interface RackResponse {
10
+ public void respond(RubyHttpServerResponse response);
11
+ }
@@ -0,0 +1,88 @@
1
+ package org.jruby.jubilee;
2
+
3
+ import org.jruby.*;
4
+ import org.jruby.anno.JRubyClass;
5
+ import org.jruby.anno.JRubyMethod;
6
+ import org.jruby.runtime.ObjectAllocator;
7
+ import org.jruby.runtime.ThreadContext;
8
+ import org.jruby.runtime.builtin.IRubyObject;
9
+ import org.vertx.java.core.http.HttpServerResponse;
10
+
11
+ import java.util.Arrays;
12
+
13
+ /**
14
+ * Created by isaiah on 21/12/2013.
15
+ */
16
+ @JRubyClass(name = "HttpServerResponse")
17
+ public class RubyHttpServerResponse extends RubyObject {
18
+ private HttpServerResponse resp;
19
+ private String lineSeparator;
20
+
21
+ public static RubyClass createHttpServerResponseClass(final Ruby runtime) {
22
+ RubyModule mJubilee = runtime.getOrCreateModule("Jubilee");
23
+ RubyClass klazz = mJubilee.defineClassUnder("HttpServerResponse", runtime.getObject(), new ObjectAllocator() {
24
+ @Override
25
+ public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
26
+ return new RubyHttpServerResponse(ruby, rubyClass);
27
+ }
28
+ });
29
+ klazz.defineAnnotatedMethods(RubyHttpServerResponse.class);
30
+ return klazz;
31
+ }
32
+
33
+ public RubyHttpServerResponse(Ruby ruby, RubyClass rubyClass) {
34
+ super(ruby, rubyClass);
35
+ }
36
+
37
+ public RubyHttpServerResponse(Ruby ruby, RubyClass rubyClass, HttpServerResponse resp) {
38
+ super(ruby, rubyClass);
39
+ this.resp = resp;
40
+ this.lineSeparator = System.getProperty("line.separator");
41
+ }
42
+
43
+ @JRubyMethod
44
+ public IRubyObject write(ThreadContext context, IRubyObject string) {
45
+ this.resp.write(string.asJavaString());
46
+ return context.runtime.getNil();
47
+ }
48
+
49
+ @JRubyMethod(name = "status_code=")
50
+ public IRubyObject setStatusCode(ThreadContext context, IRubyObject statusCode) {
51
+ this.resp.setStatusCode(RubyNumeric.num2int(statusCode));
52
+ return context.runtime.getNil();
53
+ }
54
+
55
+ @JRubyMethod(name = "chunked=")
56
+ public IRubyObject setChunked(ThreadContext context, IRubyObject chunked) {
57
+ this.resp.setChunked(chunked.isTrue());
58
+ return context.runtime.getNil();
59
+ }
60
+
61
+ @JRubyMethod(name = "put_header")
62
+ public IRubyObject putHeader(ThreadContext context, IRubyObject key, IRubyObject val) {
63
+ String cookie = val.asJavaString();
64
+ if (cookie.indexOf(this.lineSeparator) != -1)
65
+ this.resp.putHeader(key.asJavaString(),
66
+ Arrays.asList(val.asJavaString().split(this.lineSeparator)));
67
+ else this.resp.putHeader(key.asJavaString(), val.asJavaString());
68
+ return context.runtime.getNil();
69
+ }
70
+
71
+ @JRubyMethod(name = "send_file")
72
+ public IRubyObject sendFile(ThreadContext context, IRubyObject filePath) {
73
+ this.resp.sendFile(filePath.asJavaString());
74
+ return context.runtime.getNil();
75
+ }
76
+
77
+ @JRubyMethod
78
+ public IRubyObject end(ThreadContext context) {
79
+ this.resp.end();
80
+ return context.runtime.getNil();
81
+ }
82
+
83
+ @JRubyMethod(name = "put_default_headers")
84
+ public IRubyObject putDefaultHeaders(ThreadContext context) {
85
+ this.resp.putHeader("Server", Const.JUBILEE_VERSION);
86
+ return context.runtime.getNil();
87
+ }
88
+ }
@@ -0,0 +1,171 @@
1
+ package org.jruby.jubilee;
2
+
3
+ import org.jruby.*;
4
+ import org.jruby.anno.JRubyMethod;
5
+ import org.jruby.jubilee.vertx.JubileeVertx;
6
+ import org.jruby.runtime.Block;
7
+ import org.jruby.runtime.ObjectAllocator;
8
+ import org.jruby.runtime.ThreadContext;
9
+ import org.jruby.runtime.builtin.IRubyObject;
10
+ import org.vertx.java.core.Handler;
11
+ import org.vertx.java.core.Vertx;
12
+ import org.vertx.java.core.http.HttpServer;
13
+ import org.vertx.java.core.http.HttpServerRequest;
14
+ import org.vertx.java.core.json.JsonArray;
15
+ import org.vertx.java.core.json.JsonObject;
16
+
17
+ import java.io.IOException;
18
+
19
+ public class RubyServer extends RubyObject {
20
+ private Vertx vertx;
21
+ private HttpServer httpServer;
22
+ private RackApplication app;
23
+ private boolean running = false;
24
+ private boolean ssl = false;
25
+ private String keyStorePath;
26
+ private String keyStorePassword;
27
+ private String eventBusPrefix;
28
+ private int port;
29
+ private String host;
30
+ private int clusterPort;
31
+ private String clusterHost;
32
+
33
+ public static void createServerClass(Ruby runtime) {
34
+ RubyModule mJubilee = runtime.defineModule("Jubilee");
35
+ RubyClass serverClass = mJubilee.defineClassUnder("VertxServer", runtime.getObject(), ALLOCATOR);
36
+ serverClass.defineAnnotatedMethods(RubyServer.class);
37
+ }
38
+
39
+ private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
40
+ public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
41
+ return new RubyServer(ruby, rubyClass);
42
+ }
43
+ };
44
+
45
+ public RubyServer(Ruby ruby, RubyClass rubyClass) {
46
+ super(ruby, rubyClass);
47
+ }
48
+
49
+ /**
50
+ * Initialize jubilee server, take a rack application and a configuration hash as parameter
51
+ *
52
+ * @param context
53
+ * @param app
54
+ * @param config
55
+ * @param block
56
+ * @return
57
+ */
58
+ @JRubyMethod(name = "initialize")
59
+ public IRubyObject initialize(ThreadContext context, IRubyObject app, IRubyObject config, Block block) {
60
+ Ruby runtime = getRuntime();
61
+ /* configuration keys */
62
+ RubyHash options = config.convertToHash();
63
+ RubySymbol port_k = runtime.newSymbol("Port");
64
+ RubySymbol host_k = runtime.newSymbol("Host");
65
+ RubySymbol cluster_port_k = runtime.newSymbol("cluster_port");
66
+ RubySymbol cluster_host_k = runtime.newSymbol("cluster_host");
67
+ RubySymbol ssl_k = runtime.newSymbol("ssl");
68
+ RubySymbol keystore_path_k = runtime.newSymbol("keystore_path");
69
+ RubySymbol keystore_password_k = runtime.newSymbol("keystore_password");
70
+ RubySymbol eventbus_prefix_k = runtime.newSymbol("eventbus_prefix");
71
+
72
+ /* retrieve from passed in options */
73
+ this.port = Integer.parseInt(options.op_aref(context, port_k).toString());
74
+ this.host = options.op_aref(context, host_k).toString();
75
+
76
+ this.ssl = options.op_aref(context, ssl_k).isTrue();
77
+ if (options.has_key_p(keystore_path_k).isTrue()) {
78
+ this.keyStorePath = options.op_aref(context, keystore_path_k).toString();
79
+ this.keyStorePassword = options.op_aref(context, keystore_password_k).toString();
80
+ }
81
+ if (options.has_key_p(eventbus_prefix_k).isTrue())
82
+ this.eventBusPrefix = options.op_aref(context, eventbus_prefix_k).toString();
83
+
84
+ /* init vertx */
85
+ if (options.has_key_p(cluster_host_k).isTrue()) {
86
+ this.clusterHost = options.op_aref(context, cluster_host_k).toString();
87
+ if (options.has_key_p(cluster_port_k).isTrue()) {
88
+ this.clusterPort = Integer.parseInt(options.op_aref(context, cluster_port_k).toString());
89
+ this.vertx = JubileeVertx.init(clusterPort, clusterHost);
90
+ }
91
+ this.vertx = JubileeVertx.init(clusterHost);
92
+ } else {
93
+ this.vertx = JubileeVertx.init();
94
+ }
95
+
96
+ httpServer = vertx.createHttpServer();
97
+ try {
98
+ this.app = new RackApplication(vertx, context, app, this.ssl);
99
+ if (block.isGiven()) block.yieldSpecific(context, this);
100
+ } catch (IOException e) {
101
+ // noop
102
+ }
103
+ return this;
104
+ }
105
+
106
+ /**
107
+ * Start http server, initialize states
108
+ *
109
+ * @param context
110
+ * @param block
111
+ * @return
112
+ */
113
+ @JRubyMethod(name = "start")
114
+ public IRubyObject start(final ThreadContext context, final Block block) {
115
+ httpServer.setAcceptBacklog(10000);
116
+ httpServer.requestHandler(new Handler<HttpServerRequest>() {
117
+ public void handle(final HttpServerRequest req) {
118
+ app.call(req);
119
+ }
120
+ });
121
+ if (eventBusPrefix != null) {
122
+ JsonObject config = new JsonObject().putString("prefix", eventBusPrefix);
123
+ JsonArray allowAll = new JsonArray();
124
+ allowAll.add(new JsonObject());
125
+ // TODO read inbounds and outbounds from config file
126
+ vertx.createSockJSServer(httpServer).bridge(config, allowAll, allowAll);
127
+ }
128
+ if (ssl) httpServer.setSSL(true).setKeyStorePath(this.keyStorePath)
129
+ .setKeyStorePassword(this.keyStorePassword);
130
+ httpServer.listen(this.port, this.host);
131
+ this.running = true;
132
+ if (block.isGiven()) block.yieldSpecific(context, this);
133
+ return this;
134
+ }
135
+
136
+ /**
137
+ * Set timeout for keep alive connection
138
+ *
139
+ * @param context
140
+ * @param timeout (in TimeUnit.SECONDS)
141
+ * @return this
142
+ */
143
+ @JRubyMethod(name = "persistent_timeout=")
144
+ public IRubyObject setPersistentTimeout(final ThreadContext context, final IRubyObject timeout) {
145
+ // FIXME
146
+ //httpServer.setPersistentTimeout(RubyInteger.fix2long(timeout) * 1000);
147
+ return this;
148
+ }
149
+
150
+ /**
151
+ * Stop the HttpServer
152
+ *
153
+ * @param context
154
+ * @param args if shutdown abruptly
155
+ * @param block callback on close
156
+ * @return
157
+ */
158
+ @JRubyMethod(name = {"stop", "close"}, optional = 1)
159
+ public IRubyObject close(ThreadContext context, IRubyObject[] args, Block block) {
160
+ if (running) {
161
+ this.running = false;
162
+ httpServer.close();
163
+ // DO I need to stop?
164
+ //vertx.stop();
165
+ if (block.isGiven()) block.yieldSpecific(context);
166
+ } else {
167
+ getRuntime().getOutputStream().println("jubilee server not running?");
168
+ }
169
+ return getRuntime().getNil();
170
+ }
171
+ }
@@ -0,0 +1,26 @@
1
+ package org.jruby.jubilee.deploy;
2
+
3
+ import java.util.concurrent.CountDownLatch;
4
+
5
+ /**
6
+ * Created with IntelliJ IDEA.
7
+ * User: isaiah
8
+ * Date: 12/3/12
9
+ * Time: 7:37 PM
10
+ */
11
+ public class Starter {
12
+ private CountDownLatch stopLatch = new CountDownLatch(1);
13
+
14
+ public void block() {
15
+ while(true) {
16
+ try {
17
+ stopLatch.await();
18
+ break;
19
+ } catch (InterruptedException ignore) {}
20
+ }
21
+ }
22
+
23
+ public void unblock() {
24
+ stopLatch.countDown();
25
+ }
26
+ }
@@ -0,0 +1,201 @@
1
+ package org.jruby.jubilee.impl;
2
+
3
+ import io.netty.buffer.ByteBuf;
4
+ import org.jcodings.specific.ASCIIEncoding;
5
+ import org.jcodings.Encoding;
6
+ import org.jruby.*;
7
+ import org.jruby.anno.JRubyMethod;
8
+ import org.jruby.jubilee.Const;
9
+ import org.jruby.jubilee.RackInput;
10
+ import org.jruby.runtime.Block;
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+ import org.jruby.util.ByteList;
15
+ import org.jruby.util.StringSupport;
16
+ import org.vertx.java.core.http.HttpServerRequest;
17
+
18
+ import java.util.concurrent.atomic.AtomicBoolean;
19
+
20
+ /**
21
+ * Created with IntelliJ IDEA.
22
+ * User: isaiah
23
+ * Date: 11/26/12
24
+ * Time: 10:12 PM
25
+ */
26
+ public class RubyIORackInput extends RubyObject implements RackInput {
27
+ private Encoding BINARY = ASCIIEncoding.INSTANCE;
28
+ private int len;
29
+ private boolean chunked;
30
+ private ByteBuf buf;
31
+ private AtomicBoolean eof;
32
+
33
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
34
+ @Override
35
+ public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
36
+ return new RubyIORackInput(ruby, rubyClass);
37
+ }
38
+ };
39
+
40
+ public static RubyClass createIORackInputClass(Ruby runtime) {
41
+ RubyModule jModule = runtime.getOrCreateModule("Jubilee");
42
+ RubyClass rackIOInputClass = jModule.defineClassUnder("IORackInput", runtime.getObject(), ALLOCATOR);
43
+ rackIOInputClass.defineAnnotatedMethods(RubyIORackInput.class);
44
+ return rackIOInputClass;
45
+ }
46
+
47
+ public RubyIORackInput(Ruby runtime, RubyClass metaClass) {
48
+ super(runtime, metaClass);
49
+ }
50
+
51
+ public RubyIORackInput(Ruby runtime, RubyClass rubyClass, HttpServerRequest request, ByteBuf buf, AtomicBoolean eof) {
52
+ this(runtime, rubyClass);
53
+ String hdr = request.headers().get(Const.Vertx.CONTENT_LENGTH);
54
+ this.chunked = hdr == null;
55
+ this.len = this.chunked ? 0 : Integer.parseInt(hdr);
56
+ this.buf = buf;
57
+ this.eof = eof;
58
+ }
59
+
60
+ /**
61
+ * gets must be called without arguments and return a string, or nil on EOF.
62
+ * <p/>
63
+ * this method return one line a time.
64
+ *
65
+ * @param context it's a JRuby thing
66
+ * @return a string, or nil on EOF
67
+ */
68
+ @Override
69
+ @JRubyMethod
70
+ public IRubyObject gets(ThreadContext context) {
71
+ Ruby runtime = context.runtime;
72
+ RubyString line = RubyString.newEmptyString(context.runtime, BINARY);
73
+
74
+ if (isEOF()) return runtime.getNil();
75
+
76
+ int lineEnd = -1;
77
+ while (lineEnd == -1 && !eof.get()) {
78
+ lineEnd = buf.indexOf(buf.readerIndex(), buf.writerIndex(), Const.EOL);
79
+ }
80
+
81
+ // No line break found, read all
82
+ if (lineEnd == -1)
83
+ return readAll(runtime, line);
84
+
85
+ int readLength = lineEnd - buf.readerIndex();
86
+ byte[] dst = new byte[readLength + 1];
87
+ buf.readBytes(dst);
88
+ return line.cat(dst);
89
+ }
90
+
91
+ /**
92
+ * read behaves like IO#read. Its signature is read([length, [buffer]]). If given,
93
+ * length must be an non-negative Integer (>= 0) or nil, and buffer must be a
94
+ * String and may not be nil. If length is given and not nil, then this method
95
+ * reads at most length bytes from the input stream. If length is not given or
96
+ * nil, then this method reads all data until EOF. When EOF is reached, this
97
+ * method returns nil if length is given and not nil, or "" if) length is not
98
+ * given or is nil. If buffer is given, then the read data will be placed into
99
+ * buffer instead of a newly created String object.
100
+ *
101
+ * @param context it's a JRuby thing
102
+ * @param args [length, [buffer]]
103
+ * @return nil if length is given and not nil, or "" if length is not given or nil
104
+ */
105
+ @Override
106
+ @JRubyMethod(optional = 2)
107
+ public IRubyObject read(ThreadContext context, IRubyObject[] args) {
108
+ Ruby runtime = context.runtime;
109
+ RubyString dst = RubyString.newStringNoCopy(context.runtime, new ByteList(), BINARY, StringSupport.CR_VALID);
110
+ if (isEOF())
111
+ return runtime.getNil();
112
+ int length;
113
+ switch (args.length) {
114
+ case 0:
115
+ return readAll(runtime, dst);
116
+ case 1:
117
+ length = RubyNumeric.num2int(args[0]);
118
+ break;
119
+ default:
120
+ length = RubyNumeric.num2int(args[0]);
121
+ dst = (RubyString) args[1];
122
+ }
123
+ if (length < 0)
124
+ runtime.newArgumentError("Negative length " + length + " given");
125
+ byte[] buffer = new byte[length];
126
+ int toRead = length;
127
+ while (toRead > 0 && !isEOF()) {
128
+ int len = Math.min(toRead, readableBytes());
129
+ buf.readBytes(buffer, length - toRead, len);
130
+ toRead = toRead - len;
131
+ }
132
+ if (toRead > 0) length -= toRead;
133
+ return dst.cat(buffer, 0, length);
134
+ }
135
+
136
+ /**
137
+ * each must be called without arguments and only yield Strings.
138
+ *
139
+ * @param context it's a JRuby thing
140
+ * @param block that receives yield of Strings
141
+ * @return pretty much nil
142
+ */
143
+ @Override
144
+ @JRubyMethod
145
+ public IRubyObject each(ThreadContext context, Block block) {
146
+ IRubyObject str;
147
+ while (!(str = gets(context)).isNil()) {
148
+ block.yield(context, str);
149
+ }
150
+ return context.runtime.getNil();
151
+ }
152
+
153
+ /**
154
+ * rewind must be called without arguments. It rewinds the input stream back
155
+ * to the beginning. It must not raise Errno::ESPIPE: that is, it may not be
156
+ * a pipe or a socket. Therefore, handler developers must buffer the input
157
+ * data into some rewindable object if the underlying input stream is not rewindable.
158
+ *
159
+ * @param context it's a JRuby thing
160
+ * @return pretty much nil
161
+ */
162
+ @Override
163
+ @JRubyMethod
164
+ public IRubyObject rewind(ThreadContext context) {
165
+ buf.readerIndex(0);
166
+ return context.runtime.getNil();
167
+ }
168
+
169
+ /**
170
+ * Close the input. Exposed only to the Java side because the Rack spec says
171
+ * that application code must not call close, so we don't expose a close method to Ruby.
172
+ */
173
+ @JRubyMethod
174
+ public IRubyObject close(ThreadContext context) {
175
+ buf.clear();
176
+ return context.runtime.getNil();
177
+ }
178
+
179
+ private int readableBytes() {
180
+ if (! this.chunked) {
181
+ return Math.min(buf.readableBytes(), this.len - buf.readerIndex());
182
+ }
183
+ return buf.readableBytes();
184
+ }
185
+
186
+ private boolean isEOF() {
187
+ while (buf.readableBytes() == 0 && !eof.get())
188
+ ; // wait while there is nothing to read
189
+ return buf.readableBytes() == 0 && eof.get();
190
+ }
191
+
192
+ private IRubyObject readAll(Ruby runtime, RubyString dst) {
193
+ while(!eof.get())
194
+ ; // wait until all data received
195
+ int length = this.chunked ? buf.readableBytes() : Math.min(this.len, buf.readableBytes());
196
+ byte[] data = new byte[length];
197
+ buf.readBytes(data);
198
+ dst.cat(data);
199
+ return dst.isEmpty() ? runtime.getNil() : dst;
200
+ }
201
+ }