Miles to go …

February 5, 2010

TOTD #120: Deployment Descriptor-free Java EE 6 application using JSF 2.0 + EJB 3.1 + Servlets 3.0

Filed under: General — arungupta @ 2:00 pm

Here is trivial Java EE 6 application that is keeping you away from any deployment descriptors. It uses Java Server Faces 2.0, Enterprise Java Beans 3.1, and Servlet 3.0. This application shows the following Java EE 6 features:

  1. No-interface view for EJB
  2. EJBs packaged in a WAR file
  3. Optional "faces-config.xml" for Java Server Faces
  4. FacesServlet registered using Servlet 3.0 programmatic registration APIs
  5. Java Server Faces navigation rules using convention-over-configuration
  6. Optional "web.xml" for Servlets 3.0

The WAR file structure is:

./index.jsp
./index.xhtml
./META-INF
./show.xhtml
./WEB-INF
./WEB-INF/classes
./WEB-INF/classes/org
./WEB-INF/classes/org/glassfish
./WEB-INF/classes/org/glassfish/samples
./WEB-INF/classes/org/glassfish/samples/SimpleBean.class
./WEB-INF/classes/org/glassfish/samples/SimpleEJB.class
./WEB-INF/classes/org/glassfish/samples/SimpleServlet.class

Look ma, no deployment descriptors!

So how do you create this application:

mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=org.glassfish.samples -DartifactId=simplewebapp

This application is purposely not generated as a web application (missing "-DarchetypeArtifactId=maven-archetype-webapp"). If you specify this property then it will generate "WEB-INF/web.xml" which we don’t intend to use.

Change "pom.xml" to:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.glassfish.samples</groupId>
   <artifactId>simplewebapp</artifactId>
   <packaging>war</packaging>
   <version>1.0-SNAPSHOT</version>
   <name>simplewebapp</name>
   <url>http://maven.apache.org</url>
   <repositories>
     <repository>
       <id>glassfish-repository</id>
       <name>Java.net Repository for Glassfish</name>
       <url>http://download.java.net/maven/glassfish</url>
     </repository>
   </repositories>
   <build>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>2.0.2</version>
         <configuration>
           <source>1.5</source>
           <target>1.5</target>
         </configuration>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-war-plugin</artifactId>
         <version>2.1-beta-1</version>
         <configuration>
           <failOnMissingWebXml>false</failOnMissingWebXml>
         </configuration>
       </plugin>
     </plugins>
   </build>
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>3.8.1</version>
       <scope>test</scope>
     </dependency>
    <dependency>
       <groupId>javax</groupId>
       <artifactId>javaee-api</artifactId>
       <version>6.0</version>
       <scope>provided</scope>
    </dependency>
   </dependencies>
</project>

In the above code:

  • "maven-compiler-plugin" needs to be specified as the default source level for Maven compile plugin is JDK 1.3. It’s been over 9 years JDK 1.3 was released, not even listed on Java SE standard downloads page, EOLed many years ago. Vote/Comment for the issue MCOMPILER-80 if you’d like this bug to be fixed.
  • Adding "failOnMissingWebXml" ensures that Maven packages the WAR file even though no "web.xml" is present.
  • The complete list of Maven coordinates for GlassFish are available here.

Create the directory structure as:

./src/main
./src/main/java
./src/main/java/org
./src/main/java/org/glassfish
./src/main/java/org/glassfish/samples
./src/main/java/org/glassfish/samples/SimpleBean.java
./src/main/java/org/glassfish/samples/SimpleEJB.java
./src/main/java/org/glassfish/samples/SimpleServlet.java
./src/main/webapp
./src/main/webapp/index.jsp
./src/main/webapp/index.xhtml
./src/main/webapp/show.xhtml

Once again, there are no deployment descriptors, just plain Java files and XHTML/JSP pages.

Here are the different source files with explanation after each one of them:

SimpleBean.java

package org.glassfish.samples;
import javax.faces.bean.ManagedBean;
@ManagedBean(name="simplebean")
public class SimpleBean {
    private String name;
    private int age;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

This is currently a simple JSF managed bean. TOTD #109 explains how to convert a JSF managed bean to use CDI. A future blog will show how to convert this sample to use CDI.

SimpleEJB.java

package org.glassfish.samples;
import javax.ejb.Stateless;
@Stateless
public class SimpleEJB {
    public String sayHello(String name) {
        return "Hello " + name + "!!!";
    }
}

The session bean has no interface, just the @Stateless annotation.

SimpleServlet.java

package org.glassfish.samples;
import javax.ejb.EJB;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.PrintWriter;
import java.io.IOException;
/**
* Hello world!
*/
@WebServlet(urlPatterns={"/SimpleServlet"})
public class SimpleServlet extends HttpServlet {
@EJB SimpleEJB bean;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h2>Serving at: " + request.getContextPath() + "</h2>");
out.println("<h2>Invoking EJB: " + bean.sayHello("Duke") + "</h2>");
out.println("</body></html>");
}
}

