/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.runtime.concurrency;
import org.objectweb.perseus.concurrency.lib.Semaphore;
import org.objectweb.speedo.SpeedoTestHelper;
import org.objectweb.speedo.api.SpeedoProperties;
import org.objectweb.speedo.pobjects.basic.BasicA;
import org.objectweb.util.monolog.api.BasicLevel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import junit.framework.Assert;
/**
*
* @author S.Chassande-Barrioz
*/
public class TestSeveralPMUsers extends SpeedoTestHelper {
public TestSeveralPMUsers(String s) {
super(s);
}
protected String getLoggerName() {
return LOG_NAME + ".rt.concurrency.TestSeveralPMUsers";
}
public Properties getPMFProperties() {
Properties p = super.getPMFProperties();
p.setProperty(SpeedoProperties.JDO_OPTION_MULTITREADED, "true");
p.setProperty(SpeedoProperties.MAPPING_STRUCTURE, SpeedoProperties.MAPPING_STRUCTURE_DD);
return p;
}
public void test2thread1PMNoTx() {
final PersistenceManager pm = pmf.getPersistenceManager();
BasicA ba = new BasicA();
ba.writeF1("test2thread1PMNoTx.thread1");
ba.writeF2(2);
pm.makePersistent(ba);
final Object oid = pm.getObjectId(ba);
Thread t = new Thread(
new Runnable() {
public void run() {
try {
BasicA _ba = ((BasicA) pm.getObjectById(oid, false));
_ba.readF1_F2();
_ba.writeF1("test2thread1PMNoTx.thread2");
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
}
Assert.assertTrue("The thread2 is not finished", !t.isAlive());
pm.currentTransaction().begin();
pm.deletePersistent(ba);
pm.currentTransaction().commit();
pm.close();
}
public void test2thread1PMTx() {
final PersistenceManager pm = pmf.getPersistenceManager();
BasicA ba = new BasicA();
ba.writeF1("test2thread1PM.thread1");
ba.writeF2(1);
pm.currentTransaction().begin();
pm.makePersistent(ba);
final Object oid = pm.getObjectId(ba);
Thread t = new Thread(
new Runnable() {
public void run() {
try {
BasicA _ba = ((BasicA) pm.getObjectById(oid, false));
_ba.readF1_F2();
_ba.writeF1("test2thread1PM.thread2");
_ba.writeF2(2);
pm.currentTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
}
Assert.assertTrue("The thread2 is not finished", !t.isAlive());
pm.currentTransaction().begin();
Assert.assertEquals("Bad f2 value", 2, ba.readF2());
ba.writeF2(3);
pm.currentTransaction().commit();
pm.currentTransaction().begin();
pm.deletePersistent(ba);
pm.currentTransaction().commit();
pm.close();
}
public void testManyThreadConcurrentData20th70ob30m0q0i() {
testManyThreadConcurrentData(20, 70, 30, 5, 5);
}
public void testManyThreadConcurrentData70th70ob0m5q5i() {
testManyThreadConcurrentData(70, 70, 0, 5, 5);
}
public void testManyThreadConcurrentData100th100ob10m5q5i() {
testManyThreadConcurrentData(100, 100, 10, 5, 5);
}
public void testManyThreadConcurrentData(
final int NB_THREAD,
final int NB_OBJECT,
final int NB_MODIF,
final int NB_QUERY,
final int INTERVAL) {
logger.log(BasicLevel.INFO, "testManyThreadConcurrentData: "
+ " \n\tNB_THREAD=" + NB_THREAD
+ " \n\tNB_OBJECT=" + NB_OBJECT
+ " \n\tNB_MODIF=" + NB_MODIF
+ " \n\tNB_QUERY=" + NB_QUERY
+ " \n\tINTERVAL=" + INTERVAL
);
assertTrue("Bad configuration, the number of object must be greater or equal than the number of thread", NB_OBJECT >= NB_THREAD);
final PersistenceManager pm = pmf.getPersistenceManager();
pm.currentTransaction().begin();
final Object[] oids = new Object[NB_OBJECT];
final Semaphore[] s = new Semaphore[NB_OBJECT];
for(int i=0; i<NB_OBJECT; i++) {
BasicA ba = new BasicA();
ba.writeF1("testManyThreadConcurrentData_" + i);
ba.writeF2(i);
pm.makePersistent(ba);
oids[i] = pm.getObjectId(ba);
s[i] = new Semaphore();
}
pm.currentTransaction().commit();
//clean the cache
pmf.getDataStoreCache().evictAll();
Thread[] threads = new Thread[NB_THREAD];
ThreadTestManyThreadConcurrentData[] runners = new ThreadTestManyThreadConcurrentData[NB_THREAD];
for(int i=0; i<NB_THREAD; i++) {
final int threadId = i;
threads[i] = new Thread(runners[i]);
runners[i] = new ThreadTestManyThreadConcurrentData(
threadId, NB_OBJECT, NB_MODIF, NB_QUERY, INTERVAL,
oids, s, pm, this);
}
pm.currentTransaction().begin();
for(int i=0; i<NB_THREAD; i++) {
threads[i].start();
}
for(int i=0; i<NB_THREAD; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
}
}
int error = 0;
for(int i=0; i<NB_THREAD; i++) {
if (runners[i].throwable!= null) {
logger.log(BasicLevel.ERROR, "Error in the thread " + i
+ ": ", runners[i].throwable);
error++;
}
}
pm.currentTransaction().commit();
pm.currentTransaction().begin();
pm.deletePersistentAll(pm.getObjectsById(oids));
pm.currentTransaction().commit();
pm.close();
if (error > 0) {
fail(error + " error(s) occur.");
}
}
}
class ThreadTestManyThreadConcurrentData implements Runnable {
private final int threadId;
private final int NB_OBJECT;
private final int NB_MODIF;
private final int NB_QUERY;
private final int INTERVAL;
private final Object[] oids;
private final PersistenceManager pm;
private final Semaphore[] s;
private final SpeedoTestHelper st;
public Throwable throwable = null;
public ThreadTestManyThreadConcurrentData(final int threadId,
final int NB_OBJECT,
final int NB_MODIF,
final int NB_QUERY,
final int INTERVAL,
final Object[] oids,
final Semaphore[] s,
final PersistenceManager pm,
final SpeedoTestHelper st) {
this.threadId = threadId;
this.NB_OBJECT = NB_OBJECT;
this.NB_MODIF = NB_MODIF;
this.NB_QUERY = NB_QUERY;
this.INTERVAL = INTERVAL;
this.oids = oids;
this.s = s;
this.pm = pm;
this.st = st;
}
public void run() {
try {
execute();
} catch (Throwable t) {
throwable = t;
}
}
private void execute() {
//test concurrent loading in the same transaction
BasicA ba = ((BasicA) pm.getObjectById(oids[NB_OBJECT-1], false));
ba.readF1_F2();
ba = ((BasicA) pm.getObjectById(oids[threadId], false));
ba.writeF1(ba.readF1() + "modifiedBy" + threadId);
for(int j=0; j<NB_MODIF; j++) {
// Choose an object: alternativly on each side
// of the threadId
int id = j;
if (j % 2 == 0) {
id = Math.min(NB_OBJECT-1, threadId + j);
} else {
id = Math.max(0, threadId - j);
}
ba = ((BasicA) pm.getObjectById(oids[id], false));
s[id].P();
try {
ba.writeF1(ba.readF1() + "modifiedBy" + threadId);
} finally {
s[id].V();
}
}
for(int j=0; j<NB_QUERY; j++) {
final int min = Math.max(0, threadId - (INTERVAL/2));
final int max = Math.min(NB_OBJECT-1, threadId + (INTERVAL/2));
Query q = pm.newQuery(BasicA.class);
q.declareParameters("int pmin, int pmax");
q.setFilter("(f2 >= pmin) && (f2 <= pmax)");
Collection c = (Collection) q.execute(
new Integer(min), new Integer(max));
Collection expected = new ArrayList();
for(int id=min; id<=max; id++) {
expected.add(new Integer(id));
}
st.getLogger().log(BasicLevel.DEBUG, "Thread " + threadId + " expect: " + expected);
Collection found = new ArrayList();
for (Iterator iter = c.iterator(); iter.hasNext();) {
ba = (BasicA) iter.next();
int f2 = ba.readF2();
found.add(new Integer(f2));
}
q.closeAll();
st.getLogger().log(BasicLevel.DEBUG, "Thread " + threadId + " found: " + found);
st.assertSameCollection("Bad query result"
+ ", threadId=" + threadId
+ ", min=" + min
+ ", max=" + max,
expected, found);
}
}
}