/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat, Inc. and/or its affiliates, * and individual contributors as indicated by the @author tags. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2010, * @author JBoss, by Red Hat. */ package org.jboss.jbossts.qa.junit; import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean; import com.arjuna.ats.arjuna.common.recoveryPropertyManager; import com.arjuna.ats.arjuna.objectstore.StoreManager; import com.arjuna.ats.arjuna.recovery.RecoveryManager; import com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqJournalEnvironmentBean; import com.arjuna.ats.internal.jts.ORBManager; import com.arjuna.ats.jts.common.jtsPropertyManager; import com.arjuna.common.internal.util.propertyservice.BeanPopulator; import org.jboss.jbossts.qa.Utils.EmptyObjectStore; import org.jboss.jbossts.qa.Utils.OAInterface; import org.jboss.jbossts.qa.Utils.ORBInterface; import org.omg.CORBA.ORBPackage.InvalidName; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * Wrapper which alters crash recovery configuration of the tests, such that they use a * uniq objectstore dir and in-process cr per process, rather than the previous behaviour * of having a shared dir and single cr mgr process per test. * * @author Jonathan Halliday (jonathan.halliday@redhat.com), 2010-04 */ public class ExecutionWrapper { private static final File recMgrFile = new File(System.getProperty("java.io.tmpdir"), "recMgrLockFile"); public static void main(String[] args) throws Exception { String className = args[0]; String[] subArgs = new String[args.length-1]; System.arraycopy(args, 1, subArgs, 0, args.length-1); if(className.equals("com.arjuna.ats.arjuna.recovery.RecoveryManager")) { if(!recMgrFile.createNewFile()) { System.err.println("Recovery manager already running?"); System.exit(-1); } recMgrFile.deleteOnExit(); System.out.println("Ready"); Thread.sleep(Long.MAX_VALUE); } else if(className.equals("org.jboss.jbossts.qa.Utils.EmptyObjectStore")) { String objectStoreBaseDirBaseName = System.getProperty("ObjectStoreBaseDir"); // strip off the trailing '/emptyObjectStore' to get the test rather than task dir objectStoreBaseDirBaseName = objectStoreBaseDirBaseName.substring(0, objectStoreBaseDirBaseName.lastIndexOf(System.getProperty("file.separator"))); File directory = new File(objectStoreBaseDirBaseName); for(File candidateFile : directory.listFiles()) { if(candidateFile.isDirectory()) { System.err.println("emptying "+candidateFile.getCanonicalPath()); EmptyObjectStore.removeContents(candidateFile); } } System.out.println("Passed"); } else { int portOffset = Integer.valueOf(System.getProperty("portOffsetId"))*20; int recoveryOrbPortBase = jtsPropertyManager.getJTSEnvironmentBean().getRecoveryManagerPort(); int recoveryOrbPort = recoveryOrbPortBase+portOffset; jtsPropertyManager.getJTSEnvironmentBean().setRecoveryManagerPort(recoveryOrbPort); int recoveryManagerPortBase = recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryPort(); int recoveryManagerPort = recoveryManagerPortBase+portOffset; recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryPort(recoveryManagerPort); System.out.println("using ports "+recoveryOrbPort+" and "+recoveryManagerPort); String objectStoreBaseDirBaseName = System.getProperty("ObjectStoreBaseDir"); File directory = new File(objectStoreBaseDirBaseName); // full path incl taskName, see TestGroupBase File hornetqStoreDir = new File(directory, "HornetQStore"); //additionalCommandLineElements.add("-DHornetqJournalEnvironmentBean.storeDir="+hornetqStoreDir); BeanPopulator.getDefaultInstance(HornetqJournalEnvironmentBean.class) .setStoreDir(hornetqStoreDir.getCanonicalPath()); BeanPopulator.getDefaultInstance(ObjectStoreEnvironmentBean.class) .setObjectStoreType("com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqObjectStoreAdaptor"); /* [junit] Running org.jboss.jbossts.qa.junit.testgroup.TestGroup_txcore_recovery [junit] Tests run: 36, Failures: 24, Errors: 0, Time elapsed: 971.637 sec [junit] Test org.jboss.jbossts.qa.junit.testgroup.TestGroup_txcore_recovery FAILED */ File ostoreDir = new File(directory, "ObjectStore"); BeanPopulator.getDefaultInstance(ObjectStoreEnvironmentBean.class) .setObjectStoreDir(ostoreDir.getCanonicalPath()); BeanPopulator.getNamedInstance(ObjectStoreEnvironmentBean.class, "communicationStore") .setObjectStoreDir(ostoreDir.getCanonicalPath()); BeanPopulator.getNamedInstance(ObjectStoreEnvironmentBean.class, "stateStore") .setObjectStoreDir(ostoreDir.getCanonicalPath()); final Properties p = new Properties(); p.setProperty("OAPort", ""+recoveryOrbPort); // for persistent servers the JavaIdl orb requires you to explicitly define which port the // server will run on and to provide a unique id per server per machine: p.setProperty("com.sun.CORBA.POA.ORBPersistentServerPort", ""+recoveryOrbPort); p.setProperty("com.sun.CORBA.POA.ORBServerId", ""+recoveryOrbPort); initOrb(p, 10, recoveryOrbPort, args); RecoveryManager manager = RecoveryManager.manager(); Class clazz = Class.forName(className); Method mainMethod = clazz.getMethod("main", new Class[] {subArgs.getClass()}); before(); mainMethod.invoke(null, new Object[] {subArgs}); after(); manager.terminate(); System.exit(0); } } /** * initialize the orb and OA * @param orbProps * @param retryCount when a socket is closed it transitions into the TIME_WAIT state so it is not always immediately * available the next time the orb is initialized. To avoid this problem of rapid recycling of * ports between tests we allow retries - note that there is a real time delay before * each retry attempt to give the socket close protocol sufficient time to complete. * @param recoveryOrbPort * @param args */ private static void initOrb(Properties orbProps, int retryCount, int recoveryOrbPort, String... args) { int i = 0; while (!isAvailable("127.0.0.1", recoveryOrbPort)) { // probably recycling port allocation too fast. wait a bit and retry. if (i >= retryCount) { System.err.printf("orb port %d still in use after %d seconds%n", recoveryOrbPort, (retryCount * 10)); fingerCulprit(recoveryOrbPort); throw new RuntimeException("Still cannot initialize ORB/OA on port " + recoveryOrbPort); } i += 1; System.err.printf("orb port %d is in use%n", recoveryOrbPort); try { Thread.sleep(10000); // was 2000 * i CrashRecovery02_2_Test26 seems to take quite a while } catch (InterruptedException e1) { throw new RuntimeException(e1); } } try { ORBInterface.initORB(args, orbProps); OAInterface.initializeOA(); // don't use initOA because it swallows exception! } catch (InvalidName invalidName) { invalidName.printStackTrace(); throw new RuntimeException("Cannot initialize ORB/OA on port - reason: " + invalidName.getMessage()); } } private static boolean isAvailable(String host, int port) { try { (new Socket(host, port)).close(); // Successful connection means the port is taken. return false; } catch (IOException e) { // Could not connect so it must be available return true; } } /** * Find out which process has a particular port open and print its pid, its command line and, * if its a JVM its stack traces. * * Only works on platforms that have the lsof command, the proc fs and the jstack command * * @param port port number to debug * @throws Exception */ public static void fingerCulprit(int port) { // not supported on windows platforms if (System.getProperty("os.name", "Linux").contains("indows")) return; try { // who has tcp port 4731 open String pid = printLines(startProcess("lsof", "-t", "-i", "tcp:" + port), 1); System.out.printf("pid: %s%n", pid); // find its cmd line StringBuilder sb = new StringBuilder("/proc/").append(pid).append("/cmdline"); String cmdline = printLines(startProcess("cat", sb.toString()), 1); System.out.printf("cmdline: %s%n", cmdline); // if its a JVM get its stack traces System.out.printf("jstack for JVM %s%n", pid); printLines(startProcess("jstack", pid), -1); System.out.printf("end of jstack for JVM %s%n", pid); } catch (Exception e) { System.out.printf("Exception %s whilst checking who has port %d open%n", e.getMessage(), port); } } private static String printLines(Process p, int howMany) { BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream())); String s = null; try { if (howMany == -1) { while ((s = stdInput.readLine()) != null) System.out.println(s); } else { for (int i = 0; i < howMany; i++) s = stdInput.readLine(); } } catch (IOException e) { e.printStackTrace(); } finally { try { stdInput.close(); } catch (IOException e) { } p.destroy(); } return s; } private static Process startProcess(String ... args) throws Exception { List<String> pArgs = new ArrayList<String>(); for (String arg : args) pArgs.add(arg); Process p = new ProcessBuilder(args).start(); p.waitFor(); return p; } private static void before() { //System.out.println("before"); } private static void after() { //System.out.println("after"); StoreManager.shutdown(); } }