alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (SocketTransportService.java)

This example Java source code file (SocketTransportService.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

closedconnectionexception, eof, illegalargumentexception, inetsocketaddress, invalid, ioexception, jdi, net, network, numberformatexception, object, serversocket, socketconnection, socketlistenkey, sockettimeoutexception, sockettransportservicecapabilities, string, util

The SocketTransportService.java Java example source code

/*
 * Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 in the LICENSE file that
 * accompanied this code).
 *
 * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.jdi;

import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.connect.spi.*;
import java.net.*;
import java.io.*;
import java.util.Map;
import java.util.ResourceBundle;

/*
 * A transport service based on a TCP connection between the
 * debugger and debugee.
 */

public class SocketTransportService extends TransportService {
    private ResourceBundle messages = null;

    /**
     * The listener returned by startListening encapsulates
     * the ServerSocket.
     */
    static class SocketListenKey extends ListenKey {
        ServerSocket ss;

        SocketListenKey(ServerSocket ss) {
            this.ss = ss;
        }

        ServerSocket socket() {
            return ss;
        }

        /*
         * Returns the string representation of the address that this
         * listen key represents.
         */
        public String address() {
            InetAddress address = ss.getInetAddress();

            /*
             * If bound to the wildcard address then use current local
             * hostname. In the event that we don't know our own hostname
             * then assume that host supports IPv4 and return something to
             * represent the loopback address.
             */
            if (address.isAnyLocalAddress()) {
                try {
                    address = InetAddress.getLocalHost();
                } catch (UnknownHostException uhe) {
                    byte[] loopback = {0x7f,0x00,0x00,0x01};
                    try {
                        address = InetAddress.getByAddress("127.0.0.1", loopback);
                    } catch (UnknownHostException x) {
                        throw new InternalError("unable to get local hostname");
                    }
                }
            }

            /*
             * Now decide if we return a hostname or IP address. Where possible
             * return a hostname but in the case that we are bound to an
             * address that isn't registered in the name service then we
             * return an address.
             */
            String result;
            String hostname = address.getHostName();
            String hostaddr = address.getHostAddress();
            if (hostname.equals(hostaddr)) {
                if (address instanceof Inet6Address) {
                    result = "[" + hostaddr + "]";
                } else {
                    result = hostaddr;
                }
            } else {
                result = hostname;
            }

            /*
             * Finally return "hostname:port", "ipv4-address:port" or
             * "[ipv6-address]:port".
             */
            return result + ":" + ss.getLocalPort();
        }

        public String toString() {
            return address();
        }
    }

    /**
     * Handshake with the debuggee
     */
    void handshake(Socket s, long timeout) throws IOException {
        s.setSoTimeout((int)timeout);

        byte[] hello = "JDWP-Handshake".getBytes("UTF-8");
        s.getOutputStream().write(hello);

        byte[] b = new byte[hello.length];
        int received = 0;
        while (received < hello.length) {
            int n;
            try {
                n = s.getInputStream().read(b, received, hello.length-received);
            } catch (SocketTimeoutException x) {
                throw new IOException("handshake timeout");
            }
            if (n < 0) {
                s.close();
                throw new IOException("handshake failed - connection prematurally closed");
            }
            received += n;
        }
        for (int i=0; i<hello.length; i++) {
            if (b[i] != hello[i]) {
                throw new IOException("handshake failed - unrecognized message from target VM");
            }
        }

        // disable read timeout
        s.setSoTimeout(0);
    }

    /**
     * No-arg constructor
     */
    public SocketTransportService() {
    }

    /**
     * The name of this transport service
     */
    public String name() {
        return "Socket";
    }

    /**
     * Return localized description of this transport service
     */
    public String description() {
        synchronized (this) {
            if (messages == null) {
                messages = ResourceBundle.getBundle("com.sun.tools.jdi.resources.jdi");
            }
        }
        return messages.getString("socket_transportservice.description");
    }

    /**
     * Return the capabilities of this transport service
     */
    public Capabilities capabilities() {
        return new SocketTransportServiceCapabilities();
    }


    /**
     * Attach to the specified address with optional attach and handshake
     * timeout.
     */
    public Connection attach(String address, long attachTimeout, long handshakeTimeout)
        throws IOException {

        if (address == null) {
            throw new NullPointerException("address is null");
        }
        if (attachTimeout < 0 || handshakeTimeout < 0) {
            throw new IllegalArgumentException("timeout is negative");
        }

        int splitIndex = address.indexOf(':');
        String host;
        String portStr;
        if (splitIndex < 0) {
            host = InetAddress.getLocalHost().getHostName();
            portStr = address;
        } else {
            host = address.substring(0, splitIndex);
            portStr = address.substring(splitIndex+1);
        }

        int port;
        try {
            port = Integer.decode(portStr).intValue();
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                "unable to parse port number in address");
        }


        // open TCP connection to VM

        InetSocketAddress sa = new InetSocketAddress(host, port);
        Socket s = new Socket();
        try {
            s.connect(sa, (int)attachTimeout);
        } catch (SocketTimeoutException exc) {
            try {
                s.close();
            } catch (IOException x) { }
            throw new TransportTimeoutException("timed out trying to establish connection");
        }

        // handshake with the target VM
        try {
            handshake(s, handshakeTimeout);
        } catch (IOException exc) {
            try {
                s.close();
            } catch (IOException x) { }
            throw exc;
        }

        return new SocketConnection(s);
    }

    /*
     * Listen on the specified address and port. Return a listener
     * that encapsulates the ServerSocket.
     */
    ListenKey startListening(String localaddress, int port) throws IOException {
        InetSocketAddress sa;
        if (localaddress == null) {
            sa = new InetSocketAddress(port);
        } else {
            sa = new InetSocketAddress(localaddress, port);
        }
        ServerSocket ss = new ServerSocket();
        ss.bind(sa);
        return new SocketListenKey(ss);
    }

    /**
     * Listen on the specified address
     */
    public ListenKey startListening(String address) throws IOException {
        // use ephemeral port if address isn't specified.
        if (address == null || address.length() == 0) {
            address = "0";
        }

        int splitIndex = address.indexOf(':');
        String localaddr = null;
        if (splitIndex >= 0) {
            localaddr = address.substring(0, splitIndex);
            address = address.substring(splitIndex+1);
        }

        int port;
        try {
            port = Integer.decode(address).intValue();
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                    "unable to parse port number in address");
        }

        return startListening(localaddr, port);
    }

    /**
     * Listen on the default address
     */
    public ListenKey startListening() throws IOException {
        return startListening(null, 0);
    }

    /**
     * Stop the listener
     */
    public void stopListening(ListenKey listener) throws IOException {
        if (!(listener instanceof SocketListenKey)) {
            throw new IllegalArgumentException("Invalid listener");
        }

        synchronized (listener) {
            ServerSocket ss = ((SocketListenKey)listener).socket();

            // if the ServerSocket has been closed it means
            // the listener is invalid
            if (ss.isClosed()) {
                throw new IllegalArgumentException("Invalid listener");
            }
            ss.close();
        }
    }

    /**
     * Accept a connection from a debuggee and handshake with it.
     */
    public Connection accept(ListenKey listener, long acceptTimeout, long handshakeTimeout) throws IOException {
        if (acceptTimeout < 0 || handshakeTimeout < 0) {
            throw new IllegalArgumentException("timeout is negative");
        }
        if (!(listener instanceof SocketListenKey)) {
            throw new IllegalArgumentException("Invalid listener");
        }
        ServerSocket ss;

        // obtain the ServerSocket from the listener - if the
        // socket is closed it means the listener is invalid
        synchronized (listener) {
            ss = ((SocketListenKey)listener).socket();
            if (ss.isClosed()) {
               throw new IllegalArgumentException("Invalid listener");
            }
        }

        // from here onwards it's possible that the ServerSocket
        // may be closed by a call to stopListening - that's okay
        // because the ServerSocket methods will throw an
        // IOException indicating the socket is closed.
        //
        // Additionally, it's possible that another thread calls accept
        // with a different accept timeout - that creates a same race
        // condition between setting the timeout and calling accept.
        // As it is such an unlikely scenario (requires both threads
        // to be using the same listener we've chosen to ignore the issue).

        ss.setSoTimeout((int)acceptTimeout);
        Socket s;
        try {
            s = ss.accept();
        } catch (SocketTimeoutException x) {
            throw new TransportTimeoutException("timeout waiting for connection");
        }

        // handshake here
        handshake(s, handshakeTimeout);

        return new SocketConnection(s);
    }

    public String toString() {
       return name();
    }
}


