/*BEGINPROLOGUE****************************************************************
 * @copyright(disclaimer)                                                     *
 *                                                                            *
 * DISCLAIMER OF WARRANTIES.                                                  *
 *                                                                            *
 * The following IBM Content Manager Enterprise Edition code is sample code   *
 * created by IBM Corporation. IBM grants you a nonexclusive copyright        *
 * license to use this sample code example to generate similar function       *
 * tailored to your own specific needs. This sample code is not part of any   *
 * standard IBM product and is provided to you solely for the purpose of      *
 * assisting you in the development of your applications. This example has    *
 * not been thoroughly tested under all conditions. IBM, therefore cannot     *
 * guarantee nor may you imply reliability, serviceability, or function of    *
 * these programs. The code is provided "AS IS", without warranty of any      *
 * kind. IBM shall not be liable for any damages arising out of your or any   *
 * other parties use of the sample code, even if IBM has been advised of the  *
 * possibility of such damages. If you do not agree with these terms, do not  *
 * use the sample code.                                                       *
 *                                                                            *
 * Licensed Materials - Property of IBM                                       *
 * 5724-B19, 5697-H60                                                         *
 * © Copyright IBM Corp. 1994, 2013 All Rights Reserved.                      *
 *                                                                            *
 * US Government Users Restricted Rights - Use, duplication or disclosure     *
 * restricted by GSA ADP Schedule Contract with IBM Corp.                     *
 *                                                                            *
 * @endCopyright                                                              *
 ******************************************************************************

Definitions
    Tx          : Abbreviation of "Transaction".
    Transaction : A transaction is a sequence of operations that make up a
                  single "atomic" change, meaning all changes need to happen
                  together.  The individual changes are not desired if all
                  cannot be completed.
                  
Explicit Transacitons
    The User-Program may explicitly start & commit transactions.  This is
    useful when the User-Program wishes all or none of actions to be made
    permanent.  If the User-Program detects an error, it may undo / rollback
    all since the start transaction command.
    
    An example of one such use is in an online ordering system for Airplane
    Tickets.  Suppose a user places an order for one airline ticket.  The
    system proceeds to charge the credit card and reserve the seat.  If an
    error occurs while reserving the seat within the application, the credit
    card charge can be easily undone with the system.

    Another example, if a bank customer transfers money from one account to
    the other, the subtraction from one account and addition to the other
    account need to happen at the same time.  The back does not want the
    addition to proceed if the subtraction does not.
    
    For important operations, your application cannot rely on reversing a
    series of updates itself that it made prior to the operation that
    failed.  Perhaps the connection is lost or your system crashed due to
    a power failure and you cannot undo the prior transactions.  Your
    application might not remember what it was doing before the crash if
    it did not have a logging and recover mechanism.  With a transaction,
    you tell the system to start recording your changes, but drop them if
    you do not explicitly tell it that you are done.  Let the system take
    care of remembering to roll back uncommitted changes.
    
    Transaction
    Operations    Function            Description
    ------------  ------------------  ----------------------------------------------------------
    Start         startTransaction()  Start a new transaction
    ............................................................................................
    Rollback      rollback()          Undo / rollback all changes on the persistent datastore
                                      since the startTransaction() call.  Variables & DDOs in
                                      local memory will not be affected and should be considered
                                      invalid.
    ............................................................................................
    Commit        commit()            Commit changes / Make changes permanent.
    --------------------------------------------------------------------------------------------

Rollback
    Both your application and the system can roll back changes depending on who
    detects the error.  Your application can have additional checks that might
    be valid in the data model in the system, but an invalid condition according
    to your application.
    
    After the transaction has been rolled back, the Explicit Transaction is
    over and all subsequent operations are processed outside of an explicit
    transaction until another start Explicit Transaction request is made.
    All valid subsequent operations succeed as an individual implicit
    transaction regardless of the success or failure of any previous Explicit
    Transaction.  Once any explicit transaction is committed or rolled back,
    the system is back in normal operating mode with no memory of the
    canceled transaction.
    
    When the system detects an error during a persistent operation the system
    will automatically roll back the explicit transaction by default and the
    explicit transaction is ended.  With Version 8.2 with fix pack 7 or later,
    administrators can change the automatic closure of the open Explicit
    Transaction that has failed.  If during an Explicit Transaction the 
    system detects invalid data such as a missing required attribute value,
    the system will or will not automatically close the open transaction.
    
    Automatic Closure with System Error Rollback (Default)
        With Version 8.1 through Version 8.2 with fix pack 6, the system will
        always automatically rollback and end the Explicit Transaction that
        is in progress when the system detects an error during a persistent
        operation.  With Version 8.2 with fix pack 7 or later, the system will
        automatically close the transaction by default unless the administrator
        specifically configures the system to behave differently.
        
        After the Explicit Transaction is rolled back and closed, all subsequent
        operations succeed if they are valid on an individual basis.  Although
        you never called commit or rollback to explicitly end the open
        transaction, the system notified your application that an error
        occurred through an exception.  The system will not notify your
        application again and will accept subsequent operations as valid
        individual operations regardless of the state of the canceled Explicit
        Transaction.  The system believes it is back in normal operating mode.
        All subsequent operations proceed individually outside of any explicit
        transaction.
    
    Keep Open with System Error Rollback (Doomed to Fail Until Closed)
        With Version 8.2 with fix pack 7 or later, the system will mark the
        transaction as failed when it detects an error during a persistent
        operation, but it will not automatically end the transaction that
        was in progress.  Persistently, the entire transaction is still
        internally rolled back in the datastore, but the behavior for
        subsequent operations has changed.  The Expliict Transaction is
        considered open, but remains in a failed state.  
        
        There is no way to recover from the failure other than to rollback
        the entire transaction.  Subsequent operations, commit requests,
        and new start transaction requests fail until you explicitly roll
        back the transaction state.  This forces your application to
        acknowledge that the explicit transaction has ended.
        
        This choice provides symmetry with respect to always pairing 
        closure of the transaction through explicit commit() or rollback()
        with the application that explicitly opened the transaction with
        a start transaction request.
        
    Refer to sample STransactionsRollbackChangeICM for information on 
    changing the the behavior when the system detects errors.
    
    NOTE:  Rolling back changes affects the persistent datastore.  It does not 
           affect variables & DDOs in local memory.  The User-Application may 
           need to refresh all data in memory with the information from the 
           persistent datastore.

*******************************************************************************/

