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>