This is the specification for Isode's MHFCM (M-Switch HF Circuit Management) java class library.

The goal of the MHFCM API is to enable management of circuits. Typical circuits:

  1. ACP 127 Circuit -> Serial Hub -> Crypto -> Modem -> HF Radio
  2. ACP 142 Channel -> STANAG 5066 Server -> Serial Hub -> Crypto -> Modem -> HF Radio

The MHFCM API will be used by configuration applications that control and build such circuits. Circuits of this nature will typically be needed in environments where there is insufficient hardware to maintain static configurations for all of the circuits needed.

The endpoint of a circuit will be either an ACP 127 Circuit or and ACP 142 Channel. OTAM (Off The Air Management) is associated with broadcast sending circuits and will also connect through a serial interface. These endpoints are configured in M-Switch (and associated Isode products) using Isode configuration management tools (primarily MConsole). The MHFCM API is a client API that accesses the Isode servers (M-Switch, M-Vault, Icon 5066) through client/server management protocols and provides a simple management interface for an application controlling the full circuits. MHFCM API provides two broad function:

  1. It allows configuration of the Serial Interface (typically a Serial Hub connection) from the Isode elements, so that the Isode circuit endpoints can be connected to other elements of the circuit using a serial interface. Some other parameters can also be configured.
  2. It allows the circuit end points (ACP 127 Circuits and ACP 142 channels) to be enabled or disabled. This allows the application using MHFCM API to control when messages are sent over circuits, so that traffic will only be sent when circuits are set up and correctly configured (e.g., HF Frequency selected).

The MHFCM class library consists of a set of java classes which provide functionality to allow an application to connect to a configured M-Switch to obtain the configuration, make selected changes to that configuration and control operations in the context of that configuration.

The MHFCM class library connects to the Isode servers as follows:

  1. M-Vault using LDAP
  2. M-Switch QMGR using SOM (Isode Switch Operational Management protocol).
  3. ACP127 channels using SOM
  4. ACP142 channels using SOM
  5. M-Switch OTAM using SOM
  6. Icon 5066 using HTTP

The MHFCM API provides a single integrated API that handles all of these protocols and connections. The MHFCM API will connect to these servers when appropriate. Connection to each server may be configured independently (as parameters to the MHFCM API), but generally each server will run on the same host on default ports and will all use the same authentication credentials.

Callers use an AuthenticationDetails object to specify credentials. In an environment where one set of credentials is common to all servers, then it is sufficient only to provide the one set of AuthenticationDetails credentials for the Queue Manager (the API will use these credentials for all servers). In an environment where different credentials are used for different servers, a calling program may either specify separate AuthenticationDetails objects when initialising the MHFCM API, or may use the AuthDetailsProvider to have the API generate a callback so that, for example, an operator can be prompted to supply the username/password as required.

The primary access to MHFCM API is through the MHFCMContext object. An application may have multiple MHFCMContext objects, each associated with a separate M-Switch configuration. The primary functions of MHFMCContext are:

  1. List ACP127 Circuits. This downloads all of the ACP 127 circuits and associated configuration.
  2. List ACP 142 Channels. This downloads all of the ACP 142 Channels and associated configuration.
  3. List STANAG 5066 Servers. This downloads a list of configured STANAG 5066 servers, which are referenced by the ACP 127 Circuits and ACP 142 Channels.

These three operations download the full M-Switch and associated configuration to the client. This will typically be done at start-up and repeated from time to time to pick up any changes made to the configuration not made by this instance of the MHFCM API.

The MHFCM API user will use the various getXXXX methods to obtain information from the MHFCMContext about the ACP 127 Circuits, ACP 142 Channels, and STANAG 5066 Servers in the configuration. The functions are now looked at in more detail.

ACP 127 Circuits

