Class Channel

java.lang.Object
org.spiderwiz.core.Channel
All Implemented Interfaces:
Runnable, ZDispenser<String>

public abstract class Channel
extends Object
implements Runnable
A base class for classes that handle data communication.

Spiderwiz framework comes out of the box with implementations of predefined communication channels, such as TCP/IP sockets, WebSockets and disc files (the latter is helpful for offline debugging). The type of the channel to use is configured in the application's configuration file. These are all implemented by extending the Channel class.

In addition to the predefined channels users can write plugins with their own custom channel implementation. You develop a custom channel by extending Channel, and use your implementation class by assigning its fully qualified name to the class tag in the configuration file.

There are two ways to use this base class for implementing a custom channel. The first relies on its builtin mechanism of using InputStream and OutputStream. If you go this way all you have to do is provide your extensions to these classes in getInputStream() and getOutputStream(). The other way is to provide your own i/o access code. In this case you will need to override open(), readLine(), writeLine(), close() and maybe also flush().

In many cases channels are used in a client-server architecture, in which the client side of the channel applies for a connection to a configured server address, while the server accepts the requests and opens the server side of the channel. The Channel class should be used for both ends of the connection. At the client side, the channel is configured by configure() method that you must implement.

If you implement a custom Channel in client-server architecture, you shall also provide a custom implementation of ServerChannel that will accept requests from your client implementation and open its server side.

Here is a full example taken from Spiderwiz implementation of a TCP/IP socket:

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;
import org.spiderwiz.core.Channel;
import org.spiderwiz.core.Main;
import org.spiderwiz.zutils.ZUtilities;

public class TcpSocket extends Channel{
    private Socket socket = null;
    private String ip;
    private int port;

    @Override
    protected boolean configure(Map<String, String> initParams, int type, int n) {
        return init(
            initParams.get("ip"), ZUtilities.parseInt((initParams.get("port"))), type, n);
    }

    private boolean init(String ip, int port, int type, int n) {
        final String NO_TCP_PARAMS = "No IP address or port number specified for %1$s-%2$d";
        final String TYPES[] = {"import", "producer", "consumer"};
        if (ip == null || port == 0) {
            Main.getInstance().sendExceptionMail(null, String.format(NO_TCP_PARAMS, TYPES[type], n), null, true);
            return false;
        }
        this.ip = ip;
        this.port = port;
        return true;
    }

    @Override
    protected boolean open() throws UnknownHostException, IOException {
        socket = new Socket(InetAddress.getByName(ip).getHostAddress(), port);
        return true;
    }

    @Override
    protected void close() throws IOException {
        if (socket != null)
            socket.close();
        socket = null;
    }

    @Override
    protected InputStream getInputStream() throws IOException {
        return socket == null ? null : socket.getInputStream();
    }

    @Override
    protected OutputStream getOutputStream() throws IOException {
        return socket == null ? null : socket.getOutputStream();
    }

    @Override
    public String getRemoteAddress() {
        return ip + (port == 0 ? "" : ":" + getPort());
    }
 }