// Imports
import com.ibm.mm.sdk.common.*;
import com.ibm.mm.sdk.server.*;
import java.io.*;

/************************************************************************************************
 *          FILENAME: STransactionsICM.java
 *                    ---------------------------------------------------------------------------
 *       DESCRIPTION: Explicit Transactions, Starting Transactions, Rolling Back Transactions,
 *                    Committing Transactions.
 *                    ---------------------------------------------------------------------------
 *     DEMONSTRATION: Explicit Transactions
 *                    Start Transaction
 *                    Rollback Transaction
 *                    Commit Transaction
 *                    ---------------------------------------------------------------------------
 * COMMANDLINE USAGE: java STransactionsICM <database> <userName> <password>
 *                    ---------------------------------------------------------------------------
 *     PREREQUISITES: The Data Model must be defined.  If needed please run the following Samples  
 *                        - SAttributeDefinitionCreationICM
 *                        - SAttributeGroupDefCreationICM
 *                        - SReferenceAttrDefCreationICM
 *                        - SItemTypeCreationICM
 *                    ---------------------------------------------------------------------------
 *    FILES REQUIRED: SConnectDisconnectICM.java
 *                    SSearchICM.java
 ************************************************************************************************/
public class STransactionsICM{
    
    //-------------------------------------------------------------
    // Main
    //-------------------------------------------------------------
    /**
     * Run the Sample.
     * @param argv[] String Array containing arguments.  Optional arguments are <databse> <userName> <password>.
     */
    public static void main(String argv[]) throws DKException, Exception{
    
        // Defaults for connecting to the database.
        String database = SConnectDisconnectICM.DEFAULT_DATABASE;
        String userName = SConnectDisconnectICM.DEFAULT_USERNAME;
        String password = SConnectDisconnectICM.DEFAULT_PASSWORD;
        
        //------------------------------------------------------------
        // Checking for input parameters
        //--------------------------------------------------------------
        if (argv.length < 3) { // if not all 3 arguments were specified, use defaults and report correct usage.
            System.out.println("Usage: " );
            System.out.println("  java STransactionsICM <database> <userName> <password>" );
            System.out.println("  *** Some parameters not specified, using defaults..." );
            System.out.println("");
        } else {  // otherwise enough parameters were specified, use the first 3 for the 3 parameters.
            if (argv.length > 0) database = argv[0];
            if (argv.length > 1) userName = argv[1];
            if (argv.length > 2) password = argv[2];
        }//end else
        String ver = SConnectDisconnectICM.VERSION;
        
        System.out.println("===========================================");
        System.out.println("IBM DB2 Content Manager                v"+ver);
        System.out.println("Sample Program:  STransactionsICM");
        System.out.println("-------------------------------------------");
        System.out.println(" Database: "+database);
        System.out.println(" UserName: "+userName);
        System.out.println("===========================================");
        
        try{
            //-------------------------------------------------------------
            // Connect to datastore
            //-------------------------------------------------------------
            // See Sample SConnectDisconnectICM for more information
            System.out.println("Connecting to datastore (Database '"+database+"', UserName '"+userName+"')...");

                DKDatastoreICM dsICM = new DKDatastoreICM();  // Create new datastore object.
                dsICM.connect(database,userName,password,""); // Connect to the datastore.

            System.out.println("Connected to datastore (Database '"+dsICM.datastoreName()+"', UserName '"+dsICM.userName()+"').");

            //-----------------------------------------------------------------
			// Start an Explicit Transaction
			//-----------------------------------------------------------------
			System.out.println("Starting an explicit transaction...");
						
				dsICM.startTransaction();
						
			System.out.println("Started an explicit transaction.");

			//----------------------------------------------------------------
			// Do the something within transaction..
			//----------------------------------------------------------------
            // Perform operations within a transaction, such as creating
            // a new Item.
            System.out.println("Creating an Item...");

	            DKDDO ddoDocument = dsICM.createDDO("S_simple", DKConstant.DK_CM_DOCUMENT);
	            ddoDocument.add(); // Save the DDO in the persistent datastore.

	            String itemId = ((DKPidICM)ddoDocument.getPidObject()).getItemId();  // Save Item ID for later use.
	            System.out.println("Created Item '"+itemId+"'.");

            System.out.println("Created an Item.");
						
			//-----------------------------------------------------------------
    		// Rollback our changes.
			//-----------------------------------------------------------------
			System.out.println("Rolling back / Undoing changes...");
						
				dsICM.rollback();
						
			System.out.println("Rolled back changes.");

    		//-----------------------------------------------------------------
	        // Proving transaction was undone
    		//-----------------------------------------------------------------
	        // The item should not be added to the datastore.
	        // We can search for the Item ID, and no results should be found.
	        //
	        // For more information on Search, please refer to SSearchICM.
	        System.out.println("Proving transaction was undone...");
	        
                String query = "/*[@ITEMID=\""+itemId+"\"]";  // Looks like: /*[@ITEMID=\"A1001001A01J09B00241C95000\"];
                
                // Always Specify Retrieve Options, Effectively Doing an "IDONLY" Search for This Check
                DKRetrieveOptionsICM dkRetrieveOptions = DKRetrieveOptionsICM.createInstance(dsICM);
                
                // Specify Search / Query Options
                DKNVPair options[] = new DKNVPair[3];
                options[0] = new DKNVPair(DKConstant.DK_CM_PARM_MAX_RESULTS, "1");               // Specify max using a string value.
                options[1] = new DKNVPair(DKConstant.DK_CM_PARM_RETRIEVE,    dkRetrieveOptions); // Always specify desired Retrieve Options.
                options[2] = new DKNVPair(DKConstant.DK_CM_PARM_END,         null);              // Must mark the end of the NVPair

                DKResults results = (DKResults)dsICM.evaluate(query, DKConstant.DK_CM_XQPE_QL_TYPE, options);
                
                // No results should be found.
                SSearchICM.printResults(results);  // Print Results Using Wrapper in SSearchICM.
                if(results.cardinality()>0)
                    throw new Exception("Item(s) were found that match the Item ID.  The transaction was not undone.");

	        System.out.println("Proved transaction was undone.");

            //-----------------------------------------------------------------
			// Start another Explicit Transaction
			//-----------------------------------------------------------------
			System.out.println("Starting an explicit transaction...");
						
				dsICM.startTransaction();
						
			System.out.println("Started an explicit transaction.");

			//----------------------------------------------------------------
			// Do the something within transaction..
			//----------------------------------------------------------------
            // Perform operations within a transaction, such as creating
            // a new Item.
            System.out.println("Creating an Item...");

	            ddoDocument = dsICM.createDDO("S_simple", DKConstant.DK_CM_DOCUMENT);
	            ddoDocument.add(); // Save the DDO in the persistent datastore.

	            itemId = ((DKPidICM)ddoDocument.getPidObject()).getItemId();  // Save Item ID for later use.
	            System.out.println("Created Item '"+itemId+"'.");

            System.out.println("Created an Item.");
						
			//-----------------------------------------------------------------
    		// Commit Changes
			//-----------------------------------------------------------------
			// Make all changes permanent.
			System.out.println("Committing Changes / Making Changes Permanent...");
						
				dsICM.commit();
						
			System.out.println("Committed Changes / Made Changes Permanent.");
            
            //-------------------------------------------------------------
            // Deteting System Error Rollback Automatic Closure Setting
            //-------------------------------------------------------------
            // As explained in the "Rollback" section in the header 
            // documentation, the system can be configured by the 
            // administrator to automatically end the transaction when
            // the system detects an error and rolls back (default) or
            // require the caller to explicitly close out the transaction.
            //
            // PERFORMANCE:  This makes a call to the server and should only
            //   WARNING     be used to check the setting by an administrator
            //               Calling from your program will invoke a call to
            //               the server for each check.
            System.out.println("Detecting System Error Rollback Automatic Closure Setting...");

                    System.out.println("    - Sending Request to Server...");

                if(isTransactionErrorRollbackAutoClosed(dsICM))
                    System.out.println("    - AUTO: Automatically Close Transaction on System Error Rollback (Default)");
                else
                    System.out.println("    - KEEP: Keep Transaction Open / Application Closure on System Error Rollback");

            System.out.println("Detected System Error Rollback Automatic Closure Setting.");

            //-------------------------------------------------------------
            // Clean Up Database After Sample
            //-------------------------------------------------------------
            // This sample will clean up / undo everything that stored
            // in the database.
            System.out.println("Cleaning Up...");

                ddoDocument.del();

            System.out.println("Cleaned Up.");
            
            //-------------------------------------------------------------
            // Disconnect from datastore & Destroy Reference
            //-------------------------------------------------------------
            // See Sample SConnectDisconnectICM for more information
            System.out.println("Disconnecting from datastore & destroying reference...");            

                dsICM.disconnect();
                dsICM.destroy();

            System.out.println("Disconnected from datastore & destroying reference.");

            //-------------------------------------------------------------
            // Sample program completed without exception
            //-------------------------------------------------------------
            System.out.println("\n==========================================");
            System.out.println("Sample program completed.");
            System.out.println("==========================================\n");
        }
        //------------------------------------------------------------
        // Catch & Print Exceptions        
        //------------------------------------------------------------
        catch (DKException exc){
            SConnectDisconnectICM.printException(exc);  // Print the exception using the function listed below.
            throw(exc);
        } catch (Exception exc) {
            SConnectDisconnectICM.printException(exc);  // Print the exception using the function listed below.
            throw(exc);
        }
    }// end main

