package com.koushikdutta.async;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.callback.DataCallback;
import com.koushikdutta.async.callback.WritableCallback;
import com.koushikdutta.async.util.Allocator;
import com.koushikdutta.async.util.StreamUtility;
import com.koushikdutta.async.wrapper.AsyncSocketWrapper;
import com.koushikdutta.async.wrapper.DataEmitterWrapper;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
public class Util {
public static boolean SUPRESS_DEBUG_EXCEPTIONS = false;
public static void emitAllData(DataEmitter emitter, ByteBufferList list) {
int remaining;
DataCallback handler = null;
while (!emitter.isPaused() && (handler = emitter.getDataCallback()) != null && (remaining = list.remaining()) > 0) {
handler.onDataAvailable(emitter, list);
if (remaining == list.remaining() && handler == emitter.getDataCallback() && !emitter.isPaused()) {
// this is generally indicative of failure...
// 1) The data callback has not changed
// 2) no data was consumed
// 3) the data emitter was not paused
// call byteBufferList.recycle() or read all the data to prevent this assertion.
// this is nice to have, as it identifies protocol or parsing errors.
// System.out.println("Data: " + list.peekString());
System.out.println("handler: " + handler);
list.recycle();
if (SUPRESS_DEBUG_EXCEPTIONS)
return;
assert false;
throw new RuntimeException("mDataHandler failed to consume data, yet remains the mDataHandler.");
}
}
if (list.remaining() != 0 && !emitter.isPaused()) {
// not all the data was consumed...
// call byteBufferList.recycle() or read all the data to prevent this assertion.
// this is nice to have, as it identifies protocol or parsing errors.
// System.out.println("Data: " + list.peekString());
System.out.println("handler: " + handler);
System.out.println("emitter: " + emitter);
list.recycle();
if (SUPRESS_DEBUG_EXCEPTIONS)
return;
assert false;
throw new RuntimeException("Not all data was consumed by Util.emitAllData");
}
}
public static void pump(final InputStream is, final DataSink ds, final CompletedCallback callback) {
pump(is, Integer.MAX_VALUE, ds, callback);
}
public static void pump(final InputStream is, final long max, final DataSink ds, final CompletedCallback callback) {
final CompletedCallback wrapper = new CompletedCallback() {
boolean reported;
@Override
public void onCompleted(Exception ex) {
if (reported)
return;
reported = true;
callback.onCompleted(ex);
}
};
final WritableCallback cb = new WritableCallback() {
int totalRead = 0;
private void cleanup() {
ds.setClosedCallback(null);
ds.setWriteableCallback(null);
pending.recycle();
StreamUtility.closeQuietly(is);
}
ByteBufferList pending = new ByteBufferList();
Allocator allocator = new Allocator();
@Override
public void onWriteable() {
try {
do {
if (!pending.hasRemaining()) {
ByteBuffer b = allocator.allocate();
long toRead = Math.min(max - totalRead, b.capacity());
int read = is.read(b.array(), 0, (int)toRead);
if (read == -1 || totalRead == max) {
cleanup();
wrapper.onCompleted(null);
return;
}
allocator.track(read);
totalRead += read;
b.position(0);
b.limit(read);
pending.add(b);
}
ds.write(pending);
}
while (!pending.hasRemaining());
}
catch (Exception e) {
cleanup();
wrapper.onCompleted(e);
}
}
};
ds.setWriteableCallback(cb);
ds.setClosedCallback(wrapper);
cb.onWriteable();
}
public static void pump(final DataEmitter emitter, final DataSink sink, final CompletedCallback callback) {
final DataCallback dataCallback = new DataCallback() {
@Override
public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
sink.write(bb);
if (bb.remaining() > 0)
emitter.pause();
}
};
emitter.setDataCallback(dataCallback);
sink.setWriteableCallback(new WritableCallback() {
@Override
public void onWriteable() {
emitter.resume();
}
});
final CompletedCallback wrapper = new CompletedCallback() {
boolean reported;
@Override
public void onCompleted(Exception ex) {
if (reported)
return;
reported = true;
emitter.setDataCallback(null);
emitter.setEndCallback(null);
sink.setClosedCallback(null);
sink.setWriteableCallback(null);
callback.onCompleted(ex);
}
};
emitter.setEndCallback(wrapper);
sink.setClosedCallback(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
if (ex == null)
ex = new IOException("sink was closed before emitter ended");
wrapper.onCompleted(ex);
}
});
}
public static void stream(AsyncSocket s1, AsyncSocket s2, CompletedCallback callback) {
pump(s1, s2, callback);
pump(s2, s1, callback);
}
public static void pump(final File file, final DataSink ds, final CompletedCallback callback) {
try {
if (file == null || ds == null) {
callback.onCompleted(null);
return;
}
final InputStream is = new FileInputStream(file);
pump(is, ds, new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
try {
is.close();
callback.onCompleted(ex);
}
catch (IOException e) {
callback.onCompleted(e);
}
}
});
}
catch (Exception e) {
callback.onCompleted(e);
}
}
public static void writeAll(final DataSink sink, final ByteBufferList bb, final CompletedCallback callback) {
WritableCallback wc;
sink.setWriteableCallback(wc = new WritableCallback() {
@Override
public void onWriteable() {
sink.write(bb);
if (bb.remaining() == 0 && callback != null) {
sink.setWriteableCallback(null);
callback.onCompleted(null);
}
}
});
wc.onWriteable();
}
public static void writeAll(DataSink sink, byte[] bytes, CompletedCallback callback) {
ByteBuffer bb = ByteBufferList.obtain(bytes.length);
bb.put(bytes);
bb.flip();
ByteBufferList bbl = new ByteBufferList();
bbl.add(bb);
writeAll(sink, bbl, callback);
}
public static <T extends AsyncSocket> T getWrappedSocket(AsyncSocket socket, Class<T> wrappedClass) {
if (wrappedClass.isInstance(socket))
return (T)socket;
while (socket instanceof AsyncSocketWrapper) {
socket = ((AsyncSocketWrapper)socket).getSocket();
if (wrappedClass.isInstance(socket))
return (T)socket;
}
return null;
}
public static DataEmitter getWrappedDataEmitter(DataEmitter emitter, Class wrappedClass) {
if (wrappedClass.isInstance(emitter))
return emitter;
while (emitter instanceof DataEmitterWrapper) {
emitter = ((AsyncSocketWrapper)emitter).getSocket();
if (wrappedClass.isInstance(emitter))
return emitter;
}
return null;
}
public static void end(DataEmitter emitter, Exception e) {
if (emitter == null)
return;
end(emitter.getEndCallback(), e);
}
public static void end(CompletedCallback end, Exception e) {
if (end != null)
end.onCompleted(e);
}
public static void writable(DataSink emitter) {
if (emitter == null)
return;
writable(emitter.getWriteableCallback());
}
public static void writable(WritableCallback writable) {
if (writable != null)
writable.onWriteable();
}
}