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 }