Skip to content
Browse files

Merge branch 'java-output-extension' into fishwife-dev

  • Loading branch information...
2 parents 3a3e09a + 4fc5344 commit 067d9ff7c0aa7177439889596f8aacdff6fdd54a @dekellum committed
View
5 .gitignore
@@ -2,3 +2,8 @@
doc/
pkg/
.bundle/
+target/
+.classpath
+.project
+.settings
+lib/fishwife/*.jar
View
17 Manifest.static
@@ -0,0 +1,17 @@
+History.rdoc
+LICENSE
+Manifest.txt
+README.rdoc
+Rakefile
+bin/fishwife
+example/config.ru
+lib/fishwife/base.rb
+lib/fishwife.rb
+lib/fishwife/http_server.rb
+lib/fishwife/rack_servlet.rb
+lib/rack/handler/fishwife.rb
+spec/fishwife_spec.rb
+spec/spec_helper.rb
+spec/test_app.rb
+spec/data/hello.txt
+spec/data/reddit-icon.png
View
1 Manifest.txt
@@ -15,3 +15,4 @@ spec/spec_helper.rb
spec/test_app.rb
spec/data/hello.txt
spec/data/reddit-icon.png
+lib/fishwife/fishwife-1.3.0.jar
View
2 fishwife.gemspec
@@ -19,5 +19,5 @@ RJack::TarPit.specify do |s|
s.depend 'rjack-logback', '~> 1.2', :dev
s.depend 'rspec', '~> 2.8.0', :dev
- s.platform = :java
+ s.maven_strategy = :no_assembly
end
View
6 lib/fishwife.rb
@@ -23,6 +23,12 @@
# Load Jetty JARs.
require 'rjack-jetty'
+require 'fishwife/base'
+
+module Fishwife
+ require "#{LIB_DIR}/fishwife-#{VERSION}.jar"
+end
+
require 'rack'
require 'fishwife/rack_servlet'
require 'fishwife/http_server'
View
1 lib/fishwife/base.rb
@@ -16,4 +16,5 @@
module Fishwife
VERSION = '1.3.0'
+ LIB_DIR = File.dirname( __FILE__ )
end
View
34 lib/fishwife/rack_servlet.rb
@@ -15,6 +15,9 @@
# permissions and limitations under the License.
#++
+# Magic loader hook -> JRubyService
+require 'fishwife/JRuby'
+
#
# Wraps a Rack application in a Java servlet.
#
@@ -214,29 +217,20 @@ def rack_to_servlet(rack_response, response)
end
end
- # How else would we write output?
output = response.getOutputStream
- # Turn the body into something nice and Java-y.
- if(body.respond_to?(:to_path))
- # We've been told to serve a file; use FileInputStream to
- # stream the file directly to the servlet, because this is a
- # lot faster than doing it with Ruby.
- file = java.io.File.new(body.to_path)
-
- # We set the content-length so we can use Keep-Alive, unless
- # this is an async request.
- response.setContentLength(file.length) unless content_length
-
- # Stream the file directly.
- buffer = Java::byte[4096].new
- input_stream = FileInputStream.new(file)
- while((count = input_stream.read(buffer)) != -1)
- output.write(buffer, 0, count)
- end
- input_stream.close
+ if body.respond_to?( :to_path )
+
+ path = body.to_path
+
+ # Set Content-Length unless this is an async request.
+ response.setContentLength( File.size( path ) ) unless content_length
+
+ # FIXME: Support ranges?
+
+ OutputUtil.write_file( path, output )
else
- body.each { |l| output.write(l.to_java_bytes) }
+ OutputUtil.write_body( body, output )
end
# Close the body if we're supposed to.
View
52 pom.xml
@@ -0,0 +1,52 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>fishwife</groupId>
+ <artifactId>fishwife</artifactId>
+ <packaging>jar</packaging>
+ <version>1.3.0</version>
+ <name>Fishwife Java Extension</name>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.jruby.extras</groupId>
+ <artifactId>bytelist</artifactId>
+ <version>1.0.10</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jruby</groupId>
+ <artifactId>jruby-core</artifactId>
+ <version>1.6.6</version>
+ <optional>true</optional>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <optimize>true</optimize>
+ <debug>true</debug>
+ <encoding>UTF-8</encoding>
+ <showDeprecation>true</showDeprecation>
+ <showWarnings>true</showWarnings>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+</project>
View
40 src/main/java/fishwife/JRubyService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 David Kellum
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you
+ * may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package fishwife;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.load.BasicLibraryService;
+
+public class JRubyService implements BasicLibraryService
+{
+ public boolean basicLoad( Ruby runtime )
+ {
+ RubyModule fmod = runtime.defineModule( "Fishwife" );
+
+ RubyClass ouClass =
+ fmod.defineClassUnder( "OutputUtil",
+ runtime.getObject(),
+ ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR );
+
+ ouClass.defineAnnotatedMethods( OutputUtil.class );
+
+ return true;
+ }
+}
View
137 src/main/java/fishwife/OutputUtil.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2012 David Kellum
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you
+ * may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package fishwife;
+
+import static org.jruby.exceptions.RaiseException.createNativeRaiseException;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.jruby.Ruby;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.BlockCallback;
+import org.jruby.runtime.CallBlock;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+@JRubyClass( name="Fishwife::OutputUtil" )
+public class OutputUtil
+{
+ @JRubyMethod( name = "write_file",
+ meta = true,
+ required = 2,
+ argTypes = { RubyObject.class,
+ OutputStream.class } )
+ public static IRubyObject writeFile( ThreadContext tc,
+ IRubyObject klazz,
+ IRubyObject file,
+ IRubyObject ostr )
+ {
+ FileInputStream in = null;
+ try {
+ try {
+ String filePath = file.convertToString().asJavaString();
+ in = new FileInputStream( filePath );
+ OutputStream out =
+ (OutputStream) ostr.toJava( OutputStream.class );
+
+ final byte[] buff = new byte[ 8 * 1024 ];
+ while( true ) {
+ final int len = in.read( buff );
+ if( len > 0 ) {
+ out.write( buff, 0, len );
+ }
+ else break;
+ }
+ return tc.getRuntime().getNil();
+ }
+ finally {
+ if( in != null ) in.close();
+ }
+ }
+ catch( FileNotFoundException x ) {
+ throw createNativeRaiseException( tc.getRuntime(), x );
+ }
+ catch( IOException x ) {
+ throw createNativeRaiseException( tc.getRuntime(), x );
+ }
+ }
+
+ @JRubyMethod( name = "write_body",
+ meta = true,
+ required = 2,
+ argTypes = { RubyObject.class,
+ OutputStream.class } )
+ public static IRubyObject writeBody( ThreadContext tc,
+ IRubyObject klazz,
+ IRubyObject body,
+ IRubyObject out )
+ {
+ OutputStream ostream = (OutputStream) out.toJava( OutputStream.class );
+
+ RuntimeHelpers.invoke( tc, body, "each",
+ CallBlock.newCallClosure( klazz,
+ tc.getRuntime().getEnumerable(),
+ Arity.ONE_ARGUMENT,
+ new AppendBlockCallback( tc.getRuntime(),
+ ostream ),
+ tc ) );
+
+ return tc.getRuntime().getNil();
+ }
+
+ public static final class AppendBlockCallback implements BlockCallback
+ {
+ public AppendBlockCallback(Ruby runtime, OutputStream out)
+ {
+ _runtime = runtime;
+ _out = out;
+ }
+
+ public IRubyObject call( ThreadContext context,
+ IRubyObject[] args,
+ Block blk )
+ {
+ try {
+ final RubyString str = args[0].convertToString();
+ final ByteList blist = str.getByteList();
+
+ _out.write( blist.unsafeBytes(),
+ blist.begin(),
+ blist.length() );
+
+ return _runtime.getNil();
+ }
+ catch( IOException x ) {
+ throw createNativeRaiseException( _runtime, x );
+ }
+ }
+
+ private final Ruby _runtime;
+ private final OutputStream _out;
+ }
+
+}

0 comments on commit 067d9ff

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