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.tc.objectserver.control;
006:
007: import org.apache.commons.lang.ArrayUtils;
008:
009: import com.tc.config.Directories;
010: import com.tc.config.schema.setup.StandardTVSConfigurationSetupManagerFactory;
011: import com.tc.process.LinkedJavaProcess;
012: import com.tc.process.StreamCopier;
013: import com.tc.properties.TCPropertiesImpl;
014: import com.tc.test.TestConfigObject;
015: import com.tc.util.runtime.Os;
016: import com.tc.util.runtime.Vm;
017:
018: import java.io.ByteArrayOutputStream;
019: import java.io.File;
020: import java.io.FileOutputStream;
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: public class ExtraProcessServerControl extends ServerControlBase {
027: private static final String NOT_DEF = "";
028: private static final String ERR_STREAM = "ERR";
029: private static final String OUT_STREAM = "OUT";
030: private final long SHUTDOWN_WAIT_TIME = 2 * 60 * 1000;
031:
032: private final String name;
033: private final boolean mergeOutput;
034:
035: protected LinkedJavaProcess process;
036: protected File javaHome;
037: protected final String configFileLoc;
038: protected final List jvmArgs;
039: private final File runningDirectory;
040: private final String serverName;
041: private File out;
042: private FileOutputStream fileOut;
043: private StreamCopier outCopier;
044: private StreamCopier errCopier;
045: private final boolean useIdentifier;
046: private String stopperOutput;
047:
048: // constructor 1: used by container tests
049: public ExtraProcessServerControl(String host, int dsoPort,
050: int adminPort, String configFileLoc, boolean mergeOutput) {
051: this (new DebugParams(), host, dsoPort, adminPort,
052: configFileLoc, mergeOutput);
053: }
054:
055: // constructor 2: used by ExtraL1ProceddControl and constructor 1
056: public ExtraProcessServerControl(DebugParams debugParams,
057: String host, int dsoPort, int adminPort,
058: String configFileLoc, boolean mergeOutput) {
059: this (debugParams, host, dsoPort, adminPort, configFileLoc,
060: null, mergeOutput, null, new ArrayList(), NOT_DEF,
061: null, false);
062: }
063:
064: public ExtraProcessServerControl(DebugParams params, String host,
065: int dsoPort, int adminPort, String configFileLoc,
066: boolean mergeOutput, List jvmArgs) {
067: this (params, host, dsoPort, adminPort, configFileLoc, null,
068: mergeOutput, null, jvmArgs, NOT_DEF, null, false);
069: }
070:
071: // constructor 3: used by ControlSetup, Setup, and container tests
072: public ExtraProcessServerControl(DebugParams debugParams,
073: String host, int dsoPort, int adminPort,
074: String configFileLoc, File runningDirectory,
075: boolean mergeOutput, List jvmArgs, String undefString) {
076: this (debugParams, host, dsoPort, adminPort, configFileLoc,
077: runningDirectory, mergeOutput, null, jvmArgs,
078: undefString, null, false);
079: }
080:
081: // constructor 4: used by TransparentTestBase for single failure case
082: public ExtraProcessServerControl(String host, int dsoPort,
083: int adminPort, String configFileLoc, boolean mergeOutput,
084: File javaHome) {
085: this (new DebugParams(), host, dsoPort, adminPort,
086: configFileLoc, mergeOutput, javaHome);
087: }
088:
089: public ExtraProcessServerControl(String host, int dsoPort,
090: int adminPort, String configFileLoc, boolean mergeOutput,
091: File javaHome, List jvmArgs) {
092: this (new DebugParams(), host, dsoPort, adminPort,
093: configFileLoc, mergeOutput, javaHome, jvmArgs);
094: }
095:
096: // constructor 5: used by active-passive tests
097: public ExtraProcessServerControl(String host, int dsoPort,
098: int adminPort, String configFileLoc, boolean mergeOutput,
099: String servername, List additionalJvmArgs, File javaHome,
100: boolean useIdentifier) {
101: this (new DebugParams(), host, dsoPort, adminPort,
102: configFileLoc, null, mergeOutput, servername,
103: additionalJvmArgs, NOT_DEF, javaHome, useIdentifier);
104: }
105:
106: // constructor 6: used by constructor 4, crash tests, and normal tests running in 1.4 jvm
107: public ExtraProcessServerControl(DebugParams debugParams,
108: String host, int dsoPort, int adminPort,
109: String configFileLoc, boolean mergeOutput, File javaHome) {
110: this (debugParams, host, dsoPort, adminPort, configFileLoc,
111: null, mergeOutput, null, new ArrayList(), NOT_DEF,
112: javaHome, false);
113: }
114:
115: public ExtraProcessServerControl(DebugParams debugParams,
116: String host, int dsoPort, int adminPort,
117: String configFileLoc, boolean mergeOutput, File javaHome,
118: List jvmArgs) {
119: this (debugParams, host, dsoPort, adminPort, configFileLoc,
120: null, mergeOutput, null, jvmArgs, NOT_DEF, javaHome,
121: false);
122: }
123:
124: // only called by constructors in this class
125: protected ExtraProcessServerControl(DebugParams debugParams,
126: String host, int dsoPort, int adminPort,
127: String configFileLoc, File runningDirectory,
128: boolean mergeOutput, String serverName,
129: List additionalJvmArgs, String undefString, File javaHome,
130: boolean useIdentifier) {
131: super (host, dsoPort, adminPort);
132: this .useIdentifier = useIdentifier;
133: this .javaHome = javaHome;
134: this .serverName = serverName;
135: jvmArgs = new ArrayList();
136:
137: if (additionalJvmArgs != null) {
138: for (Iterator i = additionalJvmArgs.iterator(); i.hasNext();) {
139: String next = (String) i.next();
140: if (!next.equals(undefString)) {
141: this .jvmArgs.add(next);
142: }
143: }
144: }
145:
146: this .configFileLoc = configFileLoc;
147: this .mergeOutput = mergeOutput;
148: this .name = "DSO process @ " + getHost() + ":" + getDsoPort()
149: + ", jmx-port:" + adminPort;
150: this .runningDirectory = runningDirectory;
151: jvmArgs.add("-Dcom.tc.l1.modules.repositories="
152: + System.getProperty("com.tc.l1.modules.repositories"));
153: jvmArgs.add("-Dtc.base-dir="
154: + System.getProperty("tc.base-dir"));
155: jvmArgs
156: .add("-D"
157: + Directories.TC_INSTALL_ROOT_IGNORE_CHECKS_PROPERTY_NAME
158: + "=true");
159: jvmArgs.add("-Djava.net.preferIPv4Stack=true");
160: debugParams.addDebugParamsTo(jvmArgs);
161: jvmArgs.add("-D" + TCPropertiesImpl.SYSTEM_PROP_PREFIX
162: + "tc.management.test.mbeans.enabled=true");
163: addClasspath(jvmArgs);
164: addLibPath(jvmArgs);
165: addEnvVarsForWindows(jvmArgs);
166:
167: if (!Vm.isIBM() && !(Os.isMac() && Vm.isJDK14())) {
168: jvmArgs.add("-XX:+HeapDumpOnOutOfMemoryError");
169: }
170: }
171:
172: private String getStreamIdentifier(int dsoPort, String streamType) {
173: String portString = "" + dsoPort;
174: int numSpaces = 5 - portString.length();
175: for (int i = 0; i < numSpaces; i++) {
176: portString = " " + portString;
177: }
178: return "[" + portString + "][" + streamType + "] ";
179: }
180:
181: private void addLibPath(List args) {
182: String libPath = System.getProperty("java.library.path");
183: if (libPath == null || libPath.equals("")) {
184: throw new AssertionError("java.library.path is not set!");
185: }
186: args.add("-Djava.library.path=" + libPath);
187: }
188:
189: private void addClasspath(List args) {
190: String classpath = System.getProperty("java.class.path");
191: if (classpath == null || classpath.equals("")) {
192: throw new AssertionError("java.class.path is not set!");
193: }
194: args.add("-Djava.class.path=" + classpath);
195: }
196:
197: private void addEnvVarsForWindows(List args) {
198: String tcBaseDir = System.getProperty("tc.base-dir");
199: if (tcBaseDir == null || tcBaseDir.equals("")) {
200: throw new AssertionError("tc.base-dir is not set!");
201: }
202: args.add("-Dtc.base-dir=" + tcBaseDir);
203: String val = System.getProperty("tc.tests.info.property-files");
204: if (val != null && !val.trim().equals("")) {
205: args.add("-Dtc.tests.info.property-files=" + val);
206: }
207: }
208:
209: public void mergeSTDOUT() {
210: if (useIdentifier) {
211: process.mergeSTDOUT(getStreamIdentifier(getDsoPort(),
212: OUT_STREAM));
213: } else {
214: process.mergeSTDOUT();
215: }
216: }
217:
218: public void mergeSTDERR() {
219: if (useIdentifier) {
220: process.mergeSTDERR(getStreamIdentifier(getDsoPort(),
221: ERR_STREAM));
222: } else {
223: process.mergeSTDERR();
224: }
225: }
226:
227: protected String getMainClassName() {
228: return "com.tc.server.TCServerMain";
229: }
230:
231: /**
232: * The JAVA_HOME for the JVM to use when creating a {@link LinkedChildProcess}.
233: */
234: public File getJavaHome() {
235: if (javaHome == null) {
236: javaHome = new File(TestConfigObject.getInstance()
237: .getL2StartupJavaHome());
238: }
239: return javaHome;
240: }
241:
242: public void setJavaHome(File javaHome) {
243: this .javaHome = javaHome;
244: }
245:
246: protected String[] getMainClassArguments() {
247: if (serverName != null && !serverName.equals("")) {
248: return new String[] {
249: StandardTVSConfigurationSetupManagerFactory.CONFIG_SPEC_ARGUMENT_WORD,
250: this .configFileLoc,
251: StandardTVSConfigurationSetupManagerFactory.SERVER_NAME_ARGUMENT_WORD,
252: serverName };
253: } else {
254: return new String[] {
255: StandardTVSConfigurationSetupManagerFactory.CONFIG_SPEC_ARGUMENT_WORD,
256: this .configFileLoc };
257: }
258: }
259:
260: public void writeOutputTo(File outputFile) {
261: if (mergeOutput) {
262: throw new IllegalStateException();
263: }
264: this .out = outputFile;
265: }
266:
267: public void start() throws Exception {
268: System.err.println("Starting " + this .name
269: + /* ": jvmArgs=" + jvmArgs + */", main="
270: + getMainClassName() + ", main args="
271: + ArrayUtils.toString(getMainClassArguments())
272: + ", jvm=[" + getJavaHome() + "]");
273: process = createLinkedJavaProcess();
274: process.setJavaArguments((String[]) jvmArgs
275: .toArray(new String[jvmArgs.size()]));
276: process.start();
277: if (mergeOutput) {
278: mergeSTDOUT();
279: mergeSTDERR();
280: } else if (out != null) {
281: fileOut = new FileOutputStream(out);
282: outCopier = new StreamCopier(process.STDOUT(), fileOut);
283: errCopier = new StreamCopier(process.STDERR(), fileOut);
284: outCopier.start();
285: errCopier.start();
286: }
287: waitUntilStarted();
288: System.err.println(this .name + " started.");
289: }
290:
291: protected LinkedJavaProcess createLinkedJavaProcess(
292: String mainClassName, String[] arguments) {
293: LinkedJavaProcess result = new LinkedJavaProcess(mainClassName,
294: arguments);
295: result.setDirectory(this .runningDirectory);
296: File processJavaHome = getJavaHome();
297: if (processJavaHome != null) {
298: result.setJavaHome(processJavaHome);
299: }
300: return result;
301: }
302:
303: protected LinkedJavaProcess createLinkedJavaProcess() {
304: return createLinkedJavaProcess(getMainClassName(),
305: getMainClassArguments());
306: }
307:
308: public void crash() throws Exception {
309: System.out.println("Crashing server " + this .name + "...");
310: if (process != null) {
311: process.destroy();
312: waitUntilShutdown();
313: }
314: System.out.println(this .name + " crashed.");
315: }
316:
317: public void attemptShutdown() throws Exception {
318: System.out.println("Shutting down server " + this .name + "...");
319: String[] args = getMainClassArguments();
320: LinkedJavaProcess stopper = createLinkedJavaProcess(
321: "com.tc.admin.TCStop", args);
322: stopper.start();
323:
324: ByteArrayOutputStream stopperLog = null;
325: try {
326: stopperLog = new ByteArrayOutputStream();
327: StreamCopier stdoutCopier = new StreamCopier(stopper
328: .STDOUT(), stopperLog);
329: StreamCopier stderrCopier = new StreamCopier(stopper
330: .STDERR(), stopperLog);
331:
332: stdoutCopier.start();
333: stderrCopier.start();
334:
335: stdoutCopier.join(60 * 1000);
336: stderrCopier.join(60 * 1000);
337:
338: } finally {
339: if (stopperLog != null) {
340: stopperOutput = stopperLog.toString();
341: stopperLog.close();
342: }
343: stopper.STDIN().close();
344: }
345:
346: }
347:
348: public void shutdown() throws Exception {
349: try {
350: attemptShutdown();
351: } catch (Exception e) {
352: System.err
353: .println("Attempt to shutdown server but it might have already crashed: "
354: + e.getMessage());
355: }
356: waitUntilShutdown();
357: System.out.println(this .name + " stopped.");
358: }
359:
360: private void waitUntilStarted() throws Exception {
361: while (true) {
362: if (isRunning())
363: return;
364: Thread.sleep(1000);
365: }
366: }
367:
368: public void waitUntilShutdown() throws Exception {
369: long start = System.currentTimeMillis();
370: long timeout = start + SHUTDOWN_WAIT_TIME;
371: while (isRunning()) {
372: Thread.sleep(1000);
373: if (System.currentTimeMillis() > timeout) {
374: System.err.println("TCStoper output: " + stopperOutput);
375: throw new Exception(
376: "Server was shutdown but still up after "
377: + SHUTDOWN_WAIT_TIME + " ms");
378: }
379: }
380: }
381:
382: public int waitFor() throws Exception {
383: int rv = process.waitFor();
384:
385: if (outCopier != null) {
386: try {
387: outCopier.join();
388: } catch (Exception e) {
389: e.printStackTrace();
390: }
391: }
392:
393: if (errCopier != null) {
394: try {
395: errCopier.join();
396: } catch (Exception e) {
397: e.printStackTrace();
398: }
399: }
400:
401: if (fileOut != null) {
402: try {
403: fileOut.close();
404: } catch (Exception e) {
405: e.printStackTrace();
406: }
407: }
408:
409: return rv;
410: }
411:
412: public static final class DebugParams {
413: private final boolean debug;
414: private final int debugPort;
415:
416: public DebugParams() {
417: this (false, 0);
418: }
419:
420: public DebugParams(int debugPort) {
421: this (true, debugPort);
422: }
423:
424: private DebugParams(boolean debug, int debugPort) {
425: if (debugPort < 0)
426: throw new AssertionError("Debug port must be >= 0: "
427: + debugPort);
428: this .debugPort = debugPort;
429: this .debug = debug;
430: }
431:
432: private void addDebugParamsTo(Collection jvmArgs) {
433: if (debug) {
434: jvmArgs.add("-Xdebug");
435: String address = debugPort > 0 ? "address=" + debugPort
436: + "," : "";
437: jvmArgs.add("-Xrunjdwp:transport=dt_socket," + address
438: + "server=y,suspend=n");
439: }
440: }
441: }
442:
443: public List getJvmArgs() {
444: return jvmArgs;
445: }
446:
447: }
|