Tutorial JUL 12, 2024 • 06 MIN READ

SPRING BOOT
SERVER
USING JPOS

Abstract technical background

First, what is Jpos? Jpos is a framework for creating ISO8583-based applications. Then what is iso8583? Iso8583 is an internasional standart for financial transaction card originated messaging.

So here we wan't to make ISO8583 service using Jpos.

Prerequired

  • check_circle Girlfriend (not found)
  • check_circle Most beautifull IDE (IntelIj Idea)
  • check_circle Java 17
  • check_circle Maven

Good to be prepared

  • check_circle Good mood
  • check_circle Snacks
  • check_circle Cup of tea

Initiate Project

Go to Spring Initialzr Spring Initializr then create new project.

Initial spring project

Dependencies.

Add this dependency: jpos:2.1.7

Write Code

Create folder deploy, this folder contains the config of this service.

Add folder log, to save log file.

Add folder packager, to save xml field data element.

Add these config xml inside folder deploy

file 00_logger.xml

00_logger.XML JAVA
<?xml version="1.0" encoding="UTF-8"?>
<logger name="Q2" class="org.jpos.q2.qbean.LoggerAdaptor">
    <log-listener class="org.jpos.util.SimpleLogListener" />

    <log-listener class="org.jpos.util.DailyLogListener">
        <property name="window" value="86400" />
        <property name="prefix" value="log/server" />
        <property name="suffix" value=".log"/>
        <property name="date-format" value="-yyyy-MM-dd-HH"/>
        <property name="compression-format" value="gzip"/>
    </log-listener>
</logger>

file 11_server.xml

11_server.XML JAVA
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="iso-channel">
    <attr name="port" type="java.lang.Integer">10001</attr>
    <attr name="maxSessions" type="java.lang.Integer">500</attr>
    <channel class="org.jpos.iso.channel.ASCIIChannel" packager="org.jpos.iso.packager.GenericPackager"
             logger="Q2" name="server-channel">
        <property name="packager-config" value="packager/iso87ascii.xml" />
    </channel>
    <request-listener class="id.co.pandz.iso_server.service.RequestListener" logger="Q2">

    </request-listener>
</server>

file 99_sysmon.xml

99_sysmon.XML JAVA
<sysmon logger="Q2">
    <attr name="sleepTime" type="java.lang.Long">3600000</attr>
    <attr name="detailRequired" type="java.lang.Boolean">true</attr>
</sysmon>

Add xml data element inside folder packager

iso87ascii.xml, download it here iso87ascii.xml or you can create your own.

Create folder service di src/main/java/id/co/pandz/iso_server

Create class RequestListener inside folder service, here we write the logic when server take the request.

RequestListener.JAVA JAVA
public class RequestListener implements ISORequestListener {

    @Override
    public boolean process(ISOSource isoSrc, ISOMsg isoMsg) {
        try {
            System.out.println("ISO8583 Message received on host ["+ ((BaseChannel) isoSrc).getSocket().getInetAddress().getHostAddress() + "]");
            receiveMessage(isoSrc, isoMsg);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return true;
    }

    private void receiveMessage(ISOSource isoSrc, ISOMsg isoMsg) throws ISOException, IOException {
        ISOMsg reply = (ISOMsg) isoMsg.clone();

        GenericPackager packager = new GenericPackager("./packager/iso87ascii.xml");
        isoMsg.setPackager(packager);

        reply.setMTI("0210");
        reply.set(39, "00");

        isoSrc.send(reply);
    }
}

Create class SendRequest inside folder service, this class is used for simulate send request message ISO to the server.

SendRequest.JAVA JAVA
public class SendRequest {
    public static void main(String[] args) throws IOException, ISOException {
        GenericPackager packager = new GenericPackager("./packager/iso87ascii.xml");

        ISOMsg isoMsg = new ISOMsg();
        isoMsg.setPackager(packager);
        isoMsg.setMTI("0200");
        isoMsg.set(2, "0000000000628567782");
        isoMsg.set(3, "301000");
        isoMsg.set(4, "000025000000");
        isoMsg.set(7, "0726025012");
        isoMsg.set(11, "123456");
        isoMsg.set(12, "025012");
        isoMsg.set(13, "0726");
        isoMsg.set(14, "0726");
        isoMsg.set(15, "0726");
        isoMsg.set(30, "000650000");
        isoMsg.set(32, "3020");
        isoMsg.set(37, "ESB1000009");
        isoMsg.set(41, "1002");
        isoMsg.set(42, "123002");
        isoMsg.set(43, "BTN ID                JKT          01211");
        isoMsg.set(48, "123123123123        TRX12309787234");
        isoMsg.set(49, "360");
        isoMsg.set(56, "08555555555");
        isoMsg.set(62, "001002003");
        isoMsg.set(102, "0000000008555555555");

        ASCIIChannel channel = new ASCIIChannel("127.0.0.1", 10001, packager);
        logISOMsg(isoMsg);

        byte[] rawMessage = isoMsg.pack();
        System.out.println(ISOUtil.hexdump(rawMessage));

        channel.connect();
        channel.send(isoMsg);

        ISOMsg response = channel.receive();
        System.out.println("**************** Response *********************");
        logISOMsg(response);
    }

