You’ve just defined the domain model of your application, and all the entities managed directly by the end-users. Now it’s time to build an administration GUI for the TicketMonster application using JSF and RichFaces. After reading this guide, you’ll understand how to use JBoss Forge to create the views from the entities, how to "soup up" the UI using RichFaces, and how to test the UI using Selenium:
We’ll round out the guide by revealing the required, yet short and sweet, configuration.
The tutorial will show you how to perform all these steps in JBoss Developer Studio, including screenshots that guide you through. For those of you who prefer to watch and learn, the included video shows you how we performed all the steps.
If you are using JBoss Enterprise Application Platform 6, Forge is available in JBoss Developer Studio 5 (Beta1 or newer).
To show the Forge Console, navigate to Window → Show View → Other, locate Forge Console and click OK. Then click the Start button in top right corner of the view.
If you are using JBoss AS 7, you should install JBoss Forge version 1.0.2.Final or higher. Follow the instructions at Installing Forge.
Open a command line and navigate to the root directory of this quickstart.
Launch Forge by typing the following command:
forge
Forge comes with a number of built in plugins, including the "scaffold" plugin, which is able to generate a full CRUD UI from JPA entities. The generated UI uses JSF as the view layer, backed by CDI beans. Internally, Forge uses Metawidget to create the CRUD screens.
Forge also includes a powerful plugin management system. The RichFaces plugin isn’t bundled with Forge, but it’s easy to install. First use the forge find-plugin
command to locate it
forge find-plugin richfaces
In this case, the plugin is just called richfaces
- easy! We can install it using the forge install-plugin
command:
forge install-plugin richfaces
This will download, compile and install the RichFaces plugin.
Forge is a powerful rapid application development (aimed at Java EE 6) and project comprehension tool. It can operate both on projects it creates, and on existing projects, such as TicketMonster. If you want to learn more about Forge …
When you cd
into a project with Forge, it inspects the project, and detects what technologies you are using in the project. Let’s see this in action:
project list-facets
Those facets detected are colored green.
As you can see, Forge has detected all the technologies we are using, such as JPA, JAX-RS, CDI and Bean Validation.
Forge supports the execution of scripts. The generation of the CRUD UI is provided as a Forge script in TicketMonster, so you don’t need to type the commands everytime you want to regenerate the Admin UI. The script will also prompt you to applyTo run the script:
run admin_layer.fsh
First, we need to add Scaffold to the project. Run:
scaffold setup --targetDir admin
to instruct Forge to generate the css, images and templates used by the scaffolded UI. Forge also adds an error page to be used when a 404 or a 500 error is encountered.
Now, we need to add RichFaces to the project. Run:
richfaces setup
You’ll be prompted for the version of RichFaces to use. Choose version 4.0.0.Final
(the default), by pressing Enter.
You can either scaffold the entities one-by-one, which allows to control which UIs are generated, or you can generate a CRUD UI for all the entities. We’ll do the latter:
scaffold from-entity org.jboss.jdf.example.ticketmonster.model.* --targetDir admin --overwrite
Forge asks us whether we want to overwrite every file - which get’s a bit tedious!
Specifying --overwrite
allows Forge to overwrite files without prompt - much better!
We now have a CRUD UI for all the entities used in TicketMonster!
Let’s test our UI on our local JBoss AS instance. As usual, we’ll build and deploy using Maven:
mvn clean package jboss-as:deploy
Let’s add support for images to the Admin UI. TicketMonster doesn’t provide support for storing images, but allows you to reference images from hosting sites on the internet. TicketMonster caches the images, so you can still use the application when you aren’t connected to the internet.
We’ll use JSF 2’s composite components, which allow to easily create new components.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<head>
<title>Cached Image</title>
</head>
<body>
<composite:interface>
<composite:attribute name="media" type="org.jboss.jdf.example.ticketmonster.services.MediaPath"/>
<composite:attribute name="id" type="java.lang.String" />
</composite:interface>
<composite:implementation>
<h:graphicImage value="#{cc.attrs.media.url}" rendered="#{!cc.attrs.media.cached}"/>
<h:graphicImage value="/rest/media/cache/#{cc.attrs.media.url}" rendered="#{cc.attrs.media.cached}"/>
</composite:implementation>
</body>
</html>
---------------------------------------------------------------------------------------------------------
The `image` composite component encapsulates the rendering of the image, pulling it from the remote location if the item is available and not cached, or pulling it from the cache if otherwise.
Adding this file to `/src/main/webapp/resources/tm/` automatically registers the component with JSF, using the namespace `xmlns:tm="http://java.sun.com/jsf/composite/tm`.
Let's go ahead and use this component to display the image in `src/main/webapp/admin/event/view.xhtml` - the page an admin uses to view an event before editing it. Open up the file in JBoss Developer Studio (or your favourite IDE or text editor). Forge has generated an entry in panel grid to display the image URL, so we can just add `<tm:image media="#{mediaManager.getPath(eventBean.event.picture)}" />` to the `<h:link>` with the id `eventBeanEventPicture`. We need to register the namespace as well, so add `xmlns:tm="http://java.sun.com/jsf/composite/tm"` to the `<ui:composition>` tag. You should end up with a file that looks a bit like:
./src/main/webapp/admin/event/view.xhtml
[source,html]
<?xml version=1.0 encoding=UTF-8 ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:tm="http://java.sun.com/jsf/composite/tm" template="/resources/scaffold/pageTemplate.xhtml">
<f:metadata> <f:viewParam name="id" value="#{eventBean.id}" /> <f:event type="preRenderView" listener="#{eventBean.retrieve}" /> </f:metadata>
<ui:param name="pageTitle" value="View Event" />
<ui:define name="header"> Event </ui:define>
<ui:define name="subheader"> View existing Event </ui:define>
<ui:define name="footer" />
<ui:define name="main"> <h:panelGrid columnClasses="label,component,required" columns="3"> <h:outputLabel for="eventBeanEventName" value="Name:" /> <h:outputText id="eventBeanEventName" value="#{eventBean.event.name}" /> <h:outputText /> <h:outputLabel for="eventBeanEventPicture" value="Picture:" /> <h:link id="eventBeanEventPicture" outcome="/admin/mediaItem/view" value="#{eventBean.event.picture}"> <tm:image media="#{mediaManager.getPath(eventBean.event.picture)}" /> <f:param name="id" value="#{eventBean.event.picture.id}" /> </h:link> <h:outputText /> <h:outputLabel for="eventBeanEventCategory" value="Category:" /> <h:link id="eventBeanEventCategory" outcome="/admin/eventCategory/view" value="#{eventBean.event.category}"> <f:param name="id" value="#{eventBean.event.category.id}" />
</h:link> <h:outputText /> <h:outputLabel for="eventBeanEventDescription" value="Description:" /> <h:outputText id="eventBeanEventDescription" value="#{eventBean.event.description}" /> <h:outputText /> <h:outputLabel value="Major:" /> <h:outputText styleClass="#{eventBean.event.major ? 'boolean-true' : 'boolean-false'}" /> <h:outputText /> </h:panelGrid>
<div class="buttons"> <h:link value="View All" outcome="search" /> <h:link value="Edit" outcome="create" includeViewParams="true" /> <h:link value="Create New" outcome="create" /> </div> </ui:define>
</ui:composition>
We can test these changes by running mvn clean package jboss-as:deploy as usual.