JSF Problems And Solutions

From WikiGuruja

Jump to: navigation, search

Contents

Retrieving values of HTML components

To retrieve the value of a HTML component adjust the following code:

FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot view = context.getViewRoot();
HtmlInputText  txtAnswer = (HtmlInputText)view.findComponent("testform:txtAnswer");

The parameter testform:txtAnswer indicates that the field txtAnswer is a child of the container testform.

Retrieving values of Managed Beans

Sometimes, you need to evaluate a value binding expression in your Java code. Use a sequence of statements such as the following:

FacesContext context = FacesContext.getCurrentInstance();
ValueBinding binding = context.getApplication().createValueBinding("#{user.name}");
String name = (String) binding.getValue(context);

Including JSP pages

  • To include a JSP page with dynamic includes use <jsp:include> or <c:import> tags.
  • You must enclose the page in the <f:subview>
  • You must provide an unique id for the subview
  • All template text and non-JSF tags inside included pages should be enclosed with the JSF <f:verbatim> core tag.
  • If can load a Resource Bundle again in the included page.

Example

File: index.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

<f:loadBundle basename="de.jaraujo.LocalizationResources" var="msg"/>
<f:view>
  <html>
    <head>
      <title> My Title  </title>
      <link rel="stylesheet" type="text/css" href="css/cssexamples.css"/>
    </head>
    <body>
      <h:outputText styleClass="title" value="#{msg.jsfbyexamples}" />
      <h:form id="welcomeForm">
 	    <jsp:include page="navbuttons.jsp" />
      </h:form>
	</body>
  </html>
</f:view>

File: navbuttons.jsp

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>

<f:loadBundle basename="de.jaraujo.LocalizationResources" var="msg"/>

<f:subview id="navView">	
   	<h:commandButton value="Previous page" action="#{exBean.previousPage}" />
   	<h:commandButton value="Next page" type="submit"/>
	    <h:outputLabel for="txtExample">
    		<h:outputText id="helloInputLabel" value="#{msg.gotoexample}"/>
	    </h:outputLabel>
   	<h:inputText id="txtExample" styleClass="input1" >
    	    <f:validateLongRange minimum="1" maximum="#{exBean.maxpages}" />
   	</h:inputText>
   	<h:message id="errors" for="txtExample" style="color: red"/>
   	<f:verbatim>
    		<html>
    	</f:verbatim>
</f:subview>

</html></pre>

Advanced Navigation

To create an hyperlink to navigate to external pages programmaticaly you can use the code bellow:

	HtmlCommandLink comLink = new HtmlCommandLink();
	comLink.setOnmousedown("javascript:window.open('http://www.google.de','open_window');");
	HtmlOutputText output = (HtmlOutputText)application.createComponent(HtmlOutputText.COMPONENT_TYPE);
	output.setValue("Open Google");
	comLink.getChildren().add(output);

or you can use the <h:commandLink> tag:

        <h:outputLink id="outID1" value="http://www.google.de" target="_blank">
              <h:outputText id="outID2" value="Navigate to Google"/>
        </h:outputLink>

To redirect a request to another page use the following code:

<h:commandButton value="TESTING" type="submit" id="btntest" action="#{ebean.RedirectME}"></h:commandButton>

// Then create the code in your bean:
public String RedirectME( ) {
    FacesContext context = FacesContext.getCurrentInstance( );
    ExternalContext ec = context.getExternalContext( );
    try {
        ec.redirect("http://www.amazon.com");
    }
    catch (IOException ioe) {
        return "failure";
    }
    context.responseComplete( );
    return "success";
}

Creating and Binding Action and ActionListener

  • To create dynamically a link to a defined outcome in the faces-config.xml, create a MethodBinding object and associate it to your component.
  • Don't use methods like addActionListener, they will probably not work, because the faces context will not get to restore the state of your listener class.

Example 1: Creating an ActionListener for your component.

MethodBinding mb = facesContext.getApplication()
  .createMethodBinding("#{ebean.doIt}", new Class[] {ActionEvent.class});
  
HtmlCommandLink comLink = new HtmlCommandLink();
comLink.setActionListener(mb);