There are a number of elements associated with each ACP 127 Circuit, some of which may be set and some are read only:

  1. Description (Read Only). This can be used to display and identify a given circuit to the operator. Part or all of the description may be used by the MHFCM user as a key, to enable the MHFCM API user to associate additional external information with the circuit.
  2. Mode (Read Only). May be "Broadcast Sender"; "Broadcast Receiver"; or "Point to Point".
  3. Configured STANAG 5066 Server (read only). If STANAG 5066 is used, the circuit will identify the server by reference. See below for detailed description.
  4. ARQ or Non-ARQ (read only). ARQ will be used for point to point circuits and non-ARQ for broadcasts. Parameter only available when STANAG 5066 is used.
  5. STANAG 5066 SAP (default 1).
  6. Serial Line. If a serial interface is used, information is configured with the circuit. This is described below.
  7. ITA5/ITA2. ITA2 may be configured to give a more compact encoding. When used with a serial interface, ITA2 will typically be used with a 5 bit serial option. When used with STANAG 5066, two options for ITA2 encoding (following the COSS specification) are provided.

An OTAM service may be configured for an ACP 127 Broadcast Sender service. The following parameters may be configured for the OTAM

  1. Serial Interface. This is configured for the inbound stream.
  2. Error Rate. This is a percentage (byte error rate). If this error rate is exceeded an alert will be triggered.
  3. Alert Handler. The user of the MHFCHM API may register a handler for each configured OTAM service. This handler is called when the alert is triggered.

An ACP 127 Circuit may be enabled or disabled. Typically the user of the MHFCM API will start with all circuits disabled and then will enable circuits when all aspects of the circuit have been correctly configured. When a circuit is enabled, M-Switch will send messages over it.

ACP 142 Channels

There are a number of elements associated with each ACP 142 Channel, some of which may be set and some are read only:

  1. Description (Read Only). This can be used to display and identify a given circuit to the operator. Part or all of the description may be used by the MHFCM user as a key, to enable the MHFCM API user to associate additional external information with the circuit.
  2. Configured STANAG 5066 Server (read only). This is identified by reference. See below for detailed description.
  3. STANAG 5066 SAP (default 2).

An ACP 142 Channel may be enabled or disabled. Typically the user of the MHFCM API will start with all ACP 142 channels disabled and then will enable the channel when it is correctly connected to modem/radio. When an ACP 142 channel is enabled, M-Switch will send messages over it.

STANAG 5066 Servers

STANAG 5066 servers may be shared between ACP 127 Circuits and ACP 142 channels. Each STANAG 5066 Server is identified by a description (read only). A STANAG 5066 server may be configured in two ways.

The first option is as an external server, and this is simply a host and port. These parameters can be modified by the user of the MHFCM API, so that the STANAG 5066 server being used can be controlled.

The second option is to use Icon 5066. When this is used, connection configuration between ACP 127 or ACP 142 and Icon 5066 is not exposed in the API.

The current version of the API for Icon 5066 exposes a serial interface, with configuration options described below. This allows connection of Icon 5066 into a circuit with a serial interface.

Future version of the API may allow for other options. For example, data connection over a serial interface (which would typically go to a crypto box) and a control interface using a modem vendor specific protocol (e.g., RAP1 to an RapidM RM series modem) to enable STANAG 5066 control of link transmission speed.

Serial Lines

Serial line interfaces may be configured for ACP 127 Circuits, OTAM service or Icon 5066. This is a part of each configuration. All aspects of the serial interface that can be configured from the Isode management APIs may also be configured through MHFCM API for each of these uses. The following parameters are of particular note.

  1. Interface type. This reflects serial interfaces supported by Isode. Current options are:
    1. Digiport TS Sever Serial Hub (using RFC 2217)
    2. eLogic Radio Hub (to be implemented)
    3. MoRaSky (Isode Serial Interface simulator)
  2. Synchronous/Asynchronous (some interfaces may only support one of these options)
  3. Domain and Port. Host or IP for a serial hub.
  4. Line speed (set according to line speed mode)
  5. Line speed mode: (options available depend on interface)
    1. Set Serial Speed. The interface allows control of the line speed, and this should be set to the value set from MHFCM API.
    2. Read Serial Speed. The interface allows the line speed to be read. The value should be read through the MHFM API.
    3. Independent. The value for M-Switch should be set through the MHFCM API, to match the value configured for the serial line.
  6. Transmit speed (async interfaces only). This is the speed of the modem, which will be slower than the async line speed. For sync interfaces, this speed is the same as line speed.
