/* * 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); } }