FileDocCategorySizeDatePackage
ASClientSocketImpl.javaAPI DocGlassfish v2 API18433Tue May 29 01:53:52 BST 2007com.sun.enterprise.server.ss.provider

ASClientSocketImpl.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package com.sun.enterprise.server.ss.provider;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;

import java.util.logging.*;
import com.sun.enterprise.server.ss.spi.ASSocketFacadeUtils;
import com.sun.enterprise.server.ss.spi.ASSocketServiceFacade;
import com.sun.logging.LogDomains;

/**
 * NIO based SocketImpl implementation used by java.net.Socket. The implementation
 * is used always in the non-blocking mode.
 */

final class ASClientSocketImpl extends java.net.SocketImpl {
    private static final Logger logger = LogDomains.getLogger(LogDomains.CORE_LOGGER);

    private SocketChannel sc;
    private Socket sock;
    private InputStream is;

    private boolean usePlainSockets = true;
    private ASPlainSocketImpl ps = null;

    public int available() throws IOException {
        if (usePlainSockets) {
            return getPlainSocket().available();
        }
        return 0;
    }

    /**
     * Apart from closing the internal socket ant it's channel,
     * we need to shutdown the output also. This is necessary because
     * 1) A normal socket's InputStream will get an EOF, when its remote
     *    socket closes.
     * 2) But in case of non blocking IO, if a selector is registered
     *    on the channel with OP_WRITE key, the output of the socket 
     *    doesnt get shutdown immediately even when the socket/channel 
     *    is closed. This could lead to its corrsponding InputStream
     *    longer to get closed.
     * We explicitely shutdown to fix this problem.
     * 
     * Note that, if a program, expects its output stream to be open
     * for a few microseconds after the socket is closed and try to do
     * some smart stuff, may not work correctly. But that is a very 
     * very very rare case.
     */
    public void close() throws IOException {
        if (usePlainSockets) {
            getPlainSocket().close();
            return;
        }
        if (sock != null) {
            try {
                sock.shutdownOutput();
            } catch (Exception e) {
                if ( logger.isLoggable(Level.FINER) ) {
                    logger.log(Level.FINER, "" + e.getMessage(), e);
                }
            }
            try {
	        sock.close();
                sc.close();
            } catch (IOException ie) {
                if ( logger.isLoggable(Level.FINER) ) {
                    logger.log(Level.FINER, "" + ie.getMessage(), ie);
                }
                //throw ie;
            } catch (Error er) {
                if ( logger.isLoggable(Level.FINE) ) {
                    logger.log(Level.FINE, "" + er.getMessage(), er);
                }
                // This ideally should not happen...
            }
            is = null;
        }
    }

    public InetAddress getInetAddress() {
        if (usePlainSockets) {
            return getPlainSocket().getInetAddress();
        }
        try {
            return getClientSocket().getInetAddress();
        } catch (IOException ie) {
            if ( logger.isLoggable(Level.FINE) ) {
                 logger.log(Level.FINE, "" + ie.getMessage(), ie);
            }
            // Typically this will never be executed.
            return super.getInetAddress();
        }
    }


    public int getLocalPort() {
        if (usePlainSockets) {
            return getPlainSocket().getLocalPort();
        }
        try {
            return getClientSocket().getLocalPort();
        } catch (IOException ie) {
            if ( logger.isLoggable(Level.FINE) ) {
                 logger.log(Level.FINE, "" + ie.getMessage(), ie);
            }
            // Typically this will never be executed.
            return super.getLocalPort();
        }
    }

    public int getPort() {

        if (usePlainSockets) {
            return getPlainSocket().getPort();
        }

        try {
            return getClientSocket().getPort();
        } catch (IOException ie) {
            if ( logger.isLoggable(Level.FINE) ) {
                 logger.log(Level.FINE, "" + ie.getMessage(), ie);
            }
            // Typically this will never be executed.
            return super.getPort();
        }
    }

