jdk/src/share/classes/sun/rmi/registry/RegistryImpl.java
changeset 2 90ce3da70b43
child 51 6fe31bc95bbc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/rmi/registry/RegistryImpl.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,360 @@
+/*
+ * Copyright 1996-2006 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.rmi.registry;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.io.IOException;
+import java.net.*;
+import java.rmi.*;
+import java.rmi.server.ObjID;
+import java.rmi.server.RemoteServer;
+import java.rmi.server.ServerNotActiveException;
+import java.rmi.registry.Registry;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
+import java.security.PrivilegedActionException;
+import java.text.MessageFormat;
+import sun.rmi.server.UnicastServerRef;
+import sun.rmi.server.UnicastServerRef2;
+import sun.rmi.transport.LiveRef;
+import sun.rmi.transport.ObjectTable;
+import sun.rmi.transport.Target;
+
+/**
+ * A "registry" exists on every node that allows RMI connections to
+ * servers on that node.  The registry on a particular node contains a
+ * transient database that maps names to remote objects.  When the
+ * node boots, the registry database is empty.  The names stored in the
+ * registry are pure and are not parsed.  A service storing itself in
+ * the registry may want to prefix its name of the service by a package
+ * name (although not required), to reduce name collisions in the
+ * registry.
+ *
+ * The LocateRegistry class is used to obtain registry for different hosts.
+ *
+ * @see java.rmi.registry.LocateRegistry
+ */
+public class RegistryImpl extends java.rmi.server.RemoteServer
+        implements Registry
+{
+
+    /* indicate compatibility with JDK 1.1.x version of class */
+    private static final long serialVersionUID = 4666870661827494597L;
+    private Hashtable bindings = new Hashtable(101);
+    private static Hashtable allowedAccessCache = new Hashtable(3);
+    private static RegistryImpl registry;
+    private static ObjID id = new ObjID(ObjID.REGISTRY_ID);
+
+    private static ResourceBundle resources = null;
+
+    /**
+     * Construct a new RegistryImpl on the specified port with the
+     * given custom socket factory pair.
+     */
+    public RegistryImpl(int port,
+                        RMIClientSocketFactory csf,
+                        RMIServerSocketFactory ssf)
+        throws RemoteException
+    {
+        LiveRef lref = new LiveRef(id, port, csf, ssf);
+        setup(new UnicastServerRef2(lref));
+    }
+
+    /**
+     * Construct a new RegistryImpl on the specified port.
+     */
+    public RegistryImpl(int port)
+        throws RemoteException
+    {
+        LiveRef lref = new LiveRef(id, port);
+        setup(new UnicastServerRef(lref));
+    }
+
+    /*
+     * Create the export the object using the parameter
+     * <code>uref</code>
+     */
+    private void setup(UnicastServerRef uref)
+        throws RemoteException
+    {
+        /* Server ref must be created and assigned before remote
+         * object 'this' can be exported.
+         */
+        ref = uref;
+        uref.exportObject(this, null, true);
+    }
+
+    /**
+     * Returns the remote object for specified name in the registry.
+     * @exception RemoteException If remote operation failed.
+     * @exception NotBound If name is not currently bound.
+     */
+    public Remote lookup(String name)
+        throws RemoteException, NotBoundException
+    {
+        synchronized (bindings) {
+            Remote obj = (Remote)bindings.get(name);
+            if (obj == null)
+                throw new NotBoundException(name);
+            return obj;
+        }
+    }
+
+    /**
+     * Binds the name to the specified remote object.
+     * @exception RemoteException If remote operation failed.
+     * @exception AlreadyBoundException If name is already bound.
+     */
+    public void bind(String name, Remote obj)
+        throws RemoteException, AlreadyBoundException, AccessException
+    {
+        checkAccess("Registry.bind");
+        synchronized (bindings) {
+            Remote curr = (Remote)bindings.get(name);
+            if (curr != null)
+                throw new AlreadyBoundException(name);
+            bindings.put(name, obj);
+        }
+    }
+
+    /**
+     * Unbind the name.
+     * @exception RemoteException If remote operation failed.
+     * @exception NotBound If name is not currently bound.
+     */
+    public void unbind(String name)
+        throws RemoteException, NotBoundException, AccessException
+    {
+        checkAccess("Registry.unbind");
+        synchronized (bindings) {
+            Remote obj = (Remote)bindings.get(name);
+            if (obj == null)
+                throw new NotBoundException(name);
+            bindings.remove(name);
+        }
+    }
+
+    /**
+     * Rebind the name to a new object, replaces any existing binding.
+     * @exception RemoteException If remote operation failed.
+     */
+    public void rebind(String name, Remote obj)
+        throws RemoteException, AccessException
+    {
+        checkAccess("Registry.rebind");
+        bindings.put(name, obj);
+    }
+
+    /**
+     * Returns an enumeration of the names in the registry.
+     * @exception RemoteException If remote operation failed.
+     */
+    public String[] list()
+        throws RemoteException
+    {
+        String[] names;
+        synchronized (bindings) {
+            int i = bindings.size();
+            names = new String[i];
+            Enumeration enum_ = bindings.keys();
+            while ((--i) >= 0)
+                names[i] = (String)enum_.nextElement();
+        }
+        return names;
+    }
+
+    /**
+     * Check that the caller has access to perform indicated operation.
+     * The client must be on same the same host as this server.
+     */
+    public static void checkAccess(String op) throws AccessException {
+
+        try {
+            /*
+             * Get client host that this registry operation was made from.
+             */
+            final String clientHostName = getClientHost();
+            InetAddress clientHost;
+
+            try {
+                clientHost = (InetAddress)
+                    java.security.AccessController.doPrivileged(
+                        new java.security.PrivilegedExceptionAction() {
+                        public Object run()
+                            throws java.net.UnknownHostException
+                        {
+                            return InetAddress.getByName(clientHostName);
+                        }
+                    });
+            } catch (PrivilegedActionException pae) {
+                throw (java.net.UnknownHostException) pae.getException();
+            }
+
+            // if client not yet seen, make sure client allowed access
+            if (allowedAccessCache.get(clientHost) == null) {
+
+                if (clientHost.isAnyLocalAddress()) {
+                    throw new AccessException(
+                        "Registry." + op + " disallowed; origin unknown");
+                }
+
+                try {
+                    final InetAddress finalClientHost = clientHost;
+
+                    java.security.AccessController.doPrivileged(
+                        new java.security.PrivilegedExceptionAction() {
+                            public Object run() throws java.io.IOException {
+                                /*
+                                 * if a ServerSocket can be bound to the client's
+                                 * address then that address must be local
+                                 */
+                                (new ServerSocket(0, 10, finalClientHost)).close();
+                                allowedAccessCache.put(finalClientHost,
+                                                       finalClientHost);
+                                return null;
+                            }
+                    });
+                } catch (PrivilegedActionException pae) {
+                    // must have been an IOException
+
+                    throw new AccessException(
+                        "Registry." + op + " disallowed; origin " +
+                        clientHost + " is non-local host");
+                }
+            }
+        } catch (ServerNotActiveException ex) {
+            /*
+             * Local call from this VM: allow access.
+             */
+        } catch (java.net.UnknownHostException ex) {
+            throw new AccessException("Registry." + op +
+                                      " disallowed; origin is unknown host");
+        }
+    }
+
+    public static ObjID getID() {
+        return id;
+    }
+
+    /**
+     * Retrieves text resources from the locale-specific properties file.
+     */
+    private static String getTextResource(String key) {
+        if (resources == null) {
+            try {
+                resources = ResourceBundle.getBundle(
+                    "sun.rmi.registry.resources.rmiregistry");
+            } catch (MissingResourceException mre) {
+            }
+            if (resources == null) {
+                // throwing an Error is a bit extreme, methinks
+                return ("[missing resource file: " + key + "]");
+            }
+        }
+
+        String val = null;
+        try {
+            val = resources.getString(key);
+        } catch (MissingResourceException mre) {
+        }
+
+        if (val == null) {
+            return ("[missing resource: " + key + "]");
+        } else {
+            return (val);
+        }
+    }
+
+    /**
+     * Main program to start a registry. <br>
+     * The port number can be specified on the command line.
+     */
+    public static void main(String args[])
+    {
+        // Create and install the security manager if one is not installed
+        // already.
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            /*
+             * Fix bugid 4147561: When JDK tools are executed, the value of
+             * the CLASSPATH environment variable for the shell in which they
+             * were invoked is no longer incorporated into the application
+             * class path; CLASSPATH's only effect is to be the value of the
+             * system property "env.class.path".  To preserve the previous
+             * (JDK1.1 and JDK1.2beta3) behavior of this tool, however, its
+             * CLASSPATH should still be considered when resolving classes
+             * being unmarshalled.  To effect this old behavior, a class
+             * loader that loads from the file path specified in the
+             * "env.class.path" property is created and set to be the context
+             * class loader before the remote object is exported.
+             */
+            String envcp = System.getProperty("env.class.path");
+            if (envcp == null) {
+                envcp = ".";            // preserve old default behavior
+            }
+            URL[] urls = sun.misc.URLClassPath.pathToURLs(envcp);
+            ClassLoader cl = new URLClassLoader(urls);
+
+            /*
+             * Fix bugid 4242317: Classes defined by this class loader should
+             * be annotated with the value of the "java.rmi.server.codebase"
+             * property, not the "file:" URLs for the CLASSPATH elements.
+             */
+            sun.rmi.server.LoaderHandler.registerCodebaseLoader(cl);
+
+            Thread.currentThread().setContextClassLoader(cl);
+
+            int regPort = Registry.REGISTRY_PORT;
+            if (args.length >= 1) {
+                regPort = Integer.parseInt(args[0]);
+            }
+            registry = new RegistryImpl(regPort);
+            // prevent registry from exiting
+            while (true) {
+                try {
+                    Thread.sleep(Long.MAX_VALUE);
+                } catch (InterruptedException e) {
+                }
+            }
+        } catch (NumberFormatException e) {
+            System.err.println(MessageFormat.format(
+                getTextResource("rmiregistry.port.badnumber"),
+                args[0] ));
+            System.err.println(MessageFormat.format(
+                getTextResource("rmiregistry.usage"),
+                "rmiregistry" ));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        System.exit(1);
+    }
+}