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.deployment;
006:
007: import org.apache.commons.httpclient.HttpClient;
008: import org.apache.commons.io.IOUtils;
009: import org.codehaus.cargo.container.deployable.WAR;
010: import org.codehaus.cargo.container.property.RemotePropertySet;
011: import org.codehaus.cargo.container.tomcat.Tomcat5xRemoteContainer;
012: import org.codehaus.cargo.container.tomcat.Tomcat5xRemoteDeployer;
013: import org.codehaus.cargo.container.tomcat.TomcatPropertySet;
014: import org.codehaus.cargo.container.tomcat.TomcatRuntimeConfiguration;
015: import org.codehaus.cargo.util.log.SimpleLogger;
016: import org.springframework.remoting.RemoteLookupFailureException;
017: import org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor;
018: import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
019: import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
020: import org.springframework.remoting.rmi.RmiProxyFactoryBean;
021: import org.springframework.remoting.rmi.RmiServiceExporter;
022: import org.xml.sax.SAXException;
023:
024: import com.meterware.httpunit.WebConversation;
025: import com.meterware.httpunit.WebResponse;
026: import com.tc.management.JMXConnectorProxy;
027: import com.tc.test.TestConfigObject;
028: import com.tc.test.server.ServerResult;
029: import com.tc.test.server.appserver.AppServer;
030: import com.tc.test.server.appserver.AppServerFactory;
031: import com.tc.test.server.appserver.AppServerInstallation;
032: import com.tc.test.server.appserver.StandardAppServerParameters;
033: import com.tc.test.server.util.AppServerUtil;
034: import com.tc.test.server.util.TcConfigBuilder;
035: import com.tc.text.Banner;
036: import com.tc.util.runtime.Os;
037: import com.tc.util.runtime.ThreadDump;
038: import com.tc.util.runtime.Vm;
039:
040: import java.io.File;
041: import java.io.FileOutputStream;
042: import java.io.IOException;
043: import java.net.MalformedURLException;
044: import java.util.HashMap;
045: import java.util.Map;
046:
047: import javax.management.MBeanServerConnection;
048:
049: import junit.framework.Assert;
050:
051: public class GenericServer extends AbstractStoppable implements
052: WebApplicationServer {
053: private static final String SERVER = "server_";
054: private static final boolean GC_LOGGGING = true;
055: private static final boolean ENABLE_DEBUGGER = false;
056: private static final ThreadLocal dsoEnabled = new ThreadLocal() {
057: protected Object initialValue() {
058: return Boolean.TRUE;
059: }
060: };
061:
062: private final int jmxRemotePort;
063: private final int rmiRegistryPort;
064: private final AppServerFactory factory;
065: private AppServer server;
066: private final StandardAppServerParameters parameters;
067: private ServerResult result;
068: private final AppServerInstallation installation;
069: private final Map proxyBuilderMap = new HashMap();
070: private ProxyBuilder proxyBuilder = null;
071: private File workingDir;
072: private String serverInstanceName;
073:
074: public GenericServer(TestConfigObject config,
075: AppServerFactory factory,
076: AppServerInstallation installation,
077: FileSystemPath tcConfigPath, int serverId, File tempDir)
078: throws Exception {
079: this (config, factory, installation, new TcConfigBuilder(
080: tcConfigPath.getFile()), serverId, tempDir);
081: }
082:
083: public GenericServer(TestConfigObject config,
084: AppServerFactory factory,
085: AppServerInstallation installation,
086: TcConfigBuilder tcConfigbuilder, int serverId, File tempDir)
087: throws Exception {
088: this .factory = factory;
089: this .installation = installation;
090: this .rmiRegistryPort = AppServerUtil.getPort();
091: this .jmxRemotePort = AppServerUtil.getPort();
092: this .serverInstanceName = SERVER + serverId;
093: this .parameters = (StandardAppServerParameters) factory
094: .createParameters(serverInstanceName);
095: this .workingDir = new File(installation.sandboxDirectory(),
096: serverInstanceName);
097:
098: File bootJarFile = new File(config.normalBootJar());
099: File tcConfigFile = new File(tempDir, "tc-config.xml");
100: tcConfigbuilder.saveToFile(tcConfigFile);
101:
102: if (dsoEnabled()) {
103: parameters.appendSysProp("tc.base-dir", System
104: .getProperty(TestConfigObject.TC_BASE_DIR));
105: parameters
106: .appendSysProp(
107: "com.tc.l1.modules.repositories",
108: System
109: .getProperty("com.tc.l1.modules.repositories"));
110: parameters.appendSysProp("tc.config", tcConfigFile
111: .getAbsolutePath());
112: parameters.appendJvmArgs("-Xbootclasspath/p:"
113: + bootJarFile.getAbsolutePath());
114: parameters.appendSysProp("tc.classpath",
115: writeTerracottaClassPathFile());
116: parameters.appendSysProp("tc.session.classpath", config
117: .sessionClasspath());
118: }
119:
120: if (!Vm.isIBM() && !(Os.isMac() && Vm.isJDK14())) {
121: parameters.appendJvmArgs("-XX:+HeapDumpOnOutOfMemoryError");
122: }
123:
124: int appId = AppServerFactory.getCurrentAppServerId();
125: // glassfish fails with these options on
126: if (appId != AppServerFactory.GLASSFISH) {
127: parameters.appendSysProp("com.sun.management.jmxremote");
128: parameters.appendSysProp(
129: "com.sun.management.jmxremote.authenticate", false);
130: parameters.appendSysProp(
131: "com.sun.management.jmxremote.ssl", false);
132: parameters.appendSysProp(
133: "com.sun.management.jmxremote.port",
134: this .jmxRemotePort);
135: }
136:
137: parameters.appendSysProp("rmi.registry.port",
138: this .rmiRegistryPort);
139:
140: String[] params = { "tc.classloader.writeToDisk",
141: "tc.objectmanager.dumpHierarchy",
142: "aspectwerkz.deployment.info", "aspectwerkz.details",
143: "aspectwerkz.gen.closures", "aspectwerkz.dump.pattern",
144: "aspectwerkz.dump.closures",
145: "aspectwerkz.dump.factories",
146: "aspectwerkz.aspectmodules" };
147: for (int i = 0; i < params.length; i++) {
148: if (Boolean.getBoolean(params[i])) {
149: parameters.appendSysProp(params[i], true);
150: }
151: }
152:
153: enableDebug(serverId);
154:
155: // app server specific system props
156: switch (appId) {
157: case AppServerFactory.TOMCAT:
158: case AppServerFactory.JBOSS:
159: parameters
160: .appendJvmArgs("-Djvmroute=" + serverInstanceName);
161: break;
162: case AppServerFactory.WEBSPHERE:
163: parameters.appendSysProp(
164: "javax.management.builder.initial", "");
165: break;
166: case AppServerFactory.WEBLOGIC:
167: // bumped up because ContainerHibernateTest was failing with WL 9
168: parameters.appendJvmArgs("-XX:MaxPermSize=128m");
169: parameters.appendJvmArgs("-Xms128m -Xmx256m");
170: break;
171: }
172:
173: proxyBuilderMap.put(RmiServiceExporter.class,
174: new RMIProxyBuilder());
175: proxyBuilderMap.put(HttpInvokerServiceExporter.class,
176: new HttpInvokerProxyBuilder());
177: }
178:
179: private static boolean dsoEnabled() {
180: return ((Boolean) dsoEnabled.get()).booleanValue();
181: }
182:
183: public static void setDsoEnabled(boolean b) {
184: dsoEnabled.set(Boolean.valueOf(b));
185: }
186:
187: public StandardAppServerParameters getServerParameters() {
188: return parameters;
189: }
190:
191: public int getPort() {
192: if (result == null) {
193: throw new IllegalStateException("Server has not started.");
194: }
195: return result.serverPort();
196: }
197:
198: private void enableDebug(int serverId) {
199: if (GC_LOGGGING && !Vm.isIBM()) {
200: parameters.appendJvmArgs("-verbose:gc");
201: parameters.appendJvmArgs("-XX:+PrintGCDetails");
202: parameters.appendJvmArgs("-XX:+PrintGCTimeStamps");
203: parameters.appendJvmArgs("-Xloggc:"
204: + new File(this .installation.sandboxDirectory(),
205: serverInstanceName + "-gc.log")
206: .getAbsolutePath());
207: }
208:
209: if (ENABLE_DEBUGGER) {
210: int debugPort = 8000 + serverId;
211: parameters.appendJvmArgs("-Xdebug");
212: parameters
213: .appendJvmArgs("-Xrunjdwp:server=y,transport=dt_socket,address="
214: + debugPort + ",suspend=y");
215: parameters.appendSysProp("aspectwerkz.transform.verbose",
216: true);
217: parameters.appendSysProp("aspectwerkz.transform.details",
218: true);
219: Banner
220: .warnBanner("Waiting for debugger to connect on port "
221: + debugPort);
222: }
223: }
224:
225: private class RMIProxyBuilder implements ProxyBuilder {
226: public Object createProxy(Class serviceType, String url,
227: Map initialContext) throws Exception {
228: String rmiURL = "rmi://localhost:" + rmiRegistryPort + "/"
229: + url;
230: logger.debug("Getting proxy for: " + rmiRegistryPort
231: + " on " + result.serverPort());
232: Exception e = null;
233: for (int i = 5; i > 0; i--) {
234: try {
235: RmiProxyFactoryBean prfb = new RmiProxyFactoryBean();
236: prfb.setServiceUrl(rmiURL);
237: prfb.setServiceInterface(serviceType);
238: prfb.afterPropertiesSet();
239: return prfb.getObject();
240: } catch (RemoteLookupFailureException lookupException) {
241: e = lookupException;
242: }
243: Thread.sleep(30 * 1000L);
244: }
245: throw e;
246: }
247: }
248:
249: public class HttpInvokerProxyBuilder implements ProxyBuilder {
250: private HttpClient client;
251:
252: public Object createProxy(Class serviceType, String url,
253: Map initialContext) throws Exception {
254: String serviceURL = "http://localhost:"
255: + result.serverPort() + "/" + url;
256: logger.debug("Getting proxy for: " + serviceURL);
257: HttpInvokerProxyFactoryBean prfb = new HttpInvokerProxyFactoryBean();
258: prfb.setServiceUrl(serviceURL);
259: prfb.setServiceInterface(serviceType);
260: CommonsHttpInvokerRequestExecutor executor;
261: if (initialContext != null) {
262: client = (HttpClient) initialContext
263: .get(ProxyBuilder.HTTP_CLIENT_KEY);
264: }
265:
266: if (client == null) {
267: executor = new CommonsHttpInvokerRequestExecutor();
268: client = executor.getHttpClient();
269: if (initialContext != null) {
270: initialContext.put(ProxyBuilder.HTTP_CLIENT_KEY,
271: client);
272: }
273: } else {
274: executor = new CommonsHttpInvokerRequestExecutor(client);
275: }
276:
277: prfb.setHttpInvokerRequestExecutor(executor);
278: prfb.afterPropertiesSet();
279: return prfb.getObject();
280: }
281:
282: public HttpClient getClient() {
283: return client;
284: }
285:
286: public void setClient(HttpClient client) {
287: this .client = client;
288: }
289: }
290:
291: public Object getProxy(Class serviceType, String url)
292: throws Exception {
293: if (this .proxyBuilder != null) {
294: return proxyBuilder.createProxy(serviceType, url, null);
295: }
296: Map initCtx = new HashMap();
297: initCtx.put(ProxyBuilder.EXPORTER_TYPE_KEY,
298: RmiServiceExporter.class);
299: return getProxy(serviceType, url, initCtx);
300: }
301:
302: public Object getProxy(Class serviceType, String url,
303: Map initialContext) throws Exception {
304: Class exporterClass = (Class) initialContext
305: .get(ProxyBuilder.EXPORTER_TYPE_KEY);
306: this .proxyBuilder = (ProxyBuilder) proxyBuilderMap
307: .get(exporterClass);
308: return this .proxyBuilder.createProxy(serviceType, url,
309: initialContext);
310: }
311:
312: public MBeanServerConnection getMBeanServerConnection()
313: throws Exception {
314: JMXConnectorProxy jmxConnectorProxy = new JMXConnectorProxy(
315: "localhost", this .jmxRemotePort);
316: return jmxConnectorProxy.getMBeanServerConnection();
317: }
318:
319: public WebApplicationServer addWarDeployment(
320: Deployment warDeployment, String context) {
321: parameters.addWar(context, warDeployment.getFileSystemPath()
322: .getFile());
323: return this ;
324: }
325:
326: protected void doStart() throws Exception {
327: try {
328: result = getAppServer().start(parameters);
329: } catch (Exception e) {
330: dumpThreadsAndRethrow(e);
331: }
332: }
333:
334: private void dumpThreadsAndRethrow(Exception e) throws Exception {
335: try {
336: if (!Os.isWindows()) {
337: ThreadDump.dumpProcessGroup();
338: }
339: } catch (Throwable t) {
340: t.printStackTrace();
341: } finally {
342: if (true)
343: throw e; // if (true) used to silence warning
344: }
345: }
346:
347: protected void doStop() throws Exception {
348: try {
349: server.stop();
350: } catch (Exception e) {
351: dumpThreadsAndRethrow(e);
352: }
353: }
354:
355: /**
356: * url: /<CONTEXT>/<MAPPING>?params=etc
357: */
358: public WebResponse ping(String url) throws MalformedURLException,
359: IOException, SAXException {
360: return ping(url, new WebConversation());
361: }
362:
363: /**
364: * url: /<CONTEXT>/<MAPPING>?params=etc
365: */
366: public WebResponse ping(String url, WebConversation wc)
367: throws MalformedURLException, IOException, SAXException {
368: String fullURL = "http://localhost:" + result.serverPort()
369: + url;
370: logger.debug("Getting page: " + fullURL);
371:
372: wc.setExceptionsThrownOnErrorStatus(false);
373: WebResponse response = wc.getResponse(fullURL);
374: Assert.assertEquals("Server error:\n" + response.getText(),
375: 200, response.getResponseCode());
376: logger.debug("Got page: " + fullURL);
377: return response;
378: }
379:
380: public void redeployWar(Deployment warDeployment, String context) {
381: getRemoteDeployer().redeploy(
382: makeWar(context, warDeployment.getFileSystemPath()));
383: }
384:
385: public void deployWar(Deployment warDeployment, String context) {
386: getRemoteDeployer().deploy(
387: makeWar(context, warDeployment.getFileSystemPath()));
388: }
389:
390: public void undeployWar(Deployment warDeployment, String context) {
391: getRemoteDeployer().undeploy(
392: makeWar(context, warDeployment.getFileSystemPath()));
393: }
394:
395: // TODO - CARGO specific code
396:
397: private WAR makeWar(String warContext, FileSystemPath warPath) {
398: WAR war = new WAR(warPath.toString());
399: war.setContext(warContext);
400: war.setLogger(new SimpleLogger());
401: return war;
402: }
403:
404: // TODO - Tomcat specific code
405:
406: private Tomcat5xRemoteDeployer getRemoteDeployer() {
407: TomcatRuntimeConfiguration runtimeConfiguration = new TomcatRuntimeConfiguration();
408: runtimeConfiguration.setProperty(RemotePropertySet.USERNAME,
409: "admin");
410: runtimeConfiguration
411: .setProperty(RemotePropertySet.PASSWORD, "");
412: runtimeConfiguration.setProperty(TomcatPropertySet.MANAGER_URL,
413: "http://localhost:" + result.serverPort() + "/manager");
414:
415: Tomcat5xRemoteContainer remoteContainer = new Tomcat5xRemoteContainer(
416: runtimeConfiguration);
417: Tomcat5xRemoteDeployer deployer = new Tomcat5xRemoteDeployer(
418: remoteContainer);
419: return deployer;
420: }
421:
422: // end tomcat specific code
423:
424: private String writeTerracottaClassPathFile() {
425: FileOutputStream fos = null;
426:
427: try {
428: File tempFile = File.createTempFile("tc-classpath",
429: parameters.instanceName());
430: tempFile.deleteOnExit();
431: fos = new FileOutputStream(tempFile);
432: fos.write(System.getProperty("java.class.path").getBytes());
433:
434: return tempFile.toURI().toString();
435: } catch (IOException ioe) {
436: throw new AssertionError(ioe);
437: } finally {
438: IOUtils.closeQuietly(fos);
439: }
440:
441: }
442:
443: public Server restart() throws Exception {
444: stop();
445: start();
446: return this ;
447: }
448:
449: public String toString() {
450: return "Generic Server"
451: + (result != null ? "; port:" + result.serverPort()
452: : "");
453: }
454:
455: public File getWorkingDirectory() {
456: return workingDir;
457: }
458:
459: public AppServer getAppServer() {
460: if (server == null) {
461: server = factory.createAppServer(installation);
462: }
463: return server;
464: }
465: }
|