    //=================================================================
    // Wrapper Functions
    //=================================================================
    // The following are wrapper functions for functionality covered
    // in this sample.  These functions can be used by other samples.

   /**
    * Ask the server if the Explicit Transactions are configured
    * to automatically close the explicit transaction when the server
    * detects an error during a persistent operation and rolls back or
    * if the application has to close the transaction before continuing.
    * For more information, refer to the header documentation of this
    * sample, section "Rollback".
    *
    * PERFORMANCE:  This makes a call to the server and should only
    *   WARNING     be used to check the setting by an administrator
    *               Calling from your program will invoke a call to
    *               the server for each check.
    * 
    * @param dsICM - Connected DKDatastoreICM object.
    * @return Returns 'true' if the transaction behavior is configured to 
    *         automatically close when the system detects and error and it
    *         automatically rolls back, thereby ending the explicit
    *         transaction.  Returns 'false' if the transaction behavior is
    *         configured to keep the explicit transaction open in a failed
    *         state, requiring the caller to explicitly rollback before continuing.
    **/
    public static boolean isTransactionErrorRollbackAutoClosed(DKDatastoreICM dsICM) throws Exception {
        if(dsICM==null)                throw new Exception("Invalid Parameter to STransactionsICM::isTransactionErrorRollbackAutoClosed(DKDatastoreICM dsICM).  Parameter 'dsICM' is null.");
        if(dsICM.isConnected()==false) throw new Exception("Invalid Parameter to STransactionsICM::isTransactionErrorRollbackAutoClosed(DKDatastoreICM dsICM).  Parameter 'dsICM' is a DKDatastoreICM that is not connected.");
        
        DKDatastoreDefICM        dsDefICM = (DKDatastoreDefICM)dsICM.datastoreDef();                     // Get the Datastore Definition Object
        DKDatastoreAdminICM      dsAdmin  = (DKDatastoreAdminICM) dsDefICM.datastoreAdmin();             // Get the Datastore Administration
        DKConfigurationMgmtICM   cfgMgmt  = (DKConfigurationMgmtICM) dsAdmin.configurationManagement();  // Get the configuration management.
        DKLSConfigurationMgmtICM lsCfg    = cfgMgmt.lsConfigurationMgmt();                               // Get the Resource Manager configuration.
        
        // Get the Library Server configuration
        DKLSCfgDefICM lsCfgDef = lsCfg.retrieveConfigParameters();
        
        return(!lsCfgDef.isExpTransAutoRollbackDoomedEnabled());
    }//end isTransactionErrorRollbackAutoClosed()

}//end class STransactionsICM