CVS: Tapestry/framework/src/net/sf/tapestry/record SessionPageRecorder.java,NONE,1.1.2.1 DefaultValuePersister.java,NONE,1.1.2.1 ImmutableValueCopier.java,NONE,1.1.2.1 EJBCopier.java,NONE,1.1.2.1 IValueCopier.java,NONE,1.1.2.1 ListCopier.java,NONE,1.1.2.1 EJBWrapperCopier.java,NONE,1.1.2.1 ArrayCopier.java,NONE,1.1.2.1 EJBWrapper.java,NONE,1.1.2.1 IValuePersister.java,NONE,1.1.2.1 ChangeKey.java,1.5,1.5.2.1 PageRecorder.java,1.9,1.9.2.1 PageChange.java,1.5,1.5.2.1 SimplePageRecorder.java,1.8,NONE

Howard Lewis Ship <[email protected]>
Newsgroups gmane.comp.java.tapestry.cvs
Message-ID <[email protected]>
Update of /cvsroot/tapestry/Tapestry/framework/src/net/sf/tapestry/record
In directory sc8-pr-cvs1:/tmp/cvs-serv9155/framework/src/net/sf/tapestry/record

Modified Files:
      Tag: hship-2-3
	ChangeKey.java PageRecorder.java PageChange.java 
Added Files:
      Tag: hship-2-3
	SessionPageRecorder.java DefaultValuePersister.java 
	ImmutableValueCopier.java EJBCopier.java IValueCopier.java 
	ListCopier.java EJBWrapperCopier.java ArrayCopier.java 
	EJBWrapper.java IValuePersister.java 
Removed Files:
      Tag: hship-2-3
	SimplePageRecorder.java 
Log Message:
Automatically register components as page listeners if they implement necessary interfaces
Major rework on page property persistance
[ 653358 ] IPage.getName() == qualified name
[ 608768 ] Changes saved AFTER IPage.detach()

--- NEW FILE: SessionPageRecorder.java ---
package net.sf.tapestry.record;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.tapestry.IRequestCycle;
import net.sf.tapestry.PageRecorderCommitException;
import net.sf.tapestry.RequestContext;
import net.sf.tapestry.Tapestry;
import net.sf.tapestry.util.StringSplitter;

/**
 *  Simple implementation of {@link net.sf.tapestry.IPageRecorder}
 *  that stores page changes as {@link javax.servlet.http.HttpSession} attributes.
 *
 *
 *  @author Howard Ship
 *  @version $Id: SessionPageRecorder.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 * 
 **/

public class SessionPageRecorder extends PageRecorder
{
    private static final Log LOG = LogFactory.getLog(SessionPageRecorder.class);
    
    /**
     *  Dictionary of changes, keyed on an instance of 
     *  {@link ChangeKey}
     *  (which enapsulates component path and property name).  The
     *  value is the new value for the object.
     *  The same information is stored into the
     *  {@link HttpSession}, which is  used as a kind of
     *  write-behind cache.
     *
     **/

    private Map _changes;

    /**
     *  The session into which changes are recorded.
     * 
     *  @since 2.4
     * 
     **/

    private HttpSession _session;

    /**
     *  The fully qualified name of the page being recorded.
     * 
     *  @since 2.4
     * 
     **/

    private String _pageName;

    /**
     *  The prefix (for {@link HttpSession} attributes) used by this
     *  page recorder.
     * 
     **/

    private String _attributePrefix;

    public void initialize(String pageName, IRequestCycle cycle)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Initializing for " + pageName);

        super.initialize(pageName, cycle);

        RequestContext context = cycle.getRequestContext();

        _pageName = pageName;
        _session = context.getSession();

        _attributePrefix = context.getServlet().getServletName() + "/" + _pageName + "/";

