diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,599 @@ +/* + * Copyright 1999-2005 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 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 javax.naming.*; +import javax.naming.spi.NamingManager; + + +/** + * 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"; + + 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. + */ + 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) env.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). + */ + // %%% 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. + */ + 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. + */ + 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 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. + */ + 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. + */ + 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. + */ + 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. + * + * @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. + * + * @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; + 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. + */ +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". + */ +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. + */ +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(); + } +}