Author: markt
Date: Fri Mar 29 21:34:18 2013
New Revision: 1462653

URL: http://svn.apache.org/r1462653
Log:
Switch to a Filter based rather than Servlet based implementation and fully 
implement the mapping rules for WebSocket requests.
Added:
    tomcat/trunk/java/org/apache/tomcat/websocket/server/WsFilter.java
      - copied, changed from r1461327, 
tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java
    tomcat/trunk/java/org/apache/tomcat/websocket/server/WsMappingResult.java   
(with props)
Removed:
    tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java
Modified:
    tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java
    tomcat/trunk/java/org/apache/tomcat/websocket/server/UriTemplate.java
    tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServerContainer.java
    tomcat/trunk/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java
    tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
    tomcat/trunk/test/org/apache/tomcat/websocket/TesterEchoServer.java
    tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java
    tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoEndpointBase.java
    
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java
    tomcat/trunk/test/org/apache/tomcat/websocket/server/TestUriTemplate.java

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java Fri Mar 
29 21:34:18 2013
@@ -23,7 +23,6 @@ public class Constants {
 
     protected static final String PACKAGE_NAME =
             Constants.class.getPackage().getName();
-    protected static final String SERVLET_NAME = WsServlet.class.getName();
 
     public static final String BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM =
             "org.apache.tomcat.websocket.binaryBufferSize";

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/server/UriTemplate.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/UriTemplate.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/server/UriTemplate.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/server/UriTemplate.java Fri 
Mar 29 21:34:18 2013
@@ -17,14 +17,10 @@
 package org.apache.tomcat.websocket.server;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.tomcat.util.res.StringManager;
 
 /**
  * Extracts path parameters from URIs used to create web socket connections
@@ -32,68 +28,119 @@ import org.apache.tomcat.util.res.String
  */
 public class UriTemplate {
 
-    private static final StringManager sm =
-            StringManager.getManager(Constants.PACKAGE_NAME);
+    private final String normalized;
+    private final List<Segment> segments = new ArrayList<>();
+    private final boolean hasParameters;
+
+
+    public UriTemplate(String path) {
+        StringBuilder normalized = new StringBuilder(path.length());
+
+        String[] segments = path.split("/");
+        int paramCount = 0;
+        int segmentCount = 0;
+
+        for (int i = 0; i < segments.length; i++) {
+            String segment = segments[i];
+            if (segment.length() == 0) {
+                continue;
+            }
+            normalized.append('/');
+            int index = -1;
+            if (segment.startsWith("{") && segment.endsWith("}")) {
+                index = segmentCount;
+                segment = segment.substring(1, segment.length() - 1);
+                normalized.append('{');
+                normalized.append(paramCount++);
+                normalized.append('}');
+            } else {
+                if (segment.contains("{") || segment.contains("}")) {
+                    // TODO i18n
+                    throw new IllegalArgumentException();
+                }
+                normalized.append(segment);
+            }
+            this.segments.add(new Segment(index, segment));
+            segmentCount++;
+        }
 
-    private final String template;
-    private final Pattern pattern;
-    private final List<String> names = new ArrayList<>();
-
-
-    public UriTemplate(String template) {
-        this.template = template;
-        // +10 is just a guess at this point
-        StringBuilder pattern = new StringBuilder(template.length() + 10);
-        int pos = 0;
-        int end = 0;
-        int start = template.indexOf('{');
-        while (start > -1) {
-            end = template.indexOf('}', start);
-            pattern.append('(');
-            pattern.append(Pattern.quote(template.substring(pos, start)));
-            pattern.append(")([^/]*)");
-            names.add(template.substring(start + 1, end));
-            pos = end + 1;
-            start = template.indexOf('{', pos);
-        }
-        // No more matches, append current position to end
-        if (pos < template.length()) {
-            pattern.append('(');
-            pattern.append(template.substring(pos));
-            pattern.append(")?");
-        }
-        this.pattern = Pattern.compile(pattern.toString());
+        this.normalized = normalized.toString();
+        this.hasParameters = paramCount > 0;
     }
 
 
-    public boolean contains(String name) {
-        return names.contains(name);
-    }
-
+    public Map<String,String> match(UriTemplate candidate) {
 
-    /**
-     * Extract the path parameters from the provided pathInfo based on the
-     * template with which this UriTemplate was constructed.
-     *
-     * @param pathInfo The pathInfo from which the path parameters are to be
-     *            extracted
-     * @return A map of parameter names to values
-     */
-    public Map<String,String> match(String pathInfo) {
         Map<String,String> result = new HashMap<>();
-        Matcher m = pattern.matcher(pathInfo);
-        if (!m.matches()) {
-            throw new IllegalArgumentException(sm.getString(
-                    "uriTemplate.noMatch", template, pattern, pathInfo));
-        }
-        int group = 2;
-        for (String name : names) {
-            String value = m.group(group);
-            if (value != null && value.length() > 0) {
-                result.put(name, value);
+
+        // Should not happen but for safety
+        if (candidate.getSegmentCount() != getSegmentCount()) {
+            return null;
+        }
+
+        Iterator<Segment> candidateSegments =
+                candidate.getSegments().iterator();
+        Iterator<Segment> targetSegments = segments.iterator();
+
+        while (candidateSegments.hasNext()) {
+            Segment candidateSegment = candidateSegments.next();
+            Segment targetSegment = targetSegments.next();
+
+            if (targetSegment.getParameterIndex() == -1) {
+                // Not a parameter - values must match
+                if (!targetSegment.getValue().equals(
+                        candidateSegment.getValue())) {
+                    // Not a match. Stop here
+                    return null;
+                }
+            } else {
+                // Parameter
+                result.put(targetSegment.getValue(),
+                        candidateSegment.getValue());
             }
-            group += 2;
         }
-        return Collections.unmodifiableMap(result);
+
+        return result;
+    }
+
+
+    public boolean hasParameters() {
+        return hasParameters;
+    }
+
+
+    public int getSegmentCount() {
+        return segments.size();
+    }
+
+
+    public String getNormalizedPath() {
+        return normalized;
+    }
+
+
+    private List<Segment> getSegments() {
+        return segments;
+    }
+
+
+    private static class Segment {
+        private final int parameterIndex;
+        private final String value;
+
+        public Segment(int parameterIndex, String value) {
+            this.parameterIndex = parameterIndex;
+            this.value = value;
+        }
+
+
+        public int getParameterIndex() {
+            return parameterIndex;
+        }
+
+
+        public String getValue() {
+            return value;
+        }
     }
 }

Copied: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsFilter.java 
(from r1461327, 
tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java)
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsFilter.java?p2=tomcat/trunk/java/org/apache/tomcat/websocket/server/WsFilter.java&p1=tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java&r1=1461327&r2=1462653&rev=1462653&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsFilter.java Fri Mar 
29 21:34:18 2013
@@ -25,13 +25,16 @@ import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.websocket.Endpoint;
@@ -47,29 +50,49 @@ import org.apache.tomcat.websocket.pojo.
 /**
  * Handles the initial HTTP connection for WebSocket connections.
  */
-public class WsServlet extends HttpServlet {
+public class WsFilter implements Filter {
 
-    private static final long serialVersionUID = 1L;
     private static final Charset ISO_8859_1;
     static {
         ISO_8859_1 = Charset.forName("ISO-8859-1");
     }
-    private static final byte[] WS_ACCEPT = 
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(ISO_8859_1);
-    private final Queue<MessageDigest> sha1Helpers = new 
ConcurrentLinkedQueue<>();
+    private static final byte[] WS_ACCEPT =
+            "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(ISO_8859_1);
+    private final Queue<MessageDigest> sha1Helpers =
+            new ConcurrentLinkedQueue<>();
 
 
     @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
-            throws ServletException, IOException {
-        // Information required to send the server handshake message
+    public void init(FilterConfig filterConfig) throws ServletException {
+        // NO-OP
+    }
+
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+
+        // This filter only needs to handle WebSocket upgrade requests
+        if (!(request instanceof HttpServletRequest) ||
+                !(response instanceof HttpServletResponse) ||
+                !headerContainsToken((HttpServletRequest) request,
+                        Constants.UPGRADE_HEADER_NAME,
+                        Constants.UPGRADE_HEADER_VALUE)) {
+            // Note an HTTP request that includes a valid upgrade request to
+            // web socket
+            chain.doFilter(request, response);
+            return;
+        }
+
+        // HTTP request with an upgrade header for WebSocket present
+        // Validate the rest of the headers and reject the request if that
+        // validation fails
+        HttpServletRequest req = (HttpServletRequest) request;
+        HttpServletResponse resp = (HttpServletResponse) response;
+
         String key;
         String subProtocol = null;
         List<Extension> extensions = Collections.emptyList();
-        if (!headerContainsToken(req, Constants.UPGRADE_HEADER_NAME,
-                Constants.UPGRADE_HEADER_VALUE)) {
-            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
-            return;
-        }
         if (!headerContainsToken(req, Constants.CONNECTION_HEADER_NAME,
                 Constants.CONNECTION_HEADER_VALUE)) {
             resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
@@ -87,12 +110,20 @@ public class WsServlet extends HttpServl
             resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
             return;
         }
+
         // Need an Endpoint instance to progress this further
         WsServerContainer sc = WsServerContainer.getServerContainer();
-        Map<String,String> pathParameters = sc.getPathParameters(
-                req.getServletPath(),  req.getPathInfo());
-        ServerEndpointConfig sec = sc.getServerEndpointConfiguration(
-                req.getServletPath(), pathParameters);
+        String path;
+        String pathInfo = req.getPathInfo();
+        if (pathInfo == null) {
+            path = req.getServletPath();
+        } else {
+            path = req.getServletPath() + pathInfo;
+        }
+        WsMappingResult mappingResult = sc.findMapping(path);
+
+        ServerEndpointConfig sec = mappingResult.getConfig();
+
         // Origin check
         String origin = req.getHeader("Origin");
         if (!sec.getConfigurator().checkOrigin(origin)) {
@@ -136,7 +167,7 @@ public class WsServlet extends HttpServl
             Class<?> clazz = sec.getEndpointClass();
             if (Endpoint.class.isAssignableFrom(clazz)) {
                 ep = (Endpoint) sec.getConfigurator().getEndpointInstance(
-                        sec.getEndpointClass());
+                        clazz);
             } else {
                 ep = new PojoEndpointServer();
             }
@@ -159,8 +190,15 @@ public class WsServlet extends HttpServl
 
         WsHttpUpgradeHandler wsHandler =
                 req.upgrade(WsHttpUpgradeHandler.class);
-        wsHandler.preInit(ep, sec, sc, wsRequest, subProtocol, pathParameters,
-                req.isSecure());
+        wsHandler.preInit(ep, sec, sc, wsRequest, subProtocol,
+                mappingResult.getPathParams(), req.isSecure());
+
+    }
+
+
+    @Override
+    public void destroy() {
+        // NO-OP
     }
 
 

Added: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsMappingResult.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsMappingResult.java?rev=1462653&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/server/WsMappingResult.java 
(added)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsMappingResult.java 
Fri Mar 29 21:34:18 2013
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.tomcat.websocket.server;
+
+import java.util.Map;
+
+import javax.websocket.server.ServerEndpointConfig;
+
+class WsMappingResult {
+
+    private final ServerEndpointConfig config;
+    private final Map<String,String> pathParams;
+
+
+    WsMappingResult(ServerEndpointConfig config,
+            Map<String,String> pathParams) {
+        this.config = config;
+        this.pathParams = pathParams;
+    }
+
+
+    ServerEndpointConfig getConfig() {
+        return config;
+    }
+
+
+    Map<String,String> getPathParams() {
+        return pathParams;
+    }
+}

Propchange: 
tomcat/trunk/java/org/apache/tomcat/websocket/server/WsMappingResult.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServerContainer.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServerContainer.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServerContainer.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServerContainer.java 
Fri Mar 29 21:34:18 2013
@@ -18,20 +18,21 @@ package org.apache.tomcat.websocket.serv
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
 
+import javax.servlet.FilterRegistration;
 import javax.servlet.ServletContext;
-import javax.servlet.ServletRegistration;
 import javax.websocket.DeploymentException;
 import javax.websocket.server.ServerContainer;
 import javax.websocket.server.ServerEndpoint;
 import javax.websocket.server.ServerEndpointConfig;
 import javax.websocket.server.ServerEndpointConfig.Configurator;
 
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.res.StringManager;
 import org.apache.tomcat.websocket.WsSession;
 import org.apache.tomcat.websocket.WsWebSocketContainer;
@@ -58,7 +59,6 @@ public class WsServerContainer extends W
     private static final Object classLoaderContainerMapLock = new Object();
     private static final StringManager sm =
             StringManager.getManager(Constants.PACKAGE_NAME);
-    private final Log log = LogFactory.getLog(WsServerContainer.class);
 
 
     public static WsServerContainer getServerContainer() {
@@ -77,13 +77,10 @@ public class WsServerContainer extends W
     private final WsWriteTimeout wsWriteTimeout = new WsWriteTimeout();
 
     private volatile ServletContext servletContext = null;
-    private final Map<String,ServerEndpointConfig> configMap =
-            new ConcurrentHashMap<>();
-    private final Map<String,UriTemplate> templateMap =
-            new ConcurrentHashMap<>();
-    private final Map<String,Class<?>> pojoMap = new ConcurrentHashMap<>();
-    private final Map<Class<?>,PojoMethodMapping> pojoMethodMap =
-            new ConcurrentHashMap<>();
+    private final Map<String,ServerEndpointConfig> configExactMatchMap =
+            new HashMap<>();
+    private final Map<Integer,SortedSet<TemplatePathMatch>>
+            configTemplateMatchMap = new HashMap<>();
 
 
     private WsServerContainer() {
@@ -111,6 +108,10 @@ public class WsServerContainer extends W
         if (value != null) {
             setDefaultMaxTextMessageBufferSize(Integer.parseInt(value));
         }
+
+        FilterRegistration fr = servletContext.addFilter(
+                WsFilter.class.getName(), WsFilter.class);
+        fr.addMappingForUrlPatterns(null, false, "/*");
     }
 
 
@@ -130,23 +131,21 @@ public class WsServerContainer extends W
                     sm.getString("serverContainer.servletContextMissing"));
         }
         String path = sec.getPath();
-        String servletPath = getServletPath(path);
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("serverContainer.endpointDeploy",
-                    sec.getEndpointClass(), path,
-                    servletContext.getContextPath()));
-        }
-
-        // Remove the trailing /* before adding it to the map
-        String mapPath = servletPath.substring(0, servletPath.length() - 2);
 
-        if (path.length() > servletPath.length()) {
-            templateMap.put(mapPath,
-                    new UriTemplate(path.substring(mapPath.length())));
+        UriTemplate uriTemplate = new UriTemplate(path);
+        if (uriTemplate.hasParameters()) {
+            Integer key = Integer.valueOf(uriTemplate.getSegmentCount());
+            SortedSet<TemplatePathMatch> templateMatches =
+                    configTemplateMatchMap.get(key);
+            if (templateMatches == null) {
+                templateMatches = new TreeSet<>();
+                configTemplateMatchMap.put(key, templateMatches);
+            }
+            templateMatches.add(new TemplatePathMatch(sec, uriTemplate));
+        } else {
+            // Exact match
+            configExactMatchMap.put(path, sec);
         }
-
-        configMap.put(mapPath, sec);
-        addWsServletMapping(servletPath);
     }
 
 
@@ -166,94 +165,97 @@ public class WsServerContainer extends W
                     sm.getString("serverContainer.missingAnnotation",
                             pojo.getName()));
         }
-        String wsPath = annotation.value();
-
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("serverContainer.pojoDeploy",
-                    pojo.getName(), wsPath, servletContext.getContextPath()));
-        }
+        String path = annotation.value();
 
