Revision: 1019
          http://stripes.svn.sourceforge.net/stripes/?rev=1019&view=rev
Author:   bengunter
Date:     2008-12-23 16:24:56 +0000 (Tue, 23 Dec 2008)

Log Message:
-----------
Applied fix for STS-617 from trunk

Modified Paths:
--------------
    
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java
    
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java
    
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/UrlBindingFactory.java
    branches/1.5.x/stripes/src/net/sourceforge/stripes/util/UrlBuilder.java

Added Paths:
-----------
    
branches/1.5.x/stripes/src/net/sourceforge/stripes/exception/UrlBindingConflictException.java

Modified: 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java
===================================================================
--- 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java
     2008-12-23 16:01:51 UTC (rev 1018)
+++ 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/AnnotatedClassActionResolver.java
     2008-12-23 16:24:56 UTC (rev 1019)
@@ -116,7 +116,6 @@
         }
     }
 
-
     /**
      * Adds an ActionBean class to the set that this resolver can resolve. 
Identifies
      * the URL binding and the events managed by the class and stores them in 
Maps
@@ -161,6 +160,20 @@
     }
 
     /**
+     * Removes an ActionBean class from the set that this resolver can 
resolve. The URL binding
+     * and the events managed by the class are removed from the cache.
+     *
+     * @param clazz a class that implements ActionBean
+     */
+    protected void removeActionBean(Class<? extends ActionBean> clazz) {
+        String binding = getUrlBinding(clazz);
+        if (binding != null) {
+            UrlBindingFactory.getInstance().removeBinding(clazz);
+        }
+        eventMappings.remove(clazz);
+    }
+
+    /**
      * Returns the URL binding that is a substring of the path provided. For 
example, if there
      * is an ActionBean bound to {...@code /user/Profile.action} the path
      * {...@code /user/Profile.action/view} would return {...@code 
/user/Profile.action}.
@@ -172,7 +185,7 @@
      */
     public String getUrlBindingFromPath(String path) {
         UrlBinding mapping = 
UrlBindingFactory.getInstance().getBindingPrototype(path);
-        return mapping == null ? null : mapping.getPath();
+        return mapping == null ? null : mapping.toString();
     }
 
     /**
@@ -186,7 +199,7 @@
      */
     public String getUrlBinding(Class<? extends ActionBean> clazz) {
         UrlBinding mapping = 
UrlBindingFactory.getInstance().getBindingPrototype(clazz);
-        return mapping == null ? null : mapping.getPath();
+        return mapping == null ? null : mapping.toString();
     }
 
     /**
@@ -275,8 +288,7 @@
      */
     public ActionBean getActionBean(ActionBeanContext context) throws 