/*
 * The Connection returned by attach and accept is one of these
 */
class SocketConnection extends Connection {
    private Socket socket;
    private boolean closed = false;
    private OutputStream socketOutput;
    private InputStream socketInput;
    private Object receiveLock = new Object();
    private Object sendLock = new Object();
    private Object closeLock = new Object();

    SocketConnection(Socket socket) throws IOException {
        this.socket = socket;
        socket.setTcpNoDelay(true);
        socketInput = socket.getInputStream();
        socketOutput = socket.getOutputStream();
    }

    public void close() throws IOException {
        synchronized (closeLock) {
           if (closed) {
                return;
           }
           socketOutput.close();
           socketInput.close();
           socket.close();
           closed = true;
        }
    }

    public boolean isOpen() {
        synchronized (closeLock) {
            return !closed;
        }
    }

    public byte[] readPacket() throws IOException {
        if (!isOpen()) {
            throw new ClosedConnectionException("connection is closed");
        }
        synchronized (receiveLock) {
            int b1,b2,b3,b4;

            // length
            try {
                b1 = socketInput.read();
                b2 = socketInput.read();
                b3 = socketInput.read();
                b4 = socketInput.read();
            } catch (IOException ioe) {
                if (!isOpen()) {
                    throw new ClosedConnectionException("connection is closed");
                } else {
                    throw ioe;
                }
            }

            // EOF
            if (b1<0) {
               return new byte[0];
            }

            if (b2<0 || b3<0 || b4<0) {
                throw new IOException("protocol error - premature EOF");
            }

            int len = ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0));

