BlockDefinition.java

/*                          
** Module   : BlockDefinition.java
** Abstract : stores block-related information for runtime processing
**
** Copyright (c) 2005-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
** 001 GES 20050809   @22097 Created initial version, supporting
**                           block information for undo and retry.
** 002 GES 20060202   @24224 Added external flag to allow processing that
**                           is dependent upon the external procedure to
**                           be properly hooked.  This enables the proper 
**                           cleanup of resources like temp-tables, 
**                           frames and streams.
** 003 GES 20060202   @24246 Added init and iterate flags to allow 
**                           the blockSetup() method of TransactionManager
**                           to compensate for Java continue statements
**                           which otherwise bypass iterate() calls.
** 004 GES 20060314   @25059 Added toString().
** 005 GES 20060510   @26057 Added blockTop flag for use during iterate()
**                           processing.
** 006 GES 20060626   @27568 Added suppressError flag.
** 007 GES 20060804   @28428 Removed iterate and blockTop flags.
** 008 CA  20080207   @37025 Added block type field.
** 009 ECF 20080218   @37117 Integrated generics.
** 010 GES 20080505   @38208 Changed undoables to be an array for 
**                           performance.
** 011 GES 20080613   @38883 Added hadPause and endkeyRetry flags, the
**                           retryCount counter and the backupUndoList.
** 012 GES 20080625   @38957 Changed to BlockType enum.
** 013 GES 20080627   @38991 Added isUndoLevel flag.
** 014 CA  20081127   @40707 Added iterated flag - indicates if a block
**                           was iterated at least once.
** 015 GES 20081120   @40739 Added properties bitmap and interactions
**                           counter.
** 016 GES 20081210   @40865 Added rollup property.
** 017 GES 20090422   @41904 Converted to standard string formatting.
** 018 ECF 20090816   @43652 Added OutputParameterAssigner field.
** 019 CA  20121220          Added the Block instance to the block definition.
** 020 CA  20160627          Reworked undoable support - the undoables register themselves with
**                           all blocks up the stack, until either 1. the tx block which created
**                           it is reached or 2. the last tx block where it was saved is reached.
** 021 ECF 20171030          Added transId for transaction metadata support.
** 022 OM  20190117          Removed transId because it is not used any more.
**     CA  20190219          Added CATCH block support.
** 023 ECF 20190714          Added FOR block aggressive flushing support.
** 024 AIL 20191120          Added database trigger flag.
** 025 GES 20210809          Added finallyBlock runnable.
**     CA  20220120          Improved performance for finalizables, keep them already sorted by their weight.
**                           ConditionListener's are kept in their own list.
**     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.
**     CA  20221006          Lazy initialization for commitables and conditionListeners. 'finalizables' can be
**                           a set instead of a list, as their order is not important.  Refs #6824
**     SVL 20230108          Improved performance by replacing some "for-each" loops with indexed "for" loops.
** 026 CA  20230615          Added possibleCatch runnable, to delay execution of the catch block just before
**                           the FINALLY block executes.
** 027 CA  20231026          Do not pin the Block instance to memory.
** 028 CA  20231221          Refactored the WidgetPool scopeable support to register for processing as needed.
** 029 ES  20240513          Added hasStopAfter flag.
** 030 CA  20240708          A block needs to be aware if a RETURN ERROR or UNDO, THROW was used for exit, 
**                           as this will make a FINALLY block to act as if it executes in silent mode.
** 031 ES  20240919          Added undoThrow flag.
** 032 ES  20240410          Removed catchBlock variable, CATCH block will be marked as BlockType.CATCH. 
*/

/*
** 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 java.util.*;
import java.util.function.*;

import com.goldencode.p2j.security.*;

/**
 * Container of block information for Progress compatible rollback (undo)
 * commit and retry.  The members of this class are intentionally made
 * <code>public</code> since this is just a simple container.
 */
