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 }