api/src/main/java/net/signalr/client/util/concurrent/promise/Promises.java
public final class Promises {

    /**
     * Initializes a new instance of the {@link Promises} class.
     */
    private Promises() {
    }

    /**
     * Returns a new success promise.
     * 
     * @return The promise.
     */
    public static Promise<Void> newSuccess() {
        return newSuccess(null);
    }

    /**
     * Returns a new success promise.
     * 
     * @param value The value.
     * @return The promise.
     */
    public static <T> Promise<T> newSuccess(final T value) {
        return new Deferred<>(value);
    }

    /**
     * Returns a new failure promise.
     * 
     * @param cause The cause.
     * @return The promise.
     */
    public static <T> Promise<T> newFailure(final Throwable cause) {
        return new Deferred<>(cause);
    }

    /**
     * Returns a new promise based on the execution of the specified runnable.
     * 
     * @param runnable The runnable.
     * @return The promise.
     */
    public static Promise<Void> newPromise(final Runnable runnable) {
        if (runnable == null) {
            throw new IllegalArgumentException("Runnable must not be null");
        }

        try {
            runnable.run();
        } catch (final Throwable t) {
            return newFailure(t);
        }

        return newSuccess();
    }

    /**
     * Returns a new promise based on the execution of the specified callable.
     * 
     * @param callable The callable.
     * @return The promise.
     */
    public static <V> Promise<V> newPromise(final Callable<V> callable) {
        if (callable == null) {
            throw new IllegalArgumentException("Callable must not be null");
        }

        try {
            final V value = callable.call();

            return newSuccess(value);
        } catch (final Throwable t) {
            return newFailure(t);
        }
    }

    /**
     * Awaits the specified promise.
     * 
     * @param promise The promise.
     * @return The value.
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public static <T> T await(final Promise<T> promise) throws InterruptedException, ExecutionException {
        if (promise == null) {
            throw new IllegalArgumentException("Promise must not be null");
        }

        final CompletableFuture<T> future = new CompletableFuture<>();

        promise.then(future);

        return future.get();
    }

    /**
     * Awaits the specified promise.
     * 
     * @param promise The promise.
     * @param timeout The timeout.
     * @param timeUnit The time unit.
     * @return The value.
     * @throws InterruptedException
     * @throws ExecutionException
     * @throws TimeoutException
     */
    public static <T> T await(final Promise<T> promise, final long timeout, final TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
        if (promise == null) {
            throw new IllegalArgumentException("Promise must not be null");
        }

        final CompletableFuture<T> future = new CompletableFuture<>();

        promise.then(future);

        return future.get(timeout, timeUnit);
    }

    /**
     * Returns a future for the specified promise.
     * 
     * @param promise The promise.
     * @return The future.
     */
    public static <T> Future<T> toFuture(final Promise<T> promise) {
        if (promise == null) {
            throw new IllegalArgumentException("Promise must not be null");
        }

        final CompletableFuture<T> future = new CompletableFuture<>();

        promise.then(future);

        return future;
    }
}
api/src/main/java/net/signalr/client/transport/NegotiationResponse.java
public final class NegotiationResponse implements JsonReadable, TransportOptions {

    /**
     * The relative URL.
     */
    private String _relativeUrl;

    /**
     * The connection token.
     */
    private String _connectionToken;

    /**
     * The connection ID.
     */
    private String _connectionId;

    /**
     * The protocol version.
     */
    private String _protocolVersion;

    /**
     * A value indicating whether web sockets should be tried.
     */
    private boolean _tryWebSockets;

    /**
     * The keep-alive timeout in milliseconds.
     */
    private long _keepAliveTimeout;

    /**
     * The disconnect timeout in milliseconds.
     */
    private long _disconnectTimeout;

    /**
     * The transport connect timeout in milliseconds.
     */
    private long _connectTimeout;

    /**
     * Initializes a new instance of the {@link NegotiationResponse} class.
     */
    public NegotiationResponse() {
        _keepAliveTimeout = -1;
        _disconnectTimeout = -1;
        _connectTimeout = -1;
    }