FAB (Frequency Assignment Broadcast)

FAB provides HF channel availability information to ships for use in ship to shore links. The FAB broadcast is also used to convey message processing status information from shore to ship on assigned channels, transmitting this information as short status messages. This information needs to be communicated from the MFG System on shore (under control of operator receiving a message) to the MFG system on ship (to the operator controlling sending of the message). The MHFCM API enables this integration.

The forwardFABStatus method is used on a ship by the FAB broadcast receiver to convey FAB status messages to the ship MFG to present to the operator managing the circuit with which the API call is associated. Note this API is "design only" and not currently implemented.

The ACP127Circuit.getCircuitStatus() API allows the MHFCM user to query an ACP127 Circuit to find information about its status. The API user will use this value when deciding which FAB status to broadcast for the circuit, and will ensure that critical messages (e.g., message received or request to retransmit) are broadcast at least once.

The ACP127Circuit.getOperatorMessages() API allows the MHFCM user to query an ACP127 Circuit to obtain the list of operator messages which have been sent, as well as the time that they were sent. This will enable an operator to ensure that critical messages get broadcast.

Building and Deploying an application which uses the MHFCM API

The MHFCM API class library is contained inside isode-mhfcm-api.jar, which by default is installed (as well as various other class libraries) in the directory (LIBDIR)/java/classes. To compile an application which uses Java DSAPI, you must ensure that isode-mhfcm-api.jar appears on the CLASSPATH used by the compiler.

To run an application which uses the MHFCM API, you must have isode-mhfcm-api.jar on your CLASSPATH, and you must also make sure that the JVM is able to locate shared libraries that are used by MHFCM API. These shared libraries are installed in LIBDIR.

For example, to build an application on Unix, you would use something like:

% javac -cp /opt/isode/lib/java/classes/isode-mhfchm-api.jar MyClass.java
and to run the application:
% java -Djava.library.path=/opt/isode/lib -cp /opt/isode/lib/java/classes/isode-mhfcm.jar:. MyClass

On Windows, you would use something like (depending on installation paths):

 % javac -cp "C:\Program Files\Isode\bin\java\classes\isode-mhfcm-api.jar" MyClass.java
When running the program, you need to ensure your PATH environment variable includes the directory containing Isode libraries. For example:
% PATH=%PATH%;C:\Program Files\Isode\bin
% java -cp "C:\Program Files\Isode\bin\java\classes\isode-mhfcm-api.jar;." TestMHFCM

Example Program

import java.io.Console;
import java.util.List;

import com.isode.mhfcm.ACP127Circuit;
import com.isode.mhfcm.ACP142Channel;
import com.isode.mhfcm.AuthDetailsProvider;
import com.isode.mhfcm.AuthenticationDetails;
import com.isode.mhfcm.BindProfile;
import com.isode.mhfcm.Component;
import com.isode.mhfcm.ConfigInconsistencyException;
import com.isode.mhfcm.DSABindProfile;
import com.isode.mhfcm.MHFCMContext;
import com.isode.mhfcm.MHFCMContextParameters;
import com.isode.mhfcm.MHFCMException;
import com.isode.mhfcm.SerialLine;
import com.isode.mhfcm.SerialLine.InterfaceType;


/*  Copyright (c) 2016, Isode Limited, London, England.
 *  All rights reserved.
 *
 *  Acquisition and use of this software and related materials for any
 *  purpose requires a written licence agreement from Isode Limited,
 *  or a written licence from an organisation licenced by Isode Limited
 *  to grant such a licence.
 *
 */