        restorePageChanges();
    }
    
    public void discard()
    {
        if (Tapestry.isEmpty(_changes))
            return;
            
        Iterator i = _changes.keySet().iterator();
        
        while (i.hasNext())
        {
            ChangeKey key = (ChangeKey)i.next();
            
            String attributeKey = constructAttributeKey(key.getComponentPath(), key.getPropertyName());
            
            if (LOG.isDebugEnabled())
            LOG.debug("Removing session attribute " + attributeKey);
            
            _session.removeAttribute(attributeKey);
        }
    }

    /**
     *  Simply clears the dirty flag, because there is no external place
     *  to store changed page properties.  Sets the locked flag to prevent
     *  subsequent changes from occuring now.
     *
     **/

    public void commit() throws PageRecorderCommitException
    {
        setDirty(false);
        setLocked(true);
    }

    /**
     *  Returns true if the recorder has any changes recorded.
     *
     **/

    public boolean getHasChanges()
    {
        if (_changes == null)
            return false;

        return (_changes.size() > 0);
    }

    public Collection getChanges()
    {
        if (_changes == null)
            return Collections.EMPTY_LIST;

        int count = _changes.size();
        Collection result = new ArrayList(count);

        Iterator i = _changes.entrySet().iterator();
        while (i.hasNext())
        {
            Map.Entry entry = (Map.Entry) i.next();

            ChangeKey key = (ChangeKey) entry.getKey();

            Object value = entry.getValue();

            PageChange change = new PageChange(key.getComponentPath(), key.getPropertyName(), value);

            result.add(change);
        }

        return result;
    }

    protected void recordChange(String componentPath, String propertyName, Object newValue)
    {
        ChangeKey key = new ChangeKey(componentPath, propertyName);

        if (_changes == null)
            _changes = new HashMap();

        // Check the prior value.  If this is not an actual change,
        // then don't bother recording it, or marking this page recorder
        // dirty.

        Object oldValue = _changes.get(key);
        if (newValue == oldValue)
            return;

        try
        {
            if (oldValue != null && oldValue.equals(newValue))
                return;
        }
        catch (Exception ex)
        {
            // Ignore.
        }

        setDirty(true);

        _changes.put(key, newValue);

        // Now, build a key used to store the new value
        // in the HttpSession

        String attributeKey = constructAttributeKey(componentPath, propertyName);

        _session.setAttribute(attributeKey, newValue);

        if (LOG.isDebugEnabled())
            LOG.debug("Stored session attribute " + attributeKey + " = " + newValue);
    }

    private String constructAttributeKey(String componentPath, String propertyName)
    {
        StringBuffer buffer = new StringBuffer(_attributePrefix);
        
        if (componentPath != null)
        {
            buffer.append(componentPath);
            buffer.append('/');
        }
        
        buffer.append(propertyName);
        
        return buffer.toString();
    }

    private void restorePageChanges()
    {
        int count = 0;
        Enumeration e = _session.getAttributeNames();
        StringSplitter splitter = null;

        while (e.hasMoreElements())
        {
            String key = (String) e.nextElement();

            if (!key.startsWith(_attributePrefix))
                continue;

            if (LOG.isDebugEnabled())
                LOG.debug("Restoring page change from session attribute " + key);

            if (_changes == null)
            {
                _changes = new HashMap();

                splitter = new StringSplitter('/');
            }

            String[] names = splitter.splitToArray(key);

            // The first name is the servlet name, which allows
            // multiple Tapestry apps to share a HttpSession, even
            // when they use the same page names.  The second name
            // is the page name, which we already know.

            int i = 2;

            String componentPath = (names.length == 4) ? names[i++] : null;
            String propertyName = names[i++];
            Object value = _session.getAttribute(key);

            ChangeKey changeKey = new ChangeKey(componentPath, propertyName);

            _changes.put(changeKey, value);

            count++;
        }

        if (LOG.isDebugEnabled())
            LOG.debug(count == 0 ? "No recorded changes." : "Restored " + count + " recorded changes.");
    }

}
--- NEW FILE: DefaultValuePersister.java ---
package net.sf.tapestry.record;

import java.rmi.RemoteException;
import java.util.Date;
import java.util.List;

import javax.ejb.EJBObject;
import javax.ejb.Handle;

import net.sf.tapestry.ApplicationRuntimeException;
import net.sf.tapestry.Tapestry;
import net.sf.tapestry.util.AdaptorRegistry;
import net.sf.tapestry.util.IImmutable;

