Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

MELJUN CORTES Jedi course notes web programming-lesson10-advanced jsf


Published on

MELJUN CORTES Jedi course notes web programming-lesson10-advanced jsf

  • Be the first to comment

  • Be the first to like this

MELJUN CORTES Jedi course notes web programming-lesson10-advanced jsf

  1. 1. Advanced JavaServerFaces TopicsIntroductionIn the previous lesson, we learned about the Java Server Faces framework: how itcombines a front controller servlet, action handlers, and a set of UI components tosimplify the task of developing an application under the Model – View – Controllerarchitecture. We have seen how JSF can create a more Swing-like style of programminginto the web environment, with its event-based component architecture.In this lesson, we will discuss the FacesContext object, which holds all contextualinformation within the JSF framework. We will see another set of components providedfor use by the framework: validators and type converters. We will also learn how tocreate such components on our own, and how to develop our own set of custom tagssuch that the components we create can be represented more easily in JSP pages.FacesContextIn the previous discussion, we have briefly seen how we can make use of theFacesContext object to access a Map of the objects currently in the user session. We willcover that topic and another use of the FacesContext object in more detail here.As a review item, an instance of the FacesContext object can be obtained by calling theFacesContext.getCurrentInstance() method.FacesContext and the Component TreeFor each of the views making use of JSF UI elements, there is a correspondingcomponent tree model for it. The JSF specification requires that all implementations ofJSF store a this component tree inside the FacesContext object. This allows developersaccess to all of the user interface components and their data.There are a number of things that we can do with this kind of access to the componenttree. We can programatically add components to the view, dynamically change or addconverters or validators, or remove components.Most often though, we use this capability to provide screen redirection within the Facesframework. This can be done by changing the current component tree being accessed bythe user with the component tree that makes up another page.Below is a sample piece of code that accomplishes this task, with the relevant line inbold.
  2. 2. ... String targetPage = "/newPage.jsp"; FacesContext context = FacesContext.getCurrentInstance(); context.getApplication().getViewHandler().createView(context,targetPage);...The createView method creates a copy of the component tree that makes up thetargetPage and places it as the current view component tree. Once the frameworkdisplays content again, it will display the contents of the target page.FacesContext and the External ContextThe ExternalContext object accessible through FacesContext gives us access to theenvironment the framework is running on. In our case (a web application), it gives usaccess to the HttpServletRequest object representing the current request, a Map of theobject stored in the user session, as well as the ServletContext object representing thecontext of the whole web application.To retrieve the HttpServletRequest object: FacesContext context = FacesContext.getCurrentInstance(); HttpServletRequest request = (HttpServletRequest)context.getExternalContext() .getRequest();Unfortunately, there is no way to have direct access to the actual HttpSession objectassociated with our session, but the objects stored within it can be accessed through aMap object: Map sessionMap = context.getExternalContext().getSessionMap();Retrieving the ServletContext representing our entire application is done by performingthe following code: ServletContext servletContext = (ServletContext)context.getExternalContext().getContext();Once we have access to these objects, we can then make use of other web programmingtechniques.ValidatorsIn our second lesson on the Struts framework, we have discussed how important datavalidation is in any data-based application. As people would say: garbage in- garbageout; any applications results is always restrained by the correctness of the data ithandles.JSF has also recognized this need for validation and provides us with a set of validatorsthat we can use out-of-the-box for our application. It also gives us several ways toattach validation code into our data input components.
  3. 3. JSF Standard ValidatorsJSF provides for us 3 standard validators: • DoubleRangeValidator – checks if the value in the input field is convertible into a double and that it is within the supplied minimum and maximum values. JSP Tag: validateDoubleRange. Attributes: minimum, maximum. • LengthValidator – checks if the value in the input field is convertible into a String and that its length is within the supplied minimum and maximum values. JSP Tag: validateLength. Attributes: minimum, maximum. • LongRangeValidator - checks if the value in the input field is convertible into a long and that it is within the supplied minimum and maximum values. JSP Tag: validateLongRange. Attributes: minimum, maximum.The JSP tags for the validators can all be found inside the core tag library. REMINDER: tomake use of tags within the core library, we add the following line to the top of thepage:<%@taglib uri ="" prefix="f" %>Using the standard validatorsMaking use of the standard validators is as easy as inserting the JSP tag for the validatorinside the body of the input component to be validated. Look at the following example:... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validateLength minimum=”4”/> </h:inputSecret><br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/> </h:form>...This is the JSP code for the login page we implemented in the previous lesson using JSF.Only, it is modified such that the password field would accept only an input with aminimum length of 4. The exact modifications done were: • the tag defining the password field (<h:inputSecret>) was changed such that the tag no longer closed itself on the same line it was declared. It now makes use of a matching </h:inputSecret> tag to close itself. • the tag defining the validation to be performed (<f:validateLength>) was inserted between the opening and closing <h:inputSecret> tags.If we had wanted to accept input with a maximum length instead of a minimum, we
  4. 4. could replace the minimum attribute with the maximum attribute, and specified the maxlength there. We could also make use of both attributes at the same time, to define arange in between which an input will be accepted. This works the same way for the<f:validateLongRange> and <f:validateDoubleRange> tags, the only difference beingthe data types that would be accepted.Customized ValidationThe standard validators are pretty limited in what they are able to do; we very likelyneed to create validation code of our own to properly check the data for our application.There are three ways to do so: we could either a) extend the class of the UI componentthat accepts our input so that we could override its validate method; b) create anexternal validation method; or c) create our own separate Validator implementations,register them in the framework, then plug it into the UI component.The problem with the first method, that of extending a class to override its validatemethod, is that it is non-portable: the validation can only be performed when using thespecific UIComponent. In this lesson, we will be concentrating more on the two otheroptions. . Using an external validation methodThe first option we will discuss in creating our custom validation is the creation of anexternal validation method. This procedure is similar to creating an application methodthat will handle action events. We also need to create a method adhering to a set ofrules inside a JavaBean managed by the framework and later bind that method to theappropriate UI component.Creating an external validation methodMethod signatureThe method that we create must conform to the following rules: • The method must be declared public, with a return type of void. • There is no limitation to the method name. • It must take the following parameters in this order: FacesContext ctxt, UIInput component, Object value • It must be declared to throw a ValidatorExceptionIf we were to create a custom validation method for our password field in the previousexample, one that would accept only alternating letters and numbers, its methodsignature would look like this:public void validatePassword(FacesContext ctxt, UIInput component, Object value) throws ValidatorExceptionMethod implementationIn our implementation of the method, we can make use of data provided through theparameters. The FacesContext object provides us access to external resources such asthe objects in request and session scope, as well as other objects within the Facesframework. The UIInput object is the instance of the input component that requires
  5. 5. validation; having an instance of this object gives us access to the components state.Finally, the Object value is the value inside the component that requires validation.The validation process is simple: if the framework does not receive anyValidatorExceptions, the input is accepted. If the framework does receive aValidatorException, further processing is halted, and the page containing the inputcomponent is redisplayed.Below is a sample of what the implementation for our method would look like:public void validatePassword(FacesContext ctxt, UIInput component, Object value) throws ValidatorException { if (null == value) return; if ( ! (isPasswordValid(value.toString() ) ) { FacesMessage message = new FacesMessage(“Input error”, “Password is notvalid”); throw new ValidatorException(message); }}protected boolean isPasswordValid(String password) {...We encounter a new class in the above sample: FacesMessage. This class simply modelsa message within the Faces framework. The first parameter passed into its constructor asummary, while the second passed the details of the actual message. It is used here toto indicate to the framework which message will be recognized by the framework as anerror message.To be able to actually display the generated error message, developers should add an<h:messages> or <h:message> tag in the JSP page as shown below:... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validateLength minimum=”4”/> </h:inputSecret> &nbsp; <h:message for=”password” styleClass=”error”/><br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/> </h:form>...The <h:message> tag displays messages only for a specific component, indicated by the
  6. 6. value in the for attribute. We can also specify the CSS style class that will be applied tothe message. An <h:messages> tag will display all the messages available for all thecomponents in the page. . Creating a separate Validator implementationInstead of creating our method inside a managed JavaBean, we could also create aseparate class that implements the Validator interface. This interface defines onemethod: a validate method whose signature is exactly that of an external validationmethod: its public, returns void, and passed instances of the FacesContext, UIInput,and Object classes as parameters.In terms of internal method implementation, it is no different from that of a separateValidator implementation and that of an external validation method. What differentiatesthese two then is in how they are used: an external validation method is used more forvalidation code specific to a particular component, while a separate Validatorimplementation is used to contain general-purpose validation code that will be reusedextensively within your application(s).Registering a Validator componentTo use a custom Validator component, we must first register it to be recognized by theJSF Framework. This is done by placing a configuration entry in the faces-config.xml file.An example is shown below:<validator> <description>A validator for checking the password field</description> <validator-id>AlternatingValidator</validator-id> <validator-class>jedi.sample.jsf.validator.AlternatingValidator</validator-class></validator>As we can see, a configuration entry for a new validator is as simple as defining an id bywhich it would be referred to by the framework, as well as the fully qualified class namethat implements the validating functionality.Using a validator componentTo use the Validator component that we just registered, we use the <f:validator> tagand supply its validatorId attribute our validators id as illustrated below:
  7. 7. ... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validator validatorId=”AlternatingValidator”/> </h:inputSecret> &nbsp; <h:message for=”password” styleClass=”error”/> <br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/> </h:form>...Adding attributes to our validatorIf we were to look at the validateLength standard validator supplied by the JSFframework, we could see that it contains two attributes by which its operation can befurther modified. We could similarly have attributes for our custom validator.To do this, we add a property within our class for each of the attributes it would supportand implement get and set methods for each of them. Next, we include an entry for eachof the attributes within our configuration entry for the validator.package jedi.sample.jsf.validator;public class AlternatingValidator implements Validator { private Integer interval; public Integer getInterval() { return interval; } public void setInterval(Integer interval) { this.interval = interval; } public void validate(FacesContext ctxt, UIInput component, Object value) throws ValidatorException { if (null == value || interval == null) return; if ( ! (isPasswordValid(value.toString() ) ) { FacesMessage message = new FacesMessage(“Input error”, “Password is notvalid”); throw new ValidatorException(message); } } protected boolean isPasswordValid(String password) { ...
  8. 8. <validator> <description>A validator for checking the password field</description> <validator-id>AlternatingValidator</validator-id> <validator-class>jedi.sample.jsf.validator.AlternatingValidator</validator-class> <attribute> <attribute-name>interval</attribute-name> <attribute-class>java.lang.Integer</attribute-class> </attribute></validator>A value can be supplied for this attribute by making use of the <f:attribute> tag:... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validator validatorId=”AlternatingValidator”/> <f:attribute name=”interval” value=”5”/> </h:inputSecret> &nbsp; <h:message for=”password” styleClass=”error”/> <br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/> </h:form>...ConvertersConverters are another important component set that JSF provides. They provide asolution to a basic problem plaguing web programmers: how to convert the values givenby the user from their native string representation into the appropriate format or typeused internally by the server. They are also bi-directional int that they can also be usedto change how internal data is displayed to the user.Converters are able to do this by defining a getAsString() and getAsObject() method thatcan be called by the framework at the appropriate time. The getAsString is called toprovide a String representation to the data, while the getAsObject is used to convert theString into the type required.Converters are associated to input types by making use of the converter attribute built-in into some of the input components provided by Faces. This is illustrated by theexample below, which modifies an inputText tag so that it naturally allows for Integerconversion:<h:inputText converter=”Integer” value=”#{myFormBean.myIntProperty}”/>Aside from the inputText component, the converter attribute is also supported by theinputSecret, inputHidden, and outputText components.
  9. 9. Integer here is the converter ID assigned to one of the standard converter elementsprovided by the framework. The other standard converters are listed below:
  10. 10. Converter Class Converter ID BigDecimalConverter BigDecimal BigIntegerConverter BigInteger IntegerConverter Integer ShortConverter Short ByteConverter Byte ShortConverter Short CharacterConverter Character FloatConverter Float DoubleConverter Double BooleanConverter Boolean DateTimeConverter DateTime NumberConverter NumberOf this set of converters, DateTimeConverter and NumberConverter deserve specialmention. The other converter sets are non-configurable; they can only be used like inthe example above. DateTimeConverter and NumberConverter, however, can make useof special tags. Through these tags they can expose attributes through which adeveloper can customize their behavior.DateTimeConverterThe DateTimeConverter can be used to convert use input into instances of java.util.Date.It provides a number of attributes with which the developer can specify the format usedin the conversion. It is important to note though, that the format specified is enforcedONLY when the user supplies his input. That is, if the user does not supply his input inthe format specified in the attributes, a conversion error will occur, and the frameworkwill halt further processing of data.Here are the attributes available: • dateStyle – one of the date styles defined in java.text.DateFormat. Its possible values are: default, short, long, medium, or full. • parseLocale – the locale to use during conversion. • pattern – the formatting pattern to be used in conversion. If a value for this attribute is specified, the system will ignore any values for dateStyle, timeStyle, and type. • timeStyle – one of the time styles defined in java.text.DateFormat. The possible values for this attribute are: default, short, medium, long, or full. • timeZone – the time zone to use. • type – a String that defines whether the output will be a data or time instance, or both. The possible values are: date, time, and both. Default value is date.These attributes are made available to the developer with the <f:convertDateTime> tag.Lets make a short example in the usage of the DateTimeConverter. First off, create a
  11. 11. blank web application ready for JSF, as per instructions in the previous lesson. Then,lets start off by creating a simple bean that will hold the date we will input later:import java.util.Date;public class DateBean { private Date date; public Date getDate() { return date; } public void setDate(Date date) { = date; }}Then, lets add its configuration entry in the faces-config.xml: <managed-bean> <description> This bean serves as the backing model for our date conversion example </description> <managed-bean-name>dateBean</managed-bean-name> <managed-bean-class>jedi.sample.DateBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>date</property-name> <null-value/> </managed-property> </managed-bean>Finally, we create the JSP file that will generate the view:<%@taglib uri="" prefix="f"%><%@taglib uri="" prefix="h"%><f:view> <h:form id="testForm"> <h:outputLabel for="dateField">Date : </h:outputLabel> <h:inputText id="dateField" value="#{}" required="true"> <f:convertDateTime pattern="M/d/yy"/> </h:inputText> <br/> <h:message for="dateField"/> <br/> <h:commandButton id="button" value="Start conversion"/> </h:form></f:view>
  12. 12. A couple of notes for the sample JSP page: • For our DateTimeConverter, we specified a pattern of M/d/yy. Since the converter enforces this pattern on the user, the user would have to input something like 1/13/06 as a date before he can continue. For other possible patterns, check out the API documentation for java.text.SimpleDateFormat. • We added a required attribute with a true value for our input field. This ensures that the user cannot post blank inputs. • We made use of an <h:message> tag associated with our dateField input. This makes sure that any messages generated by the input field (like those generated by conversion errors) are displayed. • The action attribute for the commandButton has deliberately been left blank, so we can focus solely on the messages generated by the converter.Loading the web application into a compatible server (in our case an instance ofAppServer 8.1) and accessing the page we just created would result in the followingscreen being shown: Figure 1: Caption of the Sample JSPTrying out an input of January 13, 2006 would result in the following:
  13. 13. Figure 2: Result of the Sample JSP Using ConverterEntering a date in the proper format would result in no change to our page. Thisindicates that the data was successfully converted and stored in our bean.What if we did not use the converter?To illustrate the benefit of the converter, lets modify the JSP page such that theconverter is removed:<%@taglib uri="" prefix="f"%><%@taglib uri="" prefix="h"%><f:view> <h:form id="testForm"> <h:outputLabel for="dateField">Date : </h:outputLabel> <h:inputText id="dateField" value="#{}" required="true"/> <br/> <h:message for="dateField"/> <br/> <h:commandButton id="button" value="Start conversion"/> </h:form></f:view>Now, entering a date of any format will cause an error:
  14. 14. Figure 3: Result of Sample JSP without converterThis is because the property associated with our input field is a Date object. Theframework cannot automatically change the String representation of the input into adate, so it looks for a Converter object that will handle it. Since none were found, itdisplays a null error.NumberConverterThe other special converter that we will look at is the NumberConverter. This converteris used to convert number inputs or enforce special formatting on them. The followingare the attributes by which we can control this converters behaviour: • currencyCode – an ISO 4217 currency code to be applied when formatting currency. • currencySymbol – the currency symbol to use when formatting currency. • groupingUsed – indicates whether a grouping symbol will be used (for example, commas after every 3 digits). • integerOnly – indicates whether the integer part only of the number will be displayed. For inputs, this means that the decimal part will be truncated before the data is stored in the bound property. • locale – the locale used. • maxIntegerDigits – the maximum number of digits shown or used when handling the integer part of the number. • maxFractionDigits – the maximum number of digits that will be shown or used when handling the decimal part of the number. • minFractionDigits - the minimum number of digits that will be shown or used when handling the decimal part of the number. • minIntegerDigits - the minimum number of digits that will be shown or used when handling the integer part of the number. • pattern – the pattern to be used when formatting or displaying the number. For more details on the allowed pattern, check the JavaDoc for java.text.NumberFormat.
  15. 15. • type – indicates whether the number to be converted should be treated as a currency, percent, or number. Possible values are currency, percent, and number. For more details, check the JavaDoc for java.text.NumberFormat.One important thing to note in using NumberConverter is that, like inDateTimeConverter, any patterns or symbols indicated in the attributes are enforced asrules on the user. If the users input does not follow the pattern or does not indicate therequired symbol in his input, a conversion error will occur and further processing on thedata will halt.One of the problems with the converters supplied as standard with JSF is that, theyeither perform only standard conversion or they enforce input patterns on the user. Forexample, in the NumberConverter, if we had specified a value of P in thecurrencySymbol attribute, any user not entering a P along with the numbers wouldencounter an error.Fortunately, JSF provides an easy way for developers to create their custom convertersfor use in the framework.Custom ConvertersCustom converters can be created by creating a class which implements the Converterinterface. This interface defines two methods: • public Object getAsObject(FacesContext ctxt, UIComponent component, String input) • public Object getAsString(FacesContext ctxt, UIComponent component, Object source)Consider the following scenario: we have a form page that requires the user to input amonetary amount. We would like to store this input as a Double object so as to facilitateeasier processing later on. However, the input field must be flexible enough to handlestraight number inputs (ex. 2000), or inputs that include grouping symbols (2,000). Asanother caveat, since the page will revert control back to itself, the data should beredisplayed as having grouping symbols.One would think that since this operation involves numbers and grouping symbols, wecould simply make use of the NumberConverter supplied with the framework. However,the NumberConverter enforces whatever pattern we supply to it, so if we asked it tohandle currencies with grouping symbols it would handle ONLY currencies with groupingsymbols. Straight numerical input would result in conversion errors. It is also notpossible to have multiple converters for a single input component, so we cannot simplymake use of two different NumberConverter instances. It is with this scenario in mindthat the samples for custom converter creation were made. . getAsObject methodThis method is called by the framework on the associated converter when it needs toconvert the users input from its string representation into another object type. Theconcept in implementing this method is simple: perform processing operations on theprovided String parameter and return the Object that will represent it on the server.
  16. 16. Considering our scenario above, our task in this method is to consistently convert theString input into a Double object, regardless of whether the input is a straight number,or one with grouping symbols.The implementation for this method is given below: public Object getAsObject(FacesContext facesContext, UIComponent uIComponent, String str) { Double doubleObject = null; try { doubleObject = Double.valueOf(str); } catch (NumberFormatException nfe) { //if exception occurs, one probable cause is existence of grouping symbol //retrieve an instance of a NumberFormat object //and make it recognize grouping symbols NumberFormat formatter = NumberFormat.getNumberInstance(); formatter.setGroupingUsed(true); // use the NumberFormat object to retrieve the numerical value of the input try { Number number = formatter.parse(str); if (number.doubleValue() <= Long.MAX_VALUE) { Long value = (Long) number; doubleObject = new Double(value.doubleValue()); } else { doubleObject = (Double)number; } } catch (ParseException pe) { // if code reaches this point, none of the supported formats were followed. // create an error message and display it to the user through the exceptionobject FacesMessage message = new FacesMessage("Unsupported format", "This field supports only numbers (5000), or numbers with commas(5,000)"); throw new ConverterException(message); } } return doubleObject; } . getAsStringThis method makes Converters bi-directional. It dictates the String representation of therelevant internal data. The concept behind the implementation of this object is simplerthan that of the getAsObject method above. It displays the amount stored within theDouble object as a number with grouping symbols. This implementation simpler becausewe know with certainty the exact format of our input.
  17. 17. Heres the implementation: public String getAsString(FacesContext context, UIComponent component, Objectsource) { Double doubleValue = (Double)obj; NumberFormat formatter = NumberFormat.getNumberInstance(); formatter.setGroupingUsed(true); return formatter.format(doubleValue); }Using the custom converterAfter we have created our custom converter, one of the first steps we have to take is toconfigure the framework so that it can recognize its existence. This is done by adding aconfiguration entry in the faces-config.xml, which defines a strict structure for itselements. Configuration entries for converters come after all entries for validators, butbefore any entries for managed beans.Below is the configuration entry for the converter we had just created.<converter> <description>Custom converter for accepting monetary input</description> <converter-id>myConverter</custom-id> <converter-class>jedi.sample.MyCustomConverter</converter-class></converter>After we have created a configuration entry for our converter, using it for our inputelements is as easy as specifying its converter id within an <f:converter> tag, like so:<h:inputText id="amount" value="#{numberBean.amount}"> <f:converter converterId="myConverter"/></h:inputText>