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