/**
 *  Default implementation of {@link DefaultValuePersister}.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: DefaultValuePersister.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public class DefaultValuePersister implements IValuePersister
{
    private AdaptorRegistry _registry = new AdaptorRegistry();

    public DefaultValuePersister()
    {
        registerValueCopiers();
    }

    /**
     *  Invoked from {@link #registerValueCopiers()} to register the copier
     *  for a particular class.
     * 
     **/

    protected void registerValueCopier(Class registrationClass, IValueCopier copier)
    {
        _registry.register(registrationClass, copier);
    }

    protected IValueCopier getCopier(Object value)
    {
        Class valueClass = value.getClass();

        try
        {
            return (IValueCopier) _registry.getAdaptor(valueClass);
        }
        catch (IllegalArgumentException ex)
        {
            throw new ApplicationRuntimeException(
                Tapestry.getString("DefaultValuePersister.no-value-copier-for-class", valueClass.getName()),
                ex);
        }
    }

    /**
     *  Registers copiers.  Subclasses may override to add additional registrations
     *  beyond the default set.  An {@link net.sf.tapestry.record.ImmutableValueCopier}
     *  instance is registered for
     *  {@link net.sf.tapestry.util.IImmutable},
     *  String, Character, Number, Boolean and Date (even though Date is, technically, mutable)
     * 
     *  <p>
     *  An instance of {@link ListCopier} is registered for {@link java.util.List}.
     * 
     *  <p>
     *  An instance of {@link EJBCopier} for {@link EJBObject}, and {@link EJBWrapperCopier}
     *  for {@link EJBWrapper}.
     * 
     *  <p>
     *  An instance of {@link ArrayCopier} for <code>Object[]</code>.
     *  
     **/

    protected void registerValueCopiers()
    {
        IValueCopier immutable = new ImmutableValueCopier();

        registerValueCopier(IImmutable.class, immutable);
        registerValueCopier(String.class, immutable);
        registerValueCopier(Character.class, immutable);
        registerValueCopier(Number.class, immutable);
        registerValueCopier(Boolean.class, immutable);
        registerValueCopier(Date.class, immutable);
        registerValueCopier(Handle.class, immutable);

        registerValueCopier(List.class, new ListCopier());
        
        registerValueCopier(EJBObject.class, new EJBCopier());
        registerValueCopier(EJBWrapper.class, new EJBWrapperCopier());
        
        registerValueCopier(Object[].class, new ArrayCopier());
    }

    protected Object copy(Object value)
    {
        IValueCopier copier = getCopier(value);

        return copier.makeCopyOfValue(value);
    }

    public Object convertToActiveValue(Object value) throws PageRecorderSerializationException
    {
        if (value == null)
            return null;

        return copy(value);
    }

    public Object convertToStorableValue(Object value) throws PageRecorderSerializationException
    {
        if (value == null)
            return null;

        return copy(value);
    }
}

--- NEW FILE: ImmutableValueCopier.java ---
package net.sf.tapestry.record;

/**
 *  Copier used when the value is immutable (and thus, no copy is really needed).
 *
 *  @author Howard Lewis Ship
 *  @version $Id: ImmutableValueCopier.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public class ImmutableValueCopier implements IValueCopier
{
    public Object makeCopyOfValue(Object value)
    {
        return value;
    }

}

--- NEW FILE: EJBCopier.java ---
package net.sf.tapestry.record;

import javax.ejb.EJBObject;

/**
 *  Makes a copy of an EJB reference ({@link javax.ejb.EJBObject}) by wrapping
 *  the EJB in a {@link net.sf.tapestry.record.EJBWrapper}.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: EJBCopier.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public class EJBCopier implements IValueCopier
{

    public Object makeCopyOfValue(Object value)
    {
        EJBObject ejb = (EJBObject) value;

        return new EJBWrapper(ejb);
    }

}

--- NEW FILE: IValueCopier.java ---
package net.sf.tapestry.record;

/**
 *  Interface used to define how to make a copy of an object to 
 *
 *  @author Howard Lewis Ship
 *  @version $Id: IValueCopier.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public interface IValueCopier
{
    /**
     *  Passed an object, this should make a copy of that object.
     * 
     **/
    
    public Object makeCopyOfValue(Object value);
}

--- NEW FILE: ListCopier.java ---
package net.sf.tapestry.record;

import java.util.ArrayList;
import java.util.List;

