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: glassfish, javaee — arungupta @ 5:29 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 & Age</title>
 </h:head>
 <h:body>
 <h1>Show Name & 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
  • email
  • StumbleUpon
  • Technorati
  • Twitter
  • Slashdot
Related posts:
  1. Deployment Descriptor-free Web services in GlassFish
  2. 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
  3. TOTD #132: Servlets 3.0 in Embedded GlassFish Reloaded – lightweight Java EE 6
  4. TOTD #139: Asynchronous Request Processing using Servlets 3.0 and Java EE 6
  5. TOTD #136: Default Error Page using Servlets 3.0 – Improved productivity using Java EE 6

No Comments »

No comments yet.

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