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