/**
 * Program to demonstrate how to use the MHFCM API
 * 
 * The program expects a set of environment variables to have been
 * defined, which are used to construct a MHFCMContext
 * (see the usage() method for a list).
 * 
 * To compile the program, use a command of the form
 * 
 * % javac -cp "C:\Program Files\Isode\bin\java\classes\isode-mhfcm-api.jar" TestMHFCM.java
 * 
 * where the argument for -cp includes the full path to the isode-mhfcm-api.jar
 * file. To run the program, use a command of the form:
 * 
 * % java -Djava.library.path="C:\Program Files\Isode\bin" -cp "C:\Program Files\Isode\bin\java\classes\isode-mhfcm-api.jar;." TestMHFCM
 * 
 * where the argument for -Djava.library.path is the full path of
 * the directory containing Isode shared libraries, and -cp contains the
 * path to the isode-mhfcm-api.jar file as well as the path to the class file
 * TestMHFCM.class
 *
 * The contents of a sample Windows ".BAT" file to set up environment variables is shown
 * below:
 *
 * set DSA_HOSTNAME=centos64-1.isode.net
 * set DSA_PORTNUMBER=19389
 * set DSA_USERNAME=cn=Fred Johnson,cn=Users,o=messaging
 * set DSA_PASSWORD=secret
 * set DSA_MTA_DN=cn=centos64-1,cn=Messaging Configuration,o=Isode,o=messaging
 * set QM_HOST=centos64-1.isode.net
 * set QM_USERNAME=fred.johnson@centos64-1.isode.net
 * set QM_PASSWORD=secret
 * set QM_PORTNUMBER=18001
 * set QM_SASLMECHS=CRAM-MD5
 */
public class TestMHFCM {

    /**
     * Hostname of DSA that contains config info
     */
    static final String dsaHost = System.getenv("DSA_HOSTNAME");

    /**
     * Port number of DSA that contains config info
     */
    static final int dsaPortNumber = getIntFromEnv("DSA_PORTNUMBER");

    /**
     * Username/SASL ID for DSA that contains config info
     */
    static final String dsaUsername = System.getenv("DSA_USERNAME");
    /**
     * SASL ID password for DSA that contains config info
     */
    static final String dsaPassword = System.getenv("DSA_PASSWORD");

    /**
     * SASL mech for DSA that contains config info
     */
    static final String dsaSASLMechs = System.getenv("DSA_SASLMECHS");

    /**
     * DN of the MTA configuration in the DSA
     */
    static final String dsaMTADN = System.getenv("DSA_MTA_DN");

    /**
     * Hostname of queue manager for SOM connections
     */
    static final String qmHost = System.getenv("QM_HOST");

    /**
     * Port number of queue manager for SOM connections
     */
    static final int qmPortNumber = getIntFromEnv("QM_PORTNUMBER");


    /**
     * Username/SASL ID for queue manager
     */
    static final String qmUsername = System.getenv("QM_USERNAME");
    /**
     * password for queue manager
     */
    static final String qmPassword = System.getenv("QM_PASSWORD");

    /**
     * SASL mech for queue manager
     */
    static final String qmSASLMechs = System.getenv("QM_SASLMECHS");

    /**
     * Translate an environment variable setting into an integer
     * @param envVariable
     * @return the integer, or -1 if the variable doesn't exist or isn't a number
     */
    private static int getIntFromEnv(String envVariable) {
        try {
            return Integer.parseInt(System.getenv(envVariable));
        }
        catch (Exception e) {
            return -1;
        }
    }


