# UnnamedStreams.java

    /*
    ** Module   : UnnamedStreams.java
    ** Abstract : support for the unnamed in and unnamed out streams
    **
    ** Copyright (c) 2006-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- -----------------------------------Description----------------------------------
    ** 001 GES 20060207   @24331 Created initial version, supports unnamed
    **                           input and unnamed output.
    ** 002 GES 20060215   @24678 New features to redirect the terminal and
    **                           handle stacking of top level block defaults.
    ** 003 GES 20060227   @24773 Redirection clearing flow control change.
    ** 004 GES 20060323   @25208 Stream related processing can be called on
    **                           the unnamed streams when they are not
    **                           redirected.  These all need to be protected
    **                           from null pointers by using a special "safe"
    **                           getter that always returns a stream instance
    **                           even if that instance is essentially bogus.
    ** 005 GES 20061009   @30223 Support for headless mode.
    ** 006 NVS 20070817   @34882 Quick return for no-op call of assignOut().
    ** 007 NVS 20070829   @34974 Added isOut() method that checks for the
    **                           redirection status of the unnamed stream.
    ** 008 EVL 20071003   @35355 Fix to register callback to automatically
    **                           close input or output stream for internal
    **                           block as well as for external.
    ** 009 EVL 20071010   @35416 Additional check for in/out stream to be
    **                           valid for the input/output method.
    ** 010 CA  20071121   @35920 Implemented the correct behavior for the
    **                           situation when there is no explicit
    **                           "output/input close" statement. Refactored
    **                           the assignIn/Out methods to call
    **                           assignIn/OutWorker(Stream,boolean).
    ** 011 ECF 20071213   @36378 Fixed #010 (@35920). New instance variables
    **                           closerIn/closerOut were incorrectly added to
    **                           ContextContainer inner class, when they were
    **                           required in WorkArea. This was causing these
    **                           stacks to be shared across contexts, which
    **                           resulted in abnormal ends of client sessions.
    ** 012 CA  20080304   @37296 Mark the stream as unnamed when a new stream
    **                           stream is opened, in assignOutWorker.
    ** 013 SIY 20080408   @37869 Do not close current stream if switching to
    **                           terminal stream. Otherwise explicit opening of
    **                           terminal stream closes previously opened
    **                           non-terminal stream.
    **                           Inferred generic type arguments.
    **                           Ref: #37868
    ** 014 SVL 20080603   @38471 Fixed assignOutWorker() for the case when the
    **                           output is redirected to a non-terminal then
    **                           to a terminal stream.
    ** 015 SVL 20090612   @42674 If a StopCondition has been raised into
    **                           assignOutWorker(), we let the old stream to be
    **                           closed in a usual way.
    ** 016 CA  20131013          Added no-op deleted() and scopeDeleted() methods, requried by the
    **                           changes in Scopeable and Finalizable interfaces.
    ** 017 EVL 20131205          Adding support for batch mode error messages.
    ** 018 OM  20160128          Use TransactionManager._isHeadless() in initialize().
    ** 019 CA  20190326          Implemented TransactionHelper instead of direct usage of the static
    **                           API.  This allows the elimination of context local usage in TM.
    ** 020 CA  20190707          Stub support for OUTPUT TO ... LOB-DIR.
    ** 021 CA  20200930          Refactored to remove the singleton scopeable instance (which forced the scope
    **                           notifications to use context-local state).
    **     CA  20201015          Replaced java.util.Stack with a non-synchronized custom implementation.
    **     OM  20211006          Streams are closed on procedure return instead of TM's scope finished.
    **     RFB 20220114          Clearing of the closer stacks is the shared responsibility of executeOnReturn
    **                           and scopeFinished. Once something non-null is added to the closer stack, then
    **                           it's the job of executeOnReturn to pop the stack, otherwise, it's on the
    **                           scopeFinished. Ref #5880.
    **     CA  20220901          Refactored scope notification support: ScopeableFactory was removed, and the
    **                           registration is now specific to each type of scopeable.  For each case, the block
    **                           will be registered for scope support (for that particular scopeable) only when
    **                           the scopeable is 'active' (i.e. unnamed streams or accumulators are used).  This
    **                           allows a lazy registration of scopeables, to avoid the unnecessary overhead of
    **                           processing all the scopeables for each and every block.
    **     AL2 20230613          Added procedure helper to the contextual state. Use it where possible.
    ** 022 CA  20230703          If an unnamed stream is explicitly closed, deregister its 'executeOnReturn'
    **                           cleaner.
    ** 023 CA  20230929          Javadoc fixes.
    ** 024 CA  20231031          Added 'BlockDefinition' parameter to 'scopeStart'.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import com.goldencode.util.Stack;
    import com.goldencode.p2j.security.ContextLocal;
    import com.goldencode.p2j.ui.*;

    /**
     * Stores state and provides functional support for the unnamed in and unnamed out streams.
     */
    public class UnnamedStreams
    implements Scopeable
    {
       /** Stores context-local state variables. */
       private static final ContextContainer work = new ContextContainer();

       /** The state for this instance. */
       private final WorkArea wa = new WorkArea();

       /**
        * Private constructor so external code cannot instantiate this class.
        */
       private UnnamedStreams()
       {
       }

       /**
        * Gets the stream representing the unnamed input stream.
        *
        * @return   The unnamed input stream or <code>null</code> if the unnamed
        *           input stream has not been explicitly redirected.
        */
       public static Stream input()
       {
          WorkArea wa = work.get().wa;
          Stream result = wa.in;

          if (result != null && result.isValid())
             return result;
          else
          {
             wa.in = null;
             return null;
          }
       }

       /**
        * Gets the stream representing the unnamed input stream or a safe stream
        * instance that can be used for operations that should effectively be
        * <code>NOP</code>.
        *
        * @return   The unnamed input stream to which input is redirected or
        *           a <code>NullStream</code> instance if the unnamed
        *           input stream has not been explicitly redirected.
        */
       public static Stream safeInput()
       {
          return makeSafe(work.get().wa.in);
       }

       /**
        * Assigns the stream representing the unnamed input stream. Closes any
        * unnamed input stream that is already open.
        * Calls assignInWorker(Stream,true).
        *
        * @param    in
        *           The unnamed input stream to set or <code>null</code> if any
        *           current input stream redirection is to be cleared.
        */
       public static void assignIn(Stream in)
       {
          // Batch mode special processing.
          if (in == null)
          {
             // Trying to return input to terminal inside batch mode
             if (ErrorManager.isInBatchMode())
             {
                // We have to display message and stop the client execution
                ErrorManager.displayError(120, "There is no terminal", true);
                ErrorManager.stopBatchSession();
             }
          }

          assignInWorker(work.get().wa, in, true);
       }

       /**
        * Closes the stream representing the unnamed input stream.  This will
        * restore the default input stream for the current top level scope, which
        * may be <code>null</code> (if the current redirection when the scope
        * opened was <code>null</code>).
        */
       public static void closeIn()
       {
          closeInInt(work.get().wa);
       }

       /**
        * Gets the stream representing the unnamed output stream.
        *
        * @return   The unnamed output stream or <code>null</code> if the unnamed
        *           output stream has not been explicitly redirected.
        */
       public static Stream output()
       {
          WorkArea wa = work.get().wa;
          Stream result = wa.out;

          if (result != null && result.isValid())
             return result;
          else
          {
             wa.out = null;
             return null;
          }
       }

       /**
        * Gets the stream representing the unnamed output stream or a safe stream
        * instance that can be used for operations that should effectively be
        * <code>NOP</code>.
        *
        * @return   The unnamed output stream to which output is redirected or
        *           a <code>NullStream</code> instance if the unnamed
        *           output stream has not been explicitly redirected.
        */
       public static Stream safeOutput()
       {
          return makeSafe(work.get().wa.out);
       }

       /**
        * Assigns the stream representing the unnamed output stream. Closes any
        * stream that is already open. Calls assignOutWorker(Stream,true).
        *
        * @param    out
        *           The unnamed output stream to set or <code>null</code> if any
        *           current output stream redirection is to be cleared.
        * @param    lobDir
        *           The folder where to write any CLOB or BLOB fields.
        */
       public static void assignOut(Stream out, String lobDir)
       {
          // TODO: manager lobDir
          assignOut(out);
       }

       /**
        * Assigns the stream representing the unnamed output stream. Closes any
        * stream that is already open. Calls assignOutWorker(Stream,true).
        *
        * @param    out
        *           The unnamed output stream to set or <code>null</code> if any
        *           current output stream redirection is to be cleared.
        */
       public static void assignOut(Stream out)
       {
          // Batch mode special processing.
          if (out == null)
          {
             // Trying to return output to terminal inside batch mode
             if (ErrorManager.isInBatchMode())
             {
                // We have to display message and stop the client execution
                ErrorManager.displayError(120, "There is no terminal", true);
                ErrorManager.stopBatchSession();
             }
          }

          assignOutWorker(work.get().wa, out, true);
       }

       /**
        * Closes the stream representing the unnamed output stream.  This will
        * restore the default output stream for the current top level scope, which
        * may be <code>null</code> (if the current redirection when the scope
        * opened was <code>null</code>).
        */
       public static void closeOut()
       {
          closeOutInt(work.get().wa);
       }

       /**
        * Assigns the streams representing both the unnamed output stream and the
        * unnamed input stream. Closes the input or output streams if either
        * are already open.
        *
        * @param    stream
        *           The unnamed output stream to set or <code>null</code> if any
        *           current output stream redirection is to be cleared.
        */
       public static void assignBoth(Stream stream)
       {
          assignIn(stream);
          assignOut(stream);

          work.get().wa.both = true;
       }

       /**
        * Closes both the unnamed input stream and the unnamed output stream.
        */
       public static void closeBoth()
       {
          closeIn();
          closeOut();

          work.get().wa.both = false;
       }

       /**
        * Provides a notification that a new scope is about to be entered. Used
        * to store the new default input and output streams for this scope.
        *
        * @param    block
        *           The explicit block definition which required this notification.
        */
       @Override
       public void scopeStart(BlockDefinition block)
       {
          wa.lastIn.push(wa.in);
          wa.lastOut.push(wa.out);
          wa.closerIn.push(null);
          wa.closerOut.push(null);
       }

       /**
        * Provides a notification that a scope is about to be exited. Used to
        * clear the default input and output streams back to the previous scope's
        * versions, unless it is the responsibility of executeOnReturn.
        */
       public void scopeFinished()
       {
          wa.lastIn.pop();
          wa.lastOut.pop();

          if (wa.closerIn.peek() == null)
          {
             wa.closerIn.pop();
          }
          if (wa.closerOut.peek() == null)
          {
             wa.closerOut.pop();
          }
       }

       /**
        * Provides notification that the external procedure scope has been deleted.
        * <p>
        * This is a no-op for unnamed streams.
        */
       @Override
       public void scopeDeleted()
       {
          // no-op
       }

       /**
        * Get the {@link ScopeId} for the instance.
        *
        * @return   {@link ScopeId#UNNAMED_STREAMS}.
        */
       @Override
       public ScopeId getScopeId()
       {
          return ScopeId.UNNAMED_STREAMS;
       }

       /**
        * State of the output side of the unnamed stream.
        *
        * @return   <code>true</code> if the output side of the stream is active.
        */
       public static boolean isOut()
       {
          return work.get().wa.out != null;
       }

       /**
        * Returns the given stream if it is not <code>null</code>, otherwise it
        * returns a safe stream instance that can be used for operations that
        * should effectively be a <code>NOP</code>.
        *
        * @param    safe
        *           The stream to check.
        *
        * @return   The unnamed input stream given or a <code>NullStream</code>
        *           instance if the given stream is <code>null</code>.
        */
       private static Stream makeSafe(Stream safe)
       {
          // we aren't redirected
          if (safe == null)
          {
             WorkArea wa = work.get().wa;

             // setup the bogus stream the first time in
             if (wa.bogus == null)
             {
                wa.bogus = new NullStream();
             }

             safe = wa.bogus;
          }

          return safe;
       }

       /**
        * Assigns the stream representing the unnamed input stream. Closes any
        * unnamed input stream that is already open.
        *
        * @param    wa
        *           The context-local state.
        * @param    in
        *           The unnamed input stream to set or <code>null</code> if any
        *           current input stream redirection is to be cleared.
        * @param    register
        *           If <code>true</code> and the stream parameter is non-null,
        *           then a call to registerTopLevelFinalizable will be performed.
        */
       private static void assignInWorker(WorkArea wa, Stream in, boolean register)
       {
          wa.tm.registerTopLevelScopeable(work.get());

          // default input for this top level scope
          Stream def = wa.lastIn.peek();

          if (wa.in != null)
          {
             if (wa.in == def && in != null)
             {
                // don't close the default stream when we are switching to
                // another (non-terminal) stream
             }
             else
             {
                if (wa.both)
                {
                   wa.in.closeIn();
                }
                else
                {
                   wa.in.close();
                }
             }
          }

          wa.both = false;
          wa.in   = in;

          if (register && in != null)
          {
             // the InputCloser must be executed BEFORE the Stream is finalized this is required because the
             // RemoteStream.close will set the stream ID to -1 and so the UnnamedStreams.closeIn method will
             // never be executed if there is no registered input closer, we register it
             if (wa.closerIn.peek() == null)
             {
                InputCloser closer = new InputCloser(wa);
                wa.pm.executeOnReturn(closer, closer.weight());
                // add the closer to the stack
                wa.closerIn.pop();
                wa.closerIn.push(closer);
             }

             wa.pm.executeOnReturn(in::finished, in.weight());
          }
       }

       /**
        * Assigns the stream representing the unnamed output stream. Closes any
        * stream that is already open.
        *
        * @param    wa
        *           The context-local state.
        * @param    out
        *           The unnamed output stream to set or <code>null</code> if any
        *           current output stream redirection is to be cleared.
        * @param    register
        *           If <code>true</code> and the stream parameter is non-null,
        *           then a call to registerTopLevelFinalizable will be performed.
        */
       private static void assignOutWorker(WorkArea wa, Stream out, boolean register)
       {
          wa.tm.registerTopLevelScopeable(work.get());

          // save ids before we make any state changes
          int oldId = ((wa.out == null) ? -1 : ((RemoteStream) wa.out).getId());
          int newId = ((out    == null) ? -1 : ((RemoteStream) out).getId());

          if (out != null)
          {
             out.setUnnamed(true);
          }

          if (newId == oldId)
          {
             return;
          }

          // default output for this top level scope
          Stream def = wa.lastOut.peek();

          StopConditionException exc = null;

          // close the stream resources
          if (wa.out != null)
          {
             if (wa.out == def && out != null)
             {
                // don't close the default stream when we are switching to
                // another (non-terminal) stream
             }
             else if (wa.out == def && newId == -1 && register)
             {
                // don't close the default stream when we are switching to
                // terminal stream, but since we're switching do some cleanups
             }
             else
             {
                try
                {
                   if (wa.both)
                   {
                      wa.out.closeOut();
                   }
                   else
                   {
                      wa.out.close();
                   }
                }
                catch(StopConditionException e)
                {
                   exc = e;
                }
             }
          }

          // we must notify the UI to redirect output/cleanup resources (must
          // be AFTER closing the stream as close() can cause page footers to
          // be output and thus the redirection must stay until that is done)
          LogicalTerminal.redirectOutput(oldId, newId);

          wa.both = false;
          wa.out  = out;

          if (register && out != null)
          {
             // the OutputCloser must be executed BEFORE the Stream is finalized
             // this is required because the RemoteStream.close will set the
             // stream ID to -1 and so the UnnamedStreams.closeOut method will
             // never be executed
             // if there is no registered output closer, we register it
             if (wa.closerOut.peek() == null)
             {
                OutputCloser closer = new OutputCloser(wa);
                wa.pm.executeOnReturn(closer, closer.weight());
                // add the closer to the stack
                wa.closerOut.pop();
                wa.closerOut.push(closer);
             }

             wa.pm.executeOnReturn(out::finished, out.weight());
          }

          if (exc != null)
          {
             throw exc;
          }
       }

       /**
        * Closes the stream representing the unnamed input stream.  This will
        * restore the default input stream for the current top level scope, which
        * may be <code>null</code> (if the current redirection when the scope
        * opened was <code>null</code>).
        *
        * @param    wa
        *           The context-local state.
        */
       private static void closeInInt(WorkArea wa)
       {
          assignInWorker(wa, wa.lastIn.peek(), false);

          // we pop the closer from the stack
          if (!wa.closerIn.empty())
          {
             InputCloser closer = wa.closerIn.pop();
             if (closer != null)
             {
                wa.pm.deregisterExecuteOnReturn(closer, closer.weight());
             }
             wa.closerIn.push(null);
          }
       }

       /**
        * Closes the stream representing the unnamed output stream.  This will
        * restore the default output stream for the current top level scope, which
        * may be <code>null</code> (if the current redirection when the scope
        * opened was <code>null</code>).
        *
        * @param    wa
        *           The context-local state.
        */
       private static void closeOutInt(WorkArea wa)
       {
          assignOutWorker(wa, wa.lastOut.peek(), false);

          // we pop the closer from the stack
          if (!wa.closerOut.empty())
          {
             OutputCloser closer = wa.closerOut.pop();;
             if (closer != null)
             {
                wa.pm.deregisterExecuteOnReturn(closer, closer.weight());
             }
             wa.closerOut.push(null);
          }
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /** Helper to use the TM without any context local lookups. */
          private final TransactionManager.TransactionHelper tm = TransactionManager.getTransactionHelper();

          /** Helper to use the PM without any context local lookups. */
          private final ProcedureManager.ProcedureHelper pm = ProcedureManager.getProcedureHelper();

          /** Flags whether the input stream represents both destinations. */
          private boolean both = false;

          /** A fake stream for safe getters for this user's session. */
          private Stream bogus = null;

          /** The current unnamed input stream for this user's session. */
          private Stream in = null;

          /** The current unnamed output stream for this user's session. */
          private Stream out = null;

          /** Track the default unnamed input stream for the top level block. */
          private final Stack<Stream> lastIn = new Stack<>();

          /** Track the default unnamed output stream for the top level block. */
          private final Stack<Stream> lastOut = new Stack<>();

          /** Track the current InputCloser instance for this block. */
          public Stack<InputCloser> closerIn = new Stack<>();

          /** Track the current OutputCloser instance for this block. */
          public Stack<OutputCloser> closerOut = new Stack<>();
       }

       /**
        * Simple container that stores and returns a context-local instance of
        * the global work area.
        */
       private static class ContextContainer
       extends ContextLocal<UnnamedStreams>
       {
          /**
           * Initializes the work area, the first time it is requested within a
           * new context.
           * <p>
           * Initializes the closerIn and closerOut trackers, the first time it is
           * requested within a new context.
           *
           * @return   The newly instantiated work area.
           */
          protected UnnamedStreams initialValue()
          {
             UnnamedStreams instance = new UnnamedStreams();

             // make sure we always have an entry in the stacks (avoids empty
             // stack exceptions)
             instance.wa.lastIn.push(null);
             instance.wa.lastOut.push(null);

             // add a null reference to know there are no Input/OutputClosers
             // registered
             instance.wa.closerIn.push(null);
             instance.wa.closerOut.push(null);

             return instance;
          }
       }

       /**
        * Finalizable class which will perform a closeIn call only if this class
        * is in the top of the closerIn stack - this way we make sure there are
        * not possible two subsequent calls to the closeIn statement.
        */
       private static class InputCloser
       implements Finalizable,
                  Runnable
       {
          /** The context-local state. */
          private final WorkArea wa;

          /**
           * Create a new instance and cache the context-local state.
           *
           * @param    wa
           *           The context-local state.
           */
          public InputCloser(WorkArea wa)
          {
             this.wa = wa;
          }

          /**
           * Execute {@link #finished()}, required for {@link ProcedureManager$ProcedureHelper#executeOnReturn}.
           */
          @Override
          public void run()
          {
             finished();
          }

          /**
           * This method will be called when the current block will be popped out
           * of scope and will close the stream if there is no explicit
           * "input close" statement.
           */
          public void finished()
          {
             // Determine if the internal close is to be executed,
             // and if cleanup is our reponsibility instead of scopeFinished
             InputCloser topItem = wa.closerIn.peek();
             if (topItem == this)
             {
                closeInInt(wa);
             }

             if (topItem != null)
             {
                wa.closerIn.pop();
             }
          }

          /**
           * Provides a notification that the external program scope in which the object is registered is
           * is being deleted and the object's reference will be lost after this method is called.
           * <p>
           * This is a no-op for unnamed streams.
           */
          @Override
          public void deleted()
          {
             // no-op
          }

          /**
           * This method is unused.
           *
           * @see  com.goldencode.p2j.util.Finalizable#iterate()
           */
          public void iterate()
          {
             // do nothing
          }

          /**
           * This method is unused.
           *
           * @see  com.goldencode.p2j.util.Finalizable#iterate()
           */
          public void retry()
          {
             // do nothing
          }
       }

       /**
        * Finalizable class which will perform a closeOut call only if this class
        * is in the top of the closerOut stack - this way we make sure there are
        * not possible two subsequent calls to the closeOut statement.
        */
       private static class OutputCloser
       implements Finalizable,
                  Runnable
       {
          /** The context-local state. */
          private final WorkArea wa;

          /**
           * Create a new instance and cache the context-local state.
           *
           * @param    wa
           *           The context-local state.
           */
          public OutputCloser(WorkArea wa)
          {
             this.wa = wa;
          }

          /**
           * Execute {@link #finished()}, required for {@link ProcedureManager$ProcedureHelper#executeOnReturn}.
           */
          @Override
          public void run()
          {
             finished();
          }

          /**
           * This method will be called when the current block will be popped out
           * of scope and will close the stream if there is no explicit
           * "output close" statement.
           */
          public void finished()
          {
             // Determine if the internal close is to be executed,
             // and if cleanup is our reponsibility instead of scopeFinished
             OutputCloser topItem = wa.closerOut.peek();
             if (topItem == this)
             {
                closeOutInt(wa);
             }

             if (topItem != null)
             {
                wa.closerOut.pop();
             }
          }

          /**
           * Provides a notification that the external program scope in which the object is registered is
           * is being deleted and the object's reference will be lost after this method is called.
           * <p>
           * This is a no-op for unnamed streams.
           */
          @Override
          public void deleted()
          {
             // no-op
          }

          /**
           * This method is unused.
           *
           * @see  com.goldencode.p2j.util.Finalizable#iterate()
           */
          public void iterate()
          {
             // do nothing
          }

          /**
           * This method is unused.
           *
           * @see  com.goldencode.p2j.util.Finalizable#iterate()
           */
          public void retry()
          {
             // do nothing
          }
       }
    }