/*
 * Copyright (c) 1998, 2021 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,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.tests.forceupdate;

import org.eclipse.persistence.sessions.*;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.testing.framework.*;
import org.eclipse.persistence.testing.models.forceupdate.*;

/**
    Scenario:
    Two threads(here we use two UOWs to simulate two threads) read in the same emplyee
    object. The first thread updates employee's address(relationship) and commits. The
    second thread updates the emplyee's salary(non-relationship attribute based on
    where the emplyee lives)and commits. No exception is thrown but the updated salary
    is invalid. If the first thread forces update the version value of the employee, an
    optimistic lock exception is thrown in the second thread.

    (Version locking stores in the Cache)
    Test 1: (Correctly use method forceUpdateToVersionField())
    UOW1 updates employee's address,
    calls forceUpdateToVersionField(Object cloneFromUOW1,true) and commits.
    UOW2 updates the employee's salary and commits.
    The test verified an optimistic lock exception is thrown in UOW2.

    Test 2: (forceUpdateToVersionField() doesn't effect read-only UOW)
    UOW1 updates employee's address,
    calls forceUpdateToVersionField(Object cloneFromUOW1,true) and commits.
    UOW2 has only read-operation and commits.
    The test verified no optimistic lock exception is thrown in UOW2.

    Test 3: (Test method removeForceUpdateToVersionField())
    UOW1 updates employee's address,
    calls forceUpdateToVersionField(cloneFromUOW1,true),COMMIT&amp;RESUMEs.
    It calls removeForceUpdateToVersionField(cloneFromUOW1),
    updates employee's address again and commits.
    UOW2 reads employee after the first commit of UOW1,updates the emplyee's salary
    and commits.
    The test verified no optimistic lock exception is thrown in UOW2.

    Test 4: (Demonstrate the result when no using forceUpdateToVersionField())
    UOW1 updates employee's address, commits.
    UOW2 updates the employee's salary and commits.
    The test verified no optimistic lock exception is thrown in UOW2.
*/
public class FUVLVersionLockInCacheTest extends TransactionalTestCase {
    boolean exceptionCaught = false;
    int testnumber;

    public FUVLVersionLockInCacheTest(int anInt) {
        testnumber = anInt;
        setName(getName() + "(Test" + anInt + ")");
        switch (testnumber) {
        case 1:
            setDescription("Correctly use method forceUpdateToVersionField()");
            break;
        case 2:
            setDescription("forceUpdateToVersionField() doesn't effect read-only UOW");
            break;
        case 3:
            setDescription("Test method removeForceUpdateToVersionField()");
            break;
        case 4:
            setDescription("Demonstrate the result when no using forceUpdateToVersionField()");
            break;
        default:
            break;
        }
    }

    @Override
    public void test() {
        switch (testnumber) {
        case 1:
            test1();
            break;
        case 2:
            test2();
            break;
        case 3:
            test3();
            break;
        case 4:
            test4();
            break;
        default:
            break;
        }
    }

    public void test1() {
        UnitOfWork uow1, uow2;

        ExpressionBuilder bldr = new ExpressionBuilder();
        Expression exp1 = bldr.get("firstName").equal("Bob");
        Expression exp2 = bldr.get("lastName").equal("Smith");
        Expression exp = exp1.and(exp2);

        EmployeeVLIC emp = (EmployeeVLIC)getSession().readObject(EmployeeVLIC.class, exp);

        uow1 = getSession().acquireUnitOfWork();
        uow2 = getSession().acquireUnitOfWork();
        EmployeeVLIC clone1 = (EmployeeVLIC)uow1.registerObject(emp);
        EmployeeVLIC clone2 = (EmployeeVLIC)uow2.registerObject(emp);

        uow1.forceUpdateToVersionField(clone1, true);
        clone1.getAddress().setStreet("4 Hutton Centre,Suite 900");
        clone1.getAddress().setCity("Santa Ana");
        clone1.getAddress().setProvince("CA");
        clone1.getAddress().setCountry("USA");
        clone1.getAddress().setPostalCode("92797");
        uow1.commit();

        clone2.setSalary(clone2.getSalary() + 200);
        try {
            uow2.commit();
        } catch (OptimisticLockException exception) {
            exceptionCaught = true;
        }
    }

