Samstag, 30. Januar 2010

Eclipse Hot Deployment Mode For Spring DM Web Bundle

For developing web application in eclipse with spring dynamic modules (spring OSGi) there is no mode in which the web resource like JSP etc. will be refresh automatically in the tomcat container.

At the moment I must restart a war bundle when a resource changed and the war bundle will be redeployed by spring web bundle. The restart (redeploy) can be done in eclipse automatic when a resource in the eclipse project change with the STS features, see my previous post about STS (Spring Tool Suite). This is nice for Java classes but not for web resources like JSPs, CSS, etc.

How does the spring web bundle work? The bundle deploys (unzip) the war in a temp directory (can be specifiy by the system property java.io.tmpdir) and then start the deployment method of the container (context) e.g. tomcat in my case.

 
Example eclipse development project structure of a webapp for running in a OSGi container with spring web bundle.

At the development time e.g. in eclipse, normally we have no archive war or jar, we have a eclipse projects, with a war directory structure (see screenshot). So my idea was to patch the TomcatWarDeployer from the Spring Web bundle and put a hot deployment mode into it (Next step could be a own HotTomcatWarDeployer bundle).  In the hot deployment mode the war must installed from a directory (not as jar or war archive) in the OSGi container (in my case the eclipse project folder). The TomcatWarDeploy then do not deploy the files in temp folder the deployer runs the webapp in the project folder e.g. in the eclipse project.

Here is the original spring code (class org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer )for the base directory:
private String createDocBase(Bundle bundle, String contextPath) throws IOException {
        File tmpFile = File.createTempFile("tomcat-" + contextPath.substring(1), ".osgi");
 tmpFile.delete();
 tmpFile.mkdir();

 String path = tmpFile.getCanonicalPath();
 if (log.isDebugEnabled())
  log.debug("Unpacking bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "] to folder [" + path
    + "]...");

 Utils.unpackBundle(bundle, tmpFile);

 return path;
}

And here the patched code (method createDocBase from the class org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer) with the hot deployment mode for tomcat:
private String createDocBase(Bundle bundle, String contextPath) throws IOException {
 try
 {
  String hotdeployMode = System.getProperty(TOMCAT_HOTDEPLOY);
  if(hotdeployMode != null && hotdeployMode.toLowerCase().equals("true")){
   if (log.isDebugEnabled())
    log.debug("HOT Deployment mode is active. Use this feature only for developing!");
   String workspace = System.getProperty(TOMCAT_WORKSPACE);
   if(workspace != null){
    if (log.isDebugEnabled())
     log.debug("HOT Deployment workspace is set to: " + workspace);
    File workspaceFile = new File(workspace);
    if(workspaceFile.exists()){
     String projectName = bundle.getSymbolicName();
     if (log.isDebugEnabled())
      log.debug("HOT Deployment project name is: " + projectName);
     File projectFile = new File(workspaceFile, projectName);
     if(projectFile.exists() && projectFile.isDirectory()){
      if (log.isDebugEnabled())
       log.debug("HOT Deployment project path is: " + projectFile.getCanonicalPath());
      return projectFile.getCanonicalPath();
     }else{
      if (log.isDebugEnabled())
       log.debug("HOT Deployment project does not exists!");
     }
    }
    else{
     if (log.isDebugEnabled())
      log.debug("HOT Deployment workspace does not exists!");
    }
   }
   else{
    if (log.isDebugEnabled())
     log.debug("HOT Deployment workspace path is not set, please set system property " + TOMCAT_WORKSPACE);
   }
  }
 } catch(Exception exp) {
  if (log.isDebugEnabled()){
   log.debug("Error initializing HOT Deployment!");
   log.debug(exp);
  }
 }
  
 if (log.isDebugEnabled())
  log.debug("Use the normal spring deploymode with temp directory.");
  
 File tmpFile = File.createTempFile("tomcat-" + contextPath.substring(1), ".osgi");
 tmpFile.delete();
 tmpFile.mkdir();

 String path = tmpFile.getCanonicalPath();
 if (log.isDebugEnabled())
  log.debug("Unpacking bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "] to folder [" + path
    + "]...");

 Utils.unpackBundle(bundle, tmpFile);

 return path;
} 

That means you can change our web resources like JSPs etc. and the changes take directly effect. I think that’s the mode how I like to develop webapps no redeployment or file synch.

To use my patched spring web bundle you must install the patched bundle instead the normal spring web bundle (Version 1.2.1) in your OSGi container. Also the two system properties “org.springframework.osgi.web.deployer.tomcat.hotdeploy” and “org.springframework.osgi.web.deployer.tomcat.workspace” must be set see the screenshot.



So the solution is not very nice with the two system properties but at the moment it works for me. When you have ideas feel free for comments. I have also committed this idea in the spring dynamic modules (spring osgi) project JIRA see ticket http://jira.springframework.org/browse/OSGI-800.

Here you can get my example patched spring web bundle http://dl.dropbox.com/u/532968/spring-osgi-web-1.2.1-patched.jar

Have fun with the patch

Christian