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      */
Naming()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      */
lookup(String name)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      */
bind(String name, Remote obj)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      */
unbind(String name)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      */
rebind(String name, Remote obj)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      */
list(String name)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.isEmpty())
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      */
getRegistry(ParsedNamingURL parsed)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      */
parseURL(String str)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 
intParseURL(String str)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 
ParsedNamingURL(String host, int port, String name)350         ParsedNamingURL(String host, int port, String name) {
351             this.host = host;
352             this.port = port;
353             this.name = name;
354         }
355     }
356 }
357