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