Java Server Faces
Contents
Custom Component
Implementation eines CustomComponents. Netbeans Projekt download
URL, um auf Faces Pages zuzugreifen
Um die URL herauszufinden, wie man auf die JSF Seiten zugreifft, konsultiert man das web.xml File:
Der erste wichtige Teil ist:
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
Ist dieser Parameter nicht angegeben, dann ist der default Wert von javax.faces.DEFAULT_SUFFIX .jsf gültig. Facelets scheint diesen Eintrag bei mir zu ingorieren :(, dort ist er .xhtml
Die zweite wichtige Komponente im web.xml ist das Mapping von der URL zum FacesServlet:
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
Nun kann man die URL herleiten, also bei mir http://blabla/myapp/faces/hello.xhtml
Message Bundles
In faces-config.xml:
<application>
<locale-config>
<default-locale>de</default-locale>
<supported-locale>de</supported-locale>
</locale-config>
<resource-bundle>
<base-name>ch.testmaster.view.resources.Messages</base-name>
<var>messages</var>
</resource-bundle>
</application>
Die Datei im Classpath heisst Messages_de.properties!
In den JSPs oder Facelets:
<h:outputText value="#{messages.meintext}"/>
Helpermethode, um in den BackingBeans zu den Messages zu gelangen:
public static String getMessage(String key){
FacesContext context = FacesContext.getCurrentInstance();
Locale locale = context.getViewRoot().getLocale();
ResourceBundle resourceb = ResourceBundle.getBundle("ch.testmaster.view.resources.Messages", locale);
String result = resourceb.getString(key);
return result;
}
Komponente finden
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
UIInput input = (UIInput) root.findComponent("questionform:questiontext");
Object value = input.getValue();
<f:view> <h:form id="questionform"> <h:inputTextarea cols="100" rows="20" value="#{addquestionform.questionText}" id="questiontext" required="true" validatorMessage="Question must be at least ten characters" requiredMessage="Field is mandatory"> <f:validateLength minimum="10"/> </h:inputTextarea> </h:form> </f:view>
Siehe auch Methode invokeOnComponent
Selected item in Listen (z.B. DataTable)
Die Tabelle:
<h:dataTable value="#{backingbean.items}" var="item" border="1" styleClass="trefferliste"> <h:column> <f:facet name="header"> <h:outputText value="Partner"/> </f:facet> <h:outputText value="#{item.name}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Bearbeiten"/> </f:facet> <h:commandLink action="#{PartnerSuchen.action}" id="partnerIdCommandLink" actionListener="#{PartnerSuchen.partnerMutierenEvent}"> <f:param id="editId" name="id" value="#{item.id}" /> <h:graphicImage value="/images/edit.jpg" height="50" width="50"/> </h:commandLink> </h:column> </h:dataTable>
In einer Action in einem Backing-Bean in JSF 1.2:
/**
* @return the items
*/
public List<Item> getItems() {
return items;
}
/**
* @param items the items to set
*/
public void setItems(List<Item> items) {
this.items = items;
}
public String action(){
log.debug("action");
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ELResolver resolver = application.getELResolver();
ELContext elContext = context.getELContext();
Item item = (Item) resolver.getValue(elContext, null, "item");
log.debug(item.getValue1());
return null;
}
Das Objekt item stammt von <h:dataTable/> Attribut var.
Im einer Action in einem Backing-Bean in JSF 1.1:
FacesContext context = FacesContext.getCurrentInstance();
PartnerMutierenBean partnerMutieren = (PartnerMutierenBean) context.getApplication().getVariableResolver().resolveVariable(context, "item");
EL
- Tutorial JEE 1.5 http://java.sun.com/javaee/5/docs/tutorial/doc/bnahq.html
- Tutorial J2EE 1.4 http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html
- Custom EL Resolver. Siehe Custom EL-Resolver
Facelets hat andere implizite Variablen als JSP!
Select Box
Im der Faces jsp:
<h:selectOneMenu value="#{deklarationDetailBean.statusId}"> <f:selectItems value="#{deklarationDetailBean.statiItems}"/> </h:selectOneMenu>
Im Backingbean:
private Long statusId = 2L;
public SelectItem[] getStatiItems(){
SelectItem[] result = new SelectItem[3];
result[0] = new SelectItem(1, "Bewilligt");
result[1] = new SelectItem(2, "nicht bewilligt");
result[2] = new SelectItem(3, "okey dokey");
return result;
}
Messages
General messages
In der JSF-JSP:
<h:messages showDetail="true" showSummary="true" layout="table"/>
In einer Action in einem Backing Bean:
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage("MySummary", "Messagedetail"));
context.addMessage(null, new FacesMessage("MySummary2", "Messagedetail2"));
Messages for a component
In der JSF-JSP:
<f:view> <h:form id="myform"> <h:inputHidden id="hiddenid" value="#{tabletestform.hp}" /> <h:message for="hiddenid" showDetail="true" showSummary="true" style="color: red"/> </h:form> </f:view>
Im Backing-Bean:
public String action(){
log.debug("action");
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage("myform:hiddenid", new FacesMessage("MySummary3", "Messagedetail3"));
return null;
}
Beachte den String myform:hiddenid.
phase listeners
Im faces-config.xml:
<lifecycle> <phase-listener>backingbeans.MyPhaseListener</phase-listener> </lifecycle>
Im Code:
package backingbeans;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import org.apache.log4j.Logger;
/**
*
* @author Claude Glauser
*/
public class MyPhaseListener implements PhaseListener {
private static Logger log = Logger.getLogger(MyPhaseListener.class);
public void afterPhase(PhaseEvent event) {
log.debug("after phase:" +event.getPhaseId().toString());
}
public void beforePhase(PhaseEvent event) {
log.debug("before phase:" + event.getPhaseId().toString());
}
/**
* Return the identifier of the request processing phase
* during which this listener is interested
* in processing PhaseEvent events.
* @return
*/
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
}
Es gibt folgende 6 Phasen:
- RESTORE_VIEW
- APPLY_REQUEST_VALUES (immediate actions)
- PROCESS_VALIDATIONS
- UPDATE_MODEL_VALUES
- INVOKE_APPLICATION (actions)
- RENDER_RESPONSE
validation
validation
<h:message for="theinput" showDetail="true" showSummary="true" style="color: red"/> <h:inputText id="theinput" value="#{tabletestform.inputtest}" required="true"> <f:validateLength minimum="13"/> </h:inputText>
validation in einem backing bean
<h:message for="theinput" showDetail="true" showSummary="true" style="color: red"/> <h:inputText id="theinput" value="#{tabletestform.inputtest}" validator="#{tabletestform.validateAComponent}"> </h:inputText> </h:inputText>
public void validateAComponent(FacesContext context, UIComponent component, Object value){
log.debug("validating " + value);
FacesMessage message = new FacesMessage("asummary","adetail");
message.setSeverity(FacesMessage.SEVERITY_INFO);
throw new ValidatorException(message);
}
conversion
custom converter
JSF-FACES:
<h:message for="theinput2" showDetail="true" showSummary="true" style="color: red"/> <h:inputText id="theinput2" value="#{tabletestform.abigdez}" converter="bigdez"> </h:inputText>
faces-config.xml:
<converter> <converter-id>bigdez</converter-id> <converter-class>backingbeans.MyCustomConverter</converter-class> </converter>
Ausschnitt aus Backingbean:
private BigDecimal abigdez = new BigDecimal(34);
/**
* @return the abigdez
*/
public BigDecimal getAbigdez() {
return abigdez;
}
/**
* @param abigdez the abigdez to set
*/
public void setAbigdez(BigDecimal abigdez) {
this.abigdez = abigdez;
}
Converter:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package backingbeans;
import java.math.BigDecimal;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import org.apache.log4j.Logger;
/**
*
* @author Claude Glauser
*/
public class MyCustomConverter implements Converter {
private static Logger log = Logger.getLogger(MyCustomConverter.class);
public Object getAsObject(FacesContext context, UIComponent component, String value) {
log.debug("getAsObject");
try {
BigDecimal bigdezimal = new BigDecimal(value);
return bigdezimal;
} catch(NumberFormatException e){
FacesMessage message = new FacesMessage("summary convert", "detail convert");
throw new ConverterException(message);
}
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
log.debug("getAsString");
String result = value.toString();
return result;
}
}
File download
JSF-JSP:
<%-- Document : filedownload Created on : 10.02.2009, 19:16:14 Author : Claude Glauser --%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <f:view> <h:form> <h:commandButton action="#{downloaddemo.downLoadFile}" value="Hello"/> </h:form> </f:view> </body> </html>
Java Code:
package backingbeans;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
/**
*
* @author Claude Glauser
*/
public class DownloadDemo {
private static Logger log = Logger.getLogger(DownloadDemo.class);
public String downLoadFile() {
log.debug("Downloading File");
FacesContext context = javax.faces.context.FacesContext.getCurrentInstance();
ExternalContext extContext = context.getExternalContext();
HttpServletResponse response = (javax.servlet.http.HttpServletResponse) extContext.getResponse();
response.setContentType("image/jpeg");
response.addHeader("Content-Disposition","filename=firefox.jpg");
InputStream is = DownloadDemo.class.getResourceAsStream("/firefox.jpg");
byte[] buffer = new byte[100];
int bytesread;
try {
OutputStream os = response.getOutputStream();
bytesread = is.read(buffer);
while (bytesread != -1) {
os.write(buffer, 0, bytesread);
bytesread = is.read(buffer);
}
os.flush();
} catch (IOException ex) {
log.error("error steaming", ex);
}
context.responseComplete();
return null;
}
}
faces-config.xml:
<managed-bean> <managed-bean-name>downloaddemo</managed-bean-name> <managed-bean-class>backingbeans.DownloadDemo</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
ice faces
Sample fuer Netbeans mit Tomcat6 mit allen Libraries: IceFacesDemo.zip Taglibrary reference: http://www.icefaces.org/docs/v1_8_0/tld/index.html
Facelets
Doku https://facelets.dev.java.net/nonav/docs/dev/docbook.html
Facelets hat andere EL implizite Objekte:
- request anstatt pageContext.request
Templates mit Facelets
template.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/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Facelets - Template Example</title>
<link href="./css/default.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>
<ui:insert name="title">Default Title</ui:insert>
</h1>
<p>
<ui:insert name="body">Default Body</ui:insert>
</p>
</body>
</html>
template-client.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/xhtml1-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">
<body>
This text above will not be displayed.
<ui:composition template="/template.xhtml">
This text will not be displayed.
<ui:define name="title">
Facelets
</ui:define>
This text will also not be displayed.
<ui:define name="body">
Hello from the Facelets client template!
</ui:define>
This text will not be displayed.
</ui:composition>
This text below will also not be displayed.
</body>
</html>
Extending JSF
Exceptionhandling
Besseres Exceptionhandling bekommt man mit einem Custom Action Listener, welcher im faces-config.xml registriert wird:
<faces-config version="1.2"
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_1_2.xsd">
<application>
<locale-config>
<default-locale>de</default-locale>
<supported-locale>de</supported-locale>
</locale-config>
<resource-bundle>
<base-name>ch.testmaster.view.resources.Messages</base-name>
<var>messages</var>
</resource-bundle>
<view-handler>com.icesoft.faces.facelets.D2DFaceletViewHandler</view-handler>
<action-listener>com.adresse.listener.ErrorActionListener </action-listener>
</application>
</faces-config>
Anbei der Code. Achtung, hier erbt man direkt von SUN Klassen, das ergibt eine Abhängigkeit zur Implementation. In neueren Versionen ist der ActionListener final.
package com.adresse.listener;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import com.impart.adresse.exception.MyException;
import com.impart.adresse.util.Utils;
import com.sun.faces.application.ActionListenerImpl;
public class ErrorActionListener extends ActionListenerImpl implements
ActionListener {
@Override
public void processAction(ActionEvent event) {
try {
super.processAction(event);
} catch (Exception exception) {
FacesContext facesContext = FacesContext.getCurrentInstance();
String message=getMessage(getMessageKey(exception));
facesContext.addMessage("name", new FacesMessage(FacesMessage.SEVERITY_ERROR,message,"Fehler:"+exception.getLocalizedMessage()));
//back to the prev view
Application application = facesContext.getApplication();
NavigationHandler navigationHandler = application
.getNavigationHandler();
navigationHandler.handleNavigation(facesContext, null,
facesContext.getViewRoot().getViewId());
facesContext.renderResponse();
}
}
private String getMessageKey(Throwable exception) {
if (exception instanceof MyException) {
return ((MyException) exception).getMessageKey();
}
if (exception.getCause() != null) {
return getMessageKey(exception.getCause());
}
return null;
}
private String getMessage(String key) {
if(key==null || key.length()<1)return "";
FacesContext context = FacesContext.getCurrentInstance();
return Utils.getMessageResourceString(context.getApplication()
.getMessageBundle(), key, null, context.getViewRoot()
.getLocale());
}
}