    @Override
    public String getRelativeUrl() {
        return _relativeUrl;
    }

    @Override
    public String getConnectionToken() {
        return _connectionToken;
    }

    @Override
    public String getConnectionId() {
        return _connectionId;
    }

    @Override
    public String getProtocolVersion() {
        return _protocolVersion;
    }

    @Override
    public boolean getTryWebSockets() {
        return _tryWebSockets;
    }

    @Override
    public long getDisconnectTimeout() {
        return _disconnectTimeout;
    }

    @Override
    public long getKeepAliveTimeout() {
        return _keepAliveTimeout;
    }

    @Override
    public long getConnectTimeout() {
        return _connectTimeout;
    }

    @Override
    public void readJson(final JsonReader reader) {
        reader.readBeginObject();

        while (reader.read()) {
            final String name = reader.getName();

            if (name.equalsIgnoreCase("Url")) {
                _relativeUrl = reader.readString();
            } else if (name.equalsIgnoreCase("ConnectionToken")) {
                _connectionToken = reader.readString();
            } else if (name.equalsIgnoreCase("ConnectionId")) {
                _connectionId = reader.readString();
            } else if (name.equalsIgnoreCase("ProtocolVersion")) {
                _protocolVersion = reader.readString();
            } else if (name.equalsIgnoreCase("TryWebSockets")) {
                _tryWebSockets = reader.readBoolean();
            } else if (name.equalsIgnoreCase("KeepAliveTimeout")) {
                _keepAliveTimeout = (long) (reader.readDouble() * 1000L);
            } else if (name.equalsIgnoreCase("DisconnectTimeout")) {
                _disconnectTimeout = (long) (reader.readDouble() * 1000L);
            } else if (name.equalsIgnoreCase("TransportConnectTimeout")) {
                _connectTimeout = (long) (reader.readDouble() * 1000L);
            } else {
                reader.skipValue();
            }
        }

        reader.readEndObject();
    }
}
api/src/main/java/net/signalr/client/transport/PingResponse.java
public final class PingResponse implements JsonReadable {

    /**
     * The response value.
     */
    private String _value;

    /**
     * Returns the response value.
     * 
     * @return The response value.
     */
    public String getValue() {
        return _value;
    }

    @Override
    public void readJson(final JsonReader reader) {
        reader.readBeginObject();

        while (reader.read()) {
            final String name = reader.getName();

            if (name.equalsIgnoreCase("Response")) {
                _value = reader.readString();
            } else {
                reader.skipValue();
            }
        }

        reader.readEndObject();
    }
}
api/src/main/java/net/signalr/client/transport/Transports.java
public final class Transports {

    /**
     * The long polling transport name.
     */
    public static final String LONG_POLLING = "longPolling";

    /**
     * The server sent events transport name.
     */
    public static final String SERVER_SENT_EVENTS = "serverSentEvents";

    /**
     * The web sockets transport name.
     */
    public static final String WEB_SOCKETS = "webSockets";

    /**
     * The HTTPS URI scheme.
     */
    private static final String HTTPS_SCHEME = "https";

    /**
     * The WS URI scheme.
     */
    private static final String WS_SCHEME = "ws";

    /**
     * The WSS URI scheme.
     */
    private static final String WSS_SCHEME = "wss";

    /**
     * The negotiate URI.
     */
    private static final String NEGOTIATE_URI = "negotiate";

    /**
     * The ping URI.
     */
    private static final String PING_URI = "ping";

    /**
     * The abort URI.
     */
    private static final String ABORT_URI = "abort";

    /**
     * The connect URI.
     */
    private static final String CONNECT_URI = "connect";

    /**
     * The reconnect URI.
     */
    private static final String RECONNECT_URI = "reconnect";

    /**
     * The connection data parameter name.
     */
    private static final String CONNECTION_DATA_PARAMETER = "connectionData";

