Assert that all models are detached at the end of the request?

View: New views
13 Messages — Rating Filter:   Alert me  

Assert that all models are detached at the end of the request?

by hbf :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Is there an easy way to assert that all models are detached at the end  
of the
request?

It does not look so easy to check this as models do not have common  
base class
where one could register them for a check...

I often use an additional model in a component and store it as a  
member field;
if I forgot to detach() this model in the onDetach() handler, I would  
have a
"dangling" model. That caused me quite some trouble once and I want to  
avoid
it in the future.

Thanks,
Kaspar

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by igor.vaynberg :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

no, there is no easy way to "assert" that any model has been detached,
because they do not keep a flag.

in 1.5 we will implement it so that all fields of a component that
implement idetachable are detached in the end of request via
reflection, so that should help somewhat.

-igor

On Thu, Aug 28, 2008 at 1:57 PM, Kaspar Fischer <fischerk@...> wrote:

> Is there an easy way to assert that all models are detached at the end of
> the
> request?
>
> It does not look so easy to check this as models do not have common base
> class
> where one could register them for a check...
>
> I often use an additional model in a component and store it as a member
> field;
> if I forgot to detach() this model in the onDetach() handler, I would have a
> "dangling" model. That caused me quite some trouble once and I want to avoid
> it in the future.
>
> Thanks,
> Kaspar
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@...
> For additional commands, e-mail: users-help@...
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by Martijn Dashorst :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

you could extend the serializerchecker to check for underached models.
We did something similar checking for non-transient Entity objects.
Iirc the code is either on my blog or Eelco's blog.

Martijn

On 8/28/08, Igor Vaynberg <igor.vaynberg@...> wrote:

> no, there is no easy way to "assert" that any model has been detached,
> because they do not keep a flag.
>
> in 1.5 we will implement it so that all fields of a component that
> implement idetachable are detached in the end of request via
> reflection, so that should help somewhat.
>
> -igor
>
> On Thu, Aug 28, 2008 at 1:57 PM, Kaspar Fischer <fischerk@...>
> wrote:
>> Is there an easy way to assert that all models are detached at the end of
>> the
>> request?
>>
>> It does not look so easy to check this as models do not have common base
>> class
>> where one could register them for a check...
>>
>> I often use an additional model in a component and store it as a member
>> field;
>> if I forgot to detach() this model in the onDetach() handler, I would have
>> a
>> "dangling" model. That caused me quite some trouble once and I want to
>> avoid
>> it in the future.
>>
>> Thanks,
>> Kaspar
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@...
>> For additional commands, e-mail: users-help@...
>>
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@...
> For additional commands, e-mail: users-help@...
>
>


--
Become a Wicket expert, learn from the best: http://wicketinaction.com
Apache Wicket 1.3.4 is released
Get it now: http://www.apache.org/dyn/closer.cgi/wicket/1.3.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by hbf :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Matijn, thank you for your hint.

I searched on your blog, http://martijndashorst.com/blog/, and Eelco's,
http://day-to-day-stuff.blogspot.com/, but must have searched for the  
wrong
thing ("transient", "entity", "SerializableChecker")...

Anyways, I'd like to do what you suggest, but have a few question:

I guess I have to provide my own implementation of  
IObjectStreamFactory in
order to force my subclass of SerializableChecker to run (in development
mode). For this, do I subclass  
IObjectStreamFactory.DefaultObjectStreamFactory
or WicketObjectStreamFactory?

Where would I install this custom IObjectStreamFactory?

As to SerializableChecker itself, I think my version simply has to  
look for
models in

