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
021import org.jpos.core.Configurable;
022import org.jpos.core.Configuration;
023import org.jpos.core.ConfigurationException;
024import org.jpos.iso.ISOChannel;
025import org.jpos.iso.ISOException;
026import org.jpos.iso.ISOMsg;
027import org.jpos.iso.ISOPackager;
028import org.jpos.util.LogEvent;
029import org.jpos.util.LogSource;
030import org.jpos.util.Logger;
031import org.jpos.util.NameRegistrar;
032
033import java.io.IOException;
034import java.util.List;
035import java.util.Vector;
036import java.util.concurrent.locks.Lock;
037import java.util.concurrent.locks.ReentrantLock;
038
039/**
040 * A pool of {@link ISOChannel} instances; tries each in order until one connects.
041 */
042@SuppressWarnings("unchecked")
043public class ChannelPool implements ISOChannel, LogSource, Configurable, Cloneable {
044    /** Whether this pool is in a usable state. */
045    boolean usable = true;
046    /** Registered name of this pool. */
047    String name = "";
048    /** Logger for this pool. */
049    protected Logger logger;
050    /** Log realm for this pool. */
051    protected String realm;
052    Configuration cfg = null;
053    List pool;
054    ISOChannel current;
055    Lock lock = new ReentrantLock();
056
057    /** Default constructor. */
058    public ChannelPool () {
059        super ();
060        pool = new Vector ();
061    }
062    public void setPackager(ISOPackager p) {
063        // nothing to do
064    }
065    public void connect () throws IOException {
066        lock.lock();
067        try {
068            current = null;
069            LogEvent evt = new LogEvent (this, "connect");
070            evt.addMessage ("pool-size=" + Integer.toString (pool.size()));
071            for (int i=0; i<pool.size(); i++) {
072                try {
073                    evt.addMessage ("pool-" + Integer.toString (i));
074                    ISOChannel c = (ISOChannel) pool.get (i);
075                    c.connect ();
076                    if (c.isConnected()) {
077                        current = c;
078                        usable = true;
079                        break;
080                    }
081                } catch (IOException e) {
082                    evt.addMessage (e);
083                }
084            }
085            if (current == null)
086                evt.addMessage ("connect failed");
087            Logger.log (evt);
088            if (current == null) {
089                throw new IOException ("unable to connect");
090            }
091        } finally {
092            lock.unlock();
093        }
094    }
095    public void disconnect () throws IOException {
096        lock.lock();
097        try {
098            current = null;
099            LogEvent evt = new LogEvent (this, "disconnect");
100            for (Object aPool : pool) {
101                try {
102                    ISOChannel c = (ISOChannel) aPool;
103                    c.disconnect();
104                } catch (IOException e) {
105                    evt.addMessage(e);
106                }
107            }
108            Logger.log (evt);
109        } finally {
110            lock.unlock();
111        }
112
113    }
114    public void reconnect() throws IOException {
115        lock.lock();
116        try {
117            disconnect ();
118            connect ();
119        } finally {
120            lock.unlock();;
121        }
122    }
123    public boolean isConnected() {
124        lock.lock();
125        try {
126            return getCurrent().isConnected ();
127        } catch (IOException e) {
128            return false;
129        } finally {
130            lock.unlock();
131        }
132    }
133    public ISOMsg receive() throws IOException, ISOException {
134        return getCurrent().receive ();
135    }
136    public void send (ISOMsg m) throws IOException, ISOException {
137        getCurrent().send (m);
138    }
139    public void send (byte[] b) throws IOException, ISOException {
140        getCurrent().send (b);
141    }
142    public void setUsable(boolean b) {
143        this.usable = b;
144    }
145    public void setName (String name) {
146        this.name = name;
147        NameRegistrar.register ("channel."+name, this);
148    }
149    public String getName() {
150        return this.name;
151    }
152    public ISOPackager getPackager () {
153        return null;
154    }
155    public void setLogger (Logger logger, String realm) {
156        this.logger = logger;
157        this.realm  = realm;
158    }
159    public String getRealm () {
160        return realm;
161    }
162    public Logger getLogger() {
163        return logger;
164    }
165    public void setConfiguration (Configuration cfg)
166        throws ConfigurationException
167    {
168        this.cfg = cfg;
169        String channelName[] = cfg.getAll ("channel");
170        for (String aChannelName : channelName) {
171            try {
172                addChannel(aChannelName);
173            } catch (NameRegistrar.NotFoundException e) {
174                throw new ConfigurationException(e);
175            }
176        }
177    }
178    /**
179     * Adds a channel to the pool.
180     * @param channel the channel to add
181     */
182    public void addChannel (ISOChannel channel) {
183        pool.add (channel);
184    }
185    /**
186     * Adds a channel to the pool by its registered name.
187     * @param name the NameRegistrar name of the channel to add
188     * @throws NameRegistrar.NotFoundException if name not found
189     */
190    public void addChannel (String name) 
191        throws NameRegistrar.NotFoundException
192    {
193        pool.add (NameRegistrar.get ("channel."+name));
194    }
195    /**
196     * Removes a channel from the pool.
197     * @param channel the channel to remove
198     */
199    public void removeChannel (ISOChannel channel) {
200        pool.remove (channel);
201    }
202    /**
203     * Removes a channel from the pool by its registered name.
204     * @param name the channel name to remove
205     * @throws NameRegistrar.NotFoundException if name not found
206     */
207    public void removeChannel (String name) throws NameRegistrar.NotFoundException {
208        pool.remove (NameRegistrar.get ("channel."+name));
209    }
210    /**
211     * Returns the number of channels in the pool.
212     * @return channel count
213     */
214    public int size() {
215        return pool.size();
216    }
217    /** Returns the currently active channel, trying to connect if necessary.
218     * @return the active ISOChannel
219     * @throws IOException if no channel can be connected
220     */
221    public ISOChannel getCurrent () throws IOException {
222        lock.lock();
223        try {
224            if (current == null)
225                connect();
226            else if (!usable)
227                reconnect();
228        } finally {
229            lock.unlock();
230        }
231        return current;
232    }
233    
234    public Object clone(){
235      try {
236        return super.clone();
237      } catch (CloneNotSupportedException e) {
238        throw new InternalError();
239      }
240    }
241}
242