1 /*
   2  * Copyright (c) 1999, 2011, 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 javax.naming.*;
  29 import javax.naming.spi.ResolveResult;
  30 import java.util.Hashtable;
  31 
  32 /**
  33   * This class contains information required to continue
  34   * the method (place where it left off, and remaining name to
  35   * continue).
  36   *
  37   * @author Rosanna Lee
  38   */
  39 
  40 public class Continuation extends ResolveResult {
  41     /**
  42      * The name that we started out with. It is initialized by the constructor
  43      * and used to calculate to "resolved name" in NamingException in
  44      * fillInException().
  45      * %%% Note that this approach does not always do the calculation
  46      * correctly with respect to absence or presence of the trailing slash
  47      * for resolved name.
  48      */
  49     protected Name starter;
  50 
  51     /**
  52      * Whether links were encountered.
  53      */
  54     protected Object followingLink = null;
  55 
  56     /**
  57      * The environment used by the caller. Initialized by constructor and
  58      * used when filling out a CannotProceedException.
  59      */
  60     protected Hashtable<?,?> environment = null;
  61 
  62     /**
  63      * Indicates whether the Continuation instance indicates that the operation
  64      * should be continued using the data in the Continuation.
  65      * Typically, this is only false if an error has been encountered or if
  66      * the operation has succeeded.
  67      */
  68     protected boolean continuing = false;
  69 
  70     /**
  71      * The last resolved context. Used to set the "AltNameCtx" in a
  72      * CannotProceedException.
  73      */
  74     protected Context resolvedContext = null;
  75 
  76     /**
  77      * The resolved name relative to resolvedContext. Used to set the
  78      * "AltName" in a CannotProceedException.
  79      */
  80     protected Name relativeResolvedName = null;
  81 
  82     /**
  83      * Constructs a new instance of Continuation.
  84      * Used as dummy for contexts that do not do federation (e.g. for schema ops)
  85      */
  86     public Continuation() {
  87     }
  88 
  89     /**
  90      * Constructs a new instance of Continuation.
  91      * @param top The name of the object that is to be resolved/operated upon.
  92      *          This becomes the Continuation's 'starter' and is used to
  93      *          calculate the "resolved name" when filling  in a NamingException.
  94      * @param environment The environment used by the caller. It is used
  95      * when setting the "environment" of a CannotProceedException.
  96      */
  97     public Continuation(Name top, Hashtable<?,?> environment) {
  98         super();
  99         starter = top;
 100         this.environment = environment;
 101     }
 102 
 103     /**
 104      * Determines whether this Continuation contains data that should be
 105      * used to continue the operation.
 106      *
 107      * @return true if operation should continue; false if operation has
 108      * completed (successfully or unsuccessfully).
 109      */
 110     public boolean isContinue() {
 111         return continuing;
 112     }
 113 
 114     /**
 115      * Sets this Continuation to indicate successful completion.
 116      * Subsequent calls to isContinue() will return false.
 117      * This method is different from the setError() methods only from
 118      * the standpoint that this method does not set any of the other
 119      * fields such as resolved object or resolved context. This is because
 120      * this method is typically called when the context recognizes that
 121      * the operation has successfully completed and that the continuation
 122      * already contains the appropriately set fields.
 123      * @see setError
 124      * @see setErrorNNS
 125      */
 126     public void setSuccess() {
 127         continuing = false;
 128     }
 129 
 130     /**
 131      * Fills in an exception's fields using data from this Continuation.
 132      * The resolved name is set by subtracting remainingName from starter.
 133      * %%% This might not not always produce the correct answer wrt trailing "/".
 134      * If the exception is a CannotProceedException, its environment,
 135      * altName, and altNameCtx fields are set using this continuation's
 136      * environment, relativeResolvedName, and resolvedContext.
 137      *
 138      * @param e The non-null naming exception to fill.
 139      * @return The non-null naming exception with its fields set using
 140      * data from this Continuation.
 141      */
 142     public NamingException fillInException(NamingException e) {
 143         e.setRemainingName(remainingName);
 144         e.setResolvedObj(resolvedObj);
 145 
 146         if (starter == null || starter.isEmpty())
 147             e.setResolvedName(null);
 148         else if (remainingName == null)
 149             e.setResolvedName(starter);
 150         else
 151             e.setResolvedName(
 152                 starter.getPrefix(starter.size() -
 153                                   remainingName.size()));
 154 
 155         if ((e instanceof CannotProceedException)) {
 156             CannotProceedException cpe = (CannotProceedException)e;
 157             Hashtable<?,?> env = (environment == null ?
 158                 new Hashtable<>(11) : (Hashtable<?,?>)environment.clone());
 159             cpe.setEnvironment(env);
 160             cpe.setAltNameCtx(resolvedContext);
 161             cpe.setAltName(relativeResolvedName);
 162         }
 163 
 164         return e;
 165     }
 166 
 167     /**
 168      * Sets this Continuation to indicated that an error has occurred,
 169      * and that the remaining name is rename + "/".
 170      *
 171      * This method is typically called by _nns methods that have been
 172      * given a name to process. It might process part of that name but
 173      * encountered some error. Consequenetly, it would call setErrorNNS()
 174      * with the remaining name. Since the _nns method was expected to
 175      * operate upon the "nns" of the original name, the remaining name
 176      * must include the "nns". That's why this method adds a trailing "/".
 177      *<p>
 178      * After this method is called, isContinuing() returns false.
 179      *
 180      * @param resObj The possibly null object that was resolved to.
 181      * @param remain The non-null remaining name.
 182      */
 183     public void setErrorNNS(Object resObj, Name remain) {
 184         Name nm = (Name)(remain.clone());
 185         try {
 186             nm.add("");
 187         } catch (InvalidNameException e) {
 188             // ignore; can't happen for composite name
 189         }
 190         setErrorAux(resObj, nm);
 191     }
 192 
 193     /**
 194      * Form that accepts a String name instead of a Name name.
 195 
 196      * @param resObj The possibly null object that was resolved to.
 197      * @param remain The possibly String remaining name.
 198      *
 199      * @see #setErrorNNS(java.lang.Object, javax.naming.Name)
 200      */
 201     public void setErrorNNS(Object resObj, String remain) {
 202         CompositeName rname = new CompositeName();
 203         try {
 204             if (remain != null && !remain.equals(""))
 205                 rname.add(remain);
 206 
 207             rname.add("");
 208         } catch (InvalidNameException e) {
 209             // ignore, can't happen for composite name
 210         }
 211         setErrorAux(resObj, rname);
 212     }
 213 
 214     /**
 215      * Sets this Continuation to indicated that an error has occurred
 216      * and supply resolved information.
 217      *
 218      * This method is typically called by methods that have been
 219      * given a name to process. It might process part of that name but
 220      * encountered some error. Consequenetly, it would call setError()
 221      * with the resolved object and the remaining name.
 222      *<p>
 223      * After this method is called, isContinuing() returns false.
 224      *
 225      * @param resObj The possibly null object that was resolved to.
 226      * @param remain The possibly null remaining name.
 227      */
 228     public void setError(Object resObj, Name remain) {
 229         if (remain != null)
 230             remainingName = (Name)(remain.clone());
 231         else
 232             remainingName = null;
 233 
 234         setErrorAux(resObj, remainingName);
 235     }
 236 
 237 
 238     /**
 239      * Form that accepts a String name instead of a Name name.
 240 
 241      * @param resObj The possibly null object that was resolved to.
 242      * @param remain The possibly String remaining name.
 243      *
 244      * @see #setError(java.lang.Object, javax.naming.Name)
 245      */
 246     public void setError(Object resObj, String remain) {
 247         CompositeName rname = new CompositeName();
 248         if (remain != null && !remain.equals("")) {
 249             try {
 250                 rname.add(remain);
 251             } catch (InvalidNameException e) {
 252                 // ignore; can't happen for composite name
 253             }
 254         }
 255         setErrorAux(resObj, rname);
 256     }
 257 
 258     private void setErrorAux(Object resObj, Name rname) {
 259         remainingName = rname;
 260         resolvedObj = resObj;
 261         continuing = false;
 262     }
 263 
 264     private void setContinueAux(Object resObj,
 265         Name relResName, Context currCtx,  Name remain) {
 266         if (resObj instanceof LinkRef) {
 267             setContinueLink(resObj, relResName, currCtx, remain);
 268         } else {
 269             remainingName = remain;
 270             resolvedObj = resObj;
 271 
 272             relativeResolvedName = relResName;
 273             resolvedContext = currCtx;
 274 
 275             continuing = true;
 276         }
 277     }
 278 
 279     /**
 280      * Sets this Continuation with the supplied data, and set remaining name
 281      * to be "/".
 282      * This method is typically called by _nns methods that have been
 283      * given a name to process. It might the name (without the nns) and
 284      * continue process of the nns elsewhere.
 285      * Consequently, it would call this form of the setContinueNNS().
 286      * This method supplies "/" as the remaining name.
 287      *<p>
 288      * After this method is called, isContinuing() returns true.
 289      *
 290      * @param resObj The possibly null resolved object.
 291      * @param relResName The non-null resolved name relative to currCtx.
 292      * @param currCtx The non-null context from which relResName is to be resolved.
 293      */
 294     public void setContinueNNS(Object resObj, Name relResName, Context currCtx) {
 295         CompositeName rname = new CompositeName();
 296 
 297         setContinue(resObj, relResName, currCtx, PartialCompositeContext._NNS_NAME);
 298     }
 299 
 300     /**
 301      * Overloaded form that accesses String names.
 302      *
 303      * @param resObj The possibly null resolved object.
 304      * @param relResName The non-null resolved name relative to currCtx.
 305      * @param currCtx The non-null context from which relResName is to be resolved.
 306      * @see #setContinueNNS(java.lang.Object, javax.naming.Name, javax.naming.Context)
 307      */
 308     public void setContinueNNS(Object resObj, String relResName, Context currCtx) {
 309         CompositeName relname = new CompositeName();
 310         try {
 311             relname.add(relResName);
 312         } catch (NamingException e) {}
 313 
 314         setContinue(resObj, relname, currCtx, PartialCompositeContext._NNS_NAME);
 315     }
 316 
 317 
 318     /**
 319      * Sets this Continuation with the supplied data, and set remaining name
 320      * to be the empty name.
 321      * This method is typically called by list-style methods
 322      * in which the target context implementing list() expects an
 323      * empty name. For example when c_list() is given a non-empty name to
 324      * process, it would resolve that name, and then call setContinue()
 325      * with the resolved object so that the target context to be listed
 326      * would be called with the empty name (i.e. list the target context itself).
 327      *<p>
 328      * After this method is called, isContinuing() returns true.
 329      *
 330      * @param resObj The possibly null resolved object.
 331      * @param relResName The non-null resolved name relative to currCtx.
 332      * @param currCtx The non-null context from which relResName is to be resolved.
 333      */
 334     public void setContinue(Object obj, Name relResName, Context currCtx) {
 335         setContinueAux(obj, relResName, currCtx,
 336             (Name)PartialCompositeContext._EMPTY_NAME.clone());
 337     }
 338 
 339     /**
 340      * Sets this Continuation with the supplied data.
 341 
 342      * This method is typically called by a method that has been asked
 343      * to operate on a name. The method resolves part of the name
 344      * (relResName) to obj and sets the unprocessed part to rename.
 345      * It calls setContinue() so that the operation can be continued
 346      * using this data.
 347      *<p>
 348      * After this method is called, isContinuing() returns true.
 349      *
 350      * @param resObj The possibly null resolved object.
 351      * @param relResName The non-null resolved name relative to currCtx.
 352      * @param currCtx The non-null context from which relResName is to be resolved.
 353      * @param remain The non-null remaining name.
 354      */
 355     public void setContinue(Object obj, Name relResName, Context currCtx, Name remain) {
 356         if (remain != null)
 357             this.remainingName = (Name)(remain.clone());
 358         else
 359             this.remainingName = new CompositeName();
 360 
 361         setContinueAux(obj, relResName, currCtx, remainingName);
 362     }
 363 
 364     /**
 365      * String overload.
 366      *
 367      * @param resObj The possibly null resolved object.
 368      * @param relResName The non-null resolved name relative to currCtx.
 369      * @param currCtx The non-null context from which relResName is to be resolved.
 370      * @param remain The non-null remaining name.
 371      * @see #setContinue(java.lang.Object, java.lang.String, javax.naming.Context, java.lang.String)
 372      */
 373     public void setContinue(Object obj, String relResName,
 374         Context currCtx, String remain) {
 375         CompositeName relname = new CompositeName();
 376         if (!relResName.equals("")) {
 377             try {
 378                 relname.add(relResName);
 379             } catch (NamingException e){}
 380         }
 381 
 382         CompositeName rname = new CompositeName();
 383         if (!remain.equals("")) {
 384             try {
 385                 rname.add(remain);
 386             } catch (NamingException e) {
 387             }
 388         }
 389 
 390         setContinueAux(obj, relname, currCtx, rname);
 391     }
 392 
 393     /**
 394      * %%% This method is kept only for backward compatibility. Delete when
 395      * old implementations updated.
 396      *
 397      * Replaced by setContinue(obj, relResName, (Context)currCtx);
 398      *
 399      * @deprecated
 400      */
 401     @Deprecated
 402     public void setContinue(Object obj, Object currCtx) {
 403         setContinue(obj, null, (Context)currCtx);
 404     }
 405 
 406 
 407     /**
 408      * Sets this Continuation to process a linkRef.
 409      * %%% Not working yet.
 410      */
 411     private void setContinueLink(Object linkRef, Name relResName,
 412         Context resolvedCtx, Name rname) {
 413         this.followingLink = linkRef;
 414 
 415         this.remainingName = rname;
 416         this.resolvedObj = resolvedCtx;
 417 
 418         this.relativeResolvedName = PartialCompositeContext._EMPTY_NAME;
 419         this.resolvedContext = resolvedCtx;
 420 
 421         this.continuing = true;
 422     }
 423 
 424     public String toString() {
 425         if (remainingName != null)
 426             return starter.toString() + "; remainingName: '" + remainingName + "'";
 427         else
 428             return starter.toString();
 429     }
 430 
 431     public String toString(boolean detail) {
 432         if (!detail || this.resolvedObj == null)
 433                 return this.toString();
 434         return this.toString() + "; resolvedObj: " + this.resolvedObj +
 435             "; relativeResolvedName: " + relativeResolvedName +
 436             "; resolvedContext: " + resolvedContext;
 437     }
 438 
 439     private static final long serialVersionUID = 8162530656132624308L;
 440 }