    public void shutdownInput() throws IOException {
        if (usePlainSockets) {
            getPlainSocket().shutdownInput();
            return;
        }
        getClientSocket().shutdownInput();
    }

    public void shutdownOutput() throws IOException {
        if (usePlainSockets) {
            getPlainSocket().shutdownOutput();
            return;
        }
        getClientSocket().shutdownOutput();
    }

    public boolean supportsUrgentData() {
        if (usePlainSockets) {
            return getPlainSocket().supportsUrgentData();
        }
        return true;
    }

    public void sendUrgentData(int i) throws IOException {
        if (usePlainSockets) {
            getPlainSocket().sendUrgentData(i);
            return;
        }
        getClientSocket().sendUrgentData(i);
    }

    public void listen(int i) throws IOException {
	throw new UnsupportedOperationException(
		    "listen() not supported in ASClientSocketImpl");
    }

    public void create(boolean stream) throws IOException {
        if (usePlainSockets) {
            getPlainSocket().create(stream);
        }
    }
    
    public java.io.InputStream getInputStream() throws IOException {
        if (usePlainSockets) {
            return getPlainSocket().getInputStream();
        }
        if (this.is == null) {
            this.is = new ASInputStream(getClientSocketChannel(), getClientSocket()); 
        }
        return this.is;
    }

    public java.io.OutputStream getOutputStream() throws IOException {
        if (usePlainSockets) {
            return getPlainSocket().getOutputStream();
        }
        return new ASOutputStream(getClientSocketChannel(), getClientSocket());
    }

    public void bind(java.net.InetAddress ia, int port) throws IOException {
        if (usePlainSockets) {
            getPlainSocket().bind(ia, port);
            return;
        }
        InetSocketAddress isa = new InetSocketAddress(ia, port);
        getClientSocket().bind(isa);
    }

    public void connect(java.lang.String host, int port) throws IOException {
        if (usePlainSockets) {
            getPlainSocket().connect(host, port);
            return;
        }
        InetSocketAddress isa = new InetSocketAddress(host, port);
        if ( logger.isLoggable(Level.FINE) ) {
            logger.fine("In ASClientSocketImpl.connect, host = "+host
	 					     +" port = "+port);
        }
        connect(isa);
    }

    public void connect(java.net.InetAddress ia, int port) throws IOException {
        if (usePlainSockets) {
            getPlainSocket().connect(ia, port);
            return;
        }
        InetSocketAddress isa = new InetSocketAddress(ia, port);
        if ( logger.isLoggable(Level.FINE) ) {
	    logger.fine("In ASClientSocketImpl.connect, host = "+ia.getHostName()
							       +" port = "+port);
        }
        connect(isa);
    }

    private void connect(InetSocketAddress isa) throws IOException {
        connect(isa,0);
    }

    public void connect(java.net.SocketAddress sa, int timeout) throws IOException {
        if (usePlainSockets) {
            getPlainSocket().connect(sa, timeout);
            return;
        }
        if (sa == null || !(sa instanceof InetSocketAddress)) {
            throw new IllegalArgumentException("unsupported address type");
	}
	
        InetSocketAddress addr = (InetSocketAddress) sa;
        if (addr.isUnresolved()) {
            throw new UnknownHostException(addr.getHostName());
	}
        this.port = addr.getPort();
        this.address = addr.getAddress();

        // Check whether the socketservice is notified. If it is not, then
        // this is the first connection to the service. This will trigger
        // the startup. So, this connection should be serviced only after
        // the completion of service startup.
        boolean waitForStartupReqd = ! ASSocketFacadeUtils.getASSocketService().
                                      socketServiceNotified(this.port);

        SocketChannel ch = getClientSocketChannel();
        boolean connected = ch.connect(sa);
	if ( !connected ) {
	    waitForSelect(timeout);
	    if (ch.finishConnect() == false) {
		throw new IOException("Connection timed out");
	    }
	}

        if (ASSocketFacadeUtils.getASSocketService().
        isLocalClient(address) == false) {
            return;
        }

	// Inform the ASSocketService about this socket, so that it can
	// correctly block remote sockets while allowing local loopback sockets.
	// Note: this needs to be called when NIO sockets are created too,
	// for now we dont do it because the Windows NIO impl does not allow 
	// SocketChannel to be wrapped (it casts the SocketChannel to an
	// internal class).
        Socket connectedSocket = sc.socket();
        if (waitForStartupReqd) {
            ASSocketFacadeUtils.getASSocketService().
            waitOnClientConnection(connectedSocket.getPort());
        }
	ASSocketFacadeUtils.getASSocketService().clientSocketConnected(
             connectedSocket.getPort(), connectedSocket.getLocalPort());
    }

