package com.finance.iso.iso8583.mediator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOPackager;
import org.jpos.iso.packager.GenericPackager;
// TODO:here we need accomodate the logic such as
// Setup a session with XLink and maintain the session state
// throughout the request from that mobile device (Note: Each
// time a new mobile device connects to do a payment, the ESB
// will treat it like an ATM connecting to the XLink switch)
// Initiate a Key exchange handshake with XLink using ISO – 8583
// over TCPIP to obtain a Session Key
// process of obtaining key key..
/*
* If there was a previous connection between the ESB and XLink,
* the ESB shall: 1.Send a Sign Off using the ISO - 8583 message
* over the TCP connection.
*
* 2. The TCP connection between ESB – XLink shall be closed
*
* 3. ESB will be assigned a unique Master Key by XLink to
* initiate the Key Exchange process
*
* 4. ESB initiate the Key Exchange process with XLink using the
* assigned master key to the ESB On success, ESB will decode
* the response from the XLink and extract the Session Key sent
* by XLink
*
* 5. ESB will initiate a TCP Connection to XLink using the
* shared Session Key:Send a Sign On request
*
* 6.Perform an Echo test
*/
public class XLinkConnnector {
private static final Log log= LogFactory.getLog(XLinkConnnector.class);
private static XLinkConnnector connnector = new XLinkConnnector();
private Map<String, XLinkSessionWrapper> sessionMap = new ConcurrentHashMap<String, XLinkSessionWrapper>();
private List<String> expiredSessionList = Collections.synchronizedList(new ArrayList());
private Thread monitor = new MonitorThread();
private Object mutableObj = new Object();
private static int ERROR_RETRY_COUNT= 10;
private static long SUSPEND_DURATION =1000L;
private static long DEFAULT_MONITOR_DURATION=1000L;
private static int DEFAULT_TIMEOUT=60000;
public static XLinkConnnector getInstance() {
System.out.println("Connector Instantiated");
return connnector;
}
public XLinkConnnector(){
monitor.start();
}
/**
* This will handles the key exchange part between XLink.
*
* @param mobileConnectionKey
* @param host
* @param port
* @param transactionHandler
* @return
* @throws ISOException
*/
public XLinkSessionWrapper getSession(String mobileConnectionKey,
String host, String port,
XLinkISO8583TransactionHandler transactionHandler)
throws ISOException {
XLinkSessionWrapper sessionWrapper = sessionMap
.get(mobileConnectionKey);
if(sessionWrapper != null){
log.info("Connector: Session Wrapper not null for "+mobileConnectionKey);
if(sessionWrapper.getChannel().stopped){
//clean it..(a pre-measurement to clean up if connection closed,
//but there is a new request triggering..)
log.info("Connector: Removing closed seesion for "+mobileConnectionKey);
sessionMap.remove(mobileConnectionKey);
expiredSessionList.remove(mobileConnectionKey);
//To Trigger the new connection for the stale connection, make the sessionWrapper to null
sessionWrapper=null;
}
}
if (sessionWrapper == null) {
log.info("Connector : Session wrapper null");
log.info("Connector: Trying to aquire the Mutable Object Lock");
synchronized (mutableObj) {
log.info("Connector: Mutable Object Lock Aquired");
sessionWrapper = sessionMap
.get(mobileConnectionKey);//double checking...
if (sessionWrapper == null) {
sessionWrapper = new XLinkSessionWrapper();
ISOPackager packager = new GenericPackager(this.getClass()
.getResourceAsStream(
XLinkISO8583Constant.JPOS_STREM_DEF));
boolean sessionCreated = false;
int retrycount = 0;
while (!sessionCreated && retrycount != ERROR_RETRY_COUNT) {
try {
log.info("Connector: Starting to initiating XLink connection for "+mobileConnectionKey);
this.exchangeKeys(host, port, transactionHandler,
sessionWrapper, packager);
} catch (XLinkISO8583Exception e) {
log.info("Try fails, Try count "+retrycount);
retrycount++;
try {
Thread.sleep(SUSPEND_DURATION);
} catch (InterruptedException e1) {
}
continue;
}
sessionCreated = true;
}
if (retrycount == ERROR_RETRY_COUNT) {
return null; // TODO provide proper definition
}
sessionWrapper.getChannel().setMsisdnKey(
mobileConnectionKey);
sessionWrapper.getChannel().setExpiredListRef(
expiredSessionList);
// assign after
// performing the [5]
sessionMap.put(mobileConnectionKey, sessionWrapper);
log.info("Session map updated");
}else{
log.info("Connector: Session Wrapper exists in second test for "+mobileConnectionKey+" session id "+ sessionWrapper.getSessionId());
}
}
log.info("Connector Released object lock");
}else{
log.info("##### found session information " + sessionWrapper.getSessionId());
}
return sessionWrapper;
}
/**
* Method to handle key exchange and assign session key.
*
* @param host
* @param port
* @param transactionHandler
* @param sessionWrapper
* @param packager
* @param chl
* @throws ISOException
* @throws XLinkISO8583Exception
*/
private void exchangeKeys(String host, String port,
XLinkISO8583TransactionHandler transactionHandler,
XLinkSessionWrapper sessionWrapper, ISOPackager packager)
throws ISOException, XLinkISO8583Exception {
XLinkChannel chl = null;
try {
chl = new XLinkChannel(host, Integer.valueOf(port), packager);
try {
log.info("Connector connecting channel");
//chl.setTimeout(DEFAULT_TIMEOUT);
chl.connect();
} catch (IOException e) {
log.error(e);
mutableObj.notify();
log.error("Releasing Mutable Lock after IOException");
throw new XLinkISO8583Exception(
"Error while connecting XLink {system may be shutdown} I/O error",
e);
}catch (Exception e) {
log.error(e);
mutableObj.notify();
log.error("Releasing Mutable Lock after Exception");
throw new XLinkISO8583Exception(
"Error while connecting XLink {system may be unexpected} I/O error",
e);
}
sessionWrapper.setChannel(chl);
log.info("Channel started listening. Trying Sign On next");
transactionHandler.doKeyExchange(sessionWrapper);
log.info("Sign On done and proceeding ...");
} catch (XLinkISO8583Exception exception) {
try {
chl.disconnect();
} catch (IOException e) {
log.error("Error while cleaning the channels", e);
}
throw exception;
} catch (ISOException ex) {
try {
chl.disconnect();
} catch (IOException e) {
log.error("Error while cleaning the channels", e);
}
throw ex;
}
}
public Map<String, XLinkSessionWrapper> getMap() {
return sessionMap;
}
public void setXLinkOnErrorRetryCount(int count){
ERROR_RETRY_COUNT = count;
}
public void setXLinkOnRetrySuspend(long retrySuspend){
SUSPEND_DURATION = retrySuspend;
}
public void setMontorTriggerDuration(long monitorTriggerDuration){
DEFAULT_MONITOR_DURATION =monitorTriggerDuration;
}
@SuppressWarnings("unused")
private class MonitorThread extends Thread {
@Override
public void run() {
log.info("Monitor ######" + expiredSessionList.size());
while (true) {
// System.out.println("###### monitor"
// +expiredSessionList.size());
try {
Thread.sleep(DEFAULT_MONITOR_DURATION);
} catch (InterruptedException e) {
this.interrupt();
}
this.checkExpiredSessions();
}
}
private void checkExpiredSessions() {
log.debug("Monitor Thread : Check Session Starts "+expiredSessionList.size());
if (expiredSessionList.size() > 0) {
Iterator<String> listItr = expiredSessionList.iterator();
while (listItr.hasNext()) {
String expired = listItr.next();
log.debug("Monitor Thread : Removing Session "+expired);
sessionMap.remove(expired);
listItr.remove();
log.info("removed #" + expired + ": sesMap "
+ sessionMap.size() + ": expired list ->"
+ expiredSessionList.size());
}
}
log.info("Monitor Thread : Completes");
}
}
}