You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2008/02/18 22:39:54 UTC

svn commit: r628872 - in /myfaces/orchestra/trunk/core/src/main: java/org/apache/myfaces/orchestra/conversation/jsf/components/ConverterTag.java java/org/apache/myfaces/orchestra/lib/jsf/SerializableConverter.java tld/myfaces_orchestra.tld

Author: skitching
Date: Mon Feb 18 13:39:52 2008
New Revision: 628872

URL: http://svn.apache.org/viewvc?rev=628872&view=rev
Log:
ConverterTag cleanups. Add useWrapper attribute.

Modified:
    myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/jsf/components/ConverterTag.java
    myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/lib/jsf/SerializableConverter.java
    myfaces/orchestra/trunk/core/src/main/tld/myfaces_orchestra.tld

Modified: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/jsf/components/ConverterTag.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/jsf/components/ConverterTag.java?rev=628872&r1=628871&r2=628872&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/jsf/components/ConverterTag.java (original)
+++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/jsf/components/ConverterTag.java Mon Feb 18 13:39:52 2008
@@ -29,24 +29,55 @@
 import javax.servlet.jsp.tagext.Tag;
 import javax.servlet.jsp.tagext.TagSupport;
 
+import org.apache.myfaces.orchestra.lib.jsf.SerializableConverter;
+
 /**
  * Works like f:converter except that the converter instance is a managed-bean
  * instance rather than a simple class.
  * <p>
+ * In addition, the retrieved Converter instance is (by default) wrapped in
+ * a SerializableConverter instance. See the documentation for that class
+ * for further details.
+ * <p>
  * This is not actually orchestra-specific functionality; this is something
- * that the core library could offer, or an addon library such as Tomahawk.
- * But at the current time, no common library offers this feature and it
- * is important, when writing a Converter that accesses an Orchestra
- * conversation-scoped persistence context, to pulled the object from the
- * dependency-injection framework rather than create it via newInstance.
+ * that the JSF core library could offer, or an add-on library such as Tomahawk.
+ * But at the current time, no common library offers this feature and the
+ * use of a SerializableConverter wrapper is very convenient.
+ * <p>
+ * The primary use-case for this tag is custom Converter classes that access
+ * conversation state. For example, a Converter may be written to convert
+ * an object instance into a string by reading its primary key, and on
+ * postback convert the string (a primary key) back into an object 
+ * instance by loading it using the persistence context associated with
+ * a particular conversation. Of course such a converter must be configured
+ * with the appropriate Orchestra scope, and therefore must be loaded as a
+ * managed bean rather than via the standard f:converter mechanism.
  * <p>
  * An alternative to using this tag is simply to use the standard
- * "converter" attribute available on most JSF component tags.
+ * "converter" attribute available on most JSF component tags, but if
+ * a SerializableConverter wrapper is desired then two bean definitions are
+ * needed rather than just one; see class SerializableConverter for details.
+ * <p>
+ * <h2>Creating custom converter tags</h2>
+ * 
+ * If you have written a custom Converter instance that can be configured
+ * then configuration may be done via the managed-bean system. However it
+ * can also be nice to configure instances via the tag, like the standard
+ * f:dateTimeConverter or f:numberConverter. To do this, you can:
+ * <ul>
+ * <li>subclass this tag
+ * <li>add setters for all the properties that you want configurable via the tag
+ * <li>override the createConverter method and copy the tag properties onto the
+ * newly created converter instance.
+ * <li>implement the StateHolder interface on the Converter class in order to
+ * save and restore these custom properties.
+ * </ul>
  */
 public class ConverterTag extends TagSupport
 {
     private static final long serialVersionUID = 1L;
 	private String beanName;
+	private boolean useWrapper = true;
 
     public ConverterTag()
     {
@@ -58,6 +89,11 @@
     	this.beanName = beanName;
     }
 
+    public void setUseWrapper(boolean enabled)
+    {
+    	this.useWrapper = enabled;
+    }
+
     public int doStartTag()
             throws JspException
     {
@@ -72,6 +108,15 @@
         }
 
         Converter converter = createConverter(beanName);
+        
+        if (useWrapper && (converter instanceof SerializableConverter == false))
+        {
+        	// Needed to check if it is already of the specified type in case the
+        	// managed-bean framework has been configured to auto-wrap Converter
+        	// instances already (eg via a Spring BeanPostProcessor or equivalent).
+        	// This isn't the case, so wrap it now.
+        	converter = new SerializableConverter(beanName);
+        }
 
         UIComponent component = componentTag.getComponentInstance();
         if (component == null)
@@ -93,6 +138,9 @@
         beanName = null;
     }
 
