FileDocCategorySizeDatePackage
Protocol.javaAPI DocJ2ME CLDC 1.117525Wed Feb 05 15:56:00 GMT 2003com.sun.cldc.io.j2me.socket

Protocol.java

/*
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.cldc.io.j2me.socket;

import java.io.*;
import javax.microedition.io.*;
import com.sun.cldc.io.*;

/**
 * Connection to the J2ME socket API.
 *
 * @author  Nik Shaylor
 * @author  Efren Serra
 * @version 1.0 1/16/2000
 */

public class Protocol implements ConnectionBaseInterface, StreamConnection {

    /**********************************************************\
     * WARNING - 'handle' MUST be the first instance variable *
     *           It is used by native code that assumes this. *
    \**********************************************************/

    /** Socket object used by native code */
    int handle;

    /** Private variable the native code uses */
    int iocb;

    /** Access mode */
    private int mode;

    /** Open count */
    int opens = 0;

    /** Connection open flag */
    private boolean copen = false;

    /** Input stream open flag */
    protected boolean isopen = false;

    /** Output stream open flag */
    protected boolean osopen = false;

    /** Size of the read ahead / write behind buffers */
    public static int bufferSize = 0; /* Default is no buffering */

    /**
     * Class initializer
     */
    static {
        initializeInternal();

        /* See if a read ahead / write behind buffer size has been specified */
        String size = System.getProperty("com.sun.cldc.io.j2me.socket.buffersize");
        if (size != null) {
            try {
                bufferSize = Integer.parseInt(size);
            } catch(NumberFormatException ex) {}
        }
    }

    /* This class allows one to initialize the network, if necessary,
     * before any networking code is called
    */
    private static native void initializeInternal();

    /**
     * Open the connection
     */
    public void open(String name, int mode, boolean timeouts)
        throws IOException {

        throw new RuntimeException("Should not be called");
    }

    /**
     * Open the connection
     * @param name       the target for the connection
     * @param writeable  a flag that is true if the caller expects
     *                   to write to the connection
     * @param timeouts   a flag to indicate that the caller wants
     *                   timeout exceptions
     * <p>
     * The name string for this protocol should be:
     * "socket://<name or IP number>:<port number>
     *
     * We allow "socket://:nnnn" to mean "serversocket://:nnnn".
     * The native code will spot this and signal back with an
     * InterruptedException to tell the calling Java code about this.
     */
    public Connection openPrim(String name, int mode, boolean timeouts)
        throws IOException {

        try {
            open0(name, mode, timeouts);
            registerCleanup();
            opens++;
            copen = true;
            this.mode = mode;
            return this;
        } catch(InterruptedException x) {
//            com.sun.cldc.io.j2me.serversocket.Protocol con;
//            con = new com.sun.cldc.io.j2me.serversocket.Protocol();
//            con.open(name, mode, timeouts);
//            return con;
              return null;
        }
    }

    /**
     * Open the connection
     *
     * @param handle an already formed socket handle
     * <p>
     * This function is only used by com.sun.cldc.io.j2me.socketserver;
     */
    public void open(int handle, int mode) throws IOException {
        this.handle = handle;
        opens++;
        copen = true;
        this.mode = mode;
    }

    /**
     * Ensure connection is open
     */
    void ensureOpen() throws IOException {
        if (!copen) {
            throw new IOException("Connection closed");
        }
    }

    /**
     * Returns an input stream for this socket.
     *
     * @return     an input stream for reading bytes from this socket.
     * @exception  IOException  if an I/O error occurs when creating the
     *                          input stream.
     */
    synchronized public InputStream openInputStream() throws IOException {

        ensureOpen();

        if ((mode&Connector.READ) == 0) {
            throw new IOException("Connection not open for reading");
        }

        if (isopen) {
            throw new IOException("Input stream already opened");
        }

        isopen = true;
        InputStream in;

        if (bufferSize == 0) {
            in = new PrivateInputStream(this);
        } else {
            in = new PrivateInputStreamWithBuffer(this, bufferSize);
        }

        opens++;
        return in;
    }

    /**
     * Returns an output stream for this socket.
     *
     * @return     an output stream for writing bytes to this socket.
     * @exception  IOException  if an I/O error occurs when creating the
     *                          output stream.
     */
    synchronized public OutputStream openOutputStream() throws IOException {
        ensureOpen();

        if ((mode&Connector.WRITE) == 0) {
            throw new IOException("Connection not open for writing");
        }

        if (osopen) {
            throw new IOException("Output stream already opened");
        }

        osopen = true;
        OutputStream os;

        os = new PrivateOutputStream(this);

        opens++;
        return os;
    }

