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:
- No-interface view for EJB
- EJBs packaged in a WAR file
- Optional "faces-config.xml" for Java Server Faces
- FacesServlet registered using Servlet 3.0 programmatic registration APIs
- Java Server Faces navigation rules using convention-over-configuration
- 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 & Age</title>
</h:head>
<h:body>
<h1>Enter Name & 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