You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/02/15 03:27:13 UTC

svn commit: r507781 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/services/ main/resources/org/apache/tapestry/internal/services/ site/apt...

Author: hlship
Date: Wed Feb 14 18:27:11 2007
New Revision: 507781

URL: http://svn.apache.org/viewvc?view=rev&rev=507781
Log:
Improve error messages when pages, component types or mixin types can not be resolved to a class name, by listing the known names.
Document more of the request processing pipeline.
Add StreamResponse (and a basic implementation, TextStreamResponse) and hook in the ability for a component event handler to directly render a response (by returning a StreamResponse).
Interpret the root URL, "/", as a request to render the application's start page.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/StreamResponse.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TextStreamResponse.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RootPathDispatcher.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StreamResponseResultProcessor.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html
Removed:
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Start.html
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/BlockNotFoundException.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/PageResponseRendererImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResourceStreamerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResponseImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StaticFilesFilter.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Response.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/request.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/components/Border.html

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/BlockNotFoundException.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/BlockNotFoundException.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/BlockNotFoundException.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/BlockNotFoundException.java Wed Feb 14 18:27:11 2007
@@ -22,6 +22,8 @@
  */
 public class BlockNotFoundException extends RuntimeException implements Locatable
 {
+    private static final long serialVersionUID = 81221040659940576L;
+
     private final Location _location;
 
     public BlockNotFoundException(String message, Location location)

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/StreamResponse.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/StreamResponse.java?view=auto&rev=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/StreamResponse.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/StreamResponse.java Wed Feb 14 18:27:11 2007
@@ -0,0 +1,37 @@
+// Copyright 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.
+// 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;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An alternate response from a component event handler method used to directly provide a stream of
+ * data to be sent to the client, rather than indicating what page to send a render redirect request
+ * to.
+ */
+public interface StreamResponse
+{
+    /** Returns the content type to be reported to the client. */
+    String getContentType();
+
+    /**
+     * Returns the stream of bytes to be sent to the client. The stream will be closed when the end
+     * of the stream is reached. The provided stream will be wrapped in a
+     * {@link BufferedInputStream} for efficiency.
+     */
+    InputStream getStream() throws IOException;
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TextStreamResponse.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TextStreamResponse.java?view=auto&rev=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TextStreamResponse.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TextStreamResponse.java Wed Feb 14 18:27:11 2007
@@ -0,0 +1,49 @@
+// Copyright 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.
+// 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;
+
+import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
+import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class TextStreamResponse implements StreamResponse
+{
+    private final String _contentType;
+
+    private final String _text;
+
+    public TextStreamResponse(final String contentType, final String text)
+    {
+        notBlank(contentType, "contentType");
+        notNull(text, "text");
+
+        _contentType = contentType;
+        _text = text;
+    }
+
+    public String getContentType()
+    {
+        return _contentType;
+    }
+
+    public InputStream getStream() throws IOException
+    {
+        return new ByteArrayInputStream(_text.getBytes());
+    }
+
+}

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=507781&r1=507780&r2=507781
==============================================================================
--- 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 Wed Feb 14 18:27:11 2007
@@ -52,7 +52,7 @@
     // structure of Tapestry, there should not be any reader threads while the write thread
     // is operating.
 
-    private boolean _rebuild = true;
+    private boolean _needsRebuild = true;
 
     private final Map<String, String> _pageToClassName = newCaseInsensitiveMap();
 
@@ -107,7 +107,7 @@
     /** When the class loader is invalidated, clear any cached page names or component types. */
     public synchronized void objectWasInvalidated()
     {
-        _rebuild = true;
+        _needsRebuild = true;
 
         _pageToClassName.clear();
         _componentToClassName.clear();
@@ -118,7 +118,7 @@
 
     private synchronized void rebuild()
     {
-        if (!_rebuild)
+        if (!_needsRebuild)
             return;
 
         rebuild("", _appRootPackage);
@@ -133,7 +133,7 @@
                 rebuild(folder, packageName);
         }
 
-        _rebuild = false;
+        _needsRebuild = false;
     }
 
     private void rebuild(String pathPrefix, String rootPackage)
@@ -176,7 +176,18 @@
         String result = locate(pageName, _pageToClassName);
 
         if (result == null)
-            throw new IllegalArgumentException(ServicesMessages.couldNotResolvePageName(pageName));
+        {
+            // Having trouble tracking this down:
+            System.err.format("Page '%s' not found in %s!\n\n", pageName, _pageToClassName);
+
+            // I have an intuition its a race condition related to the browser asking for
+            // "favicon.ico" but since everything is properly synchronized, I'm not quite sure
+            // how that would happen.
+
+            throw new IllegalArgumentException(ServicesMessages.couldNotResolvePageName(
+                    pageName,
+                    _pageToClassName.keySet()));
+        }
 
         return result;
     }
@@ -191,8 +202,9 @@
         String result = locate(componentType, _componentToClassName);
 
         if (result == null)
-            throw new IllegalArgumentException(ServicesMessages
-                    .couldNotResolveComponentType(componentType));
+            throw new IllegalArgumentException(ServicesMessages.couldNotResolveComponentType(
+                    componentType,
+                    _componentToClassName.keySet()));
 
         return result;
     }
@@ -202,7 +214,9 @@
         String result = locate(mixinType, _mixinToClassName);
 
         if (result == null)
