/*
Copyright (c) Microsoft Open Technologies, Inc.
All Rights Reserved
See License.txt in the project root for license information.
*/
package microsoft.aspnet.signalr.client.transport;
import java.util.ArrayList;
import java.util.List;
import microsoft.aspnet.signalr.client.Action;
import microsoft.aspnet.signalr.client.ErrorCallback;
import microsoft.aspnet.signalr.client.LogLevel;
import microsoft.aspnet.signalr.client.SignalRFuture;
import microsoft.aspnet.signalr.client.ConnectionBase;
import microsoft.aspnet.signalr.client.Logger;
import microsoft.aspnet.signalr.client.NullLogger;
import microsoft.aspnet.signalr.client.http.HttpConnection;
/**
* ClientTransport implementation that selects the best available transport
*/
public class AutomaticTransport extends HttpClientTransport {
private List<ClientTransport> mTransports;
private ClientTransport mRealTransport;
/**
* Initializes the transport with a NullLogger
*/
public AutomaticTransport() {
this(new NullLogger());
}
/**
* Initializes the transport with a logger
*
* @param logger
* logger to log actions
*/
public AutomaticTransport(Logger logger) {
super(logger);
initialize(logger);
}
/**
* Initializes the transport with a logger and an httpConnection
*
* @param logger
* the logger
* @param httpConnection
* the httpConnection
*/
public AutomaticTransport(Logger logger, HttpConnection httpConnection) {
super(logger, httpConnection);
initialize(logger);
}
private void initialize(Logger logger) {
mTransports = new ArrayList<ClientTransport>();
mTransports.add(new WebsocketTransport(logger));
mTransports.add(new ServerSentEventsTransport(logger));
mTransports.add(new LongPollingTransport(logger));
}
@Override
public String getName() {
if (mRealTransport == null) {
return "AutomaticTransport";
}
return mRealTransport.getName();
}
@Override
public boolean supportKeepAlive() {
if (mRealTransport != null) {
return mRealTransport.supportKeepAlive();
}
return false;
}
private void resolveTransport(final ConnectionBase connection, final ConnectionType connectionType, final DataResultCallback callback,
final int currentTransportIndex, final SignalRFuture<Void> startFuture) {
final ClientTransport currentTransport = mTransports.get(currentTransportIndex);
final SignalRFuture<Void> transportStart = currentTransport.start(connection, connectionType, callback);
transportStart.done(new Action<Void>() {
@Override
public void run(Void obj) throws Exception {
// set the real transport and trigger end the start future
mRealTransport = currentTransport;
startFuture.setResult(null);
}
});
final ErrorCallback handleError = new ErrorCallback() {
@Override
public void onError(Throwable error) {
// if the transport is already started, forward the error
if (mRealTransport != null) {
startFuture.triggerError(error);
return;
}
log(String.format("Auto: Faild to connect using transport %s. %s", currentTransport.getName(), error.toString()), LogLevel.Information);
int next = currentTransportIndex + 1;
if (next < mTransports.size()) {
resolveTransport(connection, connectionType, callback, next, startFuture);
} else {
startFuture.triggerError(error);
}
}
};
transportStart.onError(handleError);
startFuture.onCancelled(new Runnable() {
@Override
public void run() {
// if the transport is already started, forward the cancellation
if (mRealTransport != null) {
transportStart.cancel();
return;
}
handleError.onError(new Exception("Operation cancelled"));
}
});
}
@Override
public SignalRFuture<Void> start(final ConnectionBase connection, final ConnectionType connectionType, final DataResultCallback callback) {
SignalRFuture<Void> startFuture = new SignalRFuture<Void>();
resolveTransport(connection, connectionType, callback, 0, startFuture);
return startFuture;
}
@Override
public SignalRFuture<Void> send(ConnectionBase connection, String data, DataResultCallback callback) {
if (mRealTransport != null) {
return mRealTransport.send(connection, data, callback);
}
return null;
}
@Override
public SignalRFuture<Void> abort(ConnectionBase connection) {
if (mRealTransport != null) {
return mRealTransport.abort(connection);
}
return null;
}
}