Skip to content

Commit

Permalink
Merge branch 'java-output-extension' into fishwife-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
dekellum committed Feb 12, 2012
2 parents 3a3e09a + 4fc5344 commit 067d9ff
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Expand Up @@ -2,3 +2,8 @@
doc/ doc/
pkg/ pkg/
.bundle/ .bundle/
target/
.classpath
.project
.settings
lib/fishwife/*.jar
17 changes: 17 additions & 0 deletions 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
1 change: 1 addition & 0 deletions Manifest.txt
Expand Up @@ -15,3 +15,4 @@ spec/spec_helper.rb
spec/test_app.rb spec/test_app.rb
spec/data/hello.txt spec/data/hello.txt
spec/data/reddit-icon.png spec/data/reddit-icon.png
lib/fishwife/fishwife-1.3.0.jar
2 changes: 1 addition & 1 deletion fishwife.gemspec
Expand Up @@ -19,5 +19,5 @@ RJack::TarPit.specify do |s|
s.depend 'rjack-logback', '~> 1.2', :dev s.depend 'rjack-logback', '~> 1.2', :dev
s.depend 'rspec', '~> 2.8.0', :dev s.depend 'rspec', '~> 2.8.0', :dev


s.platform = :java s.maven_strategy = :no_assembly
end end
6 changes: 6 additions & 0 deletions lib/fishwife.rb
Expand Up @@ -23,6 +23,12 @@
# Load Jetty JARs. # Load Jetty JARs.
require 'rjack-jetty' require 'rjack-jetty'


require 'fishwife/base'

module Fishwife
require "#{LIB_DIR}/fishwife-#{VERSION}.jar"
end

require 'rack' require 'rack'
require 'fishwife/rack_servlet' require 'fishwife/rack_servlet'
require 'fishwife/http_server' require 'fishwife/http_server'
Expand Down
1 change: 1 addition & 0 deletions lib/fishwife/base.rb
Expand Up @@ -16,4 +16,5 @@


module Fishwife module Fishwife
VERSION = '1.3.0' VERSION = '1.3.0'
LIB_DIR = File.dirname( __FILE__ )
end end
34 changes: 14 additions & 20 deletions lib/fishwife/rack_servlet.rb
Expand Up @@ -15,6 +15,9 @@
# permissions and limitations under the License. # permissions and limitations under the License.
#++ #++


# Magic loader hook -> JRubyService
require 'fishwife/JRuby'

# #
# Wraps a Rack application in a Java servlet. # Wraps a Rack application in a Java servlet.
# #
Expand Down Expand Up @@ -214,29 +217,20 @@ def rack_to_servlet(rack_response, response)
end end
end end


# How else would we write output?
output = response.getOutputStream output = response.getOutputStream


# Turn the body into something nice and Java-y. if body.respond_to?( :to_path )
if(body.respond_to?(:to_path))
# We've been told to serve a file; use FileInputStream to path = body.to_path
# stream the file directly to the servlet, because this is a
# lot faster than doing it with Ruby. # Set Content-Length unless this is an async request.
file = java.io.File.new(body.to_path) response.setContentLength( File.size( path ) ) unless content_length


# We set the content-length so we can use Keep-Alive, unless # FIXME: Support ranges?
# this is an async request.
response.setContentLength(file.length) unless content_length OutputUtil.write_file( path, output )

# 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
else else
body.each { |l| output.write(l.to_java_bytes) } OutputUtil.write_body( body, output )
end end


# Close the body if we're supposed to. # Close the body if we're supposed to.
Expand Down
52 changes: 52 additions & 0 deletions 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>
40 changes: 40 additions & 0 deletions 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;
}
}
137 changes: 137 additions & 0 deletions 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.