-            throw new IllegalArgumentException(ServicesMessages.couldNotResolveMixinType(mixinType));
+            throw new IllegalArgumentException(ServicesMessages.couldNotResolveMixinType(
+                    mixinType,
+                    _mixinToClassName.keySet()));
 
         return result;
     }

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=507781&r1=507780&r2=507781
==============================================================================
--- 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 Wed Feb 14 18:27:11 2007
@@ -31,6 +31,7 @@
 import org.apache.tapestry.internal.bindings.LiteralBinding;
 import org.apache.tapestry.internal.bindings.PropBindingFactory;
 import org.apache.tapestry.internal.util.IntegerRange;
+import org.apache.tapestry.ioc.Configuration;
 import org.apache.tapestry.ioc.Location;
 import org.apache.tapestry.ioc.LogSource;
 import org.apache.tapestry.ioc.MappedConfiguration;
@@ -57,6 +58,7 @@
 import org.apache.tapestry.services.ComponentClassTransformWorker;
 import org.apache.tapestry.services.ComponentMessagesSource;
 import org.apache.tapestry.services.Context;
+import org.apache.tapestry.services.InfrastructureContribution;
 import org.apache.tapestry.services.MarkupWriterFactory;
 import org.apache.tapestry.services.PageRenderInitializer;
 import org.apache.tapestry.services.PersistentFieldManager;