/**
 *  Makes a copy of a {@link java.util.List} by
 *  creating a new {@link java.util.ArrayList}.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: ListCopier.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/
public class ListCopier implements IValueCopier
{

    public Object makeCopyOfValue(Object value)
    {
        List list = (List)value;
        
        return new ArrayList(list);
    }

}

--- NEW FILE: EJBWrapperCopier.java ---
package net.sf.tapestry.record;

/**
 *  Converts an {@link net.sf.tapestry.record.EJBWrapper}
 *  back into an {@link javax.ejb.EJBObject}.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: EJBWrapperCopier.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public class EJBWrapperCopier implements IValueCopier
{
    public Object makeCopyOfValue(Object value)
    {
        EJBWrapper wrapper = (EJBWrapper)value;
        
        return wrapper.getEJBObject();
    }

}

--- NEW FILE: ArrayCopier.java ---
package net.sf.tapestry.record;

import java.util.Arrays;

/**
 *  Makes a copy of the array, by invoking <code>clone()</code>.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: ArrayCopier.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public class ArrayCopier implements IValueCopier
{

    public Object makeCopyOfValue(Object value)
    {
        Object[] array = (Object[])value;
        
        return array.clone();
    }

}

--- NEW FILE: EJBWrapper.java ---
package net.sf.tapestry.record;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import javax.ejb.EJBObject;
import javax.ejb.Handle;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
 *  Wraps an EJBObject, serializing and deserializing the EJBObject's handle.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: EJBWrapper.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public class EJBWrapper implements Externalizable
{
    private EJBObject _ejb;

    public EJBWrapper()
    {
    }

    public EJBWrapper(EJBObject ejb)
    {
        _ejb = ejb;
    }

    public EJBObject getEJBObject()
    {
        return _ejb;
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
    {
        Handle handle = (Handle)in.readObject();
        
        _ejb = (EJBObject)handle.getEJBObject();
    }

    /**
     *  Writes the handle for the EJB.
     * 
     **/
    
    public void writeExternal(ObjectOutput out) throws IOException
    {
        Handle handle = _ejb.getHandle();

        out.writeObject(handle);
    }

    public String toString()
    {
        ToStringBuilder builder = new ToStringBuilder(this);
        
        builder.append("ejb", _ejb);
        
        return builder.toString();
    }
}

--- NEW FILE: IValuePersister.java ---
package net.sf.tapestry.record;

/**
 *  Responsible for persisting values on behalf of a {@link net.sf.tapestry.IPageRecorder}.
 *  The values provided to the page recorder are not stored as is; there are several
 *  conversions that occur:
 *  <ul>
 *  <li>{@link javax.ejb.EJBObject} is wrapped up in a {@link net.sf.tapestry.record.EJBWrapper}
 *  <li>Non-immutable objects are copied, using a {@link net.sf.tapestry.record.IValueCopier}
 *  <li>null passes through unchanged
 *  </ul>
 *
 *  @author Howard Lewis Ship
 *  @version $Id: IValuePersister.java,v 1.1.2.1 2002/12/30 03:04:55 hship Exp $
 *  @since 2.4
 *
 **/

public interface IValuePersister
{
    /**
     *  Converts an active value (used by the application) to
     *  a storable value (a value which can be persisted for later access).
     * 
     **/
    
    public Object convertToStorableValue(Object value)
    throws PageRecorderSerializationException;
    
    /**
     *  Reverses {@link #convertToStorableValue(Object)}, converting values
     *  back into active values.
     * 
     **/
    
    public Object convertToActiveValue(Object value)throws PageRecorderSerializationException;
}

Index: ChangeKey.java
===================================================================
RCS file: /cvsroot/tapestry/Tapestry/framework/src/net/sf/tapestry/record/ChangeKey.java,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -C2 -d -r1.5 -r1.5.2.1
*** ChangeKey.java	27 Nov 2002 17:58:54 -0000	1.5
--- ChangeKey.java	30 Dec 2002 03:04:55 -0000	1.5.2.1
***************
*** 3,6 ****
--- 3,9 ----
  import java.io.Serializable;
  
