/*
 * Copyright (c) 2011, 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,
 * 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:
//     tware - test for bug 262157
package org.eclipse.persistence.testing.tests.simultaneous;

import java.math.BigDecimal;

import org.eclipse.persistence.testing.framework.AutoVerifyTestCase;
import org.eclipse.persistence.testing.framework.TestErrorException;
import org.eclipse.persistence.testing.tests.unitofwork.ConcurrentLargeProject;
import org.eclipse.persistence.testing.tests.unitofwork.ConcurrentPerson;

import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.sessions.UnitOfWork;
/**
 * Test for Bug 262157
 * This test tests to ensure no dealock occurs in the following scenario
 * There is a bidirectional realtionship between an object (A) without inheritance and an object (B) with inheritance with a superclass (C)
 * A eagerly maps to C and B eagerly maps to A
 * If the mappings are setup so that A1 maps to B1 and B1 maps to A1, ensure no deadlock will occur
 * This test starts a thread that reads the A and pauses the build after it has acquired its locks.  It then starts
 * a thread that reads B and tests to ensure both threads run to completion
 * @author tware
 *
 */
public class ConcurrentReadOneToOneInheritanceTest extends AutoVerifyTestCase{

    protected BigDecimal personId = null;
    protected BigDecimal projectId = null;
    protected ConcurrentPerson person = null;

    protected boolean deadlockDetected = false;

    public void setup(){
        deadlockDetected = false;
        UnitOfWork uow = getSession().acquireUnitOfWork();
        person = new ConcurrentPerson();
        person.name = "Wenger";
        ConcurrentLargeProject project = new ConcurrentLargeProject();
        project.setName("Football");
        person.setHobby(project);
        project.setSupervisor(person);
        uow.registerObject(person);
        uow.registerObject(project);
        uow.commit();
        uow.release();
        getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
        personId = person.id;
        projectId = project.getId();
    }

    public void test(){
        ConcurrentPerson.RUNNING_TEST = ConcurrentPerson.ONE_TO_ONE_INHERITANCE;
        Thread thread1 = new Thread(new PersonReader(getSession().acquireUnitOfWork(), personId));
        Thread thread2 = new Thread(new LargeProjectReader(getSession().acquireUnitOfWork(), projectId));
        thread1.start();
        thread2.start();
        try {
            thread1.join(30000);
            if (thread1.isAlive()){
                try{
                    thread1.interrupt();
                    thread2.interrupt();
                }catch (Exception e) {
                }
                deadlockDetected = true;
            }
        } catch (InterruptedException ex) {

        }
    }

    public void verify(){
        if (deadlockDetected){
            throw new TestErrorException("Deadlock detected when reading a bidirectional 1-1 with Inheritance.");
        }
    }

    public void reset(){
        ConcurrentPerson.RUNNING_TEST = ConcurrentPerson.NONE;
        UnitOfWork uow = getSession().acquireUnitOfWork();
        uow.deleteObject(person.getHobby());
        uow.deleteObject(person);
        uow.commit();
        getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
    }

    public class PersonReader implements Runnable{
        private UnitOfWork uow;
        private BigDecimal id;

        public PersonReader(UnitOfWork uow, BigDecimal id){
            this.uow = uow;
            this.id = id;
        }

        public void run(){
            uow.readObject(ConcurrentPerson.class, (new ExpressionBuilder()).get("id").equal(id));
        }
    }

    public class LargeProjectReader implements Runnable{
        private UnitOfWork uow;
        private BigDecimal id;

        public LargeProjectReader(UnitOfWork uow, BigDecimal id){
            this.uow = uow;
            this.id = id;
        }

        public void run(){
            uow.readObject(ConcurrentLargeProject.class, (new ExpressionBuilder()).get("id").equal(id));
        }
    }
}
