/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ 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. * * @version 1.0 1/16/2000 */ public class Protocol implements ConnectionBaseInterface, StreamConnection { /** Socket object used by native code */ int handle; /** 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; /** * Open the connection */ public void open(String name, int mode, boolean timeouts) throws IOException { throw new RuntimeException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Should not be called" /* #endif */ ); } /** * Open the connection * @param name the target for the connection. It must be in this * format: "//<name or IP number>:<port number>" * @param mode read/write mode of the connection (currently ignored). * @param timeouts A flag to indicate that the called wants timeout * exceptions (currently ignored). */ public Connection openPrim(String name, int mode, boolean timeouts) throws IOException { if (!name.startsWith("//")) { throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "bad socket connection name: " + name /* #endif */ ); } int i = name.indexOf(':'); if (i < 0) { throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "bad socket connection name: port missing" /* #endif */ ); } String hostname = name.substring(2, i); int port; try { port = Integer.parseInt(name.substring(i+1)); } catch (NumberFormatException e) { throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "bad socket connection name: bad port" /* #endif */ ); } // cstring is always NUL terminated (note the extra byte allocated). // This avoids awkward char array manipulation in C code. byte cstring[] = new byte[hostname.length() + 1]; for (int n=0; n<hostname.length(); n++) { cstring[n] = (byte)(hostname.charAt(n)); } if ((this.handle = open0(cstring, port, mode)) < 0) { int errorCode = this.handle & 0x7fffffff; throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "connection failed: error = " + errorCode /* #endif */ ); } opens++; copen = true; this.mode = mode; return this; } /** * 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( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Connection closed" /* #endif */ ); } } /** * 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( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Connection not open for reading" /* #endif */ ); } if (isopen) { throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Input stream already opened" /* #endif */ ); } isopen = true; InputStream in = new PrivateInputStream(this); 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( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Connection not open for writing" /* #endif */ ); } if (osopen) { throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Output stream already opened" /* #endif */ ); } osopen = true; OutputStream os = new PrivateOutputStream(this); opens++; return os; } /** * 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(this.handle); } } /** * 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()); } /* * A note about readByte() * * This function will return an unsigned byte, or -1. * -1 means that EOF was reached. */ protected static native int open0(byte hostname[], int port, int mode); protected static native int readBuf(int handle, byte b[], int off, int len); protected static native int readByte(int handle); protected static native int writeBuf(int handle, byte b[], int off, int len); protected static native int writeByte(int handle, int b); protected static native int available0(int handle); protected static native void close0(int handle); } /** * Input stream for the connection */ class PrivateInputStream extends InputStream { /** * Pointer to the connection */ private Protocol parent; /** * End of file flag */ boolean eof = false; /** * 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 it is not. */ void ensureOpen() throws IOException { if (parent == null) { throw new IOException( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Stream closed" /* #endif */ ); } } /** * Reads the next byte of data from the input 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. * * @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 { int res; ensureOpen(); if (eof) { return -1; } res = Protocol.readByte(parent.handle); if (res == -1) { eof = true; } if (parent == null) { throw new InterruptedIOException(); } 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 (len == 0) { return 0; } // Check for array index out of bounds, and NullPointerException, // so that the native code doesn't need to do it int test = b[off] + b[off + len - 1]; int n = 0; while (n < len) { int count = Protocol.readBuf(parent.handle, b, off + n, len - n); if (count == -1) { eof = true; if (n == 0) { n = -1; } break; } n += count; if (n == len) { break; } } if (parent == null) { throw new InterruptedIOException(); } return n; } /** * 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 Protocol.available0(parent.handle); } /** * 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; } } } /** * Output stream for the connection */ class PrivateOutputStream extends OutputStream { /** * Pointer to the connection */ private Protocol parent; /** * 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( /* #ifdef VERBOSE_EXCEPTIONS */ /// skipped "Stream closed" /* #endif */ ); } } /** * 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 { ensureOpen(); while (true) { int res = Protocol.writeByte(parent.handle, b); if (res != 0) { // IMPL_NOTE: should EOFException be thrown if write fails? return; } } } /** * 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 (len == 0) { return; } // Check for array index out of bounds, and NullPointerException, // so that the native code doesn't need to do it int test = b[off] + b[off + len - 1]; int n = 0; while (true) { n += Protocol.writeBuf(parent.handle, b, off + n, len - n); if (n == len) { break; } } } /** * Close the stream. * * @exception IOException if an I/O error occurs */ public void close() throws IOException { if (parent != null) { ensureOpen(); parent.realClose(); parent.osopen = false; parent = null; } } }