    /**
     * Open and return a data input stream for a connection.
     *
     * @return                 An input stream
     * @exception IOException  If an I/O error occurs
     */
    public DataInputStream openDataInputStream() throws IOException {
        return new DataInputStream(openInputStream());
    }

    /**
     * Open and return a data output stream for a connection.
     *
     * @return                 An input stream
     * @exception IOException  If an I/O error occurs
     */
    public DataOutputStream openDataOutputStream() throws IOException {
        return new DataOutputStream(openOutputStream());
    }

    /**
     * Close the connection.
     *
     * @exception  IOException  if an I/O error occurs when closing the
     *                          connection.
     */
    synchronized public void close() throws IOException {
        if (copen) {
            copen = false;
            realClose();
        }
    }

    /**
     * Close the connection.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    synchronized void realClose() throws IOException {
        if (--opens == 0) {
             close0();
        }
    }

   /*
    * A note about read0() and write0()
    *
    * These routines will return the number of bytes transferred. It this
    * value is zero then it means that the data could not be read or written
    * and the calling code should call Waiter.waitForIO() to let some other
    * thread run.
    */

    protected native void open0(String name, int mode, boolean timeouts)
        throws IOException, InterruptedException;
    protected native int  read0(byte b[], int off, int len)
        throws IOException;
    protected native int  write0(byte b[], int off, int len)
        throws IOException;

    protected native int  available0() throws IOException;
    protected native void close0() throws IOException;
    private native void registerCleanup();
}

/**
 * Input stream for the connection
 */
class PrivateInputStream extends InputStream {

    /**
     * The reference to the protocol.
     */
    protected Protocol parent;

    /**
     * The end of file flag.
     */
    boolean eof = false;

    /**
     * Single byte buffer.
     */
    byte[] bytebuf;

    /**
     * Constructor
     * @param pointer to the connection object
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public PrivateInputStream(Protocol parent) throws IOException {
        this.parent = parent;
    }

    /**
     * Check the stream is open
     *
     * @exception  IOException  if the stream is not open.
     */
    void ensureOpen() throws IOException {
        if (parent == null) {
            throw new IOException("Stream closed");
        }
    }

    /**
     * Reads the next byte of data from the input stream.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     */
    synchronized public int read() throws IOException {
        if (bytebuf == null) {
            bytebuf = new byte[1];
        }
        int res = read(bytebuf, 0, 1);
        if (res == 1) {
            return bytebuf[0] & 0xFF;
        } else {
            return res;
        }
    }

    /**
     * Reads up to <code>len</code> bytes of data from the input stream into
     * an array of bytes.
     * <p>
     * Polling the native code is done here to allow for simple
     * asynchronous native code to be written. Not all implementations
     * work this way (they block in the native code) but the same
     * Java code works for both.
     *
     * @param      b     the buffer into which the data is read.
     * @param      off   the start offset in array <code>b</code>
     *                   at which the data is written.
     * @param      len   the maximum number of bytes to read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     */
    synchronized public int read(byte b[], int off, int len)
        throws IOException {

        ensureOpen();
        if (eof) {
            return -1;
        }
        if (b == null) {
            throw new NullPointerException();
        }
        if (len == 0) {
            return 0;
        }
        if (len <0) {
            throw new IndexOutOfBoundsException();
        }
        for (;;) {
            int count = read1(b, off, len);
            if (parent == null) {
                throw new InterruptedIOException();
            }
            if (count != 0) {
                if (count < 0) {
                    eof = true;
                }
                return count;
            }
            Waiter.waitForIO(); /* Wait a while for I/O to become ready */
        }
    }

   /*
    * read1
    */
    protected int read1(byte b[], int off, int len) throws IOException {
        if (parent != null) {
            return parent.read0(b, off, len);
        } else {
            return -1;
        }
    }

    /**
     * Returns the number of bytes that can be read (or skipped over) from
     * this input stream without blocking by the next caller of a method for
     * this input stream.
     *
     * @return     the number of bytes that can be read from this input stream.
     * @exception  IOException  if an I/O error occurs.
     */
    synchronized public int available() throws IOException {
        ensureOpen();
        return parent.available0();
    }

    /**
     * Close the stream.
     *
     * @exception  IOException  if an I/O error occurs
     */
    public void close() throws IOException {
        if (parent != null) {
            ensureOpen();
            parent.realClose();
            parent.isopen = false;
            parent = null;
        }
    }
}

/**
 * Input stream for the connection
 */