+ import org.apache.commons.lang.builder.EqualsBuilder;
+ import org.apache.commons.lang.builder.HashCodeBuilder;
+ 
  /**
   *  Used to identify a property change.
***************
*** 13,30 ****
  public class ChangeKey
  {
!     String componentPath;
!     String propertyName;
  
      public ChangeKey(String componentPath, String propertyName)
      {
!         this.componentPath = componentPath;
!         this.propertyName = propertyName;
      }
  
      public boolean equals(Object object)
      {
-         boolean propertyNameIdentical;
-         boolean componentPathIdentical;
- 
          if (object == null)
              return false;
--- 16,31 ----
  public class ChangeKey
  {
!     private int _hashCode = -1;
!     private String _componentPath;
!     private String _propertyName;
  
      public ChangeKey(String componentPath, String propertyName)
      {
!         _componentPath = componentPath;
!         _propertyName = propertyName;
      }
  
      public boolean equals(Object object)
      {
          if (object == null)
              return false;
***************
*** 33,89 ****
              return true;
  
!         try
!         {
!             ChangeKey other = (ChangeKey) object;
! 
!             propertyNameIdentical = propertyName == other.propertyName;
!             componentPathIdentical = componentPath == other.componentPath;
! 
!             if (propertyNameIdentical && componentPathIdentical)
!                 return true;
! 
!             // If not identical, then see if equal. If not equal, then
!             // we don't equal the other key.
! 
!             if (!propertyNameIdentical)
!                 if (!propertyName.equals(other.propertyName))
!                     return false;
! 
!             // If this far, that propertyName is equal
! 
!             if (componentPathIdentical)
!                 return true;
! 
!             // If one's null and the other isn't then they can't
!             // be equal.
! 
!             if (componentPath == null || other.componentPath == null)
!                 return false;
  
!             // So it comes down to this ... are the two (non-null)
!             // componentPath's equal?
  
!             return componentPath.equals(other.componentPath);
!         }
!         catch (ClassCastException e)
!         {
!             return false;
!         }
      }
  
      public String getComponentPath()
      {
!         return componentPath;
      }
  
      public String getPropertyName()
      {
!         return propertyName;
      }
  
      /**
       *
!      *  Returns the propertyName's hash code, xor'ed with the
!      *  componentPath hash code (if componentPath is non-null).
       *
       **/
--- 34,64 ----
              return true;
  
!         if (!(object instanceof ChangeKey))
!             return false;
  
!         ChangeKey other = (ChangeKey) object;
  
!         EqualsBuilder builder =new EqualsBuilder();
!         
!         builder.append(_propertyName, other._propertyName);
!         builder.append(_componentPath, other._componentPath);
!         
!         return builder.isEquals();
      }
  
      public String getComponentPath()
      {
!         return _componentPath;
      }
  
      public String getPropertyName()
      {
!         return _propertyName;
      }
  
      /**
       *
!      *  Returns a hash code computed from the
!      *  property name and component path.
       *
       **/
***************
*** 91,102 ****
      public int hashCode()
      {
!         int result;
  
!         result = propertyName.hashCode();
  
!         if (componentPath != null)
!             result ^= componentPath.hashCode();
  
!         return result;
      }
  }
--- 66,80 ----
      public int hashCode()
      {
!         if (_hashCode == -1)
!         {
!             HashCodeBuilder builder = new HashCodeBuilder(257, 23);  // Random
  
!             builder.append(_propertyName);
!             builder.append(_componentPath);
  
!             _hashCode = builder.toHashCode();
!         }
  
!         return _hashCode;
      }
  }

Index: PageRecorder.java
===================================================================
RCS file: /cvsroot/tapestry/Tapestry/framework/src/net/sf/tapestry/record/PageRecorder.java,v
retrieving revision 1.9
retrieving revision 1.9.2.1
diff -C2 -d -r1.9 -r1.9.2.1
*** PageRecorder.java	27 Nov 2002 17:58:54 -0000	1.9
--- PageRecorder.java	30 Dec 2002 03:04:55 -0000	1.9.2.1
***************
*** 1,20 ****
  package net.sf.tapestry.record;
  
- import java.io.IOException;
- import java.io.Serializable;
- import java.rmi.RemoteException;
  import java.util.Collection;
  import java.util.Iterator;
  