@@ -95,19 +97,37 @@
     private final RequestPageCache _pageCache;
 
     public InternalModule(@InjectService("ComponentInstantiatorSource")
-    ComponentInstantiatorSource componentInstantiatorSource, @InjectService("UpdateListenerHub")
-    UpdateListenerHub updateListenerHub, @InjectService("tapestry.ioc.ThreadCleanupHub")
-    ThreadCleanupHub threadCleanupHub, @InjectService("ComponentTemplateSource")
+    ComponentInstantiatorSource componentInstantiatorSource,
+
+    @InjectService("UpdateListenerHub")
+    UpdateListenerHub updateListenerHub,
+
+    @InjectService("tapestry.ioc.ThreadCleanupHub")
+    ThreadCleanupHub threadCleanupHub,
+
+    @InjectService("ComponentTemplateSource")
     ComponentTemplateSource componentTemplateSource,
-            @InjectService("tapestry.ComponentClassResolver")
-            ComponentClassResolver componentClassResolver,
-            @InjectService("tapestry.ioc.ChainBuilder")
-            ChainBuilder chainBuilder, @Inject("infrastructure:Request")
-            Request request, @Inject("infrastructure:Response")
-            Response response, @InjectService("tapestry.ioc.ThreadLocale")
-            ThreadLocale threadLocale, @Inject("infrastructure:RequestGlobals")
-            RequestGlobals requestGlobals, @InjectService("RequestPageCache")
-            RequestPageCache pageCache)
+
+    @InjectService("tapestry.ComponentClassResolver")
+    ComponentClassResolver componentClassResolver,
+
+    @InjectService("tapestry.ioc.ChainBuilder")
+    ChainBuilder chainBuilder,
+
+    @Inject("infrastructure:Request")
+    Request request,
+
+    @Inject("infrastructure:Response")
+    Response response,
+
+    @InjectService("tapestry.ioc.ThreadLocale")
+    ThreadLocale threadLocale,
+
+    @Inject("infrastructure:RequestGlobals")
+    RequestGlobals requestGlobals,
+
+    @InjectService("RequestPageCache")
+    RequestPageCache pageCache)
     {
         _componentInstantiatorSource = componentInstantiatorSource;
         _updateListenerHub = updateListenerHub;
@@ -277,11 +297,12 @@
      * Contributes factory defaults that map be overridden.
      */
     @Contribute("tapestry.ioc.FactoryDefaults")
-    public void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
+    public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
     {
         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");
     }
 
     /**
@@ -291,8 +312,11 @@
     @Contribute("tapestry.ApplicationInitializer")
     public void contributeApplicationInitializerFilters(
             OrderedConfiguration<ApplicationInitializerFilter> configuration,
+
             @InjectService("tapestry.ioc.PropertyAccess")
-            final PropertyAccess propertyAccess, @Inject("infrastructure:TypeCoercer")
+            final PropertyAccess propertyAccess,
+
+            @Inject("infrastructure:TypeCoercer")
             final TypeCoercer typeCoercer)
     {
         ApplicationInitializerFilter setApplicationPackage = new ApplicationInitializerFilter()
@@ -611,5 +635,16 @@
             }
 
         };
+    }
+
+    @Contribute("tapestry.Infrastructure")
+    public static void contributeInfrastructure(
+            Configuration<InfrastructureContribution> configuration,
+
+            @InjectService("DefaultRequestExceptionHandler")
+            RequestExceptionHandler defaultRequestExceptionHandler)
+    {
+        configuration.add(new InfrastructureContribution("RequestExceptionHandler",
+                defaultRequestExceptionHandler));
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java Wed Feb 14 18:27:11 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.
@@ -25,7 +25,7 @@
 public class PageResponseRendererImpl implements PageResponseRenderer
 {
     private final PageMarkupRenderer _markupRenderer;
-    
+
     private final MarkupWriterFactory _markupWriterFactory;
 
     public PageResponseRendererImpl(MarkupWriterFactory markupWriterFactory,
@@ -41,10 +41,10 @@
         // whatever. Right now its defaulting to plain HTML.
 
         MarkupWriter writer = _markupWriterFactory.newMarkupWriter();
-        
+
         _markupRenderer.renderPageMarkup(page, writer);
 
-        PrintWriter pw = response.getPrintWriter();
+        PrintWriter pw = response.getPrintWriter("text/html");
 
         writer.toMarkup(pw);
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResourceStreamerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResourceStreamerImpl.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResourceStreamerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResourceStreamerImpl.java Wed Feb 14 18:27:11 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.
@@ -59,7 +59,12 @@
         _response.setDateHeader("Last-Modified", lastModified);
         _response.setDateHeader("Expires", lastModified + EXPIRE_DELTA);
 
-        // TODO: content type
+        String contentType = connection.getContentType();
+
+        if (contentType == null)
+            contentType = "application/octet-stream";
+
+        // TODO: if content type is null
 
         InputStream is = null;
 
@@ -69,7 +74,7 @@
 
             is = new BufferedInputStream(connection.getInputStream());
 
-            OutputStream os = _response.getOutputStream();
+            OutputStream os = _response.getOutputStream(contentType);
 
             byte[] buffer = new byte[_bufferSize];
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResponseImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResponseImpl.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResponseImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ResponseImpl.java Wed Feb 14 18:27:11 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.
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry.internal.services;
 
+import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
@@ -37,8 +39,12 @@
         _response = response;
     }
 
-    public PrintWriter getPrintWriter() throws IOException
+    public PrintWriter getPrintWriter(String contentType) throws IOException
     {
+        notBlank(contentType, "contentType");
+
+        _response.setContentType(contentType);
+
         return _response.getWriter();
     }
 
@@ -57,9 +63,11 @@
         _response.sendRedirect(URL);
     }
 
-    public OutputStream getOutputStream() throws IOException
+    public OutputStream getOutputStream(String contentType) throws IOException
     {
-        // TODO: Set content type before getting the stream.
+        notBlank(contentType, "contentType");
+
+        _response.setContentType(contentType);
 
         return _response.getOutputStream();
     }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RootPathDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RootPathDispatcher.java?view=auto&rev=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RootPathDispatcher.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RootPathDispatcher.java Wed Feb 14 18:27:11 2007
@@ -0,0 +1,84 @@
+// Copyright 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.
+// 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 java.io.IOException;
+
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.Dispatcher;
+import org.apache.tapestry.services.Request;
+import org.apache.tapestry.services.Response;
+
+/**
+ * Recognizes a request for the application root (i.e., "/") and handles this the same as a render
+ * request for the "Start" page.
+ */
+public class RootPathDispatcher implements Dispatcher
+{
+    private final ComponentClassResolver _componentClassResolver;
+
+    private final PageLinkHandler _handler;
+
+    private final PageResponseRenderer _renderer;
+
+    private final String _startPageName;
+
+    private final String[] _emptyContext = new String[0];
+
+    public RootPathDispatcher(final ComponentClassResolver componentClassResolver,
+            final PageLinkHandler handler, final PageResponseRenderer renderer,
+            final String startPageName)
+    {
+        _componentClassResolver = componentClassResolver;
+        _handler = handler;
+        _renderer = renderer;
+        _startPageName = startPageName;
+    }
+
+    public boolean dispatch(Request request, final Response response) throws IOException
+    {
+        // Only match the root path
+
+        if (!request.getPath().equals("/"))
+            return false;
+
+        if (_componentClassResolver.isPageName(_startPageName))
+        {
+            PageRenderer renderer = new PageRenderer()
+            {
+                public void renderPage(Page page)
+                {
+                    try
+                    {
+                        _renderer.renderPageResponse(page, response);
+                    }
+                    catch (IOException ex)
+                    {
+                        new RuntimeException(ex);
+                    }
+
+                }
+            };
+
+            _handler.handle(_startPageName, _emptyContext, renderer);
+
+            return true;
+        }
+
+        return false;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java Wed Feb 14 18:27:11 2007
@@ -217,19 +217,23 @@
                 .joinSorted(strategyNames));
     }
 
-    static String couldNotResolvePageName(String pageName)
+    static String couldNotResolvePageName(String pageName, Collection<String> pageNames)
     {
-        return MESSAGES.format("could-not-resolve-page-name", pageName);
+        return MESSAGES.format("could-not-resolve-page-name", pageName, InternalUtils
+                .joinSorted(pageNames));
     }
 
-    static String couldNotResolveComponentType(String componentType)
+    static String couldNotResolveComponentType(String componentType,
+            Collection<String> componentTypes)
     {
-        return MESSAGES.format("could-not-resolve-component-type", componentType);
+        return MESSAGES.format("could-not-resolve-component-type", componentType, InternalUtils
+                .joinSorted(componentTypes));
     }
 
-    static String couldNotResolveMixinType(String mixinType)
+    static String couldNotResolveMixinType(String mixinType, Collection<String> mixinTypes)
     {
-        return MESSAGES.format("could-not-resolve-mixin-type", mixinType);
+        return MESSAGES.format("could-not-resolve-mixin-type", mixinType, InternalUtils
+                .joinSorted(mixinTypes));
     }
 
     static String parameterNameMustBeUnique(String parameterName, String parameterValue)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StaticFilesFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StaticFilesFilter.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StaticFilesFilter.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StaticFilesFilter.java Wed Feb 14 18:27:11 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.
@@ -41,10 +41,17 @@
     {
         String path = request.getPath();
 
-        URL url = _context.getResource(path);
-
-        if (url != null)
-            return false;
+        // We are making the questionable assumption that all files to be vended out will contain
+        // an extension (with a dot seperator). Without this, the filter tends to match against
+        // folder names when we don't want it to (especially for the root context path).
+
+        if (path.contains("."))
+        {
+            URL url = _context.getResource(path);
+
+            if (url != null)
+                return false;
+        }
 
         return handler.service(request, response);
     }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StreamResponseResultProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StreamResponseResultProcessor.java?view=auto&rev=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StreamResponseResultProcessor.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StreamResponseResultProcessor.java Wed Feb 14 18:27:11 2007
@@ -0,0 +1,76 @@
+// Copyright 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.
+// 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 java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tapestry.StreamResponse;
+import org.apache.tapestry.internal.TapestryUtils;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.ActionResponseGenerator;
+import org.apache.tapestry.services.ComponentEventResultProcessor;
+import org.apache.tapestry.services.Response;
+
+public class StreamResponseResultProcessor implements ComponentEventResultProcessor<StreamResponse>
+{
+    private static final int BUFFER_SIZE = 5000;
+
+    public ActionResponseGenerator processComponentEvent(final StreamResponse streamResponse,
+            Component component, final String methodDescripion)
+    {
+        return new ActionResponseGenerator()
+        {
+            public void sendClientResponse(Response response) throws IOException
+            {
+                OutputStream os = null;
+                InputStream is = null;
+
+                try
+                {
+                    is = new BufferedInputStream(streamResponse.getStream());
+
+                    os = response.getOutputStream(streamResponse.getContentType());
+
+                    byte[] buffer = new byte[BUFFER_SIZE];
+
+                    while (true)
+                    {
+                        int length = is.read(buffer, 0, buffer.length);
+                        if (length < 0)
+                            break;
+
+                        os.write(buffer, 0, length);
+                    }
+
+                    os.close();
+                    os = null;
+
+                    is.close();
+                    is = null;
+                }
+                finally
+                {
+                    TapestryUtils.close(is);
+                    TapestryUtils.close(os);
+                }
+            }
+
+        };
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Response.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Response.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Response.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Response.java Wed Feb 14 18:27:11 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.
@@ -29,14 +29,21 @@
     /**
      * Returns a PrintWriter object to which output may be sent. Invoking flush() on the writer will
      * commit the output.
+     * 
+     * @param contentType
+     *            the MIME content type for the output, typically "text/html"
      */
-    PrintWriter getPrintWriter() throws IOException;
+    PrintWriter getPrintWriter(String contentType) throws IOException;
 
     /**
      * Returns an OutputStream to which byte-oriented output may be sent. Invoking flush() on the
      * stream will commit the output.
+     * 
+     * @param contentType
+     *            the MIME content type for the output, often "application/octet-stream" or
+     *            "text/plain" or one of several others
      */
-    OutputStream getOutputStream() throws IOException;
+    OutputStream getOutputStream(String contentType) throws IOException;
 
     /**
      * Sends a redirect to the client.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Wed Feb 14 18:27:11 2007
@@ -29,6 +29,7 @@
 import org.apache.tapestry.Link;
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.SelectModel;
+import org.apache.tapestry.StreamResponse;
 import org.apache.tapestry.Translator;
 import org.apache.tapestry.Validator;
 import org.apache.tapestry.annotations.AfterRender;
@@ -133,10 +134,12 @@
 import org.apache.tapestry.internal.services.ResourceStreamer;
 import org.apache.tapestry.internal.services.ResponseImpl;
 import org.apache.tapestry.internal.services.RetainWorker;
+import org.apache.tapestry.internal.services.RootPathDispatcher;
 import org.apache.tapestry.internal.services.SessionApplicationStatePersistenceStrategy;
 import org.apache.tapestry.internal.services.SessionHolder;
 import org.apache.tapestry.internal.services.SessionPersistentFieldStrategy;
 import org.apache.tapestry.internal.services.StaticFilesFilter;
+import org.apache.tapestry.internal.services.StreamResponseResultProcessor;
 import org.apache.tapestry.internal.services.StringResultProcessor;
 import org.apache.tapestry.internal.services.SupportsInformalParametersWorker;
 import org.apache.tapestry.internal.services.TranslatorDefaultSourceImpl;
@@ -490,12 +493,12 @@
             @InjectService("Context")
             Context context,
 
-            @InjectService("tapestry.internal.DefaultRequestExceptionHandler")
+            @Inject("infrastructure:RequestExceptionHandler")
             final RequestExceptionHandler exceptionHandler)
     {
         RequestFilter staticFilesFilter = new StaticFilesFilter(context);
 
-        configuration.add("StaticFilesFilter", staticFilesFilter);
+        configuration.add("StaticFiles", staticFilesFilter);
 
         RequestFilter errorFilter = new RequestFilter()
         {
@@ -540,7 +543,6 @@
 
             @InjectService("tapestry.ioc.PropertyAccess")
             PropertyAccess propertyAccess)
-
     {
         add(
                 configuration,
@@ -585,9 +587,27 @@
             MappedConfiguration<String, ObjectProvider> configuration,
 
             @InjectService("Infrastructure")
-            Infrastructure infrastructure)
+            final Infrastructure infrastructure)
     {
-        configuration.add("infrastructure", infrastructure.getObjectProvider());
+        // There's a nasty web of dependencies related to Infrastructure; this wrapper class lets us
+        // defer instantiating the Infrastructure implementation just long enough to defuse those
+        // dependencies.
+
+        ObjectProvider wrapper = new ObjectProvider()
+        {
+            public <T> T provide(String expression, Class<T> objectType, ServiceLocator locator)
+            {
+                return infrastructure.getObjectProvider().provide(expression, objectType, locator);
+            }
+        };
+
+        // Or you can defuse the dependency by using @InjectService("foo") instead of
+        // @Inject("service:foo"). The latter requires the MasterObjectProvider, which requires
+        // the Infrastructure, which then fails if any contribution
+        // to infrastructure makes use of @Inject. However, since its likely that end users will try
+        // to do this, the wrapper has been left in place (it does very little harm).
+
+        configuration.add("infrastructure", wrapper);
     }
 
     public void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration,
@@ -607,8 +627,16 @@
             ActionLinkHandler actionLinkHandler,
 
             @InjectService("tapestry.ComponentClassResolver")
-            ComponentClassResolver componentClassResolver)
+            ComponentClassResolver componentClassResolver,
+
+            @Inject("${tapestry.start-page-name}")
+            String startPageName)
     {
+        // Looks for the root path and renders the start page
+
+        configuration.add("RootPath", new RootPathDispatcher(componentClassResolver,
+                pageLinkHandler, _pageResponseRenderer, startPageName), "before:Asset");
+
         // This goes first because an asset to be streamed may have an file extension, such as
         // ".html", that will confuse the later dispatchers.
 
@@ -1268,6 +1296,8 @@
         configuration.add(String.class, new StringResultProcessor(_requestPageCache, _linkFactory));
 
         configuration.add(Component.class, componentInstanceProcessor);
+
+        configuration.add(StreamResponse.class, new StreamResponseResultProcessor());
     }
 
     public ComponentEventResultProcessor buildComponentInstanceResultProcessor(Log log)
@@ -1345,8 +1375,7 @@
      * <li>Boolean --&gt; checkbox
      * </ul>
      */
-    public static void contributeBeanModelSource(
-            MappedConfiguration<Class, String> configuration)
+    public static void contributeBeanModelSource(MappedConfiguration<Class, String> configuration)
     {
         configuration.add(Object.class, "");
         configuration.add(String.class, "text");

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties Wed Feb 14 18:27:11 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.
@@ -54,9 +54,9 @@
 exception-in-method-parameter=Exception in method %s, parameter #%d: %s
 component-event-is-aborted=Can not store result from invoking method %s, because an event result value has already been obtained from some other event handler method.
 unknown-persistent-field-strategy='%s' is not a defined persistent strategy.  Defined stategies: %s.
-could-not-resolve-page-name=Unable to resolve page '%s' to a component class name.
-could-not-resolve-component-type=Unable to resolve component type '%s' to a component class name.
-could-not-resolve-mixin-type=Unable to resolve mixin type '%s' to a component class name.
+could-not-resolve-page-name=Unable to resolve page '%s' to a component class name.  Available page names: %s.
+could-not-resolve-component-type=Unable to resolve component type '%s' to a component class name.  Available component types: %s.
+could-not-resolve-mixin-type=Unable to resolve mixin type '%s' to a component class name.  Available mixin types: %s.
 parameter-name-must-be-unique=Parameter names are required to be unique.  Parameter '%s' already has the value '%s'.
 page-is-dirty=Page %s is dirty, and will be discarded (rather than returned to the page pool).
 component-instance-is-not-a-page=Method %s (for component %s) returned component %s, which is not a page component. The page containing the component will render the client response.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt Wed Feb 14 18:27:11 2007
@@ -5,7 +5,8 @@
 Configuring Tapestry
 
   Tapestry runs on top of the standard Java Servlet API.  To the servlet container,
-  such as Tomcat, Tapestry appears to be a <servlet filter>.  
+  such as Tomcat, Tapestry appears as a <servlet filter>.  This gives Tapestry great
+  flexibility in matching URLs without requiring lots of configuration inside web.xml.
   
 * web.xml
 
@@ -48,6 +49,17 @@
   In this example, page classes will be stored in the <<<org.example.myapp.pages>>> package (or in sub-packages below).
   Likewise, component classes will be stored in the <<<org.example.myapp.components>>> package.
   
+* Tapestry Requests vs. Container Requests
+
+  The Tapestry filter matches all the requests that apply to Tapestry, and passes the rest off to the
+  servlet container.
+  
+  Actual files inside the web application take precedence over Tapestry pages, in situation where there
+  would be a naming conflict.  
+  
+  Tapestry recognizes the <root URL>, where the servlet path is simply "/", and renders the application page "Start",
+  if it exists.  
+  
 * Tapestry IoC Configuration
 
   Most other configuration occurs inside your application's module builder class.  The application module builder
@@ -67,6 +79,14 @@
   by contributing to the tapestry.ioc.ApplicationDefaults service configuration, or on the command line
   by defining JVM System Properties with the -D command line option.
   
+  [tapestry.default-cookie-max-age]
+    The default age, in seconds, that cookies created by Tapestry will be kept in the client web browser.
+    The dfault value is equal to one week. 
+    
+    Primarily, this is used with a cookie that exists 
+    to track the preferred user locale.
+    
+  
   [tapestry.file-check-interval]
     Time (in milliseconds) between file system checks. During a file system check, only a single thread is active (all others
     are blocked) and any files loaded are checked for changes (this is part of {{{reload.html}automatic component reloading}}).
@@ -78,6 +98,9 @@
     If no match can be found, the first locale in the list is treated as the default.
     
     The default is (currently) "en". 
+    
+  [tapestry.start-page-name]
+    The logical name of the start page, the page that is rendered for the <root URL>.  This is normally "start".
     
 
   

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/request.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/request.apt?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/request.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/request.apt Wed Feb 14 18:27:11 2007
@@ -27,7 +27,9 @@
   This pipeline performs initial processing of the request. It can be extended
   by contributing a
   {{{../apidocs/org/apache/tapestry/services/HttpServletRequestFilter.html}tapestry.HttpServletRequestFilter}} into
-  the tapestry.HttpServletRequestHandler service's configuration.
+  the tapestry.HttpServletRequestHandler service's configuration.'
+  
+  Tapestry does not contribute any filters into this pipeline of its own
   
   The terminator for the pipeline does two things:
   
@@ -48,15 +50,65 @@
    Tapestry require access to information in the request, such as query parameters, that information is obtained from the
    Request (or Response) objects.
    
-   The pipeline includes two built-in filters.  One filter is responsible for {{{reload.html}class and template reloading}}.
+   The pipeline includes a number of built-in filters.  
+   
+   * tapestry.internal.CheckForUpdates is responsible for {{{reload.html}class and template reloading}}.
+   
+   * tapestry.internal.Localization identifies the {{{localization.html}locale for the user}}.
    
-   A second filter detects incoming requests that are for static resources (images, stylesheets, static HTML files, etc.)
-   within the context; such requests are
-   processed by the servlet container directly.
+   * tapestry.StaticFiles checks for URLs that are for static files (files that exist inside the web context) and aborts
+     the request, so that the servlet container can handle the reuest normally.
+   
+   * tapestry.ErrorFilter catches uncaught exceptions from the lower levels of Tapestry and presents the exception report page.
+     This involves the {{{../apidocs/org/apache/tapestry/services/RequestExceptionHandler.html}infrastructure:RequestExceptionHandler}} service,
+     which is responsible for initializing and rendering the
+     {{{../apidocs/org/apache/tapestry/corelib/pages/ExceptionReport.html}core/ExceptionReport}} page. 
+    
+    
+   []
+      
+   The terminator for this pipeline stores the Request and the Response into RequestGlobals, then requests that the
+   {{{../apidocs/org/apache/tapestry/services/Dispatcher.html}tapestry.MasterDispatcher}} service figure out how to
+   handle the request (if it is, indeed, a Tapestry request).
    
-   <<TODO: Describe request dispatch, once that is understood.>>
+Master Dispatcher Service
+
+  The MasterDispatcher service is a chain-of-command, aggregating together (in a specific order), several
+  Dispatcher objects.  Each Dispatcher is built to recognize and process a particular kind of URL.
+  
+* tapestry.RootPath
+
+  As discussed {{{conf.html}elsewhere}}, requests for the context root will instead be treated like render
+  requests for the "start" page.
+
+* tapestry.Asset
+
+  Requests that being with "/assets/" are references to {{{assets.html}asset resources}} that are stored on the classpath, inside the Tapestry JARs 
+  (or perhaps inside the JAR for a component library).  The contents of the file will be pumped down to the client browser.
+
+* tapestry.PageRender
+
+  Page render requests are requests to render a particular page.  Such requests may include additional elements on the path, which will be
+  treated as {{{event.html}activation context}} (generally speaking, the primary key of some related entity object), allowing the page to
+  reconstruct the state it will need to succesfully render itself.
+  
+  Page render URLs consist of the logical name of the page plus additional path elements for the activation context.  The dispatcher
+  here strips terms off of the path until it finds a known page name. Thus, "/mypage/27" would look first for a page whose name was "mypage/27", then look for 
+  a page name "mypage". Assuming the second search was succesful, the page would be activated with the context "27".  If no logical page name
+  can be identified, control passes to the next dispatcher.
+
+* tapestry.ComponentEvent
+
+  The component event dispatcher is used to trigger events in components.
+  
+  The URL identifies the name of the page, then a series of component ids (the path from the page down to the specific component), then the name of the event to be
+  triggered on the component. The remaining path elements are used as the context for the <event> (not for the page activation, which
+  does not currently apply). For example, "/griddemo.FOO.BAR.action/3" would locate page "griddemo", then component "FOO.BAR", and trigger an event named "action", with
+  the context "3".
+  
+  The response from a component event is typically, but not universally, used to send a redirect to the client; the redirect URL is a page render URL
+  to display the response to the event.  
    
-   The terminator for this pipeline stores the Request and the Response into RequestGlobals.
    
 RequestGlobals Service
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt Wed Feb 14 18:27:11 2007
@@ -38,6 +38,13 @@
   Progress on Tapestry 5 is really taking off. This space lists some cool new features that have been added
   recently.
   
+  * Component event handlers may now return a {{{apidocs/org/apache/tapestry/StreamResponse.html}StreamResponse}}
+    to directly send a stream to the client web browser (this is intended for components that need to render
+    images, PDF, or other non-page oriented content).
+  
+  * The root URL for an application now renders the application's start page (it used to let the
+    servlet container render the static welcome page, if any). 
+  
   * Named-based lookups of messages, resources, etc., are all case-insensitive.
   
   * Fast, smart, fully customizable Grid component for displaying tabular data.
@@ -51,7 +58,7 @@
   
   * Case insensitve URLs.  Tapestry no longer cares about the case of the page names and commponent ids it puts
     into URLs, and they are now generated in all lower-case for that clean "Web 2.0" look.  And they're 
-    shorter, too!
+    shorter and "prettier", too!
   
   * Initial support for {{{guide/appstate.html}application state objects}}.
   
@@ -89,31 +96,42 @@
   when the form is submitted:
   
 +----+
-@Persist
-private String _userId;
-
-private String _password;
-
-@Component
-private Form _form;
-
-@InjectPage
-private Start _startPage;
-
-@Inject
-private LoginAuthenticator _authenticator;
-
-@OnEvent("submit")
-private Object doLogin()
+public class Login
 {
-  if (_authenticator.isValidLogin(_userId, _password))
-    return _startPage;
-    
-  // Stay on this page:
-
- _form.recordError("Invalid user name or password."); 
-  
-  return null;
+	@Persist
+	private String _userId;
+	
+	private String _password;
+	
+	@Component
+	private Form _form;
+	
+	@InjectPage
+	private Start _startPage;
+	
+	@Inject
+	private LoginAuthenticator _authenticator;
+	
+	@OnEvent("submit")
+	private Object doLogin()
+	{
+	  if (_authenticator.isValidLogin(_userId, _password))
+	    return _startPage;
+	    
+	  // Stay on this page:
+	
+	 _form.recordError("Invalid user name or password."); 
+	  
+	  return null;
+	}
+	
+	public String getUserId() { return _userId; }
+	
+	public String getPassword() { return _password; }
+	
+	public void setUserId(String userId) { _userId = userId; }
+	
+	public void setPassword(String password) { _password = password; }
 }
 +----+
 

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html?view=auto&rev=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html Wed Feb 14 18:27:11 2007
@@ -0,0 +1,114 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+    <h1> Tapestry 5 Integration Application 1</h1>
+
+    <table>
+        <tr>
+            <td>
+                <ul>
+                    <li>
+                        <a t:type="PageLink" page="MerryChristmas">Count Page</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="InjectDemo">Inject Demo</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="Countdown">Countdown Page</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="ParameterConflict">Template Overriden by Class
+                            Page</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="EnvironmentalDemo">Environmental Annotation
+                            Useage</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="expansion">Expansion Page</a>
+                    </li>
+                    <li>
+                        <a href="MissingPage">Missing Page</a> -- Used to test exception reporting </li>
+                    <li>
+                        <a href="BadTemplate">BadTemplate Page</a> -- More exception reporting </li>
+                    <li>
+                        <a t:type="PageLink" page="ActionPage">Action Page</a> -- tests fixture for
+                        ActionLink component </li>
+                    <li>
+                        <a t:type="PageLink" page="InstanceMixin">InstanceMixin</a> -- Mixin added
+                        to particular component instance </li>
+                    <li>
+                        <a t:type="PageLink" page="RenderPhaseOrder">RenderPhaseOrder</a> -- Order
+                        of operations when invoking render phase methods </li>
+                    <li><a t:type="PageLink" page="SimpleForm">SimpleForm</a> -- first pass at
+                        writing Form and TextField components </li>
+                    <li>
+                        <a t:type="PageLink" page="NumberSelect">NumberSelect</a> --
+                        passivate/activate page context demo </li>
+                    <li>
+                        <a t:type="PageLink" page="Localization">Localization</a> -- accessing
+                        localized messages from the component catalog </li>
+                    <li>
+                        <a t:type="PageLink" page="AssetDemo">AssetDemo</a> -- declaring an using
+                        Assets </li>
+
+                </ul>
+            </td>
+            <td>
+                <ul>
+                    <li>
+                        <a t:type="PageLink" page="ExpansionSubclass">ExpansionSubclass</a> --
+                        components can inherit templates from base classes </li>
+                    <li>
+                        <a href="InjectComponentMismatch">InjectComponentMismatch</a> -- check error
+                        reporting when @InjectComponent doesn't match the actual field type </li>
+                    <li>
+                        <a t:type="PageLink" page="ParameterDefault">ParameterDefault</a> --
+                        defaulter methods for component parameters </li>
+                    <li>
+                        <a t:type="PageLink" page="ValidForm">ValidForm</a> -- server-side input
+                        validation</li>
+                    <li>
+                        <a t:type="PageLink" page="AnyDemo">AnyDemo</a> -- test out the Any
+                        component </li>
+                    <li>
+                        <a t:type="PageLink" page="PasswordFieldDemo">PasswordFieldDemo</a> -- test
+                        for the PasswordField component </li>
+                    <li>
+                        <a t:type="PageLink" page="RenderComponentDemo">RenderComponentDemo</a> --
+                        components that "nominate" other components to render </li>
+                    <li>
+                        <a t:type="PageLink" page="BlockDemo">BlockDemo</a> -- use of blocks to
+                        control rendering </li>
+                    <li>
+                        <a t:type="PageLink" page="ToDoListVolatile">ToDo List (Volatile)</a> --
+                        Loops and Submit inside Form, volatile mode </li>
+                    <li>
+                        <a t:type="PageLink" page="ToDoList">ToDo List</a> -- Loops and Submit
+                        inside Form using a primary key encoder </li>
+                    <li>
+                        <a t:type="PageLink" page="FlashDemo">FlashDemo</a> -- demonstrate "flash"
+                        persistence </li>
+                    <li>
+                        <a t:type="PageLink" page="beaneditordemo">BeanEditor Demo</a> --
+                        demonstrate the BeanEditor mega-component </li>
+                    <li>
+                        <a t:type="PageLink" page="pageloadeddemo">PageLoaded Demo</a> -- shows that
+                        page lifecycle methods are invoked </li>
+                    <li>
+                        <a t:type="PageLink" page="griddemo">Grid Demo</a> -- default Grid component </li>
+                    <li>
+                        <a t:type="PageLink" page="nullgrid">Null Grid</a> -- handling of null
+                        source for Grid </li>
+                    <li>
+                        <a t:type="PageLink" page="gridenumdemo">Grid Enum Demo</a> -- handling of
+                        enum types in the Grid </li>
+                    <li>
+                        <a t:type="ActionLink" t:id="textStreamResponse">Text Stream Response</a> --
+                        component event that directly returns a stream of character (rather than a
+                        redirect) </li>
+                </ul>
+            </td>
+        </tr>
+    </table>
+
+</html>

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=507781&r1=507780&r2=507781
==============================================================================
--- 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 Wed Feb 14 18:27:11 2007
@@ -102,28 +102,6 @@
     }
 
     @Test
-    public void basic_output() throws Exception
-    {
-        _selenium.open(BASE_URL);
-
-        clickAndWait("link=Start Page");
-
-        // This comes from the Border cmponent's template
-
-        assertTrue(_selenium.getTitle().contains("Tapestry"));
-
-        // Text from Start.html
-
-        assertTextPresent("First Tapestry 5 Page");
-
-        // This is text passed from Start.html to Output as a parameter
-
-        assertTextPresent("we have basic parameters working");
-
-        assertSourcePresent("<!-- my comment -->");
-    }
-
-    @Test
     public void basic_parameters() throws Exception
     {
 
@@ -870,11 +848,37 @@
     }
 
     @Test
-    public void null_grid()
+    public void stream_response() throws Exception
     {
         _selenium.open(BASE_URL);
+
+        clickAndWait("link=Text Stream Response");
+
+        assertText("//body", "Success!");
+    }
+
+    @Test
+    public void null_grid() throws Exception
+    {
+        // Been having problems with very spurious failures of this test. Troubling, and it has
+        // repairman
+        // syndrome; only happens during a full suite, where I can't pick out the problem. These
+        // calls to System.error
+        // seem to have "fixed" it. I think TestNG may be running null_grid() first.
+
+        System.err.println("****\n\n\nNULL GRID START\n\n****\n\n");
+
+        _selenium.open(BASE_URL);
+
+        // Let's see if sleeping for a half second is enough.
+
+        Thread.sleep(5000);
+
         clickAndWait("link=Null Grid");
 
         assertTextPresent("There is no data to display.");
+
+        System.err.println("****\n\n\nNULL GRID END\n\n****\n\n");
     }
+
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java Wed Feb 14 18:27:11 2007
@@ -14,9 +14,17 @@
 
 package org.apache.tapestry.integration.app1.pages;
 
+import org.apache.tapestry.TextStreamResponse;
+
 /**
  * Have to start somewhere!
  */
 public class Start
 {
+    Object onActionFromTextStreamResponse()
+    {
+        String text = "<html><body>Success!</body></html>";
+
+        return new TextStreamResponse("text/html", text);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java Wed Feb 14 18:27:11 2007
@@ -425,9 +425,8 @@
         }
         catch (IllegalArgumentException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Unable to resolve page 'lib/deep/DeepPage' to a component class name.");
+            assertTrue(ex.getMessage().contains(
+                    "Unable to resolve page 'lib/deep/DeepPage' to a component class name."));
         }
 
         verify();
@@ -512,9 +511,8 @@
         }
         catch (IllegalArgumentException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Unable to resolve mixin type 'SimpleMixin' to a component class name.");
+            assertTrue(ex.getMessage().contains(
+                    "Unable to resolve mixin type 'SimpleMixin' to a component class name."));
         }
 
         verify();
@@ -542,9 +540,10 @@
         }
         catch (IllegalArgumentException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Unable to resolve component type 'SimpleComponent' to a component class name.");
+            assertTrue(ex
+                    .getMessage()
+                    .contains(
+                            "Unable to resolve component type 'SimpleComponent' to a component class name."));
         }
 
         verify();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/components/Border.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/components/Border.html?view=diff&rev=507781&r1=507780&r2=507781
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/components/Border.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/components/Border.html Wed Feb 14 18:27:11 2007
@@ -14,7 +14,7 @@
         
         
         <p>
-            <a href="/index.html">Back to index</a>
+            <a t:type="PageLink" page="Start">Back to index</a>
         </p>
     </body>