Deploying a WAR as a JBoss AS 7 OSGi WAB?

A J2EE container is able to load and deploy Web Archives (WAR). A WAR is presented to a container in either a compressed zip file or a directory where your web application or services reside.

In the new OSGi world, WARs can also be deployed as a OSGi bundle. When deployed in an OSGi container, a WAR will have to have it’s manifest file rewritten so that it can be transformed into a Web Application Bundle (WAB).

WABs in OSGi are very similar to WARs in J2EE. The main difference is that WABs can take advantage of the OSGi features such as OSGi life cycle and class or resource loading rules.

If you can’t sleep, are feeling bored or you’re simply interested in the subject then put your goggles on and dive in chapter 128 of OSGi Specification document.

Now the issues! The current OSGi Http Service released in JBoss AS 7 is based on PAX-WEB which in turn is based on Jetty 6. What this means is that JBoss AS 7 actually provides two totally different Web containers, one for OSGi based web bundles (PAX-WEB) running on port 8090 and another for the normal JBoss AS operation mode (JBoss Web) running on port 8080.

JBoss Web is a J2EE6 compliant container meaning that it allows for Web Servlet 3.0 and CDI annotations. PAX-WEB is not, meaning that applications developed for the latest and greatest will not work in the OSGi Http Service container.

You can follow the issue here.

The other issue is that the current JBoss OSGi WebApp bundle (org.jboss.osgi.webapp) somehow missed the PAX-WEB JSP lib. You run the OSGi container on level 3, the JBoss OSGi WebApp is loaded and eventually you’ll see the following:

19:34:29,937 INFO [org.eclipse.jetty.util.log] (MSC service thread 1-2) started HttpServiceContext{httpContext=org.ops4j.pax.web.extender.war.internal.WebAppWebContainerContext@d72e3f}
19:34:29,937 WARN [org.ops4j.pax.web.extender.war.internal.RegisterWebAppVisitorWC] (MSC service thread 1-2) Jsp support is not enabled. Is org.ops4j.pax.web.jsp bundle installed?

You can follow the issue here.

The example code is based on my previous article HTML5, CSS3, WebServlet 3.0, CDI and JBossAS 7 and adapted to run inside the JBoss OSGi container as a WAB.

The main differences are, the addition of web.xml since PAX-WEB does not support WebServlet 3.0 annotations and the instantiation of the service as opposed to a pojo CDI based injection. To gap the lacking of CDI, we could include the use of Spring Framwork in case we required inversion of control or dependicy injection. The JSP version of it is commented out since it will have to wait until JBoss updates the libs (this might never happen since they will soon be moving into an OSGi web container based on JBossWeb).

SkinnerServlet.java

package org.riaconnection.skinner.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.riaconnection.skinner.service.SkinnerService;

/**
 * <p>
 * Servlet that accept HTTP GET and PUT requests for 
 * http://{server_host_or_ip}/skinner/skinit
 * </p>
 * 
 * @author Bruno Santos
 * 
 */
public class SkinnerServlet extends HttpServlet {

   private static final long serialVersionUID = 660317586340154513L;

   static String PAGE_DOCTYPE = "<!DOCTYPE html>";

   static String PAGE_MESSAGE = "<h1>I'm a huge header</h1>" +
		   						"<h2>I'm a big header</h2>" +
		   						"<h3>I'm a header</h3>" + 
		   						"<h4>I'm a super fun header</h4>";
   static String PAGE_VIDEO = 
		   "<video id=\"movies\" onmouseover=\"this.play()\" onmouseout=\"this.pause()\" autobuffer=\"true\" width=\"400px\" height=\"300px\">" +
		   "    <source src=\"streams/Intermission-Walk-in.ogv\" type='video/ogg; codecs=\"theora, vorbis\"'>" +
		   "    <source src=\"streams/Intermission-Walk-in_512kb.mp4\" type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'>" +
		   "</video>";
   
   static String PAGE_FORM = "<form action=\"/skinner/skinit\" method=\"post\">" +  
		   					 "    <input type=\"radio\" name=\"radiobutton\" value=\"noCSS\">No CSS" +  
		   					 "    <input type=\"radio\" name=\"radiobutton\" value=\"simpleCSS\">Simple CSS3" +
		   					 "    <input type=\"radio\" name=\"radiobutton\" value=\"funCSS\">Fun CSS3" +  
		   					 "    <input type=\"submit\" name=\"Update\" value=\"Update\">" +  
		   					 "</form>";
   
   static String PAGE_FOOTER = "</body></html>";

   SkinnerService skinnerService = new SkinnerService();

   @Override
   protected void
   doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
	  resp.setContentType("text/html");
	  
      PrintWriter writer = resp.getWriter();
      writer.println(PAGE_DOCTYPE);
      writer.println("<html><head>");
      String cssFile = skinnerService.checkSkin(req.getParameter("radiobutton"));
      if (!cssFile.isEmpty())
    	  writer.println("<link rel=\"stylesheet\" href=\"styles/" + cssFile + "\"/>");
      writer.println("</head><body>");
      writer.println(PAGE_MESSAGE);
      writer.println(PAGE_VIDEO);
      writer.println(PAGE_FORM);
      writer.println(PAGE_FOOTER);
      writer.close();
   }
   
   @Override  
   public void 
   doPost(HttpServletRequest request,HttpServletResponse response)
   throws ServletException ,IOException{  
       doGet(request,response);  
   }
}

SkinnerService.java

package org.riaconnection.skinner.service;

/**
 * A simple CDI service that return the selected CSS file to be used
 * 
 * @author Bruno Santos
 * 
 */
public class SkinnerService {

   public String checkSkin(String selectedSkin) {
	   if (selectedSkin != null) {
		   if (selectedSkin.equals("simpleCSS")) {
			   return "simple.css";
		   } else if (selectedSkin.equals("funCSS")) {
			   return "fun.css";
		   } else {
			   return "";
		   }
	   }
	   return "";
   }
}

MANIFEST.MF

Manifest-Version: 1.0
Archiver-Version: JBoss Archiver
Created-By: Bruno Santos
Built-By: bsantos
Build-Jdk: 1.6.0_26
Bundle-ClassPath: .,WEB-INF/classes/
Bundle-ManifestVersion: 2
Bundle-SymbolicName: skinner
Export-Package: org.riaconnection.skinner.servlet,org.riaconnection.skinner.service
Import-Package: org.osgi.framework,javax.servlet,javax.servlet.http
Web-ContextPath: /skinner

web.xml

<?xml version="1.0" encoding="ASCII"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
  <display-name>Skinner WAB (Web Application Bundle)</display-name>
  
  <servlet>
    <servlet-name>SkinnerServlet</servlet-name>
    <servlet-class>org.riaconnection.skinner.servlet.SkinnerServlet</servlet-class>
  </servlet>
  
<!--   <servlet>
    <servlet-name>SkinnerJSPServlet</servlet-name>
    <servlet-class>org.riaconnection.skinner.servlet.SkinnerJSPServlet</servlet-class>
  </servlet>
 -->
   
  <servlet-mapping>
    <servlet-name>SkinnerServlet</servlet-name>
    <url-pattern>/skinit</url-pattern>
  </servlet-mapping>
    
<!--   <servlet-mapping>
    <servlet-name>SkinnerJSPServlet</servlet-name>
    <url-pattern>/skinit/jsp</url-pattern>
  </servlet-mapping>
 -->  
</web-app>

The example WAB code can be downloaded from here.
Rename the file to skinner.wab.7z and extract it using 7Zip.
Make sure you change the standalone.xml file configuration to run on start level 3:

<property name="org.osgi.framework.startlevel.beginning">3</property>

Deploy it in JBoss AS 7 standalone and you’ll have a running WAB.

Advertisements

About CrazyPenguin

Software Engineer
This entry was posted in HTML5, J2EE, JBoss, OSGi and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s