/*
 * Copyright (c) 2009, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.ts.tests.common.connector.whitebox.mdcomplete;

import javax.transaction.xa.Xid;

import com.sun.ts.lib.util.TestUtil;
import com.sun.ts.tests.common.connector.util.ConnectorStatus;
import com.sun.ts.tests.common.connector.whitebox.ContextWork;
import com.sun.ts.tests.common.connector.whitebox.Debug;
import com.sun.ts.tests.common.connector.whitebox.NestWork;
import com.sun.ts.tests.common.connector.whitebox.TSSecurityContext;
import com.sun.ts.tests.common.connector.whitebox.TSSecurityContextWithListener;
import com.sun.ts.tests.common.connector.whitebox.WorkImpl;
import com.sun.ts.tests.common.connector.whitebox.XidImpl;

import jakarta.resource.spi.BootstrapContext;
import jakarta.resource.spi.work.ExecutionContext;
import jakarta.resource.spi.work.SecurityContext;
import jakarta.resource.spi.work.TransactionContext;
import jakarta.resource.spi.work.WorkCompletedException;
import jakarta.resource.spi.work.WorkContextErrorCodes;
import jakarta.resource.spi.work.WorkException;
import jakarta.resource.spi.work.WorkManager;

public class MDCompleteWorkManager {
    private BootstrapContext bsc = null;

    private WorkManager wmgr;

    private String sicUser = "";

    private String sicPwd = "";

    private String eisUser = "";

    private String eisPwd = "";

    public MDCompleteWorkManager(BootstrapContext val) {
        debug("enterred constructor");
        this.bsc = val;
        this.wmgr = bsc.getWorkManager();

        this.sicUser = TestUtil.getSystemProperty("j2eelogin.name");
        this.sicPwd = TestUtil.getSystemProperty("j2eelogin.password");
        this.eisUser = TestUtil.getSystemProperty("eislogin.name");
        this.eisPwd = TestUtil.getSystemProperty("eislogin.password");
        debug("leaving constructor");
    }

    public void runTests() {
        debug("enterred runTests");
        doWork();
        testNestedContext();
        testNestedContext2();
        testNestedContext3();
        submitTICWork();
        debug("leaving runTests");
    }

    public void doWork() {
        debug("MDCompleteWorkManager enterred doWork");

        try {
            WorkImpl workimpl = new WorkImpl(wmgr);
            ExecutionContext ec = startTx();
            ContextWork w1 = new ContextWork(wmgr);

            // helps test: Connector:SPEC:72, Connector:SPEC:73, Connector:SPEC:214
            // this better generate an end result of a WorkCompletedException with
            // an error code == WorkContextErrorCodes.UNSUPPORTED_CONTEXT_TYPE
            debug("MDCompleteWorkManager.doWork() submitting UnknownWorkContext generate error code.");

            UnknownWorkContext uwc = new UnknownWorkContext();
            uwc.setXid(ec.getXid());
            w1.addWorkContext(uwc);

            // submitting work inst w/ an unknown work context shoudl throw
            // proper error code...
            wmgr.doWork(w1);

        } catch (WorkCompletedException e) {
            // this must throw exception ==
            // WorkContextErrorCodes.UNSUPPORTED_CONTEXT_TYPE
            debug("MDCompleteWorkManager WorkCompletedException thrown is " + e.getMessage());

            // this helps verify assertion Connector:SPEC:214
            // get error code and make sure we get one that makes sense
            String strErrorCode = e.getErrorCode();
            if (WorkContextErrorCodes.UNSUPPORTED_CONTEXT_TYPE.equals(strErrorCode)) {
                // excellant - this is what we expect
                ConnectorStatus.getConnectorStatus().logState("MDCompleteWorkManager threw WorkContextErrorCodes.UNSUPPORTED_CONTEXT_TYPE");
                debug("MDCompleteWorkManager threw WorkContextErrorCodes.UNSUPPORTED_CONTEXT_TYPE");
            } else {
                // doh! we got incorrect error code
                debug("MDCompleteWorkManager threw WorkContextErrorCodes = " + strErrorCode);
            }
        } catch (WorkException we) {
            debug("MDCompleteWorkManager WorkException thrown is " + we.getMessage());
            Debug.printDebugStack(we);
        } catch (Exception ex) {
            debug("MDCompleteWorkManager Exception thrown is " + ex.getMessage());
            Debug.printDebugStack(ex);
        }

        debug("MDCompleteWorkManager leaving doWork");
    }

    /*
     * this method is used to facilitate testing assertion Connector:SPEC:305 the idea here is to test as follows: - create
     * parent work obj - add valid SIC to parent work obj - create 2nd work obj to be child and get nested within parent
     * work obj - assign NO SIC to child work obj - add child work obj into parent - execute parent work obj - parent has
     * valid SIC so should be okay but child has NO SIC and should NOT inherit the parents SIC so the child workobj XXXX:
     * how to verify the workInst with no SIC did not inherit!
     */
    public void testNestedContext2() {

        try {
            debug("enterred testNestedWork2()");

            // to properly test assert Connector:SPEC:305 we need to have nested
            // work objects where each work has a context set.
            ContextWork parent = new ContextWork(wmgr);
            NestWork nw = new NestWork();

            // create valid sic / creds for parent work obj
            SecurityContext psic = new TSSecurityContextWithListener(sicUser, sicPwd, eisUser, false);

            // lets add SIC to our parent work obj only
            parent.addWorkContext(psic); // add SIC w/ valid creds

            // add our child workobj(ie nw) into our parent work obj
            parent.addNestedWork(nw);

            wmgr.doWork(parent);

        } catch (WorkException e) {
            debug("testNestedWork2() - got WorkException()");
        } catch (Exception e) {
            // flow should not go here
            debug("got exception in testNestedContext2() with user = " + sicUser + " pwd = " + sicPwd + "  principal=" + eisUser);
            debug(e.toString());
            Debug.printDebugStack(e);
        }
        debug("leaving testNestedContext2()");
    }

    /*
     * This method is used to facilitate testing assertion Connector:SPEC:210 The following steps are needed to validate
     * assertion 210. - create parent work obj - add valid SIC to parent work obj - create 2nd work obj to be child and get
     * nested within parent work obj - assign another valid (authenticatable) SIC to child work obj - add child work obj
     * into parent work obj - execute parent work obj (which, in turn, attempts to execute child ) - parent has valid SIC so
     * should be okay - but child does not
     *
     * The goal here is to show that the AS supports nested contexts by supplying 2 different SIC contexts (one valid and
     * one invalid).
     *
     */
    public void testNestedContext3() {

        try {
            debug("enterred testNestedWork3()");

            // to properly test assert Connector:SPEC:305 we need to have nested
            // work objects where each work has a context set.
            ContextWork parent = new ContextWork(wmgr);
            NestWork nw = new NestWork();

            // create valid sic / creds for parent work obj
            TSSecurityContext psic = new TSSecurityContext(sicUser, sicPwd, eisUser, false);

            // create invalid sic / creds for child work obj
            // note we pass 'false' as we expect it should fail to authenticate
            // with teh bogus creds we are passing in...if it fails to authenticate,
            // it should log appropriate msg stating so which menas that our
            // child/nested work context did not inherit security from parent.
            TSNestedSecurityContext csic = new TSNestedSecurityContext("phakeUsr", "phakePwd", "phakeEis", false, false);
            nw.addWorkContext(csic); // add SIC w/ invalid creds

            // lets add SIC to our parent work obj only
            parent.addWorkContext(psic); // add SIC w/ valid creds

            // add our child workobj(ie nw) into our parent work obj
            parent.addNestedWork(nw);

            wmgr.doWork(parent);

        } catch (WorkException e) {
            debug("testNestedWork3() - got WorkException()");
        } catch (Exception e) {
            // flow should not go here
            debug("got exception in testSecurityInflow() with user = " + sicUser + " pwd = " + sicPwd + "  principal=" + eisUser);
            debug(e.toString());
            Debug.printDebugStack(e);
        }

        debug("leaving testNestedContext3()");
    }

    /*
     * This method is used to facilitate testing assertion Connector:SPEC:210 The following steps are needed to validate
     * assertion 210. - create parent work obj - add valid SIC to parent work obj - create 2nd work obj to be child and get
     * nested within parent work obj - assign another valid (authenticatable) SIC to child work obj - add child work obj
     * into parent work obj - execute parent work obj (which, in turn, attempts to execute child ) - parent has valid SIC so
     * should be okay - same with child
     *
     * The goal here is to show that the AS supports nested contexts by supplying 2 different SIC contexts (both valid).
     *
     */
    public void testNestedContext() {

        String strPass = "Nested Work and Nested Security Context worked.";

        try {
            debug("enterred testNestedContext()");

            // to properly test assert Connector:SPEC:210 we need to have nested
            // work objects where each work has a context set.
            ContextWork parent = new ContextWork(wmgr);
            NestWork nw = new NestWork();
            SecurityContext sic = new TSSecurityContextWithListener(sicUser, sicPwd, eisUser, false);

            // add SIC that should not be able to authenticate
            SecurityContext sic2 = new TSSecurityContextWithListener(sicUser, sicPwd, eisUser, false);

            // lets add two different SICs to our work objs
            parent.addWorkContext(sic); // valid creds
            nw.addWorkContext(sic2); // valid creds

            // add our child workobj(ie nw) into our parent work obj
            parent.addNestedWork(nw);

            // this may or may not throw WorkCompletedException upon
            // successful execution of work.
            wmgr.doWork(parent);

            // note: flow should make it here and NOT yeild any exceptions.
            ConnectorStatus.getConnectorStatus().logState(strPass);
            debug(strPass);

        } catch (WorkCompletedException e) {
            // could get here upon success work completion
            ConnectorStatus.getConnectorStatus().logState(strPass);
            debug(strPass);
        } catch (Exception e) {
            // should not get here
            debug("got exception in testSecurityInflow() with user = " + sicUser + " pwd = " + sicPwd + "  principal=" + eisUser);
            debug(e.toString());
            Debug.printDebugStack(e);
        }
        debug("leaving testNestedContext()");
    }

    public void submitTICWork() {

        try {
            debug("enterred submitTICWork()");

            ExecutionContext ec = startTx();
            ContextWork w1 = new ContextWork(wmgr);
            TransactionContext tic = new TransactionContext();
            tic.setXid(ec.getXid());

            // add same tic twice and AS should throw
            // WorkContextErrorCodes.DUPLICATE_CONTEXTS
            debug("adding Duplicate WorkContext (with dup TIC Listener) should throw WorkContextErrorCodes.DUPLICATE_CONTEXTS.");
            w1.addWorkContext(tic);
            w1.addWorkContext(tic);
            wmgr.doWork(w1);

            debug("submitted Duplicate WorkContext with dup TIC Listener");

        } catch (WorkCompletedException e) {
            String strErrorCode = e.getErrorCode();
            if (WorkContextErrorCodes.DUPLICATE_CONTEXTS.equals(strErrorCode)) {
                // excellant - this is what we expect
                ConnectorStatus.getConnectorStatus().logState("MDCompleteWorkManager threw WorkContextErrorCodes.DUPLICATE_CONTEXTS");
                debug("MDCompleteWorkManager correctly threw WorkContextErrorCodes.DUPLICATE_CONTEXTS");
            } else {
                // doh! we got incorrect error code
                debug("MDCompleteWorkManager threw improper WorkContextErrorCodes = " + strErrorCode);
            }
            debug("MDCompleteWorkManager threw WorkContextErrorCodes = " + strErrorCode);
        } catch (Exception e) {
            debug("got bad exception when testing for WorkContextErrorCodes.DUPLICATE_CONTEXTS");
            Debug.printDebugStack(e);
        }
        debug("leaving submitTICWork()");
    }

    private ExecutionContext startTx() {
        ExecutionContext ec = new ExecutionContext();
        try {
            Xid xid = new XidImpl();
            ec.setXid(xid);
            ec.setTransactionTimeout(5 * 1000); // 5 seconds
        } catch (Exception ex) {
            Debug.printDebugStack(ex);
        }
        return ec;
    }

    public void debug(String out) {
        Debug.trace("MDCompleteWorkManager:  " + out);
    }

}
