[seam-commits] Seam SVN: r7488 - in trunk/src: test/unit/org/jboss/seam/test/unit and 1 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Tue Feb 26 14:18:38 EST 2008


Author: norman.richards at jboss.com
Date: 2008-02-26 14:18:37 -0500 (Tue, 26 Feb 2008)
New Revision: 7488

Added:
   trunk/src/main/org/jboss/seam/web/IncomingPattern.java
   trunk/src/main/org/jboss/seam/web/OutgoingPattern.java
   trunk/src/main/org/jboss/seam/web/Pattern.java
   trunk/src/main/org/jboss/seam/web/Rewrite.java
   trunk/src/main/org/jboss/seam/web/RewriteFilter.java
   trunk/src/main/org/jboss/seam/web/RewritingResponse.java
   trunk/src/test/unit/org/jboss/seam/test/unit/web/RewriteTest.java
Modified:
   trunk/src/test/unit/org/jboss/seam/test/unit/testng.xml
Log:
JBSEAM-274

Added: trunk/src/main/org/jboss/seam/web/IncomingPattern.java
===================================================================
--- trunk/src/main/org/jboss/seam/web/IncomingPattern.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/web/IncomingPattern.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,149 @@
+package org.jboss.seam.web;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+
+public class IncomingPattern {
+    String view;
+    String pattern;
+    
+    java.util.regex.Pattern regexp;
+    List<String> regexpArgs = new ArrayList<String>();
+    
+    public IncomingPattern(String view, String pattern) {
+        this.view = view;
+        this.pattern = pattern;
+        
+        parsePattern(pattern);
+    }
+    
+    public Rewrite rewrite(String path) {
+           return new IncomingRewrite(path);
+    }
+
+    public void parsePattern(String value) {
+        StringBuffer expr = new StringBuffer();
+          
+        expr.append("^");
+        while (value.length()>0) {
+            int pos = value.indexOf('{');
+            if (pos == -1) {
+                expr.append(regexpLiteral(value));
+                value = "";
+            } else {
+                int pos2 = value.indexOf('}');
+                if (pos2 == -1) {
+                    throw new IllegalArgumentException("invalid pattern");
+                }
+                expr.append(regexpLiteral(value.substring(0,pos)));
+                String arg = value.substring(pos+1,pos2);
+                expr.append(regexpArg(arg));
+                regexpArgs.add(arg);
+                value = value.substring(pos2+1);
+            }
+        }
+        expr.append("$");
+        
+        regexp = java.util.regex.Pattern.compile(expr.toString());
+    }
+
+    private String regexpArg(String substring) {
+        return "([^/]+)";
+    }
+
+    private String regexpLiteral(String value) {
+        StringBuffer res = new StringBuffer();
+        for (int i=0; i<value.length(); i++) {
+            char c = value.charAt(i);
+            
+            if (Character.isLetterOrDigit(c)) {
+                res.append(c);
+            } else {
+                res.append('\\').append(c);
+            }
+        }
+        return res.toString();
+    }
+    
+    
+    public class IncomingRewrite
+        implements Rewrite
+    {
+        String incoming;
+        String queryArgs;
+
+        Boolean isMatch = null;
+        private List<String> matchedArgs = new ArrayList<String>();
+
+        public IncomingRewrite(String incoming) {
+            int queryPos = incoming.indexOf('?');
+
+            if (queryPos == -1) {
+                this.incoming = incoming;
+                this.queryArgs = "";
+            } else {
+                this.incoming = incoming.substring(0, queryPos);
+                this.queryArgs = incoming.substring(queryPos+1);
+            }
+
+            this.incoming = stripTrailingSlash(this.incoming);
+        }
+
+        private String stripTrailingSlash(String text) {
+            if (text.endsWith("/")) {
+                return stripTrailingSlash(text.substring(0,text.length()-1));
+            }
+            return text;
+        }
+
+        public boolean isMatch() {
+            if (isMatch == null) {
+                isMatch = match();
+            }
+            return isMatch;
+        }
+
+        protected boolean match() {
+            if (incoming==null) {
+                return false;
+            }
+
+            Matcher matcher = regexp.matcher(incoming);
+            if (matcher.find()) {
+                for (int i=0; i<regexpArgs.size(); i++) {
+                    matchedArgs.add(matcher.group(i+1));
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public String rewrite() {
+            StringBuffer result = new StringBuffer();
+            result.append(view);
+
+            boolean first = true;
+
+            if (queryArgs.length()>0) {
+                result.append('?').append(queryArgs);
+                first = false;
+            }
+
+            for (int i=0; i<regexpArgs.size(); i++) {
+                String key   = regexpArgs.get(i);
+                String value = matchedArgs.get(i);
+
+                if (first) {
+                    result.append('?');
+                    first = false;
+                } else {
+                    result.append('&');
+                }
+                result.append(key).append('=').append(value);
+            }
+
+            return result.toString();
+        }
+    }
+}

Added: trunk/src/main/org/jboss/seam/web/OutgoingPattern.java
===================================================================
--- trunk/src/main/org/jboss/seam/web/OutgoingPattern.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/web/OutgoingPattern.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,129 @@
+package org.jboss.seam.web;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OutgoingPattern {
+    String view;
+    String pattern;
+
+    List<String> parts = new ArrayList<String>();
+    
+    public OutgoingPattern(String view, String pattern) {
+        this.view = view;
+        this.pattern = pattern;
+        
+        parsePattern(pattern);
+    }
+    
+    public Rewrite rewrite(String path) {
+        return new OutgoingRewrite(path);
+    }
+    
+    private void parsePattern(String value) {       
+        while (value.length()>0) {
+            int pos = value.indexOf('{');
+            if (pos == -1) {
+                parts.add(value);
+                value = "";
+            } else {
+                int pos2 = value.indexOf('}');
+                if (pos2 == -1) {
+                    throw new IllegalArgumentException("invalid pattern");
+                }
+                parts.add(value.substring(0,pos));
+                parts.add(value.substring(pos,pos2+1));
+                value = value.substring(pos2+1);
+            }
+        }
+    }
+    
+    public class OutgoingRewrite 
+        implements Rewrite
+    {
+        Boolean isMatch;
+        
+        private String base;
+        private List<String> queryArgs     = new ArrayList<String>();
+        private List<String> matchedArgs   = new ArrayList<String>();
+        
+        public OutgoingRewrite(String outgoing) {           
+            int queryPos = outgoing.indexOf('?');
+
+            if (queryPos == -1) {
+                this.base      = outgoing;
+            } else {
+                this.base      = outgoing.substring(0, queryPos);
+                parseArgs(outgoing.substring(queryPos+1));
+            }
+        }
+
+        private void parseArgs(String text) {
+            for (String part: text.split("\\&")) {
+                queryArgs.add(part);
+            }
+        }
+
+        public boolean isMatch() {
+            if (isMatch == null) {
+                isMatch = match();
+            }
+            return isMatch;
+        }
+        
+        private boolean match() {
+            if (!base.equals(view)) {
+                return false;
+            }
+            
+            for (String part: parts) {
+                if (part.startsWith("{") && part.endsWith("}")) {
+                    String name = part.substring(1,part.length()-1);
+                    String value = matchArg(name);
+                    
+                    if (value == null) {
+                        return false;
+                    }
+                    
+                    matchedArgs.add(value);
+                }
+            }
+            
+            return true;
+        }
+
+        private String matchArg(String argName) {
+            for (int i=0; i<queryArgs.size(); i++) {
+                String query = queryArgs.get(i);
+                int pos = query.indexOf("=");
+                
+                if (query.subSequence(0, pos).equals(argName)) {
+                    queryArgs.remove(i);
+                    return query.substring(pos+1);
+                }
+            }
+            return null;
+        }
+
+        public String rewrite() {
+            StringBuffer res = new StringBuffer();
+            
+            int matchedPosition = 0;
+            for (String part: parts) {
+                if (part.startsWith("{")) { 
+                    res.append(matchedArgs.get(matchedPosition++));
+                } else {
+                    res.append(part);
+                }
+            }
+            
+            char sep = '?';
+            for (String arg: queryArgs) {
+                res.append(sep).append(arg);
+                sep = '&';
+            }
+            
+            return res.toString();
+        }
+    }
+}

Added: trunk/src/main/org/jboss/seam/web/Pattern.java
===================================================================
--- trunk/src/main/org/jboss/seam/web/Pattern.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/web/Pattern.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,35 @@
+package org.jboss.seam.web;
+
+public class Pattern 
+{
+    String view;
+    String pattern;
+    
+    IncomingPattern inPattern;
+    OutgoingPattern outPattern;
+
+    public Pattern(String view, String pattern) {
+        this.view = view;
+        this.pattern = pattern;
+        
+        inPattern = new IncomingPattern(view, pattern);
+        outPattern = new OutgoingPattern(view, pattern);
+    }
+
+    public Rewrite matchIncoming(String path) {
+        return returnIfMatch(inPattern.rewrite(path));
+    }
+
+    public Rewrite matchOutgoing(String path) {
+        return returnIfMatch(outPattern.rewrite(path));
+    }
+    
+    @Override
+    public String toString() {
+        return "Pattern(" + view + ":" + pattern + ")";
+    }
+
+    private Rewrite returnIfMatch(Rewrite rewrite) {
+        return rewrite.isMatch() ? rewrite : null;
+    }
+}

Added: trunk/src/main/org/jboss/seam/web/Rewrite.java
===================================================================
--- trunk/src/main/org/jboss/seam/web/Rewrite.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/web/Rewrite.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,6 @@
+package org.jboss.seam.web;
+
+public interface Rewrite {
+    public boolean isMatch();
+    public String rewrite();
+}

Added: trunk/src/main/org/jboss/seam/web/RewriteFilter.java
===================================================================
--- trunk/src/main/org/jboss/seam/web/RewriteFilter.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/web/RewriteFilter.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,120 @@
+package org.jboss.seam.web;
+
+import static org.jboss.seam.ScopeType.APPLICATION;
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.Map.Entry;
+
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.annotations.web.Filter;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+
+ at Scope(APPLICATION)
+ at Name("org.jboss.seam.web.rewriteFilter")
+ at Install(precedence = BUILT_IN, classDependencies="javax.faces.context.FacesContext")
+ at BypassInterceptors
+ at Filter(around="org.jboss.seam.web.HotDeployFilter")
+public class RewriteFilter 
+    extends AbstractFilter
+{
+    private static LogProvider log = Logging.getLogProvider(RewriteFilter.class);
+
+    Collection<Pattern> patterns = null; 
+
+    // need to extract this from Pages!
+    public void setPatterns(Map<String,String> patternMap) {        
+        patterns = new TreeSet<Pattern>(new Comparator<Pattern>() {
+            public int compare(Pattern p1, Pattern p2) {
+                return p2.pattern.compareTo(p1.pattern);
+            }
+        });
+                
+        for(Entry<String, String> entry: patternMap.entrySet()) {
+            patterns.add(new Pattern(entry.getValue(), entry.getKey()));
+        }
+        
+        log.info("Rewrite patterns: " + patterns);
+    }
+
+    public void doFilter(ServletRequest request, 
+                         ServletResponse response, 
+                         FilterChain chain) 
+        throws IOException, 
+               ServletException 
+    {
+        if (patterns != null) {
+            if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
+                response = new RewritingResponse((HttpServletRequest) request,
+                        (HttpServletResponse)response,
+                        patterns);
+                process((HttpServletRequest) request, (HttpServletResponse) response);
+            }
+        }
+        
+        if (!response.isCommitted()) {
+            chain.doFilter(request, response);
+        }
+    }
+    
+    
+    @SuppressWarnings("unchecked")
+    public void process(HttpServletRequest request, 
+                        HttpServletResponse response)
+        throws IOException, 
+               ServletException 
+    {
+        String fullPath = request.getRequestURI();
+        log.info("incoming URL is " + fullPath);
+
+        String localPath = strip(fullPath, request.getContextPath());
+       
+        Rewrite rewrite = matchPatterns(localPath);
+        if (rewrite!=null) {
+            String newPath = rewrite.rewrite();
+            
+            log.info("rewritten incoming path is " + localPath);
+            
+            if (!fullPath.equals(request.getContextPath() + newPath)) {
+                RequestDispatcher dispatcher = request.getRequestDispatcher(newPath);
+                dispatcher.forward(request, response);
+            }
+        }
+    }
+
+
+    private Rewrite matchPatterns(String localPath) {
+        for (Pattern pattern: patterns) {
+            Rewrite rewrite = pattern.matchIncoming(localPath);
+            if (rewrite!=null && rewrite.isMatch()) {
+                return rewrite;
+            }
+        }
+        return null;
+    }
+
+    private String strip(String fullPath, String contextPath) {
+        if (fullPath.startsWith(contextPath)) {
+            return fullPath.substring(contextPath.length());
+        } else {
+            return fullPath;
+        }
+    }
+}
+

Added: trunk/src/main/org/jboss/seam/web/RewritingResponse.java
===================================================================
--- trunk/src/main/org/jboss/seam/web/RewritingResponse.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/web/RewritingResponse.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,76 @@
+package org.jboss.seam.web;
+
+import java.util.Collection;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+
+public class RewritingResponse 
+    extends HttpServletResponseWrapper
+{
+    private static LogProvider log = Logging.getLogProvider(RewritingResponse.class);
+
+    private HttpServletRequest request;
+    private HttpServletResponse response;
+    private Collection<Pattern> patterns;
+
+
+    public RewritingResponse(HttpServletRequest request, 
+                             HttpServletResponse response, 
+                             Collection<Pattern> patterns) 
+    {
+        super(response);
+
+        this.request  = request;
+        this.response = response;
+        this.patterns = patterns;   
+    }   
+    
+    @Override
+    public String encodeUrl(String url) {
+        return super.encodeURL(url);
+    }
+    
+    @Override
+    public String encodeRedirectUrl(String url) {
+        return encodeRedirectURL(url);
+    }
+
+    
+    @Override
+    public String encodeURL(String url) {        
+        String result = encode(url);
+        log.info("encodeURL " + url + " -> " + result);
+        return result;
+    }
+    
+    @Override
+    public String encodeRedirectURL(String url) {
+        log.info("encode redirectURL called with " + url);
+        return super.encodeRedirectURL(url);
+    }
+    
+    
+    public String encode(String originalUrl) {
+        String url = originalUrl;
+        String contextPath = request.getContextPath();
+                
+        if (url.startsWith(contextPath)) {
+            url = url.substring(contextPath.length());
+        }
+
+        for (Pattern pattern: patterns) {
+            Rewrite rewrite = pattern.matchOutgoing(url);
+            if (rewrite != null) {
+                return request.getContextPath() + rewrite.rewrite();
+            }
+        }
+        
+        return originalUrl;
+    }
+
+}

Modified: trunk/src/test/unit/org/jboss/seam/test/unit/testng.xml
===================================================================
--- trunk/src/test/unit/org/jboss/seam/test/unit/testng.xml	2008-02-26 18:46:10 UTC (rev 7487)
+++ trunk/src/test/unit/org/jboss/seam/test/unit/testng.xml	2008-02-26 19:18:37 UTC (rev 7488)
@@ -62,4 +62,11 @@
         <class name="org.jboss.seam.test.unit.InterpolatorTest"/>
      </classes>
    </test>
+
+   <test name="Seam Unit Tests: URL Rewrite">
+     <classes>
+        <class name="org.jboss.seam.test.unit.web.RewriteTest" />
+     </classes>
+   </test>
+
 </suite>

Added: trunk/src/test/unit/org/jboss/seam/test/unit/web/RewriteTest.java
===================================================================
--- trunk/src/test/unit/org/jboss/seam/test/unit/web/RewriteTest.java	                        (rev 0)
+++ trunk/src/test/unit/org/jboss/seam/test/unit/web/RewriteTest.java	2008-02-26 19:18:37 UTC (rev 7488)
@@ -0,0 +1,127 @@
+package org.jboss.seam.test.unit.web;
+
+import org.testng.annotations.Test;
+
+import org.jboss.seam.web.Pattern;
+import org.jboss.seam.web.Rewrite;
+
+import static org.testng.Assert.*;
+
+public class RewriteTest 
+{
+    @Test
+    public void testBasicInPattern()
+        throws Exception
+    {
+        Pattern pattern = new Pattern("/foo.seam", "/foo");
+                 
+        testNoMatchIn(pattern, "/bar");
+        testNoMatchIn(pattern, "/fool");
+        testNoMatchIn(pattern, "/foo.seam");
+        testNoMatchIn(pattern, "/foo/bar");
+
+        testMatchIn(pattern, "/foo",     "/foo.seam");
+        testMatchIn(pattern, "/foo/",    "/foo.seam");
+        testMatchIn(pattern, "/foo?x=y", "/foo.seam?x=y");   
+    }
+    
+    @Test
+    public void testSingleArgInPattern()
+        throws Exception
+    {
+        Pattern pattern = new Pattern("/foo.seam", "/foo/{id}");
+                        
+        testNoMatchIn(pattern, "/foo");
+        testNoMatchIn(pattern, "/foo/");
+        testNoMatchIn(pattern, "/foo/bar/baz");
+        testNoMatchIn(pattern, "/foo/bar/baz?x=y");
+        
+        testMatchIn(pattern, "/foo/bar",      "/foo.seam?id=bar");
+        testMatchIn(pattern, "/foo/bar?x=y",  "/foo.seam?x=y&id=bar");
+        testMatchIn(pattern, "/foo/bar/?x=y", "/foo.seam?x=y&id=bar");
+    }
+
+    @Test
+    public void testMultiArgInPattern()
+        throws Exception
+    {
+        Pattern pattern = new Pattern("/foo.seam", "/foo/{id}/{action}");
+                              
+        testNoMatchIn(pattern, "/foo");
+        testNoMatchIn(pattern, "/foo/bar");
+        testNoMatchIn(pattern, "/foo/bar/baz/qux");
+
+        testMatchIn(pattern, "/foo/bar/baz",     "/foo.seam?id=bar&action=baz");
+        testMatchIn(pattern, "/foo/bar/baz?x=y", "/foo.seam?x=y&id=bar&action=baz");
+    }
+    
+    
+    @Test
+    public void testBasicOutPattern()
+        throws Exception
+    {
+        Pattern pattern = new Pattern("/foo.seam", "/foo");
+                 
+        testNoMatchOut(pattern, "/bar.seam");
+        testNoMatchOut(pattern, "/fool.seam");
+        testNoMatchOut(pattern, "/foo");
+        
+        testMatchOut(pattern, "/foo.seam",      "/foo");
+        testMatchOut(pattern, "/foo.seam?x=y",  "/foo?x=y");
+    }
+    
+    
+    @Test
+    public void testSingleArgOutPattern()
+        throws Exception
+    {
+        Pattern pattern = new Pattern("/foo.seam", "/foo/{id}");
+                        
+        testNoMatchOut(pattern, "/foo.seam");
+        testNoMatchOut(pattern, "/foo.seam?x=y");
+        testNoMatchOut(pattern, "/foo.seam/bar");       
+        testNoMatchOut(pattern, "/foo.seam/bar?id=test");       
+        
+        testMatchOut(pattern, "/foo.seam?id=bar",      "/foo/bar");
+        testMatchOut(pattern, "/foo.seam?x=y&id=bar",  "/foo/bar?x=y");
+        testMatchOut(pattern, "/foo.seam?id=bar&x=y",  "/foo/bar?x=y");
+        testMatchOut(pattern, "/foo.seam?a=b&x=y&id=bar&c=d&c=e",  "/foo/bar?a=b&x=y&c=d&c=e");
+    }
+    
+    @Test
+    public void testMultiArgOutPattern()
+        throws Exception
+    {
+        Pattern pattern = new Pattern("/foo.seam", "/foo/{id}/{action}");
+                              
+        testNoMatchOut(pattern, "/foo.seam");
+        testNoMatchOut(pattern, "/foo.seam?id=bar");
+        testNoMatchOut(pattern, "/foo.seam?action=baz");
+
+        testMatchOut(pattern, "/foo.seam?action=baz&id=bar", "/foo/bar/baz");
+        testMatchOut(pattern, "/foo.seam?y=z&action=baz&n=one&n=two&id=bar&x=y", "/foo/bar/baz?y=z&n=one&n=two&x=y");
+    }
+    
+    
+    public void testNoMatchIn(Pattern pattern, String incoming) {
+        assertNull(pattern.matchIncoming(incoming), incoming);
+    }
+    
+    public void testNoMatchOut(Pattern pattern, String incoming) {
+        assertNull(pattern.matchOutgoing(incoming), incoming);
+    }
+    
+    public void testMatchIn(Pattern pattern, String incoming, String expected) {
+        Rewrite rewrite = pattern.matchIncoming(incoming);
+        assertTrue(rewrite.isMatch(), incoming);
+        assertEquals(rewrite.rewrite(), expected);
+    }
+    
+    public void testMatchOut(Pattern pattern, String incoming, String expected) {
+        Rewrite rewrite = pattern.matchOutgoing(incoming);
+        assertTrue(rewrite.isMatch(), incoming);
+        assertEquals(rewrite.rewrite(), expected);
+    }
+
+}
+




More information about the seam-commits mailing list