1 16 package com.google.gwt.dev.shell.tomcat; 17 18 import com.google.gwt.core.ext.TreeLogger; 19 import com.google.gwt.dev.util.FileOracle; 20 import com.google.gwt.dev.util.FileOracleFactory; 21 import com.google.gwt.util.tools.Utility; 22 23 import org.apache.catalina.Connector; 24 import org.apache.catalina.ContainerEvent; 25 import org.apache.catalina.ContainerListener; 26 import org.apache.catalina.Engine; 27 import org.apache.catalina.LifecycleException; 28 import org.apache.catalina.Logger; 29 import org.apache.catalina.core.StandardContext; 30 import org.apache.catalina.core.StandardHost; 31 import org.apache.catalina.startup.Embedded; 32 import org.apache.catalina.startup.HostConfig; 33 import org.apache.coyote.tomcat5.CoyoteConnector; 34 35 import java.io.File ; 36 import java.io.FileOutputStream ; 37 import java.io.IOException ; 38 import java.io.InputStream ; 39 import java.lang.reflect.Field ; 40 import java.net.InetAddress ; 41 import java.net.ServerSocket ; 42 import java.net.URL ; 43 44 47 public class EmbeddedTomcatServer { 48 49 static EmbeddedTomcatServer sTomcat; 50 51 public static int getPort() { 52 return sTomcat.port; 53 } 54 55 public static synchronized String start(TreeLogger topLogger, int port, 56 File outDir) { 57 if (sTomcat != null) { 58 throw new IllegalStateException ("Embedded Tomcat is already running"); 59 } 60 61 try { 62 new EmbeddedTomcatServer(topLogger, port, outDir); 63 return null; 64 } catch (LifecycleException e) { 65 String msg = e.getMessage(); 66 if (msg != null && msg.indexOf("already in use") != -1) { 67 msg = "Port " 68 + port 69 + " is already is use; you probably still have another session active"; 70 } else { 71 msg = "Unable to start the embedded Tomcat server; double-check that your configuration is valid"; 72 } 73 return msg; 74 } 75 } 76 77 public static synchronized void stop() { 80 if (sTomcat != null) { 81 try { 82 sTomcat.catEmbedded.stop(); 83 } catch (LifecycleException e) { 84 } finally { 88 sTomcat = null; 89 } 90 } 91 } 92 93 101 private static int computeLocalPort(Connector connector) { 102 Throwable caught = null; 103 try { 104 Field phField = CoyoteConnector.class.getDeclaredField("protocolHandler"); 105 phField.setAccessible(true); 106 Object protocolHandler = phField.get(connector); 107 108 Field epField = protocolHandler.getClass().getDeclaredField("ep"); 109 epField.setAccessible(true); 110 Object endPoint = epField.get(protocolHandler); 111 112 Field ssField = endPoint.getClass().getDeclaredField("serverSocket"); 113 ssField.setAccessible(true); 114 ServerSocket serverSocket = (ServerSocket ) ssField.get(endPoint); 115 116 return serverSocket.getLocalPort(); 117 } catch (SecurityException e) { 118 caught = e; 119 } catch (NoSuchFieldException e) { 120 caught = e; 121 } catch (IllegalArgumentException e) { 122 caught = e; 123 } catch (IllegalAccessException e) { 124 caught = e; 125 } 126 throw new RuntimeException ( 127 "Failed to retrieve the startup port from Embedded Tomcat", caught); 128 } 129 130 private Embedded catEmbedded; 131 132 private Engine catEngine; 133 134 private StandardHost catHost = null; 135 136 private int port; 137 138 private final TreeLogger startupBranchLogger; 139 140 private EmbeddedTomcatServer(final TreeLogger topLogger, int listeningPort, 141 final File outDir) throws LifecycleException { 142 if (topLogger == null) { 143 throw new NullPointerException ("No logger specified"); 144 } 145 146 final TreeLogger logger = topLogger.branch(TreeLogger.INFO, 147 "Starting HTTP on port " + listeningPort, null); 148 149 startupBranchLogger = logger; 150 151 sTomcat = this; 161 162 File topWorkDir = new File (System.getProperty("user.dir")); 165 166 String catBase = System.getProperty("catalina.base"); 169 if (catBase == null) { 170 catBase = generateDefaultCatalinaBase(logger, topWorkDir); 171 System.setProperty("catalina.base", catBase); 172 } 173 174 logger.log(TreeLogger.DEBUG, "catalina.base = " + catBase, null); 177 178 String adapterClassName = CommonsLoggerAdapter.class.getName(); 181 System.setProperty("org.apache.commons.logging.Log", adapterClassName); 182 183 Logger catalinaLogger = new CatalinaLoggerAdapter(topLogger); 186 187 catEmbedded = new Embedded(); 190 catEmbedded.setDebug(0); 191 catEmbedded.setLogger(catalinaLogger); 192 193 catEngine = catEmbedded.createEngine(); 196 catEngine.setName("gwt"); 197 catEngine.setDefaultHost("localhost"); 198 199 String appBase = catBase + "/webapps"; 203 catHost = (StandardHost) catEmbedded.createHost("localhost", appBase); 204 205 HostConfig hostConfig = new HostConfig(); 208 catHost.addLifecycleListener(hostConfig); 209 210 catHost.addContainerListener(new ContainerListener() { 214 public void containerEvent(ContainerEvent event) { 215 if (StandardHost.PRE_INSTALL_EVENT.equals(event.getType())) { 216 StandardContext webapp = (StandardContext) event.getData(); 217 publishShellLoggerAttribute(logger, topLogger, webapp); 218 publishShellOutDirAttribute(logger, outDir, webapp); 219 } 220 } 221 }); 222 223 catEngine.addChild(catHost); 226 catEngine.setDefaultHost(catHost.getName()); 227 228 catEmbedded.addEngine(catEngine); 231 InetAddress nullAddr = null; 232 Connector connector = catEmbedded.createConnector(nullAddr, listeningPort, 233 false); 234 catEmbedded.addConnector(connector); 235 236 catEmbedded.start(); 238 port = computeLocalPort(connector); 239 240 if (port != listeningPort) { 241 logger.log(TreeLogger.INFO, "HTTP listening on port " + port, null); 242 } 243 } 244 245 public TreeLogger getLogger() { 246 return startupBranchLogger; 247 } 248 249 252 private void copyFileNoOverwrite(TreeLogger logger, FileOracle fileOracle, 253 String srcResName, File catBase) { 254 255 File dest = new File (catBase, srcResName); 256 InputStream is = null; 257 FileOutputStream os = null; 258 try { 259 URL srcRes = fileOracle.find(srcResName); 260 if (srcRes == null) { 261 logger.log(TreeLogger.TRACE, "Cannot find: " + srcResName, null); 262 return; 263 } 264 265 long srcLastModified = srcRes.openConnection().getLastModified(); 268 long dstLastModified = dest.lastModified(); 269 270 if (srcLastModified < dstLastModified) { 271 logger.log(TreeLogger.TRACE, "Source is older than existing: " 274 + dest.getAbsolutePath(), null); 275 return; 276 } else if (srcLastModified == dstLastModified) { 277 return; 280 } 281 282 File destParent = dest.getParentFile(); 285 if (destParent != null) { 286 destParent.mkdirs(); 288 } 289 290 is = srcRes.openStream(); 293 os = new FileOutputStream (dest); 294 295 Utility.streamOut(is, os, 1024); 298 299 Utility.close(os); 302 dest.setLastModified(srcLastModified); 303 304 logger.log(TreeLogger.TRACE, "Wrote: " + dest.getAbsolutePath(), null); 305 } catch (IOException e) { 306 logger.log(TreeLogger.WARN, "Failed to write: " + dest.getAbsolutePath(), 307 e); 308 } finally { 309 Utility.close(is); 310 Utility.close(os); 311 } 312 } 313 314 318 private String generateDefaultCatalinaBase(TreeLogger logger, File workDir) { 319 logger = logger.branch( 320 TreeLogger.TRACE, 321 "Property 'catalina.base' not specified; checking for a standard catalina base image instead", 322 null); 323 324 FileOracleFactory fof = new FileOracleFactory(); 328 final String tomcatEtcDir = "com/google/gwt/dev/etc/tomcat/"; 329 fof.addRootPackage(tomcatEtcDir, null); 330 FileOracle fo = fof.create(logger); 331 if (fo.isEmpty()) { 332 logger.log(TreeLogger.WARN, "Could not find " + tomcatEtcDir, null); 333 return null; 334 } 335 336 File catBase = new File (workDir, "tomcat"); 337 String [] allChildren = fo.getAllFiles(); 338 for (int i = 0; i < allChildren.length; i++) { 339 String src = allChildren[i]; 340 copyFileNoOverwrite(logger, fo, src, catBase); 341 } 342 343 return catBase.getAbsolutePath(); 344 } 345 346 private void publishAttributeToWebApp(TreeLogger logger, 347 StandardContext webapp, String attrName, Object attrValue) { 348 logger.log(TreeLogger.TRACE, "Adding attribute '" + attrName 349 + "' to web app '" + webapp.getName() + "'", null); 350 webapp.getServletContext().setAttribute(attrName, attrValue); 351 } 352 353 357 private void publishShellLoggerAttribute(TreeLogger logger, 358 TreeLogger loggerToPublish, StandardContext webapp) { 359 final String attr = "com.google.gwt.dev.shell.logger"; 360 publishAttributeToWebApp(logger, webapp, attr, loggerToPublish); 361 } 362 363 367 private void publishShellOutDirAttribute(TreeLogger logger, 368 File outDirToPublish, StandardContext webapp) { 369 final String attr = "com.google.gwt.dev.shell.outdir"; 370 publishAttributeToWebApp(logger, webapp, attr, outDirToPublish); 371 } 372 } 373 | Popular Tags |