StripesServletException {
         HttpServletRequest request = context.getRequest();
-        UrlBinding binding = 
UrlBindingFactory.getInstance().getBindingPrototype(request);
-        String path = binding == null ? HttpUtil.getRequestedPath(request) : 
binding.getPath();
+        String path = HttpUtil.getRequestedPath(request);
         ActionBean bean = getActionBean(context, path);
         request.setAttribute(RESOLVED_ACTION, getUrlBindingFromPath(path));
         return bean;
@@ -524,7 +536,8 @@
                                           ActionBeanContext context) {
         Map<String,Method> mappings = this.eventMappings.get(bean);
         String path = HttpUtil.getRequestedPath(context.getRequest());
-        String binding = getUrlBindingFromPath(path);
+        UrlBinding prototype = 
UrlBindingFactory.getInstance().getBindingPrototype(path);
+        String binding = prototype == null ? null : prototype.getPath();
 
         if (binding != null && path.length() != binding.length()) {
             String extra = path.substring(binding.length() + 1);

Modified: 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java
===================================================================
--- 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java
    2008-12-23 16:01:51 UTC (rev 1018)
+++ 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java
    2008-12-23 16:24:56 UTC (rev 1019)
@@ -35,6 +35,7 @@
 import net.sourceforge.stripes.action.FileBean;
 import net.sourceforge.stripes.controller.multipart.MultipartWrapper;
 import net.sourceforge.stripes.exception.StripesServletException;
+import net.sourceforge.stripes.exception.UrlBindingConflictException;
 
 /**
  * HttpServletRequestWrapper that is used to make the file upload 
functionality transparent.
@@ -466,8 +467,15 @@
      * {...@link Map}. If no parameters are present in the URI, then return 
null.
      */
     Map<String, String[]> getUriParameters(HttpServletRequest request) {
+        UrlBinding binding = null;
+        try {
+            binding = UrlBindingFactory.getInstance().getBinding(request);
+        }
+        catch (UrlBindingConflictException e) {
+            // This can be safely ignored
+        }
+
         Map<String, String[]> params = null;
-        UrlBinding binding = 
UrlBindingFactory.getInstance().getBinding(request);
         if (binding != null && binding.getParameters().size() > 0) {
             for (UrlBindingParameter p : binding.getParameters()) {
                 String name = p.getName();

Modified: 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/UrlBindingFactory.java
===================================================================
--- 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/UrlBindingFactory.java
        2008-12-23 16:01:51 UTC (rev 1018)
+++ 
branches/1.5.x/stripes/src/net/sourceforge/stripes/controller/UrlBindingFactory.java
        2008-12-23 16:24:56 UTC (rev 1019)
@@ -22,16 +22,21 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.Map.Entry;
 
 import javax.servlet.http.HttpServletRequest;
 
 import net.sourceforge.stripes.action.ActionBean;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
+import net.sourceforge.stripes.exception.UrlBindingConflictException;
 import net.sourceforge.stripes.util.HttpUtil;
+import net.sourceforge.stripes.util.Log;
 import net.sourceforge.stripes.util.bean.ParseException;
 
 /**
@@ -54,6 +59,8 @@
  * @see UrlBindingParameter
  */
 public class UrlBindingFactory {
+    private static final Log log = Log.getInstance(UrlBindingFactory.class);
+
     /** Singleton instance */
     private static final UrlBindingFactory instance = new UrlBindingFactory();
 
@@ -72,8 +79,11 @@
     /** Maps simple paths to {...@link UrlBinding}s */
     private final Map<String, UrlBinding> pathCache = new HashMap<String, 
UrlBinding>();
 
+    /** Keeps a list of all the paths that could not be cached due to 
conflicts between URL bindings */
+    private final Map<String, List<String>> pathConflicts = new 
HashMap<String, List<String>>();
+
     /** Holds the set of paths that are cached, sorted from longest to 
shortest */
-    private final Map<String, UrlBinding> prefixCache = new TreeMap<String, 
UrlBinding>(
+    private final Map<String, Set<UrlBinding>> prefixCache = new 
TreeMap<String, Set<UrlBinding>>(
             new Comparator<String>() {
                 public int compare(String a, String b) {
                     int cmp = b.length() - a.length();
@@ -123,17 +133,82 @@
     public UrlBinding getBindingPrototype(String uri) {
         // Look for an exact match to the URI first
         UrlBinding prototype = pathCache.get(uri);
-        if (prototype != null)
+        if (prototype != null) {
+            log.debug("Matched ", uri, " to ", prototype);
             return prototype;
+        }
+        else if (pathConflicts.containsKey(uri)) {
+            throw new UrlBindingConflictException(uri, pathConflicts.get(uri));
+        }
 
-        // Then look for a matching prefix
-        for (Entry<String, UrlBinding> entry : prefixCache.entrySet()) {
+        // Get all the bindings whose prefix matches the URI
+        Set<UrlBinding> candidates = null;
+        for (Entry<String, Set<UrlBinding>> entry : prefixCache.entrySet()) {
             if (uri.startsWith(entry.getKey())) {
-                prototype = entry.getValue();
+                candidates = entry.getValue();
                 break;
             }
         }
 
+        // If none matched or exactly one matched then return now
+        if (candidates == null) {
+            log.debug("No URL binding matches ", uri);
+            return null;
+        }
+        else if (candidates.size() == 1) {
+            log.debug("Matched ", uri, " to ", candidates);
+            return candidates.iterator().next();
+        }
+
+        // Now find the one that matches deepest into the URI with the fewest 
components
+        int maxIndex = 0, minComponents = Integer.MAX_VALUE;
+        List<String> conflicts = null;
+        for (UrlBinding binding : candidates) {
+            int idx = binding.getPath().length();
+            List<Object> components = binding.getComponents();
+            int componentCount = components.size();
+
+            for (Object component : components) {
+                if (!(component instanceof String))
+                    continue;
+
+                int at = uri.indexOf((String) component, idx);
+                if (at >= 0) {
+                    idx = at + ((String) component).length();
+                }
+                else {
+                    break;
+                }
+            }
+
+            if (idx == maxIndex) {
+                if (componentCount < minComponents) {
+                    conflicts = null;
+                    minComponents = componentCount;
+                    prototype = binding;
+                }
+                else if (componentCount == minComponents) {
+                    if (conflicts == null) {
+                        conflicts = new ArrayList<String>(candidates.size());
+                        conflicts.add(prototype.toString());
+                    }
+                    conflicts.add(binding.toString());
+                    prototype = null;
+                }
+            }
+            else if (idx > maxIndex) {
+                conflicts = null;
+                minComponents = componentCount;
+                prototype = binding;
+                maxIndex = idx;
+            }
+        }
+
+        log.debug("Matched @", maxIndex, " ", uri, " to ", prototype == null ? 
conflicts : prototype);
+        if (prototype == null) {
+            throw new UrlBindingConflictException(uri, conflicts);
+        }
+
         return prototype;
     }
 
@@ -265,17 +340,188 @@
      * @param binding the URL binding
      */
     public void addBinding(Class<? extends ActionBean> beanType, UrlBinding 
binding) {
-        pathCache.put(binding.getPath(), binding);
+        /*
+         * Search for a class that has already been added with the same name 
as the class being
+         * added now. If one is found then remove its information first and 
then proceed with adding
+         * it. I know this is not technically correct because two classes from 
two different class
+         * loaders can have the same name, but this feature is valuable for 
extensions that reload
+         * classes and I consider it highly unlikely to be a problem in 
practice.
+         */
+        Class<? extends ActionBean> existing = null;
+        for (Class<? extends ActionBean> c : classCache.keySet()) {
+            if (c.getName().equals(beanType.getName())) {
+                existing = c;
+                break;
+            }
+        }
+        if (existing != null)
+            removeBinding(existing);
+
+        // And now we can safely add the class
+        for (String path : getCachedPaths(binding)) {
+            cachePath(path, binding);
+        }
+        for (String prefix : getCachedPrefixes(binding)) {
+            cachePrefix(prefix, binding);
+        }
+        classCache.put(beanType, binding);
+    }
+
+    /**
+     * Removes an {...@link ActionBean}'s URL binding.
+     * 
+     * @param beanType the {...@link ActionBean} class
+     */
+    public synchronized void removeBinding(Class<? extends ActionBean> 
beanType) {
+        UrlBinding binding = classCache.get(beanType);
+        if (binding == null)
+            return;
+
+        Set<UrlBinding> resolvedConflicts = null;
+        for (String path : getCachedPaths(binding)) {
+            log.debug("Clearing cached path ", path, " for ", binding);
+            pathCache.remove(path);
+
+            List<String> conflicts = pathConflicts.get(path);
+            if (conflicts != null) {
+                log.debug("Removing ", binding, " from conflicts list ", 
conflicts);
+                conflicts.remove(binding.toString());
+
+                if (conflicts.size() == 1) {
+                    if (resolvedConflicts == null) {
+                        resolvedConflicts = new LinkedHashSet<UrlBinding>();
+                    }
+
+                    resolvedConflicts.add(pathCache.get(conflicts.get(0)));
+                    conflicts.clear();
+                }
+
+                if (conflicts.isEmpty())
+                    pathConflicts.remove(path);
+            }
+        }
+
+        for (String prefix : getCachedPrefixes(binding)) {
+            Set<UrlBinding> bindings = prefixCache.get(prefix);
+            if (bindings != null) {
+                log.debug("Clearing cached prefix ", prefix, " for ", binding);
+                bindings.remove(binding);
+                if (bindings.isEmpty())
+                    prefixCache.remove(prefix);
+            }
+        }
+
+        classCache.remove(beanType);
+
+        if (resolvedConflicts != null) {
+            log.debug("Resolved conflicts with ", resolvedConflicts);
+
+            for (UrlBinding conflict : resolvedConflicts) {
+                removeBinding(conflict.getBeanType());
+                addBinding(conflict.getBeanType(), conflict);
+            }
+        }
+    }
+
+    /**
+     * Get a list of the request paths that will be wired directly to an 
ActionBean. In some cases,
+     * a single path might be valid for more than one ActionBean. In such a 
case, a warning will be
+     * logged at startup and an exception will be thrown if the conflicting 
path is requested.
+     */
+    protected Set<String> getCachedPaths(UrlBinding binding) {
+        Set<String> paths = new TreeSet<String>();
+
+        // Wire some paths directly to the ActionBean (path, path + /, path + 
suffix, etc.)
+        paths.add(binding.getPath());
+        paths.add(binding.toString());
+        if (!binding.getPath().endsWith("/"))
+            paths.add(binding.getPath() + '/');
         if (binding.getSuffix() != null)
-                       pathCache.put(binding.getPath() + binding.getSuffix(), 
binding);
-        prefixCache.put(binding.getPath() + '/', binding);
+            paths.add(binding.getPath() + binding.getSuffix());
+
+        return paths;
+    }
+
+    /**
+     * Get a list of the request path prefixes that <em>could</em> map to an 
ActionBean. A single
+     * prefix may map to multiple ActionBeans. In such a case, we attempt to 
determine the best
+     * match based on the literal strings and parameters defined in the 
ActionBeans' URL bindings.
+     * If no single ActionBean is determined to be a best match, then an 
exception is thrown to
+     * report the conflict.
+     */
+    protected Set<String> getCachedPrefixes(UrlBinding binding) {
+        Set<String> prefixes = new TreeSet<String>();
+
+        // Add binding as a candidate for some prefixes (path + /, path + 
leading literal, etc.)
+        if (binding.getPath().endsWith("/"))
+            prefixes.add(binding.getPath());
+        else
+            prefixes.add(binding.getPath() + '/');
+
         List<Object> components = binding.getComponents();
-        if (components != null && !components.isEmpty() && components.get(0) 
instanceof String)
-            prefixCache.put(binding.getPath() + components.get(0), binding);
-        classCache.put(beanType, binding);
+        if (components != null && !components.isEmpty() && components.get(0) 
instanceof String) {
+            prefixes.add(binding.getPath() + components.get(0));
+        }
+
+        return prefixes;
     }
 
     /**
+     * Map a path directly to a binding. If the path matches more than one 
binding, then a warning
+     * will be logged indicating such a condition, and the path will not be 
cached for any binding.
+     * 
+     * @param path The path to cache
+     * @param binding The binding to which the path should map
+     */
+    protected void cachePath(String path, UrlBinding binding) {
+        if (pathCache.containsKey(path)) {
+            UrlBinding conflict = pathCache.put(path, null);
+            List<String> list = pathConflicts.get(path);
+            if (list == null) {
+                list = new ArrayList<String>();
+                list.add(conflict.toString());
+                pathConflicts.put(path, list);
+            }
+            log.warn("The path ", path, " for ", 
binding.getBeanType().getName(), " @ ", binding,
+                    " conflicts with ", list);
+            list.add(binding.toString());
+        }
+        else {
+            log.debug("Wiring path ", path, " to ", 
binding.getBeanType().getName(), " @ ", binding);
+            pathCache.put(path, binding);
+        }
+    }
+
+    /**
+     * Add a binding to the set of bindings associated with a prefix.
+     * 
+     * @param prefix The prefix to cache
+     * @param binding The binding to map to the prefix
+     */
+    protected void cachePrefix(String prefix, UrlBinding binding) {
+        log.debug("Wiring prefix ", prefix, "* to ", 
binding.getBeanType().getName(), " @ ", binding);
+
+        // Look up existing set of bindings to which the prefix maps
+        Set<UrlBinding> bindings = prefixCache.get(prefix);
+
+        // If necessary, create and store a new set of bindings
+        if (bindings == null) {
+            bindings = new TreeSet<UrlBinding>(new Comparator<UrlBinding>() {
+                public int compare(UrlBinding o1, UrlBinding o2) {
+                    int cmp = o1.getComponents().size() - 
o2.getComponents().size();
+                    if (cmp == 0)
+                        cmp = o1.toString().compareTo(o2.toString());
+                    return cmp;
+                }
+            });
+            prefixCache.put(prefix, bindings);
+        }
+
+        // Add the binding to the set
+        bindings.add(binding);
+    }
+
+    /**
      * Parse a binding pattern and create a {...@link UrlBinding} object.
      * 
      * @param beanType the {...@link ActionBean} type whose binding is to be 
parsed

Added: 
branches/1.5.x/stripes/src/net/sourceforge/stripes/exception/UrlBindingConflictException.java
===================================================================
--- 
branches/1.5.x/stripes/src/net/sourceforge/stripes/exception/UrlBindingConflictException.java
                               (rev 0)
+++ 
branches/1.5.x/stripes/src/net/sourceforge/stripes/exception/UrlBindingConflictException.java
       2008-12-23 16:24:56 UTC (rev 1019)
@@ -0,0 +1,130 @@
+/* Copyright 2008 Ben Gunter
+ *
+ * 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 net.sourceforge.stripes.exception;
+
+import java.util.Collection;
+
+import net.sourceforge.stripes.action.ActionBean;
+
+/**
+ * <p>
+ * This exception indicates that a URL does not contain enough information to 
map it to a single
+ * {...@link ActionBean} class. In some cases, a URL may match more than one 
URL binding.
+ * </p>
+ * <p>
+ * For example, suppose you have two ActionBeans with the URL bindings 
<code>/foo/{param}/bar</code>
+ * and <code>/foo/{param}/blah</code>. The paths {...@code /foo} and {...@code 
/foo/X} -- while legal,
+ * since any number of parameters or literals may be omitted from the end of a 
clean URL -- match
+ * both of the URL bindings. Since Stripes cannot determine from the URL the 
ActionBean to which to
+ * dispatch the request, it throws this exception to indicate the conflict.
+ * </p>
+ * 
+ * @author Ben Gunter
+ * @since Stripes 1.5.1
+ */
+public class UrlBindingConflictException extends StripesRuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    /** Generate the message to pass to the superclass constructor */
+    protected static String getMessage(Class<? extends ActionBean> 
targetClass, String path,
+            Collection<String> matches) {
+        return (targetClass == null ? "" : "Failure generating URL for " + 
targetClass + ". ")
+                + "The path " + path + " cannot be mapped to a single 
ActionBean because multiple "
+                + "URL bindings match it. The matching URL bindings are " + 
matches + ". If you "
+                + "generated the URL using the Stripes tag library 
(stripes:link, stripes:url, "
+                + "stripes:form, etc.) then you must embed enough 
stripes:param tags within the "
+                + "parent tag to produce a URL that maps to exactly one of the 
indicated matches. "
+                + "If you generated the URL by some other means, then you must 
embed enough "
+                + "information in the URL to achieve the same end.";
+    }
+
+    private String path;
+    private Collection<String> matches;
+    private Class<? extends ActionBean> targetClass;
+
+    /**
+     * New exception indicating that the {...@code path} does not map to a 
single ActionBean because it
+     * potentially matches all the URL bindings in the {...@code matches} 
collection.
+     * 
+     * @param message An informative message about what went wrong
+     * @param targetClass The class for which a URL could not be generated.
+     * @param path The offending path
+     * @param matches A collection of all the potentially matching URL bindings
+     */
+    public UrlBindingConflictException(String message, Class<? extends 
ActionBean> targetClass,
+            String path, Collection<String> matches) {
+        super(message);
+        this.targetClass = targetClass;
+        this.path = path;
+        this.matches = matches;
+    }
+
+    /**
+     * New exception indicating that the {...@code path} does not map to a 
single ActionBean because it
+     * potentially matches all the URL bindings in the {...@code matches} 
collection.
+     * 
+     * @param targetClass The class for which a URL could not be generated.
+     * @param path The offending path
+     * @param matches A collection of all the potentially matching URL bindings
+     */
+    public UrlBindingConflictException(Class<? extends ActionBean> 
targetClass, String path,
+            Collection<String> matches) {
+        this(getMessage(targetClass, path, matches), targetClass, path, 
matches);
+    }
+
+    /**
+     * New exception indicating that the {...@code path} does not map to a 
single ActionBean because it
+     * potentially matches all the URL bindings in the {...@code matches} 
collection.
+     * 
+     * @param message An informative message about what went wrong
+     * @param path The offending path
+     * @param matches A collection of all the potentially matching URL bindings
+     */
+    public UrlBindingConflictException(String message, String path, 
Collection<String> matches) {
+        this(message, null, path, matches);
+    }
+
+    /**
+     * New exception indicating that the {...@code path} does not map to a 
single ActionBean because it
+     * potentially matches all the URL bindings in the {...@code matches} 
collection.
+     * 
+     * @param path The offending path
+     * @param matches A collection of all the potentially matching URL bindings
+     */
+    public UrlBindingConflictException(String path, Collection<String> 
matches) {
+        this(getMessage(null, path, matches), path, matches);
+    }
+
+    /** Get the path that failed to map to a single ActionBean */
+    public String getPath() {
+        return path;
+    }
+
+    /** Get all the URL bindings on existing ActionBeans that match the path */
+    public Collection<String> getMatches() {
+        return matches;
+    }
+
+    /**
+     * Get the {...@link ActionBean} class for which a URL was being generated 
when this exception was
+     * thrown. If the exception occurred while dispatching a request, then 
this property will be
+     * null since the path cannot be associated with an ActionBean class. 
However, if it is thrown
+     * while generating a URL that is intended to point to an ActionBean, then 
this property will
+     * indicate the class that was being targeted.
+     */
+    public Class<? extends ActionBean> getTargetClass() {
+        return targetClass;
+    }
+}

Modified: 
branches/1.5.x/stripes/src/net/sourceforge/stripes/util/UrlBuilder.java
===================================================================
--- branches/1.5.x/stripes/src/net/sourceforge/stripes/util/UrlBuilder.java     
2008-12-23 16:01:51 UTC (rev 1018)
+++ branches/1.5.x/stripes/src/net/sourceforge/stripes/util/UrlBuilder.java     
2008-12-23 16:24:56 UTC (rev 1019)
@@ -30,6 +30,7 @@
 import net.sourceforge.stripes.controller.UrlBindingFactory;
 import net.sourceforge.stripes.controller.UrlBindingParameter;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
+import net.sourceforge.stripes.exception.UrlBindingConflictException;
 import net.sourceforge.stripes.format.Formatter;
 import net.sourceforge.stripes.format.FormatterFactory;
 import net.sourceforge.stripes.validation.ValidationMetadata;
@@ -366,8 +367,14 @@
         Map<String, ValidationMetadata> validations = null;
         Configuration configuration = StripesFilter.getConfiguration();
         if (configuration != null) {
-            Class<? extends ActionBean> beanType = 
configuration.getActionResolver()
-                    .getActionBeanType(this.baseUrl);
+            Class<? extends ActionBean> beanType = null;
+            try {
+                beanType = 
configuration.getActionResolver().getActionBeanType(this.baseUrl);
+            }
+            catch (UrlBindingConflictException e) {
+                // This can be safely ignored
+            }
+
             if (beanType != null) {
                 validations = 
configuration.getValidationMetadataProvider().getValidationMetadata(
                         beanType);
@@ -443,11 +450,23 @@
      * @see #UrlBuilder(Locale, String, boolean)
      */
     protected String getBaseURL(String baseUrl, Collection<Parameter> 
parameters) {
-        UrlBinding binding = 
UrlBindingFactory.getInstance().getBindingPrototype(baseUrl);
+        UrlBinding binding = null;
+        try {
+            binding = 
UrlBindingFactory.getInstance().getBindingPrototype(baseUrl);
+        }
+        catch (UrlBindingConflictException e) {
+            // This can be safely ignored
+        }
+
         if (binding == null || binding.getParameters().size() == 0) {
             return baseUrl;
         }
 
+        // if we have a parameterized binding then we need to trim it down to 
the path
+        if (baseUrl.equals(binding.toString())) {
+            baseUrl = binding.getPath();
+        }
+
         // if any extra path info is present then do not add URI parameters
         if (binding.getPath().length() < baseUrl.length()) {
             return baseUrl;
@@ -520,6 +539,20 @@
             buf.append(binding.getSuffix());
         }
 
-        return buf.toString();
+        // Test the URL to make sure it won't throw an exception when Stripes 
tries to dispatch it
+        String url = buf.toString();
+        try {
+            
StripesFilter.getConfiguration().getActionResolver().getActionBeanType(url);
+        }
+        catch (UrlBindingConflictException e) {
+            if (binding != null) {
+                UrlBindingConflictException tmp = new 
UrlBindingConflictException(binding
+                        .getBeanType(), e.getPath(), e.getMatches());
+                tmp.setStackTrace(e.getStackTrace());
+                e = tmp;
+            }
+            throw e;
+        }
+        return url;
     }
 }


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to