Tuesday, October 07, 2008

(ISO 8583 + JPOS + Log4J) in Java Socket Programming

SECTION 1. INTRODUCTION
Chapter 1.1. ISO 8583
You can read it's explanation first in Wikipedia.
Chapter 1.2. JPOS
JPOS is opensource framework for ISO 8583. Source code and library are free but documentation is comercial. We can download it here.
Chapter 1.3. Log4J
Log4J is opensource logging library. We can download it's library here and read it's mini e-book first written by Mr. Endy in here.
Chapter 1.4. Java Socket Programming
There's no explanation. Just need the basic knowledge of Java network programming and Java Thread Programming and let's coding.

SECTION 2. CODING
First we prepare that all we need. In this time, we will use NetBeans as IDE. Besides JPOS framework and Log4J library that I have mentioned above, we still need:
1. Xerces-Java XML parser library (xercesImpl.jar)
2. Spring framework
3. Jakarta Apache Commons Logging library

Chapter 2.1. ISO 8583 + JPOS + Log4J
In NetBeans, create a new Java application named MyApp. Create a package named com.ndung.iso8583. JPOS framework need a packager to set which ISO 8583 version that will be used. In this time we will use ISO 8583 version 1987. Download first iso87ascii.xml and place it in that package. Create a class as packager factory named PackagerFactory.java.


package com.ndung.iso8583;

import java.io.InputStream;

import org.jpos.iso.ISOException;
import org.jpos.iso.ISOPackager;
import org.jpos.iso.packager.GenericPackager;

public class PackagerFactory {
public static ISOPackager getPackager() {
ISOPackager packager = null;
try {
String filename = "iso87ascii.xml";
InputStream is = PackagerFactory.class.getResourceAsStream(filename);
packager = new GenericPackager(is);
}
catch (ISOException e) {
e.printStackTrace();
}
return packager;
}
}

And then create a class named MessageHandler.java that will be used to handle received message. Download first iso87asciiProperties.xml that will be used to translate bits of ISO message to become a String message that can be read easily.


package com.ndung.iso8583;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOPackager;

public class MessageHandler {
private static ISOPackager packager = PackagerFactory.getPackager();
private Logger logger = Logger.getLogger( getClass() );
public String process(ISOMsg isomsg) throws Exception {
logger.info("ISO Message MTI is "+isomsg.getMTI());
logger.info("Is ISO message a incoming message? "+isomsg.isIncoming());
logger.info("Is ISO message a outgoing message? "+isomsg.isOutgoing());
logger.info("Is ISO message a request message? "+isomsg.isRequest());
logger.info("Is ISO message a response message? "+isomsg.isResponse());
String message = "";
for (int i=0;i<128;i++){
if (isomsg.hasField(i)){
message += loadXMLProperties().getProperty(Integer.toString(i))+"="+
isomsg.getValue(i)+"\n";
}
}
logger.info(message);
return message;
}

public ISOMsg unpackRequest(String message) throws ISOException, Exception {
ISOMsg isoMsg = new ISOMsg();
isoMsg.setPackager(packager);
isoMsg.unpack(message.getBytes());
isoMsg.dump(System.out, " ");
return isoMsg ;
}

public String packResponse(ISOMsg message) throws ISOException, Exception {
message.dump(System.out, " ");
return new String( message.pack() ) ;
}

public Properties loadXMLProperties(){
Properties prop = new Properties();
try{
FileInputStream input=new FileInputStream("iso87asciiProperties.xml");
prop.loadFromXML(input);
input.close();
}
catch(IOException e){
e.printStackTrace();
}
return prop;
}
}

Chapter 2.2. Java Socket Programming + Log4J
Create a package named com.ndung.socket and then create four classes named ServerConfig.java, SocketServerHandlerFactory.java, SocketServerHandler.java, SocketConnectionServer. Before that create log4j.properties as logging configuration in default package.

# Category Configuration
log4j.rootLogger=INFO,Konsole,Roll
# Console Appender Configuration
log4j.appender.Konsole=org.apache.log4j.ConsoleAppender
log4j.appender.Konsole.layout=org.apache.log4j.PatternLayout
# Date Format based on ISO­8601 : %d
log4j.appender.Konsole.layout.ConversionPattern=%d [%t] %5p %c ­ %m%n
# Roll Appender Configuration
log4j.appender.Roll=org.apache.log4j.RollingFileAppender
log4j.appender.Roll.File=/home/ndung/NetBeansProjects/MyApp/log/myApp.log
log4j.appender.Roll.MaxFileSize=10KB
log4j.appender.Roll.MaxBackupIndex=2
log4j.appender.Roll.layout=org.apache.log4j.PatternLayout
# Date Format based on ISO­8601 : %d
log4j.appender.Roll.layout.ConversionPattern=%d [%t] %p (%F:%L) ­ %m%n


package com.ndung.socket;
public class ServerConfig {
private int port;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}


package com.ndung.socket;

import com.ndung.iso8583.MessageHandler;
import java.io.IOException;
import java.net.Socket;

