Author: costin Date: Thu Jul 17 10:14:47 2008 New Revision: 677640 URL: http://svn.apache.org/viewvc?rev=677640&view=rev Log: Add a non-config starter for tomcat. All you need is the classpath including tomcat and the servlets - no config file. There is a requirement for a temp directory - the spec requires that webapps are provided with one I believe. It is possible to remove this or do it lazily or have some default in /tmp, if anyone wants to.
Also adding a context requires some base dir ( can be an empty one ) - so the default servlets points to something. This can also be eliminated by setting a different default servlet - needs to be done if wanted. It is also pretty fast - the unit tests show startup + first request in ~1 sec. Added: tomcat/trunk/java/org/apache/catalina/startup/Tomcat.java (with props) tomcat/trunk/test/org/apache/catalina/startup/ tomcat/trunk/test/org/apache/catalina/startup/TomcatStartupAPITest.java (with props) Added: tomcat/trunk/java/org/apache/catalina/startup/Tomcat.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/Tomcat.java?rev=677640&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/Tomcat.java (added) +++ tomcat/trunk/java/org/apache/catalina/startup/Tomcat.java Thu Jul 17 10:14:47 2008 @@ -0,0 +1,791 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.startup; + +import java.io.File; +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.Realm; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardEngine; +import org.apache.catalina.core.StandardHost; +import org.apache.catalina.core.StandardServer; +import org.apache.catalina.core.StandardService; +import org.apache.catalina.core.StandardWrapper; +import org.apache.catalina.deploy.LoginConfig; +import org.apache.catalina.realm.GenericPrincipal; +import org.apache.catalina.realm.RealmBase; +import org.apache.catalina.session.StandardManager; + +// TODO: lazy init for the temp dir - only when a JSP is compiled or +// get temp dir is called we need to create it. This will avoid the +// need for the baseDir + +// TODO: allow contexts without a base dir - i.e. +// only programmatic. This would disable the default servlet. + +/** + * Minimal tomcat starter for embedding/unit tests. + * + * Tomcat supports multiple styles of configuration and + * startup - the most common and stable is server.xml-based, + * implemented in org.apache.catalina.startup.Bootstrap. + * + * This class is for use in apps that embed tomcat. + * Requirements: + * + * - all tomcat classes and possibly servlets are in the classpath. + * ( for example all is in one big jar, or in eclipse CP, or in any other + * combination ) + * + * - we need one temporary directory for work files + * + * - no config file is required. This class provides methods to + * use if you have a webapp with a web.xml file, but it is + * optional - you can use your own servlets. + * + * This class provides a main() and few simple CLI arguments, + * see setters for doc. It can be used for simple tests and + * demo. + * + * @see TomcatStartupAPITest for examples on how to use this + * @author Costin Manolache + */ +public class Tomcat { + // Single engine, service, server, connector - few cases need more, + // they can use server.xml + protected StandardServer server; + protected StandardService service; + protected StandardEngine engine; + protected Connector connector; // for more - customize the classes + + // To make it a bit easier to config for the common case + // ( one host, one context ). + protected StandardHost host; + + // TODO: it's easy to add support for more hosts - but is it + // really needed ? + + // TODO: allow use of in-memory connector + + protected int port = 8080; + protected String hostname = "localhost"; + protected String basedir; + + // Default in-memory realm, will be set by default on + // created contexts. Can be replaced with setRealm() on + // the context. + protected Realm defaultRealm; + private Map<String, String> userPass = new HashMap<String, String>(); + private Map<String, List<String>> userRoles = + new HashMap<String, List<String>>(); + private Map<String, Principal> userPrincipals = new HashMap<String, Principal>(); + + public Tomcat() { + } + + /** + * Tomcat needs a directory for temp files. This should be the + * first method called. + * + * By default, if this method is not called, we use: + * - system properties - catalina.base, catalina.home + * - $HOME/tomcat.$PORT + * ( /tmp doesn't seem a good choice for security ). + * + * + * TODO: better default ? Maybe current dir ? + * TODO: disable work dir if not needed ( no jsp, etc ). + */ + public void setBaseDir(String basedir) { + this.basedir = basedir; + } + + /** + * Set the port for the default connector. Must + * be called before start(). + */ + public void setPort(int port) { + this.port = port; + } + + /** + * The the hostname of the default host, default is + * 'localhost'. + */ + public void setHostname(String s) { + hostname = s; + } + + /** + * Add a webapp using normal WEB-INF/web.xml if found. + * + * @param contextPath + * @param baseDir + * @return + * @throws ServletException + */ + public StandardContext addWebapp(String contextPath, + String baseDir) throws ServletException { + + return addWebapp(getHost(), contextPath, baseDir); + } + + + /** + * Add a context - programmatic mode, no web.xml used. + * + * API calls equivalent with web.xml: + * + * context-param + * ctx.addParameter("name", "value"); + * + * + * error-page + * ErrorPage ep = new ErrorPage(); + * ep.setErrorCode(500); + * ep.setLocation("/error.html"); + * ctx.addErrorPage(ep); + * + * ctx.addMimeMapping("ext", "type"); + * + * TODO: add the rest + * + * @param host NULL for the 'default' host + * @param contextPath "/" for root context. + * @param dir base dir for the context, for static files. Must exist, + * relative to the server home + */ + public StandardContext addContext(String contextPath, + String baseDir) { + return addContext(getHost(), contextPath, baseDir); + } + + public StandardWrapper addServlet(String contextPath, + String servletName, + String servletClass) { + Container ctx = getHost().findChild(contextPath); + return addServlet((StandardContext) ctx, + servletName, servletClass); + } + + /** + * Equivalent with + * <servlet><servlet-name><servlet-class>. + * + * In general it is better/faster to use the method that takes a + * Servlet as param - this one can be used if the servlet is not + * commonly used, and want to avoid loading all deps. + * ( for example: jsp servlet ) + * + * You can customize the returned servlet, ex: + * + * wrapper.addInitParameter("name", "value"); + */ + public StandardWrapper addServlet(StandardContext ctx, + String servletName, + String servletClass) { + // will do class for name and set init params + StandardWrapper sw = (StandardWrapper)ctx.createWrapper(); + sw.setServletClass(servletClass); + sw.setName(servletName); + ctx.addChild(sw); + + return sw; + } + + /** Use an existing servlet, no class.forName or initialization will be + * performed + */ + public StandardWrapper addServlet(StandardContext ctx, + String servletName, + Servlet servlet) { + // will do class for name and set init params + StandardWrapper sw = new ExistingStandardWrapper(servlet); + sw.setName(servletName); + ctx.addChild(sw); + + return sw; + } + + + /** + * Initialize and start the server. + */ + public void start() throws Exception { + setSilent(); + getServer(); + getConnector(); + server.initialize(); + server.start(); + } + + /** + * Stop the server. + */ + public void stop() throws Exception { + getServer().stop(); + } + + + /** + * Add a user for the in-memory realm. All created apps use this + * by default, can be replaced using setRealm(). + * + */ + public void addUser(String user, String pass) { + userPass.put(user, pass); + } + + /** + * @see addUser + */ + public void addRole(String user, String role) { + List<String> roles = userRoles.get(user); + if (roles == null) { + roles = new ArrayList<String>(); + userRoles.put(user, roles); + } + roles.add(role); + } + + // ------- Extra customization ------- + // You can tune individual tomcat objects, using internal APIs + + /** + * Get the default http connector. You can set more + * parameters - the port is already initialized. + * + * Alternatively, you can construct a Connector and set any params, + * then call addConnector(Connector) + * + * @return A connector object that can be customized + */ + public Connector getConnector() throws Exception { + getServer(); + if (connector != null) { + return connector; + } + // This will load Apr connector if available, + // default to nio. I'm having strange problems with apr + // and for the use case the speed benefit wouldn't matter. + + //connector = new Connector("HTTP/1.1"); + connector = new Connector("org.apache.coyote.http11.Http11Protocol"); + connector.setPort(port); + service.addConnector( connector ); + return connector; + } + + public void setConnector(Connector connector) { + this.connector = connector; + } + + /** + * Get the service object. Can be used to add more + * connectors and few other global settings. + */ + public StandardService getService() { + getServer(); + return service; + } + + /** + * Sets the current host - all future webapps will + * be added to this host. When tomcat starts, the + * host will be the default host. + * + * @param host + */ + public void setHost(StandardHost host) { + this.host = host; + } + + public StandardHost getHost() { + if (host == null) { + host = new StandardHost(); + host.setName(hostname); + + getEngine().addChild( host ); + } + return host; + } + + /** + * Set a custom realm for auth. If not called, a simple + * default will be used, using an internal map. + * + * Must be called before adding a context. + */ + public void setDefaultRealm(Realm realm) { + defaultRealm = realm; + } + + + /** + * Access to the engine, for further customization. + */ + public StandardEngine getEngine() { + if(engine == null ) { + getServer(); + engine = new StandardEngine(); + engine.setName( "default" ); + engine.setDefaultHost(hostname); + service.setContainer(engine); + } + return engine; + } + + /** + * Get the server object. You can add listeners and + * few more customizations. + */ + public StandardServer getServer() { + + if (server != null) { + return server; + } + + initBaseDir(); + + System.setProperty("catalina.useNaming", "false"); + + server = new StandardServer(); + server.setPort( -1 ); + + service = new StandardService(); + service.setName("Tomcat"); + server.addService( service ); + return server; + } + + public StandardContext addContext(StandardHost host, + String contextPath, + String dir) { + silence(contextPath); + StandardContext ctx = new StandardContext(); + ctx.setPath( contextPath ); + ctx.setDocBase(dir); + ctx.addLifecycleListener(new FixContextListener()); + + if (host == null) { + host = getHost(); + } + host.addChild(ctx); + return ctx; + } + + public StandardContext addWebapp(StandardHost host, + String url, String path) + throws ServletException { + silence(url); + + StandardContext ctx = new StandardContext(); + ctx.setPath( url ); + ctx.setDocBase(path); + if (defaultRealm == null) { + initSimpleAuth(); + } + ctx.setRealm(defaultRealm); + initWebappDefaults(ctx); + + ContextConfig ctxCfg = new ContextConfig(); + ctx.addLifecycleListener( ctxCfg ); + // prevent it from looking ( if it finds one - it'll have dup error ) + ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML"); + + if (host == null) { + host = getHost(); + } + host.addChild(ctx); + + return ctx; + } + + + + + + // ---------- Helper methods and classes ------------------- + + /** + * Initialize an in-memory realm. You can replace it + * for contexts with a real one. + */ + protected void initSimpleAuth() { + defaultRealm = new RealmBase() { + @Override + protected String getName() { + return "Simple"; + } + + @Override + protected String getPassword(String username) { + return userPass.get(username); + } + + @Override + protected Principal getPrincipal(String username) { + Principal p = userPrincipals.get(username); + if (p == null) { + String pass = userPass.get(username); + if (pass != null) { + p = new GenericPrincipal(this, username, pass, + userRoles.get(username)); + userPrincipals.put(username, p); + } + } + return p; + } + + }; + } + + protected void initBaseDir() { + if (basedir == null) { + basedir = System.getProperty("catalina.base"); + } + if (basedir == null) { + basedir = System.getProperty("catalina.home"); + } + if (basedir == null) { + // Create a temp dir. + basedir = System.getProperty("user.dir") + + "/tomcat." + port; + File home = new File(basedir); + home.mkdir(); + if (!home.isAbsolute()) { + try { + basedir = home.getCanonicalPath(); + } catch (IOException e) { + basedir = home.getAbsolutePath(); + } + } + } + System.setProperty("catalina.home", basedir); + System.setProperty("catalina.base", basedir); + } + + static String[] silences = new String[] { + "org.apache.coyote.http11.Http11Protocol", + "org.apache.catalina.core.StandardService", + "org.apache.catalina.core.StandardEngine", + "org.apache.catalina.startup.ContextConfig", + "org.apache.catalina.core.ApplicationContext", + }; + + public void setSilent() { + for (String s : silences) { + Logger.getLogger(s).setLevel(Level.WARNING); + } + } + + private void silence(String ctx) { + String base = "org.apache.catalina.core.ContainerBase.[default].["; + base += getHost().getName(); + base += "].["; + base += ctx; + base += "]"; + Logger.getLogger(base).setLevel(Level.WARNING); + } + + /** Init default servlets for the context. This should be the programmatic + * equivalent of the default web.xml. + * + * TODO: in normal tomcat, if default-web.xml is not found, use this + * method + */ + protected void initWebappDefaults(StandardContext ctx) { + // Default servlet + StandardWrapper servlet = + addServlet(ctx, "default", + //new DefaultServlet()); + // Or: + "org.apache.catalina.servlets.DefaultServlet"); + servlet.addInitParameter("listings", "false"); + servlet.setLoadOnStartup(1); + + // class name - to avoid loading all deps + servlet = addServlet(ctx, "jsp", + "org.apache.jasper.servlet.JspServlet"); + servlet.addInitParameter("fork", "false"); + servlet.addInitParameter("xpoweredBy", "false"); + + // in default web.xml - but not here, only needed if you have + // jsps. + //servlet.setLoadOnStartup(3); + + ctx.addServletMapping("/", "default"); + ctx.addServletMapping("*.jsp", "jsp"); + ctx.addServletMapping("*.jspx", "jsp"); + // Sessions + ctx.setManager( new StandardManager()); + ctx.setSessionTimeout(30); + + // TODO: read mime from /etc/mime.types on linux, or some + // resource + for (int i = 0; i < DEFAULT_MIME_MAPPINGS.length; ) { + ctx.addMimeMapping(DEFAULT_MIME_MAPPINGS[i++], + DEFAULT_MIME_MAPPINGS[i++]); + } + ctx.addWelcomeFile("index.html"); + ctx.addWelcomeFile("index.htm"); + ctx.addWelcomeFile("index.jsp"); + + ctx.setLoginConfig( new LoginConfig("NONE", null, null, null)); + + // TODO: set a default realm, add simple API to add users + } + + + /** Fix startup sequence - required if you don't use web.xml. + * + * The start() method in context will set 'configured' to false - and + * expects a listener to set it back to true. + */ + public static class FixContextListener implements LifecycleListener { + + public void lifecycleEvent(LifecycleEvent event) { + try { + Context context = (Context) event.getLifecycle(); + if (event.getType().equals(Lifecycle.START_EVENT)) { + context.setConfigured(true); + } + } catch (ClassCastException e) { + return; + } + } + + } + + /** Helper class for wrapping existing servlets. This disables servlet + * lifecycle and normal reloading, but also reduces overhead and provide + * more direct control over the servlet. + */ + public static class ExistingStandardWrapper extends StandardWrapper { + private Servlet existing; + boolean init = false; + + public ExistingStandardWrapper( Servlet existing ) { + this.existing = existing; + } + public synchronized Servlet loadServlet() throws ServletException { + if (!init) { + existing.init(facade); + init = true; + } + return existing; + + } + public long getAvailable() { + return 0; + } + public boolean isUnavailable() { + return false; + } + } + + /** + * TODO: would a properties resource be better ? Or just parsing + * /etc/mime.types ? + * This is needed because we don't use the default web.xml, where this + * is encoded. + */ + public static final String[] DEFAULT_MIME_MAPPINGS = { + "abs", "audio/x-mpeg", + "ai", "application/postscript", + "aif", "audio/x-aiff", + "aifc", "audio/x-aiff", + "aiff", "audio/x-aiff", + "aim", "application/x-aim", + "art", "image/x-jg", + "asf", "video/x-ms-asf", + "asx", "video/x-ms-asf", + "au", "audio/basic", + "avi", "video/x-msvideo", + "avx", "video/x-rad-screenplay", + "bcpio", "application/x-bcpio", + "bin", "application/octet-stream", + "bmp", "image/bmp", + "body", "text/html", + "cdf", "application/x-cdf", + "cer", "application/x-x509-ca-cert", + "class", "application/java", + "cpio", "application/x-cpio", + "csh", "application/x-csh", + "css", "text/css", + "dib", "image/bmp", + "doc", "application/msword", + "dtd", "application/xml-dtd", + "dv", "video/x-dv", + "dvi", "application/x-dvi", + "eps", "application/postscript", + "etx", "text/x-setext", + "exe", "application/octet-stream", + "gif", "image/gif", + "gtar", "application/x-gtar", + "gz", "application/x-gzip", + "hdf", "application/x-hdf", + "hqx", "application/mac-binhex40", + "htc", "text/x-component", + "htm", "text/html", + "html", "text/html", + "hqx", "application/mac-binhex40", + "ief", "image/ief", + "jad", "text/vnd.sun.j2me.app-descriptor", + "jar", "application/java-archive", + "java", "text/plain", + "jnlp", "application/x-java-jnlp-file", + "jpe", "image/jpeg", + "jpeg", "image/jpeg", + "jpg", "image/jpeg", + "js", "text/javascript", + "jsf", "text/plain", + "jspf", "text/plain", + "kar", "audio/x-midi", + "latex", "application/x-latex", + "m3u", "audio/x-mpegurl", + "mac", "image/x-macpaint", + "man", "application/x-troff-man", + "mathml", "application/mathml+xml", + "me", "application/x-troff-me", + "mid", "audio/x-midi", + "midi", "audio/x-midi", + "mif", "application/x-mif", + "mov", "video/quicktime", + "movie", "video/x-sgi-movie", + "mp1", "audio/x-mpeg", + "mp2", "audio/x-mpeg", + "mp3", "audio/x-mpeg", + "mp4", "video/mp4", + "mpa", "audio/x-mpeg", + "mpe", "video/mpeg", + "mpeg", "video/mpeg", + "mpega", "audio/x-mpeg", + "mpg", "video/mpeg", + "mpv2", "video/mpeg2", + "ms", "application/x-wais-source", + "nc", "application/x-netcdf", + "oda", "application/oda", + "odb", "application/vnd.oasis.opendocument.database", + "odc", "application/vnd.oasis.opendocument.chart", + "odf", "application/vnd.oasis.opendocument.formula", + "odg", "application/vnd.oasis.opendocument.graphics", + "odi", "application/vnd.oasis.opendocument.image", + "odm", "application/vnd.oasis.opendocument.text-master", + "odp", "application/vnd.oasis.opendocument.presentation", + "ods", "application/vnd.oasis.opendocument.spreadsheet", + "odt", "application/vnd.oasis.opendocument.text", + "ogg", "application/ogg", + "otg ", "application/vnd.oasis.opendocument.graphics-template", + "oth", "application/vnd.oasis.opendocument.text-web", + "otp", "application/vnd.oasis.opendocument.presentation-template", + "ots", "application/vnd.oasis.opendocument.spreadsheet-template ", + "ott", "application/vnd.oasis.opendocument.text-template", + "pbm", "image/x-portable-bitmap", + "pct", "image/pict", + "pdf", "application/pdf", + "pgm", "image/x-portable-graymap", + "pic", "image/pict", + "pict", "image/pict", + "pls", "audio/x-scpls", + "png", "image/png", + "pnm", "image/x-portable-anymap", + "pnt", "image/x-macpaint", + "ppm", "image/x-portable-pixmap", + "ppt", "application/powerpoint", + "ps", "application/postscript", + "psd", "image/x-photoshop", + "qt", "video/quicktime", + "qti", "image/x-quicktime", + "qtif", "image/x-quicktime", + "ras", "image/x-cmu-raster", + "rdf", "application/rdf+xml", + "rgb", "image/x-rgb", + "rm", "application/vnd.rn-realmedia", + "roff", "application/x-troff", + "rtf", "application/rtf", + "rtx", "text/richtext", + "sh", "application/x-sh", + "shar", "application/x-shar", + /*"shtml", "text/x-server-parsed-html",*/ + "smf", "audio/x-midi", + "sit", "application/x-stuffit", + "snd", "audio/basic", + "src", "application/x-wais-source", + "sv4cpio", "application/x-sv4cpio", + "sv4crc", "application/x-sv4crc", + "swf", "application/x-shockwave-flash", + "t", "application/x-troff", + "tar", "application/x-tar", + "tcl", "application/x-tcl", + "tex", "application/x-tex", + "texi", "application/x-texinfo", + "texinfo", "application/x-texinfo", + "tif", "image/tiff", + "tiff", "image/tiff", + "tr", "application/x-troff", + "tsv", "text/tab-separated-values", + "txt", "text/plain", + "ulw", "audio/basic", + "ustar", "application/x-ustar", + "vxml", "application/voicexml+xml", + "xbm", "image/x-xbitmap", + "xht", "application/xhtml+xml", + "xhtml", "application/xhtml+xml", + "xml", "application/xml", + "xpm", "image/x-xpixmap", + "xsl", "application/xml", + "xslt", "application/xslt+xml", + "xul", "application/vnd.mozilla.xul+xml", + "xwd", "image/x-xwindowdump", + "wav", "audio/x-wav", + "svg", "image/svg+xml", + "svgz", "image/svg+xml", + "vsd", "application/x-visio", + "wbmp", "image/vnd.wap.wbmp", + "wml", "text/vnd.wap.wml", + "wmlc", "application/vnd.wap.wmlc", + "wmls", "text/vnd.wap.wmlscript", + "wmlscriptc", "application/vnd.wap.wmlscriptc", + "wmv", "video/x-ms-wmv", + "wrl", "x-world/x-vrml", + "wspolicy", "application/wspolicy+xml", + "Z", "application/x-compress", + "z", "application/x-compress", + "zip", "application/zip", + "xls", "application/vnd.ms-excel", + "doc", "application/vnd.ms-word", + "ppt", "application/vnd.ms-powerpoint" + }; +} Propchange: tomcat/trunk/java/org/apache/catalina/startup/Tomcat.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/org/apache/catalina/startup/TomcatStartupAPITest.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TomcatStartupAPITest.java?rev=677640&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/startup/TomcatStartupAPITest.java (added) +++ tomcat/trunk/test/org/apache/catalina/startup/TomcatStartupAPITest.java Thu Jul 17 10:14:47 2008 @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.startup; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import junit.framework.TestCase; + +import org.apache.catalina.core.StandardContext; +import org.apache.tomcat.util.buf.ByteChunk; + +public class TomcatStartupAPITest extends TestCase { + Tomcat tomcat; + // if you run in eclipse - or tomcat src dir + String base = "./"; + File tempDir; + static int port = 8001; + long t0; + + /** + * Simple servlet to test in-line registration + */ + public static class HelloWorld extends HttpServlet { + public void doGet(HttpServletRequest req, HttpServletResponse res) + throws IOException { + res.getWriter().write("Hello world"); + } + } + + public void setUp() throws Exception { + t0 = System.currentTimeMillis(); + tempDir = new File("output/tmp"); + tempDir.mkdir(); + + tomcat = new Tomcat(); + tomcat.setBaseDir(tempDir.getAbsolutePath()); + + // If each test is running on same port - they + // may interfere with each other (on unix at least) + port++; + tomcat.setPort(port); + } + + public void tearDown() throws Exception { + tomcat.stop(); + System.err.println("Test time: " + + (System.currentTimeMillis() - t0)); + } + + /** + * Start tomcat with a single context and one + * servlet - all programmatic, no server.xml or + * web.xml used. + * + * @throws Exception + */ + public void testProgrammatic() throws Exception { + + StandardContext ctx = + tomcat.addContext("/", + tempDir.getAbsolutePath()); + // You can customize the context by calling + // its API + + tomcat.addServlet(ctx, "myServlet", + new HelloWorld()); + ctx.addServletMapping("/", "myServlet"); + + tomcat.start(); + + ByteChunk res = getUrl("http://localhost:" + port + "/"); + assertEquals(res.toString(), "Hello world"); + } + + public void testSingleWebapp() throws Exception { + // Currently in sandbox/tomcat-lite + File appDir = + new File(base + "output/build/webapps/examples"); + // app dir is relative to server home + StandardContext ctx = + tomcat.addWebapp(null, "/examples", + appDir.getAbsolutePath()); + + ctx.start(); + tomcat.start(); + + ByteChunk res = getUrl("http://localhost:" + port + "/examples/servlets/servlet/HelloWorldExample"); + assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0); + } + + public void testLaunchTime() throws Exception { + tomcat.addContext(null, "/", base); + tomcat.start(); + } + + /** + * Wrapper for getting the response. + */ + public static ByteChunk getUrl(String path) throws IOException { + ByteChunk out = new ByteChunk(); + getUrl(path, out, null); + return out; + } + + public static int getUrl(String path, + ByteChunk out, + Map<String, List<String>> resHead) throws IOException { + URL url = new URL(path); + HttpURLConnection connection = + (HttpURLConnection) url.openConnection(); + connection.setReadTimeout(100000); + connection.connect(); + int rc = connection.getResponseCode(); + if (resHead != null) { + Map<String, List<String>> head = connection.getHeaderFields(); + resHead.putAll(head); + } + InputStream is = connection.getInputStream(); + BufferedInputStream bis = new BufferedInputStream(is); + byte[] buf = new byte[2048]; + int rd = 0; + while((rd = bis.read(buf)) > 0) { + out.append(buf, 0, rd); + } + return rc; + } +} Propchange: tomcat/trunk/test/org/apache/catalina/startup/TomcatStartupAPITest.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]