! import javax.ejb.EJBObject;
! import javax.ejb.Handle;
  import net.sf.tapestry.ApplicationRuntimeException;
  import net.sf.tapestry.IComponent;
  import net.sf.tapestry.IPage;
  import net.sf.tapestry.IPageRecorder;
  import net.sf.tapestry.IResourceResolver;
  import net.sf.tapestry.PageRecorderCommitException;
  import net.sf.tapestry.Tapestry;
  import net.sf.tapestry.event.ObservedChangeEvent;
  import net.sf.tapestry.util.prop.OgnlUtils;
  
--- 1,20 ----
  package net.sf.tapestry.record;
  
  import java.util.Collection;
  import java.util.Iterator;
  
! import javax.servlet.ServletContext;
! 
  import net.sf.tapestry.ApplicationRuntimeException;
+ import net.sf.tapestry.ApplicationServlet;
  import net.sf.tapestry.IComponent;
  import net.sf.tapestry.IPage;
  import net.sf.tapestry.IPageRecorder;
+ import net.sf.tapestry.IRequestCycle;
  import net.sf.tapestry.IResourceResolver;
  import net.sf.tapestry.PageRecorderCommitException;
  import net.sf.tapestry.Tapestry;
  import net.sf.tapestry.event.ObservedChangeEvent;
+ import net.sf.tapestry.spec.IApplicationSpecification;
  import net.sf.tapestry.util.prop.OgnlUtils;
  
***************
*** 24,32 ****
   *
   *  <p>This is an abstract implementation; specific implementations can choose where
!  * and how to persist the data.
!  *
!  *  <p>Implements {@link Serializable} but does not have any state of its own.
!  *  Subclasses must implement <code>readExternal()</code> and
!  *  <code>writeExternal()</code>.
   *
   * @author Howard Ship
--- 24,28 ----
   *
   *  <p>This is an abstract implementation; specific implementations can choose where
!  *  and how to persist the data.
   *
   * @author Howard Ship
***************
*** 35,43 ****
   **/
  
! public abstract class PageRecorder implements IPageRecorder, Serializable
  {
!     protected transient boolean dirty = false;
!     protected transient boolean locked = false;
!     protected transient boolean discard = false;
  
      /**
--- 31,43 ----
   **/
  
! public abstract class PageRecorder implements IPageRecorder
  {
!     public static final String VALUE_PERSISTER_EXTENSION_NAME = "net.sf.tapestry.value-persister";
! 
!     private IValuePersister _persister;
! 
!     private boolean _dirty = false;
!     private boolean _locked = false;
!     private boolean _discard = false;
  
      /**
***************
*** 71,75 ****
      
      {
!         return dirty;
      }
  
--- 71,75 ----
      
      {
!         return _dirty;
      }
  
***************
*** 82,91 ****
      public boolean isLocked()
      {
!         return locked;
      }
  
      public void setLocked(boolean value)
      {
!         locked = value;
      }
  
--- 82,91 ----
      public boolean isLocked()
      {
!         return _locked;
      }
  
      public void setLocked(boolean value)
      {
!         _locked = value;
      }
  
***************
*** 110,139 ****
      public void observeChange(ObservedChangeEvent event)
      {
!         IComponent component;
!         String propertyName;
!         Object newValue;
! 
!         component = event.getComponent();
!         propertyName = event.getPropertyName();
  
!         if (locked)
              throw new ApplicationRuntimeException(
                  Tapestry.getString(
                      "PageRecorder.change-after-lock",
!                     component.getPage().getName(),
                      propertyName,
                      component.getExtendedId()));
  
          if (propertyName == null)
!         {
!             dirty = true;
!             return;
!         }
  
!         newValue = event.getNewValue();
  
          try
          {
!             recordChange(component.getIdPath(), propertyName, newValue);
          }
          catch (Throwable t)
--- 110,135 ----
      public void observeChange(ObservedChangeEvent event)
      {
!         IComponent component = event.getComponent();
!         String propertyName = event.getPropertyName();
  
!         if (_locked)
              throw new ApplicationRuntimeException(
                  Tapestry.getString(
                      "PageRecorder.change-after-lock",
!                     component.getPage().getPageName(),
                      propertyName,
                      component.getExtendedId()));
  
          if (propertyName == null)
!             throw new ApplicationRuntimeException(
!                 Tapestry.getString("PageRecorder.null-property-name", component.getExtendedId()));
  
!         Object activeValue = event.getNewValue();
  
          try
          {
!             Object storableValue = _persister.convertToStorableValue(activeValue);
! 
!             recordChange(component.getIdPath(), propertyName, storableValue);
          }
          catch (Throwable t)
***************
*** 141,145 ****
              t.printStackTrace();
              throw new ApplicationRuntimeException(
!                 Tapestry.getString("PageRecorder.unable-to-persist", propertyName, component.getExtendedId(), newValue),
                  t);
          }
--- 137,145 ----
              t.printStackTrace();
              throw new ApplicationRuntimeException(
!                 Tapestry.getString(
!                     "PageRecorder.unable-to-persist",
!                     propertyName,
!                     component.getExtendedId(),
!                     activeValue),
                  t);
          }
***************
*** 176,184 ****
      {
          Collection changes = getChanges();
!         
          if (changes.isEmpty())
              return;
!             
!         IResourceResolver resolver = page.getEngine().getResourceResolver();            
          Iterator i = changes.iterator();
  
--- 176,184 ----
      {
          Collection changes = getChanges();
! 
          if (changes.isEmpty())
              return;
! 
!         IResourceResolver resolver = page.getEngine().getResourceResolver();
          Iterator i = changes.iterator();
  
***************
*** 187,278 ****
              PageChange change = (PageChange) i.next();
  
!             IComponent component = page.getNestedComponent(change.componentPath);
  
              try
              {
  
!                 OgnlUtils.set(change.propertyName, resolver, component, change.newValue);
              }
              catch (Throwable t)
              {
!                 throw new RollbackException(component, change.propertyName, change.newValue, t);
              }
          }
      }
  
!     /**
!      *  Invoked by subclasses to converts an object into
!      *  a {@link Serializable} value for for persistent storage.
!      *
!      *  <p>This implementation implements a special case
!      *  for converting an {@link EJBObject} into a {@link Handle}
!      *  for storage.
!      *
!      *  @since 0.2.9
!      **/
  
!     protected Serializable persistValue(Object value) throws IOException
      {
!         if (!(value instanceof EJBObject))
!         {
!             try
!             {
!                 return (Serializable) value;
!             }
!             catch (ClassCastException ex)
!             {
!                 throw new PageRecorderSerializationException(ex);
!             }
!         }
  
!         try
!         {
!             EJBObject ejb = (EJBObject) value;
  
!             return ejb.getHandle();
!         }
!         catch (RemoteException ex)
!         {
!             throw new PageRecorderSerializationException(ex);
!         }
      }
  
      /**
!      *  Invoked by subclasses to restore a persisted value to its
!      *  runtime value.  This implementation converts {@link Handle}s, stored
!      *  persistently, back into {@link EJBObject}s.
!      *
!      *  @since 0.2.9
       **/
  
!     protected Object restoreValue(Object value) throws IOException
      {
!         if (!(value instanceof Handle))
!             return value;
  
!         try
!         {
!             Handle handle = (Handle) value;
  
!             return handle.getEJBObject();
!         }
!         catch (RemoteException ex)
!         {
!             throw new PageRecorderSerializationException(ex);
!         }
!     }
  
!     /** @since 2.0.2 **/
  
!     public boolean isMarkedForDiscard()
!     {
!         return discard;
!     }
  
!     /** @since 2.0.2 **/
  
!     public void markForDiscard()
!     {
!         discard = true;
      }
  
--- 187,267 ----
              PageChange change = (PageChange) i.next();
  
!             String propertyName = change.getPropertyName();
! 
!             IComponent component = page.getNestedComponent(change.getComponentPath());
! 
!             Object storedValue = change.getNewValue();
  
              try
              {
+                 Object activeValue = _persister.convertToActiveValue(storedValue);
  
!                 OgnlUtils.set(propertyName, resolver, component, activeValue);
              }
              catch (Throwable t)
              {
!                 throw new RollbackException(component, propertyName, storedValue, t);
              }
          }
      }
  
!     /** @since 2.0.2 **/
  
!     public boolean isMarkedForDiscard()
      {
!         return _discard;
!     }
  
!     /** @since 2.0.2 **/
  
