/* * 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.server.impl; 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.connection.TransportLayer; import com.noctarius.tengi.core.exception.NoSuchConnectionException; import com.noctarius.tengi.core.listener.ConnectedListener; import com.noctarius.tengi.core.model.Identifier; import com.noctarius.tengi.core.model.Message; import com.noctarius.tengi.server.impl.transport.negotiation.GZipNegotiator; import com.noctarius.tengi.server.impl.transport.negotiation.SSLNegotiator; import com.noctarius.tengi.server.impl.transport.negotiation.SnappyNegotiator; import com.noctarius.tengi.server.spi.negotiation.NegotiableTransport; import com.noctarius.tengi.server.spi.negotiation.Negotiator; import com.noctarius.tengi.spi.connection.ConnectionContext; import com.noctarius.tengi.spi.connection.packets.PollingRequest; import com.noctarius.tengi.spi.serialization.Serializer; import io.netty.channel.Channel; import io.netty.handler.ssl.SslContext; import io.netty.util.internal.ConcurrentSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import java.util.stream.Stream; public class ConnectionManager implements Service { private final Set<ConnectedListener> connectedListeners = new ConcurrentSet<>(); private final Map<Identifier, ClientConnection> connections = new ConcurrentHashMap<>(); private final Configuration configuration; private final SslContext sslContext; private final Serializer serializer; private final HandshakeHandler handshakeHandler; private final Transport[] negotiatableTransports; public ConnectionManager(Configuration configuration, SslContext sslContext, // Serializer serializer, HandshakeHandler handshakeHandler) { this.configuration = configuration; this.sslContext = sslContext; this.serializer = serializer; this.handshakeHandler = handshakeHandler; this.negotiatableTransports = buildNegotiableTransports(configuration); } @Override public void start() { } @Override public void stop() { } public boolean acceptTransport(Transport transport, int port) { for (Transport candidate : configuration.getTransports()) { if (candidate.equals(transport) && port == configuration.getTransportPort(transport)) { return true; } } return false; } public SslContext getSslContext() { return sslContext; } public void registerConnectedListener(ConnectedListener connectedListener) { connectedListeners.add(connectedListener); } public HandshakeHandler getHandshakeHandler() { return handshakeHandler; } public Connection assignConnection(Identifier connectionId, ConnectionContext connectionContext, Transport transport) { Connection connection = connections.computeIfAbsent(connectionId, (key) -> new ClientConnection(connectionContext, connectionId, transport, serializer)); connectedListeners.forEach((listener) -> listener.onConnection(connection)); return connection; } public void publishMessage(Channel channel, Identifier connectionId, Message message) { ClientConnection connection = connections.get(connectionId); if (connection == null) { throw new NoSuchConnectionException("ConnectionId '" + connectionId.toString() + "' is not registered"); } if (!connection.getTransport().isStreaming() && message.getBody() instanceof PollingRequest) { PollingRequest request = message.getBody(); connection.getConnectionContext().processPollingRequest(channel, connection, request); } else { connection.publishMessage(message); } } public void exceptionally(Identifier connectionId, Throwable throwable) { ClientConnection connection = connections.get(connectionId); if (connection != null) { connection.notifyException(throwable); } } public Negotiator[] findNegotiators(TransportLayer transportLayer, int port) { NegotiableTransport[] transports = Stream.of(negotiatableTransports)// .filter(transport -> transport.getTransportLayer() == transportLayer) // .filter(transport -> configuration.getTransportPort(transport) == port) // .filter(transport -> transport instanceof NegotiableTransport) // .map(transport -> (NegotiableTransport) transport) .filter(transport -> transport.getNegotiator() != null) .toArray(NegotiableTransport[]::new); Stream<Negotiator> protocolNegotiators = Stream.of(transports).map(this::extractNegotiator); Stream<Negotiator> additionalNegotiators = additionalNegotiators(transports); return Stream.concat(protocolNegotiators, additionalNegotiators).toArray(Negotiator[]::new); } private Negotiator extractNegotiator(NegotiableTransport transport) { return transport.getNegotiator(); } private Stream additionalNegotiators(NegotiableTransport[] transports) { boolean snappyEnabled = configuration.isSnappyEnabled(); boolean gzipEnabled = configuration.isGzipEnabled(); boolean sslNecessary = configuration.isSslEnabled() && // Stream.of(transports).anyMatch(transport -> transport.getTransportLayer().sslCapable()); Stream.Builder<Negotiator> builder = Stream.builder(); if (sslNecessary) { builder.add(SSLNegotiator.INSTANCE); } if (gzipEnabled) { builder.add(GZipNegotiator.INSTANCE); } if (snappyEnabled) { builder.add(SnappyNegotiator.INSTANCE); } return builder.build(); } private Transport[] buildNegotiableTransports(Configuration configuration) { List<Transport> transports = configuration.getTransports().stream() // .filter(transport -> transport instanceof NegotiableTransport) // .filter(negotiator -> negotiator != null) // .collect(Collectors.toList()); return transports.stream().toArray(Transport[]::new); } }