// You have to define the following method in your bean:
public void doIt(ActionEvent event)
{  ...
}

Example 2: Creating an Action for your component.

MethodBinding mb = facesContext.getApplication()
  .createMethodBinding("#{ebean.goToExample}", new Class[] {});
  
HtmlCommandLink comLink = new HtmlCommandLink();
comLink.setAction(mb);

// You have to define the following method in your bean:
public String goToExample()
{  ...
}

Example 3: Creating an Action for a method containing parameters. Solution using tags.

<h:commandLink action="#{UserViewPageBean.viewUser}" >
  <f:param name="UserID" value="#{user['UserID']}"/>
  <h:outputText value="#{user['UserID']}" />
</h:commandLink>

Example 4: Creating an Action for a method containing parameters. Solution using java code.

...
// create a link
HtmlCommandLink comLink = new HtmlCommandLink();
comLink.setId("linkexample" + (count + 1));
comLink.setValue(bundle.getString("example") + " " + (count +1));

// add an Action
MethodBinding mb = facesContext.getApplication()
	.createMethodBinding("#{ebean.onExampleClick}", new Class[] {});
comLink.setAction(mb);

// add the parameter for the method onExampleClick
UIParameter parameter = new UIParameter();
parameter.setName("exampleID");
parameter.setValue(outcome);
comLink.getChildren().add(parameter);
...

public String onExampleClick()
{
	try {
	// get the faces context
	FacesContext facesContext = FacesContext.getCurrentInstance();
	Map map = facesContext.getExternalContext().getRequestParameterMap();
	
	for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
	     Map.Entry entry = (Map.Entry) iterator.next();
	     String key = (String)entry.getKey();
	     String value = (String)entry.getValue();
	     
	     if (key.equals("exampleID"))
	     {
	    	 // update the current page
	    	 page = Integer.parseInt(value);
	    	 return "example" + value;
	     }
	 }
	} catch (Exception e)
	{
		e.printStackTrace();
	}
	
	return "mainmenu";
}

Example 5: Creating an Action using a constant value for the UIComponent.

...
HtmlCommandLink comLink = new HtmlCommandLink();
String outcome = "success";
setAction(comLink, outcome);
...

public static void setAction(UIComponent component, String attributeValue) {
      if (attributeValue == null) return;
      if (UIComponentTag.isValueReference(attributeValue)) 
         setMethodBinding(component, "action", attributeValue,
               new Class[] {});
      else {
         FacesContext context = FacesContext.getCurrentInstance();
         Application app = context.getApplication();
         MethodBinding mb = new ConstantMethodBinding(attributeValue);
         component.getAttributes().put("action", mb);         
      }
   }
   
   public static void setMethodBinding(UIComponent component, String attributeName,
         String attributeValue, Class[] paramTypes) {
      if (attributeValue == null)
         return;
      if (UIComponentTag.isValueReference(attributeValue)) {
         FacesContext context = FacesContext.getCurrentInstance();
         Application app = context.getApplication();
         MethodBinding mb = app.createMethodBinding(attributeValue, paramTypes);
         component.getAttributes().put(attributeName, mb);
      }
   }

Note: The setAction and setMethodBinding methods should be in your final class defined. I think that other solution can be used for the problem of action using constant values. I did not test it, but the solution in the example 4 (described above) should work on it.

Preventing validations for the other components

To prevent validations for the other components in the form, we have one more thing to do: call the faces context renderResponse method at the end of our value change listener, like this:

	public void countryChanged(ValueChangeEvent event) {
		FacesContext context = FacesContext.getCurrentInstance();
                // change the language of the view object
		if(US.equals(event.getNewValue()))
			context.getViewRoot().setLocale(Locale.US);
		else
			context.getViewRoot().setLocale(Locale.CANADA);

		context.renderResponse();
	}
}

The call to renderResponse() skips the rest of the life cycle—including validation of the rest of the input components in the form—up to Render Response. Thus, the other validations are skipped and the response is rendered normally (in this case, the current page is redisplayed).

JavaScript And JSF

