jdk/src/jdk.naming.rmi/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java
changeset 25859 3317bb8137f4
parent 24685 215fa91e1b4c
child 27936 ca9ee8e3d527
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.jndi.rmi.registry;
       
    27 
       
    28 
       
    29 import java.util.Hashtable;
       
    30 import java.util.Properties;
       
    31 import java.rmi.*;
       
    32 import java.rmi.server.*;
       
    33 import java.rmi.registry.Registry;
       
    34 import java.rmi.registry.LocateRegistry;
       
    35 
       
    36 import javax.naming.*;
       
    37 import javax.naming.spi.NamingManager;
       
    38 
       
    39 
       
    40 /**
       
    41  * A RegistryContext is a context representing a remote RMI registry.
       
    42  *
       
    43  * @author Scott Seligman
       
    44  */
       
    45 
       
    46 
       
    47 public class RegistryContext implements Context, Referenceable {
       
    48 
       
    49     private Hashtable<String, Object> environment;
       
    50     private Registry registry;
       
    51     private String host;
       
    52     private int port;
       
    53     private static final NameParser nameParser = new AtomicNameParser();
       
    54     private static final String SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
       
    55 
       
    56     Reference reference = null; // ref used to create this context, if any
       
    57 
       
    58     // Environment property that, if set, indicates that a security
       
    59     // manager should be installed (if none is already in place).
       
    60     public static final String SECURITY_MGR =
       
    61             "java.naming.rmi.security.manager";
       
    62 
       
    63     /**
       
    64      * Returns a context for the registry at a given host and port.
       
    65      * If "host" is null, uses default host.
       
    66      * If "port" is non-positive, uses default port.
       
    67      * Cloning of "env" is handled by caller; see comments within
       
    68      * RegistryContextFactory.getObjectInstance(), for example.
       
    69      */
       
    70     @SuppressWarnings("unchecked")
       
    71     public RegistryContext(String host, int port, Hashtable<?, ?> env)
       
    72             throws NamingException
       
    73     {
       
    74         environment = (env == null)
       
    75                       ? new Hashtable<String, Object>(5)
       
    76                       : (Hashtable<String, Object>) env;
       
    77         if (environment.get(SECURITY_MGR) != null) {
       
    78             installSecurityMgr();
       
    79         }
       
    80 
       
    81         // chop off '[' and ']' in an IPv6 literal address
       
    82         if ((host != null) && (host.charAt(0) == '[')) {
       
    83             host = host.substring(1, host.length() - 1);
       
    84         }
       
    85 
       
    86         RMIClientSocketFactory socketFactory =
       
    87                 (RMIClientSocketFactory) environment.get(SOCKET_FACTORY);
       
    88         registry = getRegistry(host, port, socketFactory);
       
    89         this.host = host;
       
    90         this.port = port;
       
    91     }
       
    92 
       
    93     /**
       
    94      * Returns a clone of a registry context.  The context's private state
       
    95      * is independent of the original's (so closing one context, for example,
       
    96      * won't close the other).
       
    97      */
       
    98     // %%% Alternatively, this could be done with a clone() method.
       
    99     @SuppressWarnings("unchecked") // clone()
       
   100     RegistryContext(RegistryContext ctx) {
       
   101         environment = (Hashtable<String, Object>)ctx.environment.clone();
       
   102         registry = ctx.registry;
       
   103         host = ctx.host;
       
   104         port = ctx.port;
       
   105         reference = ctx.reference;
       
   106     }
       
   107 
       
   108     protected void finalize() {
       
   109         close();
       
   110     }
       
   111 
       
   112     public Object lookup(Name name) throws NamingException {
       
   113         if (name.isEmpty()) {
       
   114             return (new RegistryContext(this));
       
   115         }
       
   116         Remote obj;
       
   117         try {
       
   118             obj = registry.lookup(name.get(0));
       
   119         } catch (NotBoundException e) {
       
   120             throw (new NameNotFoundException(name.get(0)));
       
   121         } catch (RemoteException e) {
       
   122             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   123         }
       
   124         return (decodeObject(obj, name.getPrefix(1)));
       
   125     }
       
   126 
       
   127     public Object lookup(String name) throws NamingException {
       
   128         return lookup(new CompositeName(name));
       
   129     }
       
   130 
       
   131     /**
       
   132      * If the object to be bound is both Remote and Referenceable, binds the
       
   133      * object itself, not its Reference.
       
   134      */
       
   135     public void bind(Name name, Object obj) throws NamingException {
       
   136         if (name.isEmpty()) {
       
   137             throw (new InvalidNameException(
       
   138                     "RegistryContext: Cannot bind empty name"));
       
   139         }
       
   140         try {
       
   141             registry.bind(name.get(0), encodeObject(obj, name.getPrefix(1)));
       
   142         } catch (AlreadyBoundException e) {
       
   143             NamingException ne = new NameAlreadyBoundException(name.get(0));
       
   144             ne.setRootCause(e);
       
   145             throw ne;
       
   146         } catch (RemoteException e) {
       
   147             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   148         }
       
   149     }
       
   150 
       
   151     public void bind(String name, Object obj) throws NamingException {
       
   152         bind(new CompositeName(name), obj);
       
   153     }
       
   154 
       
   155     public void rebind(Name name, Object obj) throws NamingException {
       
   156         if (name.isEmpty()) {
       
   157             throw (new InvalidNameException(
       
   158                     "RegistryContext: Cannot rebind empty name"));
       
   159         }
       
   160         try {
       
   161             registry.rebind(name.get(0), encodeObject(obj, name.getPrefix(1)));
       
   162         } catch (RemoteException e) {
       
   163             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   164         }
       
   165     }
       
   166 
       
   167     public void rebind(String name, Object obj) throws NamingException {
       
   168         rebind(new CompositeName(name), obj);
       
   169     }
       
   170 
       
   171     public void unbind(Name name) throws NamingException {
       
   172         if (name.isEmpty()) {
       
   173             throw (new InvalidNameException(
       
   174                     "RegistryContext: Cannot unbind empty name"));
       
   175         }
       
   176         try {
       
   177             registry.unbind(name.get(0));
       
   178         } catch (NotBoundException e) {
       
   179             // method is idempotent
       
   180         } catch (RemoteException e) {
       
   181             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   182         }
       
   183     }
       
   184 
       
   185     public void unbind(String name) throws NamingException {
       
   186         unbind(new CompositeName(name));
       
   187     }
       
   188 
       
   189     /**
       
   190      * Rename is implemented by this sequence of operations:
       
   191      * lookup, bind, unbind.  The sequence is not performed atomically.
       
   192      */
       
   193     public void rename(Name oldName, Name newName) throws NamingException {
       
   194         bind(newName, lookup(oldName));
       
   195         unbind(oldName);
       
   196     }
       
   197 
       
   198     public void rename(String name, String newName) throws NamingException {
       
   199         rename(new CompositeName(name), new CompositeName(newName));
       
   200     }
       
   201 
       
   202     public NamingEnumeration<NameClassPair> list(Name name) throws
       
   203             NamingException {
       
   204         if (!name.isEmpty()) {
       
   205             throw (new InvalidNameException(
       
   206                     "RegistryContext: can only list \"\""));
       
   207         }
       
   208         try {
       
   209             String[] names = registry.list();
       
   210             return (new NameClassPairEnumeration(names));
       
   211         } catch (RemoteException e) {
       
   212             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   213         }
       
   214     }
       
   215 
       
   216     public NamingEnumeration<NameClassPair> list(String name) throws
       
   217             NamingException {
       
   218         return list(new CompositeName(name));
       
   219     }
       
   220 
       
   221     public NamingEnumeration<Binding> listBindings(Name name)
       
   222             throws NamingException
       
   223     {
       
   224         if (!name.isEmpty()) {
       
   225             throw (new InvalidNameException(
       
   226                     "RegistryContext: can only list \"\""));
       
   227         }
       
   228         try {
       
   229             String[] names = registry.list();
       
   230             return (new BindingEnumeration(this, names));
       
   231         } catch (RemoteException e) {
       
   232             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   233         }
       
   234     }
       
   235 
       
   236     public NamingEnumeration<Binding> listBindings(String name) throws
       
   237             NamingException {
       
   238         return listBindings(new CompositeName(name));
       
   239     }
       
   240 
       
   241     public void destroySubcontext(Name name) throws NamingException {
       
   242         throw (new OperationNotSupportedException());
       
   243     }
       
   244 
       
   245     public void destroySubcontext(String name) throws NamingException {
       
   246         throw (new OperationNotSupportedException());
       
   247     }
       
   248 
       
   249     public Context createSubcontext(Name name) throws NamingException {
       
   250         throw (new OperationNotSupportedException());
       
   251     }
       
   252 
       
   253     public Context createSubcontext(String name) throws NamingException {
       
   254         throw (new OperationNotSupportedException());
       
   255     }
       
   256 
       
   257     public Object lookupLink(Name name) throws NamingException {
       
   258         return lookup(name);
       
   259     }
       
   260 
       
   261     public Object lookupLink(String name) throws NamingException {
       
   262         return lookup(name);
       
   263     }
       
   264 
       
   265     public NameParser getNameParser(Name name) throws NamingException {
       
   266         return nameParser;
       
   267     }
       
   268 
       
   269     public NameParser getNameParser(String name) throws NamingException {
       
   270         return nameParser;
       
   271     }
       
   272 
       
   273     public Name composeName(Name name, Name prefix) throws NamingException {
       
   274         Name result = (Name)prefix.clone();
       
   275         return result.addAll(name);
       
   276     }
       
   277 
       
   278     public String composeName(String name, String prefix)
       
   279             throws NamingException
       
   280     {
       
   281         return composeName(new CompositeName(name),
       
   282                            new CompositeName(prefix)).toString();
       
   283     }
       
   284 
       
   285     public Object removeFromEnvironment(String propName)
       
   286             throws NamingException
       
   287     {
       
   288         return environment.remove(propName);
       
   289     }
       
   290 
       
   291     public Object addToEnvironment(String propName, Object propVal)
       
   292             throws NamingException
       
   293     {
       
   294         if (propName.equals(SECURITY_MGR)) {
       
   295             installSecurityMgr();
       
   296         }
       
   297         return environment.put(propName, propVal);
       
   298     }
       
   299 
       
   300     @SuppressWarnings("unchecked") // clone()
       
   301     public Hashtable<String, Object> getEnvironment() throws NamingException {
       
   302         return (Hashtable<String, Object>)environment.clone();
       
   303     }
       
   304 
       
   305     public void close() {
       
   306         environment = null;
       
   307         registry = null;
       
   308         // &&& If we were caching registry connections, we would probably
       
   309         // uncache this one now.
       
   310     }
       
   311 
       
   312     public String getNameInNamespace() {
       
   313         return ""; // Registry has an empty name
       
   314     }
       
   315 
       
   316     /**
       
   317      * Returns an RMI registry reference for this context.
       
   318      *<p>
       
   319      * If this context was created from a reference, that reference is
       
   320      * returned.  Otherwise, an exception is thrown if the registry's
       
   321      * host is "localhost" or the default (null).  Although this could
       
   322      * possibly make for a valid reference, it's far more likely to be
       
   323      * an easily made error.
       
   324      *
       
   325      * @see RegistryContextFactory
       
   326      */
       
   327     public Reference getReference() throws NamingException {
       
   328         if (reference != null) {
       
   329             return (Reference)reference.clone();  // %%% clone the addrs too?
       
   330         }
       
   331         if (host == null || host.equals("localhost")) {
       
   332             throw (new ConfigurationException(
       
   333                     "Cannot create a reference for an RMI registry whose " +
       
   334                     "host was unspecified or specified as \"localhost\""));
       
   335         }
       
   336         String url = "rmi://";
       
   337 
       
   338         // Enclose IPv6 literal address in '[' and ']'
       
   339         url = (host.indexOf(':') > -1) ? url + "[" + host + "]" :
       
   340                                          url + host;
       
   341         if (port > 0) {
       
   342             url += ":" + Integer.toString(port);
       
   343         }
       
   344         RefAddr addr = new StringRefAddr(RegistryContextFactory.ADDRESS_TYPE,
       
   345                                          url);
       
   346         return (new Reference(RegistryContext.class.getName(),
       
   347                               addr,
       
   348                               RegistryContextFactory.class.getName(),
       
   349                               null));
       
   350     }
       
   351 
       
   352 
       
   353     /**
       
   354      * Wrap a RemoteException inside a NamingException.
       
   355      */
       
   356     public static NamingException wrapRemoteException(RemoteException re) {
       
   357 
       
   358         NamingException ne;
       
   359 
       
   360         if (re instanceof ConnectException) {
       
   361             ne = new ServiceUnavailableException();
       
   362 
       
   363         } else if (re instanceof AccessException) {
       
   364             ne = new NoPermissionException();
       
   365 
       
   366         } else if (re instanceof StubNotFoundException ||
       
   367                    re instanceof UnknownHostException ||
       
   368                    re instanceof SocketSecurityException) {
       
   369             ne = new ConfigurationException();
       
   370 
       
   371         } else if (re instanceof ExportException ||
       
   372                    re instanceof ConnectIOException ||
       
   373                    re instanceof MarshalException ||
       
   374                    re instanceof UnmarshalException ||
       
   375                    re instanceof NoSuchObjectException) {
       
   376             ne = new CommunicationException();
       
   377 
       
   378         } else if (re instanceof ServerException &&
       
   379                    re.detail instanceof RemoteException) {
       
   380             ne = wrapRemoteException((RemoteException)re.detail);
       
   381 
       
   382         } else {
       
   383             ne = new NamingException();
       
   384         }
       
   385         ne.setRootCause(re);
       
   386         return ne;
       
   387     }
       
   388 
       
   389     /**
       
   390      * Returns the registry at a given host, port and socket factory.
       
   391      * If "host" is null, uses default host.
       
   392      * If "port" is non-positive, uses default port.
       
   393      * If "socketFactory" is null, uses the default socket.
       
   394      */
       
   395     private static Registry getRegistry(String host, int port,
       
   396                 RMIClientSocketFactory socketFactory)
       
   397             throws NamingException
       
   398     {
       
   399         // %%% We could cache registry connections here.  The transport layer
       
   400         // may already reuse connections.
       
   401         try {
       
   402             if (socketFactory == null) {
       
   403                 return LocateRegistry.getRegistry(host, port);
       
   404             } else {
       
   405                 return LocateRegistry.getRegistry(host, port, socketFactory);
       
   406             }
       
   407         } catch (RemoteException e) {
       
   408             throw (NamingException)wrapRemoteException(e).fillInStackTrace();
       
   409         }
       
   410     }
       
   411 
       
   412     /**
       
   413      * Attempts to install a security manager if none is currently in
       
   414      * place.
       
   415      */
       
   416     private static void installSecurityMgr() {
       
   417 
       
   418         try {
       
   419             System.setSecurityManager(new RMISecurityManager());
       
   420         } catch (Exception e) {
       
   421         }
       
   422     }
       
   423 
       
   424     /**
       
   425      * Encodes an object prior to binding it in the registry.  First,
       
   426      * NamingManager.getStateToBind() is invoked.  If the resulting
       
   427      * object is Remote, it is returned.  If it is a Reference or
       
   428      * Referenceable, the reference is wrapped in a Remote object.
       
   429      * Otherwise, an exception is thrown.
       
   430      *
       
   431      * @param name      The object's name relative to this context.
       
   432      */
       
   433     private Remote encodeObject(Object obj, Name name)
       
   434             throws NamingException, RemoteException
       
   435     {
       
   436         obj = NamingManager.getStateToBind(obj, name, this, environment);
       
   437 
       
   438         if (obj instanceof Remote) {
       
   439             return (Remote)obj;
       
   440         }
       
   441         if (obj instanceof Reference) {
       
   442             return (new ReferenceWrapper((Reference)obj));
       
   443         }
       
   444         if (obj instanceof Referenceable) {
       
   445             return (new ReferenceWrapper(((Referenceable)obj).getReference()));
       
   446         }
       
   447         throw (new IllegalArgumentException(
       
   448                 "RegistryContext: " +
       
   449                 "object to bind must be Remote, Reference, or Referenceable"));
       
   450     }
       
   451 
       
   452     /**
       
   453      * Decodes an object that has been retrieved from the registry.
       
   454      * First, if the object is a RemoteReference, the Reference is
       
   455      * unwrapped.  Then, NamingManager.getObjectInstance() is invoked.
       
   456      *
       
   457      * @param name      The object's name relative to this context.
       
   458      */
       
   459     private Object decodeObject(Remote r, Name name) throws NamingException {
       
   460         try {
       
   461             Object obj = (r instanceof RemoteReference)
       
   462                         ? ((RemoteReference)r).getReference()
       
   463                         : (Object)r;
       
   464             return NamingManager.getObjectInstance(obj, name, this,
       
   465                                                    environment);
       
   466         } catch (NamingException e) {
       
   467             throw e;
       
   468         } catch (RemoteException e) {
       
   469             throw (NamingException)
       
   470                 wrapRemoteException(e).fillInStackTrace();
       
   471         } catch (Exception e) {
       
   472             NamingException ne = new NamingException();
       
   473             ne.setRootCause(e);
       
   474             throw ne;
       
   475         }
       
   476     }
       
   477 
       
   478 }
       
   479 
       
   480 
       
   481 /**
       
   482  * A name parser for case-sensitive atomic names.
       
   483  */
       
   484 class AtomicNameParser implements NameParser {
       
   485     private static final Properties syntax = new Properties();
       
   486 
       
   487     public Name parse(String name) throws NamingException {
       
   488         return (new CompoundName(name, syntax));
       
   489     }
       
   490 }
       
   491 
       
   492 
       
   493 /**
       
   494  * An enumeration of name / class-name pairs.
       
   495  */
       
   496 class NameClassPairEnumeration implements NamingEnumeration<NameClassPair> {
       
   497     private final String[] names;
       
   498     private int nextName;       // index into "names"
       
   499 
       
   500     NameClassPairEnumeration(String[] names) {
       
   501         this.names = names;
       
   502         nextName = 0;
       
   503     }
       
   504 
       
   505     public boolean hasMore() {
       
   506         return (nextName < names.length);
       
   507     }
       
   508 
       
   509     public NameClassPair next() throws NamingException {
       
   510         if (!hasMore()) {
       
   511             throw (new java.util.NoSuchElementException());
       
   512         }
       
   513         // Convert name to a one-element composite name, so embedded
       
   514         // meta-characters are properly escaped.
       
   515         String name = names[nextName++];
       
   516         Name cname = (new CompositeName()).add(name);
       
   517         NameClassPair ncp = new NameClassPair(cname.toString(),
       
   518                                             "java.lang.Object");
       
   519         ncp.setNameInNamespace(name);
       
   520         return ncp;
       
   521     }
       
   522 
       
   523     public boolean hasMoreElements() {
       
   524         return hasMore();
       
   525     }
       
   526 
       
   527     public NameClassPair nextElement() {
       
   528         try {
       
   529             return next();
       
   530         } catch (NamingException e) {   // should never happen
       
   531             throw (new java.util.NoSuchElementException(
       
   532                     "javax.naming.NamingException was thrown"));
       
   533         }
       
   534     }
       
   535 
       
   536     public void close() {
       
   537         nextName = names.length;
       
   538     }
       
   539 }
       
   540 
       
   541 
       
   542 /**
       
   543  * An enumeration of Bindings.
       
   544  *
       
   545  * The actual registry lookups are performed when next() is called.  It would
       
   546  * be nicer to defer this until the object (or its class name) is actually
       
   547  * requested.  The problem with that approach is that Binding.getObject()
       
   548  * cannot throw NamingException.
       
   549  */
       
   550 class BindingEnumeration implements NamingEnumeration<Binding> {
       
   551     private RegistryContext ctx;
       
   552     private final String[] names;
       
   553     private int nextName;       // index into "names"
       
   554 
       
   555     BindingEnumeration(RegistryContext ctx, String[] names) {
       
   556         // Clone ctx in case someone closes it before we're through.
       
   557         this.ctx = new RegistryContext(ctx);
       
   558         this.names = names;
       
   559         nextName = 0;
       
   560     }
       
   561 
       
   562     protected void finalize() {
       
   563         ctx.close();
       
   564     }
       
   565 
       
   566     public boolean hasMore() {
       
   567         if (nextName >= names.length) {
       
   568             ctx.close();
       
   569         }
       
   570         return (nextName < names.length);
       
   571     }
       
   572 
       
   573     public Binding next() throws NamingException {
       
   574         if (!hasMore()) {
       
   575             throw (new java.util.NoSuchElementException());
       
   576         }
       
   577         // Convert name to a one-element composite name, so embedded
       
   578         // meta-characters are properly escaped.
       
   579         String name = names[nextName++];
       
   580         Name cname = (new CompositeName()).add(name);
       
   581 
       
   582         Object obj = ctx.lookup(cname);
       
   583         String cnameStr = cname.toString();
       
   584         Binding binding = new Binding(cnameStr, obj);
       
   585         binding.setNameInNamespace(cnameStr);
       
   586         return binding;
       
   587     }
       
   588 
       
   589     public boolean hasMoreElements() {
       
   590         return hasMore();
       
   591     }
       
   592 
       
   593     public Binding nextElement() {
       
   594         try {
       
   595             return next();
       
   596         } catch (NamingException e) {
       
   597             throw (new java.util.NoSuchElementException(
       
   598                     "javax.naming.NamingException was thrown"));
       
   599         }
       
   600     }
       
   601 
       
   602     public void close () {
       
   603         finalize();
       
   604     }
       
   605 }