All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.network.connection.EndPointClient Maven / Gradle / Ivy

/*
 * Copyright 2010 dorkbox, llc
 *
 * 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 dorkbox.network.connection;

import dorkbox.network.Client;
import dorkbox.network.Configuration;
import dorkbox.network.connection.bridge.ConnectionBridge;
import dorkbox.util.exceptions.InitializationException;
import dorkbox.util.exceptions.SecurityException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import org.slf4j.Logger;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * This serves the purpose of making sure that specific methods are not available to the end user.
 */
public
class EndPointClient extends EndPoint implements Runnable {

    protected C connection;

    protected final Object registrationLock = new Object();
    protected final AtomicInteger connectingBootstrap = new AtomicInteger(0);
    protected List bootstraps = new LinkedList();
    protected volatile int connectionTimeout = 5000; // default
    protected volatile boolean registrationComplete = false;

    private volatile ConnectionBridge connectionBridgeFlushAlways;


    public
    EndPointClient(Configuration options) throws InitializationException, SecurityException, IOException {
        super(Client.class, options);
    }

    protected
    void registerNextProtocol() {
        this.registrationComplete = false; // always reset.

        new Thread(this, "Bootstrap registration").start();
    }

    @SuppressWarnings("AutoBoxing")
    @Override
    public
    void run() {
        synchronized (this.connectingBootstrap) {
            int bootstrapToRegister = this.connectingBootstrap.getAndIncrement();

            BootstrapWrapper bootstrapWrapper = this.bootstraps.get(bootstrapToRegister);

            ChannelFuture future;

            if (this.connectionTimeout != 0) {
                // must be before connect
                bootstrapWrapper.bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.connectionTimeout);
            }

            Logger logger2 = this.logger;
            try {
                // UDP : When this is CONNECT, a udp socket will ONLY accept UDP traffic from the remote address (ip/port combo).
                //       If the reply isn't from the correct port, then the other end will receive a "Port Unreachable" exception.

                future = bootstrapWrapper.bootstrap.connect();
                future.await();
            } catch (Exception e) {
                String errorMessage = stopWithErrorMessage(logger2,
                                                           "Could not connect to the " + bootstrapWrapper.type + " server on port: " +
                                                           bootstrapWrapper.port,
                                                           e);
                throw new IllegalArgumentException(errorMessage);
            }

            if (!future.isSuccess()) {
                String errorMessage = stopWithErrorMessage(logger2,
                                                           "Could not connect to the " + bootstrapWrapper.type + " server on port: " +
                                                           bootstrapWrapper.port,
                                                           future.cause());
                throw new IllegalArgumentException(errorMessage);
            }

            if (logger2.isTraceEnabled()) {
                logger2.trace("Waiting for registration from server.");
            }
            manageForShutdown(future);
        }
    }

    /**
     * Internal call by the pipeline to notify the client to continue registering the different session protocols.
     *
     * @return true if we are done registering bootstraps
     */
    @Override
    protected
    boolean registerNextProtocol0() {
        synchronized (this.connectingBootstrap) {
            this.registrationComplete = this.connectingBootstrap.get() == this.bootstraps.size();
            if (!this.registrationComplete) {
                registerNextProtocol();
            }
        }

        Logger logger2 = this.logger;
        if (logger2.isTraceEnabled()) {
            logger2.trace("Registered protocol from server.");
        }

        // only let us continue with connections (this starts up the client/server implementations) once ALL of the
        // bootstraps have connected
        return this.registrationComplete;
    }

    /**
     * Internal (to the networking stack) to notify the client that registration has COMPLETED. This is necessary because the client
     * will BLOCK until it has successfully registered it's connections.
     */
    @Override
    final
    void connectionConnected0(final ConnectionImpl connection) {
        // invokes the listener.connection() method, and initialize the connection channels with whatever extra info they might need.
        super.connectionConnected0(connection);

        this.connectionBridgeFlushAlways = new ConnectionBridge() {
            @Override
            public
            void self(Object message) {
                connection.self(message);
                flush();
            }

            @Override
            public
            ConnectionPoint TCP(Object message) {
                ConnectionPoint tcp = connection.TCP_backpressure(message);
                tcp.flush();
                return tcp;
            }

            @Override
            public
            ConnectionPoint UDP(Object message) {
                ConnectionPoint udp = connection.UDP_backpressure(message);
                udp.flush();
                return udp;
            }

            @Override
            public
            ConnectionPoint UDT(Object message) {
                ConnectionPoint udt = connection.UDT_backpressure(message);
                udt.flush();
                return udt;
            }

            @Override
            public
            Ping ping() {
                Ping ping = connection.ping();
                flush();
                return ping;
            }

            @Override
            public
            void flush() {
                connection.flush();
            }
        };


        // notify the registration we are done!
        synchronized (this.registrationLock) {
            this.registrationLock.notify();
        }
    }

    /**
     * Expose methods to send objects to a destination.
     * 

* This returns a bridge that will flush after EVERY send! This is because sending data can occur on the client, outside * of the normal eventloop patterns, and it is confusing to the user to have to manually flush the channel each time. */ @Override public ConnectionBridge send() { return this.connectionBridgeFlushAlways; } /** * Closes all connections ONLY (keeps the client running). To STOP the client, use stop(). *

* This is used, for example, when reconnecting to a server. */ @Override public void closeConnections() { super.closeConnections(); // for the CLIENT only, we clear these connections! (the server only clears them on shutdown) shutdownChannels(); // make sure we're not waiting on registration registrationComplete = true; synchronized (this.registrationLock) { this.registrationLock.notify(); } registrationComplete = false; } /** * Internal call to abort registration if the shutdown command is issued during channel registration. */ void abortRegistration() { synchronized (this.registrationLock) { this.registrationLock.notify(); } stop(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy