ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Create Desktop Applications with Java-Based Web Technologies
Pages: 1, 2, 3, 4, 5, 6

The New main()

The first example we'll look at is our core application launch logic, as shown in Example 1. We start by creating a JFrame to hold the server user interface (onjava.ServerUI) and setting the status to Launching.

Example 1. TomcatWrapper source

package onjava;

import java.net.*;
import java.io.*;
import java.awt.*;

public class TomcatWrapper
{
   public static void main(String[] args)
   {
      try
      {
         ServerUI myFrame = new ServerUI();
         myFrame.setStatus("Launching...");
         myFrame.show();
            
         // This will create a reference to a file in the
         // current working directory, which is the path
         // where the application started (at least on
         // Win32 & Mac OS X)
         File baseDirectory = new File("");
            
         // This is the path to the application's base directory
         String path = baseDirectory.getAbsolutePath();
         String[] temp =
         {path};
            
         // Launches the server
         onjava.EmbeddedTomcat.main(temp);
            
         // Wait a second to be sure the server is ready
         Thread.sleep(1000);

         myFrame.setStatus("Server running.");

         // Launches web browser pointed to the application
         edu.stanford.ejalbert.BrowserLauncher.openURL("http://127.0.0.1:8080/");
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }
};

The next thing we do is create a new java.io.File, but we only pass in a blank String to the constructor. This is an interesting thing; it creates a default reference to the current working directory at the application launch. We save and rely on this directory as the default installation directory for the web server throughout the rest of the application's life cycle. While this behavior is not to my knowledge a formal part of the specification, it's the default behavior on both the Mac OS X and Win32 JVMs.

After the server is started, we wait briefly to ensure that the server has launched, and then use the code found at browserlauncher.sourceforge.net to launch the user's browser. We use the user's loopback address (127.0.0.1) and pick a port (in this case, 8080).

Those of you who are paying attention may notice one small problem with this example: the application in the example binds the web application to port 8080, which, unfortunately, tends to be a popular port. (It's an easy-to-remember variant on port 80, the default for HTTP.) Each IP address on a computer can listen on a single port at a time to provide a service, and a great many of those ports are already used. By examining the list at www.iana.org/assignments/port-numbers, one discovers that 8080 is explicitly defined as an HTTP Alternate; good to know, but not particularly useful for resolving conflicts.

With that in mind, it's worth pondering the future. There are a great many ports that could be used in the dynamic range (and indeed could be dynamically generated at application launch), but unfortunately, users wouldn't be able to rely on any bookmarks if the ports are dynamically created. In the end, you wind up with a situation much like that of managing three-letter document types or creator codes; most software will grab certain types by default, and some will register with some central authority, but you'll probably want to provide a workaround to allow users to specify alternatives in the (hopefully rare) case of a collision for a undefined port.

Wrapping the Server

The code shown in Example 2 dives more deeply into the actual mechanics of launching the server. Here we rely on code very similar to that provided by the Tomcat documentation and James Goodwill in his aforementioned article. A few changes are required to ensure cross-platform compatibility; notably, relying on the System.getProperty("file.separator") property for determining the file path separator character (instead of hard-coding a particular / or \ character). We also make the code in the onjava.EmbeddedTomcat.main() method a bit smarter about handling and automatically registering WAR files.

Example 2 EmbeddedTomcat source

package onjava;

import java.net.URL;
import java.io.File;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Deployer;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.logger.SystemOutLogger;
import org.apache.catalina.startup.Embedded;
import org.apache.catalina.Container;

public class EmbeddedTomcat
{
   private String path = null;
   private Embedded embedded = null;
   private Host host = null;
   /**
    * Default Constructor
    *
    */
   public EmbeddedTomcat()
   {
        
   }
    
   /**
    * Basic Accessor setting the value of the context path
    *
    * @param path - the path
    */
   public void setPath(String path)
   {
     this.path = path;
   }
    
   /**
    * Basic Accessor returning the value of the context path
    *
    * @return - the context path
    */
   public String getPath()
   {
      return path;
   }
    
   private static String dirchar = System.getProperty("file.separator");
   
   /**
    * This method Starts the Tomcat server.
    */
   public void startTomcat() throws Exception
   {
      Engine engine = null;
      // Set the home directory
      System.setProperty("catalina.home", getPath());
       
      // Create an embedded server
      embedded = new Embedded();
      // print all log statements to standard error
      embedded.setDebug(0);
        
      // Create an engine
      engine = embedded.createEngine();
      engine.setDefaultHost("localhost");
       
      // Create a default virtual host
      host = embedded.createHost("localhost", getPath()
      + dirchar + "webapps");
      engine.addChild(host);
        
      // Create the ROOT context
      Context context = embedded.createContext("",
      getPath() + dirchar + "webapps" + dirchar + "ROOT");
      host.addChild(context);
        
      // Install the assembled container hierarchy
      embedded.addEngine(engine);
       
      // Assemble and install a default HTTP connector
      Connector connector =
      embedded.createConnector(null, 8080, false);
      embedded.addConnector(connector);
        
      // Start the embedded server
      embedded.start();
        
      embedded.setLogger(new SystemOutLogger());
      host.setLogger(new SystemOutLogger());
      context.setLogger(new SystemOutLogger());
   }
    
   /**
    * This method Stops the Tomcat server.
    */
   public void stopTomcat() throws Exception
   {
      // Stop the embedded server
      embedded.stop();
   }
    
   /**
    * Registers a WAR with the container.
    *
    * @param contextPath - the context path under which the
    *               application will be registered
    * @param warFile - the URL of the WAR to be
    * registered.
    */
   public void registerWAR(String contextPath, URL warFile)
   throws Exception
   {
       
      if ( contextPath == null )
      {
            
         throw new Exception("Invalid Path : " + contextPath);
      }
      if( contextPath.equals("/") )
      {
           
         contextPath = "";
      }
      if ( warFile == null )
      {
            
         throw new Exception("Invalid WAR : " + warFile);
      }
       
      Deployer deployer = (Deployer)host;
      Context context = deployer.findDeployedApp(contextPath);
        
      if (context != null)
      {
            
         throw new
         Exception("Context " + contextPath
         + " Already Exists!");
      }
      deployer.install(contextPath, warFile);
   }
    
   /**
    * Unregisters a WAR from the web server.
    *
    * @param contextPath - the context path to be removed
    */
   public void unregisterWAR(String contextPath)
   throws   Exception
   {
        
      Context context = host.map(contextPath);
      if ( context != null )
      {
           
         embedded.removeContext(context);
      }
      else
      {
           
         throw new
         Exception("Context does not exist for named path : "
         + contextPath);
      }
   }
    
   public static void main(String args[])
   {
      try
       {           
         tomcat = new EmbeddedTomcat();
         tomcat.setPath(args[0]);
         tomcat.startTomcat();
            
         System.out.println("Using path: " + args[0]);
          
         String[] wars = new File(args[0] + dirchar + "webapps").list();
         if(wars != null)
            for(int i = 0; i < wars.length ; i++ )
            {
                System.out.println(wars[i]);
                if(wars[i].endsWith(".war"))
                {
                    File temp = new File((new java.io.File(args[0] + "/webapps/" 
                                          + wars[i])).toString());
                    URL tempURL = temp.toURL();
                    System.out.println(temp);
                        
                    URL url = new URL("jar:" + tempURL.toString() + "!/");
                      
                    String context = "/" + wars[i].substring(0, wars[i].length() - 4);
                       
                    tomcat.registerWAR(context , url);
                }
            }
            
            
      }
      catch( Exception e )
      {
            
         e.printStackTrace();
      }
   }
    
   private static EmbeddedTomcat tomcat;
    
   public static void stopServer()
   {
      try
      {
         tomcat.stopTomcat();}
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
    
}

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow