You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/09/02 19:03:36 UTC

svn commit: r439617 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/ioc/services/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/internal/structure/ main/java/org/apache/tapestry/...

Author: hlship
Date: Sat Sep  2 10:03:34 2006
New Revision: 439617

URL: http://svn.apache.org/viewvc?rev=439617&view=rev
Log:
Change the LoggingDecorator to not endlessly re-log the exact same exception as the stack unwinds (when an exception is thrown).
Split a few methods of ComponentLifecycle into their own interface, PageLifecycleListener.
Add new tests for ParameterWorker, and fix some bugs (and make some improvements).

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ExceptionTracker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformUtils.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterComponent.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/TransformUtilsTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImpl.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImpl.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,38 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc.services;
+
+import java.util.Set;
+
+import org.apache.tapestry.ioc.services.ExceptionTracker;
+
+import static org.apache.tapestry.util.CollectionFactory.newSet;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class ExceptionTrackerImpl implements ExceptionTracker
+{
+    private final Set<Throwable> _exceptions = newSet();
+
+    public boolean exceptionLogged(Throwable exception)
+    {
+        boolean result = _exceptions.contains(exception);
+
+        _exceptions.add(exception);
+
+        return result;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java Sat Sep  2 10:03:34 2006
@@ -21,6 +21,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.ExceptionTracker;
 import org.apache.tapestry.ioc.services.LoggingDecorator;
 import org.apache.tapestry.ioc.services.MethodIterator;
 import org.apache.tapestry.ioc.services.MethodSignature;
@@ -36,16 +37,19 @@
 {
     private final ClassFactory _classFactory;
 
-    public LoggingDecoratorImpl(ClassFactory classFactory)
+    private final ExceptionTracker _exceptionTracker;
+
+    public LoggingDecoratorImpl(ClassFactory classFactory, ExceptionTracker exceptionTracker)
     {
         _classFactory = classFactory;
+        _exceptionTracker = exceptionTracker;
     }
 
     public <T> T build(Class<T> serviceInterface, T delegate, String serviceId, Log serviceLog)
     {
         Class interceptorClass = createInterceptorClass(serviceInterface, serviceId);
 
-        ServiceLogger logger = new ServiceLogger(serviceLog);
+        ServiceLogger logger = new ServiceLogger(serviceLog, _exceptionTracker);
 
         Constructor cc = interceptorClass.getConstructors()[0];
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java Sat Sep  2 10:03:34 2006
@@ -20,6 +20,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.ioc.services.ExceptionTracker;
 import org.apache.tapestry.util.Defense;
 
 /**
@@ -32,15 +33,18 @@
 {
     private final Log _log;
 
+    private final ExceptionTracker _exceptionTracker;
+
     private static final String ENTER = "ENTER";
 
     private static final String EXIT = " EXIT";
 
     private static final String FAIL = " FAIL";
 
-    public ServiceLogger(Log log)
+    public ServiceLogger(Log log, ExceptionTracker exceptionTracker)
     {
         _log = log;
+        _exceptionTracker = exceptionTracker;
     }
 
     /** Returns true if the debugging is enabled for the underlying Log. */
@@ -169,6 +173,11 @@
     /** Invoked when method invocation instead throws an exception. */
     public void fail(String name, Throwable t)
     {
-        _log.debug(format("[%s] %s -- %s", FAIL, name, t.getClass().getName()), t);
+        if (_log.isDebugEnabled())
+        {
+            _log.debug(
+                    format("[%s] %s -- %s", FAIL, name, t.getClass().getName()),
+                    _exceptionTracker.exceptionLogged(t) ? null : t);
+        }
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java Sat Sep  2 10:03:34 2006
@@ -535,6 +535,7 @@
 
         addMethodToDescription("extend", methodSignature, methodBody);
 
+        _addedMethods.add(method);
     }
 
     private void addMethodToDescription(String operation, MethodSignature methodSignature,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java Sat Sep  2 10:03:34 2006
@@ -16,7 +16,6 @@
 
 import java.lang.reflect.Modifier;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.internal.ioc.IOCUtilities;
@@ -26,10 +25,9 @@
 import org.apache.tapestry.services.ComponentClassTransformWorker;
 import org.apache.tapestry.services.MethodSignature;
 import org.apache.tapestry.services.TransformConstants;
+import org.apache.tapestry.services.TransformUtils;
 import org.apache.tapestry.util.BodyBuilder;
 
-import static org.apache.tapestry.util.CollectionFactory.newMap;
-
 /**
  * Responsible for identifying parameters via the {@link org.apache.tapestry.annotations.Parameter}
  * annotation on component fields. This is one of the most complex of the transformations.
@@ -38,20 +36,6 @@
  */
 public class ParameterWorker implements ComponentClassTransformWorker
 {
-    private static final Map<String, String> PRIMITIVE_TO_WRAPPER = newMap();
-
-    static
-    {
-        PRIMITIVE_TO_WRAPPER.put("boolean", "java.lang.Boolean");
-        PRIMITIVE_TO_WRAPPER.put("char", "java.lang.Character");
-        PRIMITIVE_TO_WRAPPER.put("byte", "java.lang.Byte");
-        PRIMITIVE_TO_WRAPPER.put("short", "java.lang.Short");
-        PRIMITIVE_TO_WRAPPER.put("int", "java.lang.Integer");
-        PRIMITIVE_TO_WRAPPER.put("long", "java.lang.Long");
-        PRIMITIVE_TO_WRAPPER.put("float", "java.lang.Float");
-        PRIMITIVE_TO_WRAPPER.put("double", "java.lang.Double");
-    }
-
     public void transform(ClassTransformation transformation, MutableComponentModel model)
     {
         List<String> fieldNames = transformation.findFieldsWithAnnotation(Parameter.class);
@@ -84,6 +68,8 @@
                 name,
                 parameterName,
                 cachedFieldName,
+                cache,
+                type,
                 resourcesFieldName,
                 transformation);
 
@@ -112,7 +98,8 @@
     /** Returns the name of a field that stores whether the parameter binding is invariant. */
 
     private String addParameterSetup(String fieldName, String parameterName,
-            String cachedFieldName, String resourcesFieldName, ClassTransformation transformation)
+            String cachedFieldName, boolean cache, String fieldType, String resourcesFieldName,
+            ClassTransformation transformation)
     {
         String name = transformation
                 .addField(Modifier.PRIVATE, "boolean", fieldName + "_invariant");
@@ -125,22 +112,24 @@
 
         transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, body);
 
-        // Now, when the page detaches, ensure that any variant parameters are
-        // are returned to default value.
+        // Now, when the component completes rendering, ensure that any variant parameters are
+        // are returned to default value. This isn't necessary when the parameter is not cached,
+        // because (unless the binding is invariant), there's no value to get rid of (and if it is
+        // invariant, there's no need to get rid of it).
 
-        BodyBuilder builder = new BodyBuilder();
-        builder.addln("if (! %s)", name);
-        builder.begin();
-
-        // TODO: Handle primitives
+        if (cache)
+        {
+            BodyBuilder builder = new BodyBuilder();
+            builder.addln("if (! %s)", name);
+            builder.begin();
 
-        builder.addln("%s = null;", fieldName);
-        builder.addln("%s = false;", cachedFieldName);
-        builder.end();
+            builder.addln("%s = %s;", fieldName, TransformUtils.getDefaultValue(fieldType));
+            builder.addln("%s = false;", cachedFieldName);
+            builder.end();
 
-        transformation.extendMethod(
-                TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
-                builder.toString());
+            transformation.extendMethod(TransformConstants.CLEANUP_AFTER_RENDER_SIGNATURE, builder
+                    .toString());
+        }
 
         return name;
     }
@@ -195,8 +184,7 @@
 
         builder.addln("if (%s) return %s;", cachedFieldName, fieldName);
 
-        String cast = PRIMITIVE_TO_WRAPPER.containsKey(fieldType) ? PRIMITIVE_TO_WRAPPER
-                .get(fieldType) : fieldType;
+        String cast = TransformUtils.getWrapperType(fieldType);
 
         // The ($r) cast will convert the result to the method return type; generally
         // this does nothing. but for primitive types, it will unwrap

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Sat Sep  2 10:03:34 2006
@@ -25,6 +25,7 @@
 import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.runtime.ComponentLifecycle;
 import org.apache.tapestry.runtime.LifecycleEvent;
+import org.apache.tapestry.runtime.PageLifecycleListener;
 import org.apache.tapestry.runtime.RenderCommand;
 import org.apache.tapestry.runtime.RenderQueue;
 
@@ -42,7 +43,7 @@
  * 
  * @author Howard M. Lewis Ship
  */
-public class ComponentPageElementImpl implements ComponentPageElement
+public class ComponentPageElementImpl implements ComponentPageElement, PageLifecycleListener
 {
     private final Page _page;
 
@@ -325,6 +326,14 @@
     /** Pushes AfterRender and BeforeRender phase commands onto the queue. */
     public void render(MarkupWriter writer, RenderQueue queue)
     {
+        // Here's the problem: if an exception gets thrown during rendering, then
+        // some components may never see their AfterRender phase cleanup occur, leaving
+        // the page as a whole in an undesirable state. I suppose
+        // that the pageDidDetach() would be another place to clean things up, but perhaps
+        // RenderQueue needs to catch exceptions and invoke commands that related to cleanup?
+        // Or have a way to determine if the page is "dirty" and should not go back into the
+        // pool?
+
         queue.push(_afterRender);
         queue.push(_beforeRender);
     }
@@ -424,6 +433,21 @@
     public boolean isRendering()
     {
         return _rendering;
+    }
+
+    public void containingPageDidAttach()
+    {
+        _component.containingPageDidAttach();
+    }
+
+    public void containingPageDidDetach()
+    {
+        _component.containingPageDidDetach();
+    }
+
+    public void containingPageDidLoad()
+    {
+        _component.containingPageDidLoad();
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java Sat Sep  2 10:03:34 2006
@@ -17,7 +17,7 @@
 import java.util.Locale;
 
 import org.apache.commons.logging.Log;
-import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.apache.tapestry.runtime.PageLifecycleListener;
 
 /**
  * Represents a unique page within the application.
@@ -56,7 +56,7 @@
      * Inform page that it is being detached from the current request. This occurs just before the
      * page is returned to the page pool.
      * 
-     * @see ComponentLifecycle#containingPageDidDetach()
+     * @see org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidDetach()
      */
     void detached();
 
@@ -71,17 +71,15 @@
     /**
      * Inform the page that it is now completely loaded.
      * 
-     * @see ComponentLifecycle#containingPageDidLoad()
+     * @see org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidLoad()
      */
 
     void loaded();
 
     /**
-     * Adds a component, identified via the {@link org.apache.tapestry.runtime.ComponentLifecycle}
-     * interface it implements, to the page. The component will receive various lifecycle
-     * notifications.
+     * Adds a listener that is notified of large scale page events.
      */
-    void addComponentLifecycle(ComponentLifecycle component);
+    void addLifecycleListener(PageLifecycleListener listener);
 
     /** Returns the log of the root element. */
     Log getLog();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Sat Sep  2 10:03:34 2006
@@ -18,7 +18,7 @@
 import java.util.Locale;
 
 import org.apache.commons.logging.Log;
-import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.apache.tapestry.runtime.PageLifecycleListener;
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
 
@@ -31,7 +31,7 @@
 
     private ComponentPageElement _rootElement;
 
-    private final List<ComponentLifecycle> _components = newList();
+    private final List<PageLifecycleListener> _listeners = newList();
 
     public PageImpl(String name, Locale locale)
     {
@@ -59,26 +59,27 @@
         return _rootElement;
     }
 
-    public void addComponentLifecycle(ComponentLifecycle component)
+    public void addLifecycleListener(PageLifecycleListener listener)
     {
-        _components.add(component);
+        _listeners.add(listener);
     }
 
     public void detached()
     {
-        for (ComponentLifecycle c : _components)
-            c.containingPageDidDetach();
+        for (PageLifecycleListener listener : _listeners)
+            listener.containingPageDidDetach();
     }
 
     public void loaded()
     {
-        for (ComponentLifecycle c : _components)
-            c.containingPageDidLoad();
+        for (PageLifecycleListener listener : _listeners)
+            listener.containingPageDidLoad();
     }
 
     public void attached()
     {
-        // TODO: Define methods on ComponentLifecycle to invoke
+        for (PageLifecycleListener listener : _listeners)
+            listener.containingPageDidAttach();
     }
 
     public Log getLog()

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ExceptionTracker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ExceptionTracker.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ExceptionTracker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ExceptionTracker.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,36 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+/**
+ * Used by {@link org.apache.tapestry.ioc.services.LoggingDecorator} to track which exceptions have
+ * been logged during the current request (the ExceptionTracker is perthread). This keeps redundant
+ * information from appearing in the console output.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface ExceptionTracker
+{
+    /**
+     * Returns true if the indicated exception has already been logged (it is assumed that the
+     * exception will be logged if this method returns false). The exception is recorded for later
+     * checks.
+     * 
+     * @param exception
+     *            to check
+     * @return false if the exception has not been previously checked, true otherwise
+     */
+    boolean exceptionLogged(Throwable exception);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java Sat Sep  2 10:03:34 2006
@@ -18,6 +18,7 @@
 
 import org.apache.tapestry.internal.ioc.services.ChainBuilderImpl;
 import org.apache.tapestry.internal.ioc.services.DefaultImplementationBuilderImpl;
+import org.apache.tapestry.internal.ioc.services.ExceptionTrackerImpl;
 import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImpl;
 import org.apache.tapestry.internal.ioc.services.MasterObjectProvider;
 import org.apache.tapestry.internal.ioc.services.PerThreadServiceLifecycle;
@@ -30,6 +31,7 @@
 import org.apache.tapestry.ioc.ServiceLifecycle;
 import org.apache.tapestry.ioc.annotations.Id;
 import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.ioc.annotations.Lifecycle;
 
 /**
  * Defines the base set of services for the Tapestry IOC container.
@@ -51,9 +53,10 @@
      * The LoggingDecorator service is used to decorate a service implementation so that it logs
      * method entry and exit (at level debug).
      */
-    public LoggingDecorator buildLoggingDecorator()
+    public LoggingDecorator buildLoggingDecorator(@InjectService("ExceptionTracker")
+    ExceptionTracker exceptionTracker)
     {
-        return new LoggingDecoratorImpl(_classFactory);
+        return new LoggingDecoratorImpl(_classFactory, exceptionTracker);
     }
 
     /**
@@ -146,5 +149,12 @@
             MappedConfiguration<String, ObjectProvider> configuration)
     {
         configuration.add("service", new ServiceObjectProvider());
+    }
+
+    /** Used by the {@link org.apache.tapestry.ioc.services.LoggingDecorator} service. */
+    @Lifecycle("perthread")
+    public ExceptionTracker buildExceptionTracker()
+    {
+        return new ExceptionTrackerImpl();
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java Sat Sep  2 10:03:34 2006
@@ -26,24 +26,13 @@
  * 
  * @author Howard M. Lewis Ship
  */
-public interface ComponentLifecycle extends ResourceAware
+public interface ComponentLifecycle extends ResourceAware, PageLifecycleListener
 {
-    /**
-     * Invoked when the page finishes loading. This occurs once all components are loaded and all
-     * parameters have been set.
-     */
-    void containingPageDidLoad();
-
-    /**
-     * Invoked when the page is detached, allowing components a chance to clear out any temporary or
-     * client specific state.
-     */
-    void containingPageDidDetach();
 
     /**
      * Lifecycle method invoked at the end of the
      * {@link org.apache.tapestry.annotations.AfterRender} render phase. There is no annotation for
-     * this method, it is part of AfterRender, but is always invoked. It's specific use to to allow
+     * this method, it is part of AfterRender, but is always invoked. Its specific use is to allow
      * components to clean up cached parameter values.
      */
     void cleanupAfterRender();

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,37 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.runtime;
+
+public interface PageLifecycleListener
+{
+    /**
+     * Invoked when the page finishes loading. This occurs once all components are loaded and all
+     * parameters have been set.
+     */
+    void containingPageDidLoad();
+
+    /**
+     * Invoked when the page is detached, allowing components a chance to clear out any temporary or
+     * client specific state.
+     */
+    void containingPageDidDetach();
+
+    /**
+     * Invoked when a page is first attached to the current request, giving components a chance to
+     * initialize for the current request.
+     */
+    
+    void containingPageDidAttach();
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java Sat Sep  2 10:03:34 2006
@@ -204,14 +204,15 @@
     /**
      * Replaces all read-references to the specified field with invocations of the specified method
      * name. Replacements do not occur in methods added via
-     * {@link #addMethod(MethodSignature, String)}.
+     * {@link #addMethod(MethodSignature, String)} or {@link #extendMethod(MethodSignature, String)}.
      */
     void replaceReadAccess(String fieldName, String methodName);
 
     /**
      * Replaces all write accesses to the specified field with invocations of the specified method
      * name. The method should take a single parameter of the same type as the field. Replacements
-     * do not occur in methods added via {@link #addMethod(MethodSignature, String)}.
+     * do not occur in methods added via {@link #addMethod(MethodSignature, String)} or
+     * {@link #extendMethod(MethodSignature, String)}.
      */
     void replaceWriteAccess(String fieldName, String methodName);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java Sat Sep  2 10:03:34 2006
@@ -34,7 +34,10 @@
     private static final String[] LIFECYCLE_METHOD_PARAMETER_TYPES =
     { MarkupWriter.class.getName(), LifecycleEvent.class.getName() };
 
-    /** Signature for {@link org.apache.tapestry.runtime.ComponentLifecycle#containingPageDidLoad()}. */
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidLoad()}.
+     */
     public static final MethodSignature CONTAINING_PAGE_DID_LOAD_SIGNATURE = new MethodSignature(
             "containingPageDidLoad");
 
@@ -44,7 +47,7 @@
 
     /**
      * Signature for
-     * {@link org.apache.tapestry.runtime.ComponentLifecycle#containingPageDidDetach()}.
+     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidDetach()}.
      */
     public static final MethodSignature CONTAINING_PAGE_DID_DETACH_SIGNATURE = new MethodSignature(
             "containingPageDidDetach");

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformUtils.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformUtils.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformUtils.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,97 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services;
+
+import java.util.Map;
+
+import org.apache.tapestry.internal.annotations.Utility;
+import org.apache.tapestry.util.CollectionFactory;
+
+/**
+ * Support code for generating code (used when transforming component classes).
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Utility
+public final class TransformUtils
+{
+    private static final Map<String, PrimitiveTypeInfo> _primitives = CollectionFactory.newMap();
+
+    static class PrimitiveTypeInfo
+    {
+        private String _wrapperType;
+
+        private String _defaultValue;
+
+        public PrimitiveTypeInfo(String wrapperType, String defaultValue)
+        {
+            _wrapperType = wrapperType;
+            _defaultValue = defaultValue;
+        }
+
+        public String getDefaultValue()
+        {
+            return _defaultValue;
+        }
+
+        public String getWrapperType()
+        {
+            return _wrapperType;
+        }
+    }
+
+    static
+    {
+        add("boolean", "java.lang.Boolean", "false");
+        add("byte", "java.lang.Byte", "0");
+        add("char", "java.lang.Character", "0");
+        add("short", "java.lang.Short", "0");
+        add("int", "java.lang.Integer", "0");
+        add("long", "java.lang.Long", "0L");
+        add("float", "java.lang.Float", "0.0f");
+        add("double", "java.lang.Double", "0.0d");
+    }
+
+    private static void add(String primitiveType, String wrapperType, String defaultValue)
+    {
+        _primitives.put(primitiveType, new PrimitiveTypeInfo(wrapperType, defaultValue));
+    }
+
+    /**
+     * Returns the wrapper type for a given input type. For primitive types, returns the wrapper
+     * type. For other types, returns the type itself.
+     * 
+     * @param type
+     *            primitive type name, or fully qualified class name
+     * @return
+     */
+    public static String getWrapperType(String type)
+    {
+        PrimitiveTypeInfo info = _primitives.get(type);
+
+        return info == null ? type : info.getWrapperType();
+    }
+
+    /**
+     * Returns the default value for a type. This is the string "null" for most types, or a literal
+     * value for primtive types.
+     */
+    public static String getDefaultValue(String type)
+    {
+        PrimitiveTypeInfo info = _primitives.get(type);
+
+        return info == null ? "null" : info.getDefaultValue();
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImplTest.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ExceptionTrackerImplTest.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,40 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc.services;
+
+import org.apache.tapestry.ioc.services.ExceptionTracker;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class ExceptionTrackerImplTest extends Assert
+{
+    @Test
+    public void check_exception_tracking()
+    {
+        Throwable t1 = new RuntimeException();
+        Throwable t2 = new RuntimeException();
+
+        ExceptionTracker et = new ExceptionTrackerImpl();
+
+        for (int i = 0; i < 3; i++)
+        {
+            assertEquals(et.exceptionLogged(t1), i != 0);
+            assertEquals(et.exceptionLogged(t2), i != 0);
+        }
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImplTest.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImplTest.java Sat Sep  2 10:03:34 2006
@@ -65,9 +65,8 @@
 
         replay();
 
-        LoggingDecorator ld = new LoggingDecoratorImpl(new ClassFactoryImpl());
-        Runnable interceptor = ld
-                .build(Runnable.class, delegate, "foo.Bar", log);
+        LoggingDecorator ld = newLoggingDecorator();
+        Runnable interceptor = ld.build(Runnable.class, delegate, "foo.Bar", log);
 
         interceptor.run();
 
@@ -78,6 +77,11 @@
         verify();
     }
 
+    private LoggingDecoratorImpl newLoggingDecorator()
+    {
+        return new LoggingDecoratorImpl(new ClassFactoryImpl(), new ExceptionTrackerImpl());
+    }
+
     @Test
     public void method_throws_runtime_exception()
     {
@@ -91,13 +95,14 @@
         delegate.run();
         setThrowable(t);
 
+        train_isDebugEnabled(log, true);
+        
         log.debug("[ FAIL] run -- " + t.getClass().getName(), t);
 
         replay();
 
-        LoggingDecorator ld = new LoggingDecoratorImpl(new ClassFactoryImpl());
-        Runnable interceptor = ld
-                .build(Runnable.class, delegate, "foo.Bar", log);
+        LoggingDecorator ld = newLoggingDecorator();
+        Runnable interceptor = ld.build(Runnable.class, delegate, "foo.Bar", log);
 
         try
         {
@@ -125,16 +130,14 @@
         delegate.parse();
         setThrowable(t);
 
+        train_isDebugEnabled(log, true);
+        
         log.debug("[ FAIL] parse -- " + t.getClass().getName(), t);
 
         replay();
 
-        LoggingDecorator ld = new LoggingDecoratorImpl(new ClassFactoryImpl());
-        ExceptionService interceptor = ld.build(
-                ExceptionService.class,
-                delegate,
-                "foo.Bar",
-                log);
+        LoggingDecorator ld = newLoggingDecorator();
+        ExceptionService interceptor = ld.build(ExceptionService.class, delegate, "foo.Bar", log);
 
         try
         {
@@ -168,12 +171,8 @@
 
         replay();
 
-        LoggingDecorator ld = new LoggingDecoratorImpl(new ClassFactoryImpl());
-        UpcaseService interceptor = ld.build(
-                UpcaseService.class,
-                delegate,
-                "foo.Bar",
-                log);
+        LoggingDecorator ld = newLoggingDecorator();
+        UpcaseService interceptor = ld.build(UpcaseService.class, delegate, "foo.Bar", log);
 
         assertEquals(interceptor.upcase("barney"), "BARNEY");
 
@@ -199,12 +198,8 @@
 
         replay();
 
-        LoggingDecorator ld = new LoggingDecoratorImpl(new ClassFactoryImpl());
-        AdderService interceptor = ld.build(
-                AdderService.class,
-                delegate,
-                "foo.Bar",
-                log);
+        LoggingDecorator ld = newLoggingDecorator();
+        AdderService interceptor = ld.build(AdderService.class, delegate, "foo.Bar", log);
 
         assertEquals(interceptor.add(6, 13), 19);
 
@@ -231,12 +226,8 @@
 
         replay();
 
-        LoggingDecorator ld = new LoggingDecoratorImpl(new ClassFactoryImpl());
-        ToStringService interceptor = ld.build(
-                ToStringService.class,
-                delegate,
-                "foo.Bar",
-                log);
+        LoggingDecorator ld = newLoggingDecorator();
+        ToStringService interceptor = ld.build(ToStringService.class, delegate, "foo.Bar", log);
 
         assertEquals(interceptor.toString(), "FROM DELEGATE");
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java Sat Sep  2 10:03:34 2006
@@ -17,6 +17,7 @@
 import java.util.Arrays;
 
 import org.apache.commons.logging.Log;
+import org.apache.tapestry.ioc.services.ExceptionTracker;
 import org.apache.tapestry.test.BaseTestCase;
 import org.testng.annotations.Test;
 
@@ -28,26 +29,33 @@
     private void try_entry(String methodName, String expected, Object... arguments)
     {
         Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
 
         log.debug("[ENTER] " + expected);
 
         replay();
 
-        new ServiceLogger(log).entry(methodName, arguments);
+        new ServiceLogger(log, tracker).entry(methodName, arguments);
 
         verify();
 
     }
 
+    protected final ExceptionTracker newExceptionTracker()
+    {
+        return newMock(ExceptionTracker.class);
+    }
+
     private void try_exit(String methodName, String expected, Object result)
     {
         Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
 
         log.debug("[ EXIT] " + expected);
 
         replay();
 
-        new ServiceLogger(log).exit(methodName, result);
+        new ServiceLogger(log, tracker).exit(methodName, result);
 
         verify();
     }
@@ -76,43 +84,95 @@
     public void void_exit_test()
     {
         Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
 
         log.debug("[ EXIT] wilma");
 
         replay();
 
-        new ServiceLogger(log).voidExit("wilma");
+        new ServiceLogger(log, tracker).voidExit("wilma");
 
         verify();
     }
 
     @Test
-    public void fail_test()
+    public void fail_test_exception_not_already_logged()
     {
         Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
 
         RuntimeException t = new RuntimeException("Ouch!");
 
+        train_isDebugEnabled(log, true);
+
+        train_exceptionLogged(tracker, t, false);
+
         log.debug("[ FAIL] wilma -- " + t.getClass().getName(), t);
 
         replay();
 
-        new ServiceLogger(log).fail("wilma", t);
+        new ServiceLogger(log, tracker).fail("wilma", t);
+
+        verify();
+    }
+
+    @Test
+    public void fail_test_exception_previously_logged()
+    {
+        Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
+
+        RuntimeException t = new RuntimeException("Ouch!");
+
+        train_isDebugEnabled(log, true);
+
+        train_exceptionLogged(tracker, t, true);
+
+        log.debug("[ FAIL] wilma -- " + t.getClass().getName(), null);
+
+        replay();
+
+        new ServiceLogger(log, tracker).fail("wilma", t);
+
+        verify();
+    }
+
+    @Test
+    public void fail_debug_not_enabled()
+    {
+        Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
+
+        RuntimeException t = new RuntimeException("Ouch!");
+
+        train_isDebugEnabled(log, false);
+
+        replay();
+
+        new ServiceLogger(log, tracker).fail("wilma", t);
 
         verify();
     }
 
+    private void train_exceptionLogged(ExceptionTracker tracker, Throwable exception, boolean logged)
+    {
+        tracker.exceptionLogged(exception);
+        setReturnValue(logged);
+
+    }
+
     @Test
     public void debug_enabled()
     {
         Log log = newLog();
+        ExceptionTracker tracker = newExceptionTracker();
 
         train_isDebugEnabled(log, true);
         train_isDebugEnabled(log, false);
 
         replay();
 
-        ServiceLogger logger = new ServiceLogger(log);
+        ServiceLogger logger = new ServiceLogger(log, tracker);
 
         assertTrue(logger.isDebugEnabled());
         assertFalse(logger.isDebugEnabled());

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterComponent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterComponent.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterComponent.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterComponent.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,95 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+
+/**
+ * Used by {@link org.apache.tapestry.internal.services.ParameterWorkerTest}.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@ComponentClass
+@SuppressNullCheck
+public class ParameterComponent
+{
+    @Parameter
+    private String _object;
+
+    @Parameter(cache = false, name = "uncached")
+    private String _uncachedObject;
+
+    @Parameter(required = true)
+    private int _primitive;
+
+    @Parameter
+    private String _invariantObject;
+
+    @Parameter
+    private long _invariantPrimitive;
+
+    public String getObject()
+    {
+        return _object;
+    }
+
+    public void setObject(String object)
+    {
+        _object = object;
+    }
+
+    public int getPrimitive()
+    {
+        return _primitive;
+    }
+
+    public void setPrimitive(int primitive)
+    {
+        _primitive = primitive;
+    }
+
+    public String getUncachedObject()
+    {
+        return _uncachedObject;
+    }
+
+    public void setUncachedObject(String uncachedObject)
+    {
+        _uncachedObject = uncachedObject;
+    }
+
+    public String getInvariantObject()
+    {
+        return _invariantObject;
+    }
+
+    public void setInvariantObject(String invariantObject)
+    {
+        _invariantObject = invariantObject;
+    }
+
+    public long getInvariantPrimitive()
+    {
+        return _invariantPrimitive;
+    }
+
+    public void setInvariantPrimitive(long invariantPrimitive)
+    {
+        _invariantPrimitive = invariantPrimitive;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java Sat Sep  2 10:03:34 2006
@@ -14,15 +14,18 @@
 
 package org.apache.tapestry.internal.services;
 
-import java.lang.reflect.Modifier;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.Loader;
+import javassist.LoaderClassPath;
 
-import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.internal.InternalComponentResources;
+import org.apache.tapestry.internal.ioc.services.PropertyAccessImpl;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.services.PropertyAccess;
 import org.apache.tapestry.model.MutableComponentModel;
-import org.apache.tapestry.services.ClassTransformation;
-import org.apache.tapestry.services.MethodSignature;
-import org.apache.tapestry.services.TransformConstants;
-import org.testng.annotations.DataProvider;
+import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
 /**
@@ -30,207 +33,360 @@
  */
 public class ParameterWorkerTest extends InternalBaseTestCase
 {
-    private static String f(String format, Object... args)
+    private PropertyAccess _access = new PropertyAccessImpl();
+
+    @AfterClass
+    public void cleanup()
     {
-        return String.format(format, args);
+        _access = null;
     }
 
     @Test
-    public void standard_cacheable_parameter()
+    public void page_load_behavior() throws Exception
     {
-        runWorkerCached("_foo", "foo", "", "java.lang.String", "java.lang.String");
+        InternalComponentResources resources = newInternalComponentResources();
+
+        assertNotNull(setupForIntegrationTest(resources));
     }
 
     @Test
-    public void annotation_name_overrides_default_from_field_name()
+    public void invariant_object_retained_after_detach() throws Exception
     {
-        runWorkerCached("_foo", "zippy", "zippy", "java.util.Map", "java.util.Map");
+        InternalComponentResources resources = newInternalComponentResources();
+
+        ComponentLifecycle component = setupForIntegrationTest(resources);
+
+        // On first invocation, the resources are queried.
+
+        String value = "To be in Tapestry in the spring time ...";
+
+        train_readParameter(resources, "invariantObject", String.class, value);
+
+        replay();
+
+        assertSame(_access.get(component, "invariantObject"), value);
+
+        verify();
+
+        // No further training needed here.
+
+        replay();
+
+        // Still cached ...
+
+        assertSame(_access.get(component, "invariantObject"), value);
+
+        component.cleanupAfterRender();
+
+        // Still cached ...
+
+        assertSame(_access.get(component, "invariantObject"), value);
+
+        component.containingPageDidDetach();
+
+        // Still cached ...
+
+        assertSame(_access.get(component, "invariantObject"), value);
+
+        verify();
     }
 
-    @Test(dataProvider = "primitiveTypes")
-    public void primitive_types(String fieldType, String expectedWrapperType)
+    @Test
+    public void invariant_primitive_retained_after_detach() throws Exception
     {
-        runWorkerCached("_primitive", "primitive", "", fieldType, expectedWrapperType);
+        InternalComponentResources resources = newInternalComponentResources();
+
+        ComponentLifecycle component = setupForIntegrationTest(resources);
+
+        // On first invocation, the resources are queried.
+
+        long value = 123456;
+
+        train_readParameter(resources, "invariantPrimitive", long.class, value);
+
+        replay();
+
+        assertEquals(_access.get(component, "invariantPrimitive"), value);
+
+        verify();
+
+        // No further training needed here.
+
+        replay();
+
+        // Still cached ...
+
+        assertEquals(_access.get(component, "invariantPrimitive"), value);
+
+        component.cleanupAfterRender();
+
+        // Still cached ...
+
+        assertEquals(_access.get(component, "invariantPrimitive"), value);
+
+        verify();
     }
 
-    @DataProvider(name = "primitiveTypes")
-    public Object[][] primitiveTypes()
+    @Test
+    public void cached_object_read() throws Exception
     {
-        return new Object[][]
-        {
-        { "boolean", "java.lang.Boolean" },
-        { "short", "java.lang.Short" },
-        { "int", "java.lang.Integer" },
-        { "byte", "java.lang.Byte" },
-        { "char", "java.lang.Character" },
-        { "long", "java.lang.Long" },
-        { "float", "java.lang.Float" },
-        { "double", "java.lang.Double" } };
+        InternalComponentResources resources = newInternalComponentResources();
+
+        ComponentLifecycle component = setupForIntegrationTest(resources);
+
+        train_readParameter(resources, "object", String.class, "first");
+        train_isRendering(resources, false);
+
+        replay();
+
+        assertEquals(_access.get(component, "object"), "first");
+
+        verify();
+
+        // Keeps re-reading the parameter when not rendering.
+
+        train_readParameter(resources, "object", String.class, "second");
+        train_isRendering(resources, false);
+
+        replay();
+
+        assertEquals(_access.get(component, "object"), "second");
+
+        verify();
+
+        // Now, when rendering is active, the value is cached
+
+        train_readParameter(resources, "object", String.class, "third");
+        train_isRendering(resources, true);
+
+        replay();
+
+        assertEquals(_access.get(component, "object"), "third");
+
+        // Does not cause readParameter() to be invoked:
+
+        assertEquals(_access.get(component, "object"), "third");
+
+        verify();
+
+        train_readParameter(resources, "object", String.class, "fourth");
+        train_isRendering(resources, false);
+
+        replay();
+
+        component.cleanupAfterRender();
+
+        assertEquals(_access.get(component, "object"), "fourth");
+
+        verify();
     }
 
-    private void runWorkerCached(String fieldName, String parameterName, String annotatedName,
-            String fieldType, String wrapperType)
+    @Test
+    public void cached_object_write() throws Exception
     {
-        ClassTransformation transformation = newClassTransformation();
-        MutableComponentModel model = newMutableComponentModel();
-        Parameter parameter = newMock(Parameter.class);
+        InternalComponentResources resources = newInternalComponentResources();
 
-        train_findFieldsWithAnnotation(transformation, Parameter.class, fieldName);
-        train_getFieldAnnotation(transformation, fieldName, Parameter.class, parameter);
-        train_name(parameter, annotatedName);
-        train_required(parameter, true);
-
-        model.addParameter(parameterName, true);
-
-        train_getFieldType(transformation, fieldName, fieldType);
-        train_cache(parameter, true);
-        train_addField(transformation, Modifier.PRIVATE, "boolean", fieldName + "_cached", "$cache");
-        train_getResourcesFieldName(transformation, "$res");
-
-        train_addField(
-                transformation,
-                Modifier.PRIVATE,
-                "boolean",
-                fieldName + "_invariant",
-                "$invariant");
-
-        // codeEq() lets us care less about whitespace
-
-        train_extendMethod(
-                transformation,
-                TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE,
-                String.format("$invariant = $res.isInvariant(\"%s\");", parameterName));
-
-        train_extendMethod(
-                transformation,
-                TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
-                String.format("if (! $invariant) { %s = null; $cache = false; }", fieldName));
-
-        train_newMemberName(transformation, "_read_parameter_" + parameterName, "$read");
-
-        train_addMethod(transformation, new MethodSignature(Modifier.PRIVATE, fieldType, "$read",
-                null, null), f("{ if ($cache) return %s;", fieldName), f(
-                "%s result = ($r) ((%s) $res.readParameter(\"%s\", $type));",
-                fieldType,
-                wrapperType,
-                parameterName), f(
-                "if ($invariant || $res.isRendering()) { %s = result; $cache = true; }",
-                fieldName), "return result; }");
-
-        transformation.replaceReadAccess(fieldName, "$read");
-
-        train_newMemberName(transformation, "_update_parameter_" + parameterName, "$write");
-
-        train_addMethod(
-                transformation,
-                new MethodSignature(Modifier.PRIVATE, "void", "$write", new String[]
-                { fieldType, }, null),
-                f("{ $res.writeParameter(\"%s\", ($w)$1);", parameterName),
-                f("if ($res.isRendering()) { %s = $1; $cache = true; } }", fieldName));
+        ComponentLifecycle component = setupForIntegrationTest(resources);
 
-        transformation.replaceWriteAccess(fieldName, "$write");
+        resources.writeParameter("object", "first");
+        train_isRendering(resources, false);
 
-        transformation.claimField(fieldName, parameter);
+        train_readParameter(resources, "object", String.class, "second");
+        train_isRendering(resources, false);
 
         replay();
 
-        new ParameterWorker().transform(transformation, model);
+        _access.set(component, "object", "first");
+        assertEquals(_access.get(component, "object"), "second");
+
+        verify();
+
+        // Now try during rendering ...
+
+        resources.writeParameter("object", "third");
+        train_isRendering(resources, true);
+
+        replay();
+
+        _access.set(component, "object", "third");
+        assertEquals(_access.get(component, "object"), "third");
+
+        verify();
+
+        // And the cached value is lost after rendering is complete.
+
+        train_readParameter(resources, "object", String.class, "fourth");
+        train_isRendering(resources, false);
+
+        replay();
+
+        component.cleanupAfterRender();
+
+        assertEquals(_access.get(component, "object"), "fourth");
 
         verify();
     }
 
     @Test
-    public void non_cached_parameter()
+    public void cached_primitive_write() throws Exception
     {
-        runWorkerNonCached("_foo", "foo", "", "java.lang.String", "java.lang.String");
+        InternalComponentResources resources = newInternalComponentResources();
+
+        ComponentLifecycle component = setupForIntegrationTest(resources);
+
+        resources.writeParameter("primitive", 321);
+        train_isRendering(resources, false);
+
+        train_readParameter(resources, "primitive", int.class, 123);
+        train_isRendering(resources, false);
+
+        replay();
+
+        _access.set(component, "primitive", 321);
+        assertEquals(_access.get(component, "primitive"), 123);
+
+        verify();
+
+        // Now try during rendering ...
+
+        resources.writeParameter("primitive", 567);
+        train_isRendering(resources, true);
+
+        replay();
+
+        _access.set(component, "primitive", 567);
+        assertEquals(_access.get(component, "primitive"), 567);
+
+        verify();
+
+        // And the cached value is lost after rendering is complete.
+
+        train_readParameter(resources, "primitive", int.class, 890);
+        train_isRendering(resources, false);
+
+        replay();
+
+        component.cleanupAfterRender();
+
+        assertEquals(_access.get(component, "primitive"), 890);
+
+        verify();
     }
 
-    private void runWorkerNonCached(String fieldName, String parameterName, String annotatedName,
-            String fieldType, String wrapperType)
+    @Test
+    public void uncached_object_read() throws Exception
     {
-        ClassTransformation transformation = newClassTransformation();
-        MutableComponentModel model = newMutableComponentModel();
-        Parameter parameter = newMock(Parameter.class);
+        InternalComponentResources resources = newInternalComponentResources();
 
-        train_findFieldsWithAnnotation(transformation, Parameter.class, fieldName);
-        train_getFieldAnnotation(transformation, fieldName, Parameter.class, parameter);
-        train_name(parameter, annotatedName);
-        train_required(parameter, true);
+        ComponentLifecycle component = setupForIntegrationTest(resources);
 
-        model.addParameter(parameterName, true);
+        // Notice no check for isRendering() since that is irrelevant to uncached parameters.
+        // Also note difference between field name and parameter name, due to Parameter.name() being
+        // specified.
 
-        train_getFieldType(transformation, fieldName, fieldType);
+        train_readParameter(resources, "uncached", String.class, "first");
+        train_readParameter(resources, "uncached", String.class, "second");
 
-        train_cache(parameter, false); // First difference
+        replay();
 
-        train_addField(transformation, Modifier.PRIVATE, "boolean", fieldName + "_cached", "$cache");
-        train_getResourcesFieldName(transformation, "$res");
+        assertEquals(_access.get(component, "uncachedObject"), "first");
+        assertEquals(_access.get(component, "uncachedObject"), "second");
 
-        train_addField(
-                transformation,
-                Modifier.PRIVATE,
-                "boolean",
-                fieldName + "_invariant",
-                "$invariant");
+        verify();
+    }
 
-        // codeEq() lets us care less about whitespace
+    @Test
+    public void uncached_object_write() throws Exception
+    {
+        InternalComponentResources resources = newInternalComponentResources();
 
-        train_extendMethod(
-                transformation,
-                TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE,
-                String.format("$invariant = $res.isInvariant(\"%s\");", parameterName));
+        ComponentLifecycle component = setupForIntegrationTest(resources);
 
-        train_extendMethod(
-                transformation,
-                TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
-                String.format("if (! $invariant) { %s = null; $cache = false; }", fieldName));
+        // Notice no check for isRendering() since that is irrelevant to uncached parameters.
+        // Also note difference between field name and parameter name, due to Parameter.name() being
+        // specified.
 
-        train_newMemberName(transformation, "_read_parameter_" + parameterName, "$read");
+        resources.writeParameter("uncached", "first");
+        train_readParameter(resources, "uncached", String.class, "second");
 
-        // Second difference:
+        replay();
 
-        train_addMethod(
-                transformation,
-                new MethodSignature(Modifier.PRIVATE, fieldType, "$read", null, null),
-                f("{ if ($cache) return %s;", fieldName),
-                f(
-                        "%s result = ($r) ((%s) $res.readParameter(\"%s\", $type));",
-                        fieldType,
-                        wrapperType,
-                        parameterName),
-                f("if ($invariant) { %s = result; $cache = true; }", fieldName),
-                "return result; }");
+        _access.set(component, "uncachedObject", "first");
+        assertEquals(_access.get(component, "uncachedObject"), "second");
 
-        transformation.replaceReadAccess(fieldName, "$read");
+        verify();
+    }
 
-        train_newMemberName(transformation, "_update_parameter_" + parameterName, "$write");
+    protected final void train_isRendering(InternalComponentResources resources, boolean rendering)
+    {
+        resources.isRendering();
+        setReturnValue(rendering);
+    }
 
-        // Last difference:
+    protected final <T> void train_readParameter(InternalComponentResources resources,
+            String parameterName, Class<T> expectedType, T value)
+    {
+        resources.readParameter(parameterName, expectedType);
+        setReturnValue(value);
+    }
 
-        train_addMethod(transformation, new MethodSignature(Modifier.PRIVATE, "void", "$write",
-                new String[]
-                { fieldType, }, null), f("{ $res.writeParameter(\"%s\", ($w)$1); }", parameterName));
+    private ComponentLifecycle setupForIntegrationTest(InternalComponentResources resources)
+            throws Exception
+    {
+        ClassPool pool = new ClassPool();
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        pool.appendClassPath(new LoaderClassPath(contextLoader));
+
+        Loader loader = new Loader(contextLoader, pool);
+
+        loader.delegateLoadingOf("org.apache.tapestry.");
 
-        transformation.replaceWriteAccess(fieldName, "$write");
+        CtClass ctClass = pool.get(ParameterComponent.class.getName());
+        InternalClassTransformation transformation = new InternalClassTransformationImpl(ctClass);
+
+        MutableComponentModel model = newMutableComponentModel();
 
-        transformation.claimField(fieldName, parameter);
+        model.addParameter("invariantObject", false);
+        model.addParameter("invariantPrimitive", false);
+        model.addParameter("object", false);
+        model.addParameter("primitive", true);
+        model.addParameter("uncached", false);
 
         replay();
 
         new ParameterWorker().transform(transformation, model);
 
         verify();
-    }
 
-    private final void train_required(Parameter parameter, boolean required)
-    {
-        parameter.required();
-        setReturnValue(required);
+        transformation.finish();
+
+        // System.out.println("Transformation: " + transformation);
+
+        Class transformedClass = pool.toClass(ctClass, loader);
+
+        Instantiator instantiator = transformation.createInstantiator(transformedClass);
+
+        train_isInvariant(resources, "invariantObject", true);
+        train_isInvariant(resources, "invariantPrimitive", true);
+        train_isInvariant(resources, "object", false);
+        train_isInvariant(resources, "primitive", false);
+        train_isInvariant(resources, "uncached", false);
+
+        replay();
+
+        ComponentLifecycle component = instantiator.newInstance(resources);
+
+        component.containingPageDidLoad();
+
+        verify();
+
+        return component;
     }
 
-    private void train_cache(Parameter parameter, boolean cache)
+    protected final void train_isInvariant(InternalComponentResources resources,
+            String parameterName, boolean invariant)
     {
-        parameter.cache();
-        setReturnValue(cache);
+        resources.isInvariant(parameterName);
+        setReturnValue(invariant);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java?rev=439617&r1=439616&r2=439617&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java Sat Sep  2 10:03:34 2006
@@ -17,7 +17,7 @@
 import java.util.Locale;
 
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
-import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.apache.tapestry.runtime.PageLifecycleListener;
 import org.testng.annotations.Test;
 
 /**
@@ -52,18 +52,18 @@
     @Test
     public void detach_notification()
     {
-        ComponentLifecycle cl1 = newComponentLifecycle();
-        ComponentLifecycle cl2 = newComponentLifecycle();
+        PageLifecycleListener listener1 = newPageLifecycle();
+        PageLifecycleListener listener2 = newPageLifecycle();
 
-        cl1.containingPageDidDetach();
-        cl2.containingPageDidDetach();
+        listener1.containingPageDidDetach();
+        listener2.containingPageDidDetach();
 
         replay();
 
         Page page = new PageImpl(PAGE_NAME, _locale);
 
-        page.addComponentLifecycle(cl1);
-        page.addComponentLifecycle(cl2);
+        page.addLifecycleListener(listener1);
+        page.addLifecycleListener(listener2);
 
         page.detached();
 
@@ -71,20 +71,47 @@
     }
 
     @Test
+    public void attach_notification()
+    {
+
+        PageLifecycleListener listener1 = newPageLifecycle();
+        PageLifecycleListener listener2 = newPageLifecycle();
+
+        listener1.containingPageDidAttach();
+        listener2.containingPageDidAttach();
+
+        replay();
+
+        Page page = new PageImpl(PAGE_NAME, _locale);
+
+        page.addLifecycleListener(listener1);
+        page.addLifecycleListener(listener2);
+
+        page.attached();
+
+        verify();
+    }
+
+    private PageLifecycleListener newPageLifecycle()
+    {
+        return newMock(PageLifecycleListener.class);
+    }
+
+    @Test
     public void load_notification()
     {
-        ComponentLifecycle cl1 = newComponentLifecycle();
-        ComponentLifecycle cl2 = newComponentLifecycle();
+        PageLifecycleListener listener1 = newPageLifecycle();
+        PageLifecycleListener listener2 = newPageLifecycle();
 
-        cl1.containingPageDidLoad();
-        cl2.containingPageDidLoad();
+        listener1.containingPageDidLoad();
+        listener2.containingPageDidLoad();
 
         replay();
 
         Page page = new PageImpl(PAGE_NAME, _locale);
 
-        page.addComponentLifecycle(cl1);
-        page.addComponentLifecycle(cl2);
+        page.addLifecycleListener(listener1);
+        page.addLifecycleListener(listener2);
 
         page.loaded();
 

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/TransformUtilsTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/TransformUtilsTest.java?rev=439617&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/TransformUtilsTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/TransformUtilsTest.java Sat Sep  2 10:03:34 2006
@@ -0,0 +1,41 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import static org.apache.tapestry.services.TransformUtils.getDefaultValue;
+import static org.apache.tapestry.services.TransformUtils.getWrapperType;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class TransformUtilsTest extends Assert
+{
+    @Test
+    public void wrapper_type()
+    {
+        assertEquals(getWrapperType("char"), "java.lang.Character");
+        assertEquals(getWrapperType("java.util.Map"), "java.util.Map");
+    }
+
+    @Test
+    public void default_value()
+    {
+        assertEquals(getDefaultValue("long"), "0L");
+        assertEquals(getDefaultValue("java.util.Map"), "null");
+    }
+}