Author: sergeyb Date: Wed Dec 3 10:04:20 2008 New Revision: 722988 URL: http://svn.apache.org/viewvc?rev=722988&view=rev Log: JAXRS : support for arbitrary regular expressions
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java?rev=722988&r1=722987&r2=722988&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java Wed Dec 3 10:04:20 2008 @@ -48,6 +48,12 @@ return g1 < g2 ? 1 : -1; } + int gCustom1 = e1.getURITemplate().getNumberOfGroupsWithCustomExpression(); + int gCustom2 = e2.getURITemplate().getNumberOfGroupsWithCustomExpression(); + if (gCustom1 != gCustom2) { + // descending order + return gCustom1 < gCustom2 ? 1 : -1; + } int result = JAXRSUtils.compareSortedMediaTypes( e1.getConsumeTypes(), Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java?rev=722988&r1=722987&r2=722988&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java Wed Dec 3 10:04:20 2008 @@ -21,7 +21,6 @@ import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,68 +37,63 @@ public static final String TEMPLATE_PARAMETERS = "jaxrs.template.parameters"; public static final String LIMITED_REGEX_SUFFIX = "(/.*)?"; - public static final String UNLIMITED_REGEX_SUFFIX = "(/)?"; public static final String FINAL_MATCH_GROUP = "FINAL_MATCH_GROUP"; /** * The regular expression for matching URI templates and names. */ private static final Pattern TEMPLATE_NAMES_PATTERN = - Pattern.compile("\\{(\\w[-\\w\\.]*)\\}"); - - /** - * A URI template is converted into a regular expression by substituting - * (.*?) for each occurrence of {\([w- 14 \. ]+?\)} within the URL - * template - */ - private static final String PATH_VARIABLE_REGEX = "([^/]+?)"; - private static final String PATH_UNLIMITED_VARIABLE_REGEX = "(.*?)"; + Pattern.compile("\\{(\\w[-\\w\\.]*)(\\:(.+?))?\\}"); + private static final String DEFAULT_PATH_VARIABLE_REGEX = "([^/]+?)"; + private final String template; - private final List<String> templateVariables; + private final List<String> templateVariables = new ArrayList<String>(); + private final List<String> customTemplateVariables = new ArrayList<String>(); private final Pattern templateRegexPattern; private final String literals; - public URITemplate(String theTemplate) { - this(theTemplate, true); - } - public URITemplate(String theTemplate, boolean limited) { + public URITemplate(String theTemplate) { + this.template = theTemplate; StringBuilder literalChars = new StringBuilder(); - StringBuilder stringBuilder = new StringBuilder(); - List<String> names = new ArrayList<String>(); - + StringBuilder patternBuilder = new StringBuilder(); + // compute a regular expression from URI template Matcher matcher = TEMPLATE_NAMES_PATTERN.matcher(template); int i = 0; while (matcher.find()) { - literalChars.append(template.substring(i, matcher.start())); - copyURITemplateCharacters(template, i, matcher.start(), stringBuilder); + templateVariables.add(matcher.group(1).trim()); + + String substr = escapeCharacters(template.substring(i, matcher.start())); + literalChars.append(substr); + patternBuilder.append(substr); i = matcher.end(); - if (!limited && i == template.length()) { - stringBuilder.append(PATH_UNLIMITED_VARIABLE_REGEX); + if (matcher.group(2) != null && matcher.group(3) != null) { + patternBuilder.append('('); + patternBuilder.append(matcher.group(3).trim()); + patternBuilder.append(')'); + customTemplateVariables.add(matcher.group(1).trim()); } else { - stringBuilder.append(PATH_VARIABLE_REGEX); - } - names.add(matcher.group(1)); + patternBuilder.append(DEFAULT_PATH_VARIABLE_REGEX); + } } - literalChars.append(template.substring(i, template.length())); - copyURITemplateCharacters(template, i, template.length(), stringBuilder); + String substr = escapeCharacters(template.substring(i, template.length())); + literalChars.append(substr); + patternBuilder.append(substr); literals = literalChars.toString(); - templateVariables = Collections.unmodifiableList(names); - - int endPos = stringBuilder.length() - 1; - boolean endsWithSlash = (endPos >= 0) ? stringBuilder.charAt(endPos) == '/' : false; + int endPos = patternBuilder.length() - 1; + boolean endsWithSlash = (endPos >= 0) ? patternBuilder.charAt(endPos) == '/' : false; if (endsWithSlash) { - stringBuilder.deleteCharAt(endPos); + patternBuilder.deleteCharAt(endPos); } - stringBuilder.append(limited ? LIMITED_REGEX_SUFFIX : UNLIMITED_REGEX_SUFFIX); + patternBuilder.append(LIMITED_REGEX_SUFFIX); - templateRegexPattern = Pattern.compile(stringBuilder.toString()); + templateRegexPattern = Pattern.compile(patternBuilder.toString()); } public String getLiteralChars() { @@ -114,15 +108,21 @@ return templateVariables.size(); } - private void copyURITemplateCharacters(String templateValue, int start, int end, StringBuilder b) { - for (int i = start; i < end; i++) { - char c = templateValue.charAt(i); - if (c == '?') { - b.append("\\?"); - } else { - b.append(c); - } + public int getNumberOfGroupsWithCustomExpression() { + return customTemplateVariables.size(); + } + + private static String escapeCharacters(String expression) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < expression.length(); i++) { + char ch = expression.charAt(i); + sb.append(isReservedCharater(ch) ? "\\" + ch : ch); } + return sb.toString(); + } + + private static boolean isReservedCharater(char ch) { + return '.' == ch; } public boolean match(String uri, MultivaluedMap<String, String> templateVariableToValue) { Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java?rev=722988&r1=722987&r2=722988&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java Wed Dec 3 10:04:20 2008 @@ -122,7 +122,7 @@ // with suffix values.clear(); - new URITemplate("/bar", false).match("/bar", values); + new URITemplate("/bar").match("/bar", values); u = new UriInfoImpl(mockMessage("http://localhost:8080/baz", "/bar"), values); Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java?rev=722988&r1=722987&r2=722988&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java Wed Dec 3 10:04:20 2008 @@ -35,8 +35,7 @@ @Test public void testMatchBasic() throws Exception { - URITemplate uriTemplate = new URITemplate("/customers/{id}", - false); + URITemplate uriTemplate = new URITemplate("/customers/{id}"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers/123/", values); @@ -47,8 +46,7 @@ @Test public void testMatchWithMatrixAndTemplate() throws Exception { - URITemplate uriTemplate = new URITemplate("/customers/{id}", - false); + URITemplate uriTemplate = new URITemplate("/customers/{id}"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers/123;123456/", values); @@ -59,8 +57,7 @@ @Test public void testMatchWithMatrixOnClearPath1() throws Exception { - URITemplate uriTemplate = new URITemplate("/customers/{id}", - false); + URITemplate uriTemplate = new URITemplate("/customers/{id}"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers;123456/123/", values); @@ -71,8 +68,7 @@ @Test public void testMatchWithMatrixOnClearPath2() throws Exception { - URITemplate uriTemplate = new URITemplate("/customers/{id}/orders/{order}", - false); + URITemplate uriTemplate = new URITemplate("/customers/{id}/orders/{order}"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); assertTrue(uriTemplate.match("/customers;123456/123/orders;456/3", values)); @@ -82,8 +78,7 @@ @Test public void testMatchWithMatrixOnClearPath3() throws Exception { - URITemplate uriTemplate = new URITemplate("/{id}/customers/", - false); + URITemplate uriTemplate = new URITemplate("/{id}/customers/"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/123/customers;123456/", values); @@ -94,8 +89,7 @@ @Test public void testMatchBasicTwoParametersVariation1() throws Exception { - URITemplate uriTemplate = new URITemplate("/customers/{name}/{department}", - false); + URITemplate uriTemplate = new URITemplate("/customers/{name}/{department}"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers/john/CS", values); @@ -108,8 +102,7 @@ @Test public void testMatchBasicTwoParametersVariation2() throws Exception { - URITemplate uriTemplate = new URITemplate("/customers/name/{name}/dep/{department}", - false); + URITemplate uriTemplate = new URITemplate("/customers/name/{name}/dep/{department}"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers/name/john/dep/CS", values); @@ -123,7 +116,7 @@ @Test public void testURITemplateWithSubResource() throws Exception { //So "/customers" is the URITemplate for the root resource class - URITemplate uriTemplate = new URITemplate("/customers", true); + URITemplate uriTemplate = new URITemplate("/customers"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers/123", values); @@ -135,7 +128,7 @@ @Test public void testURITemplateWithSubResourceVariation2() throws Exception { //So "/customers" is the URITemplate for the root resource class - URITemplate uriTemplate = new URITemplate("/customers", true); + URITemplate uriTemplate = new URITemplate("/customers"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/customers/name/john/dep/CS", values); @@ -150,7 +143,7 @@ * public Book getBook(@UriParam("bookId") String id) */ public void testURITemplateWithSubResourceVariation3() throws Exception { - URITemplate uriTemplate = new URITemplate("/books/{bookId}/", true); + URITemplate uriTemplate = new URITemplate("/books/{bookId}/"); MultivaluedMap<String, String> values = new MetadataMap<String, String>(); boolean match = uriTemplate.match("/books/123/chapter/1", values); @@ -158,4 +151,119 @@ String subResourcePath = values.getFirst(URITemplate.FINAL_MATCH_GROUP); assertEquals("/chapter/1", subResourcePath); } + + @Test + public void testBasicCustomExpression() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/{bookId:[^/]+?}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + boolean match = uriTemplate.match("/books/123/chapter/1", values); + assertTrue(match); + assertEquals("123", values.getFirst("bookId")); + String subResourcePath = values.getFirst(URITemplate.FINAL_MATCH_GROUP); + assertEquals("/chapter/1", subResourcePath); + } + + @Test + public void testBasicCustomExpression2() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/{bookId:123}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + boolean match = uriTemplate.match("/books/123/chapter/1", values); + assertTrue(match); + assertEquals("123", values.getFirst("bookId")); + String subResourcePath = values.getFirst(URITemplate.FINAL_MATCH_GROUP); + assertEquals("/chapter/1", subResourcePath); + } + + @Test + public void testBasicCustomExpression3() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/{bookId:\\d\\d\\d}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + boolean match = uriTemplate.match("/books/123/chapter/1", values); + assertTrue(match); + assertEquals("123", values.getFirst("bookId")); + String subResourcePath = values.getFirst(URITemplate.FINAL_MATCH_GROUP); + assertEquals("/chapter/1", subResourcePath); + } + + @Test + public void testEscaping() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/a.db"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + assertTrue(uriTemplate.match("/books/a.db", values)); + assertFalse(uriTemplate.match("/books/adbc", values)); + assertFalse(uriTemplate.match("/books/acdb", values)); + + } + + @Test + public void testBasicCustomExpression4() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/{bookId:...\\.}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + assertTrue(uriTemplate.match("/books/123.", values)); + assertEquals("123.", values.getFirst("bookId")); + values.clear(); + assertTrue(uriTemplate.match("/books/abc.", values)); + assertEquals("abc.", values.getFirst("bookId")); + assertFalse(uriTemplate.match("/books/abcd", values)); + assertFalse(uriTemplate.match("/books/abc", values)); + } + + @Test + public void testMultipleExpression2() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/{bookId:123}/chapter/{id}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + boolean match = uriTemplate.match("/books/123/chapter/1", values); + assertTrue(match); + assertEquals("123", values.getFirst("bookId")); + assertEquals("1", values.getFirst("id")); + String subResourcePath = values.getFirst(URITemplate.FINAL_MATCH_GROUP); + assertEquals("/", subResourcePath); + } + + @Test + public void testFailCustomExpression() throws Exception { + URITemplate uriTemplate = new URITemplate("/books/{bookId:124}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + + boolean match = uriTemplate.match("/books/123/chapter/1", values); + assertFalse(match); + } + + @Test + public void testBaseTail1() { + URITemplate uriTemplate = new URITemplate("/{base:base.+}/{tail}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + assertFalse(uriTemplate.match("/base/tails", values)); + assertTrue(uriTemplate.match("/base1/tails", values)); + assertEquals("base1", values.getFirst("base")); + assertEquals("tails", values.getFirst("tail")); + } + + @Test + public void testBaseTail2() { + URITemplate uriTemplate = new URITemplate("/{base:.+base}/{tail}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + assertFalse(uriTemplate.match("/base/tails", values)); + assertFalse(uriTemplate.match("/base1/tails", values)); + assertTrue(uriTemplate.match("/1base/tails", values)); + assertEquals("1base", values.getFirst("base")); + assertEquals("tails", values.getFirst("tail")); + } + + @Test + public void testBaseTail3() { + URITemplate uriTemplate = new URITemplate("/{base:base.+suffix}/{tail}"); + MultivaluedMap<String, String> values = new MetadataMap<String, String>(); + assertFalse(uriTemplate.match("/base/tails", values)); + assertFalse(uriTemplate.match("/base1/tails", values)); + assertTrue(uriTemplate.match("/base1suffix/tails", values)); + assertEquals("base1suffix", values.getFirst("base")); + assertEquals("tails", values.getFirst("tail")); + } } Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java?rev=722988&r1=722987&r2=722988&view=diff ============================================================================== --- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java (original) +++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java Wed Dec 3 10:04:20 2008 @@ -51,7 +51,7 @@ import org.apache.cxf.helpers.XMLUtils; [EMAIL PROTECTED]("/bookstore/") [EMAIL PROTECTED]("/bookstore") public class BookStore { private Map<Long, Book> books = new HashMap<Long, Book>(); @@ -139,6 +139,12 @@ } @GET + @Path("books/custom/{bookId:\\d\\d\\d}") + public Book getBookCustom(@PathParam("bookId") String id) throws BookNotFoundFault { + return doGetBook(id); + } + + @GET @Path("/books/query") public Book getBookQuery(@QueryParam("bookId") long id) throws BookNotFoundFault { return doGetBook(Long.toString(id)); Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java?rev=722988&r1=722987&r2=722988&view=diff ============================================================================== --- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java (original) +++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java Wed Dec 3 10:04:20 2008 @@ -184,6 +184,13 @@ } @Test + public void testGetBookCustomExpression() throws Exception { + getAndCompareAsStrings("http://localhost:9080/bookstore/books/custom/123", + "resources/expected_get_book123.txt", + "application/xml", 200); + } + + @Test public void testGetBook123() throws Exception { getAndCompareAsStrings("http://localhost:9080/bookstore/books/123", "resources/expected_get_book123.txt",