View Javadoc
1   /*
2    * Copyright (c) 1999, 2013, 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.toolkit.url;
27  
28  import javax.naming.*;
29  import javax.naming.spi.ResolveResult;
30  import javax.naming.spi.NamingManager;
31  
32  import java.util.Hashtable;
33  import java.net.MalformedURLException;
34  
35  /**
36   * This abstract class is a generic URL context that accepts as the
37   * name argument either a string URL or a Name whose first component
38   * is a URL. It resolves the URL to a target context and then continues
39   * the operation using the remaining name in the target context as if
40   * the first component names a junction.
41   *
42   * A subclass must define getRootURLContext()
43   * to process the URL into head/tail pieces. If it wants to control how
44   * URL strings are parsed and compared for the rename() operation, then
45   * it should override getNonRootURLSuffixes() and urlEquals().
46   *
47   * @author Scott Seligman
48   * @author Rosanna Lee
49   */
50  abstract public class GenericURLContext implements Context {
51      protected Hashtable<String, Object> myEnv = null;
52  
53      @SuppressWarnings("unchecked") // Expect Hashtable<String, Object>
54      public GenericURLContext(Hashtable<?,?> env) {
55          // context that is not tied to any specific URL
56          myEnv =
57              (Hashtable<String, Object>)(env == null ? null : env.clone());
58      }
59  
60      public void close() throws NamingException {
61          myEnv = null;
62      }
63  
64      public String getNameInNamespace() throws NamingException {
65          return ""; // %%% check this out: A URL context's name is ""
66      }
67  
68      /**
69        * Resolves 'name' into a target context with remaining name.
70        * For example, with a JNDI URL "jndi://dnsname/rest_name",
71        * this method resolves "jndi://dnsname/" to a target context,
72        * and returns the target context with "rest_name".
73        * The definition of "root URL" and how much of the URL to
74        * consume is implementation specific.
75        * If rename() is supported for a particular URL scheme,
76        * getRootURLContext(), getURLPrefix(), and getURLSuffix()
77        * must be in sync wrt how URLs are parsed and returned.
78        */
79      abstract protected ResolveResult getRootURLContext(String url,
80          Hashtable<?,?> env) throws NamingException;
81  
82      /**
83        * Returns the suffix of the url. The result should be identical to
84        * that of calling getRootURLContext().getRemainingName(), but
85        * without the overhead of doing anything with the prefix like
86        * creating a context.
87        *<p>
88        * This method returns a Name instead of a String because to give
89        * the provider an opportunity to return a Name (for example,
90        * for weakly separated naming systems like COS naming).
91        *<p>
92        * The default implementation uses skips 'prefix', calls
93        * UrlUtil.decode() on it, and returns the result as a single component
94        * CompositeName.
95        * Subclass should override if this is not appropriate.
96        * This method is used only by rename().
97        * If rename() is supported for a particular URL scheme,
98        * getRootURLContext(), getURLPrefix(), and getURLSuffix()
99        * must be in sync wrt how URLs are parsed and returned.
100       *<p>
101       * For many URL schemes, this method is very similar to URL.getFile(),
102       * except getFile() will return a leading slash in the
103       * 2nd, 3rd, and 4th cases. For schemes like "ldap" and "iiop",
104       * the leading slash must be skipped before the name is an acceptable
105       * format for operation by the Context methods. For schemes that treat the
106       * leading slash as significant (such as "file"),
107       * the subclass must override getURLSuffix() to get the correct behavior.
108       * Remember, the behavior must match getRootURLContext().
109       *
110       * URL                                     Suffix
111       * foo://host:port                         <empty string>
112       * foo://host:port/rest/of/name            rest/of/name
113       * foo:///rest/of/name                     rest/of/name
114       * foo:/rest/of/name                       rest/of/name
115       * foo:rest/of/name                        rest/of/name
116       */
117     protected Name getURLSuffix(String prefix, String url) throws NamingException {
118         String suffix = url.substring(prefix.length());
119         if (suffix.length() == 0) {
120             return new CompositeName();
121         }
122 
123         if (suffix.charAt(0) == '/') {
124             suffix = suffix.substring(1); // skip leading slash
125         }
126 
127         try {
128             return new CompositeName().add(UrlUtil.decode(suffix));
129         } catch (MalformedURLException e) {
130             throw new InvalidNameException(e.getMessage());
131         }
132     }
133 
134     /**
135       * Finds the prefix of a URL.
136       * Default implementation looks for slashes and then extracts
137       * prefixes using String.substring().
138       * Subclass should override if this is not appropriate.
139       * This method is used only by rename().
140       * If rename() is supported for a particular URL scheme,
141       * getRootURLContext(), getURLPrefix(), and getURLSuffix()
142       * must be in sync wrt how URLs are parsed and returned.
143       *<p>
144       * URL                                     Prefix
145       * foo://host:port                         foo://host:port
146       * foo://host:port/rest/of/name            foo://host:port
147       * foo:///rest/of/name                     foo://
148       * foo:/rest/of/name                       foo:
149       * foo:rest/of/name                        foo:
150       */
151     protected String getURLPrefix(String url) throws NamingException {
152         int start = url.indexOf(":");
153 
154         if (start < 0) {
155             throw new OperationNotSupportedException("Invalid URL: " + url);
156         }
157         ++start; // skip ':'
158 
159         if (url.startsWith("//", start)) {
160             start += 2;  // skip double slash
161 
162             // find last slash
163             int posn = url.indexOf("/", start);
164             if (posn >= 0) {
165                 start = posn;
166             } else {
167                 start = url.length();  // rest of URL
168             }
169         }
170 
171         // else 0 or 1 iniitial slashes; start is unchanged
172         return url.substring(0, start);
173     }
174 
175     /**
176      * Determines whether two URLs are the same.
177      * Default implementation uses String.equals().
178      * Subclass should override if this is not appropriate.
179      * This method is used by rename().
180      */
181     protected boolean urlEquals(String url1, String url2) {
182         return url1.equals(url2);
183     }
184 
185     /**
186      * Gets the context in which to continue the operation. This method
187      * is called when this context is asked to process a multicomponent
188      * Name in which the first component is a URL.
189      * Treat the first component like a junction: resolve it and then use
190      * NamingManager.getContinuationContext() to get the target context in
191      * which to operate on the remainder of the name (n.getSuffix(1)).
192      */
193     protected Context getContinuationContext(Name n) throws NamingException {
194         Object obj = lookup(n.get(0));
195         CannotProceedException cpe = new CannotProceedException();
196         cpe.setResolvedObj(obj);
197         cpe.setEnvironment(myEnv);
198         return NamingManager.getContinuationContext(cpe);
199     }
200 
201     public Object lookup(String name) throws NamingException {
202         ResolveResult res = getRootURLContext(name, myEnv);
203         Context ctx = (Context)res.getResolvedObj();
204         try {
205             return ctx.lookup(res.getRemainingName());
206         } finally {
207             ctx.close();
208         }
209     }
210 
211     public Object lookup(Name name) throws NamingException {
212         if (name.size() == 1) {
213             return lookup(name.get(0));
214         } else {
215             Context ctx = getContinuationContext(name);
216             try {
217                 return ctx.lookup(name.getSuffix(1));
218             } finally {
219                 ctx.close();
220             }
221         }
222     }
223 
224     public void bind(String name, Object obj) throws NamingException {
225         ResolveResult res = getRootURLContext(name, myEnv);
226         Context ctx = (Context)res.getResolvedObj();
227         try {
228             ctx.bind(res.getRemainingName(), obj);
229         } finally {
230             ctx.close();
231         }
232     }
233 
234     public void bind(Name name, Object obj) throws NamingException {
235         if (name.size() == 1) {
236             bind(name.get(0), obj);
237         } else {
238             Context ctx = getContinuationContext(name);
239             try {
240                 ctx.bind(name.getSuffix(1), obj);
241             } finally {
242                 ctx.close();
243             }
244         }
245     }
246 
247     public void rebind(String name, Object obj) throws NamingException {
248         ResolveResult res = getRootURLContext(name, myEnv);
249         Context ctx = (Context)res.getResolvedObj();
250         try {
251             ctx.rebind(res.getRemainingName(), obj);
252         } finally {
253             ctx.close();
254         }
255     }
256 
257     public void rebind(Name name, Object obj) throws NamingException {
258         if (name.size() == 1) {
259             rebind(name.get(0), obj);
260         } else {
261             Context ctx = getContinuationContext(name);
262             try {
263                 ctx.rebind(name.getSuffix(1), obj);
264             } finally {
265                 ctx.close();
266             }
267         }
268     }
269 
270     public void unbind(String name) throws NamingException {
271         ResolveResult res = getRootURLContext(name, myEnv);
272         Context ctx = (Context)res.getResolvedObj();
273         try {
274             ctx.unbind(res.getRemainingName());
275         } finally {
276             ctx.close();
277         }
278     }
279 
280     public void unbind(Name name) throws NamingException {
281         if (name.size() == 1) {
282             unbind(name.get(0));
283         } else {
284             Context ctx = getContinuationContext(name);
285             try {
286                 ctx.unbind(name.getSuffix(1));
287             } finally {
288                 ctx.close();
289             }
290         }
291     }
292 
293     public void rename(String oldName, String newName) throws NamingException {
294         String oldPrefix = getURLPrefix(oldName);
295         String newPrefix = getURLPrefix(newName);
296         if (!urlEquals(oldPrefix, newPrefix)) {
297             throw new OperationNotSupportedException(
298                 "Renaming using different URL prefixes not supported : " +
299                 oldName + " " + newName);
300         }
301 
302         ResolveResult res = getRootURLContext(oldName, myEnv);
303         Context ctx = (Context)res.getResolvedObj();
304         try {
305             ctx.rename(res.getRemainingName(), getURLSuffix(newPrefix, newName));
306         } finally {
307             ctx.close();
308         }
309     }
310 
311     public void rename(Name name, Name newName) throws NamingException {
312         if (name.size() == 1) {
313             if (newName.size() != 1) {
314                 throw new OperationNotSupportedException(
315             "Renaming to a Name with more components not supported: " + newName);
316             }
317             rename(name.get(0), newName.get(0));
318         } else {
319             // > 1 component with 1st one being URL
320             // URLs must be identical; cannot deal with diff URLs
321             if (!urlEquals(name.get(0), newName.get(0))) {
322                 throw new OperationNotSupportedException(
323                     "Renaming using different URLs as first components not supported: " +
324                     name + " " + newName);
325             }
326 
327             Context ctx = getContinuationContext(name);
328             try {
329                 ctx.rename(name.getSuffix(1), newName.getSuffix(1));
330             } finally {
331                 ctx.close();
332             }
333         }
334     }
335 
336     public NamingEnumeration<NameClassPair> list(String name)   throws NamingException {
337         ResolveResult res = getRootURLContext(name, myEnv);
338         Context ctx = (Context)res.getResolvedObj();
339         try {
340             return ctx.list(res.getRemainingName());
341         } finally {
342             ctx.close();
343         }
344     }
345 
346     public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
347         if (name.size() == 1) {
348             return list(name.get(0));
349         } else {
350             Context ctx = getContinuationContext(name);
351             try {
352                 return ctx.list(name.getSuffix(1));
353             } finally {
354                 ctx.close();
355             }
356         }
357     }
358 
359     public NamingEnumeration<Binding> listBindings(String name)
360         throws NamingException {
361         ResolveResult res = getRootURLContext(name, myEnv);
362         Context ctx = (Context)res.getResolvedObj();
363         try {
364             return ctx.listBindings(res.getRemainingName());
365         } finally {
366             ctx.close();
367         }
368     }
369 
370     public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
371         if (name.size() == 1) {
372             return listBindings(name.get(0));
373         } else {
374             Context ctx = getContinuationContext(name);
375             try {
376                 return ctx.listBindings(name.getSuffix(1));
377             } finally {
378                 ctx.close();
379             }
380         }
381     }
382 
383     public void destroySubcontext(String name) throws NamingException {
384         ResolveResult res = getRootURLContext(name, myEnv);
385         Context ctx = (Context)res.getResolvedObj();
386         try {
387             ctx.destroySubcontext(res.getRemainingName());
388         } finally {
389             ctx.close();
390         }
391     }
392 
393     public void destroySubcontext(Name name) throws NamingException {
394         if (name.size() == 1) {
395             destroySubcontext(name.get(0));
396         } else {
397             Context ctx = getContinuationContext(name);
398             try {
399                 ctx.destroySubcontext(name.getSuffix(1));
400             } finally {
401                 ctx.close();
402             }
403         }
404     }
405 
406     public Context createSubcontext(String name) throws NamingException {
407         ResolveResult res = getRootURLContext(name, myEnv);
408         Context ctx = (Context)res.getResolvedObj();
409         try {
410             return ctx.createSubcontext(res.getRemainingName());
411         } finally {
412             ctx.close();
413         }
414     }
415 
416     public Context createSubcontext(Name name) throws NamingException {
417         if (name.size() == 1) {
418             return createSubcontext(name.get(0));
419         } else {
420             Context ctx = getContinuationContext(name);
421             try {
422                 return ctx.createSubcontext(name.getSuffix(1));
423             } finally {
424                 ctx.close();
425             }
426         }
427     }
428 
429     public Object lookupLink(String name) throws NamingException {
430         ResolveResult res = getRootURLContext(name, myEnv);
431         Context ctx = (Context)res.getResolvedObj();
432         try {
433             return ctx.lookupLink(res.getRemainingName());
434         } finally {
435             ctx.close();
436         }
437     }
438 
439     public Object lookupLink(Name name) throws NamingException {
440         if (name.size() == 1) {
441             return lookupLink(name.get(0));
442         } else {
443             Context ctx = getContinuationContext(name);
444             try {
445                 return ctx.lookupLink(name.getSuffix(1));
446             } finally {
447                 ctx.close();
448             }
449         }
450     }
451 
452     public NameParser getNameParser(String name) throws NamingException {
453         ResolveResult res = getRootURLContext(name, myEnv);
454         Context ctx = (Context)res.getResolvedObj();
455         try {
456             return ctx.getNameParser(res.getRemainingName());
457         } finally {
458             ctx.close();
459         }
460     }
461 
462     public NameParser getNameParser(Name name) throws NamingException {
463         if (name.size() == 1) {
464             return getNameParser(name.get(0));
465         } else {
466             Context ctx = getContinuationContext(name);
467             try {
468                 return ctx.getNameParser(name.getSuffix(1));
469             } finally {
470                 ctx.close();
471             }
472         }
473     }
474 
475     public String composeName(String name, String prefix)
476         throws NamingException {
477             if (prefix.equals("")) {
478                 return name;
479             } else if (name.equals("")) {
480                 return prefix;
481             } else {
482                 return (prefix + "/" + name);
483             }
484     }
485 
486     public Name composeName(Name name, Name prefix) throws NamingException {
487         Name result = (Name)prefix.clone();
488         result.addAll(name);
489         return result;
490     }
491 
492     public Object removeFromEnvironment(String propName)
493         throws NamingException {
494             if (myEnv == null) {
495                 return null;
496             }
497             return myEnv.remove(propName);
498     }
499 
500     public Object addToEnvironment(String propName, Object propVal)
501         throws NamingException {
502             if (myEnv == null) {
503                 myEnv = new Hashtable<String, Object>(11, 0.75f);
504             }
505             return myEnv.put(propName, propVal);
506     }
507 
508     @SuppressWarnings("unchecked") // clone()
509     public Hashtable<String, Object> getEnvironment() throws NamingException {
510         if (myEnv == null) {
511             return new Hashtable<>(5, 0.75f);
512         } else {
513             return (Hashtable<String, Object>)myEnv.clone();
514         }
515     }
516 
517 /*
518 // To test, declare getURLPrefix and getURLSuffix static.
519 
520     public static void main(String[] args) throws Exception {
521         String[] tests = {"file://host:port",
522                           "file:///rest/of/name",
523                           "file://host:port/rest/of/name",
524                           "file:/rest/of/name",
525                           "file:rest/of/name"};
526         for (int i = 0; i < tests.length; i++) {
527             String pre = getURLPrefix(tests[i]);
528             System.out.println(pre);
529             System.out.println(getURLSuffix(pre, tests[i]));
530         }
531     }
532 */
533 }