Network interceptors
From JPPF 6.3 Documentation
Latest revision as of 09:14, 18 October 2020
|
Main Page > Customizing JPPF > Network interceptors |
This extension allows user-defined code to intercept and make use of any JPPF-initiated network connection as soon as it is established. A typical use of this extension is so you can perform a custom authentication procedure before a connection is considered valid, but it is not limited to that.
The network interceptors are used with all possible types of network connections in JPPF: client to driver, node to driver, driver to driver, JMX connections and recovery connections (heart-beat mechanism) between nodes and drivers.
[edit] 1 Creating a network interceptor
To define a network interceptor, simply create a class with a no-arg constructor that implements the interface NetworkCommunicationInterceptor, which is defined like this:
public interface NetworkConnectionInterceptor { // The list of interceptors loaded via the Service Provider Interface List<NetworkConnectionInterceptor> INTERCEPTORS; // Called when a Socket is accepted by a ServerSocket // on the server side of a connection boolean onAccept(Socket acceptedSocket, JPPFChannelDescriptor descriptor); // Called when a SocketChannel is accepted by a ServerSocketChannel // on the server side of a connection boolean onAccept(SocketChannel acceptedChannel, JPPFChannelDescriptor descriptor); // Called when a Socket is connected on the client side of a connection boolean onConnect(Socket connectedSocket, JPPFChannelDescriptor descriptor); // Called when a SocketChannel is connected on the client side of a connection boolean onConnect(SocketChannel connectedChannel, JPPFChannelDescriptor descriptor); }
Note that there are 2 onAccept() and 2 onConnect() methods: one takes a Socket as input, the other takes a SocketChannel. SocketChannels are used by the JPPF driver for connections with nodes, clients or other servers. The JPPF driver makes sure they are configured in blocking mode before passing them to an interceptor.
In terms of semantics, onAccept() is called from the server side of a connection: the provided Socket or SocketChannel is obtained from a call to ServerSocket.accept() and ServerSocketChannel.accept(), respectively. On the other hand, onConnect() is called from the client side of a connection, that is, the side that initiates the connection.
In practice, Socket and SocketChannel use very different APIs for I/O reads and writes. However, when a SocketChannel is configured in blocking mode, it is possible to work directly with the underlying Socket via a prior call to SocketChannel.socket(). Consequently, you could write your interceptor like this:
public class MyInterceptor implements NetworkConnectionInterceptor { @Override public boolean onAccept(Socket acceptedSocket, JPPFChannelDescriptor descriptor) { // do the job ... return ...; } @Override public boolean onConnect(Socket connectedSocket, JPPFChannelDescriptor descriptor) { // do the job ... return ...; } @Override public boolean onAccept(SocketChannel acceptedChannel, JPPFChannelDescriptor descriptor) { return onAccept(acceptedChannel.socket()); } @Override public boolean onConnect(SocketChannel connectedChannel, JPPFChannelDescriptor descriptor) { return onConnect(connectedChannel.socket()); } }
The decriptor parameter is an element of the JPPFChannelDescriptor enumeration. Its possible values are UNKNOWN, PEER_HEARTBEAT_CHANNEL, CLIENT_HEARTBEAT_CHANNEL, NODE_HEARTBEAT_CHANNEL, JMX_REMOTE_CHANNEL, ACCEPTOR_CHANNEL, CLIENT_JOB_DATA_CHANNEL, CLIENT_CLASSLOADER_CHANNEL, NODE_JOB_DATA_CHANNEL and NODE_CLASSLOADER_CHANNEL. It provides additional information on the source of the call to onAccept() or onConnect().
Each value in this enum exposes the following interface :
public enum JPPFChannelDescriptor { // returns true if this is a class loader channel, false otherwise public boolean isClassloader() // returns true if this is a job data channel, false otherwise public boolean isJobData() // returns true if this is a heartbeat channel, false otherwise public boolean isHeartbeat() // returns true if this is a class loader channel, false otherwise public boolean isJmx() // returns true if this is a client-side channel, false otherwise public boolean isClient() // returns true if this is a node-side channel, false otherwise public boolean isNode() // returns true if this is a driver-side channel, false otherwise public boolean isDriver() }
To make life even easier, JPPF provides the class AbstractNetworkConnectionInterceptor, which implements the methods from NetworkCommunicationInterceptor as in the example above, and delegates the sream-based processing to two abstract methods. Our example could then be rewritten as:
public class MyInterceptor extends AbstractNetworkConnectionInterceptor { @Override public boolean onAccept(Socket acceptedSocket, JPPFChannelDescriptor descriptor) { // do the job ... return ...; } @Override public boolean onConnect(Socket connectedSocket, JPPFChannelDescriptor descriptor) { // do the job ... return ...; } }
[edit] 2 Plugging-in the interceptor
To diescover the interceptor, JPPF uses the Service Provider Interface (SPI):
- in your sources or resources folder, create a file org.jppf.comm.interceptor.NetworkConnectionInterceptor in the META-INF/services folder
- in this file, add the fully qualified name of your interceptor implementation class, e.g.: org.example.MyInterceptor
- make sure the file META-INF/services/org.jppf.comm.interceptor.NetworkConnectionInterceptor is then placed into the resulting jar file
- add the jar file to the classpath of all JPPF components in your grid topology
[edit] 3 Related sample
The Network Interceptor demo illustrates a simple encrypted authentication scheme implemented with an interceptor.
Main Page > Customizing JPPF > Network interceptors |