Search code examples
jpos

How to properly change port of JPOS Channel-Adapter during runtime?


I would like to be able to create Channel-Adaptor connections during runtime of my program. I have a solution but it doesn't seem like the proper way to do things in JPOS.

My program will receive information from the user through a JSON Post request similar to below:

    @PostMapping(value = "/message", consumes = DEFAULT_CONTENT_TYPE, produces = 
                   DEFAULT_CONTENT_TYPE)
        void postIsoMessage(
                @RequestHeader(value = HEADER_CORRELATION_ID) String correlationId,
                @RequestBody JsonMessage requestMessage)
                throws Exception {
             //process that json message
               ...
              }

Here is a snippet of that JSON that the user will send specifying which Port and ISO Format to use:

    {
        "config": {
            "keyset": "KEYSET_1",
            "pinblockFmt": "01 - ISO 0 Format",
            "acq": {
                "format": "VISANET",
                "portNumber": 9405
            },
            "iss": {
                "format": "ISO4",
                "portNumber": 9488
            }
        }}

And my Channel-Adapter / Mux

    <channel-adaptor name="iss-channel" class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2">
      <channel class="org.jpos.iso.channel.CSChannel" packager="org.jpos.iso.packager.GenericPackager" logger="Q2">
        <property name="packager-config" value="src/main/resources/iso4.xml" />
        <property name="host" value="10.999.99.99" />
        <property name="port" value="9999" />
        <property name="timeout" value="300000" />
      </channel>
      <in>iss-channel-send</in>
      <out>iss-channel-receive</out>
      <reconnect-delay>10000</reconnect-delay>
    </channel-adaptor>

    <mux class="org.jpos.q2.iso.QMUX" logger="Q2" name="iss-mux" realm="acq-realm">
      <in>iss-channel-receive</in>
      <out>iss-channel-send</out>
      <ready>iss-channel.ready</ready>
      <request-listener class="AutoResponder"></request-listener>
    </mux>

I would like to be able to change any of the channel-adaptor ports based on what the user specifies in the JSON request.

            Q2 q2 = new Q2("src/main/java/deploy");
            q2.start();
            Thread.sleep(3000L);
            QMUX qmux = NameRegistrar.getIfExists("mux.acq-mux");
            ChannelAdaptor channel = NameRegistrar.getIfExists("iss-channel");
            if (channel == null)
                System.out.println("channel doesn't exist");
    
            else {
                channel.setPort(9488); 
                channel.startService(); //switch port from 9999 to 9488
                NameRegistrar.register("iss-channel",channel);
                Thread.sleep(3000L);
            }
    
            try {
                if (qmux.isConnected()) {
                    ISOMsg m = new ISOMsg();
                    m.setMTI("0100");
                    m.set(2, "4999999999999999");
                    ....
                    ISOMsg response = qmux.request(m, 5000);

This appears to work, I can see in the logs that the new channel connects to port 9488, and the transaction is sent like expected. But I also see that the original port 9999 attempts to connect. My solution feels a bit hacky; almost like there should be a better way to approach this. Is there? In my current solution, does that mean my iss-mux is connected to 9999 and 9488? Will this be a problem?

    <log realm="channel/10.999.99.99:9488" at="2023-03-02T08:38:04.930" lifespan="3ms">
      <connect>
        Try 0 10.999.99.99:9488
      </connect>
    </log>
    <log realm="channel" at="2023-03-02T08:38:05.171" lifespan="2051ms">
      <connect>
        Try 0 10.999.99.99:9999
          Connection refused: connect
        Unable to connect
      </connect>
    </log>

Solution

  • Based on what you shared, you'd like to do that on startup. At least on the code shared, at least until now, you are not trying to modify an already running Q2 instance, but, launch a new one with the properties changed.

    If that's the case, the best chances would be to make use of the jPOS environment feature, for that you just need to reference environment variables in your deploy files, instead of fixed values, like this:

    channel-adaptor:

        <channel-adaptor name="iss-channel" class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2">
          <channel class="${issuer.channel.class:org.jpos.iso.channel.CSChannel}" packager="org.jpos.iso.packager.GenericPackager" logger="Q2">
            <property name="packager-config" value="${issuer.channel.packager.config:src/main/resources/iso4.xml}" />
            <property name="host" value="${issuer.channel.ip:10.999.99.99}" />
            <property name="port" value="${issuer.channel.port:9999}" />
            <property name="timeout" value="${issuer.channel.timeout:300000}" />
          </channel>
          <in>iss-channel-send</in>
          <out>iss-channel-receive</out>
          <reconnect-delay>10000</reconnect-delay>
        </channel-adaptor>
    

    The ${env:def} syntax means that if there is a jPOS environment variable set with name env it will replace the expression with its value, otherwise it is replaced by def. jPOS environment variables are read from different with specific precedence, you can read jPOS programmer's guide section 3.3 Configuration.

    For further details, but in essence, you would just need to do something like this in your code for setting the issuer channel port:

    System.setProperty("issuer.channel.port", "9488");
    Q2 q2 = new Q2("src/main/java/deploy");
    q2.start();
    

    The same goes for all other properties you want to change.

    If you want to modify one of these properties for an already deployed component in a running Q2 server, you could set the java property and then force the redeployment of the component.

    You could do this, for instance, by “touching” its file from the java program. You could also have the xml as a template, read it with jdom2 and then call Q2.deployElement() method.