    /**
     * The connection token parameter name.
     */
    private static final String CONNECTION_TOKEN_PARAMETER = "connectionToken";

    /**
     * The protocol version parameter name.
     */
    private static final String PROTOCOL_VERSION_PARAMETER = "clientProtocol";

    /**
     * The transport parameter name.
     */
    private static final String TRANSPORT_PARAMETER = "transport";

    /**
     * Initializes a new instance of the {@link Transports} class.
     */
    private Transports() {
    }

    /**
     * Builds the negotiate URI.
     * 
     * @param context The transport context.
     * @return The negotiate URI.
     */
    public static URI buildNegotiateUri(final TransportContext context) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null");
        }

        final URIBuilder uriBuilder = URIBuilder.resolve(context.getUrl(), NEGOTIATE_URI);
        final Map<String, Collection<String>> parameters = context.getParameters();

        uriBuilder.addParameters(parameters);
        uriBuilder.addParameter(CONNECTION_DATA_PARAMETER, context.getConnectionData());
        uriBuilder.addParameter(PROTOCOL_VERSION_PARAMETER, context.getProtocolVersion());

        return uriBuilder.build();
    }

    /**
     * Builds the ping URI.
     * 
     * @param context The transport context.
     * @return The ping URI.
     */
    public static URI buildPingUri(final TransportContext context) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null");
        }

        final URIBuilder uriBuilder = URIBuilder.resolve(context.getUrl(), PING_URI);
        final Map<String, Collection<String>> parameters = context.getParameters();

        uriBuilder.addParameters(parameters);
        uriBuilder.addParameter(CONNECTION_DATA_PARAMETER, context.getConnectionData());

        return uriBuilder.build();
    }

    /**
     * Builds the connect URI.
     * 
     * @param context The transport context.
     * @param transport The transport.
     * @param reconnect A value indicating whether to reconnect.
     * @return The connect URI.
     */
    public static URI buildConnectUri(final TransportContext context, final Transport transport, final boolean reconnect) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null");
        }
        if (transport == null) {
            throw new IllegalArgumentException("Transport must not be null");
        }

        final URIBuilder uriBuilder = URIBuilder.resolve(context.getUrl(), reconnect ? RECONNECT_URI : CONNECT_URI);
        final String transportName = transport.getName();

        if (WEB_SOCKETS.equals(transportName)) {
            final String scheme = uriBuilder.getScheme().equals(HTTPS_SCHEME) ? WSS_SCHEME : WS_SCHEME;

            uriBuilder.setScheme(scheme);
        }
        final Map<String, Collection<String>> parameters = context.getParameters();

        uriBuilder.addParameters(parameters);
        uriBuilder.addParameter(CONNECTION_DATA_PARAMETER, context.getConnectionData());
        uriBuilder.addParameter(CONNECTION_TOKEN_PARAMETER, context.getTransportOptions().getConnectionToken());
        uriBuilder.addParameter(TRANSPORT_PARAMETER, transport.getName());

        return uriBuilder.build();
    }

    /**
     * Builds the abort URI.
     * 
     * @param context The transport context.
     * @param transport The transport.
     * @return The abort URI.
     */
    public static URI buildAbortUri(final TransportContext context, final Transport transport) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null");
        }
        if (transport == null) {
            throw new IllegalArgumentException("Transport must not be null");
        }

        final URIBuilder uriBuilder = URIBuilder.resolve(context.getUrl(), ABORT_URI);
        final Map<String, Collection<String>> parameters = context.getParameters();

        uriBuilder.addParameters(parameters);
        uriBuilder.addParameter(CONNECTION_DATA_PARAMETER, context.getConnectionData());
        final TransportOptions options = context.getTransportOptions();

        uriBuilder.addParameter(CONNECTION_TOKEN_PARAMETER, options.getConnectionToken());
        uriBuilder.addParameter(TRANSPORT_PARAMETER, transport.getName());

        return uriBuilder.build();
    }
}
api/src/main/java/net/signalr/client/util/concurrent/promise/Promise.java
public interface Promise<T> {

