Development Guide

Connection

Connection is created in proxies and its purpose is to handle connection between client ⇄ proxy ⇄ server. It reads data from client/server and sends it to PETEP using process method and writes data to client/server from queues.

In order to manage connections, you have to implement ConnectionManager or use the one of default connection manager classes.

Connection class

com.warxim.petep.core.connection.Connection
/**
 * Connection in PETEP proxy has to handle both connection between client and proxy and between
 * proxy and server. Contains two outgoing queues that contain PDUs that should be sent in a given
 * direction (C2S / S2C).
 */
@PetepAPI
public abstract class Connection {
    /**
     * Unique identifier of the connection.
     */
    protected final String code;

    /**
     * Parent proxy.
     */
    protected final Proxy proxy;

    /**
     * Outgoing queue in direction C2S (client -> server).
     */
    protected final PduQueue queueC2S;

    /**
     * Outgoing queue in direction S2C (client <- server).
     */
    protected final PduQueue queueS2C;

    /**
     * Constructs connection.
     * @param code Unique code of the connection
     * @param proxy Proxy to which the connection belongs
     */
    protected Connection(String code, Proxy proxy) {
        this.code = code;
        this.proxy = proxy;
        this.queueC2S = new PduQueue();
        this.queueS2C = new PduQueue();
    }

    public String getCode() {
        return code;
    }

    public Proxy getProxy() {
        return proxy;
    }

    /**
     * Sends PDU outside the PETEP.
     * <p>Adds the PDU into outgoing queue in direction depending on PDU.destination.</p>
     * @param pdu PDU to be sent
     */
    public final void send(PDU pdu) {
        if (pdu.getDestination() == PduDestination.SERVER) {
            queueC2S.add(pdu);
        } else {
            queueS2C.add(pdu);
        }
    }

    /**
     * Sends PDU outside the PETEP in direction C2S (client -&gt; server).
     * <p>Adds the PDU into outgoing queue in direction client -&gt; server.</p>
     * @param pdu PDU to be sent
     */
    public final void sendC2S(PDU pdu) {
        queueC2S.add(pdu);
    }

    /**
     * Sends PDU outside the PETEP in direction S2C (client &lt;- server).
     * <p>Adds the PDU into outgoing queue in direction server -&gt; client.</p>
     * @param pdu PDU to be sent
     */
    public final void sendS2C(PDU pdu) {
        queueS2C.add(pdu);
    }

    /**
     * Processes PDU in PETEP core.
     * <p>Puts PDU into internal PETEP processing, which consists of various cofnigured interceptors.</p>
     * @param pdu PDU to be processed
     */
    protected final void process(PDU pdu) {
        proxy.getHelper().processPdu(pdu);
    }

    /**
     * Checks whether the PDU is supported by this connection.
     * @param pdu PDU to be checked
     * @return {@code true} if the connection supports the specified pdu
     */
    public boolean supports(PDU pdu) {
        return proxy.supports(pdu);
    }

    /**
     * About connection.
     */
    @Override
    public String toString() {
        return "Connection '" + code + '\'';
    }

    /**
     * Starts connection.
     * <p>
     *     <b>Warning:</b> this method should return ASAP - it should be used to create threads and then return immediately.
     * </p>
     * @return {@code true} if the start was successful
     */
    public abstract boolean start();

    /**
     * Stops connection.
     */
    public abstract void stop();
}

Connection Manager class

You do not have to create your own PDU class and you can use or extend DefaultPdu class that builds PDU on top of byte arrays.

com.warxim.petep.core.connection.ConnectionManager
/**
 * Abstract class of connection manager that could be used by internal or external modules to work with connections of a given proxy.
 * <p>
 *     All methods should call appropriate listener methods, for example, when adding connection - onConnectionStart should be called.
 * </p>
 */
@PetepAPI
public abstract class ConnectionManager {
    /**
     * PETEP helper for currently running core.
     */
    protected final PetepHelper helper;

    /**
     * Connection listener for reporting new connections etc.
     */
    protected final ConnectionListener listener;

    /**
     * Constructs connection manager.
     * @param helper PETEP helper for currently running core
     */
    protected ConnectionManager(PetepHelper helper) {
        this.helper = helper;
        this.listener = helper.getConnectionListener();
    }

    /**
     * Obtains connection by code.
     * @param code Code of connection to be obtained
     * @return Connection or empty optional, if no connection exist with specified code
     */
    public abstract Optional<Connection> get(String code);

    /**
     * Adds connection to the connection manager.
     * @param connection Connection to be added
     * @return {@code true} if the connection was successfully added
     */
    public abstract boolean add(Connection connection);

    /**
     * Removes connection from the connection manager.
     * @param connection Connection to be removed
     * @return {@code true} if the connection was successfully removed (if it was present in the manager)
     */
    public abstract boolean remove(Connection connection);

    /**
     * Removes connection from the connection manager.
     * @param code Code of connection to be removed
     * @return Connection or empty optional, if no connection existed with specified code
     */
    public abstract Optional<Connection> remove(String code);

    /**
     * Get list of connections.
     * @return List of connections
     */
    public abstract List<Connection> getList();

    /**
     * Stops all connections.
     */
    public abstract void stop();
}

Integer Based Connection Manager class

Integer Based Connection Manager class is build on top of concurrent hashmap that uses integer identifier as the key.

com.warxim.petep.core.connection.IntegerBasedConnectionManager
/**
 * Default connection manager that uses ConcurrentHashMap for connection storage and Integer as connection code.
 * <p>
 *     Does not call start/stop on connection, but calls listener onStart/onStop, when connection is added/removed.
 * </p>
 */