You’ll probably add client-side scripts to your JSF pages soon after you start using JSF. One common use is to submit a request when an input’s value is changed so that value change listeners are immediately notified of the change, like this:

<h:selectOneMenu onchange="submit()"...>

Validating input at the client side

For example, you can add a command button and use JavaScript to validate the fields password and confirm password.

<h:commandButton type="button" onclick="checkPassword(this.form)"/>

... 

function checkPassword(form) {
	var password = form["registerForm:password"].value;
	var passwordConfirm = form["registerForm:passwordConfirm"].value;
	
	if (password == passwordConfirm)
		form.submit();
	else
		alert("Password and password confirm fields don't match");
}
Image:caution.png Warning: If you want to stop the call of an action or actionListener attribute, you have to use the javaScript function direct in the event returning false to stop the call for these functions. The use of JavaScript methods written in a external javaScript file has failed. See the example bellow. For more information consult the link Javascript with JSF.
<h:commandLink actionListener="#{beanCategory.deleteAction}" 
               onclick="if (!confirm('Would you really like to delete this record?')) return false">
   <h:graphicImage value="/images/delete.gif" style="border: 0px"
   alt="#{translation.delete_tooltip}" />
</h:commandLink>

Using Filters for Authorization

Filters are a standard feature of the Servlet API—they allow you to perform additional processing on a request before and after it’s handled by traditional servlets (Intercepting Filter pattern [Alur, Crupi, Malks]).

Filters are commonly used for authorization duties because they can reroute a request if the user isn’t authorized to view the requested resource.

AuthorizationFilter.java: Redirects to the login page unauthenticated users and denies access to unauthorized resources. [JSF in Action, 2005, p. 546-551]

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;

import org.jia.ptrack.domain.RoleType;

public class AuthorizationFilter implements Filter
{
  FilterConfig config = null;
  ServletContext servletContext = null;

  public AuthorizationFilter()
  {
  }

  public void init(FilterConfig filterConfig) throws ServletException
  {
    config = filterConfig;
    servletContext = config.getServletContext();
  }

  public void doFilter(ServletRequest request, ServletResponse response,
                       FilterChain chain) throws IOException, ServletException
  {
    Utils.log(servletContext, "Inside the filter");

    HttpServletRequest httpRequest = (HttpServletRequest)request;
    HttpServletResponse httpResponse = (HttpServletResponse)response;
    HttpSession session = httpRequest.getSession();

    String requestPath = httpRequest.getPathInfo();
    Visit visit = (Visit)session.getAttribute(Constants.VISIT_KEY);
    if (visit == null)
    {
      session.setAttribute(Constants.ORIGINAL_VIEW_KEY, httpRequest.getPathInfo());
      Utils.log(servletContext, "redirecting to " + httpRequest.getContextPath() +
                Constants.LOGIN_VIEW);
      httpResponse.sendRedirect(httpRequest.getContextPath() +
                                Constants.LOGIN_VIEW);
    }
    else
    {
      session.removeAttribute(Constants.ORIGINAL_VIEW_KEY);
      RoleType role = visit.getUser().getRole();

      if ((role.equals(RoleType.UPPER_MANAGER) &&
           requestPath.indexOf(Constants.PROTECTED_DIR) > 0) ||
          (!role.equals(RoleType.PROJECT_MANAGER) &&
           requestPath.indexOf(Constants.EDIT_DIR) > 0))
      {
        String text = Utils.getDisplayString(Constants.BUNDLE_BASENAME,
                                             "PathNotFound",
                                             new Object[] { requestPath },
                                             request.getLocale());
        httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND,
                               text);
      }
      else
      {
        chain.doFilter(request, response);
      }
    }
    Utils.log(servletContext, "Exiting the filter");
  }

  public void destroy()
  {
  }
}

Problems with Cookies

Problem: If you are using more than 20 cookies or the cookie size is bigger than 4Kb, please see this problem http://www.thismuchiknow.co.uk/?p=13

Solution: Try to put everything in one String separated by "@@" as separator. The order of the objects to save and load is important. It will reduce that amount of cookies.

Personal tools
    stats :