    /**
     * Returns a value indicating whether the promise is complete.
     * 
     * @return A value indicating whether the promise is complete.
     */
    boolean isComplete();

    /**
     * Adds the specified completable.
     * 
     * @param completable The completable.
     */
    void then(Completable<? super T> completable);

    /**
     * Adds the specified continuation.
     * 
     * @param continuation The continuation.
     * @return The result.
     */
    <R> Promise<R> then(Continuation<? super T, ? extends R> continuation);
}
api/src/main/java/net/signalr/client/transport/Transport.java
public interface Transport extends Lifecycle<TransportContext> {

    /**
     * Returns the transport name.
     * 
     * @return the transport name.
     */
    String getName();

    /**
     * Negotiates the transport.
     * 
     * @param context The transport context.
     * @return The negotiation response.
     */
    Promise<NegotiationResponse> negotiate(TransportContext context);

    /**
     * Connects the transport.
     * 
     * @param context The transport context.
     * @param handler The channel handler.
     * @param reconnect A value indicating whether to reconnect.
     * @return The channel.
     */
    Promise<Channel> connect(TransportContext context, ChannelHandler handler, boolean reconnect);

    /**
     * Performs a transport ping.
     * 
     * @param context The transport context.
     * @return The ping response.
     */
    Promise<PingResponse> ping(TransportContext context);

    /**
     * Aborts the transport.
     * 
     * @param context The transport context.
     * @return The abort result.
     */
    Promise<Void> abort(TransportContext context);
}
api/src/main/java/net/signalr/client/util/AbstractLifecycle.java
public abstract class AbstractLifecycle<T> implements Lifecycle<T> {

    /**
     * The private logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLifecycle.class);

    /**
     * The lifecycle state.
     */
    private final AtomicReference<State> _state;

    /**
     * Initializes a new instance of the {@link AbstractLifecycle} class
     */
    protected AbstractLifecycle() {
        _state = new AtomicReference<>(State.STOPPED);
    }

    /**
     * Changes the lifecycle state to the specified new lifecycle state.
     * 
     * @param state The lifecycle state.
     * @param newState The new lifecycle state.
     */
    private void changeState(final State state, final State newState) {
        if (!tryChangeState(state, newState)) {
            throw new IllegalStateException("Failed to change lifecycle state");
        }
    }

    /**
     * Tries to change the lifecycle state to the specified new lifecycle state.
     * 
     * @param state The lifecycle state.
     * @param newState The new lifecycle state.
     * @return A value indicating whether the lifecycle state changed.
     */
    private boolean tryChangeState(final State state, final State newState) {
        if (!_state.compareAndSet(state, newState)) {
            return false;
        }

        LOGGER.debug("{} '{}'", newState, this);

        return true;
    }

    /**
     * Starts the lifecycle.
     * 
     * @param context The lifecycle context.
     */
    protected abstract void doStart(T context) throws Exception;

    /**
     * Stops the lifecycle.
     * 
     * @param context The lifecycle context.
     */
    protected abstract void doStop(T context) throws Exception;

    @Override
    public final boolean isRunning() {
        return _state.get().isRunning();
    }

    @Override
    public final void start(final T context) {
        if (!tryChangeState(State.STOPPED, State.STARTING)) {
            return;
        }

        try {
            doStart(context);
        } catch (final Throwable t) {
            changeState(State.STARTING, State.STOPPED);

            LOGGER.warn("Failed to start '{}'", this, t);

            throw new LifecycleException("Failed to start lifecycle", t);
        }

        changeState(State.STARTING, State.STARTED);
    }

    @Override
    public final void stop(final T context) {
        if (!tryChangeState(State.STARTED, State.STOPPING)) {
            return;
        }

        try {
            doStop(context);
        } catch (final Throwable t) {
            changeState(State.STOPPING, State.STOPPED);

            LOGGER.warn("Failed to stop '{}'", this, t);

            throw new LifecycleException("Failed to stop lifecycle", t);
        }

        changeState(State.STOPPING, State.STOPPED);
    }

