jubilee 1.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }