Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add JRubyService, OutputUtil.write_body, write_file

  • Loading branch information...
commit 65102bc1977cf694153e74e41272b43c404ee8fa 1 parent 384bb5b
David Kellum authored February 12, 2012
34  lib/fishwife/rack_servlet.rb
@@ -15,6 +15,9 @@
15 15
 # permissions and limitations under the License.
16 16
 #++
17 17
 
  18
+# Magic loader hook -> JRubyService
  19
+require 'fishwife/JRuby'
  20
+
18 21
 #
19 22
 # Wraps a Rack application in a Java servlet.
20 23
 #
@@ -214,29 +217,20 @@ def rack_to_servlet(rack_response, response)
214 217
         end
215 218
       end
216 219
 
217  
-      # How else would we write output?
218 220
       output = response.getOutputStream
219 221
 
220  
-      # Turn the body into something nice and Java-y.
221  
-      if(body.respond_to?(:to_path))
222  
-        # We've been told to serve a file; use FileInputStream to
223  
-        # stream the file directly to the servlet, because this is a
224  
-        # lot faster than doing it with Ruby.
225  
-        file = java.io.File.new(body.to_path)
226  
-
227  
-        # We set the content-length so we can use Keep-Alive, unless
228  
-        # this is an async request.
229  
-        response.setContentLength(file.length) unless content_length
230  
-
231  
-        # Stream the file directly.
232  
-        buffer = Java::byte[4096].new
233  
-        input_stream = FileInputStream.new(file)
234  
-        while((count = input_stream.read(buffer)) != -1)
235  
-          output.write(buffer, 0, count)
236  
-        end
237  
-        input_stream.close
  222
+      if body.respond_to?( :to_path )
  223
+
  224
+        path = body.to_path
  225
+
  226
+        # Set Content-Length unless this is an async request.
  227
+        response.setContentLength( File.size( path ) ) unless content_length
  228
+
  229
+        # FIXME: Support ranges?
  230
+
  231
+        OutputUtil.write_file( path, output )
238 232
       else
239  
-        body.each { |l| output.write(l.to_java_bytes) }
  233
+        OutputUtil.write_body( body, output )
240 234
       end
241 235
 
242 236
       # Close the body if we're supposed to.
40  src/main/java/fishwife/JRubyService.java
... ...
@@ -0,0 +1,40 @@
  1
+/*
  2
+ * Copyright (c) 2012 David Kellum
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License"); you
  5
+ * may not use this file except in compliance with the License.  You may
  6
+ * obtain a copy of the License at
  7
+ *
  8
+ *    http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  13
+ * implied.  See the License for the specific language governing
  14
+ * permissions and limitations under the License.
  15
+ */
  16
+
  17
+package fishwife;
  18
+
  19
+import org.jruby.Ruby;
  20
+import org.jruby.RubyClass;
  21
+import org.jruby.RubyModule;
  22
+import org.jruby.runtime.ObjectAllocator;
  23
+import org.jruby.runtime.load.BasicLibraryService;
  24
+
  25
+public class JRubyService implements BasicLibraryService
  26
+{
  27
+    public boolean basicLoad( Ruby runtime )
  28
+    {
  29
+        RubyModule fmod = runtime.defineModule( "Fishwife" );
  30
+
  31
+        RubyClass ouClass =
  32
+            fmod.defineClassUnder( "OutputUtil",
  33
+                                   runtime.getObject(),
  34
+                                   ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR );
  35
+
  36
+        ouClass.defineAnnotatedMethods( OutputUtil.class );
  37
+
  38
+        return true;
  39
+    }
  40
+}
138  src/main/java/fishwife/OutputUtil.java
... ...
@@ -0,0 +1,138 @@
  1
+/*
  2
+ * Copyright (c) 2012 David Kellum
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License"); you
  5
+ * may not use this file except in compliance with the License.  You may
  6
+ * obtain a copy of the License at
  7
+ *
  8
+ *    http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  13
+ * implied.  See the License for the specific language governing
  14
+ * permissions and limitations under the License.
  15
+ */
  16
+
  17
+package fishwife;
  18
+
  19
+import java.io.FileInputStream;
  20
+import java.io.FileNotFoundException;
  21
+import java.io.IOException;
  22
+import java.io.OutputStream;
  23
+
  24
+import org.jruby.Ruby;
  25
+import org.jruby.RubyObject;
  26
+import org.jruby.RubyString;
  27
+import org.jruby.anno.JRubyClass;
  28
