001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.iso.channel;
020/**
021 * sends back requests, posibly applying filters
022 * @author <a href="mailto:apr@cs.com.uy">Alejandro P. Revilla</a>
023 * @version $Revision$ $Date$
024 * @since 1.2.2
025 */
026
027import org.jpos.iso.FilteredBase;
028import org.jpos.iso.ISOException;
029import org.jpos.iso.ISOMsg;
030import org.jpos.iso.ISOPackager;
031import org.jpos.util.*;
032
033import java.io.IOException;
034
035/**
036 * In-memory ISO channel that loops messages back through a {@link BlockingQueue},
037 * useful for testing without external network resources.
038 */
039public class LoopbackChannel extends FilteredBase implements LogSource {
040    boolean usable = true;
041    private int[] cnt;
042    String name;
043    BlockingQueue queue;
044    Logger logger;
045    String realm;
046
047    /** Default constructor. */
048    public LoopbackChannel () {
049        super();
050        cnt = new int[SIZEOF_CNT];
051        queue = new BlockingQueue();
052    }
053
054   /**
055    * setPackager is optional on LoopbackChannel, it is
056    * used for debugging/formating purposes only
057    */
058    public void setPackager(ISOPackager packager) {
059        // N/A
060    }
061    
062    public void connect () {
063        cnt[CONNECT]++;
064        usable = true;
065        setChanged();
066        notifyObservers();
067    }
068
069    /**
070     * disconnects ISOChannel
071     */
072    public void disconnect () {
073        usable = false;
074        setChanged();
075        notifyObservers();
076    }
077
078    public void reconnect() {
079        usable = true;
080        setChanged();
081        notifyObservers();
082    }
083
084    public boolean isConnected() {
085        return usable;
086    }
087
088    public void send (ISOMsg m)
089        throws IOException,ISOException {
090        if (!isConnected())
091            throw new ISOException ("unconnected ISOChannel");
092        LogEvent evt = new LogEvent (this, "loopback-send", m);
093        m = applyOutgoingFilters (m, evt);
094        queue.enqueue (m);
095        cnt[TX]++;
096        notifyObservers();
097        Logger.log (evt);
098    }
099    
100    public void send (byte[] b)
101    throws IOException,ISOException {
102    if (!isConnected())
103        throw new ISOException ("unconnected ISOChannel");
104    LogEvent evt = new LogEvent (this, "loopback-send", b);
105    queue.enqueue (b);
106    cnt[TX]++;
107    notifyObservers();
108    Logger.log (evt);
109}
110
111    public ISOMsg receive() throws IOException, ISOException
112    {
113        if (!isConnected())
114            throw new ISOException ("unconnected ISOChannel");
115        try {
116            ISOMsg m = (ISOMsg) ((ISOMsg) queue.dequeue()).clone();
117            LogEvent evt = new LogEvent (this, "loopback-receive", m);
118            m = applyIncomingFilters (m, evt);
119            cnt[RX]++;
120            notifyObservers();
121            Logger.log (evt);
122            return m;
123        } catch (InterruptedException e) {
124            throw new IOException (e.toString());
125        } catch (BlockingQueue.Closed e) {
126            throw new IOException (e.toString());
127        }
128    }
129
130    public void setUsable(boolean usable) {
131        this.usable = usable;
132        setChanged();
133        notifyObservers();
134    }
135
136    /**
137     * Returns the live counter array (transmitted/received tallies).
138     *
139     * @return counter array as managed by this channel
140     */
141    public int[] getCounters() {
142        return cnt;
143    }
144
145    public void setName (String name) {
146        this.name = name;
147        NameRegistrar.register ("channel."+name, this);
148    }
149
150    public String getName() {
151        return name;
152    }
153
154    /**
155     * Returns the packager configured on this channel.
156     *
157     * @return {@code null}; the loopback channel has no packager because messages are queued as objects
158     */
159    public ISOPackager getPackager() {
160        return null;
161    }
162
163    /** Resets the connect/transmit/receive counters to zero. */
164    public void resetCounters() {
165        for (int i=0; i<SIZEOF_CNT; i++)
166            cnt[i] = 0;
167    }
168    public void setLogger (Logger logger, String realm) {
169        this.logger = logger;
170        this.realm  = realm;
171    }
172    public String getRealm () {
173        return realm;
174    }
175    public Logger getLogger() {
176        return logger;
177    }
178}