| /* |
| * 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.identitymaps; |
| |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.sessions.*; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.testing.tests.clientserver.Server; |
| import org.eclipse.persistence.testing.framework.*; |
| import org.eclipse.persistence.testing.models.bigbad.*; |
| |
| /** |
| * This test is set up to have one thread build a BigBadObject instance, while multiple other threads |
| * attempt to access the same instance from the cache. The test fails if any of the threads access |
| * an incomplete instance of the BigBadObject object - where it has been only partially built. Only |
| * the "number02" attribute is checked, as all mapping weights are changed so this mapping is |
| * built last. Built to test bug 4772232 |
| */ |
| public class ConcurrentReadBigBadObjectTest extends AutoVerifyTestCase { |
| public ConcurrentReadBigBadObjectTest() { |
| } |
| |
| BigBadObject referenceObject; |
| protected int numOftries = 300; |
| protected int numOfThreads = 10; |
| protected boolean failed; |
| protected Server server; |
| protected int OrigDirectMapWeight; |
| |
| |
| @Override |
| public void setup() throws Exception { |
| |
| DatabaseLogin login; |
| login = (DatabaseLogin)getSession().getLogin().clone(); |
| server = new Server(login); |
| server.serverSession.setLogLevel(getSession().getLogLevel()); |
| server.serverSession.setLog(getSession().getLog()); |
| |
| |
| server.copyDescriptors(getSession()); |
| ClassDescriptor d = (server.serverSession).getClassDescriptor(BigBadObject.class); |
| DatabaseMapping m; |
| |
| Vector<DatabaseMapping> v = d.getMappings(); |
| int mappings = v.size(); |
| int i = 0; |
| while (i < mappings) { |
| m = v.get(i); |
| m.setWeight(Integer.MAX_VALUE - 1); |
| i++; |
| } |
| |
| m = d.getMappingForAttributeName("number02"); |
| m.setWeight(Integer.MAX_VALUE); |
| |
| server.login(); |
| server.serverSession.setLogLevel(getSession().getLogLevel()); |
| server.serverSession.setLog(getSession().getLog()); |
| } |
| |
| @Override |
| public void reset() throws Exception { |
| server.logout(); |
| } |
| |
| @Override |
| public void test() { |
| failed = false; |
| |
| referenceObject = (BigBadObject)this.getSession().readObject(BigBadObject.class); |
| Reader[] threadList = new Reader[numOfThreads]; |
| |
| for (int i = 0; i < numOfThreads; ) { |
| threadList[i] = new Reader(referenceObject, server.serverSession.acquireClientSession(), ++i); |
| } |
| |
| for (int i = 0; i < numOfThreads; i++) { |
| threadList[i].start(); |
| } |
| |
| try { |
| for (int i = 0; i < numOfThreads; i++) { |
| threadList[i].join(); |
| } |
| } catch (InterruptedException ex) { |
| } |
| int count = 0; |
| while (!failed && count < numOfThreads) { |
| if (threadList[count].exception != null) { |
| throw threadList[count].exception; |
| } |
| failed = threadList[count].hadError(); |
| count++; |
| } |
| if (failed) { |
| throw new TestErrorException("Test failed, getFromIdentityMap returned an object before it was finished being built"); |
| } |
| |
| } |
| |
| |
| /** |
| * Threads to read/access the cache numOftries times. Only thread 1 will initialize the cache and |
| * read the BigBadObject object instance. |
| */ |
| private class Reader extends Thread { |
| protected BigBadObject referenceObject, readObject; |
| protected Session session; |
| public int thread; |
| public int counter; |
| public RuntimeException exception; |
| |
| protected boolean experienceError = false; |
| |
| public Reader(BigBadObject object, Session session, int thread) { |
| this.referenceObject = object; |
| this.session = session; |
| this.thread = thread; |
| counter = 0; |
| } |
| |
| @Override |
| public void run() { |
| try { |
| counter = 0; |
| while (!experienceError && counter < numOftries) { |
| if (thread == 1) { |
| session.getIdentityMapAccessor().initializeIdentityMap(BigBadObject.class); |
| readObject = (BigBadObject)session.readObject(referenceObject); |
| } else { |
| readObject = (BigBadObject)session.getIdentityMapAccessor().getFromIdentityMap(referenceObject); |
| } |
| if ((readObject != null) && |
| ((readObject.number02 == null) || (!readObject.number02.equals(referenceObject.number02)))) { |
| this.experienceError = true; |
| } |
| counter++; |
| } |
| } catch (RuntimeException ex) { |
| this.experienceError = true; |
| this.exception = ex; |
| } |
| } |
| |
| public boolean hadError() { |
| return experienceError; |
| } |
| } |
| } |