public class BlockDefinition
{
   /** Sprintf format specification for string representation. */
   private static final String spec = "[label = %s; type = %s; full = %b; "  +
                                      "trans_level = %s; external = %b; "    +
                                      "top_level = %b; loop = %b; "          +
                                      "loop_protection = %b; had_pause = %b" +
                                      "; endkey_retry = %b; next_or_leave =" +
                                      " %s; is_retry = %b; needs_retry = %b" +
                                      "; FOR (aggressive) flushing = %b"     +
                                      "; ilp_count = %d; pending_break = %b" +
                                      "; database_trigger = %b; properties = '%s'" +
                                      "; finally = %s]";
   
   /** Label to identify this block. */
   public String label = null;
   
   /** Caches whether or not this block started a transaction. */
   public boolean full = false;
   
   /** Level of transaction support for this block. */
   public int level = TransactionManager.NO_TRANSACTION;
   
   /**
    * The depth of this transaction block, when {@link #level} is 
    * {@link TransactionManager#TRANSACTION} or {@link TransactionManager#SUB_TRANSACTION}.
    */
   public int txNestingLevel = -1;
   
   /** Bitmap specifying the block properties active for this block. */
   public int props = TransactionManager.PROP_NONE;
   
   /** Defines if this block is derived from an external procedure. */
   public boolean external = false;
   
   /** Defines if this block is a top-level block (a method) or not. */
   public boolean topLevel = false;
   
   /** Defines if this block is a loop or not. */
   public boolean loop = false;
   
   /** Has this block iterated at least once. */
   public boolean iterated = false;
   
   /** This block's type. */
   public BlockType type = BlockType.UNKNOWN;

   /** Defines if this iteration requires infinite loop protection. */
   public boolean loopProtection = true;
   
   /** Defines if this iteration has encountered a pause. */
   public boolean hadPause = false;
   
   /** Marks the current retry as one caused by an <code>ENDKEY</code>. */
   public boolean endkeyRetry = false;
   
   /** Counts the number of sequential retries converted to NEXT or LEAVE. */
   public int ilpCount = -1;
   
   /** Counter for the number of interactive stmts in the current block. */
   public int interactions = 0;
   
   /** Aggregate interactions counter when a client block exits. */
   public boolean rollup = true;
   
   /** Has this block initialized at least once. */
   public boolean init = false;
   
   /**
    * Defines if this iteration has a pending break which was generated by 
    * infinite loop protection during a retry.
    */
   public boolean pendingBreak = false;
   
   /** Defines if this iteration requires a retry. */
   public boolean retry = false;
   
   /** Defines if this iteration is a retry (versus the first pass). */
   public boolean wasRetried = false;
   
   /** Determines if infinite loop protection converts retry into next or leave. */
   public boolean next = false;
   
   /** Flag indicating a top-level block is exiting via RETURN ERROR. */
   public boolean wasReturnError = false;
   
   /**
    * Flag indicating this block is nested within or is itself a FOR block and as such requires
    * aggressive buffer flushing.
    */
   public boolean forFlushing = false;
   
   /** Defines if this block is a database trigger, as:
    *  - schema trigger if is topLevel external procedure in Database Trigger class
    *  - session trigger if is topLevel trigger type
    */
   public boolean databaseTrigger = false;
   
   /** Indicates whether the block has a stop-after statement. */
   public boolean hasStopAfter = false;
   
   /** Defines if this block was undone. */
   public boolean rolledBack = false;
   
   /** Defines if this block was already committed. */
   public boolean committed = false;
   
   /** Defines if this blocks is or was the target of UNDO. */
   public boolean isUndoLevel = false;
   
   /** Defines if this block's caller requires errors to be suppressed. */
   public boolean suppressError = false;
   
   /** Stores the list of undoable objects. */
   public ArrayList<UndoablePair> undoables = null;
   
   /**
    * Stores the list of commitable objects to be notified at successful
    * completion of this block.
    */
   public ArrayList<Commitable> commitables = null;
   
   /**
    * Stores the list of finalizable objects to be notified when the flow
    * of control exist from this block in any manner.
    */
   public Set<Finalizable>[] finalizables = new Set[WeightFactor.WEIGHTS_ARRAY_SIZE];
   
