001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tctest;
006:
007: import org.apache.commons.io.FileUtils;
008:
009: import com.tc.object.config.ConfigVisitor;
010: import com.tc.object.config.DSOClientConfigHelper;
011: import com.tc.objectserver.control.ExtraL1ProcessControl;
012: import com.tc.simulator.app.ApplicationConfig;
013: import com.tc.simulator.listener.ListenerProvider;
014: import com.tc.util.Assert;
015: import com.tctest.runner.AbstractTransparentApp;
016:
017: import java.io.File;
018: import java.net.URL;
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Set;
024:
025: import javax.management.MBeanServer;
026: import javax.management.MBeanServerFactory;
027: import javax.management.MBeanServerNotification;
028: import javax.management.Notification;
029: import javax.management.NotificationFilter;
030: import javax.management.NotificationListener;
031: import javax.management.ObjectName;
032:
033: public class ClusterMembershipEventJMXTestApp extends
034: AbstractTransparentApp implements NotificationListener {
035:
036: public static final String CONFIG_FILE = "config-file";
037: public static final String PORT_NUMBER = "port-number";
038: public static final String HOST_NAME = "host-name";
039:
040: private final ApplicationConfig config;
041:
042: private MBeanServer server = null;
043: private ObjectName clusterBean = null;
044: private List clusterBeanBag = new ArrayList();
045: private Map eventsCount = new HashMap();
046:
047: public ClusterMembershipEventJMXTestApp(String appId,
048: ApplicationConfig config, ListenerProvider listenerProvider) {
049: super (appId, config, listenerProvider);
050: this .config = config;
051:
052: try {
053: registerMBeanListener();
054: } catch (Exception ex) {
055: ex.printStackTrace();
056: }
057: }
058:
059: public static void visitL1DSOConfig(ConfigVisitor visitor,
060: DSOClientConfigHelper config) {
061: String testClass = ClusterMembershipEventJMXTestApp.class
062: .getName();
063: String methodExpression = "* " + testClass + "*.*(..)";
064: config.addWriteAutolock(methodExpression);
065: config.addIncludePattern(testClass + "$*");
066: }
067:
068: public void run() {
069: try {
070: runTest();
071: } catch (Throwable t) {
072: notifyError(t);
073: }
074: }
075:
076: private void runTest() throws Throwable {
077: config.getServerControl().crash();
078: while (config.getServerControl().isRunning()) {
079: Thread.sleep(5000);
080: }
081: // this sleep should be longer than l1-reconnect timeout
082: Thread.sleep(30 * 1000);
083: config.getServerControl().start();
084: while (!config.getServerControl().isRunning()) {
085: Thread.sleep(5000);
086: }
087: echo("Server restarted successfully.");
088: spawnNewClient();
089: synchronized (eventsCount) {
090: while (eventsCount.size() < 4) {
091: eventsCount.wait(3 * 60 * 1000);
092: }
093: Assert.assertTrue(((Integer) eventsCount
094: .get("com.tc.cluster.event.nodeDisconnected"))
095: .intValue() >= 1);
096: Assert.assertTrue(((Integer) eventsCount
097: .get("com.tc.cluster.event.nodeConnected"))
098: .intValue() >= 1);
099: Assert.assertTrue(((Integer) eventsCount
100: .get("com.tc.cluster.event.thisNodeDisconnected"))
101: .intValue() >= 1);
102: Assert.assertTrue(((Integer) eventsCount
103: .get("com.tc.cluster.event.thisNodeConnected"))
104: .intValue() >= 1);
105: }
106: }
107:
108: private void registerMBeanListener() throws Exception {
109: List servers = MBeanServerFactory.findMBeanServer(null);
110: if (servers.size() == 0) {
111: throw new RuntimeException("No bean server found!");
112: }
113: echo("Servers found: " + servers.size());
114: Assert.assertEquals(1, servers.size());
115: server = (MBeanServer) servers.get(0);
116:
117: // our *star* bean for memebership events
118: clusterBean = new ObjectName(
119: "org.terracotta:type=Terracotta Cluster,name=Terracotta Cluster Bean");
120:
121: // The MBeanServerDelegate emits notifications about
122: // registration/unregistration of MBeans
123: ObjectName delegateName = ObjectName
124: .getInstance("JMImplementation:type=MBeanServerDelegate");
125:
126: // listener for newly registered MBeans
127: NotificationListener listener = new NotificationListener() {
128: public void handleNotification(Notification notification,
129: Object handback) {
130: synchronized (clusterBeanBag) {
131: clusterBeanBag.add(handback);
132: clusterBeanBag.notifyAll();
133: }
134: }
135: };
136:
137: // filter to let only clusterBean passed through
138: NotificationFilter filter = new NotificationFilter() {
139: public boolean isNotificationEnabled(
140: Notification notification) {
141: if (notification.getType().equals(
142: "JMX.mbean.registered")
143: && ((MBeanServerNotification) notification)
144: .getMBeanName().equals(clusterBean))
145: return true;
146: return false;
147: }
148: };
149:
150: // add our listener for clusterBean's registration
151: server.addNotificationListener(delegateName, listener, filter,
152: clusterBean);
153:
154: // because of race condition, clusterBean might already have registered
155: // before we registered the listener
156: Set allObjectNames = server.queryNames(null, null);
157:
158: if (!allObjectNames.contains(clusterBean)) {
159: synchronized (clusterBeanBag) {
160: while (clusterBeanBag.isEmpty()) {
161: clusterBeanBag.wait();
162: }
163: }
164: }
165:
166: // clusterBean is now registered, no need to listen for it
167: server.removeNotificationListener(delegateName, listener);
168:
169: // now that we have the clusterBean, add listener for membership events
170: server.addNotificationListener(clusterBean, this , null,
171: clusterBean);
172: }
173:
174: public void handleNotification(Notification notification,
175: Object handback) {
176: synchronized (eventsCount) {
177: String msgType = notification.getType();
178: if (eventsCount.containsKey(msgType)) {
179: Integer count = (Integer) eventsCount.get(msgType);
180: eventsCount.put(msgType, new Integer(
181: count.intValue() + 1));
182: } else {
183: eventsCount.put(msgType, new Integer(1));
184: }
185: echo("type=" + notification.getType() + ", message="
186: + notification.getMessage());
187: eventsCount.notifyAll();
188: }
189: }
190:
191: private static void echo(String msg) {
192: System.out.println(msg);
193: }
194:
195: public static class L1Client {
196: public static void main(String args[]) {
197: // nothing to do
198: }
199: }
200:
201: private ExtraL1ProcessControl spawnNewClient() throws Exception {
202: final String hostName = config.getAttribute(HOST_NAME);
203: final int port = Integer.parseInt(config
204: .getAttribute(PORT_NUMBER));
205: final File configFile = new File(config
206: .getAttribute(CONFIG_FILE));
207: File workingDir = new File(configFile.getParentFile(),
208: "client-0");
209: FileUtils.forceMkdir(workingDir);
210:
211: List jvmArgs = new ArrayList();
212: addTestTcPropertiesFile(jvmArgs);
213: ExtraL1ProcessControl client = new ExtraL1ProcessControl(
214: hostName, port, L1Client.class, configFile
215: .getAbsolutePath(), new String[0], workingDir,
216: jvmArgs);
217: client.start();
218: client.mergeSTDERR();
219: client.mergeSTDOUT();
220: client.waitFor();
221: System.err.println("\n### Started New Client");
222: return client;
223: }
224:
225: private void addTestTcPropertiesFile(List jvmArgs) {
226: URL url = getClass().getResource(
227: "/com/tc/properties/tests.properties");
228: if (url == null) {
229: return;
230: }
231: String pathToTestTcProperties = url.getPath();
232: if (pathToTestTcProperties == null
233: || pathToTestTcProperties.equals("")) {
234: return;
235: }
236: System.err.println("\n##### -Dcom.tc.properties="
237: + pathToTestTcProperties);
238: jvmArgs.add("-Dcom.tc.properties=" + pathToTestTcProperties);
239: }
240:
241: }
|