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