Context
I work at « Etat de Vaud » in Switzerland and we have a lot of j2ee Servlet applications which run on Tomcat 6.0.x, that are multi-modules Maven projects.
My main concern is about development time, about the time the developer spend (or loose) compiling, packaging, deploying and running the application between 2 « tests »
Of course, we use unit tests extensively here, to develop Services, DAOs and other classes, but when it comes to pure web development (understand JSP) the « round-trip » time can become huge.
For example, on one of our project, when the developer change code in a controller (Spring MVC), it must
- Run maven to compile the code and package the war (~30-60 seconds)
- Copy the war to webapps of the Tomcat server (or have a symlink if on Linux)
- Then tomcat redeploy the application, including the Spring context (DAOs, Services, SessionFactory) which takes around 30 seconds
One code modification, round-trip time = ~1 minute…
Compare that to PHP, Ruby on Rails or even Grails which are quite instantaneous!
Multi-modules Maven projects
If you have single modules Maven projects, this is not a problem. Say we have:
myapp/webapp
which contains JSP and static contents:
myapp/webapp/css/...myapp/webapp/images/... myapp/webapp/WEB-INF/jsp/...You have all your code in one location, say
myapp/src/main/java/...then Eclipse can output compiled classes to
myapp/webapp/WEB-INF/classes/...and have your libs in
myapp/webapp/WEB-INF/lib/...Then you can start tomcat on
myapp/webapp/And that’s all.
But when you have Multi-modules Maven projects, then you have Java source files in 2-3 places and Eclipse is unable to write all the compiled classes in one place (myapp/webapp/WEB-INF/classes)
Embedding Tomcat in Eclipse to the rescue!
The idea is to embedd Tomcat in Eclipse, so Tomcat is in the same JVM/ClassLoader of Eclipse and Tomcat « sees » all the projects, and also all the compiled classes and all the libs that are configured in Eclipse.
Step by step:
- Create a muli-modules Maven project that have, say, 2 modules : business and web (web depends on business)
- Import them in Eclipse using the Maven plugin for Eclipse (http://m2eclipse.codehaus.org/)
- You then have 3 projects in Eclipse: base, business and web
- At this point, the web project sees all the classes and libs from business and web
- Create a class to Embed Tomcat in Eclipse as I’ll show below
- Run your webapp using that class and you have instantaneous view of your modifications
Embedded Tomcat, with JNDI data sources
Tomcat 6.0.x is totally able to Embed itself in your code. The idea, is to create a java main() in the test part of the web project (in myapp/src/test/java). It’s important to create it in the test part, so it won’t be in your generated WAR, and the needed dependencies (catalina.jar, jsp-api, …) won’t be neither in your packaged WAR.
Step by step:
- Create a class that implements the Embedded part of Tomcat (See below)
- Debug it as Java Application (It’s important to « Run as Debug » so Eclipse is able to « feed » the webapp with the modified code without restarting the whole application)
Embedded class:
public class TomcatRunner { private static final Logger log = Logger.getLogger(TomcatRunner.class); private Embedded embedded; private String catalinaHome; private String pwd; public TomcatRunner(String contextPath, String webappDir, String contextXmlPath) throws Exception { // Current dir => ..../myapp/web final File file = new File("."); pwd = file.getCanonicalPath(); // Create CATALINA_HOME File catalinaHomePath = new File(pwd+"/target/CATALINA_HOME"); if (!catalinaHomePath.exists()) { Assert.isTrue(catalinaHomePath.mkdirs()); } catalinaHome = catalinaHomePath.getCanonicalPath(); // Create an embedded server embedded = new Embedded(); embedded.setCatalinaHome(catalinaHome); // Create an engine final Engine engine = embedded.createEngine(); engine.setDefaultHost("localhost"); // Create a default virtual host final Host host = embedded.createHost("localhost", pwd /*Bas dir for webapps*/); engine.addChild(host); // The context of myapp // myapp/default-web.xmlis the default web.xml used for the context. // It contains the JSP and "static content" servlets. You can find that file in apache-tomcat-6.0.18/conf/web.xml final Context context = createContext(contextPath, pwd+"/"+webappDir, pwd+"/"+contextXmlPath, "myapp/default-web.xml"); host.addChild(context); // Install the assembled container hierarchy embedded.addEngine(engine); // Assemble and install a default HTTP connector final Connector connector = embedded.createConnector(""/*Default to localhost*/, 8080/*Port to listen*/, false/*Secure*/); embedded.addConnector(connector); } public void start() throws Exception { // Start the embedded server embedded.start(); } private static Context createContext(String path, String docBase, String contextFilePath, String defaultWebXmlPath) { log.debug("Creating context '" + path + "' with docBase '" + docBase + "'"); StandardContext context = new StandardContext(); context.setDocBase(docBase); context.setPath(path); ContextConfig config = new ContextConfig(); config.setDefaultWebXml(defaultWebXmlPath); config.setCustomAuthenticators(null); context.addLifecycleListener(config); // This is the config of the JDNI datasources if (StringUtils.isNotBlank(contextFilePath)) { File configFile = new File(contextFilePath); context.setConfigFile(configFile.getAbsolutePath()); } return context; } }
Then you can use it with:
// A global property configured for the server System.setProperty("myapp.appDir", "appDir/devel"); TomcatRunner runner = new TomcatRunner("/my/app", "webapp", "webapp/META-INF/context.xml"); runner.start();
And then start it as « Debug ». That’s it!
JNDI
If you have JNDI data sources, you can configure them in the file
webapp/META-INF/context.xml
Example of context.xml:
<Context debug="true" reloadable="true" crossContext="true" antiJARLocking="true"> <Resource name="jdbc/myAppDataSource" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" validationQuery="select sysdate from dual" username="WEB_USER" password="pwd" driverClassName="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@<server>:1521:orcl"/> </Context>
Usage
The goal is to be able to modifiy static content, JSPs or java code and that it is used instantaneously without needing a restart or a manual operation.
To be able to use that Embedded tomcat thing, you need to start it as debug, then browse to http://localhost:8080/my/app
You should then be able to access your application, through the embedded tomcat.
Static content (CSS, images):
Say you modify myapp/webapp/css/myapp.css, the file is directly in the webapp folder so it is reloaded next time you load a page that use that CSS. Idem for images.
JSPs
If Tomcat is configured to recompile JSPs when they are modified (which is the default), then as soon as you modify a JSP, it’s recompiled the next time you access it.
Java code
This is the greatest part. If you modify java code in Eclipse, eclipse recompiles the java file to the corresponding .class, then also feed it to the debugger so the JVM sees the new version. (That works only if you « Run as Debug » from Eclipse)
So when you reload your page, the new class is used and the modified code is used. Magic!
Limitations
There is one limitation, which is not due to this setup, eclipse or Tomcat. It’s due to the fact that Java debugger can’t be feed a class that has « changed too much ». Changing too much means that as long as you change code in a method, that’s normally fine. But if you add/remove/rename a class, a class method or a class member, then the debugger is unable to feed it to the JVM through the debugger. Eclipse warns by saying: « Hot code replaced failed », saying that your modified code is not available in the JVM (the old code stays there) and that you should restart your application.
You then need to simply stop the TomcatRunner and restart it.
Another limitation is that if the modified classes was already run and loaded, then it won’t be reloaded before a server restart. One example, is if you have a class that set up some values in a Singleton, changing the singleton code won’t change the singleton already in memory. Idem for the spring context.
Conclusion
Using this setup change the round-trip time from ~60 seconds to instantaneous, which is great. Due to the limitations above, we must restart the webapp every 3-10 minutes depending which code we are changing, but that’s acceptable. And remember we use Unit tests for all development, we use that setup only for pure web development (JSPs)
References:
http://www.onjava.com/pub/a/onjava/2002/04/03/tomcat.html
http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/startup/Embedded.html
Live Strip Cam
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
SEO
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Guest friendly hotels manila
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Zahnklinik Ungarn
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Zahnarzt Gegenofferte
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
kununu.com
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
webdesign
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
vape
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Sachbücher
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
bildercloud.net
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Spiele
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Auto Kaufen
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Dentalhygiene Bern
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Umzugsfirma
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
mietkautionschweiz.ch
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Geneva girls
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
umzug
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
hotel in rimini
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
vpayfast.com
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
tylugo.com
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Italia marketing to china
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Zahnklinik Bern
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
durchsichtige zahnspange
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
mhp-centrum.com
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Schweizer Drupal Agentur
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Online Marketing Agentur Schweiz
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
treuhandofferten.ch
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
Schrank nach Mass
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
reklamationszentrale.ch
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric
bracelet
Embedding Tomcat in Eclipse with Maven, JNDI et tutti quanti! | Le blog de Jean-Eric