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.test.server.appserver.glassfishv1;
006:
007: import org.apache.commons.io.FileUtils;
008: import org.w3c.dom.Document;
009: import org.w3c.dom.Element;
010: import org.w3c.dom.NamedNodeMap;
011: import org.w3c.dom.Node;
012: import org.w3c.dom.NodeList;
013:
014: import com.tc.process.Exec;
015: import com.tc.process.HeartBeatService;
016: import com.tc.process.Exec.Result;
017: import com.tc.test.TestConfigObject;
018: import com.tc.test.server.ServerParameters;
019: import com.tc.test.server.ServerResult;
020: import com.tc.test.server.appserver.AbstractAppServer;
021: import com.tc.test.server.appserver.AppServerParameters;
022: import com.tc.test.server.appserver.AppServerResult;
023: import com.tc.test.server.appserver.cargo.CargoLinkedChildProcess;
024: import com.tc.test.server.util.AppServerUtil;
025: import com.tc.text.Banner;
026: import com.tc.util.Assert;
027: import com.tc.util.PortChooser;
028: import com.tc.util.runtime.Os;
029:
030: import java.io.ByteArrayOutputStream;
031: import java.io.File;
032: import java.io.FileOutputStream;
033: import java.io.IOException;
034: import java.io.StringWriter;
035: import java.util.ArrayList;
036: import java.util.Arrays;
037: import java.util.Iterator;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.Map.Entry;
041:
042: import javax.xml.parsers.DocumentBuilder;
043: import javax.xml.parsers.DocumentBuilderFactory;
044: import javax.xml.transform.OutputKeys;
045: import javax.xml.transform.Transformer;
046: import javax.xml.transform.TransformerFactory;
047: import javax.xml.transform.dom.DOMSource;
048: import javax.xml.transform.stream.StreamResult;
049:
050: /**
051: * Glassfish AppServer implementation
052: */
053: public final class GlassfishV1AppServer extends AbstractAppServer {
054:
055: private static final String JAVA_CMD = System
056: .getProperty("java.home")
057: + File.separator + "bin" + File.separator + "java";
058:
059: private static final String ADMIN_USER = "admin";
060: private static final String PASSWD = "password";
061:
062: private static final long START_STOP_TIMEOUT = 1000 * 240;
063: private final PortChooser pc = new PortChooser();
064: private final int httpPort = pc.chooseRandomPort();
065: private final int adminPort = pc.chooseRandomPort();
066: private File passwdFile;
067: private Thread runner;
068: private File instanceDir;
069:
070: public GlassfishV1AppServer(
071: GlassfishV1AppServerInstallation installation) {
072: super (installation);
073: }
074:
075: private synchronized File getPasswdFile() throws IOException {
076: if (passwdFile == null) {
077: passwdFile = File.createTempFile("passwd", "");
078: passwdFile.deleteOnExit();
079:
080: FileOutputStream fos = new FileOutputStream(passwdFile);
081: try {
082: fos = new FileOutputStream(passwdFile);
083: fos.write(("AS_ADMIN_ADMINPASSWORD=" + PASSWD)
084: .getBytes());
085: fos.write("\n".getBytes());
086: fos.write(("AS_ADMIN_MASTERPASSWORD=" + PASSWD)
087: .getBytes());
088: fos.write("\n".getBytes());
089: } finally {
090: if (fos != null) {
091: try {
092: fos.close();
093: } catch (IOException ioe) {
094: // ignore
095: }
096: }
097: }
098: }
099:
100: return passwdFile;
101: }
102:
103: private static String getPlatformScript(String name) {
104: if (Os.isWindows()) {
105: return name + ".bat";
106: }
107: return name;
108: }
109:
110: private void createDomain(AppServerParameters params)
111: throws Exception {
112: File asAdminScript = getAsadminScript();
113:
114: List cmd = new ArrayList();
115: cmd.add(asAdminScript.getAbsolutePath());
116: cmd.add("create-domain");
117: cmd.add("--interactive=false");
118: cmd.add("--domaindir=" + sandboxDirectory());
119: cmd.add("--adminport");
120: cmd.add(String.valueOf(adminPort));
121: cmd.add("--adminuser");
122: cmd.add(ADMIN_USER);
123: cmd.add("--passwordfile");
124: cmd.add(getPasswdFile().getAbsolutePath());
125: cmd.add("--instanceport");
126: cmd.add(String.valueOf(httpPort));
127: cmd.add("--savemasterpassword=true");
128: cmd.add("--domainproperties");
129: cmd.add("jms.port=" + pc.chooseRandomPort() + ":"
130: + "orb.listener.port=" + pc.chooseRandomPort() + ":"
131: + "http.ssl.port=" + pc.chooseRandomPort() + ":"
132: + "orb.ssl.port=" + pc.chooseRandomPort() + ":"
133: + "orb.mutualauth.port=" + pc.chooseRandomPort() + ":"
134: + "domain.jmxPort=" + pc.chooseRandomPort());
135: cmd.add("--savelogin=true");
136: cmd.add(params.instanceName());
137:
138: Result result = Exec.execute((String[]) cmd
139: .toArray(new String[] {}), null, null, asAdminScript
140: .getParentFile());
141:
142: if (result.getExitCode() != 0) {
143: throw new RuntimeException(result.toString());
144: }
145: }
146:
147: private static void checkFile(File file) {
148: if (!file.isFile()) {
149: throw new RuntimeException(file.getAbsolutePath()
150: + " is not a file or does not exist");
151: }
152: if (!file.canRead()) {
153: throw new RuntimeException(file.getAbsolutePath()
154: + " cannot be read");
155: }
156: }
157:
158: private File getInstanceFile(String path) {
159: Assert.assertNotNull(instanceDir);
160: File f = new File(instanceDir, path);
161: checkFile(f);
162: return f;
163: }
164:
165: private File getAsadminScript() {
166: File glassfishInstall = this .serverInstallDirectory();
167: File asAdminScript = new File(
168: new File(glassfishInstall, "bin"),
169: getPlatformScript("asadmin"));
170: checkFile(asAdminScript);
171: return asAdminScript;
172: }
173:
174: public ServerResult start(ServerParameters rawParams)
175: throws Exception {
176: AppServerParameters params = (AppServerParameters) rawParams;
177: instanceDir = createInstance(params);
178:
179: instanceDir.delete(); // createDomain will fail if directory already exists
180: createDomain(params);
181:
182: modifyDomainConfig(params);
183:
184: setProperties(params, httpPort, instanceDir);
185:
186: final String cmd[] = getStartupCommand(params);
187: final String nodeLogFile = new File(instanceDir.getParent(),
188: instanceDir.getName() + ".log").getAbsolutePath();
189:
190: runner = new Thread("runner for " + params.instanceName()) {
191: public void run() {
192: try {
193: Result result = Exec.execute(cmd, nodeLogFile,
194: startupInput(), instanceDir);
195: if (result.getExitCode() != 0) {
196: System.err.println(result);
197: }
198: } catch (Throwable e) {
199: e.printStackTrace();
200: }
201: }
202: };
203: runner.start();
204: System.err.println("Starting " + params.instanceName()
205: + " on port " + httpPort + "...");
206:
207: AppServerUtil.waitForPort(adminPort, START_STOP_TIMEOUT);
208:
209: System.err.println("Started " + params.instanceName()
210: + " on port " + httpPort);
211:
212: deployWars(params.wars());
213:
214: return new AppServerResult(httpPort, this );
215: }
216:
217: private static byte[] startupInput() {
218: try {
219: String eol = System.getProperty("line.separator");
220:
221: final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
222: bytes
223: .write((ADMIN_USER + eol + PASSWD + eol + PASSWD + eol)
224: .getBytes());
225: if (Os.isWindows()) {
226: bytes.write((byte) 26); // ctrl-Z
227: } else {
228: bytes.write((byte) 4); // ctrl-D
229: }
230:
231: return bytes.toByteArray();
232: } catch (IOException ioe) {
233: throw new RuntimeException(ioe);
234: }
235: }
236:
237: private void deployWars(Map wars) throws Exception {
238: for (Iterator iter = wars.entrySet().iterator(); iter.hasNext();) {
239: Map.Entry entry = (Entry) iter.next();
240:
241: String warName = (String) entry.getKey();
242: File warFile = (File) entry.getValue();
243:
244: System.err.println("Deploying war [" + warName + "] on "
245: + instanceDir.getName());
246:
247: List cmd = new ArrayList();
248: cmd.add(getAsadminScript().getAbsolutePath());
249: cmd.add("deploy");
250: cmd.add("--interactive=false");
251: cmd.add("--user");
252: cmd.add(ADMIN_USER);
253: cmd.add("--passwordfile");
254: cmd.add(getPasswdFile().getAbsolutePath());
255: cmd.add("--contextroot=" + warName);
256: cmd.add("--port=" + adminPort);
257: cmd.add(warFile.getAbsolutePath());
258:
259: Result result = Exec.execute((String[]) cmd
260: .toArray(new String[] {}));
261: if (result.getExitCode() != 0) {
262: throw new RuntimeException("Deploy failed for "
263: + warName + ": " + result);
264: }
265: }
266: }
267:
268: private String[] getStartupCommand(AppServerParameters params)
269: throws Exception {
270: File startScript = getInstanceFile("bin/"
271: + getPlatformScript("startserv"));
272:
273: Result result = Exec.execute(new String[] {
274: startScript.getAbsolutePath(), "display" }, null, null,
275: startScript.getParentFile());
276: if (result.getExitCode() != 0) {
277: throw new RuntimeException(
278: "error executing startserv script: " + result);
279: }
280:
281: String output = result.getStdout().trim();
282:
283: if (!output.startsWith("STARTOFCOMMAND|")
284: || !output.endsWith("|ENDOFCOMMAND|")) {
285: throw new RuntimeException("cannot parse output: " + output);
286: }
287:
288: output = output.substring("STARTOFCOMMAND|".length());
289: output = output.substring(0, output.length()
290: - "|ENDOFCOMMAND|".length());
291:
292: List cmd = new ArrayList(Arrays.asList(output.split("\\|")));
293:
294: // add the linked java process stuff to classpath
295: for (int i = 0; i < cmd.size(); i++) {
296: String s = (String) cmd.get(i);
297:
298: if (s.toLowerCase().trim().equals("-classpath")
299: || s.toLowerCase().trim().equals("-cp")) {
300: // the classpath is set with java.class.path system property, check these just for good measure
301: throw new RuntimeException(
302: "unexpected classpath arguments in startup command "
303: + cmd);
304: }
305:
306: if (s.startsWith("-Djava.class.path=")) {
307: cmd.set(i, s
308: + File.pathSeparator
309: + TestConfigObject.getInstance()
310: .linkedChildProcessClasspath());
311: break;
312: }
313: }
314:
315: String mainArg = (String) cmd.remove(cmd.size() - 1);
316: String mainClass = (String) cmd.remove(cmd.size() - 1);
317:
318: if (!"com.sun.enterprise.server.PELaunch".equals(mainClass)) {
319: throw new RuntimeException("Unexpected main class: "
320: + mainClass);
321: }
322: if (!"start".equals(mainArg)) {
323: throw new RuntimeException("unexpected main argument: "
324: + mainArg);
325: }
326:
327: cmd.add(CargoLinkedChildProcess.class.getName());
328: cmd.add(mainClass);
329: cmd.add(String.valueOf(HeartBeatService.listenPort()));
330: cmd.add(instanceDir.toString());
331: cmd.add(mainArg);
332:
333: cmd.add(0, JAVA_CMD);
334: return (String[]) cmd.toArray(new String[] {});
335: }
336:
337: private void modifyDomainConfig(AppServerParameters params)
338: throws Exception {
339: File domainXML = getInstanceFile("config/domain.xml");
340:
341: System.err.println("Modifying domain configuration at "
342: + domainXML.getAbsolutePath());
343:
344: DocumentBuilderFactory factory = DocumentBuilderFactory
345: .newInstance();
346:
347: // disable the resolve of the external DTD -- The monkey failed once since it timed out talking to sun's web site
348: factory
349: .setAttribute(
350: "http://apache.org/xml/features/nonvalidating/load-external-dtd",
351: Boolean.FALSE);
352:
353: DocumentBuilder builder = factory.newDocumentBuilder();
354: Document document = builder.parse(domainXML);
355:
356: NodeList list = document.getElementsByTagName("java-config");
357:
358: if (list.getLength() != 1) {
359: throw new RuntimeException("wrong number of elements "
360: + list.getLength());
361: }
362:
363: Node javaConfig = list.item(0);
364:
365: // if you want debugging of the spawned glassfish, play with this
366: if (false) {
367: NamedNodeMap attrs = javaConfig.getAttributes();
368: attrs.getNamedItem("debug-enabled").setNodeValue("true");
369: attrs
370: .getNamedItem("debug-options")
371: .setNodeValue(
372: "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000");
373: }
374:
375: appendDSOParams(document, javaConfig, params);
376:
377: TransformerFactory transformerFactory = TransformerFactory
378: .newInstance();
379: Transformer transformer = transformerFactory.newTransformer();
380:
381: if (document.getDoctype() != null) {
382: transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
383: document.getDoctype().getPublicId());
384: transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
385: document.getDoctype().getSystemId());
386: }
387:
388: StringWriter sw = new StringWriter();
389: transformer.transform(new DOMSource(document),
390: new StreamResult(sw));
391:
392: FileUtils.writeStringToFile(domainXML, sw.toString(), "UTF-8");
393: }
394:
395: private void appendDSOParams(Document doc, Node node,
396: AppServerParameters params) {
397: String[] jvmArgs = params.jvmArgs().replaceAll("'", "").split(
398: "\\s");
399:
400: for (int i = 0; i < jvmArgs.length; i++) {
401: String arg = jvmArgs[i];
402:
403: Element element = doc.createElement("jvm-options");
404: element.appendChild(doc.createTextNode(arg));
405: node.appendChild(element);
406: }
407:
408: // workaround for DSO early initialization of NIO stuff
409: // XXX: when/if this can be magically worked around, this option can removed
410: Element element = doc.createElement("jvm-options");
411: element
412: .appendChild(doc
413: .createTextNode("-Dcom.sun.enterprise.server.ss.ASQuickStartup=false"));
414: node.appendChild(element);
415: }
416:
417: public void stop() throws Exception {
418: System.err.println("Stopping instance on port " + httpPort
419: + "...");
420:
421: File stopScript = getInstanceFile("bin/"
422: + getPlatformScript("stopserv"));
423: Result result = Exec.execute(new String[] { stopScript
424: .getAbsolutePath() }, null, null, stopScript
425: .getParentFile());
426: if (result.getExitCode() != 0) {
427: System.err.println(result);
428: }
429:
430: if (runner != null) {
431: runner.join(START_STOP_TIMEOUT);
432: }
433:
434: if (runner.isAlive()) {
435: Banner.errorBanner("instance still running on port "
436: + httpPort);
437: } else {
438: System.err.println("Stopped instance on port " + httpPort);
439: }
440:
441: }
442:
443: }
|