    public void test2() {
        UnitOfWork uow1, uow2;

        ExpressionBuilder bldr = new ExpressionBuilder();
        Expression exp1 = bldr.get("firstName").equal("Bob");
        Expression exp2 = bldr.get("lastName").equal("Smith");
        Expression exp = exp1.and(exp2);

        EmployeeVLIC emp = (EmployeeVLIC)getSession().readObject(EmployeeVLIC.class, exp);

        uow1 = getSession().acquireUnitOfWork();
        uow2 = getSession().acquireUnitOfWork();
        EmployeeVLIC clone1 = (EmployeeVLIC)uow1.registerObject(emp);
        EmployeeVLIC clone2 = (EmployeeVLIC)uow2.registerObject(emp);

        uow1.forceUpdateToVersionField(clone1, true);
        clone1.getAddress().setStreet("4 Hutton Centre,Suite 900");
        clone1.getAddress().setCity("Santa Ana");
        clone1.getAddress().setProvince("CA");
        clone1.getAddress().setCountry("USA");
        clone1.getAddress().setPostalCode("92797");
        uow1.commit();

        // uow2 has only read-operation here
        //...
        try {
            uow2.commit();
        } catch (OptimisticLockException exception) {
            exceptionCaught = true;
        }
    }

    public void test3() {
        UnitOfWork uow1, uow2;

        ExpressionBuilder bldr = new ExpressionBuilder();
        Expression exp1 = bldr.get("firstName").equal("Bob");
        Expression exp2 = bldr.get("lastName").equal("Smith");
        Expression exp = exp1.and(exp2);

        EmployeeVLIC emp = (EmployeeVLIC)getSession().readObject(EmployeeVLIC.class, exp);

        uow1 = getSession().acquireUnitOfWork();
        EmployeeVLIC clone1 = (EmployeeVLIC)uow1.registerObject(emp);

        uow1.forceUpdateToVersionField(clone1, true);
        clone1.getAddress().setStreet("100 Young Street");
        clone1.getAddress().setCity("Toronto");
        clone1.getAddress().setProvince("ON");
        clone1.getAddress().setCountry("CANADA");
        clone1.getAddress().setPostalCode("K489Y7");
        uow1.commitAndResume();
        uow1.removeForceUpdateToVersionField(clone1);

        uow2 = getSession().acquireUnitOfWork();
        EmployeeVLIC clone2 = (EmployeeVLIC)uow2.registerObject(emp);

        clone1.getAddress().setStreet("4 Hutton Centre,Suite 900");
        clone1.getAddress().setCity("Santa Ana");
        clone1.getAddress().setProvince("CA");
        clone1.getAddress().setCountry("USA");
        clone1.getAddress().setPostalCode("92797");
        uow1.commit();

        clone2.setSalary(clone2.getSalary() + 200);
        try {
            uow2.commit();
        } catch (OptimisticLockException exception) {
            exceptionCaught = true;
        }
    }

    public void test4() {
        UnitOfWork uow1, uow2;

        ExpressionBuilder bldr = new ExpressionBuilder();
        Expression exp1 = bldr.get("firstName").equal("Bob");
        Expression exp2 = bldr.get("lastName").equal("Smith");
        Expression exp = exp1.and(exp2);

        EmployeeVLIC emp = (EmployeeVLIC)getSession().readObject(EmployeeVLIC.class, exp);

        uow1 = getSession().acquireUnitOfWork();
        uow2 = getSession().acquireUnitOfWork();
        EmployeeVLIC clone1 = (EmployeeVLIC)uow1.registerObject(emp);
        EmployeeVLIC clone2 = (EmployeeVLIC)uow2.registerObject(emp);

        clone1.getAddress().setStreet("4 Hutton Centre,Suite 900");
        clone1.getAddress().setCity("Santa Ana");
        clone1.getAddress().setProvince("CA");
        clone1.getAddress().setCountry("USA");
        clone1.getAddress().setPostalCode("92797");
        uow1.commit();

        clone2.setSalary(clone2.getSalary() + 200);
        try {
            uow2.commit();
        } catch (OptimisticLockException exception) {
            exceptionCaught = true;
        }
    }

    @Override
    protected void verify() {
        switch (testnumber) {
        case 1:
            if (!exceptionCaught)
                throw new TestErrorException("No Optimistic Lock exception was thrown");
            break;
        case 2:
        case 3:
        case 4:
            if (exceptionCaught)
                throw new TestErrorException("Optimistic Lock exception should not have been thrown");
            break;
        default:
            break;
        }
    }
}
