xapi.jre.io.IOServiceDefault Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xapi-gwt Show documentation
Show all versions of xapi-gwt Show documentation
This module exists solely to package all other gwt modules into a single
uber jar. This makes deploying to non-mavenized targets much easier.
Of course, you would be wise to inherit your dependencies individually;
the uber jar is intended for projects like collide,
which have complex configuration, and adding many jars would be a pain.
The newest version!
/**
*
*/
package xapi.jre.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map.Entry;
import javax.inject.Provider;
import xapi.annotation.inject.InstanceDefault;
import xapi.annotation.inject.SingletonDefault;
import xapi.collect.X_Collect;
import xapi.collect.api.StringDictionary;
import xapi.collect.api.StringTo;
import xapi.collect.api.StringTo.Many;
import xapi.inject.impl.SingletonProvider;
import xapi.io.IOConstants;
import xapi.io.X_IO;
import xapi.io.api.CancelledException;
import xapi.io.api.IOCallback;
import xapi.io.api.IOMessage;
import xapi.io.api.IORequest;
import xapi.io.api.LineReader;
import xapi.io.api.StringReader;
import xapi.io.impl.AbstractIOService;
import xapi.io.service.IOService;
import xapi.log.X_Log;
import xapi.log.api.LogLevel;
import xapi.time.X_Time;
import xapi.time.api.Moment;
import xapi.util.X_Runtime;
import xapi.util.X_Util;
import xapi.util.api.ReceivesValue;
import xapi.util.impl.RunUnsafe;
/**
* @author James X. Nelson ([email protected], @james)
*
*/
@InstanceDefault(implFor=IOService.class)
@SingletonDefault(implFor=IOService.class)
public class IOServiceDefault extends AbstractIOService {
/**
* @author James X. Nelson ([email protected], @james)
*
*/
public class IORequestDefault extends AbstractIORequest {
private Thread connectionThread;
@Override
public void cancel() {
super.cancel();
if (connectionThread != null) {
connectionThread.interrupt();
connectionThread = null;
synchronized (this) {
notifyAll();
}
}
}
@Override
public String response() {
if (!super.isStarted()) {
synchronized (this) {
try {
wait();
} catch (final InterruptedException e) {
cancel();
Thread.currentThread().interrupt();
}
}
}
if (connectionThread != null) {
synchronized (connectionThread) {
try {
if (connectionThread != null) {
connectionThread.join();
}
} catch (final InterruptedException ignored) {
cancel();
Thread.currentThread().interrupt();
}
connectionThread = null;
}
}
return getValue();
}
public void setConnectionThread(final Thread thread) {
this.connectionThread = thread;
}
}
@Override
public IORequest get(final String uri, final StringDictionary headers, final IOCallback> callback) {
final String url = normalize(uri);
if (callback.isCancelled()) {
return cancelled;
}
final Moment startUp = X_Time.now();
try {
final URL asUrl = new URL(url);
final URLConnection connect = asUrl.openConnection();
connect.setDoInput(true);
connect.setDoOutput(false);
if (headers != null) {
headers.forKeys(new ReceivesValue() {
@Override
public void set(final String key) {
final String value = headers.getValue(key);
connect.setRequestProperty(key, value);
}
});
}
applySettings(connect, IOConstants.METHOD_GET);
final LogLevel logLevel = logLevel();
if (X_Log.loggable(logLevel)) {
X_Log.log(getClass(), logLevel, "Startup time for ",url,"took",X_Time.difference(startUp));
}
final IORequestDefault request = createRequest();
sendRequest(connect, request, callback, url, headers, null);
return request;
} catch (final Throwable e) {
callback.onError(e);
if (X_Runtime.isDebug()) {
X_Log.warn("IO Error", e);
}
return cancelled;
}
}
/**
* @see xapi.io.service.IOService#post(java.lang.String, java.lang.String, xapi.collect.api.StringDictionary, xapi.io.api.IOCallback)
*/
@Override
public IORequest post(final String uri, final String body, final StringDictionary headers,
final IOCallback> callback) {
final String url = normalize(uri);
if (callback.isCancelled()) {
return cancelled;
}
final Moment startUp = X_Time.now();
try {
final URL asUrl = new URL(url);
final URLConnection connect = asUrl.openConnection();
connect.setDoInput(true);
connect.setDoOutput(true);
if (headers != null) {
headers.forKeys(new ReceivesValue() {
@Override
public void set(final String key) {
final String value = headers.getValue(key);
connect.setRequestProperty(key, value);
}
});
}
applySettings(connect, IOConstants.METHOD_POST);
final LogLevel logLevel = logLevel();
if (X_Log.loggable(logLevel)) {
X_Log.log(getClass(), logLevel, "Startup time for ",url,"took",X_Time.difference(startUp));
}
final IORequestDefault request = createRequest();
sendRequest(connect, request, callback, url, headers, body);
return request;
} catch (final Throwable e) {
callback.onError(e);
if (X_Runtime.isDebug()) {
X_Log.warn("IO Error", e);
}
return cancelled;
}
}
protected void sendRequest(final URLConnection connect, final IORequestDefault request, final IOCallback> callback, final String url, final StringDictionary headers, final String body) {
final LogLevel logLevel = logLevel();
final Moment before = X_Time.now();
X_Time.runUnsafe(new RunUnsafe() {
@Override
protected void doRun() throws Throwable {
if (X_Log.loggable(logLevel)) {
X_Log.log(getClass(), logLevel, "Starting IO for ",url,"took",X_Time.difference(before));
}
if (request.isCancelled()) {
callback.onError(new CancelledException(request));
return;
}
request.setConnectionThread(Thread.currentThread());
synchronized (request) {
request.start();
request.notifyAll();
}
InputStream in;
String res;
try {
if (body != null) {
// We need to send data on the output stream first
final Moment start = X_Time.now();
try(
final OutputStream out = connect.getOutputStream();
) {
in = toStream(body, headers);
X_IO.drain(out, in);
if (X_Log.loggable(logLevel)) {
X_Log.log(getClass(), logLevel, "Sending data for ",url,"took",X_Time.difference(start));
}
}
}
final Moment start = X_Time.now();
try {
in = connect.getInputStream();
try {
res = drainInput(in, callback);
} finally {
in.close();
}
} catch (final SocketException e) {
in = connect.getInputStream();
if (request.isCancelled()) {
callback.onError(new CancelledException(request));
return;
}
try {
res = drainInput(in, callback);
} finally {
in.close();
}
}
if (X_Log.loggable(logLevel)) {
X_Log.log(getClass(), logLevel, "Receiving data for ",url,"took",X_Time.difference(start));
}
if (request.isCancelled()) {
callback.onError(new CancelledException(request));
return;
}
final Provider> resultHeaders = new SingletonProvider>() {
@Override
protected Many initialValue() {
final Many headers = X_Collect.newStringMultiMap(String.class);
for (final Entry> entry : connect.getHeaderFields().entrySet()) {
for (final String value : entry.getValue()) {
headers.add(entry.getKey(), value);
}
}
return headers;
}
};
request.setValue(res);
request.setResultHeaders(resultHeaders);
if (connect instanceof HttpURLConnection) {
final int status = ((HttpURLConnection)connect).getResponseCode();
final String message = ((HttpURLConnection)connect).getResponseMessage();
request.setStatus(status, message);
} else {
request.setStatus(IORequest.STATUS_NOT_HTTP, "Request not using http: "+connect.getClass());
}
final Moment callbackTime = X_Time.now();
try {
callback.onSuccess(new IOMessage() {
@Override
public String body() {
return request.getValue();
}
@Override
public int modifier() {
return IOConstants.METHOD_GET;
}
@Override
public String url() {
return url;
}
@Override
public StringTo.Many headers() {
return resultHeaders.get();
}
@Override
public int statusCode() {
return request.getStatusCode();
}
@Override
public String statusMessage() {
return request.getStatusText();
}
});
} catch (final Throwable t) {
X_Log.error("Error invoking IO callback on",callback,"for request",url, t);
callback.onError(X_Util.unwrap(t));
}
if (X_Log.loggable(logLevel)) {
X_Log.log(getClass(), logLevel, "Callback time for ",url,"took",X_Time.difference(callbackTime));
}
} catch (final Throwable t) {
request.cancel();
callback.onError(X_Util.unwrap(t));
} finally {
request.connectionThread = null;
}
}
});
}
private InputStream toStream(final String body, final StringDictionary headers) {
return X_IO.toStreamUtf8(body);
}
protected IORequestDefault createRequest() {
return new IORequestDefault();
}
protected String drainInput
( final InputStream in, final IOCallback> callback)
throws IOException {
try {
final String message;
final BufferedReader read = new BufferedReader(new InputStreamReader(in));
final StringReader messageReader = new StringReader();
if (callback instanceof LineReader) {
messageReader.forwardTo((LineReader)callback);
}
String line;
messageReader.onStart();
while ((line = read.readLine())!=null) {
messageReader.onLine(line);
}
// grab body >before< calling onEnd, as it cleans up its memory
message = messageReader.toString();
messageReader.onEnd();
// All done.
return message;
} finally {
in.close();
}
}
}