/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, 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) 2009, * @author JBoss Inc. */ package com.hp.mwtests.ts.arjuna.recovery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import org.jboss.byteman.contrib.bmunit.BMScript; import org.jboss.byteman.contrib.bmunit.BMUnitRunner; import org.junit.Test; import org.junit.Before; import org.junit.runner.RunWith; import com.arjuna.ats.arjuna.common.recoveryPropertyManager; import com.arjuna.ats.arjuna.recovery.RecoveryManager; /** * test to ensure that the recovery manager cleans up all its threads when terminated */ @RunWith(BMUnitRunner.class) @BMScript("recovery") public class RecoveryManagerStartStopTest { @Before public void enableSocketBasedRecovery() { recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryListener(true); } @Test public void testStartStop() throws Exception { recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryPort(4712); // check how many threads there are running ThreadGroup thg = Thread.currentThread().getThreadGroup(); int activeCount = thg.activeCount(); dumpThreadGroup(thg, "Before recovery manager create"); RecoveryManager.delayRecoveryManagerThread(); RecoveryManager manager = RecoveryManager.manager(RecoveryManager.INDIRECT_MANAGEMENT); dumpThreadGroup(thg, "Before recovery manager initialize"); manager.initialize(); dumpThreadGroup(thg, "Before recovery manager start periodic recovery thread"); manager.startRecoveryManagerThread(); dumpThreadGroup(thg, "Before recovery manager client create"); // Thread.sleep(1000); // we need to open several connections to the recovery manager listener service and then // ensure they get closed down addRecoveryClient(); addRecoveryClient(); dumpThreadGroup(thg, "Before recovery manager terminate"); manager.terminate(); // ensure the client threads get killed ensureRecoveryClientsTerminated(); dumpThreadGroup(thg, "After recovery manager terminate"); int newActiveCount = thg.activeCount(); assertEquals(activeCount, newActiveCount); } private void ensureRecoveryClientsTerminated() { // check that any threads added to talk to the recovery listener get their sockets closed for (RecoveryManagerStartStopTestThread client : clients) { try { client.join(); } catch (InterruptedException e) { // do nothing } assertFalse(client.failed()); } } private void addRecoveryClient() { // open a connection to the recovery listener service in a new thread and ensure that the // thread is terminated by having its socket closed. RecoveryManagerStartStopTestThread client = new RecoveryManagerStartStopTestThread(); clients.add(client); client.start(); client.ensureStarted(); } private void dumpThreadGroup(ThreadGroup thg, String header) { int activeCount = thg.activeCount(); Thread[] threads = new Thread[activeCount]; int reported = thg.enumerate(threads); System.out.println(header); System.out.println("Thread count == " + activeCount); for (int i = 0; i < reported; i++) { System.out.println("Thread[" + i + "] == " + threads[i].getName()); } System.out.flush(); } private List<RecoveryManagerStartStopTestThread> clients = new ArrayList<RecoveryManagerStartStopTestThread>(); private static class RecoveryManagerStartStopTestThread extends Thread { private boolean failed = true; private boolean started = false; private boolean stopped = false; public RecoveryManagerStartStopTestThread() { super("Recovery Listener Client"); } public boolean failed() { return failed; } public void run() { BufferedReader fromServer = null; Socket connectorSocket = null; // get a socket connected to the listener // don't write anything just sit on a read until the socket is closed try { String host; int port; host = InetAddress.getLocalHost().getHostName(); port = recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryPort(); System.out.println("client attempting to connect to host " + host + " port " + port); System.out.flush(); try { connectorSocket = new Socket(host, port); } catch (final Exception ex) { // in case local host name bind fails (e.g., on Mac OS) System.out.println("caught exception " + ex.getMessage() + " trying IPv4 loopback connection instead"); try { connectorSocket = new Socket("localhost", port); } catch (IOException e) { System.out.println("caught exception " + ex.getMessage() + " trying IPv6 loopback connection instead"); connectorSocket = new Socket("::1", port); } } System.out.println("connected!!!"); System.out.flush(); fromServer = new BufferedReader(new InputStreamReader(connectorSocket.getInputStream())); } catch (Exception e) { System.out.println("Failed to set up listener input stream!!!"); e.printStackTrace(); System.out.flush(); return; } finally { notifyStarted(); } try { String result = fromServer.readLine(); if (result == null || result.equals("")) { System.out.println("Recovery Listener Client got empty string from readline() as expected"); System.out.flush(); failed = false; } } catch (SocketException e) { if (!connectorSocket.isClosed()) { try { connectorSocket.close(); } catch (IOException e1) { // ignore } } System.out.println("Recovery Listener Client got socket exception as expected"); e.printStackTrace(); System.out.flush(); failed = false; } catch (IOException e) { if (!connectorSocket.isClosed()) { System.out.println("Recovery Listener Client got non socket IO exception without socket being closed"); try { connectorSocket.close(); } catch (IOException e1) { // ignore } } else { System.out.println("Recovery Listener Client got IO exception under readline() as expected"); failed = false; } e.printStackTrace(); System.out.flush(); } catch (Exception e) { System.out.println("Recovery Listener Client got non IO exception"); e.printStackTrace(); System.out.flush(); } } public synchronized void notifyStarted() { started = true; notify(); } public synchronized void ensureStarted() { while (!started) { try { wait(); } catch (InterruptedException e) { // ignore } } } } }