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 }