private void check(Object obj)
{
   if (obj == null)
   {
     return;
   }
   Class<?> cls = obj.getClass();
   nameStack.add(simpleName);
   traceStack.add(new TraceSlot(obj, fieldDescription));
   if (!(obj instanceof Serializable) && (!Proxy.isProxyClass(cls)))
   {
     throw new WicketNotSerializableException(
     toPrettyPrintedStack(obj.getClass().getName()), exception);
   }
   // NEW
   if (obj instanceof LoadableDetachableModel) {
     LoadableDetachableModel m = (LoadableDetachableModel)m;
     if (m.isAttached())
     {
       throw new IllegalStateException("Model not detached!");
     }
   }
   // ...

Regards,
Kaspar

On 29.08.2008, at 08:20, Martijn Dashorst wrote:

> you could extend the serializerchecker to check for underached models.
> We did something similar checking for non-transient Entity objects.
> Iirc the code is either on my blog or Eelco's blog.
>
> Martijn
>
> On 8/28/08, Igor Vaynberg <igor.vaynberg@...> wrote:
>> no, there is no easy way to "assert" that any model has been  
>> detached,
>> because they do not keep a flag.
>>
>> in 1.5 we will implement it so that all fields of a component that
>> implement idetachable are detached in the end of request via
>> reflection, so that should help somewhat.
>>
>> -igor
>>
>> On Thu, Aug 28, 2008 at 1:57 PM, Kaspar Fischer  
>> <fischerk@...>
>> wrote:
>>> Is there an easy way to assert that all models are detached at the  
>>> end of
>>> the
>>> request?
>>>
>>> It does not look so easy to check this as models do not have  
>>> common base
>>> class
>>> where one could register them for a check...
>>>
>>> I often use an additional model in a component and store it as a  
>>> member
>>> field;
>>> if I forgot to detach() this model in the onDetach() handler, I  
>>> would have
>>> a
>>> "dangling" model. That caused me quite some trouble once and I  
>>> want to
>>> avoid
>>> it in the future.
>>>
>>> Thanks,
>>> Kaspar
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@...
>>> For additional commands, e-mail: users-help@...
>>>
>>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@...
>> For additional commands, e-mail: users-help@...
>>
>>
>
>
> --
> Become a Wicket expert, learn from the best: http://wicketinaction.com
> Apache Wicket 1.3.4 is released
> Get it now: http://www.apache.org/dyn/closer.cgi/wicket/1.3.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@...
> For additional commands, e-mail: users-help@...
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by Martijn Dashorst :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

We just do it in CustomRequestCycle#onEndRequest():

        @Override
        protected void onEndRequest()
        {
                if (Application.get().isDevelopment())
                {
                        // controleer of er hibernate objecten in de pagina vastgehouden worden.
                        // eerst de pagina die het request heeft beantwoord

                        Page requestPage = getRequest().getPage();
                        testDetachedObjects(requestPage);

                        // als de response een Page heeft, dan deze controleren op de
aanwezigheid van
                        // hibernate objecten.
                        if (getRequestTarget() instanceof IPageRequestTarget)
                        {
                                Page responsePage = ((IPageRequestTarget) getRequestTarget()).getPage();

                                if (responsePage != requestPage)
                                {
                                        testDetachedObjects(responsePage);
                                }
                        }
                }


And:

        private void testDetachedObjects(final Page page)
        {
                if (page == null)
                {
                        return;
                }

                try
                {
                        NotSerializableException exception = new NotSerializableException();
                        EntityAndSerializableChecker checker = new
EntityAndSerializableChecker(exception);
                        checker.writeObject(page);
                }
                catch (Exception ex)
                {
                        log.error("Couldn't test/serialize the Page: " + page + ", error: " + ex);
                }
        }

--
Become a Wicket expert, learn from the best: http://wicketinaction.com
Apache Wicket 1.3.4 is released
Get it now: http://www.apache.org/dyn/closer.cgi/wicket/1.3.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by Martijn Dashorst :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

and yes, your implementation of the check looks good.

Martijn

--
Become a Wicket expert, learn from the best: http://wicketinaction.com
Apache Wicket 1.3.4 is released
Get it now: http://www.apache.org/dyn/closer.cgi/wicket/1.3.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by hbf :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Many, many thanks for this! Very much appreciated. - Kaspar

On 29.08.2008, at 15:15, Martijn Dashorst wrote:

> We just do it in CustomRequestCycle#onEndRequest():
>
> @Override
> protected void onEndRequest()
> {
> if (Application.get().isDevelopment())
> {
> // controleer of er hibernate objecten in de pagina vastgehouden  
> worden.
> // eerst de pagina die het request heeft beantwoord
>
> Page requestPage = getRequest().getPage();
> testDetachedObjects(requestPage);
>
> // als de response een Page heeft, dan deze controleren op de
> aanwezigheid van
> // hibernate objecten.
> if (getRequestTarget() instanceof IPageRequestTarget)
> {
> Page responsePage = ((IPageRequestTarget)  
> getRequestTarget()).getPage();
>
> if (responsePage != requestPage)
> {
> testDetachedObjects(responsePage);
> }
> }
> }
>
>
> And:
>
> private void testDetachedObjects(final Page page)
> {
> if (page == null)
> {
> return;
> }
>
> try
> {
> NotSerializableException exception = new  
> NotSerializableException();
> EntityAndSerializableChecker checker = new
> EntityAndSerializableChecker(exception);
> checker.writeObject(page);
> }
> catch (Exception ex)
> {
> log.error("Couldn't test/serialize the Page: " + page + ", error:  
> " + ex);
> }
> }
>
> --
> Become a Wicket expert, learn from the best: http://wicketinaction.com
> Apache Wicket 1.3.4 is released
> Get it now: http://www.apache.org/dyn/closer.cgi/wicket/1.3.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@...
> For additional commands, e-mail: users-help@...
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by Erik van Oosten :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Eelco is on http://chillenious.wordpress.com.
The link you mentioned http://day-to-day-stuff.blogspot.com/ is from
your's truly.

Regards,
     Erik.

Kaspar Fischer wrote:
> Matijn, thank you for your hint.
>
> I searched on your blog, http://martijndashorst.com/blog/, and Eelco's,
> http://day-to-day-stuff.blogspot.com/, but must have searched for the
> wrong
> thing ("transient", "entity", "SerializableChecker")...
>
>


--
Erik van Oosten
http://day-to-day-stuff.blogspot.com/


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by hbf :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

For the sake of completeness, here is the solution I am currently  
using. It uses,
as suggested by Martijn, a custom request cycle and a modified version  
of
SerializableChecker. You have to install the custom request cycle in  
your application
using

   @Override
   public RequestCycle newRequestCycle(Request request, Response  
response)
   {
     return new CustomRequestCycle(this, (WebRequest) request,  
(WebResponse) response);
   }

Hope this helps others, too!
Kaspar

// ***** FILE: CustomRequestCycle.java *****
import java.io.NotSerializableException;

import org.apache.wicket.Page;
import org.apache.wicket.Response;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.protocol.http.WebRequestCycle;
import org.apache.wicket.request.target.component.IPageRequestTarget;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
  * A custom request cycle that checks, when in development mode, that  
all models of a page are
  * detached. Currently, only model that are instances of  
LoadableDetachableModel (including
  * subclasses) are checked.
  */
public class CustomRequestCycle extends WebRequestCycle
{
   /** Logging object */
   private static final Logger log =  
LoggerFactory.getLogger(WebRequestCycle.class);

   public CustomRequestCycle(WebApplication application, WebRequest  
request, Response response)
   {
     super(application, request, response);
   }

   @Override
   protected void onEndRequest()
   {
     super.onEndRequest();

     if  
(WebApplication
.DEVELOPMENT
.equalsIgnoreCase(WebApplication.get().getConfigurationType()))
     {
       Page requestPage = getRequest().getPage();
       testDetachedObjects(requestPage);

       if (getRequestTarget() instanceof IPageRequestTarget)
       {
         Page responsePage = ((IPageRequestTarget)  
getRequestTarget()).getPage();

         if (responsePage != requestPage)
         {
           testDetachedObjects(responsePage);
         }
       }
     }
   }

   private void testDetachedObjects(final Page page)
   {
     if (page == null)
     {
       return;
     }

     try
     {
       NotSerializableException exception = new  
NotSerializableException(
           "Model is not detached when attempting to serialize!");
       DetachedChecker checker = new DetachedChecker(exception);
       checker.writeObject(page);
     }
     catch (Exception ex)
     {
       log.error("Couldn't test/serialize the Page: " + page + ",  
error: " + ex);
     }
   }
}


// ***** FILE: DetachedChecker.java *****
import java.io.Externalizable;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.util.lang.Generics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
  * This is taken from Wicket SerializableChecker.java (SVN r687197)  
and customized slightly
  * (see comments containing "KF"). See the latter file for all  
details, including terms of
  * use. Notice that this does not replace SerializableChecker; the  
latter is still run.
  */
public final class DetachedChecker extends ObjectOutputStream
{
   /**
    * Exception that is thrown when a non-serializable object was found.
    */
   public static final class WicketNotSerializableException extends  
WicketRuntimeException
   {
     private static final long serialVersionUID = 1L;

     WicketNotSerializableException(String message, Throwable cause)
     {
       super(message, cause);
     }
   }

   /**
    * Does absolutely nothing.
    */
   private static class NoopOutputStream extends OutputStream
   {
     @Override
     public void close()
     {
     }

     @Override
     public void flush()
     {
     }

     @Override
     public void write(byte[] b)
     {
     }

     @Override
     public void write(byte[] b, int i, int l)
     {
     }

     @Override
     public void write(int b)
     {
     }
   }

   private static abstract class ObjectOutputAdaptor implements  
ObjectOutput
   {

     public void close() throws IOException
     {
     }

     public void flush() throws IOException
     {
     }

     public void write(byte[] b) throws IOException
     {
     }

     public void write(byte[] b, int off, int len) throws IOException
     {
     }

     public void write(int b) throws IOException
     {
     }

     public void writeBoolean(boolean v) throws IOException
     {
     }

     public void writeByte(int v) throws IOException
     {
     }

     public void writeBytes(String s) throws IOException
     {
     }

     public void writeChar(int v) throws IOException
     {
     }

     public void writeChars(String s) throws IOException
     {
     }

     public void writeDouble(double v) throws IOException
     {
     }

     public void writeFloat(float v) throws IOException
     {
     }

     public void writeInt(int v) throws IOException
     {
     }

     public void writeLong(long v) throws IOException
     {
     }

     public void writeShort(int v) throws IOException
     {
     }

     public void writeUTF(String str) throws IOException
     {
     }
   }

   /** Holds information about the field and the resulting object  
being traced. */
   private static final class TraceSlot
   {
     private final String fieldDescription;

     private final Object object;

     TraceSlot(Object object, String fieldDescription)
     {
       super();
       this.object = object;
       this.fieldDescription = fieldDescription;
     }

     @Override
     public String toString()
     {
       return object.getClass() + " - " + fieldDescription;
     }
   }

   private static final NoopOutputStream DUMMY_OUTPUT_STREAM = new  
NoopOutputStream();

   /** log. */
   private static final Logger log =  
LoggerFactory.getLogger(DetachedChecker.class);

   /** Whether we can execute the tests. If false, check will just  
return. */
   private static boolean available = true;

   // this hack - accessing the serialization API through  
introspection - is
   // the only way to use Java serialization for our purposes without  
writing
   // the whole thing from scratch (and even then, it would be  
limited). This
   // way of working is of course fragile for internal API changes,  
but as we
   // do an extra check on availability and we report when we can't  
use this
   // introspection fu, we'll find out soon enough and clients on this  
class
   // can fall back on Java's default exception for serialization  
errors (which
   // sucks and is the main reason for this attempt).
   private static final Method LOOKUP_METHOD;

   private static final Method GET_CLASS_DATA_LAYOUT_METHOD;

   private static final Method GET_NUM_OBJ_FIELDS_METHOD;

   private static final Method GET_OBJ_FIELD_VALUES_METHOD;

   private static final Method GET_FIELD_METHOD;

   private static final Method HAS_WRITE_REPLACE_METHOD_METHOD;

   private static final Method INVOKE_WRITE_REPLACE_METHOD;

   static
   {
     try
     {
       LOOKUP_METHOD =  
ObjectStreamClass.class.getDeclaredMethod("lookup", new Class[] {
           Class.class, Boolean.TYPE
       });
       LOOKUP_METHOD.setAccessible(true);

       GET_CLASS_DATA_LAYOUT_METHOD =  
ObjectStreamClass.class.getDeclaredMethod("getClassDataLayout",  
(Class[]) null);
       GET_CLASS_DATA_LAYOUT_METHOD.setAccessible(true);

       GET_NUM_OBJ_FIELDS_METHOD =  
ObjectStreamClass.class.getDeclaredMethod("getNumObjFields", (Class[])  
null);
       GET_NUM_OBJ_FIELDS_METHOD.setAccessible(true);

       GET_OBJ_FIELD_VALUES_METHOD =  
ObjectStreamClass.class.getDeclaredMethod("getObjFieldValues", new  
Class[] {
           Object.class, Object[].class
       });
       GET_OBJ_FIELD_VALUES_METHOD.setAccessible(true);

       GET_FIELD_METHOD =  
ObjectStreamField.class.getDeclaredMethod("getField", (Class[]) null);
       GET_FIELD_METHOD.setAccessible(true);

       HAS_WRITE_REPLACE_METHOD_METHOD =  
ObjectStreamClass.class.getDeclaredMethod("hasWriteReplaceMethod",
           (Class[]) null);
       HAS_WRITE_REPLACE_METHOD_METHOD.setAccessible(true);

       INVOKE_WRITE_REPLACE_METHOD =  
ObjectStreamClass.class.getDeclaredMethod("invokeWriteReplace", new  
Class[] {
         Object.class
       });
       INVOKE_WRITE_REPLACE_METHOD.setAccessible(true);
     }
     catch (SecurityException e)
     {
       available = false;
       throw new RuntimeException(e);
     }
     catch (NoSuchMethodException e)
     {
       available = false;
       throw new RuntimeException(e);
     }
   }

   /**
    * Gets whether we can execute the tests. If false, calling {@link  
#check(Object)} will just
    * return and you are advised to rely on the {@link  
NotSerializableException}. Clients are
    * advised to call this method prior to calling the check method.
    *
    * @return whether security settings and underlying API etc allow  
for accessing the serialization
    *         API using introspection
    */
   public static boolean isAvailable()
   {
     return available;
   }

   /** object stack that with the trace path. */
   private final LinkedList<TraceSlot> traceStack = new  
LinkedList<TraceSlot>();

   /** set for checking circular references. */
   private final Map<Object, Object> checked = new  
IdentityHashMap<Object, Object>();

   /** string stack with current names pushed. */
   private final LinkedList<String> nameStack = new  
LinkedList<String>();

   /** root object being analyzed. */
   private Object root;

   /** cache for classes - writeObject methods. */
   private final Map<Class<?>, Object> writeObjectMethodCache =  
Generics.newHashMap();

   /** current simple field name. */
   private String simpleName = "";

   /** current full field description. */
   private String fieldDescription;

   /** Exception that should be set as the cause when throwing a new  
exception. */
   private final NotSerializableException exception;

   /**
    * Construct.
    *
    * @param exception
    *          exception that should be set as the cause when throwing  
a new exception
    *
    * @throws IOException
    */
   public DetachedChecker(NotSerializableException exception) throws  
IOException
   {
     this.exception = exception;
   }

   /**
    * @see java.io.ObjectOutputStream#reset()
    */
   @Override
   public void reset() throws IOException
   {
     root = null;
     checked.clear();
     fieldDescription = null;
     simpleName = null;
     traceStack.clear();
     nameStack.clear();
     writeObjectMethodCache.clear();
   }

   private void check(Object obj)
   {
     if (obj == null)
     {
       return;
     }

     Class<?> cls = obj.getClass();
     nameStack.add(simpleName);
     traceStack.add(new TraceSlot(obj, fieldDescription));

     if (!(obj instanceof Serializable) && (!Proxy.isProxyClass(cls)))
     {
       throw new  
WicketNotSerializableException
(toPrettyPrintedStack(obj.getClass().getName()), exception);
     }

     // BEGIN KF
     if (obj instanceof LoadableDetachableModel)
     {
       LoadableDetachableModel model = (LoadableDetachableModel) obj;
       if (model.isAttached())
       {
         Object value = model.getObject();
         throw new  
WicketNotSerializableException
(toPrettyPrintedStack(obj.getClass().getName()) + "\nmodel object: "
             + value, exception);
       }
     }
     // END KF

     ObjectStreamClass desc;
     for (;;)
     {
       try
       {
         desc = (ObjectStreamClass) LOOKUP_METHOD.invoke(null, new  
Object[] {
             cls, Boolean.TRUE
         });
         Class<?> repCl;
         if (!((Boolean) HAS_WRITE_REPLACE_METHOD_METHOD.invoke(desc,  
(Object[]) null)).booleanValue()
             || (obj = INVOKE_WRITE_REPLACE_METHOD.invoke(desc, new  
Object[] {
               obj
             })) == null || (repCl = obj.getClass()) == cls)
         {
           break;
         }
         cls = repCl;
       }
       catch (IllegalAccessException e)
       {
         throw new RuntimeException(e);
       }
       catch (InvocationTargetException e)
       {
         throw new RuntimeException(e);
       }
     }

     if (cls.isPrimitive())
     {
       // skip
     }
     else if (cls.isArray())
     {
       checked.put(obj, null);
       Class<?> ccl = cls.getComponentType();
       if (!(ccl.isPrimitive()))
       {
         Object[] objs = (Object[]) obj;
         for (int i = 0; i < objs.length; i++)
         {
           String arrayPos = "[" + i + "]";
           simpleName = arrayPos;
           fieldDescription += arrayPos;
           check(objs[i]);
         }
       }
     }
     else if (obj instanceof Externalizable && (!
Proxy.isProxyClass(cls)))
     {
       Externalizable extObj = (Externalizable) obj;
       try
       {
         extObj.writeExternal(new ObjectOutputAdaptor()
         {
           private int count = 0;

           public void writeObject(Object streamObj) throws IOException
           {
             // Check for circular reference.
             if (checked.containsKey(streamObj))
             {
               return;
             }

             checked.put(streamObj, null);
             String arrayPos = "[write:" + count++ + "]";
             simpleName = arrayPos;
             fieldDescription += arrayPos;

             check(streamObj);
           }
         });
       }
       catch (Exception e)
       {
         if (e instanceof WicketNotSerializableException)
         {
           throw (WicketNotSerializableException) e;
         }
         log.warn("error delegating to Externalizable : " +  
e.getMessage() + ", path: " + currentPath());
       }
     }
     else
     {
       Method writeObjectMethod = null;
       Object o = writeObjectMethodCache.get(cls);
       if (o != null)
       {
         if (o instanceof Method)
         {
           writeObjectMethod = (Method) o;
         }
       }
       else
       {
         try
         {
           writeObjectMethod = cls.getDeclaredMethod("writeObject",  
new Class[] {
             java.io.ObjectOutputStream.class
           });
         }
         catch (SecurityException e)
         {
           // we can't access/ set accessible to true
           writeObjectMethodCache.put(cls, Boolean.FALSE);
         }
         catch (NoSuchMethodException e)
         {
           // cls doesn't have that method
           writeObjectMethodCache.put(cls, Boolean.FALSE);
         }
       }

       final Object original = obj;
       if (writeObjectMethod != null)
       {
         class InterceptingObjectOutputStream extends ObjectOutputStream
         {
           private int counter;

           InterceptingObjectOutputStream() throws IOException
           {
             super(DUMMY_OUTPUT_STREAM);
             enableReplaceObject(true);
           }

           @Override
           protected Object replaceObject(Object streamObj) throws  
IOException
           {
             if (streamObj == original)
             {
               return streamObj;
             }

             counter++;
             // Check for circular reference.
             if (checked.containsKey(streamObj))
             {
               return null;
             }

             checked.put(original, null);
             String arrayPos = "[write:" + counter + "]";
             simpleName = arrayPos;
             fieldDescription += arrayPos;
             check(streamObj);
             return streamObj;
           }
         }
         try
         {
           InterceptingObjectOutputStream ioos = new  
InterceptingObjectOutputStream();
           ioos.writeObject(obj);
         }
         catch (Exception e)
         {
           if (e instanceof WicketNotSerializableException)
           {
             throw (WicketNotSerializableException) e;
           }
           log.warn("error delegating to writeObject : " +  
e.getMessage() + ", path: " + currentPath());
         }
       }
       else
       {
         Object[] slots;
         try
         {
           slots = (Object[])  
GET_CLASS_DATA_LAYOUT_METHOD.invoke(desc, (Object[]) null);
         }
         catch (Exception e)
         {
           throw new RuntimeException(e);
         }
         for (int i = 0; i < slots.length; i++)
         {
           ObjectStreamClass slotDesc;
           try
           {
             Field descField =  
slots[i].getClass().getDeclaredField("desc");
             descField.setAccessible(true);
             slotDesc = (ObjectStreamClass) descField.get(slots[i]);
           }
           catch (Exception e)
           {
             throw new RuntimeException(e);
           }
           checked.put(obj, null);
           checkFields(obj, slotDesc);
         }
       }
     }

     traceStack.removeLast();
     nameStack.removeLast();
   }

   private void checkFields(Object obj, ObjectStreamClass desc)
   {
     int numFields;
     try
     {
       numFields = ((Integer) GET_NUM_OBJ_FIELDS_METHOD.invoke(desc,  
(Object[]) null)).intValue();
     }
     catch (IllegalAccessException e)
     {
       throw new RuntimeException(e);
     }
     catch (InvocationTargetException e)
     {
       throw new RuntimeException(e);
     }

     if (numFields > 0)
     {
       int numPrimFields;
       ObjectStreamField[] fields = desc.getFields();
       Object[] objVals = new Object[numFields];
       numPrimFields = fields.length - objVals.length;
       try
       {
         GET_OBJ_FIELD_VALUES_METHOD.invoke(desc, new Object[] {
             obj, objVals
         });
       }
       catch (IllegalAccessException e)
       {
         throw new RuntimeException(e);
       }
       catch (InvocationTargetException e)
       {
         throw new RuntimeException(e);
       }
       for (int i = 0; i < objVals.length; i++)
       {
         if (objVals[i] instanceof String || objVals[i] instanceof  
Number || objVals[i] instanceof Date
             || objVals[i] instanceof Boolean || objVals[i] instanceof  
Class)
         {
           // filter out common cases
           continue;
         }

         // Check for circular reference.
         if (checked.containsKey(objVals[i]))
         {
           continue;
         }

         ObjectStreamField fieldDesc = fields[numPrimFields + i];
         Field field;
         try
         {
           field = (Field) GET_FIELD_METHOD.invoke(fieldDesc,  
(Object[]) null);
         }
         catch (IllegalAccessException e)
         {
           throw new RuntimeException(e);
         }
         catch (InvocationTargetException e)
         {
           throw new RuntimeException(e);
         }

         String fieldName = field.getName();
         simpleName = field.getName();
         fieldDescription = field.toString();
         check(objVals[i]);
       }
     }
   }

   /**
    * @return name from root to current node concatenated with slashes
    */
   private StringBuffer currentPath()
   {
     StringBuffer b = new StringBuffer();
     for (Iterator<String> it = nameStack.iterator(); it.hasNext();)
     {
       b.append(it.next());
       if (it.hasNext())
       {
         b.append('/');
       }
     }
     return b;
   }

   /**
    * Dump with indentation.
    *
    * @param type
    *          the type that couldn't be serialized
    * @return A very pretty dump
    */
   private final String toPrettyPrintedStack(String type)
   {
     StringBuffer result = new StringBuffer();
     StringBuffer spaces = new StringBuffer();
     result.append("Unable to serialize class: ");
     result.append(type);
     result.append("\nField hierarchy is:");
     for (Iterator<TraceSlot> i = traceStack.listIterator();  
i.hasNext();)
     {
       spaces.append("  ");
       TraceSlot slot = i.next();
       result.append("\n").append(spaces).append(slot.fieldDescription);
       
result.append(" [class=").append(slot.object.getClass().getName());
       if (slot.object instanceof Component)
       {
         Component component = (Component) slot.object;
         result.append(", path=").append(component.getPath());
       }
       result.append("]");
       result.append(" {object:" + slot.object + "}");
     }
     result.append(" <----- model that is not detached"); // KF
     return result.toString();
   }

   /**
    * @see  
java.io.ObjectOutputStream#writeObjectOverride(java.lang.Object)
    */
   @Override
   protected final void writeObjectOverride(Object obj) throws  
IOException
   {
     if (!available)
     {
       return;
     }
     root = obj;
     if (fieldDescription == null)
     {
       fieldDescription = (root instanceof Component) ? ((Component)  
root).getPath() : "";
     }

     check(root);
   }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by jwcarman :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Could you perhaps use an aspect for this?

On Thu, Aug 28, 2008 at 4:57 PM, Kaspar Fischer <fischerk@...> wrote:

> Is there an easy way to assert that all models are detached at the end of
> the
> request?
>
> It does not look so easy to check this as models do not have common base
> class
> where one could register them for a check...
>
> I often use an additional model in a component and store it as a member
> field;
> if I forgot to detach() this model in the onDetach() handler, I would have a
> "dangling" model. That caused me quite some trouble once and I want to avoid
> it in the future.
>
> Thanks,
> Kaspar
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@...
> For additional commands, e-mail: users-help@...
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: Assert that all models are detached at the end of the request?

by egolan74 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Why do you throw WicketNotSerializableException when the model is still
detached?

On Fri, Sep 5, 2008 at 11:11 AM, Kaspar Fischer <fischerk@...>wrote:

> For the sake of completeness, here is the solution I am currently using. It
> uses,
> as suggested by Martijn, a custom request cycle and a modified version of
> SerializableChecker. You have to install the custom request cycle in your
> application
> using
>
>  @Override
>  public RequestCycle newRequestCycle(Request request, Response response)
>  {
>    return new CustomRequestCycle(this, (WebRequest) request, (WebResponse)
> response);
>  }
>
> Hope this helps others, too!
> Kaspar
>
> // ***** FILE: CustomRequestCycle.java *****
> import java.io.NotSerializableException;
>
> import org.apache.wicket.Page;
> import org.apache.wicket.Response;
> import org.apache.wicket.protocol.http.WebApplication;
> import org.apache.wicket.protocol.http.WebRequest;
> import org.apache.wicket.protocol.http.WebRequestCycle;
> import org.apache.wicket.request.target.component.IPageRequestTarget;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
>
> /**
>  * A custom request cycle that checks, when in development mode, that all
> models of a page are
>  * detached. Currently, only model that are instances of
> LoadableDetachableModel (including
>  * subclasses) are checked.
>  */
> public class CustomRequestCycle extends WebRequestCycle
> {
>  /** Logging object */
>  private static final Logger log =
> LoggerFactory.getLogger(WebRequestCycle.class);
>
>  public CustomRequestCycle(WebApplication application, WebRequest request,
> Response response)
>  {
>    super(application, request, response);
>  }
>
>  @Override
>  protected void onEndRequest()
>  {
>    super.onEndRequest();
>
>    if
> (WebApplication.DEVELOPMENT.equalsIgnoreCase(WebApplication.get().getConfigurationType()))
>    {
>      Page requestPage = getRequest().getPage();
>      testDetachedObjects(requestPage);
>
>      if (getRequestTarget() instanceof IPageRequestTarget)
>      {
>        Page responsePage = ((IPageRequestTarget)
> getRequestTarget()).getPage();
>
>        if (responsePage != requestPage)
>        {
>          testDetachedObjects(responsePage);
>        }
>      }
>    }
>  }
>
>  private void testDetachedObjects(final Page page)
>  {
>    if (page == null)
>    {
>      return;
>    }
>
>    try
>    {
>      NotSerializableException exception = new NotSerializableException(
>          "Model is not detached when attempting to serialize!");
>      DetachedChecker checker = new DetachedChecker(exception);
>      checker.writeObject(page);
>    }
>    catch (Exception ex)
>    {
>      log.error("Couldn't test/serialize the Page: " + page + ", error: " +
> ex);
>    }
>  }
> }
>
>
> //