The servlet injects the EJB in the application, display the servlet context and the result of invoking the business operation of the EJB.

index.jsp

<html>
<body>
<h2>Hello World!</h2>
Invoke the Servlet by clicking <a href="SimpleServlet">here</a>.
</body>
</html>

This is just a placeholder for invoking the servlet.

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtm
l1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Enter Name &amp; Age</title>
</h:head>
<h:body>
<h1>Enter Name &amp; Age</h1>
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Name:"/>
<h:inputText value="#{simplebean.name}" title="name" id="name" required="true"/>
<h:outputText value="Age:"/>
<h:inputText value="#{simplebean.age}" title="age" id="age" required="true"/>
</h:panelGrid>
<h:commandButton action="show" value="submit"/>
</h:form>
</h:body>
</html>

JSF 2 uses Facelets as viewing technology and so an ".xhtml" file is used for all the JSF tags. This page is intentionally kept simple and not using any templating, composition, or any other features of Facelets. This page renders an HTML form with two text boxes and a command button, binds the value of text box to the managed bean, and displays the page "show.xhtml" when the command button is clicked. The default JSF 2 navigation handler try to match a view on the disk ("show.xhtml" in this case) based upon the "action" attribute.

show.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtm
l1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Show Name &amp; Age</title>
</h:head>
<h:body>
<h1>Show Name &amp; Age</h1>
<h:form action="show">
<h:panelGrid columns="2">
<h:outputText value="Name:"/>
<h:outputText value="#{simplebean.name}" />
<h:outputText value="Age:"/>
<h:outputText value="#{simplebean.age}" />
</h:panelGrid>
</h:form>
</h:body>
</html>

This page reads the bean properties (stored from previous page) and displays them on the page.

How do you build this entire application ?

mvn clean package

Lets deploy the application on a Java EE 6 compliant application server, GlassFish v3 (download here):

./bin/asadmin deploy --force=true ~/samples/javaee6/simplewebapp/target/simplewebapp-1.0-SNAPSHOT.war

And now your application is accessible at "http://localhost:8080/simplewebapp-1.0-SNAPSHOT/index.jsp" and looks like:

Clicking on "here" looks like:

The JSF page is accessible at "http://localhost:8080/simplewebapp-1.0-SNAPSHOT/index.jsf" and looks like (after entering the values):

Notice that even though the page is named "index.xhtml", it’s accessed as "index.jsf". This is because the JSF specification provides recommended mapping for FacesServlet to "*.faces" and "/faces/*". In addition, Mojarra (Reference Implementation of JSF2 in GlassFish) also adds a mapping to "*.jsf". Any views using these URL pattersn are routed through FacesServlet. So alternative URLs for our page are "http://localhost:8080/simplewebapp-1.0-SNAPSHOT/index.faces" and "http://localhost:8080/simplewebapp-1.0-SNAPSHOT/faces/index.xhtml".

Clicking on "Submit" shows the following page:

That’s it!

Here are several other useful entries:

  • TOTD #109 : How to convert a JSF managed bean to JSR 299 bean (Web Beans) ?
  • TOTD #108 : Java EE 6 web application (JSF 2.0 + JPA 2.0 + EJB 3.1) using Oracle, NetBeans, and GlassFish
  • TOTD #102 : Java EE 6 (Servlet 3.0 and EJB 3.1) wizards in Eclipse
  • TOTD #99 : Creating a Java EE 6 application using MySQL, JPA 2.0 and Servlet 3.0 with GlassFish Tools Bundle for Eclipse
  • TOTD #98 : Create a Metro JAX-WS Web service using GlassFish Tools Bundle for Eclipse
  • TOTD #95 : EJB 3.1 + Java Server Faces 2.0 + JPA 2.0 web application – Getting Started with Java EE 6 using NetBeans 6.8 M1 & GlassFish v3
  • TOTD #94 : A simple Java Server Faces 2.0 + JPA 2.0 application – Getting Started with Java EE 6 using NetBeans 6.8 M1 & GlassFish v3
  • TOTD #93 : Getting Started with Java EE 6 using NetBeans 6.8 M1 & GlassFish v3 – A simple Servlet 3.0 + JPA 2.0 app