public class SocketServerHandlerFactory {
private MessageHandler messageHandler;
public SocketServerHandlerFactory(MessageHandler messageHandler) {
this.messageHandler = messageHandler;
}
public SocketServerHandler createHandler(Socket socket) throws IOException {
return new SocketServerHandler(socket, messageHandler);
}
}


package com.ndung.socket;

import com.ndung.iso8583.MessageHandler;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import org.apache.log4j.Logger;
import org.jpos.iso.ISOMsg;

public class SocketServerHandler extends Thread{
private Logger logger = Logger.getLogger( getClass() );
private Socket serverSocket ;
private BufferedReader inFromClient;
private DataOutputStream outToClient;
private MessageHandler messageHandler;
private String datafromClient;

public SocketServerHandler(Socket socket, MessageHandler messageHandler) throws IOException {
super("SocketHandler (" + socket.getInetAddress().getHostAddress() + ")");
this.serverSocket = socket ;
this.messageHandler = messageHandler;
this.inFromClient = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
this.outToClient = new DataOutputStream(serverSocket.getOutputStream());
}
@Override
public void run() {
try {
logger.info("Server is ready...");
while (true) {
logger.info("There is a client connected...");
outToClient.writeBytes("InfoServer version 0.1\n");
datafromClient = inFromClient.readLine();
logger.info("Data From Client : "+datafromClient);
ISOMsg isomsg = messageHandler.unpackRequest(datafromClient);
outToClient.writeBytes(messageHandler.process(isomsg));
}
}
catch (IOException ioe) {
logger.error("error: " + ioe);
}
catch (Exception e) {
logger.error("error: " + e);
}
finally {
try {
if (inFromClient != null) inFromClient.close();
if (outToClient != null) outToClient.close();
if (serverSocket != null) serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}


package com.ndung.socket;

import java.io.IOException;
import java.net.ServerSocket;

public class SocketConnectionServer {
private ServerConfig config;
private SocketServerHandlerFactory handlerFactory;
private boolean stop;
public SocketConnectionServer(ServerConfig config, SocketServerHandlerFactory handlerFactory) {
this.config = config;
this.handlerFactory = handlerFactory;
}
public void start() throws IOException {
stop = false;
final ServerSocket serverSocket = new ServerSocket(config.getPort());
new Thread(new Runnable() {
public void run() {
while (!stop) {
try {
handlerFactory.createHandler(serverSocket.accept()).start();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void stop() {
stop = true;
}
}

Create a socket server class named MyServer.java. This class also as a main class for our application. And then we will create a socket client class named MyClient.java. Before that create applicationContext.xml in default package as Spring configuration injection for our main class.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="socketConnectionServer" class="com.ndung.socket.SocketConnectionServer">
<constructor-arg>
<ref local="config" />
</constructor-arg>
<constructor-arg>
<ref local="socketServerHandlerFactory" />
</constructor-arg>
</bean>

<bean id="config" class="com.ndung.socket.ServerConfig">
<property name="port">
<value>50000</value>
</property>
</bean>

<bean id="socketServerHandlerFactory" class="com.ndung.socket.SocketServerHandlerFactory">
<constructor-arg>
<ref local="messageHandler" />
</constructor-arg>
</bean>

<bean id="messageHandler" class="com.ndung.iso8583.MessageHandler">
</bean>

</beans>


package com.ndung.socket;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyServer {

public static void main(String[] args) throws IOException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SocketConnectionServer server = (SocketConnectionServer) ctx.getBean("socketConnectionServer");
server.start();
}
}


package com.ndung.socket;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import org.apache.log4j.Logger;

public class MyClient {
private final int MY_PORT=50000;
private final String TargetHost = "localhost";
private final String QUIT = "QUIT";
private Logger logger = Logger.getLogger( getClass() );
public MyClient() {
try {
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
Socket clientSocket = new Socket(TargetHost, MY_PORT);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
logger.info(inFromServer.readLine());
boolean isQuit = false;
while (!isQuit) {
System.out.print("Your data : ");
String cmd = inFromUser.readLine();
cmd = cmd.toUpperCase();
if (cmd.equals(QUIT)) {
isQuit = true;
}
outToServer.writeBytes(cmd + "\n");
String message = inFromServer.readLine();
while (message!=null){
logger.info("From Server: " + message);
message = inFromServer.readLine();
}
}
outToServer.close();
inFromServer.close();
clientSocket.close();
}
catch (IOException ioe) {
logger.error("Error:" + ioe);
}
catch (Exception e) {
logger.error("Error:" + e);
}
}
public static void main(String[] args) {
new MyClient();
}
}

Run our application first. It means our main class (MyServer.java) will be run first. And then run client as much that we want. It means MyClient.java will be run twice or more. And then in one of our client application console enter an input data. It means a String of ISO message. As example:
0210723A00010A808400185936001410010999990110000000100000001007021533000001191533
10061007065656561006090102240000000901360020100236C0102240000000
Look in both of our application console either MyServer or MyClient. What do you see?
Btw, Happy Eid Mubarak...

Read More..