One of the more anticipated features of Tomcat 7 is the ability to run as an embedded server like Jetty. We use Tomcat 6 in production, but embedded Jetty more and more for running and testing during development (in Eclipse). The Tomcat 7 beta has been out for a while, but there seems to be little documentation out there on how to embed it, other than some suggestions to look at the unit tests for examples. So that’s what I did! First, here is the guts of our original Main method in Jetty:
public static void main(String[] args) throws Exception {
String weppAppHome = args[0];
Integer port = Integer.valueOf(args[1]);
Server server = new Server(port);
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/myapp");
webapp.setCompactPath(true);
webapp.setDescriptor(weppAppHome + "/WEB-INF/web.xml");
webapp.setResourceBase(weppAppHome);
webapp.setParentLoaderPriority(true);
server.setHandler(webapp);
server.start();
server.join();
}
To switch to Tomcat 7, add these dependencies to your pom.xml:
- org.apache.tomcat:tomcat-catalina:7.0.0
- org.apache.tomcat.embed:tomcat-embed-core:7.0.0
- org.apache.tomcat:tomcat-jasper:7.0.0
Note that the tomcat-jasper pom.xml lists org.eclipse.jdt:ecj:3.5.1 as a dependency, which can’t be found. I had to replace it in our local repository with org.eclipse.jdt.core.compiler:ecj:3.5.1.
UPDATE: This dependency has been fixed in the latest version, 7.0.21.
Here is the Tomcat 7 version:
public static void main(String[] args) throws Exception {
String appBase = args[0];
Integer port = Integer.valueOf(args[1]);
Tomcat tomcat = new Tomcat();
tomcat.setPort(port);
tomcat.setBaseDir(".");
tomcat.getHost().setAppBase(appBase);
String contextPath = "/myapp";
// Add AprLifecycleListener
StandardServer server = (StandardServer)tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
server.addLifecycleListener(listener);
tomcat.addWebapp(contextPath, appBase);
tomcat.start();
tomcat.getServer().await();
}
Without the await() call at the end, the server quits right after it starts, which you may or may not want.
Launch it! We normally set up a launch configuration in Eclipse to run it. It’s also easy to run on the command-line using java -jar after you’ve built your jar.
Pingback: tomcat 7.0 embedded usage : 14024
Tomcat 6 / GREAT!!!!!
Great post.
the problem with org.eclipse.jdt:ecj:3.5.1 persists in tomcat 7.0.2, which needs org.eclipse.jdt:ecj:3.6 (and maven cant find it). Tomcat 7 has wrong pom.xml or is it expecting another repo?
As far as I can tell, it seems to be a problem in their pom, but I haven’t investigated it further or taken it up on the tomcat forums.
ObjectWeb
ow2.org
http://maven.ow2.org/maven2/
default
I’ve just search many post and finally find this.it’s great help!
Hi,
I tried your tomcat 7 example, but when I try to open the URL http://localhost:port/myapp I get a 404 page from tomcat.
What am I doing wrong?
I assume you’re putting a real number into the url instead of the word, ‘port’? It’s hard to say off-hand. I would double check that your appBase is valid.
Do you have a running example with source, not sure what to put in or pass in for the appBase or BaseDir settings.
I’m trying to run this from inside Eclipse.
Ok, I finally got this to work in Eclipse but I had to add the following things.
appBase is set to “.”
BaseDir is set to “.”
contextPath is set to “” or “/” (either work)
For Servlets to be found I had to add this :
org.apache.catalina.Context ctx = tomcat.addWebapp(contextPath, appBase);
Tomcat.addServlet(ctx, “test”, “com.eonegroup.servlets.TestBuild”);
ctx.addServletMapping(“/test”, “test”);
When I export to a JAR and run it the jsp’s aren’t found, unless I put them into the directory with my JAR instead of in it and of course the Tomcat Embedded Jars aren’t in my main JAR so I have to create a script to run it all by adding all of them to the CLASSPATH then running my main method. There has to be an easier way to do this, right?
at last, the API looks human-kind
but still you have to put tons of JARs to into the CLASSPATH to make it work whereas with Jetty it is just jetty.jar..
Pingback: Embedding Tomcat 7 in your App | Outrospective.org
Hi,
The minimal code i found to load a servlet on embedded tomcat 7.
Need embed core / embed logging juli / embed logging log4j jar for tomcat and servlet-api.jar for servlet.
MyServlet is a classical HttpServlet class
public class TestServlet {
public static void main(String[] args) {
try {
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir(".");
tomcat.setPort(8090);
File docBase = new File(".");
Context ctxt = tomcat.addContext("/", docBase.getAbsolutePath());
Tomcat.addServlet(ctxt, "servletName", new MyServlet());
ctxt.addServletMapping("/*", "servletName");
tomcat.start();
tomcat.getServer().await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
It gives me the following error:
Error: Could not find or load main class TestServlet
Works great. What’s the best way to stop the Tomcat server?
I’m usually running the embedded server while debugging in Eclipse so I just kill it from there. The org.apache.catalina.startup.Tomcat class has a stop method which you should be able to use to stop it more gracefully.
I have the following in my unit test:
@BeforeClass
public static void setUpClass() throws ServletException, LifecycleException, MalformedURLException {
tomcat = new Tomcat();
tomcat.setBaseDir(“.”);
tomcat.setPort(8084);
Context ctx = tomcat.addWebapp(“/”, System.getProperty(“user.dir”) + “/build/web”);
tomcat.setHostname(“localhost”);
File contextFile = new File(System.getProperty(“user.dir”) + “/build/web/META-INF/context.xml”);
ctx.setConfigFile(contextFile.toURI().toURL());
tomcat.enableNaming();
tomcat.start();
tomcat.getServer().await();
}
Without the line: `tomcat.getServer().await();`, Tomcat simply stops. Adding `tomcat.getServer().await();` my unit tests don’t execute.. any idea?
You might try shutting down the server in your teardown method. Also, you could check out unit tests in the tomcat source code, which is easy to download, for examples.
This worked great until I integrated it into my application. Now it wont start-up and it looks like it’s related to class loading. Any easy way to enable a separate class loader for embedded Tomcat or is there a better way?
I would start by trying to figure out the specific classes that are having problems. It seems common to have problems with different versions of servlet-api, for example.
I have the same problem. Did you solve it ?
Anyway to point a war file to be deployed for something like tomcat.addWebapp versus a directory or do I have to expand the war file myself?
Nevermind, I figured it out. I was passing in an invalid path to tomcat.addWebapp which caused my war file to not be found.
I haven’t used it, but it looks like you could call addWebApp on a path to a war file in your appBase directory. You might also look at the setUnpackWARs(true), setAutoDeploy(true), and/or setDeployOnStartup(true) methods on the host object.
We are using tomcat 6 and org.apache.catalina.startup.Embedded in our server application, but class Embedded was deprecated in tomcat 7 and was replaced by org.apache.catalina.startup.Tomcat. This class has only one connector , but we use 2 different connectors for http and https (different ports) . What to do ?
You could try creating a Connector object for https, then getting the service object off the Tomcat object and calling addConnector() on it. Haven’t tried it but it seems plausible…
Hey,
I’ve got to the point where I can run the main class in eclipse but it failed because it couldn’t find the /WEB-INF/myapp-servlet.xml.
Any hint?
Are you setting the appBase correctly? You WEB-INF folder should be in there.
Great post, but unfortunately everything seems to have changed, again. Per the javadoc in Tomcat 7.0.21, you should use org.apache.catalina.startup.Tomcat for embedded apps. With that, you can supposedly set up everything internal to your app, with no need for external Tomcat config files.
Personally, I don’t trust it not to change. Instead, I’m going to try using org.apache.catalina.startup.Bootstrap, pretending it’s called by the scripts, but really calling main() or whatever from my app. I’ll do my config using the expected Tomcat files and dir structure. Basically, the only thing “different” I’ll be doing is calling Bootstrap from my app’s JVM… That way startup/shutdown is synchronized by nature of the single JVM (I don’t have to worry about coordinating two processes… my app vs. Tomcat), and my JSPs will have access to the objects in my app living in the same JVM, without requiring some form of RMI.
What Post did you read?!?!?! Does this code snippet not use org.apache.catalina.startup.Tomcat !!!!
Pingback: 在应用程序中嵌入Tomcat7 | Wenming's Blog
Thanks, I have tried your code snippet with tomcat 7.0.21, The problem with missing org.eclipse.jdt.core.compiler:ecj dependency has been resolved in the new version.
Thanks for pointing that out.
Take a look at http://blog.afkham.org/2011/09/embedding-tomcat-7-bettertomcat.html. This blogpost provides code segments which show how simple it is to embed Tomcat 7
It’s not a bad idea to use a wrapper around the Tomcat object, but it’s so simple, you might as well just write your own instead of adding another third-party dependency and learning someone else’s API.
Yes, that is correct. I just wanted to demonstrate how we could simplify the case, and provide a better API, when there are multiple connectors involved.
I’m trying to pack it all into a self-contained jar file. The ideal format to distribute demo versions and prototypes.
The trouble is that it seems to expect files on the filesystem for it application base. So WEB-INF can not be a directory in a jar file. Or does it ?
Jan,
Do you mean you want to have a single, self contained jar file which can be used for embedding Tomcat 7?
The org.wso2.carbon.tomcat OSGi bundle is such a self contained jar file. For details see http://blog.afkham.org/2011/09/embedding-tomcat-7-bettertomcat.html
I think you’ll need a war file. I don’t believe you can pack all your app files into a jar but you could take a look at org.apache.catalina.core.StandardHost and how it handles the appBase. A war file would be easy to distribute but not quite as easy as a jar to run.
I am unable to start tomcat server while writing code in Before class.getting below error please advise me how to proceed further
Test set: com.ticketmaster.venue.authoring.service.impl.VenueAuthoringServiceImplTest
——————————————————————————-
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.187 sec <<< FAILURE!
com.ticketmaster.venue.authoring.service.impl.VenueAuthoringServiceImplTest Time elapsed: 0 sec <<< ERROR!
java.lang.NoSuchMethodError: org.apache.catalina.Context.addLifecycleListener(Lorg/apache/catalina/LifecycleListener;)V
at org.apache.catalina.startup.Tomcat.addWebapp(Tomcat.java:528)
at org.apache.catalina.startup.Tomcat.addWebapp(Tomcat.java:513)
at org.apache.catalina.startup.Tomcat.addWebapp(Tomcat.java:201)
With a NoSuchMethodError exception, it looks like you have the wrong version of servlet jars (ie. servlet-api) in your classpath.