+    /**
+     * Override this method in order to customise the bean instance.
+     */
     protected static Converter createConverter(String beanName)
             throws JspException
     {

Modified: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/lib/jsf/SerializableConverter.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/lib/jsf/SerializableConverter.java?rev=628872&r1=628871&r2=628872&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/lib/jsf/SerializableConverter.java (original)
+++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/lib/jsf/SerializableConverter.java Mon Feb 18 13:39:52 2008
@@ -18,13 +18,13 @@
  */
 package org.apache.myfaces.orchestra.lib.jsf;
 
-import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
+import java.io.Serializable;
 
+import javax.faces.application.Application;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
 import javax.faces.convert.Converter;
 import javax.faces.convert.ConverterException;
-import javax.faces.context.FacesContext;
-import javax.faces.component.UIComponent;
-import java.io.Serializable;
 
 /**
  * Wraps a converter so that when the parent component is serialized,
@@ -35,35 +35,87 @@
  * all state that the converter instance has, but for most converters
  * that doesn't matter.
  * <p>
- * This wrapper is of course applicable only to converters that are
- * fetched using an EL expression. It cannot be applied to converters
- * that are retrieved via Application.createConverter(id), ie where
- * a new instance is created directly from a Class that was registered
- * with the JSF framework.
- * <p>
- * To use this class, a component in a page should specify
- * <code>converter="#{someBeanName}"</code>. The definition for bean
- * "someBeanName" should specify a non-singleton instance of this class.
- * The constructor should be passed a string which specifies the name of
- * the real Constructor implementation that is to be used.
+ * If the converter does implement the JSF StateHolder interface, then
+ * that will be called on serialize and deserialize to explicitly save
+ * and restore the state on the object. However implementing the
+ * standard java.io.Serializable interfaces on Converter objects is
+ * not currently supported.
+ * <p>
+ * This class is primarily intended to wrap Converter beans which are
+ * associated with Orchestra conversations. Objects in conversation scopes
+ * are wrapped in proxy objects that are unfortunately not serializable;
+ * this is not a problem for backing beans in orchestra scopes as they are
+ * not serialized along with the view tree but it is a problem for Converter
+ * objects as they are part of the view tree and get serialized at the end
+ * of each request (and deserialized at the start of each postback). Using
+ * the SerializableConverter as a wrapper solves this issue by simply storing
+ * the beanName of the converter when serialization occurs, and reloading
+ * the bean by name on deserialize.
+ * <p>
+ * If the converter has no internal state (other than that defined in the
+ * bean definition) then nothing more needs to be done. However when the
+ * converter does have state, it should implement the JSF StateHolder
+ * interface; the SerializableConverter wrapper will invoke that 
+ * 
+ * <h2>Using from an orchestra:converter tag</h2>
+ * 
+ * When the orchestra:converter tag is used, the fetched object is wrapped in
+ * an instance of this type by default.
+ * 
+ * <h2>Using from a converter attribute</h2>
+ * 
+ * A component in a page can specify <code>converter="#{someBeanName}"</code>.
+ * The definition for bean "someBeanName" should specify that a non-singleton
+ * instance of this class should be created, and the "beanName" constructor
+ * parameter should be set to refer to another bean-definition that is the
+ * actual converter type to be instantiated.
+ * <p>
+ * When using Spring, a BeanPostProcessor class could also be defined that
+ * intercepts creation of all Converter instances and automatically wraps
+ * them in a SerializableConverter.
+ * 
+ * <h2>Outstanding Issues</h2>
+ *
+ * We do want to serialize conversation-scoped managed beans when a session
+ * is transferred to another machine, or is passivated. Therefore we need to
+ * solve the serializing of proxied objects in a general way eventually. And
+ * that will then make this workaround irrelevant.
+ * <p>
+ * If we can drill down from the proxy to the underlying bean, then we could
+ * use standard java.io.Serialization on it if that is supported. This
+ * "drilling down" functionality is very useful in other cases too.
+ * <p>
+ * We already have a method ConversationUtils.getCurrentBean which has a comment
+ * that a generic "proxy this object" mechanism would be useful. If that existed,
+ * then deserializing could use the same approach: drill down and serialize the
+ * underlying bean, then later unserialize the underlying bean and generate new
+ * proxies for it.
  */
 public class SerializableConverter implements Converter, Serializable
 {
 	private static final long serialVersionUID = 2L;
 
-	private final String converterId;
+	private final String beanName;
 	private transient Converter converter;
 
-	public SerializableConverter(String converterId)
+	public SerializableConverter(String beanName)
+	{
+		this.beanName = beanName;
+	}
+
+	public SerializableConverter(String beanName, Converter instance)
 	{
-		this.converterId = converterId;
+		this.beanName = beanName;
+		this.converter = instance;
 	}
 
 	protected Converter getConverter()
 	{
 		if (this.converter == null)
 		{
-			this.converter = (Converter) FrameworkAdapter.getCurrentInstance().getBean(this.converterId);
+	        FacesContext facesContext = FacesContext.getCurrentInstance();
+	        Application application = facesContext.getApplication();
+	        this.converter = (Converter) application.getVariableResolver().resolveVariable(facesContext, beanName);
 		}
 
 		return this.converter;

Modified: myfaces/orchestra/trunk/core/src/main/tld/myfaces_orchestra.tld
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/tld/myfaces_orchestra.tld?rev=628872&r1=628871&r2=628872&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/main/tld/myfaces_orchestra.tld (original)
+++ myfaces/orchestra/trunk/core/src/main/tld/myfaces_orchestra.tld Mon Feb 18 13:39:52 2008
@@ -94,5 +94,14 @@
                 interface, and should be of scope 'none'.
             </description>
         </attribute>
+        <attribute>
+            <name>useWrapper</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                When set to true, a SerializableConverter instance will automatically
+                be wrapped around the returned converter. Defaults to true.
+            </description>
+        </attribute>
     </tag>
 </taglib>