-        String servletPath = getServletPath(wsPath);
-        // Remove the trailing /* before adding it to the map
-        String mapPath = servletPath.substring(0, servletPath.length() - 2);
+        // Uri Template
+        UriTemplate uriTemplate = new UriTemplate(path);
 
-        if (wsPath.length() > servletPath.length()) {
-            templateMap.put(mapPath,
-                    new UriTemplate(wsPath.substring(mapPath.length())));
+        // Method mapping
+        PojoMethodMapping methodMapping = new PojoMethodMapping(pojo,
+                annotation.decoders(), path);
+
+        // ServerEndpointConfig
+        ServerEndpointConfig sec;
+        Class<? extends Configurator> configuratorClazz =
+                annotation.configurator();
+        Configurator configurator = null;
+        if (!configuratorClazz.equals(Configurator.class)) {
+            try {
+                configurator = annotation.configurator().newInstance();
+            } catch (InstantiationException | IllegalAccessException e) {
+                throw new IllegalStateException(sm.getString(
+                        "serverContainer.configuratorFail",
+                        annotation.configurator().getName(),
+                        pojo.getClass().getName()), e);
+            }
         }
-
-        pojoMap.put(mapPath, pojo);
-        pojoMethodMap.put(pojo,
-                new PojoMethodMapping(pojo, annotation.decoders(), wsPath));
-        addWsServletMapping(servletPath);
-    }
-
-
-    private void addWsServletMapping(String servletPath) {
-        ServletRegistration sr =
-                servletContext.getServletRegistration(Constants.SERVLET_NAME);
-        if (sr == null) {
-            sr = servletContext.addServlet(Constants.SERVLET_NAME,
-                    WsServlet.class);
+        sec = ServerEndpointConfig.Builder.create(pojo, path).
+                decoders(Arrays.asList(annotation.decoders())).
+                encoders(Arrays.asList(annotation.encoders())).
+                configurator(configurator).
+                build();
+        sec.getUserProperties().put(
+                PojoEndpointServer.POJO_METHOD_MAPPING_KEY,
+                methodMapping);
+
+
+        if (uriTemplate.hasParameters()) {
+            Integer key = Integer.valueOf(uriTemplate.getSegmentCount());
+            SortedSet<TemplatePathMatch> templateMatches =
+                    configTemplateMatchMap.get(key);
+            if (templateMatches == null) {
+                templateMatches =
+                        new TreeSet<>(new TemplatePathMatchComparator());
+                configTemplateMatchMap.put(key, templateMatches);
+            }
+            templateMatches.add(new TemplatePathMatch(sec, uriTemplate));
+        } else {
+            // Exact match
+            configExactMatchMap.put(path, sec);
         }
-        sr.addMapping(servletPath);
     }
 
 
-    public ServerEndpointConfig getServerEndpointConfiguration(
-            String servletPath, Map<String,String> pathParameters) {
-        ServerEndpointConfig sec = configMap.get(servletPath);
+    public WsMappingResult findMapping(String path) {
+
+        // Check an exact match. Simple case as there are no templates.
+        ServerEndpointConfig sec = configExactMatchMap.get(path);
         if (sec != null) {
-            return sec;
+            return new WsMappingResult(sec, Collections.EMPTY_MAP);
         }
-        Class<?> pojo = pojoMap.get(servletPath);
-        if (pojo != null) {
-            ServerEndpoint annotation =
-                    pojo.getAnnotation(ServerEndpoint.class);
-            PojoMethodMapping methodMapping = pojoMethodMap.get(pojo);
-            if (methodMapping != null) {
-                Class<? extends Configurator> configuratorClazz =
-                        annotation.configurator();
-                Configurator configurator = null;
-                if (!configuratorClazz.equals(Configurator.class)) {
-                    try {
-                        configurator = annotation.configurator().newInstance();
-                    } catch (InstantiationException |
-                            IllegalAccessException e) {
-                        throw new IllegalStateException(sm.getString(
-                                "serverContainer.configuratorFail",
-                                annotation.configurator().getName(),
-                                pojo.getClass().getName()), e);
-                    }
-                }
-                sec = ServerEndpointConfig.Builder.create(
-                        pojo, methodMapping.getWsPath()).
-                        decoders(Arrays.asList(annotation.decoders())).
-                        encoders(Arrays.asList(annotation.encoders())).
-                        configurator(configurator).
-                        build();
-                sec.getUserProperties().put(
-                        PojoEndpointServer.POJO_PATH_PARAM_KEY,
-                        pathParameters);
-                sec.getUserProperties().put(
-                        PojoEndpointServer.POJO_METHOD_MAPPING_KEY,
-                        methodMapping);
-                return sec;
+
+        // No exact match. Need to look for template matches.
+        UriTemplate pathUriTemplate = new UriTemplate(path);
+
+        // Number of segments has to match
+        Integer key = Integer.valueOf(pathUriTemplate.getSegmentCount());
+        SortedSet<TemplatePathMatch> templateMatches =
+                configTemplateMatchMap.get(key);
+
+        // List is in alphabetical order of normalised templates.
+        // Correct match is the first one that matches.
+        Map<String,String> pathParams = null;
+        for (TemplatePathMatch templateMatch : templateMatches) {
+            pathParams = templateMatch.getUriTemplate().match(pathUriTemplate);
+            if (pathParams != null) {
+                sec = templateMatch.getConfig();
+                break;
             }
         }
-        throw new IllegalStateException(sm.getString(
-                "serverContainer.missingEndpoint", servletPath));
-    }
 
+        if (sec == null) {
+            // No match
+            return null;
+        }
 
-    public Map<String,String> getPathParameters(String servletPath,
-            String pathInfo) {
-        UriTemplate template = templateMap.get(servletPath);
-        if (template == null) {
-            return Collections.EMPTY_MAP;
-        } else {
-            return template.match(pathInfo);
+        if 
(!PojoEndpointServer.class.isAssignableFrom(sec.getEndpointClass())) {
+            // Need to make path params available to POJO
+            sec.getUserProperties().put(
+                    PojoEndpointServer.POJO_PATH_PARAM_KEY,
+                    pathParams);
         }
+
+        return new WsMappingResult(sec, pathParams);
     }
 
 
@@ -308,4 +310,37 @@ public class WsServerContainer extends W
             }
         }
     }
+
+
+    private static class TemplatePathMatch {
+        private final ServerEndpointConfig config;
+        private final UriTemplate uriTemplate;
+
+        public TemplatePathMatch(ServerEndpointConfig config,
+                UriTemplate uriTemplate) {
+            this.config = config;
+            this.uriTemplate = uriTemplate;
+        }
+
+
+        public ServerEndpointConfig getConfig() {
+            return config;
+        }
+
+
+        public UriTemplate getUriTemplate() {
+            return uriTemplate;
+        }
+    }
+
+
+    private static class TemplatePathMatchComparator
+            implements Comparator<TemplatePathMatch> {
+
+        @Override
+        public int compare(TemplatePathMatch tpm1, TemplatePathMatch tpm2) {
+            return tpm1.getUriTemplate().getNormalizedPath().compareTo(
+                    tpm2.getUriTemplate().getNormalizedPath());
+        }
+    }
 }

Modified: 
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java 
(original)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java Fri 
Mar 29 21:34:18 2013
@@ -32,6 +32,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.websocket.TesterSingleMessageClient.AsyncHandler;
@@ -69,6 +70,8 @@ public class TestWsRemoteEndpoint extend
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();

Modified: 
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java 
(original)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java 
Fri Mar 29 21:34:18 2013
@@ -43,6 +43,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.coyote.http11.Http11Protocol;
@@ -79,12 +80,15 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         tomcat.start();
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();
-        Session wsSession = 
wsContainer.connectToServer(TesterProgrammaticEndpoint.class,
+        Session wsSession = wsContainer.connectToServer(
+                TesterProgrammaticEndpoint.class,
                 ClientEndpointConfig.Builder.create().build(),
                 new URI("ws://localhost:" + getPort() +
                         TesterEchoServer.Config.PATH_ASYNC));
@@ -196,6 +200,8 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();
@@ -282,6 +288,8 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(BlockingConfig.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();
@@ -368,6 +376,8 @@ public class TestWsWebSocketContainer ex
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(WsListener.class.getName());
         ctx.addApplicationListener(ConstantTxConfig.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();
@@ -544,6 +554,8 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         tomcat.start();
 
@@ -586,6 +598,8 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         tomcat.start();
 
@@ -638,6 +652,8 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         tomcat.start();
 
@@ -720,6 +736,8 @@ public class TestWsWebSocketContainer ex
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         TesterSupport.initSsl(tomcat);
 

Modified: tomcat/trunk/test/org/apache/tomcat/websocket/TesterEchoServer.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TesterEchoServer.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/TesterEchoServer.java 
(original)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/TesterEchoServer.java Fri Mar 
29 21:34:18 2013
@@ -39,7 +39,6 @@ public class TesterEchoServer {
         public void contextInitialized(ServletContextEvent sce) {
             super.contextInitialized(sce);
             WsServerContainer sc = WsServerContainer.getServerContainer();
-            sc.setServletContext(sce.getServletContext());
             try {
                 sc.addEndpoint(Async.class);
                 sc.addEndpoint(Basic.class);

Modified: 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java 
Fri Mar 29 21:34:18 2013
@@ -36,6 +36,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.websocket.pojo.Util.ServerConfigListener;
@@ -58,6 +59,8 @@ public class TestEncodingDecoding extend
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(ServerConfigListener.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();

Modified: 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoEndpointBase.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoEndpointBase.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoEndpointBase.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoEndpointBase.java 
Fri Mar 29 21:34:18 2013
@@ -31,6 +31,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.websocket.pojo.Util.ServerConfigListener;
@@ -51,6 +52,8 @@ public class TestPojoEndpointBase extend
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(ServerConfigListener.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();

Modified: 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/websocket/pojo/TestPojoMethodMapping.java 
Fri Mar 29 21:34:18 2013
@@ -33,6 +33,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.websocket.pojo.Util.ServerConfigListener;
@@ -58,6 +59,8 @@ public class TestPojoMethodMapping exten
         Context ctx =
             tomcat.addContext("", System.getProperty("java.io.tmpdir"));
         ctx.addApplicationListener(ServerConfigListener.class.getName());
+        Tomcat.addServlet(ctx, "default", new DefaultServlet());
+        ctx.addServletMapping("/", "default");
 
         WebSocketContainer wsContainer =
                 ContainerProvider.getWebSocketContainer();

Modified: 
tomcat/trunk/test/org/apache/tomcat/websocket/server/TestUriTemplate.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/server/TestUriTemplate.java?rev=1462653&r1=1462652&r2=1462653&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/server/TestUriTemplate.java 
(original)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/server/TestUriTemplate.java 
Fri Mar 29 21:34:18 2013
@@ -26,7 +26,7 @@ public class TestUriTemplate {
     @Test
     public void testBasic() throws Exception {
         UriTemplate t = new UriTemplate("/{a}/{b}");
-        Map<String,String> result = t.match("/foo/bar");
+        Map<String,String> result = t.match(new UriTemplate("/foo/bar"));
 
         Assert.assertEquals(2, result.size());
         Assert.assertTrue(result.containsKey("a"));
@@ -36,62 +36,53 @@ public class TestUriTemplate {
     }
 
 
-    @Test(expected=java.lang.IllegalArgumentException.class)
+    @Test
     public void testOneOfTwo() throws Exception {
         UriTemplate t = new UriTemplate("/{a}/{b}");
-        t.match("/foo");
+        Map<String,String> result = t.match(new UriTemplate("/foo"));
+        Assert.assertNull(result);
     }
 
 
-    @Test
+    @Test(expected=java.lang.IllegalArgumentException.class)
     public void testBasicPrefix() throws Exception {
+        @SuppressWarnings("unused")
         UriTemplate t = new UriTemplate("/x{a}/y{b}");
-        Map<String,String> result = t.match("/xfoo/ybar");
-
-        Assert.assertEquals(2, result.size());
-        Assert.assertTrue(result.containsKey("a"));
-        Assert.assertTrue(result.containsKey("b"));
-        Assert.assertEquals("foo", result.get("a"));
-        Assert.assertEquals("bar", result.get("b"));
     }
 
 
     @Test(expected=java.lang.IllegalArgumentException.class)
     public void testPrefixOneOfTwo() throws Exception {
         UriTemplate t = new UriTemplate("/x{a}/y{b}");
-        t.match("/xfoo");
+        t.match(new UriTemplate("/xfoo"));
     }
 
 
     @Test(expected=java.lang.IllegalArgumentException.class)
     public void testPrefixTwoOfTwo() throws Exception {
         UriTemplate t = new UriTemplate("/x{a}/y{b}");
-        t.match("/ybar");
+        t.match(new UriTemplate("/ybar"));
     }
 
 
     @Test(expected=java.lang.IllegalArgumentException.class)
     public void testQuote1() throws Exception {
         UriTemplate t = new UriTemplate("/.{a}");
-        t.match("/yfoo");
+        t.match(new UriTemplate("/yfoo"));
     }
 
 
-    @Test
+    @Test(expected=java.lang.IllegalArgumentException.class)
     public void testQuote2() throws Exception {
+        @SuppressWarnings("unused")
         UriTemplate t = new UriTemplate("/.{a}");
-        Map<String,String> result = t.match("/.foo");
-
-        Assert.assertEquals(1, result.size());
-        Assert.assertTrue(result.containsKey("a"));
-        Assert.assertEquals("foo", result.get("a"));
     }
 
 
     @Test
     public void testNoParams() throws Exception {
         UriTemplate t = new UriTemplate("/foo/bar");
-        Map<String,String> result = t.match("/foo/bar");
+        Map<String,String> result = t.match(new UriTemplate("/foo/bar"));
 
         Assert.assertEquals(0, result.size());
     }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to