Package org.jpos.q2.iso

Source Code of org.jpos.q2.iso.QMUX$AsyncRequest

/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2014 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 <http://www.gnu.org/licenses/>.
*/

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<ISORequestListener> listeners;
    int rx, tx, rxExpired, txExpired, rxPending, rxUnhandled, rxForwarded;
    long lastTxn = 0L;
    boolean listenerRegistered;
    public QMUX () {
        super ();
        listeners = new ArrayList<ISORequestListener>();
    }
    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<mtiMapping.length; i++) {
                    int c = mti.charAt (i) - '0';
                    if (c >= 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<String> l = new ArrayList<String>();
            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);
        }
    }
}
TOP

Related Classes of org.jpos.q2.iso.QMUX$AsyncRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.