    public void accept(java.net.SocketImpl si) throws IOException {
	throw new UnsupportedOperationException(
		    "accept() not supported in ASClientSocketImpl");
    }

    public void setOption(int opt,java.lang.Object val) throws SocketException {
        if (usePlainSockets) {
            getPlainSocket().setOption(opt, val);
            return;
        }
	try {
	    switch (opt) {
	    case SO_LINGER:
		if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
		    throw new SocketException("Bad parameter for option");
		if (val instanceof Boolean) {
		    if ( ((Boolean)val).booleanValue() == true )
			throw new SocketException("Bad parameter for option");
		    getClientSocket().setSoLinger( false, 0); 
		} else {
		    getClientSocket().setSoLinger( true, ((Integer) val).intValue());
		}
		break;
	    case SO_TIMEOUT:
		if (val == null || (!(val instanceof Integer)))
		    throw new SocketException("Bad parameter for SO_TIMEOUT");
		int tmp = ((Integer) val).intValue();
		if (tmp < 0)
		    throw new IllegalArgumentException("timeout < 0");
		//timeout = tmp;
		getClientSocket().setSoTimeout( tmp );
		break;
	    case IP_TOS:
		 if (val == null || !(val instanceof Integer)) {
		     throw new SocketException("bad argument for IP_TOS");
		 }
		 int trafficClass = ((Integer)val).intValue();
		 getClientSocket().setTrafficClass( trafficClass );
		 break;
	    case SO_BINDADDR:
		throw new SocketException("Cannot re-bind socket");
	    case TCP_NODELAY:
		if (val == null || !(val instanceof Boolean))
		    throw new SocketException("bad parameter for TCP_NODELAY");
		
		getClientSocket().setTcpNoDelay( ((Boolean)val).booleanValue() );
		break;
	    case SO_SNDBUF:
		if (val == null || !(val instanceof Integer) ||
		    !(((Integer)val).intValue() > 0)) {
		    throw new SocketException("bad parameter for SO_SNDBUF " );
		}
		getClientSocket().setSendBufferSize( ((Integer) val).intValue() );
		break;
	    case SO_RCVBUF:
		if (val == null || !(val instanceof Integer) ||
		    !(((Integer)val).intValue() > 0)) {
		    throw new SocketException("bad parameter for SO_SNDBUF " +
					      "or SO_RCVBUF");
		}
		getClientSocket().setReceiveBufferSize( ((Integer) val).intValue() );
		break;
	    case SO_KEEPALIVE:
		if (val == null || !(val instanceof Boolean))
		    throw new SocketException("bad parameter for SO_KEEPALIVE");
		getClientSocket().setKeepAlive( ((Boolean)val).booleanValue() );
		break;
	    case SO_OOBINLINE:
		if (val == null || !(val instanceof Boolean))
		    throw new SocketException("bad parameter for SO_OOBINLINE");
		getClientSocket().setOOBInline( ((Boolean)val).booleanValue() );
		break;
	    case SO_REUSEADDR:
		if (val == null || !(val instanceof Boolean)) 
		    throw new SocketException("bad parameter for SO_REUSEADDR");
		getClientSocket().setReuseAddress( ((Boolean)val).booleanValue() );
		break;
	    default:
		throw new SocketException("unrecognized TCP option: " + opt);
	    }

	} catch ( IOException ioex ) {
	    if ( ioex instanceof SocketException )
		throw ((SocketException)ioex);
	    else
		throw (SocketException)(new SocketException()).initCause(ioex);
	}
    }

