Issue NETTY-175 - Large data transfer
* Added ChunkStream and FileChunkStream * Added ChunkStreamWriteHandler
This commit is contained in:
parent
ee8ffec265
commit
c08e7dd397
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source
|
||||||
|
*
|
||||||
|
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
||||||
|
* by the @author tags. See the COPYRIGHT.txt in the distribution for a
|
||||||
|
* full listing of individual contributors.
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this software; if not, write to the Free
|
||||||
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
|
*/
|
||||||
|
package org.jboss.netty.handler.stream;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||||
|
* @author Trustin Lee (tlee@redhat.com)
|
||||||
|
* @version $Rev$, $Date$
|
||||||
|
*/
|
||||||
|
public interface ChunkStream {
|
||||||
|
boolean available() throws Exception;
|
||||||
|
Object readChunk() throws Exception;
|
||||||
|
void close() throws Exception;
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source
|
||||||
|
*
|
||||||
|
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
||||||
|
* by the @author tags. See the COPYRIGHT.txt in the distribution for a
|
||||||
|
* full listing of individual contributors.
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this software; if not, write to the Free
|
||||||
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
|
*/
|
||||||
|
package org.jboss.netty.handler.stream;
|
||||||
|
|
||||||
|
import static org.jboss.netty.channel.Channels.*;
|
||||||
|
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelDownstreamHandler;
|
||||||
|
import org.jboss.netty.channel.ChannelEvent;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
|
import org.jboss.netty.channel.ChannelFutureListener;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.ChannelPipelineCoverage;
|
||||||
|
import org.jboss.netty.channel.ChannelState;
|
||||||
|
import org.jboss.netty.channel.ChannelStateEvent;
|
||||||
|
import org.jboss.netty.channel.ChannelUpstreamHandler;
|
||||||
|
import org.jboss.netty.channel.Channels;
|
||||||
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
|
import org.jboss.netty.logging.InternalLogger;
|
||||||
|
import org.jboss.netty.logging.InternalLoggerFactory;
|
||||||
|
import org.jboss.netty.util.internal.LinkedTransferQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||||
|
* @author Trustin Lee (tlee@redhat.com)
|
||||||
|
* @version $Rev$, $Date$
|
||||||
|
*/
|
||||||
|
@ChannelPipelineCoverage("one")
|
||||||
|
public class ChunkStreamWriteHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler {
|
||||||
|
|
||||||
|
private static final InternalLogger logger =
|
||||||
|
InternalLoggerFactory.getInstance(ChunkStreamWriteHandler.class);
|
||||||
|
|
||||||
|
private final Queue<MessageEvent> queue =
|
||||||
|
new LinkedTransferQueue<MessageEvent>();
|
||||||
|
|
||||||
|
private MessageEvent currentEvent;
|
||||||
|
|
||||||
|
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
|
||||||
|
throws Exception {
|
||||||
|
if (!(e instanceof MessageEvent)) {
|
||||||
|
ctx.sendDownstream(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.offer((MessageEvent) e);
|
||||||
|
if (ctx.getChannel().isWritable()) {
|
||||||
|
flushWriteEventQueue(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
|
||||||
|
throws Exception {
|
||||||
|
if (!(e instanceof ChannelStateEvent)) {
|
||||||
|
ctx.sendUpstream(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelStateEvent cse = (ChannelStateEvent) e;
|
||||||
|
if (cse.getState() != ChannelState.INTEREST_OPS) {
|
||||||
|
ctx.sendUpstream(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.getChannel().isWritable()) {
|
||||||
|
flushWriteEventQueue(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void flushWriteEventQueue(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
Channel channel = ctx.getChannel();
|
||||||
|
do {
|
||||||
|
if (currentEvent == null) {
|
||||||
|
currentEvent = queue.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentEvent == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object m = currentEvent.getMessage();
|
||||||
|
if (m instanceof ChunkStream) {
|
||||||
|
ChunkStream stream = (ChunkStream) m;
|
||||||
|
Object chunk;
|
||||||
|
boolean last;
|
||||||
|
try {
|
||||||
|
chunk = stream.readChunk();
|
||||||
|
last = !stream.available();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
currentEvent.getFuture().setFailure(t);
|
||||||
|
fireExceptionCaught(ctx, t);
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (Throwable t2) {
|
||||||
|
logger.warn("Failed to close a stream.", t2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
ChannelFuture writeFuture;
|
||||||
|
final MessageEvent currentEvent = this.currentEvent;
|
||||||
|
if (last) {
|
||||||
|
this.currentEvent = null;
|
||||||
|
writeFuture = currentEvent.getFuture();
|
||||||
|
writeFuture.addListener(new ChannelFutureListener() {
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
((ChunkStream) currentEvent.getMessage()).close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
writeFuture = future(channel);
|
||||||
|
writeFuture.addListener(new ChannelFutureListener() {
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
if (!future.isSuccess()) {
|
||||||
|
currentEvent.getFuture().setFailure(future.getCause());
|
||||||
|
((ChunkStream) currentEvent.getMessage()).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Channels.write(
|
||||||
|
ctx, writeFuture, chunk,
|
||||||
|
currentEvent.getRemoteAddress());
|
||||||
|
} else {
|
||||||
|
currentEvent = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.sendDownstream(currentEvent);
|
||||||
|
currentEvent = null;
|
||||||
|
}
|
||||||
|
} while (channel.isWritable());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source
|
||||||
|
*
|
||||||
|
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
||||||
|
* by the @author tags. See the COPYRIGHT.txt in the distribution for a
|
||||||
|
* full listing of individual contributors.
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this software; if not, write to the Free
|
||||||
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
|
*/
|
||||||
|
package org.jboss.netty.handler.stream;
|
||||||
|
|
||||||
|
import static org.jboss.netty.buffer.ChannelBuffers.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||||
|
* @author Trustin Lee (tlee@redhat.com)
|
||||||
|
* @version $Rev$, $Date$
|
||||||
|
*/
|
||||||
|
public class FileChunkStream implements ChunkStream {
|
||||||
|
|
||||||
|
private static final int DEFAULT_CHUNK_SIZE = 8192;
|
||||||
|
|
||||||
|
private final RandomAccessFile file;
|
||||||
|
private final long startOffset;
|
||||||
|
private final long endOffset;
|
||||||
|
private final int chunkSize;
|
||||||
|
private volatile long offset;
|
||||||
|
|
||||||
|
public FileChunkStream(File file) throws IOException {
|
||||||
|
this(file, DEFAULT_CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChunkStream(File file, int chunkSize) throws IOException {
|
||||||
|
this(new RandomAccessFile(file, "r"), chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChunkStream(RandomAccessFile file, int chunkSize) throws IOException {
|
||||||
|
this(file, 0, file.length(), chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChunkStream(RandomAccessFile file, long offset, long length, int chunkSize) throws IOException {
|
||||||
|
if (file == null) {
|
||||||
|
throw new NullPointerException("file");
|
||||||
|
}
|
||||||
|
if (offset < 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"offset: " + offset + " (expected: 0 or greater)");
|
||||||
|
}
|
||||||
|
if (length < 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"length: " + length + " (expected: 0 or greater)");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.file = file;
|
||||||
|
this.offset = startOffset = offset;
|
||||||
|
endOffset = offset + length;
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
|
|
||||||
|
file.seek(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStartOffset() {
|
||||||
|
return startOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEndOffset() {
|
||||||
|
return endOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCurrentOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean available() throws Exception {
|
||||||
|
return offset < endOffset && file.getChannel().isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws Exception {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object readChunk() throws Exception {
|
||||||
|
long offset = this.offset;
|
||||||
|
if (offset >= endOffset) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset);
|
||||||
|
byte[] chunk = new byte[chunkSize];
|
||||||
|
file.readFully(chunk);
|
||||||
|
this.offset = offset + chunkSize;
|
||||||
|
return wrappedBuffer(chunk);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user