class PrivateInputStreamWithBuffer extends PrivateInputStream {

    /**
     * The internal buffer array where the data is stored.
     * When necessary, it may be replaced by another array
     * of a different size.
     */
    protected byte buf[];

    /**
     * The index one greater than the index of the last valid
     * byte in the buffer.
     * This value is always in the range
     * <code>0</code> through <code>buf.length</code>;
     * elements <code>buf[0]</code> through <code>buf[count-1]
     * </code>contain buffered input data obtained
     * from the underlying input stream.
     */
    protected int count;

    /**
     * The current position in the buffer. This is the index
     * of the next character to be read from the
     * <code>buf</code> array.
     * <p>
     * This value is always in the range <code>0</code>
     * through <code>count</code>. If it is less
     * than <code>count</code>, then <code>buf[pos]</code>
     * is the next byte to be supplied as input;
     * if it is equal to <code>count</code>, then
     * the  next <code>read</code> or <code>skip</code>
     * operation will require more bytes to be
     * read from the contained input stream.
     */
    protected int pos;

    /**
     * Constructor
     * @param pointer to the connection object
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public PrivateInputStreamWithBuffer(Protocol parent, int bufferSize)
        throws IOException {

        super(parent);
        buf = new byte[bufferSize];
    }

   /*
    * read1
    */
    protected int read1(byte b[], int off, int len) throws IOException {
        if (count == 0) {
            if (len >= buf.length) {
                return super.read1(b, off, len);
            } else {
                pos = 0;
                int res = super.read1(buf, pos, buf.length);
                if (res <= 0) {
                    return res;
                } else {
                    count = res;
                }
            }
        }
        if (len > count) {
            len = count;
        }
        System.arraycopy(buf, pos, b, off, len);
        count -= len;
        pos   += len;
        return len;
    }

    /**
     * Returns the number of bytes that can be read (or skipped over) from
     * this input stream without blocking by the next caller of a method for
     * this input stream.
     *
     * @return     the number of bytes that can be read from this
     *             input stream.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized int available() throws IOException {
        ensureOpen();
        return count + super.available();
    }
}

/**
 * Output stream for the connection
 */
class PrivateOutputStream extends OutputStream {

    /**
     * Pointer to the connection
     */
    protected Protocol parent;

    /**
     * Single byte buffer.
     */
    byte[] bytebuf;

    /**
     * Constructor
     * @param pointer to the connection object
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public PrivateOutputStream(Protocol parent) throws IOException {
        this.parent = parent;
    }

    /**
     * Check the stream is open
     *
     * @exception  IOException  if it is not.
     */
    void ensureOpen() throws IOException {
        if (parent == null) {
            throw new IOException("Stream closed");
        }
    }

    /**
     * Writes the specified byte to this output stream.
     * <p>
     * Polling the native code is done here to allow for simple
     * asynchronous native code to be written. Not all implementations
     * work this way (they block in the native code) but the same
     * Java code works for both.
     *
     * @param      b   the <code>byte</code>.
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> may be thrown if the
     *             output stream has been closed.
     */
    synchronized public void write(int b) throws IOException {
        if (bytebuf == null) {
            bytebuf = new byte[1];
        }
        bytebuf[0] = (byte)b;
        write(bytebuf, 0, 1);
    }

    /**
     * Writes <code>len</code> bytes from the specified byte array
     * starting at offset <code>off</code> to this output stream.
     * <p>
     * Polling the native code is done here to allow for simple
     * asynchronous native code to be written. Not all implementations
     * work this way (they block in the native code) but the same
     * Java code works for both.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> is thrown if the output
     *             stream is closed.
     */
    synchronized public void write(byte b[], int off, int len)
        throws IOException {

        ensureOpen();
        if (b == null) {
            throw new NullPointerException();
        }
        if (len == 0) {
            return;
        }
        int n = 0;
        while (true) {
            int count = write1(b, off + n, len - n);
            n += count;
            if (n == len) {
                break;
            }
            if (count == 0) {
                Waiter.waitForIO(); /* Wait a while for I/O to become ready */
            }
        }
    }

   /**
    * write1
    */
    protected int write1(byte b[], int off, int len) throws IOException {
        if (parent == null) {
            throw new InterruptedIOException();
        }
        return parent.write0(b, off, len);
    }

    /**
     * Close the stream.
     *
     * @exception  IOException  if an I/O error occurs
     */
    public void close() throws IOException {
        if (parent != null) {
            ensureOpen();
            flush();
            parent.realClose();
            parent.osopen = false;
            parent = null;
        }
    }
}