org.xsocket.connection.http.NonBlockingBodyDataSource Maven / Gradle / Ivy
/*
* Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xsocket.org/
*/
package org.xsocket.connection.http;
import java.io.Closeable;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.IDataSource;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.AbstractNonBlockingStream;
import org.xsocket.connection.http.AbstractHttpConnection.ICompleteListener;
import org.xsocket.connection.spi.DefaultIoProvider;
/**
*
* I/O resource capable of providing body data in a non blocking way.
* Read operations returns immediately
*
* @author grro
*
*/
public class NonBlockingBodyDataSource extends AbstractNonBlockingStream implements IDataSource, ReadableByteChannel, Closeable, Cloneable {
private static final Logger LOG = Logger.getLogger(NonBlockingBodyDataSource.class.getName());
private final HandlerCaller handlerCaller = new HandlerCaller();
private List completeListeners = new ArrayList();
private boolean isComplete = false;
private IBodyHandler handler = null;
private Execution.Mode handlerExecutionMode = null;
/**
* constructor
*
* @param encoding the encoding
*/
NonBlockingBodyDataSource(String encoding) {
setEncoding(encoding);
}
/**
* constructor
*
* @param body the body
* @param encoding the encoding
*/
NonBlockingBodyDataSource(String body, String encoding) {
setEncoding(encoding);
appendDataToReadBuffer(new ByteBuffer[] {DataConverter.toByteBuffer(body, encoding)});
isComplete = true;
}
/**
* constructor
*
* @param body the body
* @param encoding the encoding
*/
NonBlockingBodyDataSource(byte[] body, String encoding) {
setEncoding(encoding);
appendDataToReadBuffer(new ByteBuffer[] { DataConverter.toByteBuffer(body) });
isComplete = true;
}
/**
* constructor
*
* @param body the body
* @param encoding the encoding
*/
NonBlockingBodyDataSource(ByteBuffer[] body, String encoding) {
setEncoding(encoding);
appendDataToReadBuffer(body);
isComplete = true;
}
/**
* constructor
*
* @param bodyDatasource the body data source
* @param encoding the encoding
*/
NonBlockingBodyDataSource(ReadableByteChannel bodyDatasource, String encoding) throws IOException {
setEncoding(encoding);
int chunkSize = 8192;
List buffers = new ArrayList();
int read = 0;
do {
ByteBuffer transferBuffer = ByteBuffer.allocate(chunkSize);
read = bodyDatasource.read(transferBuffer);
if (read > 0) {
if (transferBuffer.remaining() == 0) {
transferBuffer.flip();
buffers.add(transferBuffer);
} else {
transferBuffer.flip();
buffers.add(transferBuffer.slice());
}
}
} while (read > 0);
appendDataToReadBuffer(buffers.toArray(new ByteBuffer[buffers.size()]));
isComplete = true;
}
/**
* adds a complete listener
*
* @param listener the complete listener
*/
void addListener(ICompleteListener listener) {
assert (DefaultIoProvider.isDispatcherThread());
completeListeners.add(listener);
if (isComplete) {
listener.onComplete();
}
}
/**
* set complete flag
*
* @param isComplete the complete flag
*/
void setComplete(boolean isComplete) {
assert (DefaultIoProvider.isDispatcherThread());
this.isComplete = isComplete;
// call body handler
callBodyHandler(true);
// notify listeners
if (!completeListeners.isEmpty()) {
for (ICompleteListener listener : completeListeners) {
listener.onComplete();
}
}
}
/**
* return true, if all body data has been received
*
* @return true, if all body data has been received
*/
public boolean isComplete() {
return isComplete;
}
/**
* set the body handler
*
* @param bodyHandler the body handler
*/
public void setDataHandler(IBodyHandler bodyHandler) {
setDataHandler(bodyHandler, HttpUtils.getExecutionMode(bodyHandler));
}
public IBodyHandler getDataHandler() {
return handler;
}
private void setDataHandler(IBodyHandler bodyHandler, Execution.Mode executionMode) {
this.handler = bodyHandler;
this.handlerExecutionMode = executionMode;
callBodyHandler(false);
}
/**
* {@inheritDoc}
*/
@Override
protected void onPostRead() {
if ((available() == 0) && (isComplete)) {
try {
close();
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by closing body data source " + ioe.toString());
}
}
}
}
/**
* appends data
*
* @param data the data to append
*/
void append(ByteBuffer data) {
append(new ByteBuffer[] { data });
}
/**
* appends data
*
* @param data the data to append
*/
void append(ByteBuffer[] data) {
appendDataToReadBuffer(data);
callBodyHandler(false);
}
private void callBodyHandler(boolean forceCall) {
if (handler != null) {
handlerCaller.setForceCall(forceCall);
doCallBodyHandler(handlerCaller, handlerExecutionMode);
}
}
/**
* calls th ebody handler call back
*
* @param handlerCaller the handler caller
* @param handlerExecutionMode the execution mode
*/
void doCallBodyHandler(HandlerCaller handlerCaller, Execution.Mode handlerExecutionMode) {
try {
handler.onData(this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by onData " + handler + " " + ioe.toString());
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected Object clone() throws CloneNotSupportedException {
NonBlockingBodyDataSource copy = (NonBlockingBodyDataSource) super.clone();
copy.handler = null;
copy.completeListeners = new ArrayList();
copy.handlerExecutionMode = null;
return copy;
}
/**
* duplicates the body data source
* @return the copy
*/
public NonBlockingBodyDataSource duplicate() {
try {
return (NonBlockingBodyDataSource) clone();
} catch (CloneNotSupportedException cnse) {
throw new RuntimeException("could not clone " + this + " " + cnse.toString());
}
}
private void closeSilence() {
try {
close();
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Error occured by closing data source " + this + " " + ioe.toString());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return printReadBuffer(getEncoding());
}
/**
* the handler caller
*
*/
final class HandlerCaller implements Runnable {
private boolean forceCall = false;
private void setForceCall(boolean forceCall) {
this.forceCall = forceCall;
}
/**
* {@inheritDoc}
*/
public void run() {
while ((available() > 0) || forceCall) {
int version = getReadBufferVersion();
boolean success = call();
if (!success) {
return;
}
int newVersion = getReadBufferVersion();
if (newVersion != version) {
version = newVersion;
} else {
return;
}
}
}
private boolean call() {
try {
handler.onData(NonBlockingBodyDataSource.this);
return true;
} catch (MaxReadSizeExceededException mee) {
closeSilence();
} catch (BufferUnderflowException bue) {
// ignore
} catch (RuntimeException re) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("closing data source because an error has been occured by handling data by bodyHandler. " + handler + " Reason: " + re.toString());
}
closeSilence();
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("closing data source because an error has been occured by handling data by bodyHandler. " + handler + " Reason: " + ioe.toString());
}
closeSilence();
}
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy