1 /*
   2  * Copyright (c) 1996, 2005, 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 package java.rmi;
  26 
  27 import java.rmi.registry.*;
  28 import java.net.MalformedURLException;
  29 import java.net.URI;
  30 import java.net.URISyntaxException;
  31 
  32 /**
  33  * The <code>Naming</code> class provides methods for storing and obtaining
  34  * references to remote objects in a remote object registry.  Each method of
  35  * the <code>Naming</code> class takes as one of its arguments a name that
  36  * is a <code>java.lang.String</code> in URL format (without the
  37  * scheme component) of the form:
  38  *
  39  * <PRE>
  40  *    //host:port/name
  41  * </PRE>
  42  *
  43  * <P>where <code>host</code> is the host (remote or local) where the registry
  44  * is located, <code>port</code> is the port number on which the registry
  45  * accepts calls, and where <code>name</code> is a simple string uninterpreted
  46  * by the registry. Both <code>host</code> and <code>port</code> are optional.
  47  * If <code>host</code> is omitted, the host defaults to the local host. If
  48  * <code>port</code> is omitted, then the port defaults to 1099, the
  49  * "well-known" port that RMI's registry, <code>rmiregistry</code>, uses.
  50  *
  51  * <P><em>Binding</em> a name for a remote object is associating or
  52  * registering a name for a remote object that can be used at a later time to
  53  * look up that remote object.  A remote object can be associated with a name
  54  * using the <code>Naming</code> class's <code>bind</code> or
  55  * <code>rebind</code> methods.
  56  *
  57  * <P>Once a remote object is registered (bound) with the RMI registry on the
  58  * local host, callers on a remote (or local) host can lookup the remote
  59  * object by name, obtain its reference, and then invoke remote methods on the
  60  * object.  A registry may be shared by all servers running on a host or an
  61  * individual server process may create and use its own registry if desired
  62  * (see <code>java.rmi.registry.LocateRegistry.createRegistry</code> method
  63  * for details).
  64  *
  65  * @author  Ann Wollrath
  66  * @author  Roger Riggs
  67  * @since   1.1
  68  * @see     java.rmi.registry.Registry
  69  * @see     java.rmi.registry.LocateRegistry
  70  * @see     java.rmi.registry.LocateRegistry#createRegistry(int)
  71  */
  72 public final class Naming {
  73     /**
  74      * Disallow anyone from creating one of these
  75      */
  76     private Naming() {}
  77 
  78     /**
  79      * Returns a reference, a stub, for the remote object associated
  80      * with the specified <code>name</code>.
  81      *
  82      * @param name a name in URL format (without the scheme component)
  83      * @return a reference for a remote object
  84      * @exception NotBoundException if name is not currently bound
  85      * @exception RemoteException if registry could not be contacted
  86      * @exception AccessException if this operation is not permitted
  87      * @exception MalformedURLException if the name is not an appropriately
  88      *  formatted URL
  89      * @since 1.1
  90      */
  91     public static Remote lookup(String name)
  92         throws NotBoundException,
  93             java.net.MalformedURLException,
  94             RemoteException
  95     {
  96         ParsedNamingURL parsed = parseURL(name);
  97         Registry registry = getRegistry(parsed);
  98 
  99         if (parsed.name == null)
 100             return registry;
 101         return registry.lookup(parsed.name);
 102     }
 103 
 104     /**
 105      * Binds the specified <code>name</code> to a remote object.
 106      *
 107      * @param name a name in URL format (without the scheme component)
 108      * @param obj a reference for the remote object (usually a stub)
 109      * @exception AlreadyBoundException if name is already bound
 110      * @exception MalformedURLException if the name is not an appropriately
 111      *  formatted URL
 112      * @exception RemoteException if registry could not be contacted
 113      * @exception AccessException if this operation is not permitted (if
 114      * originating from a non-local host, for example)
 115      * @since 1.1
 116      */
 117     public static void bind(String name, Remote obj)
 118         throws AlreadyBoundException,
 119             java.net.MalformedURLException,
 120             RemoteException
 121     {
 122         ParsedNamingURL parsed = parseURL(name);
 123         Registry registry = getRegistry(parsed);
 124 
 125         if (obj == null)
 126             throw new NullPointerException("cannot bind to null");
 127 
 128         registry.bind(parsed.name, obj);
 129     }
 130 
 131     /**
 132      * Destroys the binding for the specified name that is associated
 133      * with a remote object.
 134      *
 135      * @param name a name in URL format (without the scheme component)
 136      * @exception NotBoundException if name is not currently bound
 137      * @exception MalformedURLException if the name is not an appropriately
 138      *  formatted URL
 139      * @exception RemoteException if registry could not be contacted
 140      * @exception AccessException if this operation is not permitted (if
 141      * originating from a non-local host, for example)
 142      * @since 1.1
 143      */
 144     public static void unbind(String name)
 145         throws RemoteException,
 146             NotBoundException,
 147             java.net.MalformedURLException
 148     {
 149         ParsedNamingURL parsed = parseURL(name);
 150         Registry registry = getRegistry(parsed);
 151 
 152         registry.unbind(parsed.name);
 153     }
 154 
 155     /**
 156      * Rebinds the specified name to a new remote object. Any existing
 157      * binding for the name is replaced.
 158      *
 159      * @param name a name in URL format (without the scheme component)
 160      * @param obj new remote object to associate with the name
 161      * @exception MalformedURLException if the name is not an appropriately
 162      *  formatted URL
 163      * @exception RemoteException if registry could not be contacted
 164      * @exception AccessException if this operation is not permitted (if
 165      * originating from a non-local host, for example)
 166      * @since 1.1
 167      */
 168     public static void rebind(String name, Remote obj)
 169         throws RemoteException, java.net.MalformedURLException
 170     {
 171         ParsedNamingURL parsed = parseURL(name);
 172         Registry registry = getRegistry(parsed);
 173 
 174         if (obj == null)
 175             throw new NullPointerException("cannot bind to null");
 176 
 177         registry.rebind(parsed.name, obj);
 178     }
 179 
 180     /**
 181      * Returns an array of the names bound in the registry.  The names are
 182      * URL-formatted (without the scheme component) strings. The array contains
 183      * a snapshot of the names present in the registry at the time of the
 184      * call.
 185      *
 186      * @param   name a registry name in URL format (without the scheme
 187      *          component)
 188      * @return  an array of names (in the appropriate format) bound
 189      *          in the registry
 190      * @exception MalformedURLException if the name is not an appropriately
 191      *  formatted URL
 192      * @exception RemoteException if registry could not be contacted.
 193      * @since 1.1
 194      */
 195     public static String[] list(String name)
 196         throws RemoteException, java.net.MalformedURLException
 197     {
 198         ParsedNamingURL parsed = parseURL(name);
 199         Registry registry = getRegistry(parsed);
 200 
 201         String prefix = "";
 202         if (parsed.port > 0 || !parsed.host.equals(""))
 203             prefix += "//" + parsed.host;
 204         if (parsed.port > 0)
 205             prefix += ":" + parsed.port;
 206         prefix += "/";
 207 
 208         String[] names = registry.list();
 209         for (int i = 0; i < names.length; i++) {
 210             names[i] = prefix + names[i];
 211         }
 212         return names;
 213     }
 214 
 215     /**
 216      * Returns a registry reference obtained from information in the URL.
 217      */
 218     private static Registry getRegistry(ParsedNamingURL parsed)
 219         throws RemoteException
 220     {
 221         return LocateRegistry.getRegistry(parsed.host, parsed.port);
 222     }
 223 
 224     /**
 225      * Dissect Naming URL strings to obtain referenced host, port and
 226      * object name.
 227      *
 228      * @return an object which contains each of the above
 229      * components.
 230      *
 231      * @exception MalformedURLException if given url string is malformed
 232      */
 233     private static ParsedNamingURL parseURL(String str)
 234         throws MalformedURLException
 235     {
 236         try {
 237             return intParseURL(str);
 238         } catch (URISyntaxException ex) {
 239             /* With RFC 3986 URI handling, 'rmi://:<port>' and
 240              * '//:<port>' forms will result in a URI syntax exception
 241              * Convert the authority to a localhost:<port> form
 242              */
 243             MalformedURLException mue = new MalformedURLException(
 244                 "invalid URL String: " + str);
 245             mue.initCause(ex);
 246             int indexSchemeEnd = str.indexOf(':');
 247             int indexAuthorityBegin = str.indexOf("//:");
 248             if (indexAuthorityBegin < 0) {
 249                 throw mue;
 250             }
 251             if ((indexAuthorityBegin == 0) ||
 252                     ((indexSchemeEnd > 0) &&
 253                     (indexAuthorityBegin == indexSchemeEnd + 1))) {
 254                 int indexHostBegin = indexAuthorityBegin + 2;
 255                 String newStr = str.substring(0, indexHostBegin) +
 256                                 "localhost" +
 257                                 str.substring(indexHostBegin);
 258                 try {
 259                     return intParseURL(newStr);
 260                 } catch (URISyntaxException inte) {
 261                     throw mue;
 262                 } catch (MalformedURLException inte) {
 263                     throw inte;
 264                 }
 265             }
 266             throw mue;
 267         }
 268     }
 269 
 270     private static ParsedNamingURL intParseURL(String str)
 271         throws MalformedURLException, URISyntaxException
 272     {
 273         URI uri = new URI(str);
 274         if (uri.isOpaque()) {
 275             throw new MalformedURLException(
 276                 "not a hierarchical URL: " + str);
 277         }
 278         if (uri.getFragment() != null) {
 279             throw new MalformedURLException(
 280                 "invalid character, '#', in URL name: " + str);
 281         } else if (uri.getQuery() != null) {
 282             throw new MalformedURLException(
 283                 "invalid character, '?', in URL name: " + str);
 284         } else if (uri.getUserInfo() != null) {
 285             throw new MalformedURLException(
 286                 "invalid character, '@', in URL host: " + str);
 287         }
 288         String scheme = uri.getScheme();
 289         if (scheme != null && !scheme.equals("rmi")) {
 290             throw new MalformedURLException("invalid URL scheme: " + str);
 291         }
 292 
 293         String name = uri.getPath();
 294         if (name != null) {
 295             if (name.startsWith("/")) {
 296                 name = name.substring(1);
 297             }
 298             if (name.length() == 0) {
 299                 name = null;
 300             }
 301         }
 302 
 303         String host = uri.getHost();
 304         if (host == null) {
 305             host = "";
 306             try {
 307                 /*
 308                  * With 2396 URI handling, forms such as 'rmi://host:bar'
 309                  * or 'rmi://:<port>' are parsed into a registry based
 310                  * authority. We only want to allow server based naming
 311                  * authorities.
 312                  */
 313                 uri.parseServerAuthority();
 314             } catch (URISyntaxException use) {
 315                 // Check if the authority is of form ':<port>'
 316                 String authority = uri.getAuthority();
 317                 if (authority != null && authority.startsWith(":")) {
 318                     // Convert the authority to 'localhost:<port>' form
 319                     authority = "localhost" + authority;
 320                     try {
 321                         uri = new URI(null, authority, null, null, null);
 322                         // Make sure it now parses to a valid server based
 323                         // naming authority
 324                         uri.parseServerAuthority();
 325                     } catch (URISyntaxException use2) {
 326                         throw new
 327                             MalformedURLException("invalid authority: " + str);
 328                     }
 329                 } else {
 330                     throw new
 331                         MalformedURLException("invalid authority: " + str);
 332                 }
 333             }
 334         }
 335         int port = uri.getPort();
 336         if (port == -1) {
 337             port = Registry.REGISTRY_PORT;
 338         }
 339         return new ParsedNamingURL(host, port, name);
 340     }
 341 
 342     /**
 343      * Simple class to enable multiple URL return values.
 344      */
 345     private static class ParsedNamingURL {
 346         String host;
 347         int port;
 348         String name;
 349 
 350         ParsedNamingURL(String host, int port, String name) {
 351             this.host = host;
 352             this.port = port;
 353             this.name = name;
 354         }
 355     }
 356 }