/*
 * Copyright (c) 1999, 2005, 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.jndi.rmi.registry;


import java.util.Hashtable;
import java.util.Properties;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.security.AccessController;
import java.security.PrivilegedAction;

import javax.naming.*;
import javax.naming.spi.NamingManager;


A RegistryContext is a context representing a remote RMI registry.
Author:Scott Seligman
/** * A RegistryContext is a context representing a remote RMI registry. * * @author Scott Seligman */
public class RegistryContext implements Context, Referenceable { private Hashtable environment; private Registry registry; private String host; private int port; private static final NameParser nameParser = new AtomicNameParser(); private static final String SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
Determines whether classes may be loaded from an arbitrary URL code base.
/** * Determines whether classes may be loaded from an arbitrary URL code base. */
static final boolean trustURLCodebase; static { // System property to control whether classes may be loaded from an // arbitrary URL codebase String trust = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("com.sun.jndi.rmi.object.trustURLCodebase", "false"); } }); trustURLCodebase = "true".equalsIgnoreCase(trust); } Reference reference = null; // ref used to create this context, if any // Environment property that, if set, indicates that a security // manager should be installed (if none is already in place). public static final String SECURITY_MGR = "java.naming.rmi.security.manager";
Returns a context for the registry at a given host and port. If "host" is null, uses default host. If "port" is non-positive, uses default port. Cloning of "env" is handled by caller; see comments within RegistryContextFactory.getObjectInstance(), for example.
/** * Returns a context for the registry at a given host and port. * If "host" is null, uses default host. * If "port" is non-positive, uses default port. * Cloning of "env" is handled by caller; see comments within * RegistryContextFactory.getObjectInstance(), for example. */
public RegistryContext(String host, int port, Hashtable env) throws NamingException { environment = ((env == null) ? new Hashtable(5) : env); if (environment.get(SECURITY_MGR) != null) { installSecurityMgr(); } // chop off '[' and ']' in an IPv6 literal address if ((host != null) && (host.charAt(0) == '[')) { host = host.substring(1, host.length() - 1); } RMIClientSocketFactory socketFactory = (RMIClientSocketFactory) environment.get(SOCKET_FACTORY); registry = getRegistry(host, port, socketFactory); this.host = host; this.port = port; }
Returns a clone of a registry context. The context's private state is independent of the original's (so closing one context, for example, won't close the other).
/** * Returns a clone of a registry context. The context's private state * is independent of the original's (so closing one context, for example, * won't close the other). */
// %%% Alternatively, this could be done with a clone() method. RegistryContext(RegistryContext ctx) { environment = (Hashtable)ctx.environment.clone(); registry = ctx.registry; host = ctx.host; port = ctx.port; reference = ctx.reference; } protected void finalize() { close(); } public Object lookup(Name name) throws NamingException { if (name.isEmpty()) { return (new RegistryContext(this)); } Remote obj; try { obj = registry.lookup(name.get(0)); } catch (NotBoundException e) { throw (new NameNotFoundException(name.get(0))); } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } return (decodeObject(obj, name.getPrefix(1))); } public Object lookup(String name) throws NamingException { return lookup(new CompositeName(name)); }
If the object to be bound is both Remote and Referenceable, binds the object itself, not its Reference.
/** * If the object to be bound is both Remote and Referenceable, binds the * object itself, not its Reference. */
public void bind(Name name, Object obj) throws NamingException { if (name.isEmpty()) { throw (new InvalidNameException( "RegistryContext: Cannot bind empty name")); } try { registry.bind(name.get(0), encodeObject(obj, name.getPrefix(1))); } catch (AlreadyBoundException e) { NamingException ne = new NameAlreadyBoundException(name.get(0)); ne.setRootCause(e); throw ne; } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } } public void bind(String name, Object obj) throws NamingException { bind(new CompositeName(name), obj); } public void rebind(Name name, Object obj) throws NamingException { if (name.isEmpty()) { throw (new InvalidNameException( "RegistryContext: Cannot rebind empty name")); } try { registry.rebind(name.get(0), encodeObject(obj, name.getPrefix(1))); } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } } public void rebind(String name, Object obj) throws NamingException { rebind(new CompositeName(name), obj); } public void unbind(Name name) throws NamingException { if (name.isEmpty()) { throw (new InvalidNameException( "RegistryContext: Cannot unbind empty name")); } try { registry.unbind(name.get(0)); } catch (NotBoundException e) { // method is idempotent } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } } public void unbind(String name) throws NamingException { unbind(new CompositeName(name)); }
Rename is implemented by this sequence of operations: lookup, bind, unbind. The sequence is not performed atomically.
/** * Rename is implemented by this sequence of operations: * lookup, bind, unbind. The sequence is not performed atomically. */
public void rename(Name oldName, Name newName) throws NamingException { bind(newName, lookup(oldName)); unbind(oldName); } public void rename(String name, String newName) throws NamingException { rename(new CompositeName(name), new CompositeName(newName)); } public NamingEnumeration list(Name name) throws NamingException { if (!name.isEmpty()) { throw (new InvalidNameException( "RegistryContext: can only list \"\"")); } try { String[] names = registry.list(); return (new NameClassPairEnumeration(names)); } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } } public NamingEnumeration list(String name) throws NamingException { return list(new CompositeName(name)); } public NamingEnumeration listBindings(Name name) throws NamingException { if (!name.isEmpty()) { throw (new InvalidNameException( "RegistryContext: can only list \"\"")); } try { String[] names = registry.list(); return (new BindingEnumeration(this, names)); } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } } public NamingEnumeration listBindings(String name) throws NamingException { return listBindings(new CompositeName(name)); } public void destroySubcontext(Name name) throws NamingException { throw (new OperationNotSupportedException()); } public void destroySubcontext(String name) throws NamingException { throw (new OperationNotSupportedException()); } public Context createSubcontext(Name name) throws NamingException { throw (new OperationNotSupportedException()); } public Context createSubcontext(String name) throws NamingException { throw (new OperationNotSupportedException()); } public Object lookupLink(Name name) throws NamingException { return lookup(name); } public Object lookupLink(String name) throws NamingException { return lookup(name); } public NameParser getNameParser(Name name) throws NamingException { return nameParser; } public NameParser getNameParser(String name) throws NamingException { return nameParser; } public Name composeName(Name name, Name prefix) throws NamingException { Name result = (Name)prefix.clone(); return result.addAll(name); } public String composeName(String name, String prefix) throws NamingException { return composeName(new CompositeName(name), new CompositeName(prefix)).toString(); } public Object removeFromEnvironment(String propName) throws NamingException { return environment.remove(propName); } public Object addToEnvironment(String propName, Object propVal) throws NamingException { if (propName.equals(SECURITY_MGR)) { installSecurityMgr(); } return environment.put(propName, propVal); } public Hashtable getEnvironment() throws NamingException { return (Hashtable)environment.clone(); } public void close() { environment = null; registry = null; // &&& If we were caching registry connections, we would probably // uncache this one now. } public String getNameInNamespace() { return ""; // Registry has an empty name }
Returns an RMI registry reference for this context.

If this context was created from a reference, that reference is returned. Otherwise, an exception is thrown if the registry's host is "localhost" or the default (null). Although this could possibly make for a valid reference, it's far more likely to be an easily made error.

See Also:
  • RegistryContextFactory
/** * Returns an RMI registry reference for this context. *<p> * If this context was created from a reference, that reference is * returned. Otherwise, an exception is thrown if the registry's * host is "localhost" or the default (null). Although this could * possibly make for a valid reference, it's far more likely to be * an easily made error. * * @see RegistryContextFactory */
public Reference getReference() throws NamingException { if (reference != null) { return (Reference)reference.clone(); // %%% clone the addrs too? } if (host == null || host.equals("localhost")) { throw (new ConfigurationException( "Cannot create a reference for an RMI registry whose " + "host was unspecified or specified as \"localhost\"")); } String url = "rmi://"; // Enclose IPv6 literal address in '[' and ']' url = (host.indexOf(":") > -1) ? url + "[" + host + "]" : url + host; if (port > 0) { url += ":" + Integer.toString(port); } RefAddr addr = new StringRefAddr(RegistryContextFactory.ADDRESS_TYPE, url); return (new Reference(RegistryContext.class.getName(), addr, RegistryContextFactory.class.getName(), null)); }
Wrap a RemoteException inside a NamingException.
/** * Wrap a RemoteException inside a NamingException. */
public static NamingException wrapRemoteException(RemoteException re) { NamingException ne; if (re instanceof ConnectException) { ne = new ServiceUnavailableException(); } else if (re instanceof AccessException) { ne = new NoPermissionException(); } else if (re instanceof StubNotFoundException || re instanceof UnknownHostException || re instanceof SocketSecurityException) { ne = new ConfigurationException(); } else if (re instanceof ExportException || re instanceof ConnectIOException || re instanceof MarshalException || re instanceof UnmarshalException || re instanceof NoSuchObjectException) { ne = new CommunicationException(); } else if (re instanceof ServerException && re.detail instanceof RemoteException) { ne = wrapRemoteException((RemoteException)re.detail); } else { ne = new NamingException(); } ne.setRootCause(re); return ne; }
Returns the registry at a given host, port and socket factory. If "host" is null, uses default host. If "port" is non-positive, uses default port. If "socketFactory" is null, uses the default socket.
/** * Returns the registry at a given host, port and socket factory. * If "host" is null, uses default host. * If "port" is non-positive, uses default port. * If "socketFactory" is null, uses the default socket. */
private static Registry getRegistry(String host, int port, RMIClientSocketFactory socketFactory) throws NamingException { // %%% We could cache registry connections here. The transport layer // may already reuse connections. try { if (socketFactory == null) { return LocateRegistry.getRegistry(host, port); } else { return LocateRegistry.getRegistry(host, port, socketFactory); } } catch (RemoteException e) { throw (NamingException)wrapRemoteException(e).fillInStackTrace(); } }
Attempts to install a security manager if none is currently in place.
/** * Attempts to install a security manager if none is currently in * place. */
private static void installSecurityMgr() { try { System.setSecurityManager(new RMISecurityManager()); } catch (Exception e) { } }
Encodes an object prior to binding it in the registry. First, NamingManager.getStateToBind() is invoked. If the resulting object is Remote, it is returned. If it is a Reference or Referenceable, the reference is wrapped in a Remote object. Otherwise, an exception is thrown.
Params:
  • name – The object's name relative to this context.
/** * Encodes an object prior to binding it in the registry. First, * NamingManager.getStateToBind() is invoked. If the resulting * object is Remote, it is returned. If it is a Reference or * Referenceable, the reference is wrapped in a Remote object. * Otherwise, an exception is thrown. * * @param name The object's name relative to this context. */
private Remote encodeObject(Object obj, Name name) throws NamingException, RemoteException { obj = NamingManager.getStateToBind(obj, name, this, environment); if (obj instanceof Remote) { return (Remote)obj; } if (obj instanceof Reference) { return (new ReferenceWrapper((Reference)obj)); } if (obj instanceof Referenceable) { return (new ReferenceWrapper(((Referenceable)obj).getReference())); } throw (new IllegalArgumentException( "RegistryContext: " + "object to bind must be Remote, Reference, or Referenceable")); }
Decodes an object that has been retrieved from the registry. First, if the object is a RemoteReference, the Reference is unwrapped. Then, NamingManager.getObjectInstance() is invoked.
Params:
  • name – The object's name relative to this context.
/** * Decodes an object that has been retrieved from the registry. * First, if the object is a RemoteReference, the Reference is * unwrapped. Then, NamingManager.getObjectInstance() is invoked. * * @param name The object's name relative to this context. */
private Object decodeObject(Remote r, Name name) throws NamingException { try { Object obj = (r instanceof RemoteReference) ? ((RemoteReference)r).getReference() : (Object)r; /* * Classes may only be loaded from an arbitrary URL codebase when * the system property com.sun.jndi.rmi.object.trustURLCodebase * has been set to "true". */ // Use reference if possible Reference ref = null; if (obj instanceof Reference) { ref = (Reference) obj; } else if (obj instanceof Referenceable) { ref = ((Referenceable)(obj)).getReference(); } if (ref != null && ref.getFactoryClassLocation() != null && !trustURLCodebase) { throw new ConfigurationException( "The object factory is untrusted. Set the system property" + " 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'."); } return NamingManager.getObjectInstance(obj, name, this, environment); } catch (NamingException e) { throw e; } catch (RemoteException e) { throw (NamingException) wrapRemoteException(e).fillInStackTrace(); } catch (Exception e) { NamingException ne = new NamingException(); ne.setRootCause(e); throw ne; } } }
A name parser for case-sensitive atomic names.
/** * A name parser for case-sensitive atomic names. */
class AtomicNameParser implements NameParser { private static final Properties syntax = new Properties(); public Name parse(String name) throws NamingException { return (new CompoundName(name, syntax)); } }
An enumeration of name / class-name pairs. Since we don't know anything about the classes, each class name is returned as the generic "java.lang.Object".
/** * An enumeration of name / class-name pairs. Since we don't know anything * about the classes, each class name is returned as the generic * "java.lang.Object". */
class NameClassPairEnumeration implements NamingEnumeration { private final String[] names; private int nextName; // index into "names" NameClassPairEnumeration(String[] names) { this.names = names; nextName = 0; } public boolean hasMore() { return (nextName < names.length); } public Object next() throws NamingException { if (!hasMore()) { throw (new java.util.NoSuchElementException()); } // Convert name to a one-element composite name, so embedded // meta-characters are properly escaped. String name = names[nextName++]; Name cname = (new CompositeName()).add(name); NameClassPair ncp = new NameClassPair(cname.toString(), "java.lang.Object"); ncp.setNameInNamespace(name); return ncp; } public boolean hasMoreElements() { return hasMore(); } public Object nextElement() { try { return next(); } catch (NamingException e) { // should never happen throw (new java.util.NoSuchElementException( "javax.naming.NamingException was thrown")); } } public void close() { nextName = names.length; } }
An enumeration of Bindings. The actual registry lookups are performed when next() is called. It would be nicer to defer this until the object (or its class name) is actually requested. The problem with that approach is that Binding.getObject() cannot throw NamingException.
/** * An enumeration of Bindings. * * The actual registry lookups are performed when next() is called. It would * be nicer to defer this until the object (or its class name) is actually * requested. The problem with that approach is that Binding.getObject() * cannot throw NamingException. */
class BindingEnumeration implements NamingEnumeration { private RegistryContext ctx; private final String[] names; private int nextName; // index into "names" BindingEnumeration(RegistryContext ctx, String[] names) { // Clone ctx in case someone closes it before we're through. this.ctx = new RegistryContext(ctx); this.names = names; nextName = 0; } protected void finalize() { ctx.close(); } public boolean hasMore() { if (nextName >= names.length) { ctx.close(); } return (nextName < names.length); } public Object next() throws NamingException { if (!hasMore()) { throw (new java.util.NoSuchElementException()); } // Convert name to a one-element composite name, so embedded // meta-characters are properly escaped. String name = names[nextName++]; Name cname = (new CompositeName()).add(name); Object obj = ctx.lookup(cname); String cnameStr = cname.toString(); Binding binding = new Binding(cnameStr, obj); binding.setNameInNamespace(cnameStr); return binding; } public boolean hasMoreElements() { return hasMore(); } public Object nextElement() { try { return next(); } catch (NamingException e) { throw (new java.util.NoSuchElementException( "javax.naming.NamingException was thrown")); } } public void close () { finalize(); } }