    /**
     * Defines all lifecycle states.
     */
    private enum State {

        /**
         * Lifecycle is going to be started.
         */
        STARTING("Starting", true),

        /**
         * Lifecycle has been started.
         */
        STARTED("Started", true),

        /**
         * Lifecycle is going to be stopped.
         */
        STOPPING("Stopping", false),

        /**
         * Lifecycle has been stopped.
         */
        STOPPED("Stopped", false);

        /**
         * The state name.
         */
        private final String _name;

        /**
         * A value indicating whether the lifecycle is running.
         */
        private final boolean _running;

        /**
         * Initializes a new instance of the {@link State} class.
         * 
         * @param name The state name.
         * @param running A value indicating whether the lifecycle is running.
         */
        State(final String name, final boolean running) {
            if (name == null) {
                throw new IllegalArgumentException("Name must not be null");
            }

            _name = name;
            _running = running;
        }

        /**
         * Returns a value indicating whether the lifecycle is running.
         * 
         * @return A value indicating whether the lifecycle is running.
         */
        public boolean isRunning() {
            return _running;
        }

        @Override
        public String toString() {
            return _name;
        }
    }
}
api/src/main/java/net/signalr/client/util/concurrent/promise/Apply.java
public abstract class Apply<T, R> implements Continuation<T, R> {

    /**
     * Handles the apply continuation.
     * 
     * @param value The value.
     * @return The result.
     * @throws Exception
     */
    protected abstract R doApply(T value) throws Exception;

    /**
     * Handles the failure continuation.
     * 
     * @param cause The cause.
     * @throws Exception
     */
    protected void onFailure(final Throwable cause) throws Exception {
    }

    @Override
    public final void onSuccess(final T value, final Completable<? super R> result) throws Exception {
        final R newValue = doApply(value);

        result.setSuccess(newValue);
    }

    @Override
    public final void onFailure(final Throwable cause, final Completable<? super R> result) throws Exception {
        onFailure(cause);
        result.setFailure(cause);
    }
}
api/src/main/java/net/signalr/client/transport/TransportContext.java
public interface TransportContext {

    /**
     * Returns the protocol version.
     * 
     * @return The protocol version.
     */
    String getProtocolVersion();

    /**
     * Returns the URL.
     * 
     * @return The URL.
     */
    String getUrl();

    /**
     * Returns the executor.
     * 
     * @return The executor.
     */
    Executor getExecutor();

    /**
     * Returns the scheduler.
     * 
     * @return The scheduler.
     */
    Scheduler getScheduler();

    /**
     * Returns the mapper.
     * 
     * @return The mapper.
     */
    JsonMapper getMapper();

    /**
     * Returns the headers.
     * 
     * @return The headers.
     */
    Map<String, Collection<String>> getHeaders();

    /**
     * Returns the parameters.
     * 
     * @return The parameters.
     */
    Map<String, Collection<String>> getParameters();

    /**
     * Returns the connection data.
     * 
     * @return The connection data.
     */
    String getConnectionData();

    /**
     * Returns the transport options.
     * 
     * @return The transport options.
     */
    TransportOptions getTransportOptions();
}
api/src/main/java/net/signalr/client/json/JsonMapper.java
public interface JsonMapper {

    /**
     * Converts the specified text into an element.
     * 
     * @param text The text.
     * @return The element.
     */
    JsonElement toElement(String text);

    /**
     * Converts the specified text into an object.
     * 
     * @param text The text.
     * @param type The object type.
     * @return The object.
     */
    <T extends JsonReadable> T toObject(String text, Class<T> type);

    /**
     * Converts the specified object into a text.
     * 
     * @param object The object.
     * @return The text.
     */
    String toJson(JsonWriteable object);
}