@PetepAPI
public class IntegerBasedConnectionManager extends ConnectionManager {
    /**
     * Map of connections.
     */
    protected final ConcurrentHashMap<Integer, Connection> connections;

    /**
     * ID of last connection.
     */
    private final AtomicInteger lastId;

    /**
     * Constructs connection manager based on integer codes.
     * @param helper PETEP helper for currently running core
     */
    public IntegerBasedConnectionManager(PetepHelper helper) {
        super(helper);
        connections = new ConcurrentHashMap<>();
        lastId = new AtomicInteger(0);
    }

    @Override
    public Optional<Connection> get(String code) {
        return Optional.ofNullable(connections.get(Integer.parseInt(code)));
    }

    /**
     * {@inheritDoc}
     * <p>
     *  <b>Note:</b> Does not start the connection, but reports it as started to listener.
     * </p>
     * @param connection Connection to be added
     * @return {@code true} if the connection was successfully added
     */
    @Override
    public boolean add(Connection connection) {
        if (connections.putIfAbsent(Integer.parseInt(connection.getCode()), connection) == null) {
            listener.onConnectionStart(connection);
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * <p>
     *  <b>Note:</b> Does not stop the connection, but reports it as stopped to listener.
     * </p>
     * @param connection Connection to be removed
     * @return {@code true} if the connection was successfully removed (if it was present in the manager)
     */
    @Override
    public boolean remove(Connection connection) {
        if (connections.remove(Integer.parseInt(connection.getCode()), connection)) {
            listener.onConnectionStop(connection);
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * <p>
     *  <b>Note:</b> Does not stop the connection, but reports it as stopped to listener.
     * </p>
     * @param code Code of connection to be removed (automatically converted to integer)
     * @return Connection or empty optional, if no connection existed with specified code
     */
    @Override
    public Optional<Connection> remove(String code) {
        var connection = connections.remove(Integer.parseInt(code));
        if (connection != null) {
            listener.onConnectionStop(connection);
        }
        return Optional.ofNullable(connection);
    }

    @Override
    public List<Connection> getList() {
        return new ArrayList<>(connections.values());
    }

    @Override
    public void stop() {
        connections.values().parallelStream().forEach(Connection::stop);
    }

    /**
     * Generates new id for connection.
     * @return (Last ID + 1) converted to string.
     */
    public String nextCode() {
        return String.valueOf(lastId.incrementAndGet());
    }
}

String Based Connection Manager class

String Based Connection Manager class is build on top of concurrent hashmap that uses string identifier as the key.

com.warxim.petep.core.connection.StringBasedConnectionManager
/**
 * Default connection manager that uses ConcurrentHashMap for connection storage and any String as connection code.
 * <p>
 *     Does not call start/stop on connection, but calls listener onStart/onStop, when connection is added/removed.
 * </p>
 */
@PetepAPI
public class StringBasedConnectionManager extends ConnectionManager {
    /**
     * Map of connections.
     */
    protected final ConcurrentHashMap<String, Connection> connections;

    /**
     * Constructs connection manager based on string codes.
     * @param helper PETEP helper for currently running core
     */
    public StringBasedConnectionManager(PetepHelper helper) {
        super(helper);
        connections = new ConcurrentHashMap<>();
    }

    @Override
    public Optional<Connection> get(String code) {
        return Optional.ofNullable(connections.get(code));
    }

    /**
     * {@inheritDoc}
     * <p>
     *  <b>Note:</b> Does not start the connection, but reports it as started to listener.
     * </p>
     * @param connection Connection to be added
     * @return {@code true} if the connection was successfully added
     */
    @Override
    public boolean add(Connection connection) {
        if (connections.putIfAbsent(connection.getCode(), connection) == null) {
            listener.onConnectionStart(connection);
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * <p>
     *  <b>Note:</b> Does not stop the connection, but reports it as stopped to listener.
     * </p>
     * @param connection Connection to be removed
     * @return {@code true} if the connection was successfully removed (if it was present in the manager)
     */
    @Override
    public boolean remove(Connection connection) {
        if (connections.remove(connection.getCode(), connection)) {
            listener.onConnectionStop(connection);
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * <p>
     *  <b>Note:</b> Does not stop the connection, but reports it as stopped to listener.
     * </p>
     * @param code Code of connection to be removed
     * @return Connection or empty optional, if no connection existed with specified code
     */
    @Override
    public Optional<Connection> remove(String code) {
        var connection = connections.remove(code);
        if (connection != null) {
            listener.onConnectionStop(connection);
        }
        return Optional.ofNullable(connection);
    }

    @Override
    public List<Connection> getList() {
        return new ArrayList<>(connections.values());
    }

    @Override
    public void stop() {
        connections.values().parallelStream().forEach(Connection::stop);
    }

}

Connection listener

Warning: Listeners are persisted using weak references, so you have to keep the strong reference yourself (avoid lambdas).

com.warxim.petep.core.listener.ConnectionListener
/**
 * ConnectionListener allows code to listen for connection start/stop events.
 * <p>
 *     All event handlers should be called from ConnectionManager.
 * </p>
 */
@PetepAPI
public interface ConnectionListener {
    /**
     * Event for connection start.
     * @param connection Connection that started
     */
    default void onConnectionStart(Connection connection) {}

    /**
     * Event for connection stop.
     * @param connection Connection that stopped
     */
    default void onConnectionStop(Connection connection) {}
}