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 2007/03/05 18:36:19 UTC

svn commit: r514746 - in /tapestry/tapestry5: tapestry-core/trunk/src/main/java/org/apache/tapestry/ tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/ tapestry-io...

Author: hlship
Date: Mon Mar  5 09:36:17 2007
New Revision: 514746

URL: http://svn.apache.org/viewvc?view=rev&rev=514746
Log:
TAPESTRY-1291: Tighten up some synchronized collection access and add additional tests and checks for threading errors.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/LaunchCrusherTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryFilter.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/OrIdMatcher.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/CtClassSource.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/test/TestBase.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/CaseInsensitiveMapTest.java
    tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/AbstractIntegrationTestSuite.java
    tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/ErrorReportingCommandProcessor.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryFilter.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryFilter.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryFilter.java Mon Mar  5 09:36:17 2007
@@ -58,7 +58,9 @@
      * defined by the <code>tapestry.app-package</code> context init parameter and the application
      * name is the capitalization of the filter name (as specified in web.xml).
      */
-    public final void init(FilterConfig filterConfig) throws ServletException
+    // Why synchronized?  To tell the JVM to flush out any changes (such as the creation of the Registry) that
+    // occur within this method.  It's just that runtime behavior has gotten strange.
+    public synchronized final void init(FilterConfig filterConfig) throws ServletException
     {
         _config = filterConfig;
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java Mon Mar  5 09:36:17 2007
@@ -222,7 +222,7 @@
      *            mapping from logical name to class name
      * @return the located class name or null
      */
-    private String locate(String logicalName, Map<String, String> logicalNameToClassName)
+    private synchronized String locate(String logicalName, Map<String, String> logicalNameToClassName)
     {
         rebuild();
 
@@ -237,7 +237,7 @@
         return result;
     }
 
-    public String resolvePageClassNameToPageName(String pageClassName)
+    public synchronized String resolvePageClassNameToPageName(String pageClassName)
     {
         rebuild();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Mon Mar  5 09:36:17 2007
@@ -314,13 +314,13 @@
     @Contribute("tapestry.ioc.FactoryDefaults")
     public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
     {
-        // Remember this is request-to-request time, presumably it'll take  the developer more than
+        // Remember this is request-to-request time, presumably it'll take the developer more than
         // one second to make a change, save it, and switch back to the browser.
-        
+
         configuration.add("tapestry.file-check-interval", "1000"); // 1 second
         configuration.add("tapestry.supported-locales", "en");
         configuration.add("tapestry.default-cookie-max-age", "604800"); // One week
-              
+
         configuration.add("tapestry.start-page-name", "start");
 
         // This is designed to make it easy to keep syncrhonized with script.aculo.ous. As we

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Mon Mar  5 09:36:17 2007
@@ -24,11 +24,11 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.codec.net.URLCodec;
 import org.apache.tapestry.ComponentEventHandler;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.TapestryConstants;
 import org.apache.tapestry.internal.InternalConstants;
+import org.apache.tapestry.internal.TapestryInternalUtils;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.ioc.services.TypeCoercer;
@@ -54,9 +54,6 @@
 
     private final StrategyRegistry<PassivateContextHandler> _registry;
 
-    // URLCodec is thread-safe and sharable.
-    private final URLCodec _urlCodec = new URLCodec();
-
     private interface PassivateContextHandler<T>
     {
         void handle(T result, List context);
@@ -156,19 +153,12 @@
 
         StringBuilder builder = new StringBuilder();
 
-        try
+        for (int i = 0; i < activationContext.length; i++)
         {
-            for (int i = 0; i < activationContext.length; i++)
-            {
-                if (i > 0)
-                    builder.append("/");
+            if (i > 0)
+                builder.append("/");
 
-                builder.append(_urlCodec.encode(activationContext[i]));
-            }
-        }
-        catch (Exception ex)
-        {
-            throw new RuntimeException(ex);
+            builder.append(TapestryInternalUtils.urlEncode(activationContext[i]));
         }
 
         link.addParameter(InternalConstants.PAGE_CONTEXT_NAME, builder.toString());

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Mon Mar  5 09:36:17 2007
@@ -31,7 +31,7 @@
  * my system, Skype is listening on localhost:80.
  */
 @Test(timeOut = 50000, sequential = true, groups =
-{ "integration" })
+{ "integration" }, enabled=true)
 public class IntegrationTests extends AbstractIntegrationTestSuite
 {
     @Test

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/LaunchCrusherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/LaunchCrusherTest.java?view=auto&rev=514746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/LaunchCrusherTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/LaunchCrusherTest.java Mon Mar  5 09:36:17 2007
@@ -0,0 +1,171 @@
+package org.apache.tapestry.integration;
+
+import static java.lang.String.format;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+
+import org.apache.tapestry.ioc.internal.util.InternalUtils;
+import org.apache.tapestry.test.JettyRunner;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Tapestry seems least stable at initial startup (all those proxies and generated code). So we're
+ * giving it a crushing just as it starts up.
+ */
+public class LaunchCrusherTest extends Assert
+{
+    // Jetty's default thread pool max size is 256.  If you push this number too high, you get connection reset by peer
+    // errors (I think Jetty reject requests when there's no available thread, after a short timeout).
+    private static final int THREAD_COUNT = 50;
+
+    /** The port on which the internal copy of Jetty is executed. */
+    public static final int JETTY_PORT = 9999;
+
+    private JettyRunner _jetty;
+
+    private final CyclicBarrier _barrier = new CyclicBarrier(THREAD_COUNT + 1);
+
+    class Worker implements Runnable
+    {
+        private String _name;
+
+        private String _content;
+
+        public synchronized String getContent()
+        {
+            return _content;
+        }
+
+        public void run()
+        {
+            _name = Thread.currentThread().getName();
+
+            System.out.printf("[%s] waiting ...\n", _name);
+
+            try
+            {
+                _barrier.await();
+
+                URL url = new URL(format("http://localhost:%d/", JETTY_PORT));
+
+                readContent(url);
+
+                _barrier.await();
+            }
+            catch (Exception ex)
+            {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        private void readContent(URL url)
+        {
+            System.out.printf("[%s] reading ...\n", _name);
+
+            try
+            {
+                InputStream is = url.openStream();
+                InputStreamReader reader = new InputStreamReader(new BufferedInputStream(is));
+
+                StringBuilder builder = new StringBuilder();
+
+                char[] buffer = new char[10000];
+
+                while (true)
+                {
+                    int length = reader.read(buffer);
+
+                    if (length < 0)
+                        break;
+
+                    builder.append(buffer, 0, length);
+                }
+
+                reader.close();
+
+                synchronized (this)
+                {
+                    _content = builder.toString();
+                }
+
+                System.out.printf("[%s] done.\n", _name);
+            }
+            catch (Exception ex)
+            {
+                System.out.printf("[%s] fail: %s.\n", _name, ex.toString());
+
+                synchronized (this)
+                {
+                    _content = "[" + ex.toString() + "]";
+                }
+            }
+        }
+    }
+
+    @BeforeClass
+    public void setup()
+    {
+        _jetty = new JettyRunner("/", JETTY_PORT, "src/test/app1");
+    }
+
+    @AfterClass
+    public void cleanup()
+    {
+        _jetty.stop();
+    }
+
+    @Test
+    public void crushing_number_of_threads_at_startup() throws Exception
+    {
+        Worker[] workers = new Worker[THREAD_COUNT];
+
+        for (int i = 0; i < THREAD_COUNT; i++)
+        {
+            workers[i] = new Worker();
+
+            new Thread(workers[i]).start();
+        }
+
+        _barrier.await();
+
+        System.out.printf("%d threads started\n", THREAD_COUNT);
+
+        _barrier.await();
+
+        String expected = workers[0].getContent();
+        String failureContent = null;
+        List<Integer> failures = newList();
+
+        System.out.printf("*****\n%s\n*****\n", expected);
+
+        for (int i = 1; i < THREAD_COUNT; i++)
+        {
+            String actual = workers[i].getContent();
+
+            if (!actual.equals(expected))
+            {
+                failures.add(i);
+
+                if (failureContent == null)
+                    failureContent = actual;
+
+            }
+        }
+
+        if (failureContent != null)
+        {
+            System.err.println("Failures in thread(s): " + InternalUtils.join(failures));
+
+            assertEquals(failureContent, expected);
+        }
+    }
+}

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java Mon Mar  5 09:36:17 2007
@@ -46,7 +46,7 @@
     /**
      * Service implementation overrides, keyed on service id. Service implementations are most
      * useful when perfroming integration tests on services. As one service can bring in another, we
-     * have to stop at a certain "bounary" services by provide stub/ mock objects as their
+     * have to stop at a certain "boundary" services by provide stub/ mock objects as their
      * implementations.
      */
     private final Map<String, Object> _serviceOverrides = newCaseInsensitiveMap();

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java Mon Mar  5 09:36:17 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.ioc.internal;
-
+package org.apache.tapestry.ioc.internal;
+
 import static org.apache.tapestry.ioc.internal.util.InternalUtils.asString;
 import static org.apache.tapestry.ioc.services.ClassFabUtils.getJavaClassName;
 
@@ -27,265 +27,265 @@
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.def.ContributionDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
+import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.internal.util.MessagesImpl;
-
-public final class IOCMessages
-{
-    private static final Messages MESSAGES = MessagesImpl.forClass(IOCMessages.class);
-
-    private IOCMessages()
-    {
-    }
-
-    static String buildMethodConflict(Method conflict, String existing)
-    {
-        return MESSAGES.format("build-method-conflict", asString(conflict), existing);
-    }
-
-    static String buildMethodWrongReturnType(Method method)
-    {
-        return MESSAGES.format("build-method-wrong-return-type", asString(method), method
-                .getReturnType().getCanonicalName());
-    }
-
-    static String decoratorMethodWrongReturnType(Method method)
-    {
-        return MESSAGES.format("decorator-method-wrong-return-type", asString(method), method
-                .getReturnType().getCanonicalName());
-    }
-
-    static String noSuchModule(String moduleId)
-    {
-        return MESSAGES.format("no-such-module", moduleId);
-    }
-
-    static String missingService(String serviceId)
-    {
-        return MESSAGES.format("missing-service", serviceId);
-    }
-
-    public static String builderLocked()
-    {
-        return MESSAGES.get("builder-locked");
-    }
-
-    public static String moduleIdConflict(String id)
-    {
-        return MESSAGES.format("module-id-conflict", id);
-    }
-
-    static String serviceWrongInterface(String serviceId, Class actualInterface,
-            Class requestedInterface)
-    {
-        return MESSAGES.format(
-                "service-wrong-interface",
-                serviceId,
-                actualInterface.getName(),
-                requestedInterface.getName());
-    }
-
-    static String instantiateBuilderError(Class builderClass, String moduleId, Throwable cause)
-    {
-        return MESSAGES
-                .format("instantiate-builder-error", builderClass.getName(), moduleId, cause);
-    }
-
-    static String builderMethodError(Method method, String serviceId, Throwable cause)
-    {
-        return MESSAGES.format("builder-method-error", asString(method), serviceId, cause);
-    }
-
-    static String decoratorMethodError(Method method, String serviceId, Throwable cause)
-    {
-        return MESSAGES.format("decorator-method-error", asString(method), serviceId, cause);
-    }
-
-    static String builderMethodReturnedNull(Method method, String serviceId)
-    {
-        return MESSAGES.format("builder-method-returned-null", asString(method), serviceId);
-    }
-
-    static String serviceIsPrivate(String serviceId)
-    {
-        return MESSAGES.format("service-is-private", serviceId);
-    }
-
-    static String noServiceMatchesType(Class serviceInterface)
-    {
-        return MESSAGES.format("no-service-matches-type", serviceInterface.getName());
-    }
-
-    static String manyServiceMatches(Class serviceInterface, List<String> ids)
-    {
-        StringBuilder buffer = new StringBuilder();
-
-        for (int i = 0; i < ids.size(); i++)
-        {
-            if (i > 0)
-                buffer.append(", ");
-
-            buffer.append(ids.get(i));
-        }
-
-        return MESSAGES.format(
-                "many-service-matches",
-                serviceInterface.getName(),
-                ids.size(),
-                buffer.toString());
-    }
-
-    static String unknownLifecycle(String name)
-    {
-        return MESSAGES.format("unknown-lifecycle", name);
-    }
-
-    static String decoratorMethodNeedsDelegateParameter(Method method)
-    {
-        return MESSAGES.format("decorator-method-needs-delegate-parameter", asString(method));
-    }
-
-    static String decoratorReturnedWrongType(Method method, String serviceId, Object returned,
-            Class serviceInterface)
-    {
-        return MESSAGES.format(
-                "decorator-returned-wrong-type",
-                asString(method),
-                serviceId,
-                returned,
-                serviceInterface.getName());
-    }
-
-    static String creatingService(String serviceId)
-    {
-        return MESSAGES.format("creating-service", serviceId);
-    }
-
-    static String invokingMethod(Method method)
-    {
-        return MESSAGES.format("invoking-method", asString(method));
-    }
-
-    static String invokingMethod(ContributionDef def)
-    {
-        // The toString() of a contribution def is the name of the method.
-        return MESSAGES.format("invoking-method", def);
-    }
-
-    static String recursiveServiceBuild(ServiceDef def)
-    {
-        return MESSAGES.format("recursive-service-build", def.getServiceId(), def.toString());
-    }
-
-    static String contributionWrongReturnType(Method method)
-    {
-        return MESSAGES.format(
-                "contribution-wrong-return-type",
-                asString(method),
-                getJavaClassName(method.getReturnType()));
-    }
-
-    static String tooManyContributionParameters(Method method)
-    {
-        return MESSAGES.format("too-many-contribution-parameters", asString(method));
-    }
-
-    static String noContributionParameter(Method method)
-    {
-        return MESSAGES.format("no-contribution-parameter", asString(method));
-    }
-
-    static String contributionMethodError(Method method, Throwable cause)
-    {
-        return MESSAGES.format("contribution-method-error", asString(method), cause);
-    }
-
-    static String contributionWasNull(String serviceId, ContributionDef def)
-    {
-        return MESSAGES.format("contribution-was-null", serviceId, def);
-    }
-
-    static String contributionKeyWasNull(String serviceId, ContributionDef def)
-    {
-        return MESSAGES.format("contribution-key-was-null", serviceId, def);
-    }
-
-    static String contributionWrongValueType(String serviceId, ContributionDef def,
-            Class actualClass, Class expectedClass)
-    {
-        return MESSAGES.format("contribution-wrong-value-type", serviceId, def, actualClass
-                .getName(), expectedClass.getName());
-    }
-
-    static String contributionWrongKeyType(String serviceId, ContributionDef def,
-            Class actualClass, Class expectedClass)
-    {
-        return MESSAGES.format(
-                "contribution-wrong-key-type",
-                serviceId,
-                def,
-                actualClass.getName(),
-                expectedClass.getName());
-    }
-
-    static String tooManyConfigurationParameters(Method method)
-    {
-        return MESSAGES.format("too-many-configuration-parameters", asString(method));
-    }
-
-    static String genericTypeNotSupported(Type type)
-    {
-        return MESSAGES.format("generic-type-not-supported", type);
-    }
-
-    static String contributionDuplicateKey(String serviceId, ContributionDef contributionDef,
-            ContributionDef existingDef)
-    {
-        return MESSAGES.format(
-                "contribution-duplicate-key",
-                serviceId,
-                contributionDef,
-                existingDef);
-    }
-
-    static String errorBuildingService(String serviceId, ServiceDef serviceDef, Throwable cause)
-    {
-        return MESSAGES.format("error-building-service", serviceId, serviceDef, cause);
-    }
-
-    static String noPublicConstructors(String moduleId, Class moduleBuilderClass)
-    {
-        return MESSAGES.format("no-public-constructors", moduleId, moduleBuilderClass.getName());
-    }
-
-    static String tooManyPublicConstructors(String moduleId, Class moduleBuilderClass,
-            Constructor constructor)
-    {
-        return MESSAGES.format("too-many-public-constructors", moduleId, moduleBuilderClass
-                .getName(), constructor);
-    }
-
-    static String recursiveModuleConstructor(String moduleId, Class builderClass,
-            Constructor constructor)
-    {
-        return MESSAGES.format(
-                "recursive-module-constructor",
-                moduleId,
-                builderClass.getName(),
-                constructor);
-    }
-
-    static String registryShutdown(String serviceId)
-    {
-        return MESSAGES.format("registry-shutdown", serviceId);
-    }
-
-    static String constructedConfiguration(Collection result)
-    {
-        return MESSAGES.format("constructed-configuration", result);
-    }
-
-    static String constructedConfiguration(Map result)
-    {
-        return MESSAGES.format("constructed-configuration", result);
-    }
-}
+
+public final class IOCMessages
+{
+    private static final Messages MESSAGES = MessagesImpl.forClass(IOCMessages.class);
+
+    private IOCMessages()
+    {
+    }
+
+    static String buildMethodConflict(Method conflict, String existing)
+    {
+        return MESSAGES.format("build-method-conflict", asString(conflict), existing);
+    }
+
+    static String buildMethodWrongReturnType(Method method)
+    {
+        return MESSAGES.format("build-method-wrong-return-type", asString(method), method
+                .getReturnType().getCanonicalName());
+    }
+
+    static String decoratorMethodWrongReturnType(Method method)
+    {
+        return MESSAGES.format("decorator-method-wrong-return-type", asString(method), method
+                .getReturnType().getCanonicalName());
+    }
+
+    static String noSuchModule(String moduleId, Collection<String> moduleIds)
+    {
+        return MESSAGES.format("no-such-module", moduleId, InternalUtils.joinSorted(moduleIds));
+    }
+
+    static String missingService(String serviceId)
+    {
+        return MESSAGES.format("missing-service", serviceId);
+    }
+
+    public static String builderLocked()
+    {
+        return MESSAGES.get("builder-locked");
+    }
+
+    public static String moduleIdConflict(String id)
+    {
+        return MESSAGES.format("module-id-conflict", id);
+    }
+
+    static String serviceWrongInterface(String serviceId, Class actualInterface,
+            Class requestedInterface)
+    {
+        return MESSAGES.format(
+                "service-wrong-interface",
+                serviceId,
+                actualInterface.getName(),
+                requestedInterface.getName());
+    }
+
+    static String instantiateBuilderError(Class builderClass, String moduleId, Throwable cause)
+    {
+        return MESSAGES
+                .format("instantiate-builder-error", builderClass.getName(), moduleId, cause);
+    }
+
+    static String builderMethodError(Method method, String serviceId, Throwable cause)
+    {
+        return MESSAGES.format("builder-method-error", asString(method), serviceId, cause);
+    }
+
+    static String decoratorMethodError(Method method, String serviceId, Throwable cause)
+    {
+        return MESSAGES.format("decorator-method-error", asString(method), serviceId, cause);
+    }
+
+    static String builderMethodReturnedNull(Method method, String serviceId)
+    {
+        return MESSAGES.format("builder-method-returned-null", asString(method), serviceId);
+    }
+
+    static String serviceIsPrivate(String serviceId)
+    {
+        return MESSAGES.format("service-is-private", serviceId);
+    }
+
+    static String noServiceMatchesType(Class serviceInterface)
+    {
+        return MESSAGES.format("no-service-matches-type", serviceInterface.getName());
+    }
+
+    static String manyServiceMatches(Class serviceInterface, List<String> ids)
+    {
+        StringBuilder buffer = new StringBuilder();
+
+        for (int i = 0; i < ids.size(); i++)
+        {
+            if (i > 0) buffer.append(", ");
+
+            buffer.append(ids.get(i));
+        }
+
+        return MESSAGES.format(
+                "many-service-matches",
+                serviceInterface.getName(),
+                ids.size(),
+                buffer.toString());
+    }
+
+    static String unknownLifecycle(String name)
+    {
+        return MESSAGES.format("unknown-lifecycle", name);
+    }
+
+    static String decoratorMethodNeedsDelegateParameter(Method method)
+    {
+        return MESSAGES.format("decorator-method-needs-delegate-parameter", asString(method));
+    }
+
+    static String decoratorReturnedWrongType(Method method, String serviceId, Object returned,
+            Class serviceInterface)
+    {
+        return MESSAGES.format(
+                "decorator-returned-wrong-type",
+                asString(method),
+                serviceId,
+                returned,
+                serviceInterface.getName());
+    }
+
+    static String creatingService(String serviceId)
+    {
+        return MESSAGES.format("creating-service", serviceId);
+    }
+
+    static String invokingMethod(Method method)
+    {
+        return MESSAGES.format("invoking-method", asString(method));
+    }
+
+    static String invokingMethod(ContributionDef def)
+    {
+        // The toString() of a contribution def is the name of the method.
+        return MESSAGES.format("invoking-method", def);
+    }
+
+    static String recursiveServiceBuild(ServiceDef def)
+    {
+        return MESSAGES.format("recursive-service-build", def.getServiceId(), def.toString());
+    }
+
+    static String contributionWrongReturnType(Method method)
+    {
+        return MESSAGES.format(
+                "contribution-wrong-return-type",
+                asString(method),
+                getJavaClassName(method.getReturnType()));
+    }
+
+    static String tooManyContributionParameters(Method method)
+    {
+        return MESSAGES.format("too-many-contribution-parameters", asString(method));
+    }
+
+    static String noContributionParameter(Method method)
+    {
+        return MESSAGES.format("no-contribution-parameter", asString(method));
+    }
+
+    static String contributionMethodError(Method method, Throwable cause)
+    {
+        return MESSAGES.format("contribution-method-error", asString(method), cause);
+    }
+
+    static String contributionWasNull(String serviceId, ContributionDef def)
+    {
+        return MESSAGES.format("contribution-was-null", serviceId, def);
+    }
+
+    static String contributionKeyWasNull(String serviceId, ContributionDef def)
+    {
+        return MESSAGES.format("contribution-key-was-null", serviceId, def);
+    }
+
+    static String contributionWrongValueType(String serviceId, ContributionDef def,
+            Class actualClass, Class expectedClass)
+    {
+        return MESSAGES.format("contribution-wrong-value-type", serviceId, def, actualClass
+                .getName(), expectedClass.getName());
+    }
+
+    static String contributionWrongKeyType(String serviceId, ContributionDef def,
+            Class actualClass, Class expectedClass)
+    {
+        return MESSAGES.format(
+                "contribution-wrong-key-type",
+                serviceId,
+                def,
+                actualClass.getName(),
+                expectedClass.getName());
+    }
+
+    static String tooManyConfigurationParameters(Method method)
+    {
+        return MESSAGES.format("too-many-configuration-parameters", asString(method));
+    }
+
+    static String genericTypeNotSupported(Type type)
+    {
+        return MESSAGES.format("generic-type-not-supported", type);
+    }
+
+    static String contributionDuplicateKey(String serviceId, ContributionDef contributionDef,
+            ContributionDef existingDef)
+    {
+        return MESSAGES.format(
+                "contribution-duplicate-key",
+                serviceId,
+                contributionDef,
+                existingDef);
+    }
+
+    static String errorBuildingService(String serviceId, ServiceDef serviceDef, Throwable cause)
+    {
+        return MESSAGES.format("error-building-service", serviceId, serviceDef, cause);
+    }
+
+    static String noPublicConstructors(String moduleId, Class moduleBuilderClass)
+    {
+        return MESSAGES.format("no-public-constructors", moduleId, moduleBuilderClass.getName());
+    }
+
+    static String tooManyPublicConstructors(String moduleId, Class moduleBuilderClass,
+            Constructor constructor)
+    {
+        return MESSAGES.format("too-many-public-constructors", moduleId, moduleBuilderClass
+                .getName(), constructor);
+    }
+
+    static String recursiveModuleConstructor(String moduleId, Class builderClass,
+            Constructor constructor)
+    {
+        return MESSAGES.format(
+                "recursive-module-constructor",
+                moduleId,
+                builderClass.getName(),
+                constructor);
+    }
+
+    static String registryShutdown(String serviceId)
+    {
+        return MESSAGES.format("registry-shutdown", serviceId);
+    }
+
+    static String constructedConfiguration(Collection result)
+    {
+        return MESSAGES.format("constructed-configuration", result);
+    }
+
+    static String constructedConfiguration(Map result)
+    {
+        return MESSAGES.format("constructed-configuration", result);
+    }
+}

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java Mon Mar  5 09:36:17 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -49,9 +49,6 @@
 import org.apache.tapestry.ioc.services.RegistryShutdownListener;
 import org.apache.tapestry.ioc.util.BodyBuilder;
 
-/**
- * 
- */
 public class ModuleImpl implements Module
 {
     private final InternalRegistry _registry;
@@ -64,8 +61,15 @@
 
     private final static String INTERNAL_MODULE_ID = "tapestry.ioc";
 
+    /**
+     * A single mutex, shared by all modules, that serializes creation of services across all
+     * threads. This is a bit draconian, but appears to be necessary. Fortunately, service creation
+     * is a very tiny part of any individual service's lifecycle.
+     */
+    private static final Object MUTEX = new Object();
+
     // Set to true when invoking the module constructor. Used to
-    // detect endless loops caused by irresponsible dependencies into
+    // detect endless loops caused by irresponsible dependencies in
     // the constructor.
 
     private boolean _insideConstructor;
@@ -154,27 +158,31 @@
         return result;
     }
 
-    // Why synchronized here? Two reasons. First, with some lifecycle models (or perhaps in some
-    // scenarios using interceptors), we may try to acquire the write lock a second time and the
-    // @Concurrent.Write annotation doesn't currently support that. Second, I'm concerned about
-    // multiple threads building services simultaneously, and getting into a thread deadlock. Of
-    // course, this isn't a solution for that ... we may need a global mutex to handle that specific
-    // case! Alternately, I've thought about creating a "service creation" thread at startup and
-    // queuing service creation requests to that thread, and blocking the local thread.
-
-    private synchronized Object findOrCreate(ServiceDef def)
+    /**
+     * Locates the service proxy for a particular service (from the service definition).
+     * <p>
+     * Access is synchronized via {@link #MUTEX}.
+     * 
+     * @param def
+     *            defines the service
+     * @return the service proxy
+     */
+    private Object findOrCreate(ServiceDef def)
     {
-        String key = def.getServiceId();
+        synchronized (MUTEX)
+        {
+            String key = def.getServiceId();
 
-        Object result = _services.get(key);
+            Object result = _services.get(key);
 
-        if (result == null)
-        {
-            result = create(def);
-            _services.put(key, result);
-        }
+            if (result == null)
+            {
+                result = create(def);
+                _services.put(key, result);
+            }
 
-        return result;
+            return result;
+        }
     }
 
     public void eagerLoadServices()
@@ -195,8 +203,8 @@
     }
 
     /**
-     * Creates the service and updates the cache of created services. Method is called from
-     * synchronized block.
+     * Creates the service and updates the cache of created services. Access is synchronized via
+     * {@link #MUTEX}.
      */
     private Object create(ServiceDef def)
     {
@@ -228,11 +236,7 @@
 
             creator = new OneShotServiceCreator(def, creator);
 
-            Object service = createProxy(resources, creator, def.isEagerLoad());
-
-            _services.put(serviceId, service);
-
-            return service;
+            return createProxy(resources, creator, def.isEagerLoad());
         }
         catch (Exception ex)
         {
@@ -240,13 +244,17 @@
         }
     }
 
-    public synchronized Object getModuleBuilder()
+    public Object getModuleBuilder()
     {
-        if (_moduleBuilder == null) _moduleBuilder = instantiateModuleBuilder();
+        synchronized (MUTEX)
+        {
+            if (_moduleBuilder == null) _moduleBuilder = instantiateModuleBuilder();
 
-        return _moduleBuilder;
+            return _moduleBuilder;
+        }
     }
 
+    /** Access synchronized by MUTEX. */
     private Object instantiateModuleBuilder()
     {
         Class builderClass = _moduleDef.getBuilderClass();

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/OrIdMatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/OrIdMatcher.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/OrIdMatcher.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/OrIdMatcher.java Mon Mar  5 09:36:17 2007
@@ -29,7 +29,7 @@
 
     public OrIdMatcher(Collection<IdMatcher> matchers)
     {
-        _matchers = matchers.toArray(new IdMatcher[0]);
+        _matchers = matchers.toArray(new IdMatcher[matchers.size()]);
     }
 
     public boolean matches(String id)

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java Mon Mar  5 09:36:17 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -192,7 +192,7 @@
         _registryShutdownHub.fireRegistryDidShutdown();
     }
 
-    /** Internal access, usualy from another module. */
+    /** Internal access, usually from another module. */
     public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
     {
         _lock.check();
@@ -268,7 +268,8 @@
 
         Module module = _modules.get(moduleId);
 
-        if (module == null) throw new RuntimeException(IOCMessages.noSuchModule(moduleId));
+        if (module == null)
+            throw new RuntimeException(IOCMessages.noSuchModule(moduleId, _modules.keySet()));
 
         return module;
     }
@@ -493,6 +494,11 @@
     public List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef)
     {
         _lock.check();
+
+        // This simply should never happen, yet we're seeing it. This falls into the
+        // category of "scary things that happen in multi-threaded applications".
+
+        assert serviceDef != null;
 
         Log log = getLog(serviceDef.getServiceId());
 

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java Mon Mar  5 09:36:17 2007
@@ -80,7 +80,7 @@
         _loader = loader;
     }
 
-    public ClassLoader getLoader()
+    public synchronized ClassLoader getLoader()
     {
         return _loader;
     }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/CtClassSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/CtClassSource.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/CtClassSource.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/CtClassSource.java Mon Mar  5 09:36:17 2007
@@ -40,7 +40,7 @@
     /**
      * Returns the number of classes (and interfaces) created by this source.
      */
-    public int getCreatedClassCount()
+    public synchronized int getCreatedClassCount()
     {
         return _createdClassCount;
     }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/test/TestBase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/test/TestBase.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/test/TestBase.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/test/TestBase.java Mon Mar  5 09:36:17 2007
@@ -40,7 +40,7 @@
  */
 public class TestBase extends Assert
 {
-    private class ThreadLocalControl extends ThreadLocal<IMocksControl>
+    private static class ThreadLocalControl extends ThreadLocal<IMocksControl>
     {
         @Override
         protected IMocksControl initialValue()

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java Mon Mar  5 09:36:17 2007
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry.ioc.util;
 
+import java.io.Serializable;
 import java.util.AbstractMap;
 import java.util.AbstractSet;
 import java.util.ConcurrentModificationException;
@@ -34,14 +35,18 @@
  * @param <V>
  *            the type of value stored
  */
-public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Map<String, V>
+public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Serializable
 {
+    private static final long serialVersionUID = 3362718337611953298L;
+
     private static final int NULL_HASH = Integer.MIN_VALUE;
 
     private static final int DEFAULT_SIZE = 20;
 
-    private static class CIMEntry<V> implements Map.Entry<String, V>
+    private static class CIMEntry<V> implements Map.Entry<String, V>, Serializable
     {
+        private static final long serialVersionUID = 6713986085221148350L;
+
         private String _key;
 
         private final int _hashCode;

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties Mon Mar  5 09:36:17 2007
@@ -22,7 +22,7 @@
 missing-service=Service '%s' does not exist.
 builder-locked=The Registry Builder has created the Registry, further operations are not allowed.
 module-id-conflict=Module '%s' has already been defined. The duplicate definition will be ignored.
-no-such-module=Module '%s' does not exist. Please ensure that the JAR file for the module is on the classpath.
+no-such-module=Module '%s' does not exist. Please ensure that the JAR file for the module is on the classpath. Available modules: %s.
 service-wrong-interface=Service '%s' implements interface %s, which is not compatible with the requested type %s.
 instantiate-builder-error=Unable to instantiate class %s as builder for module '%s': %s
 builder-method-error=Error invoking service builder method %s (for service '%s'): %s

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/CaseInsensitiveMapTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/CaseInsensitiveMapTest.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/CaseInsensitiveMapTest.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/CaseInsensitiveMapTest.java Mon Mar  5 09:36:17 2007
@@ -16,6 +16,10 @@
 
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.ConcurrentModificationException;
@@ -309,6 +313,30 @@
         Collections.sort(keys);
 
         assertEquals(keys, Arrays.asList("barney", "betty", "fred", "wilma"));
+    }
+
+    @Test
+    public void serialize_deserialize() throws Exception
+    {
+        Map<String, String> map = newCaseInsensitiveMap();
+
+        map.put("fred", "flintstone");
+        map.put("barney", "rubble");
+        map.put("wilma", "flinstone");
+        map.put("betty", "rubble");
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+        oos.writeObject(map);
+        oos.close();
+
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+
+        Map<String, String> copy = (Map<String, String>) ois.readObject();
+
+        assertEquals(copy, map);
     }
 
     @SuppressWarnings("unchecked")

Modified: tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/AbstractIntegrationTestSuite.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/AbstractIntegrationTestSuite.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/AbstractIntegrationTestSuite.java (original)
+++ tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/AbstractIntegrationTestSuite.java Mon Mar  5 09:36:17 2007
@@ -37,7 +37,8 @@
  * jumping all over your web application in an unpredictable order.
  * <p>
  * This class implements the {@link Selenium} interface, and delegates all those methods to the
- * {@link DefaultSelenium} instance it creates.
+ * {@link DefaultSelenium} instance it creates. It also extends the normal exception reporting for
+ * any failed command or query to produce a more detailed report to the main console.
  * 
  * @see JettyRunner
  */
@@ -413,6 +414,8 @@
   public void open(String url)
   {
     _selenium.open(url);
+
+    waitForPageToLoad(PAGE_LOAD_TIMEOUT);
   }
 
   public void refresh()
@@ -483,6 +486,14 @@
   public void waitForPageToLoad(String timeout)
   {
     _selenium.waitForPageToLoad(timeout);
+  }
+
+  /**
+   * Waits the default time for the page to load.
+   */
+  public void waitForPageToLoad()
+  {
+    waitForPageToLoad(PAGE_LOAD_TIMEOUT);
   }
 
   public void waitForPopUp(String windowID, String timeout)

Modified: tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/ErrorReportingCommandProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/ErrorReportingCommandProcessor.java?view=diff&rev=514746&r1=514745&r2=514746
==============================================================================
--- tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/ErrorReportingCommandProcessor.java (original)
+++ tapestry/tapestry5/tapestry-test/trunk/src/main/java/org/apache/tapestry/test/ErrorReportingCommandProcessor.java Mon Mar  5 09:36:17 2007
@@ -29,11 +29,14 @@
     _delegate = delegate;
   }
 
+  private static final String BORDER = "**********************************************************************";
+  
   private void reportError(String command, String[] args, RuntimeException ex)
   {
     StringBuilder builder = new StringBuilder();
 
-    builder.append("Seleninum failure processing comamnd ");
+    builder.append(BORDER);
+    builder.append("\nSeleninum failure processing comamnd ");
     builder.append(command);
     builder.append("(");
 
@@ -53,7 +56,8 @@
     builder.append(_delegate.getString("getHtmlSource", new String[] {}));
 
     builder.append("\n");
-
+    builder.append(BORDER);
+        
     System.err.println(builder.toString());
   }