/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.deadlock.test; import javax.naming.Context; import javax.naming.InitialContext; import javax.ejb.DuplicateKeyException; import javax.ejb.ObjectNotFoundException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.Random; import junit.framework.Test; import org.jboss.test.deadlock.interfaces.BeanOrder; import org.jboss.test.deadlock.interfaces.EnterpriseEntityHome; import org.jboss.test.deadlock.interfaces.EnterpriseEntity; import org.jboss.test.deadlock.interfaces.StatelessSessionHome; import org.jboss.test.deadlock.interfaces.StatelessSession; import org.jboss.test.JBossTestCase; import org.jboss.ejb.plugins.TxInterceptorCMT; /** * Sample client for the jboss container. * * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Id: BeanStressTestCase.java 81036 2008-11-14 13:36:39Z dimitris@jboss.org $ */ public class BeanStressTestCase extends JBossTestCase { org.jboss.logging.Logger log = getLog(); static boolean deployed = false; static int test = 0; static Date startDate = new Date(); protected final String namingFactory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); protected final String providerURL = System.getProperty(Context.PROVIDER_URL); public BeanStressTestCase(String name) { super(name); } boolean failed = false; private StatelessSession getSession() throws Exception { StatelessSessionHome home = (StatelessSessionHome) new InitialContext().lookup("nextgen.StatelessSession"); return home.create(); } public class RunTest implements Runnable { public String test; public RunTest(String test) { this.test = test; } public void run() { if (test.equals("AB")) runAB(); else runBA(); } private void runAB() { log.debug("running AB"); try { getSession().callAB(); } catch (Exception ex) { failed = true; } } private void runBA() { log.debug("running BA"); try { getSession().callBA(); } catch (Exception ex) { failed = true; } } } public void testDeadLock() throws Exception { EnterpriseEntityHome home = (EnterpriseEntityHome) new InitialContext().lookup("nextgenEnterpriseEntity"); try { EnterpriseEntity A = home.findByPrimaryKey("A"); } catch (ObjectNotFoundException ex) { home.create("A"); } try { EnterpriseEntity B = home.findByPrimaryKey("B"); } catch (ObjectNotFoundException ex) { home.create("B"); } Thread one = new Thread(new RunTest("AB")); Thread two = new Thread(new RunTest("BA")); one.start(); two.start(); one.join(); two.join(); if (failed) { fail("testing of deadlock AB BA scenario failed"); } } Random random = new Random(); int target; int iterations; Object lock = new Object(); int completed = 0; Exception unexpected; public class OrderTest implements Runnable { BeanOrder beanOrder; EnterpriseEntityHome home; String toStringCached; public OrderTest(EnterpriseEntityHome home, int beanCount, int depth) { // Create the list of beans ArrayList list = new ArrayList(); for (int i = 0; i < depth; i++) list.add(new Integer(i % beanCount).toString()); // Shuffle them Collections.shuffle(list, random); beanOrder = new BeanOrder((String[]) list.toArray(new String[beanCount])); this.home = home; } public void run() { try { EnterpriseEntity bean = home.findByPrimaryKey(beanOrder.order[0]); home = null; for (int i = 0; i < iterations; i++) { log.debug("Before: iter=" + i + " " + this); bean.callAnotherBean(beanOrder); log.debug("After : iter=" + i + " " + this); } } catch (Exception e) { if (TxInterceptorCMT.isADE(e) == null) { log.debug("Saw exception for " + this, e); unexpected = e; } } } public String toString() { if (toStringCached != null) return toStringCached; StringBuffer buffer = new StringBuffer(); buffer.append(" hash=").append(hashCode()); buffer.append(" order=").append(Arrays.asList(beanOrder.order)); toStringCached = buffer.toString(); return toStringCached; } } public class TestThread extends Thread { OrderTest test; public TestThread(OrderTest test) { super(test); this.test = test; } public void run() { super.run(); synchronized (lock) { completed++; log.debug("Completed " + completed + " of " + target); lock.notifyAll(); } } } public void waitForCompletion() throws Exception { log.debug("Waiting for completion"); synchronized (lock) { while (completed < target) { lock.wait(); } } if (unexpected != null) { log.error("Unexpected exception", unexpected); fail("Unexpected exception"); } } /** * Creates a number of threads to invoke on the * session beans at random to produce deadlocks. * The test will timeout if a deadlock detection is missed. */ public void testAllCompleteOrFail() throws Exception { doAllCompleteOrFail("nextgenEnterpriseEntity" ,2); } /** * Creates a number of threads to invoke on the * session beans at random to produce deadlocks. * The test will timeout if a deadlock detection is missed. */ public void testAllCompleteOrFailReentrant() throws Exception { doAllCompleteOrFail("nextgenEnterpriseEntityReentrant", 4); } /** * Creates a number of threads to invoke on the * session beans at random to produce deadlocks. * The test will timeout if a deadlock detection is missed. */ public void testAllCompleteOrFailNotSupported() throws Exception { doAllCompleteOrFail("nextgenEnterpriseEntityNotSupported", 2); } /** * Creates a number of threads to invoke on the * session beans at random to produce deadlocks. * The test will timeout if a deadlock detection is missed. */ public void testAllCompleteOrFailNotSupportedReentrant() throws Exception { doAllCompleteOrFail("nextgenEnterpriseEntityNotSupportedReentrant", 4); } /** * Creates a number of threads to invoke on the * session beans at random to produce deadlocks. * The test will timeout if a deadlock detection is missed. */ public void doAllCompleteOrFail(String jndiName, int depth) throws Exception { log.debug("========= Starting " + getName()); // Non-standard: We want a lot of threads and a small number of beans // for maximum contention // target = getThreadCount(); // int beanCount = getBeanCount(); target = 40; int beanCount = 2; completed = 0; unexpected = null; // Create some beans EnterpriseEntityHome home = (EnterpriseEntityHome) new InitialContext().lookup(jndiName); for (int i = 0; i < beanCount; i++) { try { home.create(new Integer(i).toString()); } catch (DuplicateKeyException weDontCare) { } } // Create some threads TestThread[] threads = new TestThread[target]; for (int i = 0; i < target; i++) threads[i] = new TestThread(new OrderTest(home, beanCount, depth)); // Start the threads for (int i = 0; i < target; i++) { log.debug("Starting " + threads[i].test); threads[i].start(); } waitForCompletion(); log.debug("========= Completed " + getName()); } public class CMRTest implements Runnable { StatelessSession session; String jndiName; String start; public CMRTest(StatelessSession session, String jndiName, String start) { this.session = session; this.jndiName = jndiName; this.start = start; } public void run() { try { session.cmrTest(jndiName, start); } catch (Exception e) { if (TxInterceptorCMT.isADE(e) == null) { log.debug("Saw exception for " + this, e); unexpected = e; } } } public String toString() { return hashCode() + " " + start; } } public class CMRTestThread extends Thread { CMRTest test; public CMRTestThread(CMRTest test) { super(test); this.test = test; } public void run() { super.run(); synchronized (lock) { completed++; log.debug("Completed " + completed + " of " + target); lock.notifyAll(); } } } /** * Creates a number of threads to CMR relationships. * The test will timeout if a deadlock detection is missed. */ public void testAllCompleteOrFailCMR() throws Exception { doAllCompleteOrFailCMR("local/nextgenEnterpriseEntity"); } /** * Creates a number of threads to CMR relationships. * The test will timeout if a deadlock detection is missed. */ public void doAllCompleteOrFailCMR(String jndiName) throws Exception { log.debug("========= Starting " + getName()); // Non-standard: We want a lot of threads and a small number of beans // for maximum contention // target = getThreadCount(); // int beanCount = getBeanCount(); target = 40; completed = 0; unexpected = null; // Create some beans StatelessSessionHome home = (StatelessSessionHome) new InitialContext().lookup("nextgen.StatelessSession"); StatelessSession session = home.create(); session.createCMRTestData(jndiName); // Create some threads CMRTestThread[] threads = new CMRTestThread[target]; for (int i = 0; i < target; i++) threads[i] = new CMRTestThread(new CMRTest(session, jndiName, i % 2 == 0 ? "First" : "Second")); // Start the threads for (int i = 0; i < target; i++) { log.debug("Starting " + threads[i].test); threads[i].start(); } waitForCompletion(); log.debug("========= Completed " + getName()); } /* public void testRequiresNewDeadlock() throws Exception { EnterpriseEntityHome home = (EnterpriseEntityHome)new InitialContext().lookup("nextgenEnterpriseEntity"); try { EnterpriseEntity C = home.findByPrimaryKey("C"); } catch (ObjectNotFoundException ex) { home.create("C"); } boolean deadlockExceptionThrown = false; try { getSession().requiresNewTest(true); } catch (RemoteException ex) { if (ex.detail instanceof ApplicationDeadlockException) { deadlockExceptionThrown = true; } } assertTrue("ApplicationDeadlockException was not thrown", deadlockExceptionThrown); } */ public void testCleanup() throws Exception { // Restart the db pool super.restartDBPool(); } public static Test suite() throws Exception { return getDeploySetup(BeanStressTestCase.class, "deadlock.jar"); } }