JBoss.orgCommunity Documentation
We use a simple "TODO" application to show how JSF and EJB3 work together in a web application. The "TODO" application works like this: You can create a new 'todo' task item using the "Create" web form. Each 'todo' item has a 'title' and a 'description'. When you submit the form, the application saves your task to a relational database. Using the application, you can view all 'todo' items, edit/delete an existing 'todo' item and update the task in the database.
The sample application comprises the following components:
Entity objects - These objects represent the data model; the properties in the object are mapped to column values in relational database tables.
JSF web pages - The web interface used to capture input data and display result data. The data fields on these web pages are mapped to the data model via the JSF Expression Language (EL).
EJB3 Session Bean - This is where the functionality is implemented. We make use of a Stateless Session Bean.
Let's take a look at the contents of the Data Model represented by the Todo
class in the Todo.java
file. Each instance of the Todo
class corresponds to a row in the relational database table. The 'Todo' class has three properties: id, title and description. Each of these correspond to a column in the database table.
The 'Entity class' to 'Database Table' mapping information is specified using EJB3 Annotations in the 'Todo' class. This eliminates the need for XML configuration and makes it a lot clearer. The @Entity
annotation defines the Todo
class as an Entity Bean. The @Id
and @GeneratedValue
annotations on the id
property indicate that the id
column is the primary key and that the server automatically generates its value for each Todo
object saved into the database.
@Entity public class Todo implements Serializable { private long id; private String title; private String description; public Todo () { title =""; description =""; } @Id @GeneratedValue public long getId() { return id;} public void setId(long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) {this.title = title;} public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
In this section we will show you how the web interface is defined using JSF pages. We will also see how the data model is mapped to the web form using JSF EL. Using the #{...} notation to reference Java objects is called JSF EL (JSF Expression Language). Lets take a look at the pages used in our application:
index.xhtml: This page displays two options: 1. Create New Todo 2. Show all Todos. When you click on the Submit button the corresponding action is invoked.
<h:form> <ul> <li><h:commandLink type="submit" value="Create New Todo" action="create"/></li> <li><h:commandLink type="submit" value="Show All Todos" action="todos"/></li> </ul> </h:form>
create.xhtml: When you try to create a new task, this JSF page captures the input data. We use the todoBean
to back the form input text fields. The #{todoBean.todo.title} symbol refers to the "title" property of the "todo" object in the "TodoBean" class. The #{todoBean.todo.description} symbol refers to the "description" property of the "todo" object in the "TodoBean" class. The #{todoBean.persist} symbol refers to the "persist" method in the "TodoBean" class. This method creates the "Todo" instance with the input data (title and description) and persists the data.
<h:form id="create"> <table> <tr> <td>Title:</td> <td> <h:inputText id="title" value="#{todoBean.todo.title}" size="15"> <f:validateLength minimum="2"/> </h:inputText> </td> </tr> <tr> <td>Description:</td> <td> <h:inputTextarea id="description" value="#{todoBean.todo.description}"> <f:validateLength minimum="2" maximum="250"/> </h:inputTextarea> </td> </tr> </table> <h:commandButton type="submit" id="create" value="Create" action="#{todoBean.persist}"/> </h:form>
Figure 12.1, “The "Create Todo" web page ” shows the "Create Todo" web page with the input fields mapped to the data model.
todos.xhtml: This page displays the list of all "todos" created. There is also an option to choose a "todo" item for 'edit' or 'delete'.
The list of all 'todos' is fetched by #{todoBean.todos} symbol referring to the 'getTodos()' property in the 'TodoBean' class. The JSF dataTable
iterates through the list and displays each Todo
object in a row. The 'Edit' option is available across each row. The #{todo.id} symbol represents the "id" property of the "todo" object.
<h:form> <h:dataTable value="#{todoBean.todos}" var="todo"> <h:column> <f:facet name="header">Title</f:facet> #{todo.title} </h:column> <h:column> <f:facet name="header">Description</f:facet> #{todo.description} </h:column> <h:column> <a href="edit.faces?tid=#{todo.id}">Edit</a> </h:column> </h:dataTable> <center> <h:commandButton action="create" value="Create New Todo" type="submit"/> </center> </h:form>
Figure 12.2, “The "Show All Todos" web page ” shows the "Show All Todos" web page with the data fields mapped to the data model.
edit.xhtml: This page allows you to edit the "todo" item's 'title' and 'description' properties. The #{todoBean.update} and #{todoBean.delete} symbols represent the "update" and "delete" methods in the "TodoBean" class.
<h2>Edit #{todoBean.todo.title}</h2> <h:form id="edit"> <input type="hidden" name="tid" value="#{todoBean.todo.id}"/> <table> <tr> <td>Title:</td> <td> <h:inputText id="title" value="#{todoBean.todo.title}" size="15"> <f:validateLength minimum="2"/> </h:inputText> </td> </tr> <tr> <td>Description:</td> <td> <h:inputTextarea id="description" value="#{todoBean.todo.description}"> <f:validateLength minimum="2" maximum="250"/> </h:inputTextarea> </td> </tr> </table> <h:commandButton type="submit" id="update" value="Update" action="#{todoBean.update}"/> <h:commandButton type="submit" id="delete" value="Delete" action="#{todoBean.delete}"/> </h:form>
Figure 12.3, “The "Edit Todo" web page ” shows the "Edit Todo" web page with the mapping to the data model.
We have used XHTML pages in the sample applications because we recommend using Facelets instead of JSP to render JSF view pages.
EJB 3.0 is one of the major improvements introduced with Java EE 5.0. It aims at reducing the complexity of older versions of EJB and simplifies Enterprise Java development and deployment. You will notice that to declare a class as a 'Session Bean' you simply have to annotate it. Using annotations eliminates the complexity involved with too many deployment descriptors. Also the only interface an EJB3 Session Bean requires is a business interface that declares all the business methods that must be implemented by the bean.
We will explore the two important source files associated with the Bean implementation in our application: TodoDaoInt.java
and TodoDao.java
.
Business interface: TodoDaoInt.java
We define here the methods that need to be implemented by the bean implementation class. Basically, the business methods that will be used in our application are defined here.
</span> <!-- --><br/><span class="java_keyword">public</span><span class="java_plain"> </span><span class="java_keyword">interface</span><span class="java_plain"> </span><span class="java_type">TodoDaoInt</span><span class="java_plain"> </span><span class="java_separator">{</span> </span> <!-- --><br/><span class="java_plain"> </span><span class="java_keyword">public</span><span class="java_plain"> </span><span class="java_type">void</span><span class="java_plain"> persist </span><span class="java_separator">(</span><span class="java_type">Todo</span><span class="java_plain"> todo</span><span class="java_separator">);</span> <!-- --><br/><span class="java_plain"> </span><span class="java_keyword">public</span><span class="java_plain"> </span><span class="java_type">void</span><span class="java_plain"> delete </span><span class="java_separator">(</span><span class="java_type">Todo</span><span class="java_plain"> todo</span><span class="java_separator">);</span> <!-- --><br/><span class="java_plain"> </span><span class="java_keyword">public</span><span class="java_plain"> </span><span class="java_type">void</span><span class="java_plain"> update </span><span class="java_separator">(</span><span class="java_type">Todo</span><span class="java_plain"> todo</span><span class="java_separator">);</span> </span> <!-- --><br/><span class="java_plain"> </span><span class="java_keyword">public</span><span class="java_plain"> </span><span class="java_type">List</span><span class="java_plain"> </span><span class="java_operator"><</span><span class="java_type">Todo</span><span class="java_operator">></span><span class="java_plain"> findTodos </span><span class="java_separator">();</span> <!-- --><br/><span class="java_plain"> </span><span class="java_keyword">public</span><span class="java_plain"> </span><span class="java_type">Todo</span><span class="java_plain"> findTodo </span><span class="java_separator">(</span><span class="java_type">String</span><span class="java_plain"> id</span><span class="java_separator">);</span> <!-- --><br/><span class="java_separator">}</span>
Stateless Session Bean: TodoDao.java
The @Stateless
annotation marks the bean as a stateless session bean. In this class, we need to access the Entity bean Todo
defined earlier. For this we need an EntityManager
. The @PersistenceContext
annotation tells the JBoss Server to inject an entity manager during deployment.
@Stateless public class TodoDao implements TodoDaoInt { @PersistenceContext private EntityManager em; public void persist (Todo todo) { em.persist (todo); } public void delete (Todo todo) { Todo t = em.merge (todo); em.remove( t ); } public void update (Todo todo) { em.merge (todo); } public List <Todo> findTodos () { return (List <Todo>) em.createQuery("select t from Todo t") .getResultList(); } public Todo findTodo (String id) { return (Todo) em.find(Todo.class, Long.parseLong(id)); } }
We will build the sample application using Ant and explore the configuration and packaging details. Please install Ant if currently not installed on your computer.
Let's look at building the example application and then explore the configuration files in detail.
In Chapter 11, Sample Applications, we looked at the directory structure of the jsfejb3
sample application. At the command line, go to the jsfejb3
directory. There you will see a build.xml
file. This is our Ant build script for compiling and packaging the archives. To build the application, you need to first of all edit the build.xml
file and edit the value of jboss-dist
to reflect the location where the JBoss Application Server is installed. Once you have done this, just type the command ant
and your output should look like this:
[user@localhost jsfejb3]$ ant Buildfile: build.xml compile: [mkdir] Created dir: /jboss/gettingstarted/jsfejb3/build/classes [javac] Compiling 4 source files to /home/user/Desktop/gettingstarted/jsfejb3/build/classes [javac] Note: /jboss/gettingstarted/jsfejb3/src/TodoDao.java uses unchecked or unsafe operations. [javac] Note: Recompile with -Xlint:unchecked for details. war: [mkdir] Created dir: /jboss/gettingstarted/jsfejb3/build/jars [war] Building war: /jboss/gettingstarted/jsfejb3/build/jars/app.war ejb3jar: [jar] Building jar: /jboss/gettingstarted/jsfejb3/build/jars/app.jar ear: [ear] Building ear: /jboss/gettingstarted/jsfejb3/build/jars/jsfejb3.ear main: BUILD SUCCESSFUL Total time: 3 seconds
If you get the BUILD SUCCESSFUL message, you will find a newly created build
directory with 2 sub-directories in it:
classes: containing the compiled class files.
jars: containing three archives - app.jar
, app.war
and jsfejb3.ear
.
app.jar : EJB code and descriptors.
app.war : web application which provides the front end to allow users to interact with the business components (the EJBs). The web source (HTML, images etc.) contained in the jsfejb3/view
directory is added unmodified to this archive. The Ant task also adds the WEB-INF
directory that contains the files which aren’t meant to be directly accessed by a web browser but are still part of the web application. These include the deployment descriptors (web.xml
) and extra jars required by the web application.
jsfejb3.ear : The EAR file is the complete application, containing the EJB modules and the web module. It also contains an additional descriptor, application.xml
. It is also possible to deploy EJBs and web application modules individually but the EAR provides a convenient single unit.
Now that we have built the application, lets take a closer look at some of the important Configuration files. We have built the final archive ready for deployment - jsfejb3.ear
. The contents of your EAR file should look like this:
jsfejb3.ear |+ app.jar // contains the EJB code |+ import.sql |+ Todo.class |+ TodoDao.class |+ TodoDaoInt.class |+ META-INF |+ persistence.xml |+ app.war // contains web UI |+ index.html |+ index.xhtml |+ create.xhtml |+ edit.xhtml |+ todos.xhtml |+ TodoBean.class |+ style.css |+ META-INF |+ WEB-INF |+ faces-config.xml |+ navigation.xml |+ web.xml |+ META-INF // contains the descriptors |+ application.xml |+ jboss-app.xml
application.xml
: This file lists the JAR files in the EAR (in our case app.jar
) and tells the JBoss server what files to look for and where. The root URL for the application is also specified in this file as 'context-root'.
<application> <display-name>Sample Todo</display-name> <module> <web> <web-uri>app.war</web-uri> <context-root>/jsfejb3</context-root> </web> </module> <module> <ejb>app.jar</ejb> </module> </application>
jboss-app.xml
: Every EAR application should specify a unique string name for the class loader. In our case, we use the application name 'jsfejb3' as the class loader name.
<jboss-app> <loader-repository> jsfejb3:archive=jsfejb3.ear </loader-repository> </jboss-app>
app.jar
: This contains EJB3 Session Bean and Entity Bean classes and the related configuration files. In addition, the persistence.xml
file configures the back-end data source (in our case the default HSQL database) for the EntityManager
.
<persistence> <persistence-unit name="helloworld"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/DefaultDS</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit> </persistence>
app.war
: This contains the Web UI files packaged according to the Web Application aRchive (WAR) specification. It contains all the web pages and the required configuration files. The web.xml
file is an important file for all JAVA EE web applications. It is the web deployment descriptor file. The faces-config.xml
file is the configuration file for JSF. The navigation.xml
file contains the rules for JSF page navigation.
//faces-config.xml <faces-config> <application> <view-handler> com.sun.facelets.FaceletViewHandler </view-handler> </application> <managed-bean> <description>Dao</description> <managed-bean-name>todoBean</managed-bean-name> <managed-bean-class>TodoBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> </faces-config>
To pre-populate the database, we have supplied SQL Code (import.sql
) to run with HSQL in the examples/jsfejb3/resources
directory. When you build the application using Ant, this is packaged in the app.jar file within the jsfejb3.ear file. When the application is deployed, you should be able to view the pre-populated data.
Just as a quick aside at this point, start up the JMX console application and click on the service=Hypersonic
link which you’ll find under the section jboss
. If you can’t find this, make sure the Hypersonic service is enabled in the hsqldb-ds.xml
file.
This will take you to the information for the Hypersonic service MBean. Scroll down to the bottom of the page and click the invoke
button for the startDatabaseManager()
operation. This starts up the HSQL Manager, a Java GUI application which you can use to manipulate the database directly.
Deploying an application in JBoss is simple and easy. You just have to copy the EAR file to the deploy
directory in the 'server configuration' directory of your choice. Here, we will deploy it to the 'default' configuration, so we copy the EAR file to the JBOSS_DIST/jboss-as/server/default/deploy
directory.
You should see something close to the following output from the server:
15:32:23,997 INFO [EARDeployer] Init J2EE application: file:/jboss/jboss-as-5.0.0<release>/server/default/deploy/jsfejb3.ear 15:32:24,212 INFO [JmxKernelAbstraction] creating wrapper delegate for: org.jboss.ejb3. entity.PersistenceUnitDeployment 15:32:24,213 INFO [JmxKernelAbstraction] installing MBean: persistence.units:ear= jsfejb3.ear,jar=app.jar,unitName=helloworld with dependencies: 15:32:24,213 INFO [JmxKernelAbstraction] jboss.jca:name=DefaultDS,service= DataSourceBinding 15:32:24,275 INFO [PersistenceUnitDeployment] Starting persistence unit persistence. units:ear=jsfejb3.ear,jar=app.jar,unitName=helloworld 15:32:24,392 INFO [Ejb3Configuration] found EJB3 Entity bean: Todo 15:32:24,450 WARN [Ejb3Configuration] Persistence provider caller does not implements the EJB3 spec correctly. PersistenceUnitInfo.getNewTempClassLoader() is null. 15:32:24,512 INFO [Configuration] Reading mappings from resource : META-INF/orm.xml 15:32:24,512 INFO [Ejb3Configuration] [PersistenceUnit: helloworld] no META-INF/orm.xml found 15:32:24,585 INFO [AnnotationBinder] Binding entity from annotated class: Todo 15:32:24,586 INFO [EntityBinder] Bind entity Todo on table Todo . . . . 15:32:26,311 INFO [SchemaExport] Running hbm2ddl schema export 15:32:26,312 INFO [SchemaExport] exporting generated schema to database 15:32:26,314 INFO [SchemaExport] Executing import script: /import.sql 15:32:26,418 INFO [SchemaExport] schema export complete 15:32:26,454 INFO [NamingHelper] JNDI InitialContext properties:{java.naming.factory. initial=org.jnp.interfaces.NamingContextFactory, java.naming.factory.url.pkgs=org.jboss. naming:org.jnp.interfaces} 15:32:26,484 INFO [JmxKernelAbstraction] creating wrapper delegate for: org.jboss.ejb3. stateless.StatelessContainer 15:32:26,485 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:ear=jsfejb3.ear, jar=app.jar,name=TodoDao,service=EJB3 with dependencies: 15:32:26,513 INFO [JmxKernelAbstraction] persistence.units:ear=jsfejb3.ear, jar=app.jar,unitName=helloworld 15:32:26,557 INFO [EJBContainer] STARTED EJB: TodoDao ejbName: TodoDao 15:32:26,596 INFO [EJB3Deployer] Deployed: file:/jboss/jboss-as-5.0.0<release> server/default/tmp/deploy/ tmp33761jsfejb3.ear-contents/app.jar 15:32:26,625 INFO [TomcatDeployer] deploy, ctxPath=/jsfejb3, warUrl=.../tmp/deploy/ tmp33761jsfejb3.ear-contents/app-exp.war/ 15:32:26,914 INFO [EARDeployer] Started J2EE application: file:/jboss/jboss-as-5.0.0<release>/server/default/deploy/jsfejb3.ear
If there are any errors or exceptions, make a note of the error message. Check that the EAR is complete and inspect the WAR file and the EJB jar files to make sure they contain all the necessary components (classes, descriptors etc.).
You can safely redeploy the application if it is already deployed. To undeploy it you just have to remove the archive from the deploy
directory. There’s no need to restart the server in either case. If everything seems to have gone OK, then point your browser at the application URL.
You will be forwarded to the application main page. Figure 12.5, “Sample TODO” shows the sample application in action.