See Also:
ServerChannel
  • Constructor Details

    • Channel

      public Channel()
      Class constructor.
  • Method Details

    • configure

      protected abstract boolean configure​(Map<String,​String> configParams, int type, int n)
      Configures the channel.

      An abstract method that you must implement to configure a client channel from parameters specified by producer-n, consumer-n or import-n properties in the application's configuration file. The values of these properties must be a list of pairs key=value concatenated by a semicolon. For instance, let's say you implement a custom TCP/IP socket, then its configuration may look something like:

           class=com.mydomain.myplugins.MyTcpIp;ip=192.185.6.24;port=31415
           
      The framework will then instantiate your class com.mydomain.myplugins.MyTcpIp and call its configure() method with a map in configParams containing the following pairs (order not guarantee):
           class=com.mydomain.myplugins.MyTcpIp
           ip=192.185.6.24
           port=31415
           
      Parameters:
      configParams - a map of key=value configuration parameters.
      type - type of channel - 0: import, 1:producer, 2:consumer.
      n - the n value of import-n, producer-n or consumer-n.
      Returns:
      true if and only if configuration is successful.
    • getInputStream

      protected abstract InputStream getInputStream() throws Exception
      Returns the channel input stream.

      An abstract method you must implement to provide the input stream of the channel. If you do, this is the only method you need to implement for that purpose. Alternatively you can return null in this method and implement direct input by overriding readLine(), open() and close().

      Returns:
      the InputStream object used by your implementation, or null if you implement direct input.
      Throws:
      Exception
    • getOutputStream

      protected abstract OutputStream getOutputStream() throws Exception
      Returns the channel output stream.

      An abstract method you must implement to provide the output stream of the channel. If you do, this is the only method you need to implement for that purpose. Alternatively you can return null in this method and implement direct output by overriding writeLine(), open() and close().

      Returns:
      the OutputStream object used by your implementation, or null if you implement direct input.
      Throws:
      Exception
    • open

      protected boolean open() throws Exception
      Opens the channel for communication.

      Override this method to provide code for opening the channel, if needed. This method is called before getInputStream() and getOutputStream() are called. The default implementation of this method does nothing and returns true.

      See the example in the class description.

      Returns:
      true if and only if the operation is successful.
      Throws:
      Exception
    • readLine

      protected String readLine() throws IOException
      Reads a line of text.

      Override this method if and only if your implementation of getInputStream() returns null.

      Returns:
      the read line, not including any line-termination characters, or null if the end of the data has been reached.
      Throws:
      IOException
    • writeLine

      protected void writeLine​(String line)
      Writes a line of text.

      Override this method if and only if your implementation of getOutputStream() returns null.

      Parameters:
      line - the line to be written.
    • flush

      protected void flush()
      Flushes any remaining data in the output buffer.

      Override this method if and only if your implementation of getOutputStream() returns null.

    • close

      protected void close() throws Exception
      Closes this channel.

      Override this method to provide code for closing the channel, if needed. This method is called after the streams returned by getInputStream() getOutputStream() are closed. The default implementation of this method does nothing.

      See the example in the class description.

      Throws:
      Exception
    • getRemoteAddress

      public abstract String getRemoteAddress()
      Returns the address of the endpoint this channel is connected to, or null if it is unknown.

      This is an abstract method that you must implement to provide the endpoint address of this channel, for instance a remote IP address.

      See the example in the class description.

      Returns:
      an address representing the remote endpoint of this channel, or null if it is unknown.
    • isFileChannel

      public boolean isFileChannel()
      Returns true if this channel reads from a disk file.

      Override this method if this is a file channel. The default implementation returns false.

      Returns:
      true if any only if this is a file channel.
    • isCompressable

      protected boolean isCompressable()
      Returns true if ZIP algorithm shall be used to compress the data on this channel.

      This method is relevant only if you implement getInputStream() and getOutputStream() to return non-null values. If the method returns true data written to the output stream will be compressed before, and data read from the input stream will be decompressed after. The method has no effect if those methods return null.

      The default implementation returns true if any only if this is not a file channel. Override the method to provide a different implementation.

      Returns:
      true if any only if ZIP algorithm shall be used to compress the data on this channel.
    • dontReconnect

      protected boolean dontReconnect()
      Returns true if this is a client channel that shall not try to reconnect automatically if disconnected.

      If this method returns false, the framework will try repeatedly to reconnect the channel whenever it is disconnected. The method affects only client channels. If a server channel is disconnected, the server would just wait for the client to reconnect.

      By default the method returns the value returned by isFileChannel(). Override it to provide a different behavior.

      Returns:
      true if this is a client channel that shall not try to reconnect automatically if disconnected.
    • getKeepAliveInterval

      protected long getKeepAliveInterval()
      Returns the time interval that the framework writes an empty string to the channel to keep it alive.

      There are scenarios in which if no output is written to a communication channel for a certain amount of time the system treats the channel as idle and disconnects it. If your implementation involves this scenario, you can use this method to set the time interval that the framework checks whether the channel is idle and sends an empty string to it to keep it alive.

      The default value that the method returns is one minute. Override it to provide a different value in milliseconds, or zero if "keep alive" is not needed.

      Returns:
      the time interval in milliseconds that the framework will write an empty string to the channel to keep it alive, or zero if this is not needed.
    • keepAliveOnEndOfData

      protected boolean keepAliveOnEndOfData()
      Returns true if the channel should be considered connected after the end of data has been reached.

      In some cases there is a need to consider the channel connected even though reading from it has reached the end of data. For instance, if this is a file channel that uses one file for input and one file for output, even though the end of data from the input file has been reached the channel should stay connected so that output to the output file shall continue to be written.

      By default the method returns the value returned from isFileChannel(). Override it to provide a different behavior.

      Returns:
      true if and only if the channel should be considered connected after the end of data has been reached.
    • disconnect

      public final void disconnect​(String reason)
      Disconnects the channel.

      Call this method if your implementation detects a disconnection state that cannot be detected by the framework. The reason parameter should be a free textual description of the reason that caused the disconnection, used for logging purposes. A null value will yield the generic text "Channel closed by other peer".

      Parameters:
      reason - a textual description of the reason that caused the disconnection.
    • onError

      protected final void onError​(Throwable ex)
      Reports an exception.

      Call this method if your implementation catches an exception that could not be caught by the framework.

      Parameters:
      ex - the exception object caught by your implementation.
    • run

      public final void run()
      Implementation of Runnable.run().

      Used internally by the framework. Do not call or override.

      Specified by:
      run in interface Runnable
    • dispense

      public final void dispense​(String line, boolean flush)
      Implementation of ZDispenser.dispense().

      Used internally by the framework. Do not call or override.

      Specified by:
      dispense in interface ZDispenser<String>
      Parameters:
      line - Line to dispense.
      flush - Flush buffered data if set to true.
    • handleException

      public void handleException​(Exception ex)
      Description copied from interface: ZDispenser
      Handles an exception occurred while processing the associated ZBuffer object.

      Implement this method to handle exceptions in the associated buffer.

      Specified by:
      handleException in interface ZDispenser<T>
      Parameters:
      ex - the exception.