The next follow up blog will show "Hello World"s of Context & Dependency Injection, Bean Validation, Java API for Restful Web services, Java Persistence API, Interceptors, and other Java EE 6 specifications in this application.

Technorati: totd javaee glassfish v3 javaserverfaces servlet3 ejb maven

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • StumbleUpon
  • Technorati
  • Twitter
  • Slashdot
Related posts:
  1. TOTD #123: f:ajax, Bean Validation for JSF, CDI for JSF and JPA 2.0 Criteria API – all in one Java EE 6 sample application
  2. TOTD #146: Understanding the EJB 3.1 Timer service in Java EE 6 – Programmatic, Deployment Descriptor, @Schedule
  3. Deployment Descriptor-free Web services in GlassFish
  4. TOTD #95: EJB 3.1 + Java Server Faces 2.0 + JPA 2.0 web application – Getting Started with Java EE 6 using NetBeans 6.8 M1 & GlassFish v3
  5. TOTD #94: A simple Java Server Faces 2.0 + JPA 2.0 application – Getting Started with Java EE 6 using NetBeans 6.8 M1 & GlassFish v3

10 Comments »

  1. [Trackback] This post was mentioned on Twitter by roofimon: RT @SunBlogs: TOTD #120: Deployment Descriptor-free Java EE 6 application using JSF 2.0 + EJB 3.1 + Servlets 3.0 http://bit.ly/d2IDTa

    Comment by uberVU - social comments — February 7, 2010 @ 2:00 am

  2. Very Simple and to the point solution.
    This is the best starting point for anyone starting to work on JavaEE6

    Comment by Yogendra Rampuria — February 14, 2010 @ 5:05 am

  3. Hi Arun,

    As written I’m getting an exception when show.xhtl is parsed: "The entity name must immediately follow the ‘&’ in the entity reference."

    But by using "and" or "&amp;" instead of "&" it’s all ok. Just wanted to comment in case someone else ran into it.

    Cheers,
    Bobby

    Comment by Bobby Bissett — March 9, 2010 @ 1:53 pm

  4. Thanks Bobby for the catch, now fixed!

    Comment by Arun Gupta — March 9, 2010 @ 2:32 pm

  5. Cool! I also left out the "nice blog!" part of my comment. I needed a quick intro to JSF 2.0 in the EE 6 world, and this fit the bill (and answered a nagging maven question I had).

    Cheers

    Comment by Bobby Bissett — March 9, 2010 @ 3:14 pm

  6. Glad you liked it :-)

    Feel free to suggest ideas.

    Comment by Arun Gupta — March 9, 2010 @ 3:28 pm

  7. I believe its to do with not having a faces-config.xml but when I try to run the example, the JSF elements are not rendered, they are in the source but not displayed in the browser.

    Any ideas on what’s missing or causing the error?

    Mike

    Comment by Mike Lythgoe — March 14, 2010 @ 2:52 pm

  8. Mike,

    As mentioned above, with no faces-config.xml, the default rendering rules are:

    - FacesServlet to "*.faces" and "/faces/*"
    - In addition, Mojarra (Reference Implementation of JSF2 in GlassFish) also adds a mapping to "*.jsf". Any views using these URL pattersn are routed through FacesServlet.

    So alternative URLs for our page are "http://localhost:8080/simplewebapp-1.0-SNAPSHOT/index.faces" and "http://localhost:8080/simplewebapp-1.0-SNAPSHOT/faces/index.xhtml".

    Are you trying something else ?

    Comment by Arun Gupta — March 14, 2010 @ 5:34 pm

  9. OK, thanks for the suggestions, my web app name is "JavaEE6WithMaven" because I already had a simplewebapp deployed but it deploys OK and I can get to the index.jsp and have that run correctly.

    But the jsf pages still won’t display, I’ve tried:
    http://localhost:8080/JavaEE6WithMaven/faces/index.xhtml
    and
    http://localhost:8080/JavaEE6WithMaven/index.jsf

    but in both cases, I get a 404 error and:

    The requested resource () is not available.

    I’m sure I’ve done something stupid but I can’t see what, particularly as the jsp page can call the Servlet which in turn is using the injected Session Bean.

    Mike

    Comment by Mike Lythgoe — March 15, 2010 @ 5:44 am

  10. Well, I’d obviously just an idiot. Redid the code, just following the instructions exactly and it works, so it’s obviously my error in the original version I made.

    Many thanks for the example, as soon as I figure out what I did wrong, I’ll be back for more TOTDs.

    Mike

    Comment by Mike Lythgoe — March 15, 2010 @ 12:58 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.
Powered by WordPress