    private static void logISOMsg(ISOMsg msg) {
        System.out.println("----ISO MESSAGE-----");
        try {
            System.out.println("  MTI : " + msg.getMTI());
            for (int i = 1; i <= msg.getMaxField(); i++) {
                if (msg.hasField(i)) {
                    System.out.println("    Field-" + i + " : "
                            + msg.getString(i));
                }
            }
        } catch (ISOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("--------------------");
        }
    }
}

Next, in the main class IsoServerApplication, start server using Q2.

IsoServerApplication.JAVA JAVA
@SpringBootApplication
public class IsoServerApplication {
  public static void main(String[] args) {
    Q2 appServer = new Q2(new String[]{"deploy", "-r"});
    appServer.start();
  }
}

Run server, IsoServerApplication

Console.LOG TERMINAL
<log realm="iso-channel.server" at="2024-08-07T16:02:36.660636">
  <iso-server>
    listening on port 10001
  </iso-server>
</log>

Send request, run class SendRequest

Console.LOG TERMINAL
<log realm="channel/127.0.0.1:61678" at="2024-08-07T16:03:37.415909">
  <send>
    <isomsg direction="outgoing">
      
      <field id="0" value="0210"/>
      <field id="2" value="0000000000628567782"/>
      <field id="3" value="301000"/>
      <field id="4" value="000025000000"/>
      <field id="7" value="0726025012"/>
      <field id="11" value="123456"/>
      <field id="12" value="025012"/>
      <field id="13" value="0726"/>
      <field id="14" value="0726"/>
      <field id="15" value="0726"/>
      <field id="30" value="000650000"/>
      <field id="32" value="3020"/>
      <field id="37" value="ESB1000009  "/>
      <field id="39" value="00"/>
      <field id="41" value="1002            "/>
      <field id="42" value="123002         "/>
      <field id="43" value="BTN ID                JKT          01211"/>
      <field id="49" value="360"/>
      <field id="56" value="08555555555"/>
      <field id="62" value="001002003"/>
      <field id="102" value="0000000008555555555"/>
    </isomsg>
  </send>
</log>

Request goes to server and server returns success response.

Then what if we want to get data from API rest?

Add more dependencies ini: spring-boot-starter-web and lombok

Add folder dto inside folder iso_server. Create class ParamReq iniside folder dto

ParamNameReq.java JAVA
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ParamNameReq {
    private String paramName;
}

Enhance code at RequestListener, we will retrieve rest service to get value then use it to the response.

RequestListener.java JAVA
String mti = reply.getMTI();
if ("0200".equalsIgnoreCase(mti)) {
    RestTemplate httpClient = new RestTemplate();
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

    ParamNameReq request = ParamNameReq.builder()
            .paramName("NEW_SIM:bal-inq:httpCode")
            .build();
    HttpEntity<Object> entity = new HttpEntity<>(request, httpHeaders);
    String result = httpClient.postForObject("http://localhost:8081/simulator/parameter/value", entity, String.class);
    System.out.println("Response param from API: " + result); // 200

    reply.set(48, result);
}

Let's test it!

Console.LOG TERMINAL
<log realm="channel/127.0.0.1:61678" at="2024-08-07T16:03:37.415909">
  <send>
    <isomsg direction="outgoing">
      
      <field id="0" value="0210"/>
      <field id="2" value="0000000000628567782"/>
      <field id="3" value="301000"/>
      <field id="4" value="000025000000"/>
      <field id="7" value="0726025012"/>
      <field id="11" value="123456"/>
      <field id="12" value="025012"/>
      <field id="13" value="0726"/>
      <field id="14" value="0726"/>
      <field id="15" value="0726"/>
      <field id="30" value="000650000"/>
      <field id="32" value="3020"/>
      <field id="37" value="ESB1000009  "/>
      <field id="39" value="00"/>
      <field id="41" value="1002            "/>
      <field id="42" value="123002         "/>
      <field id="43" value="BTN ID                JKT          01211"/>
      <field id="48" value="200"/>
      <field id="49" value="360"/>
      <field id="56" value="08555555555"/>
      <field id="62" value="001002003"/>
      <field id="102" value="0000000008555555555"/>
    </isomsg>
  </send>
</log>

Nice!

How about getting the data from the database?

Okee… First add the following dependencies: jakarta.persistence-api:3.0.0, hibernate-core:6.1.7.Final, hibernate-hikaricp:6.1.7.Final, oracle ojdbc11:23.2.0.0.

Create folder dao at src/main/java/id/co/pandz/iso_server. Then create class ParameterDao.

ParameterDao.java JAVA
@Setter
@Getter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "PARAMETER", schema = "CONFIG")
public class ParameterDao {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME")
    private String name;

    @Column(name = "VALUE")
    private String value;
}

Create config connection to database. Add file hibernate.cfg.xml at src/main/resources