    public Object getOption(int opt) throws SocketException {
        if (usePlainSockets) {
            return getPlainSocket().getOption(opt);
        }
        try {
            switch (opt) {
                case SO_LINGER:
                    return new Integer(getClientSocket().getSoLinger());
                case SO_TIMEOUT:
                    return new Integer(getClientSocket().getSoTimeout());
                case IP_TOS:
                    return new Integer(getClientSocket().getTrafficClass());
                case SO_BINDADDR:
	            return getClientSocket().getInetAddress();
                case TCP_NODELAY:
                    return new Boolean(getClientSocket().getTcpNoDelay());
                case SO_SNDBUF:
                    return new Integer(getClientSocket().getSendBufferSize());
                case SO_RCVBUF:
                    return 
                    new Integer(getClientSocket().getReceiveBufferSize());
                case SO_KEEPALIVE:
                    return new Boolean(getClientSocket().getKeepAlive());
                case SO_OOBINLINE:
                    return new Boolean(getClientSocket().getOOBInline());
                case SO_REUSEADDR:
                    return new Boolean(getClientSocket().getReuseAddress());
                default:
                    throw 
                    new SocketException("unrecognized TCP option: " + opt);
            }
        } catch (SocketException se) {
            throw se;
        } catch (IOException ie) {
            throw (SocketException)
            (new SocketException(ie.getMessage())).initCause(ie);
        }
    }

    private Socket getClientSocket() throws IOException {
	if ( sock == null ) {
            createSocket();
	}
	return sock;
    }

    private SocketChannel getClientSocketChannel() throws IOException {
	if ( sc == null ) {
            createSocket();
	}
	return sc;
    }

    private void createSocket() throws IOException {
	sc = SocketChannel.open();
        sc.configureBlocking(false);
	sock = sc.socket();
    }


    private void waitForSelect(long timeout) throws IOException {
        Selector selector = Selector.open();
        this.sc.register(selector, SelectionKey.OP_CONNECT); 
        
        selectorblock:
            while (true) {
                try {
                    int n = selector.select(timeout);
                    if (n==0 && this.sc.finishConnect()) {
                        break;
                    }
                    Iterator it = selector.selectedKeys().iterator();
                    while (it.hasNext()) {
                        SelectionKey selKey = (SelectionKey)it.next();
                        if (selKey.isValid() && selKey.isConnectable()) {
                            it.remove();
                            break selectorblock;
                        }
                    }
                } catch (Exception e) {
                    throw (IOException) (new IOException()).initCause(e);
                }
            }
        try {
            selector.close();
        } catch (IOException ie) {
            if ( logger.isLoggable(Level.FINE) ) {
                logger.log(Level.FINE, ie.getMessage(), ie);
            }
        }
    }

    // Called from ASServerSocketImpl
    void setClientSocket(Socket s) throws IOException {
        usePlainSockets = false;
	sock = s;
	sc = sock.getChannel();
        sc.configureBlocking(false);
        localport = sock.getLocalPort();
        port = sock.getPort();
        address = sock.getInetAddress();
    }

    /**
     * The plain socket implementation will be used for
     * any outbound communication will use this socketimpl.
     */
    ASPlainSocketImpl getPlainSocket() {
        if (ps == null) {
            ps = new ASPlainSocketImpl();
        }
        return ps;
    }

    protected void finalize() throws Throwable {
        try {
            close();
        } catch (Throwable t) {}
    }
    
}