            if (len < 0) {
                throw new IOException("protocol error - invalid length");
            }

            byte b[] = new byte[len];
            b[0] = (byte)b1;
            b[1] = (byte)b2;
            b[2] = (byte)b3;
            b[3] = (byte)b4;

            int off = 4;
            len -= off;

            while (len > 0) {
                int count;
                try {
                    count = socketInput.read(b, off, len);
                } catch (IOException ioe) {
                    if (!isOpen()) {
                        throw new ClosedConnectionException("connection is closed");
                    } else {
                        throw ioe;
                    }
                }
                if (count < 0) {
                    throw new IOException("protocol error - premature EOF");
                }
                len -= count;
                off += count;
            }

            return b;
        }
    }

    public void writePacket(byte b[]) throws IOException {
        if (!isOpen()) {
            throw new ClosedConnectionException("connection is closed");
        }

        /*
         * Check the packet size
         */
        if (b.length < 11) {
            throw new IllegalArgumentException("packet is insufficient size");
        }
        int b0 = b[0] & 0xff;
        int b1 = b[1] & 0xff;
        int b2 = b[2] & 0xff;
        int b3 = b[3] & 0xff;
        int len = ((b0 << 24) | (b1 << 16) | (b2 << 8) | (b3 << 0));
        if (len < 11) {
            throw new IllegalArgumentException("packet is insufficient size");
        }

        /*
         * Check that the byte array contains the complete packet
         */
        if (len > b.length) {
            throw new IllegalArgumentException("length mis-match");
        }

        synchronized (sendLock) {
            try {
                /*
                 * Send the packet (ignoring any bytes that follow
                 * the packet in the byte array).
                 */
                socketOutput.write(b, 0, len);
            } catch (IOException ioe) {
                if (!isOpen()) {
                    throw new ClosedConnectionException("connection is closed");
                } else {
                    throw ioe;
                }
            }
        }
    }
}


/*
 * The capabilities of the socket transport service
 */
class SocketTransportServiceCapabilities extends TransportService.Capabilities {

    public boolean supportsMultipleConnections() {
        return true;
    }

    public boolean supportsAttachTimeout() {
        return true;
    }

    public boolean supportsAcceptTimeout() {
        return true;
    }

    public boolean supportsHandshakeTimeout() {
        return true;
    }

}

Other Java examples (source code examples)

Here is a short list of links related to this Java SocketTransportService.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.