The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Extending the Grizzly HTTP Runtime part V: Programatically configuring Servlet and GrizzlyAdapter

Posted by jfarcand on January 8, 2009 at 4:12 PM PST

A lot of applications/framework (like Jersey, Hudson, DWR, etc.) out there just consist of a single Servlet which dispatch requests to its appropriate internal components. In such case, a full blown Servlet Container might not be needed....but a tiny 800k Grizzly might be!

IMG_0146.JPG

Like I've described in previous post on the topic (I,II,II,IV), it is extremely simple to build an HTTP extension/WebServer using Grizzly:

  1 GrizzlyWebServer ws = new GrizzlyWebServer(80);
  2 GrizzlyAdapter adapter = new GrizzlyAdapter() {
  3     @Override               
  4         public void service(GrizzlyRequest req, GrizzlyResponse res) {
  5             String uri = req.getRequestURI();                   
  6             File file = new File("/var/www",uri);                               
  7             long length = file.length();                                                        
  8                                                                                                                 
  9             if (file.exists()){                                                                                 
 10                 FileInputStream fis = null;                                                                                             
 11                 try{
 12                     fis = new FileInputStream(file);                                                                                           
 13                     FileOutputBuffer fob = (FileOutputBuffer)res.getOutputBuffer();
 14                     long nWrite = 0;                                                                                                           
 15                     while (nWrite < length) {
 16                         nWrite += fob.sendFile(fis.getChannel(), nWrite, length - nWrite);                                                     
 17                     }                                        
 18                 } catch (IOException ex){                                                                                                      
 19                     res.setStatus(404);                                              
 20                     res.setDetailMessage("Not Found");                                                                                         
 21                 } finally {                                                                                  
 22                     try{                                                                                                                       
 23                         fis.close();                                                                                                         
 24                     } catch (IOException ex){};
 25                 }                                                                                                                              
 26             } else {                                                                                                                           
 27                 res.setStatus(404);
 28                 res.setDetailMessage("Not Found");                                                                                             
 29             }                                                                                                                    
 30         }
 31 };                                                                                                                                             
 32 ws.addGrizzlyAdapter(adapter, new String[]{"/"});   

In 30 lines you just wrote a simple file server. But what about Servlet? Let's take a very difficult application and try to launch it with Grizzly: Hudson. Let's programmatically configure Hudson, similar to what we did for our simple file server. First, let's create a simple wrapper.

  1 GrizzlyWebServer ws = new GrizzlyWebServer(80);
  2 ServletAdapter adapter = new ServletAdapter();
  3 adapter.setRootFolder("/tmp/hudson");
  4 adapter.setHandleStaticResources(true);
  5                                         
  6 adapter.setContextPath("/hudson");      
  7                                                 
  8 Servlet servlet = (Servlet)ClassLoaderUtil.load("org.kohsuke.stapler.Stapler");
  9 adapter.setServletInstance(servlet);                    
 10                                                                 
 11 Filter filter = (Filter)ClassLoaderUtil.load("hudson.security.HudsonFilter"); 
 12 adapter.addFilter(filter, "authentication-filter", null);               
 13                                                                                 
 14 adapter.addInitParameter("default-encoding", "UTF-8");                          
 15 
 16 filter = (Filter)ClassLoaderUtil.load("hudson.util.PluginServletFilter");               
 17 adapter.addFilter(filter, "plugins-filter", null);
 18                                                                                                 
 19 adapter.addServletListener("hudson.WebAppMain");
 20                                                                                                         
 21 adapter.setProperty("load-on-startup","1");                                                             
 22 
 23 ws.addGrizzlyAdapter(adapter, new String[]{"/hudson"});        

As you can see, this is quite simple. First, we create an instance of ServletAdapter (line 2), then configure the appropriate Servlet, Filter, and deployment information (line 8 to 21), taken from Hudson's web.xml. Note line 23 where we finally add the Adapter to GrizzlyWebServer: just pass the ServletAdapter and a mapping (context) used to map requests. That's it. You can download the result here and lunch Hudson on top of Grizzly by just doing:

java -jar grizzly-hudson-1.9.3.jar -p 8080 -a hudson.war

OK but this is cool as a demo, but doesn't add anything special to Hudson. Let's extends it by adding a chat functionality so we can blame each other when someone breaks the build. Let first add another Adapter that support the Bayeux protocol so we can chat using Comet

 24 ws.addGrizzlyAdapter(new CometdAdapter(), new String[]{"/cometd"});

So now we can package our 24 lines of code, and have a very small Hudson server with chat capability. Requests to /hudson goes to Hudson, /cometd goes to our Bayeux/Cometd chat application. Nothing fancy here as you can already do that using GlassFish. But quite simple to do it programmatically!. Another way to add Bayeux support could have been:

 24 ServletAdapter adapter2 = new ServletAdapter();
 25 adapter2.setRootFolder("/tmp/hudson");
 26 adapter2.setHandleStaticResources(true);
 27 
 28 adapter2.setContextPath("/cometd");
 29 
 30 Servlet servlet = (Servlet)ClassLoaderUtil.load("com.sun.grizzly.cometd.servlet.CometdServlet");
 31 adapter2.setServletInstance(servlet);
 32 
 33 ws.addGrizzlyAdapter(adapter2, new String[]{"/cometd"});

A little more complicated, but just to demonstrate how to programmatically deploy more than one Servlet.

Want to try it? download the grizzly-webserver.jar and uses your favorite IDE to build powerful HTTP based applications. Need help: ping us on users@grizzly.dev.java.net, or Tweet us on Twitter.

technorati:

Related Topics >> Glassfish      
Comments
Comments are listed in date ascending order (oldest first)

Is it possible to have an embedded version but that reads the configuration from files like a regular container would do?

I say so because I currently use Jetty embedded and I use always the same "Jetty launcher" code and I just point it to the proper directory to be mounted and it reads the web.xml and deploys the application, etc.

This was I can re-use the same code for every tiny application I want to, without harcoding the servlets, filters etc. that I need. And at the same time, the web application can be mounted in a regular container without a change.
I understand how doing everything in code can be very useful sometimes, but in my case I'd rather do it like that..
Salute and good job!

See the answer here: http://tinyurl.com/9p6kmm and the following discussion.

When adding filters is it also possible to set a filter mapping and the dispatcher types? I need this and it can be done with Jetty.

I've posted the response here -> http://tinyurl.com/btmo2x so the community can comments/help. Thanks!!

I see, thank you. However my use case is a bit more complicated ;-) I have two filters with different mappings and a context listener. It is important that the listener has servlet API semantics (singleton, methods invoked just once, ...). Currently my code in Jetty looks with this: Context context = new Context(null, "/", options); context.addEventListener(new EventListener()); context.addFilter(AFilter.class, "/a*", Handler.REQUEST); context.addFilter(BFilter.class, "/b*", Handler.REQUEST); which is very simple. I fear that when I add two ServletAdapters to a GrizzlyWebServer or a GrizzlyAdapterChain that the context listener will receive events twice.

@bragest -> I've posted the response here: http://is.gd/hlgc . Ins hort. I agree with you we need to improve the API.