View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.orchestra.conversation.jsf.components;
21  
22  import javax.faces.application.Application;
23  import javax.faces.component.UIComponent;
24  import javax.faces.component.ValueHolder;
25  import javax.faces.context.FacesContext;
26  import javax.faces.convert.Converter;
27  import javax.faces.webapp.UIComponentTag;
28  import javax.servlet.jsp.JspException;
29  import javax.servlet.jsp.tagext.Tag;
30  import javax.servlet.jsp.tagext.TagSupport;
31  
32  import org.apache.myfaces.orchestra.lib.jsf.SerializableConverter;
33  
34  /**
35   * Works like f:converter except that the converter instance is a managed-bean
36   * instance rather than a simple class.
37   * <p>
38   * In addition, the retrieved Converter instance is (by default) wrapped in
39   * a SerializableConverter instance. See the documentation for that class
40   * for further details.
41   * <p>
42   * This is not actually orchestra-specific functionality; this is something
43   * that the JSF core library could offer, or an add-on library such as Tomahawk.
44   * But at the current time, no common library offers this feature and the
45   * use of a SerializableConverter wrapper is very convenient.
46   * <p>
47   * The primary use-case for this tag is custom Converter classes that access
48   * conversation state. For example, a Converter may be written to convert
49   * an object instance into a string by reading its primary key, and on
50   * postback convert the string (a primary key) back into an object 
51   * instance by loading it using the persistence context associated with
52   * a particular conversation. Of course such a converter must be configured
53   * with the appropriate Orchestra scope, and therefore must be loaded as a
54   * managed bean rather than via the standard f:converter mechanism.
55   * <p>
56   * An alternative to using this tag is simply to use the standard
57   * "converter" attribute available on most JSF component tags, but if
58   * a SerializableConverter wrapper is desired then two bean definitions are
59   * needed rather than just one; see class SerializableConverter for details.
60   * <p>
61   * <h2>Creating custom converter tags</h2>
62   * 
63   * If you have written a custom Converter instance that can be configured
64   * then configuration may be done via the managed-bean system. However it
65   * can also be nice to configure instances via the tag, like the standard
66   * f:dateTimeConverter or f:numberConverter. To do this, you can:
67   * <ul>
68   * <li>subclass this tag
69   * <li>add setters for all the properties that you want configurable via the tag
70   * <li>override the createConverter method and copy the tag properties onto the
71   * newly created converter instance.
72   * <li>implement the StateHolder interface on the Converter class in order to
73   * save and restore these custom properties.
74   * </ul>
75   */
76  public class ConverterTag extends TagSupport
77  {
78      private static final long serialVersionUID = 1L;
79      private String beanName;
80      private boolean useWrapper = true;
81  
82      public ConverterTag()
83      {
84          super();
85      }
86  
87      public void setBeanName(String beanName)
88      {
89          this.beanName = beanName;
90      }
91  
92      public void setUseWrapper(boolean enabled)
93      {
94          this.useWrapper = enabled;
95      }
96  
97      public int doStartTag()
98              throws JspException
99      {
100         UIComponentTag componentTag = UIComponentTag.getParentUIComponentTag(pageContext);
101         if (componentTag == null)
102         {
103             throw new JspException("no parent UIComponentTag found");
104         }
105         if (!componentTag.getCreated())
106         {
107             return Tag.SKIP_BODY;
108         }
109 
110         Converter converter = createConverter(beanName);
111         
112         if (useWrapper && (converter instanceof SerializableConverter == false))
113         {
114             // Needed to check if it is already of the specified type in case the
115             // managed-bean framework has been configured to auto-wrap Converter
116             // instances already (eg via a Spring BeanPostProcessor or equivalent).
117             // This isn't the case, so wrap it now.
118             converter = new SerializableConverter(beanName, converter);
119         }
120 
121         UIComponent component = componentTag.getComponentInstance();
122         if (component == null)
123         {
124             throw new JspException("parent UIComponentTag has no UIComponent");
125         }
126         if (!(component instanceof ValueHolder))
127         {
128             throw new JspException("UIComponent is no ValueHolder");
129         }
130         ((ValueHolder)component).setConverter(converter);
131 
132         return Tag.SKIP_BODY;
133     }
134 
135     public void release()
136     {
137         super.release();
138         beanName = null;
139     }
140 
141     /**
142      * Override this method in order to customise the bean instance.
143      */
144     protected static Converter createConverter(String beanName)
145             throws JspException
146     {
147         FacesContext facesContext = FacesContext.getCurrentInstance();
148         Application application = facesContext.getApplication();
149         Object converter = application.getVariableResolver().resolveVariable(facesContext, beanName);
150         return (Converter) converter;
151     }
152 }