!     public void markForDiscard()
!     {
!         _discard = true;
!     }
! 
!     protected void setDirty(boolean dirty)
!     {
!         this._dirty = dirty;
!     }
! 
!     protected boolean getDirty()
!     {
!         return _dirty;
      }
  
      /**
!      *  Finds the {@link net.sf.tapestry.record.IValuePersister} as an
!      *  attribute in the {@link javax.servlet.ServletContext}.  If not
!      *  found it is created.  If an application extension named
!      *  <code>net.sf.tapestry.value-persister</code>
!      *  exists, it is used as the shared persister, otherwise
!      *  an instance of {@link net.sf.tapestry.record.DefaultValuePersister}
!      *  is created.  Subclasses may override this method, but must
!      *  invoke this implementation.
!      * 
       **/
  
!     public void initialize(String pageName, IRequestCycle cycle)
      {
!         ApplicationServlet servlet = cycle.getRequestContext().getServlet();
!         String servletName = servlet.getServletName();
  
!         ServletContext context = servlet.getServletContext();
  
!         String name = VALUE_PERSISTER_EXTENSION_NAME + "." + servletName;
  
!         _persister = (IValuePersister) context.getAttribute(name);
  
!         if (_persister == null)
!         {
!             IApplicationSpecification spec = cycle.getEngine().getSpecification();
  
!             if (spec.checkExtension(VALUE_PERSISTER_EXTENSION_NAME))
!                 _persister = (IValuePersister) spec.getExtension(VALUE_PERSISTER_EXTENSION_NAME, IValuePersister.class);
!             else
!                 _persister = new DefaultValuePersister();
  
!             context.setAttribute(name, _persister);
!         }
      }
  

Index: PageChange.java
===================================================================
RCS file: /cvsroot/tapestry/Tapestry/framework/src/net/sf/tapestry/record/PageChange.java,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -C2 -d -r1.5 -r1.5.2.1
*** PageChange.java	27 Nov 2002 17:58:54 -0000	1.5
--- PageChange.java	30 Dec 2002 03:04:55 -0000	1.5.2.1
***************
*** 1,4 ****
--- 1,6 ----
  package net.sf.tapestry.record;
  
+ import org.apache.commons.lang.builder.ToStringBuilder;
+ 
  import net.sf.tapestry.IPageChange;
  
***************
*** 13,25 ****
  public class PageChange implements IPageChange
  {
!     String componentPath;
!     String propertyName;
!     Object newValue;
  
      public PageChange(String componentPath, String propertyName, Object newValue)
      {
!         this.componentPath = componentPath;
!         this.propertyName = propertyName;
!         this.newValue = newValue;
      }
  
--- 15,27 ----
  public class PageChange implements IPageChange
  {
!     private String _componentPath;
!     private String _propertyName;
!     private Object _newValue;
  
      public PageChange(String componentPath, String propertyName, Object newValue)
      {
!         _componentPath = componentPath;
!         _propertyName = propertyName;
!         _newValue = newValue;
      }
  
***************
*** 32,36 ****
      public String getComponentPath()
      {
!         return componentPath;
      }
  
--- 34,38 ----
      public String getComponentPath()
      {
!         return _componentPath;
      }
  
***************
*** 42,46 ****
      public Object getNewValue()
      {
!         return newValue;
      }
  
--- 44,48 ----
      public Object getNewValue()
      {
!         return _newValue;
      }
  
***************
*** 52,80 ****
      public String getPropertyName()
      {
!         return propertyName;
      }
  
      public String toString()
      {
!         StringBuffer buffer;
! 
!         buffer = new StringBuffer(getClass().getName());
! 
!         buffer.append('[');
! 
!         if (componentPath != null)
!         {
!             buffer.append(componentPath);
!             buffer.append(' ');
!         }
! 
!         buffer.append(propertyName);
! 
!         buffer.append(' ');
!         buffer.append(newValue);
! 
!         buffer.append(']');
! 
!         return buffer.toString();
      }
  }
--- 54,69 ----
      public String getPropertyName()
      {
!         return _propertyName;
      }
  
      public String toString()
      {
!         ToStringBuilder builder = new ToStringBuilder(this);
!         
!         builder.append("componentPath", _componentPath);
!         builder.append("propertyName", _propertyName);
!         builder.append("newValue", _newValue);
!         
!         return builder.toString();
      }
  }

--- SimplePageRecorder.java DELETED ---



-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.