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