jdk/src/java.rmi/share/classes/sun/rmi/transport/proxy/RMIMasterSocketFactory.java
changeset 37947 f69560487686
parent 37946 e420b9f05aaf
parent 37936 428ebc487445
child 37948 caf97b37ebec
--- a/jdk/src/java.rmi/share/classes/sun/rmi/transport/proxy/RMIMasterSocketFactory.java	Sat May 14 08:03:03 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,468 +0,0 @@
-/*
- * Copyright (c) 1996, 2013, 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 sun.rmi.transport.proxy;
-
-import java.io.*;
-import java.net.*;
-import java.security.*;
-import java.util.*;
-import java.rmi.server.LogStream;
-import java.rmi.server.RMISocketFactory;
-import sun.rmi.runtime.Log;
-import sun.rmi.runtime.NewThreadAction;
-
-/**
- * RMIMasterSocketFactory attempts to create a socket connection to the
- * specified host using successively less efficient mechanisms
- * until one succeeds.  If the host is successfully connected to,
- * the factory for the successful mechanism is stored in an internal
- * hash table keyed by the host name, so that future attempts to
- * connect to the same host will automatically use the same
- * mechanism.
- */
-@SuppressWarnings("deprecation")
-public class RMIMasterSocketFactory extends RMISocketFactory {
-
-    /** "proxy" package log level */
-    static int logLevel = LogStream.parseLevel(getLogLevel());
-
-    private static String getLogLevel() {
-        return java.security.AccessController.doPrivileged(
-            (PrivilegedAction<String>) () -> System.getProperty("sun.rmi.transport.proxy.logLevel"));
-    }
-
-    /* proxy package log */
-    static final Log proxyLog =
-        Log.getLog("sun.rmi.transport.tcp.proxy",
-                   "transport", RMIMasterSocketFactory.logLevel);
-
-    /** timeout for attemping direct socket connections */
-    private static long connectTimeout = getConnectTimeout();
-
-    private static long getConnectTimeout() {
-        return java.security.AccessController.doPrivileged((PrivilegedAction<Long>) () ->
-            Long.getLong("sun.rmi.transport.proxy.connectTimeout", 15000)); // default: 15 seconds
-    }
-
-    /** whether to fallback to HTTP on general connect failures */
-    private static final boolean eagerHttpFallback =
-        java.security.AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
-            Boolean.getBoolean("sun.rmi.transport.proxy.eagerHttpFallback"));
-
-    /** table of hosts successfully connected to and the factory used */
-    private Hashtable<String, RMISocketFactory> successTable =
-        new Hashtable<>();
-
-    /** maximum number of hosts to remember successful connection to */
-    private static final int MaxRememberedHosts = 64;
-
-    /** list of the hosts in successTable in initial connection order */
-    private Vector<String> hostList = new Vector<>(MaxRememberedHosts);
-
-    /** default factory for initial use for direct socket connection */
-    protected RMISocketFactory initialFactory = new RMIDirectSocketFactory();
-
-    /** ordered list of factories to try as alternate connection
-      * mechanisms if a direct socket connections fails */
-    protected Vector<RMISocketFactory> altFactoryList;
-
-    /**
-     * Create a RMIMasterSocketFactory object.  Establish order of
-     * connection mechanisms to attempt on createSocket, if a direct
-     * socket connection fails.
-     */
-    public RMIMasterSocketFactory() {
-        altFactoryList = new Vector<>(2);
-        boolean setFactories = false;
-
-        try {
-            String proxyHost;
-            proxyHost = java.security.AccessController.doPrivileged(
-               (PrivilegedAction<String>) () -> System.getProperty("http.proxyHost"));
-
-            if (proxyHost == null)
-                proxyHost = java.security.AccessController.doPrivileged(
-                    (PrivilegedAction<String>) () -> System.getProperty("proxyHost"));
-
-            boolean disable = java.security.AccessController.doPrivileged(
-                (PrivilegedAction<String>) () -> System.getProperty("java.rmi.server.disableHttp", "true"))
-                .equalsIgnoreCase("true");
-
-            if (!disable && proxyHost != null && proxyHost.length() > 0) {
-                setFactories = true;
-            }
-        } catch (Exception e) {
-            // unable to obtain the properties, so use the default behavior.
-        }
-
-        if (setFactories) {
-            altFactoryList.addElement(new RMIHttpToPortSocketFactory());
-            altFactoryList.addElement(new RMIHttpToCGISocketFactory());
-        }
-    }
-
-    /**
-     * Create a new client socket.  If we remember connecting to this host
-     * successfully before, then use the same factory again.  Otherwise,
-     * try using a direct socket connection and then the alternate factories
-     * in the order specified in altFactoryList.
-     */
-    public Socket createSocket(String host, int port)
-        throws IOException
-    {
-        if (proxyLog.isLoggable(Log.BRIEF)) {
-            proxyLog.log(Log.BRIEF, "host: " + host + ", port: " + port);
-        }
-
-        /*
-         * If we don't have any alternate factories to consult, short circuit
-         * the fallback procedure and delegate to the initial factory.
-         */
-        if (altFactoryList.size() == 0) {
-            return initialFactory.createSocket(host, port);
-        }
-
-        RMISocketFactory factory;
-
-        /*
-         * If we remember successfully connecting to this host before,
-         * use the same factory.
-         */
-        factory = successTable.get(host);
-        if (factory != null) {
-            if (proxyLog.isLoggable(Log.BRIEF)) {
-                proxyLog.log(Log.BRIEF,
-                    "previously successful factory found: " + factory);
-            }
-            return factory.createSocket(host, port);
-        }
-
-        /*
-         * Next, try a direct socket connection.  Open socket in another
-         * thread and only wait for specified timeout, in case the socket
-         * would otherwise spend minutes trying an unreachable host.
-         */
-        Socket initialSocket = null;
-        Socket fallbackSocket = null;
-        final AsyncConnector connector =
-            new AsyncConnector(initialFactory, host, port,
-                AccessController.getContext());
-                // connection must be attempted with
-                // this thread's access control context
-        IOException initialFailure = null;
-
-        try {
-            synchronized (connector) {
-
-                Thread t = java.security.AccessController.doPrivileged(
-                    new NewThreadAction(connector, "AsyncConnector", true));
-                t.start();
-
-                try {
-                    long now = System.currentTimeMillis();
-                    long deadline = now + connectTimeout;
-                    do {
-                        connector.wait(deadline - now);
-                        initialSocket = checkConnector(connector);
-                        if (initialSocket != null)
-                            break;
-                        now = System.currentTimeMillis();
-                    } while (now < deadline);
-                } catch (InterruptedException e) {
-                    throw new InterruptedIOException(
-                        "interrupted while waiting for connector");
-                }
-            }
-
-            // assume no route to host (for now) if no connection yet
-            if (initialSocket == null)
-                throw new NoRouteToHostException(
-                    "connect timed out: " + host);
-
-            proxyLog.log(Log.BRIEF, "direct socket connection successful");
-
-            return initialSocket;
-
-        } catch (UnknownHostException | NoRouteToHostException e) {
-            initialFailure = e;
-        } catch (SocketException e) {
-            if (eagerHttpFallback) {
-                initialFailure = e;
-            } else {
-                throw e;
-            }
-        } finally {
-            if (initialFailure != null) {
-
-                if (proxyLog.isLoggable(Log.BRIEF)) {
-                    proxyLog.log(Log.BRIEF,
-                        "direct socket connection failed: ", initialFailure);
-                }
-
-                // Finally, try any alternate connection mechanisms.
-                for (int i = 0; i < altFactoryList.size(); ++ i) {
-                    factory = altFactoryList.elementAt(i);
-                    if (proxyLog.isLoggable(Log.BRIEF)) {
-                        proxyLog.log(Log.BRIEF,
-                            "trying with factory: " + factory);
-                    }
-                    try (Socket testSocket =
-                            factory.createSocket(host, port)) {
-                        // For HTTP connections, the output (POST request) must
-                        // be sent before we verify a successful connection.
-                        // So, sacrifice a socket for the sake of testing...
-                        // The following sequence should verify a successful
-                        // HTTP connection if no IOException is thrown.
-                        InputStream in = testSocket.getInputStream();
-                        int b = in.read(); // probably -1 for EOF...
-                    } catch (IOException ex) {
-                        if (proxyLog.isLoggable(Log.BRIEF)) {
-                            proxyLog.log(Log.BRIEF, "factory failed: ", ex);
-                        }
-
-                        continue;
-                    }
-                    proxyLog.log(Log.BRIEF, "factory succeeded");
-
-                    // factory succeeded, open new socket for caller's use
-                    try {
-                        fallbackSocket = factory.createSocket(host, port);
-                    } catch (IOException ex) {  // if it fails 2nd time,
-                    }                           // just give up
-                    break;
-                }
-            }
-        }
-
-        synchronized (successTable) {
-            try {
-                // check once again to see if direct connection succeeded
-                synchronized (connector) {
-                    initialSocket = checkConnector(connector);
-                }
-                if (initialSocket != null) {
-                    // if we had made another one as well, clean it up...
-                    if (fallbackSocket != null)
-                        fallbackSocket.close();
-                    return initialSocket;
-                }
-                // if connector ever does get socket, it won't be used
-                connector.notUsed();
-            } catch (UnknownHostException | NoRouteToHostException e) {
-                initialFailure = e;
-            } catch (SocketException e) {
-                if (eagerHttpFallback) {
-                    initialFailure = e;
-                } else {
-                    throw e;
-                }
-            }
-            // if we had found an alternate mechanism, go and use it
-            if (fallbackSocket != null) {
-                // remember this successful host/factory pair
-                rememberFactory(host, factory);
-                return fallbackSocket;
-            }
-            throw initialFailure;
-        }
-    }
-
-    /**
-     * Remember a successful factory for connecting to host.
-     * Currently, excess hosts are removed from the remembered list
-     * using a Least Recently Created strategy.
-     */
-    void rememberFactory(String host, RMISocketFactory factory) {
-        synchronized (successTable) {
-            while (hostList.size() >= MaxRememberedHosts) {
-                successTable.remove(hostList.elementAt(0));
-                hostList.removeElementAt(0);
-            }
-            hostList.addElement(host);
-            successTable.put(host, factory);
-        }
-    }
-
-    /**
-     * Check if an AsyncConnector succeeded.  If not, return socket
-     * given to fall back to.
-     */
-    Socket checkConnector(AsyncConnector connector)
-        throws IOException
-    {
-        Exception e = connector.getException();
-        if (e != null) {
-            e.fillInStackTrace();
-            /*
-             * The AsyncConnector implementation guaranteed that the exception
-             * will be either an IOException or a RuntimeException, and we can
-             * only throw one of those, so convince that compiler that it must
-             * be one of those.
-             */
-            if (e instanceof IOException) {
-                throw (IOException) e;
-            } else if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            } else {
-                throw new Error("internal error: " +
-                    "unexpected checked exception: " + e.toString());
-            }
-        }
-        return connector.getSocket();
-    }
-
-    /**
-     * Create a new server socket.
-     */
-    public ServerSocket createServerSocket(int port) throws IOException {
-        //return new HttpAwareServerSocket(port);
-        return initialFactory.createServerSocket(port);
-    }
-
-
-    /**
-     * AsyncConnector is used by RMIMasterSocketFactory to attempt socket
-     * connections on a separate thread.  This allows RMIMasterSocketFactory
-     * to control how long it will wait for the connection to succeed.
-     */
-    private class AsyncConnector implements Runnable {
-
-        /** what factory to use to attempt connection */
-        private RMISocketFactory factory;
-
-        /** the host to connect to */
-        private String host;
-
-        /** the port to connect to */
-        private int port;
-
-        /** access control context to attempt connection within */
-        private AccessControlContext acc;
-
-        /** exception that occurred during connection, if any */
-        private Exception exception = null;
-
-        /** the connected socket, if successful */
-        private Socket socket = null;
-
-        /** socket should be closed after created, if ever */
-        private boolean cleanUp = false;
-
-        /**
-         * Create a new asynchronous connector object.
-         */
-        AsyncConnector(RMISocketFactory factory, String host, int port,
-                       AccessControlContext acc)
-        {
-            this.factory = factory;
-            this.host    = host;
-            this.port    = port;
-            this.acc     = acc;
-            SecurityManager security = System.getSecurityManager();
-            if (security != null) {
-                security.checkConnect(host, port);
-            }
-        }
-
-        /**
-         * Attempt socket connection in separate thread.  If successful,
-         * notify master waiting,
-         */
-        public void run() {
-            try {
-                /*
-                 * Using the privileges of the thread that wants to make the
-                 * connection is tempting, but it will fail with applets with
-                 * the current applet security manager because the applet
-                 * network connection policy is not captured in the permission
-                 * framework of the access control context we have.
-                 *
-                 * java.security.AccessController.beginPrivileged(acc);
-                 */
-                try {
-                    Socket temp = factory.createSocket(host, port);
-                    synchronized (this) {
-                        socket = temp;
-                        notify();
-                    }
-                    rememberFactory(host, factory);
-                    synchronized (this) {
-                        if (cleanUp)
-                          try {
-                              socket.close();
-                          } catch (IOException e) {
-                          }
-                    }
-                } catch (Exception e) {
-                    /*
-                     * Note that the only exceptions which could actually have
-                     * occurred here are IOException or RuntimeException.
-                     */
-                    synchronized (this) {
-                        exception = e;
-                        notify();
-                    }
-                }
-            } finally {
-                /*
-                 * See above comments for matching beginPrivileged() call that
-                 * is also commented out.
-                 *
-                 * java.security.AccessController.endPrivileged();
-                 */
-            }
-        }
-
-        /**
-         * Get exception that occurred during connection attempt, if any.
-         * In the current implementation, this is guaranteed to be either
-         * an IOException or a RuntimeException.
-         */
-        private synchronized Exception getException() {
-            return exception;
-        }
-
-        /**
-         * Get successful socket, if any.
-         */
-        private synchronized Socket getSocket() {
-            return socket;
-        }
-
-        /**
-         * Note that this connector's socket, if ever successfully created,
-         * will not be used, so it should be cleaned up quickly
-         */
-        synchronized void notUsed() {
-            if (socket != null) {
-                try {
-                    socket.close();
-                } catch (IOException e) {
-                }
-            }
-            cleanUp = true;
-        }
-    }
-}