    static void showACP127Circuits(MHFCMContext context) throws MHFCMException {
        List circuitList = context.getACP127Circuits();
        if (circuitList.isEmpty()) {
            System.out.println("No ACP127 circuits are configured");
            return;
        }

        for (ACP127Circuit circuit:circuitList) {
            System.out.println("Found circuit " + circuit);
            System.out.println("  This circuit is " + (circuit.isEnabled() ? "enabled" : "disabled"));
            System.out.println("  description: \"" + circuit.getCircuitDescription() + "\"");
            System.out.println("  type: \"" + circuit.getCircuitType() + "\"");
            System.out.println("  country: \"" + circuit.getCountry() + "\"");
            System.out.println("  ID: \"" + circuit.getID() + "\"");
            System.out.println("  Mode: \"" + circuit.getCircuitMode() + "\"");
            System.out.println("  status: \"" + circuit.getCircuitStatus() + "\"");
            System.out.println("  type: \"" + circuit.getCircuitType() + "\"");
            System.out.println("  ACPCircuitType: \"" + circuit.getACP127CircuitType() + "\"");
            System.out.println("  Transmit speed: " + circuit.getTransmitSpeed());
            switch (circuit.getACP127CircuitType()) {
            case OTHER :
                System.out.println("    "/* + circuit.getS5066Server()*/);
                System.out.println("    S5066 ARQMode: \"" + circuit.getS5066ARQMode() + "\"");
                System.out.println("    S5066 Encoding type: \"" + circuit.getS5066EncodingType() + "\"");
                break;
            case SERIAL :
                System.out.println("    Serial Encoding Type: \"" + circuit.getSerialEncodingType() + "\"");
                SerialLine serialLine = circuit.getSerialLine();
                System.out.println("    Serial Line: \"" + serialLine + "\"");
                System.out.println("      Data Word Size:" + serialLine.getDataWordSize());
                System.out.println("      Serial Speed: " + serialLine.getSerialSpeed());
                System.out.println("      Stop bits: " + serialLine.getStopbits());
                System.out.println("      Synchronous: " + serialLine.isSynchronous());
                System.out.println("      CTS/RTS: " + serialLine.hasCTSRTS());
                System.out.println("      Driver type: " + serialLine.getDriverType());
                
                // Depending on driver type, the SerialLine will either have
                // a domainname/port or a devicename
                if (serialLine.getDriverType() == InterfaceType.DIGIPORT) {
                    System.out.println("      DomainName: " + serialLine.getDomainName());
                    System.out.println("      Port: " + serialLine.getPortNumber());
                }
                else {
                    System.out.println("      Device Name: \"" + serialLine.getDeviceName() + "\"");
                }
                break;
            case TCPIP:
                System.out.println("    TCPIP host : \"" + circuit.getTCPHost() + "\"");
                System.out.println("    TCPIP portnumber : \"" + circuit.getTCPPortNumber() + "\"");
                break;
            }
        } 
    }

    static void showACP142Channels(MHFCMContext context) throws MHFCMException {
        List channelList = context.getACP142Channels();
        if (channelList.isEmpty()) {
            System.out.println("No ACP142 channels are configured");
            return;
        }

        for (ACP142Channel channel:channelList) {
            System.out.println("Found channel " + channel);
            System.out.println("  description: \"" + channel.getDescription() + "\"");
            System.out.println("  name: \"" + channel.getName() + "\"");
            System.out.println("  id: \"" + channel.getID() + "\"");
            System.out.println("  S5066 Server: " /*channel.getS5066Server()*/);


        }
    }

    static void o(String s) {
        System.out.println(s);
    }

