1. Trang chủ
  2. » Công Nghệ Thông Tin

Mastering JavaServer™ Face phần 9 pot

49 189 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Nội dung

Renderer identified by whatever the component’s rendererType method returns. The implementation of getSharedRenderer always looks up the Renderer for UIScroller.TYPE. This strategy provides great flexibility to your component in the hands of other developers. If another developer chooses to use the scroller component but wants to render it with a different Renderer, they are free to do that but they can always revert to the packaged Renderer by simply setting the rendererType attribute to null. One final point about local rendering on components that is important to remember: The implementation of decode could have been done with a copy of the code from the Renderer (what we have been trying to avoid). In that case, the Renderer and the component would tend to have divergent imple- mentations (that is, over time bugs would be fixed in one place but not the other, and so on), which could lead to a lot of confusion and trouble in using the scroller. Remember to implement the rendering functionality only once in a default Renderer, then delegate processing to that implementation. Encoding The next group of methods handles encoding the component into HTML. The encodeBegin method is shown again for quick reference: public void encodeBegin(FacesContext context) throws IOException { logger.entering(getClass().getName(), “encodeBegin”); if (context == null) { NullPointerException npe = new NullPointerException( “FacesContext is null while trying “ + “to encode “ + getId()); logger.throwing(this.getClass().getName(), “encode”, npe); throw npe; } if (getRendererType() == null) { Renderer renderer = getSharedRenderer(context); renderer.encodeBegin(context, this); } else { // render kit render super.encodeBegin(context); } } As you can see, much of the same functionality is present in this method as in the decode method we just looked at. In the same way that we want to avoid copying and pasting the decode functionality, we want to avoid copying and pasting the encode functionality. All three encode methods have the same basic structure—they just delegate processing to the appropriate method on the Renderer. Next, we will cover the Renderer for the UIScroller component. 366 Chapter 10 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 366 UIScroller Renderer Code Listing 10.2 is the code for the scroller Renderer. In the following paragraphs, we will cover each of the interesting aspects of the implementation in detail. Keep in mind that one of the key factors in the implementation of the Renderer is to enable reuse for both local and delegated rendering. The particulars of the implementation that were written a certain way to facilitate this reuse will be called out below in the discussion. Also, recall that the Renderer has two major functions: first it must encode and decode the response and request, respec- tively. Second the render needs to work properly with development tools. The particulars of the implementation pertaining to these two basic responsibilities will also be discussed later in this section. public class ScrollerRenderer extends Renderer { private static Logger logger = Logger.getLogger(Constants.LOGGER_NAME); public static final String SELECTED_VALUE_CLASS_ATTR = “selectedClass”; public static final String TYPE = “ScrollerRenderer”; public ScrollerRenderer() { super(); } public void decode(FacesContext context, UIComponent component) { checkState(context, component); UIScroller scroller = (UIScroller) component; decodeCommandName(context, scroller); UIForm parentForm = ComponentTreeUtils.getParentForm(scroller); if (null == parentForm) { queueMissingFormErrorMessage(context, scroller); } } private void decodeCommandName( FacesContext context, UIScroller scroller) { String clientId = scroller.getClientId(context); String rightClientId = getRightClientId(clientId); String leftClientId = getLeftClientId(clientId); Map requestParameterMap = context.getExternalContext().getRequestParameterMap(); Listing 10.2 ScrollerRenderer. (continued) Custom JSF Components 367 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 367 String value = (String) requestParameterMap.get(clientId); String rightValue = (String) requestParameterMap.get(rightClientId); String leftValue = (String) requestParameterMap.get(leftClientId); String commandName = null; logger.fine(“rightValue = “ + rightValue); logger.fine(“leftValue = “ + leftValue); if (null != rightValue && clientId.equals(rightValue)) { scroller.performRightAction(context); commandName = rightValue; } else if (null != leftValue && clientId.equals(leftValue)) { scroller.performLeftAction(context); commandName = leftValue; } else { throw new IllegalStateException(“no valid value returned to decode:” + “ rightValue = “ + rightValue +” leftValue = “ + leftValue + “ clientId = “ + clientId); } } private String getLeftClientId(String clientId) { String leftClientId = clientId + “L”; return leftClientId; } private String getRightClientId(String clientId) { String rightClientId = clientId + “R”; return rightClientId; } public void encodeBegin(FacesContext context, UIComponent component) throws IOException { checkState(context, component); if (!component.isRendered()) { return; } UIScroller scroller = (UIScroller) component; UIForm form = ComponentTreeUtils.getParentForm(scroller); if (null != form) { String formName = getFormName(form); String clientId = scroller.getClientId(context); String rightClientId = getRightClientId(clientId); String leftClientId = getLeftClientId(clientId); List elements = new ArrayList(); Listing 10.2 (continued) 368 Chapter 10 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 368 // Set up the left image. elements.add( imgElement( scroller.getLeftScrollImg(), leftClientId, clientId, formName)); elements.add(selectedElement(scroller)); // Set up the right image. elements.add( imgElement( scroller.getRightScrollImg(), rightClientId, clientId, formName)); elements.add(hiddenElement(rightClientId)); elements.add(hiddenElement(leftClientId)); output(elements, context); } else { queueMissingFormErrorMessage(context, scroller); } } private void checkState( FacesContext context, UIComponent component) { if (null == context) { NullPointerException npe = new NullPointerException(“Null Faces Context”); logger.throwing(this.getClass().getName(), “encodeBegin”, npe); throw npe; } if (null == component) { NullPointerException npe = new NullPointerException(“Null Component”); logger.throwing(this.getClass().getName(), “encodeBegin”, npe); throw npe; } } public void encodeChildren( FacesContext context, UIComponent component) throws IOException { logger.entering(this.getClass().getName(), “encodeChildren”); checkState(context, component); return; } Listing 10.2 (continued) Custom JSF Components 369 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 369 public void encodeEnd(FacesContext context, UIComponent component) throws IOException { logger.entering(this.getClass().getName(), “encodeEnd”); checkState(context, component); return; } private String getFormName(UIForm form) { String formName = null; Integer formNumber = (Integer) form.getAttributes().get(“com.sun.faces.FormNumber”); if (null != formNumber) { formName = “[“ + formNumber.toString() + “]”; } else { formName = “[0]”; } return formName; } private Element selectedElement(UIScroller scroller) { // Set up the selected element. String selected = scroller.getSelectedOption(); String selectedClass = (String) scroller.getAttributes().get( ScrollerRenderer.SELECTED_VALUE_CLASS_ATTR); Element option = null; if (null == selectedClass) { option = new Element(“b”); } else { option = new Element(“span”); option.setAttribute(“class”, selectedClass); } option.addContent(selected); return option; } private void queueMissingFormErrorMessage( FacesContext context, UIScroller scroller) { FacesMessage errMsg = new FacesMessage( “The UIScroller “ + scroller.getClientId(context) + “ is not in a Form”, “Put the UIScroller “ + scroller.getClientId(context) + “ in a form”); context.addMessage((scroller.getClientId(context)), errMsg); } Listing 10.2 (continued) 370 Chapter 10 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 370 private void output(List elements, FacesContext context) throws IOException { XMLOutputter output = new XMLOutputter(“ “, true); ResponseWriter responseWriter = context.getResponseWriter(); CharArrayWriter writer = new CharArrayWriter(256); Iterator itr = elements.iterator(); while (itr.hasNext()) { output.output((Element) itr.next(), writer); } writer.flush(); String html = writer.toString(); responseWriter.write(html); } private Element hiddenElement(String clientId) { Element hidden = new Element(“input”); hidden.setAttribute(“type”, “hidden”); hidden.setAttribute(“name”, clientId); return hidden; } private Element imgElement( String uri, String commandName, String clientId, String formName) { Element imgElement = new Element(“img”); imgElement.setAttribute(“border”, “0”); imgElement.setAttribute(“src”, uri); imgElement.setAttribute( “onClick”, mouseDownString(clientId, commandName, formName)); return imgElement; } private String mouseDownString( String clientId, String commandName, String formName) { StringBuffer buffer = new StringBuffer(“document.forms”); buffer.append(formName); buffer.append(“[‘“); buffer.append(commandName); buffer.append(“‘].value=’”); buffer.append(clientId); buffer.append(“‘ ; document.forms”); buffer.append(formName); buffer.append(“.submit()”); Listing 10.2 (continued) Custom JSF Components 371 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 371 return buffer.toString(); } } Listing 10.2 (continued) Decode The first section of code in the listing is the decode method. The code is repeated here for quick reference. public void decode(FacesContext context, UIComponent component) { checkState(context, component); UIScroller scroller = (UIScroller) component; decodeCommandName(context, scroller); UIForm parentForm = ComponentTreeUtils.getParentForm(scroller); if (null == parentForm) { queueMissingFormErrorMessage(context, scroller); } } The decode method is called during the request processing and is respon- sible for looking at what has come in on the request and understanding it. If the user clicked on either the right or left button, then the decode method will post an event. Renderers look at the attributes that are present in the request in order to find out what the user did. As you recall, the scroller is implemented with JavaScript attached to the onMouseDown event for the right and left img tags. The JavaScript places information into the request as an attribute of the request and then submits the form. The decode method then looks for the attributes on the request. If they are found, then an event is posted. Looking at the request is accomplished according to the following code from Listing 10.2: private void decodeCommandName( FacesContext context, UIScroller scroller) { String clientId = scroller.getClientId(context); String rightClientId = getRightClientId(clientId); String leftClientId = getLeftClientId(clientId); Map requestParameterMap = context.getExternalContext().getRequestParameterMap(); String value = (String) requestParameterMap.get(clientId); String rightValue = (String) requestParameterMap.get(rightClientId); 372 Chapter 10 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 372 String leftValue = (String) requestParameterMap.get(leftClientId); String commandName = null; logger.fine(“rightValue = “ + rightValue); logger.fine(“leftValue = “ + leftValue); if (null != rightValue && clientId.equals(rightValue)) { scroller.performRightAction(context); commandName = rightValue; } else if (null != leftValue && clientId.equals(leftValue)) { scroller.performLeftAction(context); commandName = leftValue; } else { throw new IllegalStateException(“no valid value returned to decode:” + “ rightValue = “ + rightValue +” leftValue = “ + leftValue + “ clientId = “ + clientId); } } Another interesting thing to note about the implementation of this method is that the processing of the internal state change for the component is accom- plished via a method call to the component. This is done instead of performing the component’s functionality here in the Renderer. Many custom component writers have the tendency to place all the event handling code in the Renderer. While this approach provides some level of encapsulation, it is at the wrong level. In order for this Renderer to be able to change the currently selected item, the whole process of making the list of options a circularly linked list would have to be public. That would expose too much of the internal work- ings of the UIScroller. The important thing for you to keep in mind when building your Renderer is to make sure to keep the functionality of your component encapsulated so that details of its inner workings are not exposed to the Renderer just to meet a nonexistent requirement to keep all the event processing in one place. Encode The next section of code is related to encoding the component in HTML. The code for the encodeBegin method is included here for quick reference. public void encodeBegin(FacesContext context, UIComponent component) throws IOException { checkState(context, component); if (!component.isRendered()) { return; } Custom JSF Components 373 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 373 UIScroller scroller = (UIScroller) component; UIForm form = ComponentTreeUtils.getParentForm(scroller); if (null != form) { String formName = getFormName(form); String clientId = scroller.getClientId(context); String rightClientId = getRightClientId(clientId); String leftClientId = getLeftClientId(clientId); List elements = new ArrayList(); // Set up the left image. elements.add( imgElement( scroller.getLeftScrollImg(), leftClientId, clientId, formName)); elements.add(selectedElement(scroller)); // Set up the right image. elements.add( imgElement( scroller.getRightScrollImg(), rightClientId, clientId, formName)); elements.add(hiddenElement(rightClientId)); elements.add(hiddenElement(leftClientId)); output(elements, context); } else { queueMissingFormErrorMessage(context, scroller); } } The code simply builds a bunch of elements and streams them out to the response. The use of JDom might seem a bit of overkill for such a simple com- ponent but I find that using JDom reduces the number of silly errors made in building the HTML so much that it is definitely worth the additional .jar file. The other aspect of this implementation is checking for and reporting failure to place the tag inside a form. Many Renderer developers provide poor error reporting when a fundamental assumption of the implementation is violated. Instead of relying on the user of your component to read all the documenta- tion, provide decent error reporting so that it’s obvious what is going wrong. We will cover some of the methods called from encodeBegin in more detail later in this section. The encodeChildren and encodeEnd methods are left blank for this Renderer. If your component has children, then you will have to implement both of these methods. The last method we will look at for this Renderer is the method that builds the selected element output text. Recall that there is a Renderer-dependent attribute available to control how this text is rendered. If the attribute is 374 Chapter 10 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 374 specified (for example with the f:attribute tag in a JSP) then the value is rendered in a span element with its CSS class specified, otherwise the value is rendered as a B (that is, bold) element. Here is the code again for easy reference: private Element selectedElement(UIScroller scroller) { // Set up the selected element. String selected = scroller.getSelectedOption(); String selectedClass = (String) scroller.getAttributes().get( ScrollerRenderer.SELECTED_VALUE_CLASS_ATTR); Element option = null; if (null == selectedClass) { option = new Element(“b”); } else { option = new Element(“span”); option.setAttribute(“class”, selectedClass); } option.addContent(selected); return option; } Notice that the generic attribute API on the component is used to look for the Renderer-dependent value. The generic attribute API is intended to sup- port just this kind of flexible usage. Next, we will see how the JSP tag works that is used to integrate this component with JSP. UIScroller JSP Tag Code The next and final class needed to implement the scroller component is the JSP tag. As you recall, integration with JSP is a major goal of the JSF specification, and it is expected that JSP will be the way that most developers code their JSF user interfaces for some time to come. Listing 10.3 has the code for the scroller tag. We will cover each interesting aspect of the code in detail in this section. /** * @jsp.tag name=”simple” display-name=”SimpleTag” body-content=”JSP” * description=”Simple JSP tag.” */ public class ScrollerTag extends UIComponentTag { private static Logger logger = Logger.getLogger(Constants.LOGGER_NAME); private String selectedOption; private String options; private String rightImgURI; private String leftImgURI; private String rightAction; Listing 10.3 ScrollerTag. (continued) Custom JSF Components 375 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 375 [...]... (UIComponentTag.isValueReference(value)) { ValueBinding vb = FacesContext getCurrentInstance() getApplication() createValueBinding( value); scroller.setValueBinding(propertyName, vb); } else { scroller.getAttributes().put(propertyName, value); } } } private void setLeftAction(UIScroller scroller, String value) { if (value != null) if (UIComponentTag.isValueReference(value)) { MethodBinding mb = FacesContext getCurrentInstance()... +BlogWriter +write:void +main:void +startElement:void +endElement:void +characters:void +BlogEntry blogEntries:ArrayList Figure 11.5 Domain classes for SimpleBlogger SecurityManager +isValidUser:boolean 3 89 390 Chapter 11 The Struts Version of the SimpleBlogger Now that we have clearly defined the sample problem, this section defines the Struts implementation of SimpleBlogger Because this is not a book on... ================================ Message Resources Definitions > Listing 11.6 (continued) 391 392 Chapter 11 The global-forwards section of the struts-config.xml, given in Listing 11.6, is used to define forward paths with parameters, in our case, action parameters Names are given to the forwards... cellspacing=”0” cellpadding=”0”> Listing 11.7 (continued) 393 394 Chapter 11 ... values for the presentation layer They act as a temporary storage for user input values very similar to the JSF view beans The properties in an ActionForm class must return String values for the JSPs 395 396 Chapter 11 In addition to storage, the Struts ActionForm classes are responsible for user input validation The validate() method is called automatically by Struts if the validate attribute in the... ActionError(“logon.error.userName”)); } if (password != null) { Listing 11 .9 The LogonForm for our Struts SimpleBlogger Converting a Struts Application to JSF int passwordLength = password.length(); if (passwordLength < 6 || passwordLength > 10) errors.add(userName, new ActionError(“logon.error.password”)); } if (errors.size() > 0) return errors; return null; } } Listing 11 .9 (continued) In our Struts SimpleBlogger example,... currentMessage; } public void setCurrentMessage(String string) { currentMessage = string; } public Collection getBlogEntries() { Listing 11.10 The BlogEditForm for the Struts SimpleBlogger (continued) 397 398 Chapter 11 if (blogEntries == null) blogEntries = new ArrayList(); return (Collection)blogEntries; } public void setBlogEntries(ArrayList list) { blogEntries = list; } public String getCurrentTimestamp()... BlogAction.java public class BlogAction extends DispatchAction { public ActionForward navigate(ActionMapping mapping, ActionForm form, Listing 11.12 The BlogAction for our Struts SimpleBlogger (continued) 399 400 Chapter 11 HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ActionErrors errors = new ActionErrors(); BlogEditForm blogForm = (BlogEditForm)form;... . component); return; } Listing 10.2 (continued) Custom JSF Components 3 69 16_462071 Ch10.qxd 5/17/04 10:24 AM Page 3 69 public void encodeEnd(FacesContext context, UIComponent component) throws IOException. selectedClass); } option.addContent(selected); return option; } private void queueMissingFormErrorMessage( FacesContext context, UIScroller scroller) { FacesMessage errMsg = new FacesMessage( “The UIScroller “ + scroller.getClientId(context) +. encodeBegin(FacesContext context) throws IOException { logger.entering(getClass().getName(), “encodeBegin”); if (context == null) { NullPointerException npe = new NullPointerException( “FacesContext

Ngày đăng: 14/08/2014, 09:22