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 2012/04/06 19:44:44 UTC
svn commit: r1310501 - in /tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry5/internal/services/
test/java/org/apache/tapestry5/internal/services/
Author: hlship
Date: Fri Apr 6 17:44:44 2012
New Revision: 1310501
URL: http://svn.apache.org/viewvc?rev=1310501&view=rev
Log:
TAP5-1791: On some JDKs, the complex regular expression used by ComponentEventLinkEncoderImpl will cause a stack overflow
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventDispatcherTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java?rev=1310501&r1=1310500&r2=1310501&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java Fri Apr 6 17:44:44 2012
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2009, 2010, 2011, 2012 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.
@@ -16,14 +16,15 @@ package org.apache.tapestry5.internal.se
import org.apache.tapestry5.*;
import org.apache.tapestry5.internal.InternalConstants;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.services.*;
import org.apache.tapestry5.services.security.ClientWhitelist;
+import java.util.List;
import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class ComponentEventLinkEncoderImpl implements ComponentEventLinkEncoder
{
@@ -57,24 +58,6 @@ public class ComponentEventLinkEncoderIm
private static final char SLASH = '/';
- // A beast that recognizes all the elements of a path in a single go.
- // We skip the leading slash, then take the next few terms (until a dot or a colon)
- // as the page name. Then there's a sequence that sees a dot
- // and recognizes the nested component id (which may be missing), which ends
- // at the colon, or at the slash (or the end of the string). The colon identifies
- // the event name (the event name is also optional). A valid path will always have
- // a nested component id or an event name (or both) ... when both are missing, then the
- // path is most likely a page render request. After the optional event name,
- // the next piece is the action context, which is the remainder of the path.
-
- private final Pattern COMPONENT_EVENT_REQUEST_PATH_PATTERN;
-
- // Constants for the match groups in the above pattern.
- private static final int LOGICAL_PAGE_NAME = 1;
- private static final int NESTED_ID = 6;
- private static final int EVENT_NAME = 9;
- private static final int CONTEXT = 11;
-
public ComponentEventLinkEncoderImpl(ComponentClassResolver componentClassResolver,
ContextPathEncoder contextPathEncoder, LocalizationSetter localizationSetter, Request request,
Response response, RequestSecurityManager requestSecurityManager, BaseURLSource baseURLSource,
@@ -97,20 +80,6 @@ public class ComponentEventLinkEncoderIm
boolean hasAppFolder = applicationFolder.equals("");
applicationFolderPrefix = hasAppFolder ? null : SLASH + applicationFolder;
-
- String applicationFolderPattern = hasAppFolder ? "" : applicationFolder + SLASH;
-
- COMPONENT_EVENT_REQUEST_PATH_PATTERN = Pattern.compile(
-
- "^/" + // The leading slash is recognized but skipped
- applicationFolderPattern + // The folder containing the application (TAP5-743)
- "(((\\w(?:\\w|-)*)/)*(\\w+))" + // A series of folder names (which allow dashes) leading up to the page name, forming
- // the logical page name (may include the locale name)
- "(\\.(\\w+(\\.\\w+)*))?" + // The first dot separates the page name from the nested
- // component id
- "(\\:(\\w+))?" + // A colon, then the event type
- "(/(.*))?", // A slash, then the action context
- Pattern.COMMENTS);
}
public Link createPageRenderLink(PageRenderRequestParameters parameters)
@@ -234,69 +203,180 @@ public class ComponentEventLinkEncoderIm
return result;
}
- public ComponentEventRequestParameters decodeComponentEventRequest(Request request)
+ /**
+ * Splits path at slashes into a <em>mutable</em> list of strings. Empty terms, including the
+ * expected leading term (paths start with a '/') are dropped.
+ *
+ * @param path
+ * @return mutable list of path elements
+ */
+ private List<String> splitPath(String path)
{
- boolean explicitLocale = false;
+ String[] split = TapestryInternalUtils.splitPath(path);
- Matcher matcher = COMPONENT_EVENT_REQUEST_PATH_PATTERN.matcher(request.getPath());
+ List<String> result = CollectionFactory.newList();
- if (!matcher.matches())
- return null;
+ for (String name : split)
+ {
+ if (name.length() > 0)
+ {
+ result.add(name);
+ }
+ }
+
+ return result;
+ }
+
+ private String joinPath(List<String> path)
+ {
+ if (path.isEmpty())
+ {
+ return "";
+ }
- String nestedComponentId = matcher.group(NESTED_ID);
+ StringBuilder builder = new StringBuilder(100);
+ String sep = "";
- String eventType = matcher.group(EVENT_NAME);
+ for (String term : path)
+ {
+ builder.append(sep).append(term);
+ sep = "/";
+ }
+
+ return builder.toString();
+ }
- if (nestedComponentId == null && eventType == null)
+ private String peekFirst(List<String> path)
+ {
+ if (path.size() == 0)
+ {
return null;
+ }
+
+ return path.get(0);
+ }
- String activePageName = matcher.group(LOGICAL_PAGE_NAME);
+ public ComponentEventRequestParameters decodeComponentEventRequest(Request request)
+ {
+ String explicitLocale = null;
- int slashx = activePageName.indexOf('/');
+ // Split the path around slashes into a mutable list of terms, which will be consumed term by term.
- String possibleLocaleName = slashx > 0 ? activePageName.substring(0, slashx) : "";
+ List<String> path = splitPath(request.getPath());
- if (localizationSetter.setLocaleFromLocaleName(possibleLocaleName))
+ if (this.applicationFolder.length() > 0)
{
- activePageName = activePageName.substring(slashx + 1);
- explicitLocale = true;
+ // TODO: Should this be case insensitive
+
+ String inPath = path.remove(0);
+
+ if (!inPath.equals(this.applicationFolder))
+ {
+ return null;
+ }
}
- if (!componentClassResolver.isPageName(activePageName))
+ if (path.isEmpty())
+ {
return null;
+ }
- activePageName = componentClassResolver.canonicalizePageName(activePageName);
+ // Next up: the locale (which is optional)
- if (isWhitelistOnlyAndNotValid(activePageName))
+ String potentialLocale = path.get(0);
+
+ if (localizationSetter.isSupportedLocaleName(potentialLocale))
{
- return null;
+ explicitLocale = potentialLocale;
+ path.remove(0);
}
- EventContext eventContext = contextPathEncoder.decodePath(matcher.group(CONTEXT));
+ StringBuilder pageName = new StringBuilder(100);
+ String sep = "";
+
+ while (!path.isEmpty())
+ {
+ String name = path.remove(0);
+ String eventType = EventConstants.ACTION;
+ String nestedComponentId = "";
+
+ boolean found = false;
+
+ // First, look for an explicit action name.
- EventContext activationContext = contextPathEncoder.decodePath(request
- .getParameter(InternalConstants.PAGE_CONTEXT_NAME));
+ int colonx = name.lastIndexOf(':');
- // The event type is often omitted, and defaults to "action".
+ if (colonx > 0)
+ {
+ found = true;
+ eventType = name.substring(colonx + 1);
+ name = name.substring(0, colonx);
+ }
- if (eventType == null)
- eventType = EventConstants.ACTION;
+ int dotx = name.indexOf('.');
- if (nestedComponentId == null)
- nestedComponentId = "";
+ if (dotx > 0)
+ {
+ found = true;
+ nestedComponentId = name.substring(dotx + 1);
+ name = name.substring(0, dotx);
+ }
- String containingPageName = request.getParameter(InternalConstants.CONTAINER_PAGE_NAME);
+ pageName.append(sep).append(name);
- if (containingPageName == null)
- containingPageName = activePageName;
- else
- containingPageName = componentClassResolver.canonicalizePageName(containingPageName);
+ if (found)
+ {
+ ComponentEventRequestParameters result = validateAndConstructComponentEventRequest(request, pageName.toString(), nestedComponentId, eventType, path);
- if (!explicitLocale)
+ if (result == null)
+ {
+ return result;
+ }
+
+ if (explicitLocale == null)
+ {
+ setLocaleFromRequest(request);
+ } else
+ {
+ localizationSetter.setLocaleFromLocaleName(explicitLocale);
+ }
+
+ return result;
+ }
+
+ // Continue on to the next name in the path
+ sep = "/";
+ }
+
+ // Path empty before finding something that looks like a component id or event name, so
+ // it is not a component event request.
+
+ return null;
+ }
+
+ private ComponentEventRequestParameters validateAndConstructComponentEventRequest(Request request, String pageName, String nestedComponentId, String eventType, List<String> remainingPath)
+ {
+ if (!componentClassResolver.isPageName(pageName))
{
- setLocaleFromRequest(request);
+ return null;
}
+ String activePageName = componentClassResolver.canonicalizePageName(pageName);
+
+ if (isWhitelistOnlyAndNotValid(activePageName))
+ {
+ return null;
+ }
+
+ String value = request.getParameter(InternalConstants.CONTAINER_PAGE_NAME);
+
+ String containingPageName = value == null
+ ? activePageName
+ : componentClassResolver.canonicalizePageName(value);
+
+ EventContext eventContext = contextPathEncoder.decodePath(joinPath(remainingPath));
+ EventContext activationContext = contextPathEncoder.decodePath(request.getParameter(InternalConstants.PAGE_CONTEXT_NAME));
+
return new ComponentEventRequestParameters(activePageName, containingPageName, nestedComponentId, eventType,
activationContext, eventContext);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventDispatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventDispatcherTest.java?rev=1310501&r1=1310500&r2=1310501&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventDispatcherTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventDispatcherTest.java Fri Apr 6 17:44:44 2012
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008, 2009, 2011 The Apache Software Foundation
+// Copyright 2007, 2008, 2009, 2011, 2012 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.
@@ -46,13 +46,16 @@ public class ComponentEventDispatcherTes
{
Request request = mockRequest();
Response response = mockResponse();
+ LocalizationSetter ls = mockLocalizationSetter();
+
+ expect(ls.isSupportedLocaleName("foo")).andReturn(false);
train_getPath(request, "/foo/bar/baz");
replay();
Dispatcher dispatcher = new ComponentEventDispatcher(null,
- new ComponentEventLinkEncoderImpl(null, contextPathEncoder, null, request,
+ new ComponentEventLinkEncoderImpl(null, contextPathEncoder, ls, request,
response, null, null, null, true, "", null, null));
assertFalse(dispatcher.dispatch(request, response));
@@ -146,9 +149,10 @@ public class ComponentEventDispatcherTes
new String[]
{"alpha", "beta"}), new EmptyEventContext());
+
train_getPath(request, "/mypage:eventname");
- train_setLocaleFromLocaleName(ls, "", false);
+ expect(ls.isSupportedLocaleName("mypage:eventname")).andReturn(false);
train_isPageName(resolver, "mypage", true);
@@ -189,7 +193,7 @@ public class ComponentEventDispatcherTes
train_getPath(request, "/activepage:eventname");
- train_setLocaleFromLocaleName(ls, "", false);
+ expect(ls.isSupportedLocaleName("activepage:eventname")).andReturn(false);
train_isPageName(resolver, "activepage", true);
@@ -224,9 +228,10 @@ public class ComponentEventDispatcherTes
ComponentClassResolver resolver = mockComponentClassResolver();
LocalizationSetter ls = mockLocalizationSetter();
+ expect(ls.isSupportedLocaleName("en")).andReturn(true);
+
train_getPath(request, "/en/mypage.foo");
- train_setLocaleFromLocaleName(ls, "en", true);
train_isPageName(resolver, "mypage", false);
replay();
@@ -256,7 +261,7 @@ public class ComponentEventDispatcherTes
train_getPath(request, requestPath);
- train_setLocaleFromLocaleName(localizationSetter, localeName, false);
+ expect(localizationSetter.isSupportedLocaleName(localeName)).andReturn(false);
train_isPageName(resolver, containerPageName, true);
@@ -304,9 +309,11 @@ public class ComponentEventDispatcherTes
containerPageName, containerPageName, "", "anevent",
new EmptyEventContext(), new EmptyEventContext());
+
+
train_getPath(request, "/foo/MyPage:anevent");
- train_setLocaleFromLocaleName(localizationSetter, "foo", false);
+ expect(localizationSetter.isSupportedLocaleName("foo")).andReturn(false);
train_isPageName(resolver, containerPageName, true);
@@ -351,7 +358,7 @@ public class ComponentEventDispatcherTes
train_getPath(request, requestPath);
- train_setLocaleFromLocaleName(localizationSetter, localeName, false);
+ expect(localizationSetter.isSupportedLocaleName("foo")).andReturn(false);
train_isPageName(resolver, containerPageName, true);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImplTest.java?rev=1310501&r1=1310500&r2=1310501&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImplTest.java Fri Apr 6 17:44:44 2012
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2009, 2010, 2011, 2012 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.
@@ -420,6 +420,8 @@ public class ComponentEventLinkEncoderIm
LocalizationSetter ls = mockLocalizationSetter();
MetaDataLocator metaDataLocator = neverWhitelistProtected();
+ expect(ls.isSupportedLocaleName("foo-bar")).andReturn(false);
+
train_getParameter(request, InternalConstants.PAGE_CONTEXT_NAME, null);
train_getParameter(request, InternalConstants.CONTAINER_PAGE_NAME, null);
train_getLocale(request, Locale.ENGLISH);
@@ -429,8 +431,6 @@ public class ComponentEventLinkEncoderIm
String path = "/foo-bar/baz.biff";
train_getPath(request, path);
- train_setLocaleFromLocaleName(ls, "foo-bar", false);
-
train_isPageName(resolver, "foo-bar/baz", true);
train_canonicalizePageName(resolver, "foo-bar/baz", "foo-bar/Baz");