/*
* Copyright (c) 2015-2016, Christoph Engelbert (aka noctarius) and
* contributors. All rights reserved.
*
* 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 com.noctarius.tengi.client;
import com.noctarius.tengi.client.impl.ConnectCallback;
import com.noctarius.tengi.client.impl.Connector;
import com.noctarius.tengi.client.impl.ConnectorFactory;
import com.noctarius.tengi.client.impl.TransportHandler;
import com.noctarius.tengi.client.impl.config.ClientConfiguration;
import com.noctarius.tengi.core.config.Configuration;
import com.noctarius.tengi.core.connection.Connection;
import com.noctarius.tengi.core.connection.HandshakeHandler;
import com.noctarius.tengi.core.connection.Transport;
import com.noctarius.tengi.core.exception.ConnectionFailedException;
import com.noctarius.tengi.spi.serialization.Serializer;
import io.netty.channel.EventLoopGroup;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.concurrent.CompletableFuture;
class ConnectorContext {
private final Configuration configuration;
private final Serializer serializer;
private final TransportHandler transportHandler;
private final HandshakeHandler handshakeHandler;
private final EventLoopGroup clientGroup;
ConnectorContext(Configuration configuration, Serializer serializer, EventLoopGroup clientGroup) {
this.configuration = configuration;
this.serializer = serializer;
this.clientGroup = clientGroup;
this.transportHandler = getConnectorHandler(configuration);
this.handshakeHandler = createHandshakeHandler(configuration);
}
CompletableFuture<Connection> connect(InetAddress address) {
CompletableFuture<Connection> future = new CompletableFuture<>();
ConnectCallback connectCallback = connectFuture(future);
Iterator<Transport> transportIterator = configuration.getTransports().iterator();
connect0(address, connectCallback, transportIterator);
return future;
}
private void connect0(InetAddress address, ConnectCallback connectCallback, Iterator<Transport> transportIterator) {
if (!transportIterator.hasNext()) {
// Reached end of configured transports
connectCallback.on(new ConnectionFailedException("No transport was able to connect"));
return;
}
Transport transport = transportIterator.next();
int port = configuration.getTransportPort(transport);
// Safe cast do to check in ClientImpl
Connector connector = ((ConnectorFactory) transport).create(address, port, serializer, handshakeHandler, clientGroup);
// Start connection try
connector.connect(connectCallback(connector, address, connectCallback, transportIterator));
}
private HandshakeHandler createHandshakeHandler(Configuration configuration) {
HandshakeHandler handshakeHandler = configuration.getHandshakeHandler();
if (handshakeHandler == null) {
handshakeHandler = (connectionId, handshake) -> null;
}
return handshakeHandler;
}
private TransportHandler getConnectorHandler(Configuration configuration) {
if (configuration instanceof ClientConfiguration) {
return ((ClientConfiguration) configuration).getTransportHandler();
}
return null;
}
private ConnectCallback connectCallback(Connector connector, InetAddress address, ConnectCallback connectCallback,
Iterator<Transport> transportIterator) {
return (connection, throwable) -> {
if (transportHandler != null) {
transportHandler.onConnector(connector, throwable == null && connection != null, throwable);
}
if (throwable != null || connection == null) {
connect0(address, connectCallback, transportIterator);
} else {
connectCallback.on(connection);
}
};
}
private ConnectCallback connectFuture(CompletableFuture<Connection> future) {
return (connection, throwable) -> {
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
future.complete(connection);
}
};
}
}