Question: import net.signalr.client.transport.TransportContext;
import net.signalr.client.util.AbstractLifecycle;
import net.signalr.client.util.concurrent.promise.Apply;
import net.signalr.client.util.concurrent.promise.Promise;
import net.signalr.client.util.concurrent.promise.Promises;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;
import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
import com.ning.http.client.AsyncHttpClientConfig.Builder;
import net.signalr.client.json.JsonMapper;
import net.signalr.client.transport.NegotiationResponse;
import net.signalr.client.transport.PingResponse;
import net.signalr.client.transport.Transport;
import net.signalr.client.transport.Transports;
/*
 * Copyright © Martin Tamme
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.signalr.client.transport.asynchttpclient;




/**
 * Represents an abstract transport.
 */
public abstract class AbstractTransport extends AbstractLifecycle<TransportContext> implements Transport {

    /**
     * The user agent.
     */
    private static final String USER_AGENT = "SignalR-Client/0.1 (Java; AsyncHttpClient)";

    /**
     * The content length header name.
     */
    private static final String CONTENT_LENGTH_HEADER = "Content-Length";

    /**
     * The content type header name.
     */
    private static final String CONTENT_TYPE_HEADER = "Content-Type";

    /**
     * The HTTP provider class.
     */
    private final String _httpProviderClass;

    /**
     * The HTTP client.
     */
    private AsyncHttpClient _httpClient;

    /**
     * Initializes a new instance of the {@link AbstractTransport} class.
     * 
     * @param httpProviderClass The HTTP provider class.
     */
    protected AbstractTransport(final String httpProviderClass) {
        _httpProviderClass = httpProviderClass;
        _httpClient = null;
    }

    /**
     * Creates a new HTTP client.
     * 
     * @return The new HTTP client.
     */
    protected AsyncHttpClient newHttpClient() {
        final Builder builder = new Builder();

        builder.setAllowPoolingConnection(true);
        builder.setAllowSslConnectionPool(true);
        builder.setCompressionEnabled(true);
        builder.setUserAgent(USER_AGENT);

        if (_httpProviderClass != null) {
            return new AsyncHttpClient(_httpProviderClass, builder.build());
        }

        return new AsyncHttpClient(builder.build());
    }

    /**
     * Prepares a GET request.
     * 
     * @param uri The request URI.
     * @return The request builder.
     */
    protected final BoundRequestBuilder prepareGet(final URI uri) {
        if (uri == null) {
            throw new IllegalArgumentException("URI must not be null");
        }

        if (_httpClient == null) {
            throw new IllegalStateException("Transport has not been started");
        }

        return _httpClient.prepareGet(uri.toString());
    }

    /**
     * Prepares a POST request.
     * 
     * @param uri The request URI.
     * @return The request builder.
     */
    protected final BoundRequestBuilder preparePost(final URI uri) {
        if (uri == null) {
            throw new IllegalArgumentException("URI must not be null");
        }

        if (_httpClient == null) {
            throw new IllegalStateException("Transport has not been started");
        }

        return _httpClient.preparePost(uri.toString());
    }

    @Override
    public final Promise<NegotiationResponse> negotiate(final TransportContext context) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null");
        }

        // Setup request.
        final URI uri = Transports.buildNegotiateUri(context);
        final BoundRequestBuilder boundRequestBuilder = prepareGet(uri);
        final Map<String, Collection<String>> headers = context.getHeaders();

        boundRequestBuilder.setHeaders(headers);

        // Send request.
        final ResponseHandler handler = new ResponseHandler();

        try {
            boundRequestBuilder.execute(handler);
        } catch (final Throwable t) {
            return Promises.newFailure(t);
        }

        return handler.getResponse().then(new Apply<Response, NegotiationResponse>() {
            @Override
            protected NegotiationResponse doApply(final Response response) throws Exception {
                final JsonMapper mapper = context.getMapper();
                final String body = response.getResponseBody();

                return mapper.toObject(body, NegotiationResponse.class);
            }
        });
    }

    @Override
Answer:     public final Promise<PingResponse> ping(final TransportContext context) {