   /**
    * The list of {@link Scopeable} instances which are required to be processed in this block.  
    * <p>
    * This list is dynamically adjusted - it can be added a new scopeable before or after the block has started.
    * If the new instance is registered after the block has started, the {@link Scopeable#scopeStart} will 
    * be executed at the time of registration.
    * <p>
    * The index is the scopeable's ID, as only an instance of each type can be registered. 
    */
   public Scopeable[] scopeList = null;
   
   /** The list of condition listeners.  Processed in the registration order.. */
   public ArrayList<ConditionListener> conditionListeners = null;
   
   
   /** Stores the output parameter assigner */
   public OutputParameterAssigner outputParmAssigner = null;
   
   /** Map of defined catch blocks*/
   public Map<Class<?>, Consumer<object>> catchBlocks = null;
   
   /** FINALLY block to be executed at the next iteration, retry or finished. */
   public Runnable finallyBlock = null;

   /** Possible CATCH block to be executed at the next iteration, retry or finished. */
   public Runnable possibleCatch;
   
   /** Flag indicating if this block has widget pool scoping. */
   public boolean widgetPool = false;

   /** Flag indicating that a rollback in an undo throw has been raised */
   public boolean undoThrow = false;
   
   /**
    * Construct an instance to represent a new block.
    *
    * @param    label
    *           This scope's label.
    * @param    level
    *           The type of transaction processing this scope can participate
    *           in.  This must be set to one of the following
    *           <code>TransactionManager</code> constants:
    *           <code>NO_TRANSACTION, SUB_TRANSACTION or TRANSACTION</code>.
    * @param    props
    *           A bitfield storing block properties which will be a
    *           combination of <code>TransactionManager</code> constants:
    *           <code>PROP_NONE, PROP_ERROR, PROP_ENDKEY, PROP_STOP</code>
    *           and <code>PROP_QUIT</code>.
    * @param    full
    *           If <code>true</code> this scope starts a full transaction
    *           (this is the scope at which database commits will occur).
    * @param    external
    *           <code>true</code> if this block is associated with the
    *           external procedure (in Progress terms). This controls the
    *           cleanup of certain resources such as temp-tables, frames
    *           and streams.
    * @param    topLevel
    *           <code>true</code> if this block is a method.
    * @param    loop
    *           <code>true</code> if this block is a loop.
    * @param    next
    *           <code>true</code> if this infinite loop protection should
    *           convert a retry into a next (continue), otherwise the retry
    *           will be converted into a leave (break).
    * @param    databaseTrigger
    *           <code>true</code> if this is a topLevel block defined as a
    *           database trigger
    * @param    type
    *           This block's type.
    */
   public BlockDefinition(String    label,
                          int       level,
                          int       props,
                          boolean   full,
                          boolean   external,
                          boolean   topLevel,
                          boolean   loop,
                          boolean   next,
                          boolean   forFlushing,
                          boolean   databaseTrigger,
                          BlockType type)
   {
      this.label           = label;
      this.level           = level;
      this.props           = props;
      this.full            = full;
      this.external        = external;
      this.topLevel        = topLevel;
      this.loop            = loop;
      this.next            = next;
      this.forFlushing     = forFlushing;
      this.databaseTrigger = databaseTrigger;
      this.type            = type;
   }
   
   /**
    * Render this block's data as a human readable string.
    *
    * @return   The human readable form of this block's data.
    */
   public String toString()
   {
      Object[] parms = new Object[]
      {
         label,
         type.toString(),
         full,
         TransactionManager.decodeLevel(level),
         external,
         topLevel,
         loop,
         loopProtection,
         hadPause,
         endkeyRetry,
         (next ? "next" : "leave"),
         wasRetried,
         retry,
         forFlushing,
         ilpCount,
         pendingBreak,
         databaseTrigger,
         TransactionManager.decodeProperties(props),
         (finallyBlock == null) ? "none" : finallyBlock.toString()
      };
      
      return String.format(spec, parms);
   }
}