In this recipe, will create a JSF 2 portlet that creates a new stock purchase request. The portlet will also utilize JSR-303 for validating the form data before submission to the server.
The following are required for this recipe:
To create a portlet that uses JSF 2:
gatein.cookbook
, Artifact Id: chapter10-jsf
, and Packaging: war
.pom.xml
, add the following dependencies:<dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.portletbridge</groupId> <artifactId>portletbridge-api</artifactId> <version>3.1.0.Alpha1</version> </dependency> <dependency> <groupId>org.jboss.portletbridge</groupId> <artifactId>portletbridge-impl</artifactId> <version>3.1.0.Alpha1</version> <scope>runtime</scope> </dependency>
enum
named Status
within a package named gatein.cookbook.chapter10
with the following content:public enum Status { WAITING("Waiting"), COMPLETE("Complete"), CANCELLED("Cancelled"); private String name; Status(String name) { this.name = name; } public String getName() { return this.name; } }
enum
named TradeInstructionType
within a package named gatein.cookbook.chapter10
with the following content:public enum TradeInstructionType { MARKET("Market"), LIMIT("Limit"), STOP_LOSS("Stop Loss"), TRAILING_STOP("Trailing Stop"), GOOD_TILL_CANCELLED("Good Till Cancelled"), DAY_ORDER("Day Order"); private String name; TradeInstructionType(String name) { this.name = name; } public String getName() { return this.name; } }
enum
named OrderType
within a package named gatein.cookbook.chapter10
with the following content:public enum OrderType { BUY("Buy"), SELL("Sell"); private String name; OrderType(String name) { this.name = name; } public String getName() { return this.name; } }
OrderManager
within a package named gatein.cookbook.chapter10
with the following content:@ManagedBean @ApplicationScoped public class OrderManager implements Serializable { private static final long serialVersionUID = -5776345927432051634L; Set<Order> orders = new HashSet<Order>(); public void addOrder(Order order) { orders.add(order); } public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } }
Order
within a package named gatein.cookbook.chapter10
with the following content:@ManagedBean @RequestScoped public class Order implements Serializable { private static final long serialVersionUID = -1343926602596381203L; @ManagedProperty(value = "#{orderManager}") private OrderManager orderManager; public Order() { this.status = Status.WAITING; this.entryDate = new Date(); } private OrderType type; private TradeInstructionType tradeInstruction; private String accountNumber; private Integer numberStocks; private Date entryDate; private Status status; }
Order
:public OrderType getType() { return type; } public void setType(OrderType type) { this.type = type; } public TradeInstructionType getTradeInstruction() { return tradeInstruction; } public void setTradeInstruction(TradeInstructionType tradeInstruction) { this.tradeInstruction = tradeInstruction; } public String getAccountNumber() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; } public Integer getNumberStocks() { return numberStocks; } public void setNumberStocks(Integer numberStocks) { this.numberStocks = numberStocks; } public Date getEntryDate() { return entryDate; } public void setEntryDate(Date entryDate) { this.entryDate = entryDate; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; }
Order
:public SelectItem[] getOrderTypeValues() { SelectItem[] items = new SelectItem[OrderType.values().length]; int i=0; for (OrderType type : OrderType.values()) { items[i++] = new SelectItem(type, type.getName()); } return items; } public SelectItem[] getTradeInstructionValues() { SelectItem[] items = new SelectItem[TradeInstructionType.values().length]; int i=0; for (TradeInstructionType type : TradeInstructionType.values()) { items[i++] = new SelectItem(type, type.getName()); } return items; } public SelectItem[] getStatusValues() { SelectItem[] items = new SelectItem[Status.values().length]; int i=0; for (Status status : Status.values()) { items[i++] = new SelectItem(status, status.getName()); } return items; }
Order
:public void setOrderManager(OrderManager orderManager) { this.orderManager = orderManager; } public String create() { orderManager.addOrder(this); return "orderSuccess"; }
layout.xhtml
in the src/main/webapp/templates
folder of the project.layout.xhtml
:<f:view 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"> <h:head> </h:head> <h:body> <div id="header"> <ui:insert name="header"> <h2>New Stock Purchase</h2> </ui:insert> </div> <div id="content"> <ui:insert name="content"> </ui:insert> </div> </h:body> </f:view>
newOrder.xhtml
in the src/main/webapp
folder of the project.newOrder.xhtml
:<f:view 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"> <h:body> <ui:composition template="/templates/layout.xhtml"> <ui:define name="content"> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="Order Type" for="orderType" /> <h:selectOneMenu id="orderType" value="#{order.type}" required="true"> <f:selectItems value="#{order.orderTypeValues}"/> </h:selectOneMenu> <h:outputLabel value="Trade Instruction" for="tradeInstructionType" /> <h:selectOneMenu id="tradeInstructionType" value="#{order.tradeInstruction}" required="true"> <f:selectItems value="#{order.tradeInstructionValues}"/> </h:selectOneMenu> <h:outputLabel value="Account Number" for="acctNum" /> <h:inputText id="acctNum" value="#{order.accountNumber}" /> <h:outputLabel value="How many Stocks?" for="stocks" /> <h:inputText id="stocks" value="#{order.numberStocks}" /> </h:panelGrid> <h:commandButton action="#{order.create}" value="Create Order" /> </h:form> </ui:define> </ui:composition> </h:body> </f:view>
orderSuccess.xhtml
in the src/main/webapp
folder of the project.orderSuccess.xhtml
:<f:view 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"> <h:body> <ui:composition template="/templates/layout.xhtml"> <ui:define name="header"> <h3>Order Success!</h3> </ui:define> <ui:define name="content"> <h5>Congratulations, your order was submitted for processing</h5> <h:form> <h:commandButton action="newOrder" value="Place another order" /> </h:form> </ui:define> </ui:composition> </h:body> </f:view>
portlet.xml
in the src/main/webapp/WEB-INF
folder of the project.portlet.xml
:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>Order-JSF</portlet-name> <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class> <init-param> <name>javax.portlet.faces.defaultViewId.view</name> <value>/newOrder.xhtml</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Order JSF Portlet</title> </portlet-info> </portlet> </portlet-app>
web.xml
in the src/main/webapp/WEB-INF
folder of the project.web.xml
:<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>chapter10-jsf</display-name> <context-param> <param-name>javax.portlet.faces.renderPolicy</param-name> <param-value>ALWAYS_DELEGATE</param-value> </context-param> <context-param> <param-name>javax.faces.FACELETS_VIEW_MAPPINGS</param-name> <param-value>*.xhtml</param-value> </context-param> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> </web-app>
faces-config.xml
in the src/main/webapp/WEB-INF
folder of the project.faces-config.xml
:<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1"> </faces-config>
>mvn clean package
chapter10-jsf-1.0.0.SNAPSHOT.war
, from the target
folder into the deployment folder of where you unpacked the GateIn installation.Order-JSF
portlet to it. The Order-JSF
portlet can be found under the Chapter10-jsf
category.Step 2 adds the JBoss Portlet Bridge dependency to the project, which enables JSF 2 to work in a portal environment.
At the time of writing, the latest release of JBoss Portlet Bridge for JSF 2 is 3.1.0.Alpha1. Please check http://www.jboss.org/portletbridge for the latest available version.
Steps 3, 4, and 5 create Java enumerations for properties of an order where we have a set of valid values.
Step 6 creates OrderManager
, which uses the JSF @ManagedBean
annotation to specify it is a
JSF backing bean, and it is also marked with @ApplicationScoped
to signify that all users will see the same instance of this class. It holds a Set
of orders that have been entered through the form.
Steps 7, 8, and 9 create the Order
bean, which holds the details of an order. Step 7 specifies that the bean is @RequestScoped
, which means there will be a new instance created for each page request. Step 7 also sets the @ManagedProperty
annotation to indicate to JSF that there is a separate backing bean with the name of orderManager
that should be set onto this property.
Step 9 defines some methods to convert the enum values into SelectItems
for use in the JSF form. The methods are called from the <f:selectItems>
tag in Step 14.
Step 10 specifies the setter associated with the @ManagedProperty
, which is required for the JSF injection to work correctly. It also adds a create
method to be called when the form is submitted, with the returned string representing the name of the page to navigate to next.
Steps 11 and 12 create a basic Facelet
template for the portlet.
Steps 13 and 14 create the order form page in JSF. It consists of tags to specify different types of input elements, such as text and select lists. The action
on the h:commandButton
is set to call Order.create()
when clicked, represented through the expression language names.
Steps 15 and 16 create the success page that the user is redirected to once the order has been recorded.
Steps 17 and 18 define the metadata for the portlet. The class that the portlet uses is GenericFacesPortlet
, which is part of JBoss Portlet Bridge, so there is no need to create a portlet to use JSF. The
init-param
is important as it informs the JBoss Portlet Bridge which JSF page to display for the view Portlet Mode.
Steps 19 and 20 create the web.xml
of the project. It defines the FacesServlet
for JSF, determines which file suffixes are supported, and ensures that the JBoss Portlet Bridge should always delegate to JSF to render the content of the page.
18.119.159.178