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.iso.*;
022import org.jpos.core.Configuration;
023import org.jpos.core.ConfigurationException;
024
025import java.io.IOException;
026import java.net.ServerSocket;
027import java.math.BigInteger;
028
029/**
030 * ISOChannel implementation suitable for OASIS Ltd &copy; hosts<br>
031 * Message length header: n ASCII digits, configurable by setLengthDigits() (default: 4)
032 * or the 'length-digits' Configuration property.
033 *
034 * @author apr@cs.com.uy
035 * @version $Id$
036 * @see ISOMsg
037 * @see ISOException
038 * @see ISOChannel
039 */
040public class ASCIIChannel extends BaseChannel {
041
042    /** Number of digits for the message length header */
043    protected int lengthDigits= 4;                                      // 4 is default
044
045    private static final BigInteger ten= BigInteger.valueOf(10L);     // just a static 10
046
047    /**
048     * Public constructor (used by Class.forName("...").newInstance())
049     */
050    public ASCIIChannel () {
051        super();
052    }
053    /**
054     * Construct client ISOChannel
055     * @param host  server TCP Address
056     * @param port  server port number
057     * @param p     an ISOPackager
058     * @see ISOPackager
059     */
060    public ASCIIChannel (String host, int port, ISOPackager p) {
061        super(host, port, p);
062    }
063    /**
064     * Construct server ISOChannel
065     * @param p     an ISOPackager
066     * @exception IOException on I/O failure
067     * @see ISOPackager
068     */
069    public ASCIIChannel (ISOPackager p) throws IOException {
070        super(p);
071    }
072    /**
073     * constructs a server ISOChannel associated with a Server Socket
074     * @param p     an ISOPackager
075     * @param serverSocket where to accept a connection
076     * @exception IOException on I/O failure
077     * @see ISOPackager
078     */
079    public ASCIIChannel (ISOPackager p, ServerSocket serverSocket)
080        throws IOException
081    {
082        super(p, serverSocket);
083    }
084
085
086    /** Sets the number of ASCII digits used for the message length prefix.
087     * @param len number of length digits
088     */
089    public void setLengthDigits(int len) { lengthDigits= len; }
090    /** Returns the number of ASCII digits used for the message length prefix.
091     * @return number of length digits
092     */
093    public int getLengthDigits() { return lengthDigits; }
094
095
096    /**
097     * @param len the packed Message len
098     * @exception IOException on I/O failure
099     */
100    protected void sendMessageLength(int len) throws IOException {
101        int maxLen= ten.pow(lengthDigits).intValue() - 1;       // 10^lengthDigits - 1
102
103        if (len > maxLen)
104            throw new IOException ("len exceeded ("+len+" > "+maxLen+")");
105        else if (len < 0)
106            throw new IOException ("invalid negative length ("+len+")");
107        serverOut.write(ISOUtil.zeropad(len, lengthDigits).getBytes());
108    }
109    /**
110     * @return the Message len
111     * @exception IOException on I/O failure
112     * @exception ISOException on ISO packing/unpacking failure
113     */
114    protected int getMessageLength() throws IOException, ISOException {
115        int l = 0;
116        byte[] b = new byte[lengthDigits];
117        while (l == 0) {
118            serverIn.readFully(b, 0, lengthDigits);
119            try {
120                if ((l=Integer.parseInt(new String(b))) == 0 &&
121                    isExpectKeepAlive()) {
122
123                    serverOutLock.lock();
124                    try {
125                        serverOut.write(b);
126                        serverOut.flush();
127                    } finally {
128                        serverOutLock.unlock();
129                    }
130                }
131            } catch (NumberFormatException e) {
132                throw new ISOException ("Invalid message length "+new String(b));
133            }
134        }
135        return l;
136    }
137
138
139    /**
140     *
141     * Calls super.setConfiguration() and then reads the 'length-digits' property,
142     * defaulting to 4
143     *
144     * @param cfg Configuration
145     * @throws ConfigurationException if the configuration is invalid
146     */
147    @Override
148    public void setConfiguration (Configuration cfg) throws ConfigurationException
149    {
150        super.setConfiguration(cfg);
151        setLengthDigits(cfg.getInt("length-digits", 4));
152    }
153}
154