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.ctx;
  27 
  28 import java.util.Hashtable;
  29 import java.util.Enumeration;
  30 
  31 import javax.naming.*;
  32 import javax.naming.spi.Resolver;
  33 import javax.naming.spi.ResolveResult;
  34 import javax.naming.spi.NamingManager;
  35 
  36 /**
  37   * PartialCompositeContext implements Context operations on
  38   * composite names using implementations of the p_ interfaces
  39   * defined by its subclasses.
  40   *
  41   * The main purpose provided by this class is that it deals with
  42   * partial resolutions and continuations, so that callers of the
  43   * Context operation don't have to.
  44   *
  45   * Types of clients that will be direct subclasses of
  46   * PartialCompositeContext may be service providers that implement
  47   * one of the JNDI protocols, but which do not deal with
  48   * continuations.  Usually, service providers will be using
  49   * one of the subclasses of PartialCompositeContext.
  50   *
  51   * @author Rosanna Lee
  52   */
  53 
  54 
  55 public abstract class PartialCompositeContext implements Context, Resolver {
  56     protected static final int _PARTIAL = 1;
  57     protected static final int _COMPONENT = 2;
  58     protected static final int _ATOMIC = 3;
  59 
  60     protected int _contextType = _PARTIAL;
  61 
  62     static final CompositeName _EMPTY_NAME = new CompositeName();
  63     static CompositeName _NNS_NAME;
  64 
  65     static {
  66         try {
  67             _NNS_NAME = new CompositeName("/");
  68         } catch (InvalidNameException e) {
  69             // Should never happen
  70         }
  71     }
  72 
  73     protected PartialCompositeContext() {
  74     }
  75 
  76 // ------ Abstract methods whose implementations come from subclasses
  77 
  78     /* Equivalent to method in  Resolver interface */
  79     protected abstract ResolveResult p_resolveToClass(Name name,
  80         Class<?> contextType, Continuation cont) throws NamingException;
  81 
  82     /* Equivalent to methods in Context interface */
  83     protected abstract Object p_lookup(Name name, Continuation cont)
  84         throws NamingException;
  85     protected abstract Object p_lookupLink(Name name, Continuation cont)
  86         throws NamingException;
  87     protected abstract NamingEnumeration<NameClassPair> p_list(Name name,
  88         Continuation cont) throws NamingException;
  89     protected abstract NamingEnumeration<Binding> p_listBindings(Name name,
  90         Continuation cont) throws NamingException;
  91     protected abstract void p_bind(Name name, Object obj, Continuation cont)
  92         throws NamingException;
  93     protected abstract void p_rebind(Name name, Object obj, Continuation cont)
  94         throws NamingException;
  95     protected abstract void p_unbind(Name name, Continuation cont)
  96         throws NamingException;
  97     protected abstract void p_destroySubcontext(Name name, Continuation cont)
  98         throws NamingException;
  99     protected abstract Context p_createSubcontext(Name name, Continuation cont)
 100         throws NamingException;
 101     protected abstract void p_rename(Name oldname, Name newname,
 102                                      Continuation cont)
 103         throws NamingException;
 104     protected abstract NameParser p_getNameParser(Name name, Continuation cont)
 105         throws NamingException;
 106 
 107 // ------ should be overridden by subclass;
 108 // ------ not abstract only for backward compatibility
 109 
 110     /**
 111      * A cheap way of getting the environment.
 112      * Default implementation is NOT cheap because it simply calls
 113      * getEnvironment(), which most implementations clone before returning.
 114      * Subclass should ALWAYS override this with the cheapest possible way.
 115      * The toolkit knows to clone when necessary.
 116      * @return The possibly null environment of the context.
 117      */
 118     protected Hashtable<?,?> p_getEnvironment() throws NamingException {
 119         return getEnvironment();
 120     }
 121 
 122 
 123 // ------ implementations of methods in Resolver and Context
 124 // ------ using corresponding p_ methods provided by subclass
 125 
 126     /* implementations for method in Resolver interface using p_ method */
 127 
 128     public ResolveResult resolveToClass(String name,
 129                                         Class<? extends Context> contextType)
 130         throws NamingException
 131     {
 132         return resolveToClass(new CompositeName(name), contextType);
 133     }
 134 
 135     public ResolveResult resolveToClass(Name name,
 136                                         Class<? extends Context> contextType)
 137         throws NamingException
 138     {
 139         PartialCompositeContext ctx = this;
 140         Hashtable<?,?> env = p_getEnvironment();
 141         Continuation cont = new Continuation(name, env);
 142         ResolveResult answer;
 143         Name nm = name;
 144 
 145         try {
 146             answer = ctx.p_resolveToClass(nm, contextType, cont);
 147             while (cont.isContinue()) {
 148                 nm = cont.getRemainingName();
 149                 ctx = getPCContext(cont);
 150                 answer = ctx.p_resolveToClass(nm, contextType, cont);
 151             }
 152         } catch (CannotProceedException e) {
 153             Context cctx = NamingManager.getContinuationContext(e);
 154             if (!(cctx instanceof Resolver)) {
 155                 throw e;
 156             }
 157             answer = ((Resolver)cctx).resolveToClass(e.getRemainingName(),
 158                                                      contextType);
 159         }
 160         return answer;
 161     }
 162 
 163     /* implementations for methods in Context interface using p_ methods */
 164 
 165     public Object lookup(String name) throws NamingException {
 166         return lookup(new CompositeName(name));
 167     }
 168 
 169     public Object lookup(Name name) throws NamingException {
 170         PartialCompositeContext ctx = this;
 171         Hashtable<?,?> env = p_getEnvironment();
 172         Continuation cont = new Continuation(name, env);
 173         Object answer;
 174         Name nm = name;
 175 
 176         try {
 177             answer = ctx.p_lookup(nm, cont);
 178             while (cont.isContinue()) {
 179                 nm = cont.getRemainingName();
 180                 ctx = getPCContext(cont);
 181                 answer = ctx.p_lookup(nm, cont);
 182             }
 183         } catch (CannotProceedException e) {
 184             Context cctx = NamingManager.getContinuationContext(e);
 185             answer = cctx.lookup(e.getRemainingName());
 186         }
 187         return answer;
 188     }
 189 
 190     public void bind(String name, Object newObj) throws NamingException {
 191         bind(new CompositeName(name), newObj);
 192     }
 193 
 194     public void bind(Name name, Object newObj) throws NamingException {
 195         PartialCompositeContext ctx = this;
 196         Name nm = name;
 197         Hashtable<?,?> env = p_getEnvironment();
 198         Continuation cont = new Continuation(name, env);
 199 
 200         try {
 201             ctx.p_bind(nm, newObj, cont);
 202             while (cont.isContinue()) {
 203                 nm = cont.getRemainingName();
 204                 ctx = getPCContext(cont);
 205                 ctx.p_bind(nm, newObj, cont);
 206             }
 207         } catch (CannotProceedException e) {
 208             Context cctx = NamingManager.getContinuationContext(e);
 209             cctx.bind(e.getRemainingName(), newObj);
 210         }
 211     }
 212 
 213     public void rebind(String name, Object newObj) throws NamingException {
 214         rebind(new CompositeName(name), newObj);
 215     }
 216     public void rebind(Name name, Object newObj) throws NamingException {
 217         PartialCompositeContext ctx = this;
 218         Name nm = name;
 219         Hashtable<?,?> env = p_getEnvironment();
 220         Continuation cont = new Continuation(name, env);
 221 
 222         try {
 223             ctx.p_rebind(nm, newObj, cont);
 224             while (cont.isContinue()) {
 225                 nm = cont.getRemainingName();
 226                 ctx = getPCContext(cont);
 227                 ctx.p_rebind(nm, newObj, cont);
 228             }
 229         } catch (CannotProceedException e) {
 230             Context cctx = NamingManager.getContinuationContext(e);
 231             cctx.rebind(e.getRemainingName(), newObj);
 232         }
 233     }
 234 
 235     public void unbind(String name) throws NamingException {
 236         unbind(new CompositeName(name));
 237     }
 238     public void unbind(Name name) throws NamingException {
 239         PartialCompositeContext ctx = this;
 240         Name nm = name;
 241         Hashtable<?,?> env = p_getEnvironment();
 242         Continuation cont = new Continuation(name, env);
 243 
 244         try {
 245             ctx.p_unbind(nm, cont);
 246             while (cont.isContinue()) {
 247                 nm = cont.getRemainingName();
 248                 ctx = getPCContext(cont);
 249                 ctx.p_unbind(nm, cont);
 250             }
 251         } catch (CannotProceedException e) {
 252             Context cctx = NamingManager.getContinuationContext(e);
 253             cctx.unbind(e.getRemainingName());
 254         }
 255     }
 256 
 257     public void rename(String oldName, String newName) throws NamingException {
 258         rename(new CompositeName(oldName), new CompositeName(newName));
 259     }
 260     public void rename(Name oldName, Name newName)
 261         throws NamingException
 262     {
 263         PartialCompositeContext ctx = this;
 264         Name nm = oldName;
 265         Hashtable<?,?> env = p_getEnvironment();
 266         Continuation cont = new Continuation(oldName, env);
 267 
 268         try {
 269             ctx.p_rename(nm, newName, cont);
 270             while (cont.isContinue()) {
 271                 nm = cont.getRemainingName();
 272                 ctx = getPCContext(cont);
 273                 ctx.p_rename(nm, newName, cont);
 274             }
 275         } catch (CannotProceedException e) {
 276             Context cctx = NamingManager.getContinuationContext(e);
 277             if (e.getRemainingNewName() != null) {
 278                 // %%% e.getRemainingNewName() should never be null
 279                 newName = e.getRemainingNewName();
 280             }
 281             cctx.rename(e.getRemainingName(), newName);
 282         }
 283     }
 284 
 285     public NamingEnumeration<NameClassPair> list(String name)
 286         throws NamingException
 287     {
 288         return list(new CompositeName(name));
 289     }
 290 
 291     public NamingEnumeration<NameClassPair> list(Name name)
 292         throws NamingException
 293     {
 294         PartialCompositeContext ctx = this;
 295         Name nm = name;
 296         NamingEnumeration<NameClassPair> answer;
 297         Hashtable<?,?> env = p_getEnvironment();
 298         Continuation cont = new Continuation(name, env);
 299 
 300         try {
 301             answer = ctx.p_list(nm, cont);
 302             while (cont.isContinue()) {
 303                 nm = cont.getRemainingName();
 304                 ctx = getPCContext(cont);
 305                 answer = ctx.p_list(nm, cont);
 306             }
 307         } catch (CannotProceedException e) {
 308             Context cctx = NamingManager.getContinuationContext(e);
 309             answer = cctx.list(e.getRemainingName());
 310         }
 311         return answer;
 312     }
 313 
 314     public NamingEnumeration<Binding> listBindings(String name)
 315         throws NamingException
 316     {
 317         return listBindings(new CompositeName(name));
 318     }
 319 
 320     public NamingEnumeration<Binding> listBindings(Name name)
 321         throws NamingException
 322     {
 323         PartialCompositeContext ctx = this;
 324         Name nm = name;
 325         NamingEnumeration<Binding> answer;
 326         Hashtable<?,?> env = p_getEnvironment();
 327         Continuation cont = new Continuation(name, env);
 328 
 329         try {
 330             answer = ctx.p_listBindings(nm, cont);
 331             while (cont.isContinue()) {
 332                 nm = cont.getRemainingName();
 333                 ctx = getPCContext(cont);
 334                 answer = ctx.p_listBindings(nm, cont);
 335             }
 336         } catch (CannotProceedException e) {
 337             Context cctx = NamingManager.getContinuationContext(e);
 338             answer = cctx.listBindings(e.getRemainingName());
 339         }
 340         return answer;
 341     }
 342 
 343     public void destroySubcontext(String name) throws NamingException {
 344         destroySubcontext(new CompositeName(name));
 345     }
 346 
 347     public void destroySubcontext(Name name) throws NamingException {
 348         PartialCompositeContext ctx = this;
 349         Name nm = name;
 350         Hashtable<?,?> env = p_getEnvironment();
 351         Continuation cont = new Continuation(name, env);
 352 
 353         try {
 354             ctx.p_destroySubcontext(nm, cont);
 355             while (cont.isContinue()) {
 356                 nm = cont.getRemainingName();
 357                 ctx = getPCContext(cont);
 358                 ctx.p_destroySubcontext(nm, cont);
 359             }
 360         } catch (CannotProceedException e) {
 361             Context cctx = NamingManager.getContinuationContext(e);
 362             cctx.destroySubcontext(e.getRemainingName());
 363         }
 364     }
 365 
 366     public Context createSubcontext(String name) throws NamingException {
 367         return createSubcontext(new CompositeName(name));
 368     }
 369 
 370     public Context createSubcontext(Name name) throws NamingException {
 371         PartialCompositeContext ctx = this;
 372         Name nm = name;
 373         Context answer;
 374         Hashtable<?,?> env = p_getEnvironment();
 375         Continuation cont = new Continuation(name, env);
 376 
 377         try {
 378             answer = ctx.p_createSubcontext(nm, cont);
 379             while (cont.isContinue()) {
 380                 nm = cont.getRemainingName();
 381                 ctx = getPCContext(cont);
 382                 answer = ctx.p_createSubcontext(nm, cont);
 383             }
 384         } catch (CannotProceedException e) {
 385             Context cctx = NamingManager.getContinuationContext(e);
 386             answer = cctx.createSubcontext(e.getRemainingName());
 387         }
 388         return answer;
 389     }
 390 
 391     public Object lookupLink(String name) throws NamingException {
 392         return lookupLink(new CompositeName(name));
 393     }
 394 
 395     public Object lookupLink(Name name) throws NamingException {
 396         PartialCompositeContext ctx = this;
 397         Hashtable<?,?> env = p_getEnvironment();
 398         Continuation cont = new Continuation(name, env);
 399         Object answer;
 400         Name nm = name;
 401 
 402         try {
 403             answer = ctx.p_lookupLink(nm, cont);
 404             while (cont.isContinue()) {
 405                 nm = cont.getRemainingName();
 406                 ctx = getPCContext(cont);
 407                 answer = ctx.p_lookupLink(nm, cont);
 408             }
 409         } catch (CannotProceedException e) {
 410             Context cctx = NamingManager.getContinuationContext(e);
 411             answer = cctx.lookupLink(e.getRemainingName());
 412         }
 413         return answer;
 414     }
 415 
 416     public NameParser getNameParser(String name) throws NamingException {
 417         return getNameParser(new CompositeName(name));
 418     }
 419 
 420     public NameParser getNameParser(Name name) throws NamingException {
 421         PartialCompositeContext ctx = this;
 422         Name nm = name;
 423         NameParser answer;
 424         Hashtable<?,?> env = p_getEnvironment();
 425         Continuation cont = new Continuation(name, env);
 426 
 427         try {
 428             answer = ctx.p_getNameParser(nm, cont);
 429             while (cont.isContinue()) {
 430                 nm = cont.getRemainingName();
 431                 ctx = getPCContext(cont);
 432                 answer = ctx.p_getNameParser(nm, cont);
 433             }
 434         } catch (CannotProceedException e) {
 435             Context cctx = NamingManager.getContinuationContext(e);
 436             answer = cctx.getNameParser(e.getRemainingName());
 437         }
 438         return answer;
 439     }
 440 
 441     public String composeName(String name, String prefix)
 442             throws NamingException {
 443         Name fullName = composeName(new CompositeName(name),
 444                                     new CompositeName(prefix));
 445         return fullName.toString();
 446     }
 447 
 448     /**
 449      * This default implementation simply concatenates the two names.
 450      * There's one twist when the "java.naming.provider.compose.elideEmpty"
 451      * environment setting is set to "true":  if each name contains a
 452      * nonempty component, and if 'prefix' ends with an empty component or
 453      * 'name' starts with one, then one empty component is dropped.
 454      * For example:
 455      * <pre>{@code
 456      *                            elideEmpty=false     elideEmpty=true
 457      * {"a"} + {"b"}          =>  {"a", "b"}           {"a", "b"}
 458      * {"a"} + {""}           =>  {"a", ""}            {"a", ""}
 459      * {"a"} + {"", "b"}      =>  {"a", "", "b"}       {"a", "b"}
 460      * {"a", ""} + {"b", ""}  =>  {"a", "", "b", ""}   {"a", "b", ""}
 461      * {"a", ""} + {"", "b"}  =>  {"a", "", "", "b"}   {"a", "", "b"}
 462      * }</pre>
 463      */
 464     public Name composeName(Name name, Name prefix) throws NamingException {
 465         Name res = (Name)prefix.clone();
 466         if (name == null) {
 467             return res;
 468         }
 469         res.addAll(name);
 470 
 471         String elide = (String)
 472             p_getEnvironment().get("java.naming.provider.compose.elideEmpty");
 473         if (elide == null || !elide.equalsIgnoreCase("true")) {
 474             return res;
 475         }
 476 
 477         int len = prefix.size();
 478 
 479         if (!allEmpty(prefix) && !allEmpty(name)) {
 480             if (res.get(len - 1).isEmpty()) {
 481                 res.remove(len - 1);
 482             } else if (res.get(len).isEmpty()) {
 483                 res.remove(len);
 484             }
 485         }
 486         return res;
 487     }
 488 
 489 
 490 // ------ internal methods used by PartialCompositeContext
 491 
 492     /**
 493      * Tests whether a name contains a nonempty component.
 494      */
 495     protected static boolean allEmpty(Name name) {
 496         Enumeration<String> enum_ = name.getAll();
 497         while (enum_.hasMoreElements()) {
 498             if (!enum_.nextElement().isEmpty()) {
 499                 return false;
 500             }
 501         }
 502         return true;
 503     }
 504 
 505     /**
 506      * Retrieves a PartialCompositeContext for the resolved object in
 507      * cont.  Throws CannotProceedException if not successful.
 508      */
 509     protected static PartialCompositeContext getPCContext(Continuation cont)
 510             throws NamingException {
 511 
 512         Object obj = cont.getResolvedObj();
 513         PartialCompositeContext pctx = null;
 514 
 515         if (obj instanceof PartialCompositeContext) {
 516             // Just cast if octx already is PartialCompositeContext
 517             // %%% ignoring environment for now
 518             return (PartialCompositeContext)obj;
 519         } else {
 520             throw cont.fillInException(new CannotProceedException());
 521         }
 522     }
 523 };