+import org.jruby.anno.JRubyMethod;
  29
+import org.jruby.exceptions.RaiseException;
  30
+import org.jruby.javasupport.util.RuntimeHelpers;
  31
+import org.jruby.runtime.Arity;
  32
+import org.jruby.runtime.Block;
  33
+import org.jruby.runtime.BlockCallback;
  34
+import org.jruby.runtime.CallBlock;
  35
+import org.jruby.runtime.ThreadContext;
  36
+import org.jruby.runtime.builtin.IRubyObject;
  37
+import org.jruby.util.ByteList;
  38
+
  39
+@JRubyClass( name="Fishwife::OutputUtil" )
  40
+public class OutputUtil
  41
+{
  42
+    @JRubyMethod( name = "write_file",
  43
+                  meta = true,
  44
+                  required = 2,
  45
+                  argTypes = { RubyObject.class,
  46
+                               OutputStream.class } )
  47
+    public static IRubyObject writeFile( ThreadContext tc,
  48
+                                         IRubyObject klazz,
  49
+                                         IRubyObject file,
  50
+                                         IRubyObject ostr )
  51
+    {
  52
+        FileInputStream in = null;
  53
+        try {
  54
+            try {
  55
+                String filePath = file.convertToString().asJavaString();
  56
+                in = new FileInputStream( filePath );
  57
+                OutputStream out =
  58
+                    (OutputStream) ostr.toJava( OutputStream.class );
  59
+
  60
+                final byte[] buff = new byte[ 8 * 1024 ];
  61
+                while( true ) {
  62
+                    final int len = in.read( buff );
  63
+                    if( len > 0 ) {
  64
+                        out.write( buff, 0, len );
  65
+                    }
  66
+                    else break;
  67
+                }
  68
+                return tc.getRuntime().getNil();
  69
+            }
  70
+            finally {
  71
+                if( in != null ) in.close();
  72
+            }
  73
+        }
  74
+        catch( FileNotFoundException x ) {
  75
+            throw RaiseException.
  76
+                createNativeRaiseException( tc.getRuntime(), x );
  77
+        }
  78
+        catch( IOException x ) {
  79
+            throw RaiseException.
  80
+                createNativeRaiseException( tc.getRuntime(), x );
  81
+        }
  82
+    }
  83
+
  84
+    @JRubyMethod( name = "write_body",
  85
+                  meta = true,
  86
+                  required = 2,
  87
+                  argTypes = { RubyObject.class,
  88
+                               OutputStream.class } )
  89
+    public static IRubyObject writeBody( ThreadContext tc,
  90
+                                         IRubyObject klazz,
  91
+                                         IRubyObject body,
  92
+                                         IRubyObject out )
  93
+    {
  94
+        OutputStream ostream = (OutputStream) out.toJava( OutputStream.class );
  95
+
  96
+        RuntimeHelpers.invoke( tc, body, "each",
  97
+           CallBlock.newCallClosure( klazz,
  98
+                                     tc.getRuntime().getEnumerable(),
  99
+                                     Arity.ONE_ARGUMENT,
  100
+                                     new AppendBlockCallback( tc.getRuntime(),
  101
+                                                              ostream ),
  102
+                                                              tc ) );
  103
+
  104
+        return tc.getRuntime().getNil();
  105
+    }
  106
+
  107
+    public static final class AppendBlockCallback implements BlockCallback
  108
+    {
  109
+        public AppendBlockCallback(Ruby runtime, OutputStream out)
  110
+        {
  111
+            _runtime = runtime;
  112
+            _out = out;
  113
+        }
  114
+
  115
+        public IRubyObject call( ThreadContext context,
  116
+                                 IRubyObject[] args,
  117
+                                 Block blk )
  118
+        {
  119
+            try {
  120
+                final RubyString str = args[0].convertToString();
  121
+                final ByteList blist = str.getByteList();
  122
+
  123
+                _out.write( blist.unsafeBytes(),
  124
+                            blist.begin(),
  125
+                            blist.length() );
  126
+
  127
+                return _runtime.getNil();
  128
+            }
  129
+            catch( IOException x ) {
  130
+                throw RaiseException.createNativeRaiseException( _runtime, x );
  131
+            }
  132
+        }
  133
+
  134
+        private final Ruby _runtime;
  135
+        private final OutputStream _out;
  136
+    }
  137
+
  138
+}

0 notes on commit 65102bc

Please sign in to comment.
Something went wrong with that request. Please try again.