    static void usage() {
        o("Make sure you have set environment variables before running the program");       
        o(" DSA_HOSTNAME should be the server where the directory is running, e.g. \"server.net\"");
        o(" DSA_PORTNUMBER should be the directory server's port number, e.g. \"19389\"");
        o(" DSA_USERNAME is the bind DN or SASL ID for binding to the directory server,");
        o(" e.g. \"cn=operator,cn=users,o=server\", or \"operator@myserver.net\"");
        o(" DSA_SASLMECHS are SASL mechanisms to use for a SASL bind to the directory");
        o(" server, e.g. \"CRAM-MD5\" (or unset for a simple bind using DN)");
        o(" DSA_PASSWORD is the password to be used with DSA_USERNAME, e.g. \"secret\"");
        o(" DSA_MTA_DN is the DN of the MTA inside the directory. ");
        o(" For example, \"cn=server.net,cn=Messaging Configuration,o=messaging,o=my organization\"");
        o(" QM_HOST should be the server where the queue manager is running, e.g. \"server.net\"");
        o(" QM_USERNAME is the SASL ID for connecting to the queue manager, e.g. \"operator@myserver.net\"");
        o(" QM_PASSWORD is the password to go with QM_USERNAME, e.g. \"secret\"");
        o(" QM_PORTNUMBER is the port number of the queue manager, e.g. \"18001\"");
        o(" QM_SASLMECHS are SASL mechanism used when binding to the queue manager,");
        o(" e.g. \"CRAM-MD5\"");
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        if (dsaHost == null) {
            usage();
            return;
        }
        
        // Create an AuthDetailsProvider which can be called when the API needs
        // to ask for authentication details for a server
        AuthDetailsProvider provider = new AuthDetailsProvider() {
            
             public AuthenticationDetails getAuthenticationDetails(BindProfile bp,
                    String description, Component component) {

                if (component == Component.QUEUE_MANAGER) {
                    // Use environment variables to create queue manager authentication
                    AuthenticationDetails ad = 
                            new AuthenticationDetails(qmUsername, qmPassword, qmSASLMechs);
                    return ad;
                }
                // Prompt user for details

                Console console = System.console();
                System.out.println("The MHFCMAPI wants to know how to connect to the " + component);
                System.out.print("Username: ");
                String username = console.readLine();
                System.out.print("Password: ");
                char[] pw = console.readPassword();
                String password = new String(pw); // Demo only, not recommended
                System.out.print("SASL mechs: ");
                String saslMechs = console.readLine();
                AuthenticationDetails ad = new AuthenticationDetails(
                        username,password.toString(),saslMechs);
                return ad;
                

            }
        };

        DSABindProfile dsaBindProfile;
        try {
            AuthenticationDetails ad = 
                    new AuthenticationDetails(dsaUsername, dsaPassword, dsaSASLMechs);
            dsaBindProfile= new DSABindProfile(
                    dsaHost,dsaPortNumber,ad,dsaMTADN);
        }
        catch (ConfigInconsistencyException e) {
            System.out.println("Cannot construct DSA bind profile:\n" + e);
            usage();
            return;
        }
        catch (Exception e) {
            System.out.println("Error constructing DSA bind profile:\n" + e);
            usage();
            return;
        }

        // This BindProfile contains no AuthenticationsDetails, and will be used
        // to demonstrate how to use the AuthDetailsProvider
        BindProfile qmBindProfile;
        qmBindProfile = new BindProfile(qmHost,qmPortNumber,null);
        


        MHFCMContextParameters ctxParam;

        try {
            ctxParam = new MHFCMContextParameters(qmBindProfile, dsaBindProfile, null, null);
        }
        catch (MHFCMException e) {
            System.out.println("Error creating MHFCMContextParameters:\n" + e);
            usage();
            return;
        }
        catch (Exception e) {
            System.out.println("Error constructing MHFCMContextParameters:\n" + e);
            usage();
            return;
        }
        
        MHFCMContext context;
        try {
            context = new MHFCMContext(ctxParam, provider);
        }
        catch (MHFCMException e) {
            System.out.println("Error constructing MHFCMContext:\n" + e);
            usage();
            return;
        }
        catch (NullPointerException e) {
            System.out.println("Error constructing MHFCMContext:\n" + e);
            usage();
            return;
        }

        System.out.println("MHFCMContext has been created");

        try {
            System.out.println("Listing ACP127 circuits..");
            showACP127Circuits(context);
        }
        catch (MHFCMException e) {
            System.out.println("Error showing ACP127 circuits:\n" + e);
        }
        System.out.println();

        try {
            System.out.println("Listing ACP142 channels..");
            showACP142Channels(context);
        }
        catch (MHFCMException e) {
            System.out.println("Error showing ACP142 channels:\n" + e);
        }

    }

}
Packages 
Package Description
com.isode.mhfcm