Java Server Faces

From no name for this wiki
Jump to: navigation, search

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

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());
	}
}

Resourcen