hibernate.cfg.xml JAVA
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
        <property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
        <property name="hibernate.connection.url">jdbc:oracle:thin:@//localhost:1521/dbdev</property>
        <property name="hibernate.connection.username">admin</property>
        <property name="hibernate.connection.password">admin</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">false</property>

        
        <property name="hibernate.hikari.minimumIdle">10</property>
        <property name="hibernate.hikari.maximumPoolSize">50</property>
        <property name="hibernate.hikari.idleTimeout">30000</property>
        <property name="hibernate.hikari.connectionTimeout">20000</property>
        <property name="hibernate.hikari.maxLifetime">1800000</property>

        
        <mapping class="id.co.pandz.iso_server.dao.ParameterDao"/>
    </session-factory>
</hibernate-configuration>

Create folder repository at src/main/java/id/co/pandz/iso_server. Then create interface GenericRepository.

GenericRepository.java JAVA
public interface GenericRepository<T, ID> {
    T save(T entity);
    T findById(ID id);
    List<T> findAll();
}

Create interface ParameterRepository

ParameterRepository.java JAVA
public interface ParameterRepository extends GenericRepository<ParameterDao, Long> {
    ParameterDao findByName(String paramName);
}

Next create folder impl at src/main/java/id/co/pandz/iso_server/repository.
Create class implementation ParameterRepositoryImpl.

ParameterRepositoryImpl.java JAVA
public class ParameterRepositoryImpl implements ParameterRepository {
    Session session = HibernateUtil.getSessionFactory().openSession();
    Transaction transaction = null;

    @Override
    public ParameterDao save(ParameterDao entity) {
        session.getTransaction().begin();
        session.persist(entity);
        session.getTransaction().commit();
        session.close();
        return entity;
    }

    @Override
    public ParameterDao findById(Long id) {
        ParameterDao entity = session.find(ParameterDao.class, id);
        session.close();
        return entity;
    }

    @Override
    public List<ParameterDao> findAll() {
        List<ParameterDao> entities = session.createQuery("SELECT lp FROM ParameterDao lp ", ParameterDao.class).getResultList();
        session.close();
        return entities;
    }

    @Override
    public ParameterDao findByName(String paramName) {
        transaction = session.beginTransaction();
        ParameterDao result = null;
        try {
            // This query should refer to your entity because hibernate read your code
            result = session.createQuery("FROM ParameterDao lp WHERE lp.name = :paramName", ParameterDao.class)
                    .setParameter("paramName", paramName)
                    .getSingleResult();
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
        return result;
    }
}

We will create class connection for the database.
Create folder util at src/main/java/id/co/pandz/iso_server. Then create class HibernateUtil.

HibernateUtil.java JAVA
public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        // Close caches and connection pools
        getSessionFactory().close();
    }
}

So now we can use it to connect database. Enhance class RequestListerner.

RequestListerner.java JAVA
ParamNameReq request = ParamNameReq.builder()
        .paramName("NEW_SIM:bal-inq:httpCode")
        .build();
HttpEntity<Object> entity = new HttpEntity<>(request, httpHeaders);
String result = httpClient.postForObject("http://localhost:8081/simulator/parameter/value", entity, String.class);
System.out.println("Response param from API: " + result); // 200

ParameterRepository repository = new ParameterRepositoryImpl();
ParameterDao responseCode = repository.findByName(NEW_SIM_REFUND_CASECODE_CACHE);
System.out.println("Response param from DB: " + responseCode.getValue());

reply.set(48, result + responseCode.getValue());

Test run SendRequest again...

But wait…

After re run service, then SendRequest, it take time too long to get response. This is happen because Hibernate just starting create connection to database when service receive request.

So, let's make Hibernate start initiate connection when server startup, So when a request comes in, you can get data from the database more quickly.

Create folder qbean at src/main/java/id/co/pandz/iso_server. Create class HibernateQBean.

HibernateQBean.java JAVA
public class HibernateQBean extends QBeanSupport {
    @Override
    protected void startService() {
        // Initialize Hibernate SessionFactory
        HibernateUtil.getSessionFactory();
        log.info("Hibernate SessionFactory initialized");
    }
}

Then add more config so that qbean will be loaded at server startup. Add 07_deploy.xml at folder deploy.

07_deploy.XML JAVA
<qbean name="hibernate-init" class="id.co.pandz.iso_server.qbean.HibernateQBean" logger="Q2">
    <property name="enabled" value="true"/>
</qbean>

Re run server then retest.

Yup... give me a clap if you succeed!

You can find complete source code here: https://github.com/pandz3rd/jpos-iso8583-server

READ BEYOND THE VOID

Technical blueprint background
Tutorial

Middleware Service Translator Json to ISO8583 using Spring Boot Jpos

Create Middleware Service Json to ISO8583 in Springboot...

Read Entry
Abstract digital network
Engineering Topic

Integrate Jenkins to Gitlab Webhook

Install Jenkins and connect to Gitlab...

Read Entry
Circuit board macro
Backend Notes

Difference Between JPA and Hibernate

What difference between JPA and Hibernate...

Read Entry