001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tctest.runner;
006:
007: import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
008:
009: import com.tc.config.schema.setup.L1TVSConfigurationSetupManager;
010: import com.tc.config.schema.setup.L2TVSConfigurationSetupManager;
011: import com.tc.config.schema.setup.TestTVSConfigurationSetupManagerFactory;
012: import com.tc.lang.TCThreadGroup;
013: import com.tc.lang.ThrowableHandler;
014: import com.tc.logging.TCLogging;
015: import com.tc.object.bytecode.hook.impl.PreparedComponentsFromL2Connection;
016: import com.tc.object.config.ConfigVisitor;
017: import com.tc.object.config.DSOClientConfigHelper;
018: import com.tc.server.TCServer;
019: import com.tc.simulator.app.ApplicationBuilder;
020: import com.tc.simulator.app.ApplicationConfig;
021: import com.tc.simulator.app.DSOApplicationBuilder;
022: import com.tc.simulator.app.ErrorContext;
023: import com.tc.simulator.container.Container;
024: import com.tc.simulator.container.ContainerConfig;
025: import com.tc.simulator.container.ContainerResult;
026: import com.tc.simulator.container.ContainerStateFactory;
027: import com.tc.simulator.control.Control;
028: import com.tc.simulator.listener.ResultsListener;
029: import com.tc.test.activepassive.ActivePassiveServerManager;
030: import com.tcsimulator.ControlImpl;
031: import com.tcsimulator.container.ContainerStateFactoryObject;
032: import com.tcsimulator.listener.QueuePrinter;
033:
034: import java.lang.reflect.Constructor;
035: import java.util.ArrayList;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Map;
039:
040: /**
041: * Takes an application configuration and some parameters and runs a single-vm, multi-node (multi-classloader) test.
042: */
043: public class DistributedTestRunner implements ResultsListener {
044: private static final boolean DEBUG = true;
045:
046: private final boolean startServer;
047:
048: private final Class applicationClass;
049: private final ApplicationConfig applicationConfig;
050: private final ConfigVisitor configVisitor;
051: private final ContainerConfig containerConfig;
052: private final Control control;
053: private final ResultsListener[] resultsListeners;
054: private final DSOClientConfigHelper configHelper;
055: private final ContainerStateFactory containerStateFactory;
056: private final TestGlobalIdGenerator globalIdGenerator;
057: private final TCServer server;
058: private final List errors = new ArrayList();
059: private final List results = new ArrayList();
060: private final DistributedTestRunnerConfig config;
061: private final TestTVSConfigurationSetupManagerFactory configFactory;
062: private boolean startTimedOut;
063: private boolean executionTimedOut;
064: private final int clientCount;
065: private final int applicationInstanceCount;
066: private final LinkedQueue statsOutputQueue;
067: private final QueuePrinter statsOutputPrinter;
068:
069: private final Map optionalAttributes;
070:
071: private final boolean isMutatorValidatorTest;
072: private final int validatorCount;
073: private final boolean isActivePassiveTest;
074: private final ActivePassiveServerManager serverManager;
075:
076: private final int adaptedMutatorCount;
077: private final int adaptedValidatorCount;
078: private final Map adapterMap;
079:
080: private ApplicationBuilder[] applicationBuilders;
081: private Container[] containers;
082: private Container[] validatorContainers;
083:
084: /**
085: * @param applicationClass Class of the application to be executed. It should implement the static method required by
086: * ClassLoaderConfigVisitor.
087: * @param applicationConfig Configuration object for the test application.
088: * @param nodeCount Number of classloaders to create.
089: * @param applicationsPerNode Number of application instances per classloader to execute. This counts as number of
090: * threads per classloader.
091: */
092: public DistributedTestRunner(DistributedTestRunnerConfig config,
093: TestTVSConfigurationSetupManagerFactory configFactory,
094: DSOClientConfigHelper configHelper, Class applicationClass,
095: Map optionalAttributes,
096: ApplicationConfig applicationConfig, boolean startServer,
097: boolean isMutatorValidatorTest,
098: boolean isActivePassiveTest,
099: ActivePassiveServerManager serverManager,
100: TransparentAppConfig transparentAppConfig) throws Exception {
101: this .optionalAttributes = optionalAttributes;
102: this .clientCount = transparentAppConfig.getClientCount();
103: this .applicationInstanceCount = transparentAppConfig
104: .getApplicationInstancePerClientCount();
105: this .startServer = startServer;
106: this .config = config;
107: this .configFactory = configFactory;
108: this .configHelper = configHelper;
109: this .isMutatorValidatorTest = isMutatorValidatorTest;
110: this .validatorCount = transparentAppConfig.getValidatorCount();
111: this .isActivePassiveTest = isActivePassiveTest;
112: this .serverManager = serverManager;
113: this .globalIdGenerator = new TestGlobalIdGenerator();
114: this .applicationClass = applicationClass;
115: this .applicationConfig = applicationConfig;
116: this .configVisitor = new ConfigVisitor();
117: this .containerConfig = newContainerConfig();
118: this .statsOutputQueue = new LinkedQueue();
119: this .statsOutputPrinter = new QueuePrinter(
120: this .statsOutputQueue, System.out);
121: this .containerStateFactory = new ContainerStateFactoryObject(
122: statsOutputQueue);
123: this .control = newContainerControl();
124:
125: adaptedMutatorCount = transparentAppConfig
126: .getAdaptedMutatorCount();
127: adaptedValidatorCount = transparentAppConfig
128: .getAdaptedValidatorCount();
129: adapterMap = (Map) transparentAppConfig
130: .getAttributeObject(TransparentAppConfig.adapterMapKey);
131: if (adapterMap == null) {
132: debugPrintln("***** adapter map is null!");
133: }
134:
135: this .resultsListeners = newResultsListeners(this .clientCount
136: + this .validatorCount);
137:
138: initializedClients();
139:
140: if (this .startServer) {
141: server = instantiateTCServer();
142: } else {
143: server = null;
144: }
145: }
146:
147: protected TCServer instantiateTCServer() {
148: try {
149: Class tcServerClass = Class
150: .forName("com.tc.server.TCServerImpl");
151: Class[] constructorParameterTypes = {
152: L2TVSConfigurationSetupManager.class,
153: TCThreadGroup.class };
154: Constructor tcServerConstructor = tcServerClass
155: .getConstructor(constructorParameterTypes);
156: Object[] tcServerConstructorArgs = {
157: configFactory
158: .createL2TVSConfigurationSetupManager(null),
159: new TCThreadGroup(new ThrowableHandler(TCLogging
160: .getLogger(TCServer.class))) };
161: return (TCServer) tcServerConstructor
162: .newInstance(tcServerConstructorArgs);
163: } catch (Exception e) {
164: throw new RuntimeException(
165: "Error while instantiating TCServerImpl from DistributedTestRunner",
166: e);
167: }
168: }
169:
170: private void initializedClients() throws Exception {
171: applicationBuilders = newApplicationBuilders(this .clientCount
172: + this .validatorCount);
173:
174: createMutators();
175: createValidators();
176: }
177:
178: private void createMutators() {
179: containers = new Container[clientCount];
180: boolean mutator = true;
181: for (int i = 0; i < containers.length; i++) {
182: containers[i] = new Container(this .containerConfig,
183: this .containerStateFactory, this .globalIdGenerator,
184: this .control, this .resultsListeners[i],
185: this .applicationBuilders[i],
186: this .isMutatorValidatorTest, mutator);
187: }
188: }
189:
190: private void createValidators() {
191: boolean isMutator = false;
192: validatorContainers = new Container[validatorCount];
193: for (int i = 0; i < validatorContainers.length; i++) {
194: validatorContainers[i] = new Container(containerConfig,
195: containerStateFactory, globalIdGenerator, control,
196: resultsListeners[i + clientCount],
197: applicationBuilders[i + clientCount],
198: isMutatorValidatorTest, isMutator);
199: }
200: }
201:
202: private void debugPrintln(String s) {
203: if (DEBUG) {
204: System.err.println(s);
205: }
206: }
207:
208: public void startServer() {
209: try {
210: startStatsOutputPrinterThread();
211: visitApplicationClassLoaderConfig();
212: if (this .startServer) {
213: this .server.start();
214: }
215: } catch (Throwable t) {
216: notifyError(new ErrorContext(t));
217: } finally {
218: if (false && this .startServer)
219: this .server.stop();
220: }
221: }
222:
223: private void startStatsOutputPrinterThread() {
224: Thread statsOutputPrinterThread = new Thread(
225: this .statsOutputPrinter);
226: statsOutputPrinterThread.setDaemon(true);
227: statsOutputPrinterThread.start();
228: }
229:
230: public void run() {
231: try {
232: debugPrintln("***** control=[" + control.toString() + "]");
233:
234: for (int i = 0; i < containers.length; i++) {
235: new Thread(containers[i]).start();
236: }
237:
238: // wait for all mutators to finish before starting the validators
239: if (isMutatorValidatorTest) {
240: final boolean mutationComplete = this .control
241: .waitForMutationComplete(config
242: .executionTimeout());
243:
244: if (!mutationComplete) {
245: notifyExecutionTimeout();
246: }
247:
248: if (isActivePassiveTest) {
249: checkForErrors();
250: }
251:
252: for (int i = 0; i < validatorContainers.length; i++) {
253: new Thread(validatorContainers[i]).start();
254: }
255:
256: if (isActivePassiveTest
257: && serverManager.crashActiveServerAfterMutate()) {
258: Thread.sleep(5000);
259: debugPrintln("***** DTR: Crashing active server");
260: serverManager.crashActive();
261: debugPrintln("***** DTR: Notifying the test-wide control");
262: control.notifyValidationStart();
263: }
264: }
265:
266: if (isActivePassiveTest) {
267: checkForErrors();
268: }
269:
270: final boolean complete = this .control
271: .waitForAllComplete(this .config.executionTimeout());
272:
273: if (!complete) {
274: notifyExecutionTimeout();
275: }
276:
277: if (isActivePassiveTest) {
278: checkForErrors();
279: }
280: } catch (Throwable t) {
281: notifyError(new ErrorContext(t));
282: } finally {
283: if (false && this .startServer)
284: this .server.stop();
285: }
286: }
287:
288: private void checkForErrors() throws Exception {
289: List l = serverManager.getErrors();
290: if (l.size() > 0) {
291: for (Iterator iter = l.iterator(); iter.hasNext();) {
292: Exception e = (Exception) iter.next();
293: e.printStackTrace();
294: }
295: throw (Exception) l.get(l.size() - 1);
296: }
297: }
298:
299: public boolean success() {
300: synchronized (results) {
301: for (Iterator i = results.iterator(); i.hasNext();) {
302: ContainerResult result = (ContainerResult) i.next();
303: if (!result.success()) {
304: System.out.print(result);
305: return false;
306: }
307: }
308: }
309:
310: if (errors.size() > 0) {
311: System.err.println(errors.size() + " ERRORS PRESENT");
312: return false;
313: } else if (startTimedOut) {
314: System.err.println("START TIMED OUT; timeout="
315: + this .config.startTimeout());
316: return false;
317: } else if (executionTimedOut) {
318: System.err.println("EXECUTION TIMED OUT; timeout="
319: + this .config.executionTimeout());
320: return false;
321: } else if (results.size() != (containers.length + validatorContainers.length)) {
322: System.err.println(results.size()
323: + " results present, EXPECTING "
324: + containers.length);
325: return false;
326: } else {
327: return true;
328: }
329:
330: // unreachable
331: }
332:
333: public List getErrors() {
334: synchronized (errors) {
335: return new ArrayList(errors);
336: }
337: }
338:
339: public boolean startTimedOut() {
340: return startTimedOut;
341: }
342:
343: public boolean executionTimedOut() {
344: return executionTimedOut;
345: }
346:
347: private void visitApplicationClassLoaderConfig() {
348: if (optionalAttributes.size() > 0) {
349: this .configVisitor.visit(this .configHelper,
350: this .applicationClass, this .optionalAttributes);
351: } else {
352: this .configVisitor.visit(this .configHelper,
353: this .applicationClass);
354: }
355: }
356:
357: private ContainerConfig newContainerConfig() {
358: return new ContainerConfig() {
359:
360: public int getApplicationInstanceCount() {
361: return applicationInstanceCount;
362: }
363:
364: public long getContainerStartTimeout() {
365: return config.startTimeout();
366: }
367:
368: public long getApplicationStartTimeout() {
369: return config.startTimeout();
370: }
371:
372: public long getApplicationExecutionTimeout() {
373: return config.executionTimeout();
374: }
375:
376: public boolean isMaster() {
377: return false;
378: }
379:
380: };
381: }
382:
383: private Control newContainerControl() {
384: boolean crashActiveServerAfterMutate;
385: if (isActivePassiveTest) {
386: crashActiveServerAfterMutate = serverManager
387: .crashActiveServerAfterMutate();
388: } else {
389: crashActiveServerAfterMutate = false;
390: }
391: return new ControlImpl(this .clientCount, validatorCount,
392: crashActiveServerAfterMutate);
393: }
394:
395: private ResultsListener[] newResultsListeners(int count) {
396: ResultsListener[] rv = new ResultsListener[count];
397: for (int i = 0; i < rv.length; i++) {
398: rv[i] = this ;
399: }
400: return rv;
401: }
402:
403: private ApplicationBuilder[] newApplicationBuilders(
404: int totalClientCount) throws Exception {
405: ApplicationBuilder[] rv = new ApplicationBuilder[totalClientCount];
406:
407: for (int i = 0; i < rv.length; i++) {
408: L1TVSConfigurationSetupManager l1ConfigManager;
409: l1ConfigManager = this .configFactory
410: .createL1TVSConfigurationSetupManager();
411: l1ConfigManager.setupLogging();
412: PreparedComponentsFromL2Connection components = new PreparedComponentsFromL2Connection(
413: l1ConfigManager);
414: if (adapterMap != null
415: && ((i < clientCount && i < adaptedMutatorCount) || (i >= clientCount && i < (adaptedValidatorCount + clientCount)))) {
416: rv[i] = new DSOApplicationBuilder(this .configHelper,
417: this .applicationConfig, components, adapterMap);
418:
419: for (Iterator iter = adapterMap.keySet().iterator(); iter
420: .hasNext();) {
421: String adapteeName = (String) iter.next();
422: Class adapterClass = (Class) adapterMap
423: .get(adapteeName);
424: debugPrintln("***** Adding adapter to appBuilder=["
425: + i + "] adapteeName=[" + adapteeName
426: + "] adapterClass=["
427: + adapterClass.getName() + "]");
428: }
429: } else {
430: rv[i] = new DSOApplicationBuilder(this .configHelper,
431: this .applicationConfig, components);
432: debugPrintln("***** Creating normal DSOApplicationBuilder: i=["
433: + i + "]");
434: }
435: }
436: return rv;
437: }
438:
439: /*********************************************************************************************************************
440: * ResultsListener interface
441: */
442:
443: public void setGlobalId(long globalId) {
444: return;
445: }
446:
447: public void notifyStartTimeout() {
448: this .startTimedOut = true;
449: }
450:
451: public void notifyExecutionTimeout() {
452: this .executionTimedOut = true;
453: }
454:
455: public void notifyError(ErrorContext ectxt) {
456: synchronized (this .errors) {
457: ectxt.dump(System.err);
458: this .errors.add(ectxt);
459: }
460: }
461:
462: public void notifyResult(Object result) {
463: synchronized (this .results) {
464: this .results.add(result);
465: }
466: }
467:
468: public void dumpServer() {
469: if (server != null && startServer) {
470: System.out.println("Dumping intra-process server");
471: server.dump();
472: }
473: }
474:
475: }
|