![JAR search and dependency download from the Maven repository](/logo.png)
org.jpos.q2.iso.QMUX Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jpos Show documentation
Show all versions of jpos Show documentation
jPOS is an ISO-8583 based financial transaction
library/framework that can be customized and
extended in order to implement financial interchanges.
/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2015 Alejandro P. Revilla
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package org.jpos.q2.iso;
import org.jdom.Element;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.q2.QBeanSupport;
import org.jpos.q2.QFactory;
import org.jpos.space.*;
import org.jpos.util.Loggeable;
import org.jpos.util.NameRegistrar;
import java.io.IOException;
import java.io.PrintStream;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* @author Alejandro Revilla
*/
@SuppressWarnings("unchecked")
public class QMUX
extends QBeanSupport
implements SpaceListener, MUX, QMUXMBean, Loggeable
{
static final String nomap = "0123456789";
static final String DEFAULT_KEY = "41, 11";
protected LocalSpace sp;
protected String in, out, unhandled;
protected String[] ready;
protected String[] key;
protected String ignorerc;
protected String[] mtiMapping;
private boolean headerIsKey;
private LocalSpace isp; // internal space
List listeners;
int rx, tx, rxExpired, txExpired, rxPending, rxUnhandled, rxForwarded;
long lastTxn = 0L;
boolean listenerRegistered;
public QMUX () {
super ();
listeners = new ArrayList();
}
public void initService () throws ConfigurationException {
Element e = getPersist ();
sp = grabSpace (e.getChild ("space"));
isp = cfg.getBoolean("reuse-space", false) ? sp : new TSpace();
in = e.getChildTextTrim ("in");
out = e.getChildTextTrim ("out");
ignorerc = e.getChildTextTrim ("ignore-rc");
key = toStringArray(e.getChildTextTrim("key"), ", ", DEFAULT_KEY);
ready = toStringArray(e.getChildTextTrim ("ready"));
mtiMapping = toStringArray(e.getChildTextTrim ("mtimapping"));
if (mtiMapping == null || mtiMapping.length != 3)
mtiMapping = new String[] { nomap, nomap, "0022446789" };
addListeners ();
unhandled = e.getChildTextTrim ("unhandled");
NameRegistrar.register ("mux."+getName (), this);
}
public void startService () {
if (!listenerRegistered) {
listenerRegistered = true;
// Handle messages that could be in the in queue at start time
synchronized (sp) {
Object[] pending = SpaceUtil.inpAll(sp, in);
sp.addListener (in, this);
for (Object o : pending)
sp.out(in, o);
}
}
}
public void stopService () {
listenerRegistered = false;
sp.removeListener (in, this);
}
public void destroyService () {
NameRegistrar.unregister ("mux."+getName ());
}
/**
* @return MUX with name using NameRegistrar
* @throws NameRegistrar.NotFoundException
* @see NameRegistrar
*/
public static MUX getMUX (String name)
throws NameRegistrar.NotFoundException
{
return (MUX) NameRegistrar.get ("mux."+name);
}
/**
* @param m message to send
* @param timeout amount of time in millis to wait for a response
* @return response or null
*/
public ISOMsg request (ISOMsg m, long timeout) throws ISOException {
String key = getKey (m);
String req = key + ".req";
if (isp.rdp (req) != null)
throw new ISOException ("Duplicate key '" + req + "' detected");
isp.out (req, m);
m.setDirection(0);
if (timeout > 0)
sp.out (out, m, timeout);
else
sp.out (out, m);
ISOMsg resp = null;
try {
synchronized (this) { tx++; rxPending++; }
for (;;) {
resp = (ISOMsg) isp.rd (key, timeout);
if (shouldIgnore (resp))
continue;
isp.inp (key);
break;
}
if (resp == null && isp.inp (req) == null) {
// possible race condition, retry for a few extra seconds
resp = (ISOMsg) isp.in (key, 10000);
}
synchronized (this) {
if (resp != null)
{
rx++;
lastTxn = System.currentTimeMillis();
}else {
rxExpired++;
if (m.getDirection() != ISOMsg.OUTGOING)
txExpired++;
}
}
} finally {
synchronized (this) { rxPending--; }
}
return resp;
}
public void notify (Object k, Object value) {
Object obj = sp.inp (k);
if (obj instanceof ISOMsg) {
ISOMsg m = (ISOMsg) obj;
try {
String key = getKey (m);
String req = key + ".req";
Object r = isp.inp (req);
if (r != null) {
if (r instanceof AsyncRequest) {
((AsyncRequest) r).responseReceived (m);
} else {
isp.out (key, m);
}
return;
}
} catch (ISOException e) {
getLog().warn ("notify", e);
}
processUnhandled (m);
}
}
public String getKey (ISOMsg m) throws ISOException {
StringBuilder sb = new StringBuilder (out);
sb.append ('.');
sb.append (mapMTI(m.getMTI()));
if (headerIsKey && m.getHeader()!=null) {
sb.append ('.');
sb.append(ISOUtil.hexString(m.getHeader()));
sb.append ('.');
}
boolean hasFields = false;
for (String f : key) {
String v = m.getString(f);
if (v != null) {
if ("11".equals(f)) {
String vt = v.trim();
int l = m.getMTI().charAt(0) == '2' ? 12 : 6;
if (vt.length() < l)
v = ISOUtil.zeropad(vt, l);
}
if ("41".equals(f)) {
v = ISOUtil.zeropad(v.trim(), 16); // BIC ANSI to ISO hack
}
hasFields = true;
sb.append(v);
}
}
if (!hasFields)
throw new ISOException ("Key fields not found - not sending " + sb.toString());
return sb.toString();
}
private String mapMTI (String mti) throws ISOException {
StringBuilder sb = new StringBuilder();
if (mti != null) {
if (mti.length() < 4)
mti = ISOUtil.zeropad(mti, 4); // #jPOS-55
if (mti.length() == 4) {
for (int i=0; i= 0 && c < 10)
sb.append (mtiMapping[i].charAt(c));
}
}
}
return sb.toString();
}
public synchronized void setInQueue (String in) {
this.in = in;
getPersist().getChild("in").setText (in);
setModified (true);
}
public String getInQueue () {
return in;
}
public synchronized void setOutQueue (String out) {
this.out = out;
getPersist().getChild("out").setText (out);
setModified (true);
}
public String getOutQueue () {
return out;
}
public Space getSpace() {
return sp;
}
public synchronized void setUnhandledQueue (String unhandled) {
this.unhandled = unhandled;
getPersist().getChild("unhandled").setText (unhandled);
setModified (true);
}
public String getUnhandledQueue () {
return unhandled;
}
public void request (ISOMsg m, long timeout, ISOResponseListener rl, Object handBack)
throws ISOException
{
String key = getKey (m);
String req = key + ".req";
if (isp.rdp (req) != null)
throw new ISOException ("Duplicate key '" + req + "' detected.");
m.setDirection(0);
AsyncRequest ar = new AsyncRequest (rl, handBack);
synchronized (ar) {
if (timeout > 0)
ar.setFuture(getScheduledThreadPoolExecutor().schedule(ar, timeout, TimeUnit.MILLISECONDS));
}
isp.out (req, ar, timeout);
sp.out (out, m, timeout);
}
@SuppressWarnings("unused")
public String[] getReadyIndicatorNames() {
return ready;
}
private void addListeners ()
throws ConfigurationException
{
QFactory factory = getFactory ();
Iterator iter = getPersist().getChildren (
"request-listener"
).iterator();
while (iter.hasNext()) {
Element l = (Element) iter.next();
ISORequestListener listener = (ISORequestListener)
factory.newInstance (l.getAttributeValue ("class"));
factory.setLogger (listener, l);
factory.setConfiguration (listener, l);
addISORequestListener (listener);
}
}
public void addISORequestListener(ISORequestListener l) {
listeners.add (l);
}
public boolean removeISORequestListener(ISORequestListener l) {
return listeners.remove(l);
}
public synchronized void resetCounters() {
rx = tx = rxExpired = txExpired = rxPending = rxUnhandled = rxForwarded = 0;
lastTxn = 0l;
}
public String getCountersAsString () {
StringBuffer sb = new StringBuffer();
append (sb, "tx=", tx);
append (sb, ", rx=", rx);
append (sb, ", tx_expired=", txExpired);
append (sb, ", tx_pending=", sp.size(out));
append (sb, ", rx_expired=", rxExpired);
append (sb, ", rx_pending=", rxPending);
append (sb, ", rx_unhandled=", rxUnhandled);
append (sb, ", rx_forwarded=", rxForwarded);
sb.append (", connected=");
sb.append (Boolean.toString(isConnected()));
sb.append (", last=");
sb.append (lastTxn);
if (lastTxn > 0) {
sb.append (", idle=");
sb.append(System.currentTimeMillis() - lastTxn);
sb.append ("ms");
}
return sb.toString();
}
public int getTXCounter() {
return tx;
}
public int getRXCounter() {
return rx;
}
public long getLastTxnTimestampInMillis() {
return lastTxn;
}
public long getIdleTimeInMillis() {
return lastTxn > 0L ? System.currentTimeMillis() - lastTxn : -1L;
}
protected void processUnhandled (ISOMsg m) {
ISOSource source = m.getSource () != null ? m.getSource() : this;
Iterator iter = listeners.iterator();
if (iter.hasNext())
synchronized (this) { rxForwarded++; }
while (iter.hasNext())
if (((ISORequestListener)iter.next()).process (source, m))
return;
if (unhandled != null) {
synchronized (this) { rxUnhandled++; }
sp.out (unhandled, m, 120000);
}
}
private LocalSpace grabSpace (Element e)
throws ConfigurationException
{
String uri = e != null ? e.getText() : "";
Space sp = SpaceFactory.getSpace (uri);
if (sp instanceof LocalSpace) {
return (LocalSpace) sp;
}
throw new ConfigurationException ("Invalid space " + uri);
}
/**
* sends (or hands back) an ISOMsg
*
* @param m the Message to be sent
* @throws java.io.IOException
* @throws org.jpos.iso.ISOException
* @throws org.jpos.iso.ISOFilter.VetoException;
*/
public void send(ISOMsg m) throws IOException, ISOException {
if (!isConnected())
throw new ISOException ("MUX is not connected");
sp.out (out, m);
}
public boolean isConnected() {
if (running() && ready != null && ready.length > 0) {
for (String aReady : ready)
if (sp.rdp(aReady) != null)
return true;
return false;
}
return running();
}
public void dump (PrintStream p, String indent) {
p.println (indent + getCountersAsString());
}
private String[] toStringArray(String s, String delimiter, String def) {
if (s == null)
s = def;
String[] arr = null;
if (s != null && s.length() > 0) {
StringTokenizer st;
if (delimiter != null)
st = new StringTokenizer(s, delimiter);
else
st = new StringTokenizer(s);
List l = new ArrayList();
while (st.hasMoreTokens()) {
String t = st.nextToken();
if ("header".equalsIgnoreCase(t)) {
headerIsKey = true;
} else {
l.add (t);
}
}
arr = l.toArray(new String[l.size()]);
}
return arr;
}
private String[] toStringArray(String s) {
return toStringArray(s, null,null);
}
private boolean shouldIgnore (ISOMsg m) {
if (m != null && ignorerc != null
&& ignorerc.length() > 0 && m.hasField(39))
{
return ignorerc.contains(m.getString(39));
}
return false;
}
private void append (StringBuffer sb, String name, int value) {
sb.append (name);
sb.append (value);
}
public static class AsyncRequest implements Runnable {
ISOResponseListener rl;
Object handBack;
ScheduledFuture future;
public AsyncRequest (ISOResponseListener rl, Object handBack) {
super();
this.rl = rl;
this.handBack = handBack;
}
public void setFuture(ScheduledFuture future) {
this.future = future;
}
public void responseReceived (ISOMsg response) {
if (future == null || future.cancel(false))
rl.responseReceived (response, handBack);
}
public void run() {
rl.expired(handBack);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy