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 in PETEP proxy has to handle both connection between client and proxy and between proxy and server.
*/
@PetepAPI
public interface Connection {
/**
* Obtains connection code
* @return Connection code
*/
String getCode();
/**
* Obtains connection parent proxy
* @return Parent proxy
*/
Proxy getProxy();
/**
* Sends PDU outside PETEP
* @param pdu PDU to be sent
*/
void send(PDU 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
*/
boolean supports(PDU pdu);
/**
* Converts connection to displayable string (e.g. in JavaFX components)
* @return Displayable description of connection
*/
String toString();
/**
* 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
*/
boolean start();
/**
* Stops connection.
*/
void stop();
}
/**
* ConnectionBase is an abstract implementation of Connection, which uses two queues for outgoing data
* and implements most of the required methods.
*/
@PetepAPI
public abstract class ConnectionBase implements 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 ConnectionBase(String code, Proxy proxy) {
this.code = code;
this.proxy = proxy;
this.queueC2S = new PduQueue();
this.queueS2C = new PduQueue();
}
@Override
public String getCode() {
return code;
}
@Override
public Proxy getProxy() {
return proxy;
}
@Override
public void send(PDU pdu) {
if (pdu.getDestination() == PduDestination.SERVER) {
queueC2S.add(pdu);
} else {
queueS2C.add(pdu);
}
}
@Override
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
*/
@Override
public abstract boolean start();
/**
* Stops connection.
*/
@Override
public abstract void stop();
/**
* Processes PDU in PETEP
* <p>Puts PDU into internal PETEP processing, which consists of various configured interceptors.</p>
* @param pdu PDU to be processed
*/
protected void process(PDU pdu) {
proxy.getHelper().processPdu(pdu);
}
}
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.
/**
* Interface of connection manager that is used by internal or external modules to work with connections of a given proxy.
* <p>
* All methods should call appropriate listener methods through PetepHelper,
* for example, when adding connection - onConnectionStart should be called.
* </p>
*/
@PetepAPI
public interface ConnectionManager {
/**
* Obtains connection by code.
* @param code Code of connection to be obtained
* @return Connection or empty optional, if no connection exist with specified code
*/
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
*/
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)
*/
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
*/
Optional<Connection> remove(String code);
/**
* Get list of connections.
* @return List of connections
*/
List<Connection> getList();
/**
* Stops all connections.
*/
void stop();
}
Integer Based Connection Manager class is build on top of concurrent hashmap that uses integer identifier as the key.
/**
* 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 implements ConnectionManager {
/**
* PETEP helper for currently running core.
*/
protected final PetepHelper helper;
/**
* Connection listener for reporting new connections etc.
*/
protected final ConnectionListener listener;
/**
* 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) {
this.helper = helper;
listener = helper.getConnectionListener();
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 is build on top of concurrent hashmap that uses string identifier as the key.
/**
* 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 implements ConnectionManager {
/**
* PETEP helper for currently running core.
*/
protected final PetepHelper helper;
/**
* Connection listener for reporting new connections etc.
*/
protected final ConnectionListener listener;
/**
* 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) {
this.helper = helper;
listener = helper.getConnectionListener();
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);
}
}
Warning: Listeners are persisted using weak references, so you have to keep the strong reference yourself (avoid lambdas).
/**
* 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) {}
}