/* * Copyright (c) 1999, 2012, 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 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. */ @SuppressWarnings("unchecked") public RegistryContext(String host, int port, Hashtable env) throws NamingException { environment = (env == null) ? new Hashtable(5) : (Hashtable) 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). */ // %%% Alternatively, this could be done with a clone() method. RegistryContext(RegistryContext ctx) { environment = 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 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. */ 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 NameClassPair 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 NameClassPair 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 Binding 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 Binding nextElement() { try { return next(); } catch (NamingException e) { throw (new java.util.NoSuchElementException( "javax.naming.NamingException was thrown")); } } public void close () { finalize(); } }