From 51fa9c3ac5e6afc344ed18eae0ff4b878731cb9e Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Fri, 21 Feb 2014 14:14:20 +0000 Subject: [PATCH 1/9] Extended UriTemplateParser / UriTemplate to support basic query and matrix parameters so that the declarative link injection code could work with the same types. This is so we can support the every popuplar /collection?offset=x&limit=y style pattern. Later work will update the example with the same pattern rather than the current contrived example --- .../jersey/uri/PatternWithGroups.java | 11 +- .../org/glassfish/jersey/uri/UriTemplate.java | 242 ++++++++++++------ .../uri/internal/UriTemplateParser.java | 130 ++++++++-- .../glassfish/jersey/uri/UriTemplateTest.java | 158 +++++++++++- .../linking/DeclarativeLinkingFeature.java | 5 +- .../jersey/linking/ELLinkBuilder.java | 2 - .../linking/InjectLinkFieldDescriptor.java | 22 ++ .../linking/InjectLinksFieldDescriptor.java | 8 - .../jersey/linking/RequestLinkFilter.java | 97 +++++++ ...inkFilter.java => ResponseLinkFilter.java} | 5 +- .../jersey/linking/FieldProcessorTest.java | 58 +++++ 11 files changed, 606 insertions(+), 132 deletions(-) create mode 100644 incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java rename incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/{LinkFilter.java => ResponseLinkFilter.java} (95%) diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java b/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java index 9ec9e2b4df..910734c865 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java @@ -257,7 +257,7 @@ public String group(final int group) { @Override public int groupCount() { - return groupIndexes.length - 1; + return groupIndexes.length; } } @@ -319,7 +319,7 @@ public final boolean match(final CharSequence cs, final List groupValues groupValues.clear(); if (groupIndexes.length > 0) { - for (int i = 0; i < groupIndexes.length - 1; i++) { + for (int i = 0; i < groupIndexes.length; i++) { groupValues.add(m.group(groupIndexes[i])); } } else { @@ -368,6 +368,13 @@ public final boolean match(final CharSequence cs, final List groupNames, // Assign the matched group values to group names groupValues.clear(); + + + for (int count = 0; count <= m.groupCount(); ++count) + { + System.out.println(count + " " + m.group(count)); + } + for (int i = 0; i < groupNames.size(); i++) { String name = groupNames.get(i); String currentValue = m.group((groupIndexes.length > 0) ? groupIndexes[i] : i + 1); diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java index 8a9bb9ed0c..9e3e7db7f6 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java @@ -41,6 +41,7 @@ import java.net.URI; import java.util.ArrayDeque; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Deque; @@ -134,7 +135,8 @@ public int compare(UriTemplate o1, UriTemplate o2) { /** * The regular expression for matching URI templates and names. */ - private static final Pattern TEMPLATE_NAMES_PATTERN = Pattern.compile("\\{(\\w[-\\w\\.]*)\\}"); + private static final Pattern TEMPLATE_NAMES_PATTERN = Pattern.compile("\\{([\\w\\?;][-\\w\\.,]*)\\}"); + /** * The empty URI template that matches the null or empty URI path. */ @@ -529,28 +531,27 @@ public final boolean match(CharSequence uri, List groupValues) throws * @param values the map of template variables to template values. * @return the URI. */ - public final String createURI(Map values) { - StringBuilder b = new StringBuilder(); - // Find all template variables - Matcher m = TEMPLATE_NAMES_PATTERN.matcher(normalizedTemplate); - int i = 0; - while (m.find()) { - b.append(normalizedTemplate, i, m.start()); - String tValue = values.get(m.group(1)); - if (tValue != null) { - b.append(tValue); + public final String createURI(final Map values) { + NameStrategy ns = new NameStrategy() + { + public String getValueFor(String tVariable, String matchedGroup) + { + return values.get(tVariable); + } - i = m.end(); - } - b.append(normalizedTemplate, i, normalizedTemplate.length()); - return b.toString(); + + }; + + final StringBuilder sb = new StringBuilder(); + applyTemplateStrategy(normalizedTemplate, sb, ns); + return sb.toString(); } /** * Create a URI by substituting any template variables * for corresponding template values. *

- * A URI template varibale without a value will be substituted by the + * A URI template variable without a value will be substituted by the * empty string. * * @param values the array of template values. The values will be @@ -560,6 +561,12 @@ public final String createURI(Map values) { public final String createURI(String... values) { return createURI(values, 0, values.length); } + + + private interface NameStrategy + { + public String getValueFor(String tVariable, String matchedGroup); + } /** * Create a URI by substituting any template variables @@ -574,38 +581,101 @@ public final String createURI(String... values) { * @param length the length of the array * @return the URI. */ - public final String createURI(String[] values, int offset, int length) { - Map mapValues = new HashMap(); - StringBuilder b = new StringBuilder(); + public final String createURI(final String[] values, final int offset, final int length) { + + NameStrategy ns = new NameStrategy() + { + int lengthPlusOffset = length + offset; + int v = offset; + + + Map mapValues = new HashMap(); + + + public String getValueFor(String tVariable, String matchedGroup) + { + // Check if a template variable has already occurred + // If so use the value to ensure that two or more declarations of + // a template variable have the same value + String tValue = mapValues.get(tVariable); + if (tValue == null) { + if (v < lengthPlusOffset) { + tValue = values[v++]; + if (tValue != null) { + mapValues.put(tVariable, tValue); + } + } + } + + return tValue; + + } + + }; + + final StringBuilder sb = new StringBuilder(); + applyTemplateStrategy(normalizedTemplate, sb, ns); + return sb.toString(); + } + + /** + * Build a URI based on the parameters provided by the strategy + * @param ns The naming strategy to use + * @return the URI + */ + private static void applyTemplateStrategy( + String normalizedTemplate, + StringBuilder b, + NameStrategy ns) { // Find all template variables Matcher m = TEMPLATE_NAMES_PATTERN.matcher(normalizedTemplate); - int v = offset; - length += offset; + + + int i = 0; while (m.find()) { b.append(normalizedTemplate, i, m.start()); String tVariable = m.group(1); - // Check if a template variable has already occurred - // If so use the value to ensure that two or more declarations of - // a template variable have the same value - String tValue = mapValues.get(tVariable); - if (tValue != null) { - b.append(tValue); - } else { - if (v < length) { - tValue = values[v++]; - if (tValue != null) { - mapValues.put(tVariable, tValue); + // TODO matrix + if (tVariable.startsWith("?") || tVariable.startsWith(";")) { + + char seperator = tVariable.startsWith("?") ? '&' : ';'; + char prefix = tVariable.startsWith("?") ? '?' : ';'; + + boolean found = false; + int index = b.length(); + String variables[] = tVariable.substring(1).split(", ?"); + for (String variable : variables) { + String tValue = ns.getValueFor(variable, m.group()); + if (tValue!=null && tValue.length() > 0) { + if (index != b.length()) { + b.append(seperator); + } + + b.append(variable); + b.append('='); b.append(tValue); } } + + if (index!= b.length()) { + b.insert(index, prefix); + } + } + else { + String tValue = ns.getValueFor(tVariable, m.group()); + + if (tValue!=null) { + b.append(tValue); + } } + i = m.end(); } b.append(normalizedTemplate, i, normalizedTemplate.length()); - return b.toString(); } + @Override public final String toString() { return pattern.toString(); @@ -865,14 +935,14 @@ private static String createURIWithStringValues( } @SuppressWarnings("unchecked") - private static int createURIComponent(UriComponent.Type t, + private static int createURIComponent(final UriComponent.Type t, String template, final String[] values, final int offset, final boolean encode, final Map _mapValues, final StringBuilder b) { - Map mapValues = (Map) _mapValues; + final Map mapValues = (Map) _mapValues; if (template.indexOf('{') == -1) { b.append(template); @@ -881,34 +951,37 @@ private static int createURIComponent(UriComponent.Type t, // Find all template variables template = new UriTemplateParser(template).getNormalizedTemplate(); - final Matcher m = TEMPLATE_NAMES_PATTERN.matcher(template); - int v = offset; - int i = 0; - while (m.find()) { - b.append(template, i, m.start()); - final String tVariable = m.group(1); - // Check if a template variable has already occurred - // If so use the value to ensure that two or more declarations of - // a template variable have the same value - Object tValue = mapValues.get(tVariable); - if (tValue == null && v < values.length) { - tValue = values[v++]; - } - if (tValue != null) { - mapValues.put(tVariable, tValue); - if (encode) { - tValue = UriComponent.encode(tValue.toString(), t); + + class CountingStrategy implements NameStrategy + { + int v = offset; + + @Override + public String getValueFor(String tVariable, String matchedGroup) { + + Object tValue = mapValues.get(tVariable); + if (tValue == null && v < values.length) { + tValue = values[v++]; + } + if (tValue != null) { + mapValues.put(tVariable, tValue); + if (encode) { + tValue = UriComponent.encode(tValue.toString(), t); + } else { + tValue = UriComponent.contextualEncode(tValue.toString(), t); + } } else { - tValue = UriComponent.contextualEncode(tValue.toString(), t); + throw templateVariableHasNoValue(tVariable); } - b.append(tValue); - } else { - throw templateVariableHasNoValue(tVariable); + + return tValue!=null ? tValue.toString() : null; } - i = m.end(); + } - b.append(template, i, template.length()); - return v; + CountingStrategy cs = new CountingStrategy(); + applyTemplateStrategy(template, b, cs); + + return cs.v; } @@ -924,7 +997,7 @@ private static int createURIComponent(UriComponent.Type t, * @throws IllegalArgumentException when {@code _mapValues} value is null. */ @SuppressWarnings("unchecked") - public static String resolveTemplateValues(UriComponent.Type type, + public static String resolveTemplateValues(final UriComponent.Type type, String template, final boolean encode, final Map _mapValues) { @@ -933,37 +1006,38 @@ public static String resolveTemplateValues(UriComponent.Type type, return template; } - Map mapValues = (Map) _mapValues; + final Map mapValues = (Map) _mapValues; StringBuilder sb = new StringBuilder(); // Find all template variables template = new UriTemplateParser(template).getNormalizedTemplate(); - final Matcher m = TEMPLATE_NAMES_PATTERN.matcher(template); - - int i = 0; - while (m.find()) { - sb.append(template, i, m.start()); - final String tVariable = m.group(1); - Object tValue = mapValues.get(tVariable); - - if (tValue != null) { - if (encode) { - tValue = UriComponent.encode(tValue.toString(), type); + + NameStrategy ns = new NameStrategy() + { + @Override + public String getValueFor(String tVariable, String matchedGroup) { + + Object tValue = mapValues.get(tVariable); + + if (tValue != null) { + if (encode) { + tValue = UriComponent.encode(tValue.toString(), type); + } else { + tValue = UriComponent.contextualEncode(tValue.toString(), type); + } + return tValue.toString(); } else { - tValue = UriComponent.contextualEncode(tValue.toString(), type); - } - sb.append(tValue); - } else { - if (mapValues.containsKey(tVariable)) { - throw new IllegalArgumentException("The value associated of the template value map for key + " + tVariable - + " is null."); - } + if (mapValues.containsKey(tVariable)) { + throw new IllegalArgumentException("The value associated of the template value map for key + " + tVariable + + " is null."); + } - sb.append(m.group()); + return matchedGroup; + } } - i = m.end(); - } - sb.append(template, i, template.length()); + }; + + applyTemplateStrategy(template, sb, ns); return sb.toString(); } diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java index f7b697b0f3..fde2cf84cb 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java @@ -185,17 +185,18 @@ public final int[] getGroupIndexes() { return EMPTY_INT_ARRAY; } - int[] indexes = new int[names.size() + 1]; - indexes[0] = 1; - for (int i = 1; i < indexes.length; i++) { - indexes[i] = indexes[i - 1] + groupCounts.get(i - 1); - } + int[] indexes = new int[names.size()]; +// indexes[0] = 1; for (int i = 0; i < indexes.length; i++) { - if (indexes[i] != i + 1) { - return indexes; - } + indexes[i] = (i == 0 ? 0 : indexes[i - 1]) + groupCounts.get(i); } - return EMPTY_INT_ARRAY; + +// for (int i = 0; i < indexes.length; i++) { +// if (indexes[i] != i + 1) { +// return indexes; +// } +// } + return indexes; } /** @@ -228,11 +229,12 @@ protected String encodeLiteralCharacters(final String characters) { private void parse(final CharacterIterator ci) { try { + int skipGroup = 0; while (ci.hasNext()) { char c = ci.next(); if (c == '{') { processLiteralCharacters(); - parseName(ci); + skipGroup = parseName(ci, skipGroup); } else { literalCharactersBuffer.append(c); } @@ -294,10 +296,18 @@ private static String[] initHexToUpperCaseRegex() { } - private void parseName(final CharacterIterator ci) { + private int parseName(final CharacterIterator ci, int skipGroup) { char c = consumeWhiteSpace(ci); + char paramType = 'p'; // Normal path param unless otherwise stated StringBuilder nameBuffer = new StringBuilder(); + + // Look for query or matrix types + if (c == '?' || c==';') { + paramType = c; + c = ci.next(); + } + if (Character.isLetterOrDigit(c) || c == '_') { // Template name character nameBuffer.append(c); @@ -313,7 +323,10 @@ private void parseName(final CharacterIterator ci) { if (Character.isLetterOrDigit(c) || c == '_' || c == '-' || c == '.') { // Template name character nameBuffer.append(c); - } else if (c == ':') { + } else if (paramType!='p' && c == ',' ) { + // separator allowed + nameBuffer.append(c); + } else if (c == ':' && paramType=='p') { nameRegexString = parseRegex(ci); break; } else if (c == '}') { @@ -336,32 +349,90 @@ private void parseName(final CharacterIterator ci) { ci.pos(), template)); } } + String name = nameBuffer.toString(); - names.add(name); - + Pattern namePattern; + try { - if (nameRegexString.length() > 0) { - numOfExplicitRegexes++; - } - Pattern namePattern = (nameRegexString.length() == 0) - ? TEMPLATE_VALUE_PATTERN : Pattern.compile(nameRegexString); - if (nameToPattern.containsKey(name)) { - if (!nameToPattern.get(name).equals(namePattern)) { - throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_NAME_MORE_THAN_ONCE(name, - template)); + if (paramType == '?' || paramType == ';') { + String subNames[] = name.split(",\\s?"); + + // Build up the regex for each of these properties + + StringBuilder regexBuilder = new StringBuilder(paramType == '?' ? "\\?" : ";"); + String seperator = paramType == '?' ? "\\&" : ";/\\?"; + + // Start a group becuase each parameter could repeat + +// names.add("__" + (paramType == '?' ? "query" : "matrix")); + + boolean first = true; + + regexBuilder.append("("); + for (String subName : subNames) { + regexBuilder.append("(&?"); + regexBuilder.append(subName); + regexBuilder.append("(=([^"); + regexBuilder.append(seperator); + regexBuilder.append("]*))?"); + regexBuilder.append(")"); + if (!first) { + regexBuilder.append("|"); + } + + names.add(subName); + groupCounts.add( + first ? 5 : 3); + first = false; } + +// groupCounts.add(1); + skipGroup = 1; + + + // Knock of last bar + regexBuilder.append(")*"); + + namePattern = Pattern.compile(regexBuilder.toString()); + + + // Make sure we display something useful + name = paramType + name; } else { - nameToPattern.put(name, namePattern); + + names.add(name); + // groupCounts.add(1 + skipGroup); + + if (nameRegexString.length() > 0) { + numOfExplicitRegexes++; + } + namePattern = (nameRegexString.length() == 0) + ? TEMPLATE_VALUE_PATTERN : Pattern.compile(nameRegexString); + if (nameToPattern.containsKey(name)) { + if (!nameToPattern.get(name).equals(namePattern)) { + throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_NAME_MORE_THAN_ONCE(name, + template)); + } + } else { + nameToPattern.put(name, namePattern); + } + + // Determine group count of pattern + Matcher m = namePattern.matcher(""); + int g = m.groupCount(); + groupCounts.add(1 + skipGroup); + skipGroup = g; + } - // Determine group count of pattern - Matcher m = namePattern.matcher(""); - int g = m.groupCount(); - groupCounts.add(g + 1); + regex.append('('). append(namePattern). append(')'); + + + normalizedTemplate.append('{'). append(name). append('}'); @@ -369,6 +440,9 @@ private void parseName(final CharacterIterator ci) { throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_INVALID_SYNTAX(nameRegexString, name, template), ex); } + + // Tell the next time though the loop how many to skip + return skipGroup; } private String parseRegex(final CharacterIterator ci) { diff --git a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java index a1aad568ac..5235c1fc0d 100644 --- a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java @@ -54,6 +54,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import org.junit.Ignore; /** * Taken from Jersey 1: jersey-tests: com.sun.jersey.impl.uri.UriTemplateTest @@ -311,9 +312,9 @@ void _testMatching(String template, String uri, String... values) { assertEquals(uri.length(), mr.end()); assertEquals(0, mr.start(0)); assertEquals(uri.length(), mr.end(0)); - for (int i = 1; i <= mr.groupCount(); i++) { - assertEquals(values[i - 1], mr.group(i)); - assertEquals(values[i - 1], uri.substring(mr.start(i), mr.end(i))); + for (int i = 0; i < mr.groupCount(); i++) { + assertEquals(values[i], mr.group(i+1)); + assertEquals(values[i], uri.substring(mr.start(i+1), mr.end(i+1))); } } @@ -463,4 +464,155 @@ private void validateNormalize(String path, String expected) throws Exception { assertEquals(expected, result.toString()); } + + + @Test + public void testSingleQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query}"); + + Map result = new HashMap(); + tmpl.match("/test?query=x", result); + + assertEquals( + "incorrect size for match string", + 1, + result.size() + ); + + assertEquals( + "query parameter is not matched", + "x", + result.get("query") + ); + } + + + @Test + public void testDoubleQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query,secondQuery}"); + + List list = new ArrayList(); + tmpl.match("/test?query=x&secondQuery=y", list); + + Map result = new HashMap(); + tmpl.match("/test?query=x&secondQuery=y", result); + + assertEquals( + "incorrect size for match string", + 2, + result.size() + ); + + assertEquals( + "query parameter is not matched", + "x", + result.get("query") + ); + assertEquals( + "query parameter is not matched", + "y", + result.get("secondQuery") + ); + } + + + @Test + public void testSettingQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query}"); + + Map values = new HashMap(); + values.put("query", "example"); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is not set", + "/test?query=example", + uri + ); + + } + + @Test + public void testSettingMatrixParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{;matrix}/other"); + + Map values = new HashMap(); + values.put("matrix", "example"); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is not set", + "/test;matrix=example/other", + uri + ); + + } + + + + @Test + public void testSettingTwoQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query,other}"); + + Map values = new HashMap(); + values.put("query", "example"); + values.put("other", "otherExample"); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is not set", + "/test?query=example&other=otherExample", + uri + ); + + } + + @Test + public void testSettingTwoMatrixParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{;matrix,other}/other"); + + Map values = new HashMap(); + values.put("matrix", "example"); + values.put("other", "otherExample"); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is not set", + "/test;matrix=example;other=otherExample/other", + uri + ); + + } + + + @Test + public void testNotSettingQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query}"); + + Map values = new HashMap(); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is set", + "/test", + uri + ); + + } + + @Test + public void testNotSettingMatrixParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{;query}/other"); + + Map values = new HashMap(); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is set", + "/test/other", + uri + ); + + } + } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java index 0646cac31c..a7e4713dc3 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java @@ -62,9 +62,10 @@ public boolean configure(FeatureContext context) { Configuration config = context.getConfiguration(); - if (!config.isRegistered(LinkFilter.class)) + if (!config.isRegistered(ResponseLinkFilter.class)) { - context.register(LinkFilter.class); + context.register(ResponseLinkFilter.class); + context.register(RequestLinkFilter.class); return true; } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java index 049070b346..c17c870253 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java @@ -40,8 +40,6 @@ package org.glassfish.jersey.linking; -import org.glassfish.jersey.linking.InjectLink; -import org.glassfish.jersey.linking.InjectLinkDescriptor; import java.net.URI; import java.util.HashMap; import java.util.List; diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java index a24e57ddf3..bca271cde5 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java @@ -40,6 +40,7 @@ package org.glassfish.jersey.linking; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.net.URI; import java.util.HashMap; @@ -48,6 +49,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Link; import org.glassfish.jersey.server.model.AnnotatedMethod; @@ -139,6 +141,26 @@ public static String getLinkTemplate(InjectLink link) { if (!(template.endsWith("/") || methodTemplate.startsWith("/"))) builder.append("/"); builder.append(methodTemplate); + + // append query parameters + StringBuilder querySubString = new StringBuilder(); + for (Annotation paramAnns[] : method.getParameterAnnotations()){ + for (Annotation ann : paramAnns) { + if (ann.annotationType() == QueryParam.class) { + querySubString.append(((QueryParam)ann).value()); + querySubString.append(','); + } + } + } + + if (querySubString.length() > 0) + { + builder.append("{?"); + builder.append(querySubString.subSequence(0, querySubString.length()-1)); + builder.append("}"); + } + + template = builder.toString(); break; } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java index 1253f1e597..bcd544e643 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java @@ -42,18 +42,10 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; -import java.net.URI; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import javax.ws.rs.Path; import javax.ws.rs.core.Link; -import org.glassfish.jersey.server.model.AnnotatedMethod; - -import org.glassfish.jersey.server.model.MethodList; /** * Utility class for working with {@link InjectLinks} annotated fields diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java new file mode 100644 index 0000000000..95bfd9f622 --- /dev/null +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java @@ -0,0 +1,97 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.linking; + +import java.io.IOException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.Link; +import javax.ws.rs.ext.Provider; + +/** + * Filter that processes {@link Link} annotated fields in returned response + * entities. + *

+ * When an application is deployed as a Servlet or Filter this filter can be + * registered using the following initialization parameters: + *

+ *     <init-param>
+ *         <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
+ *         <param-value>com.sun.jersey.server.linking.ResponseLinkFilter</param-value>
+ *     </init-param>
+ * 
+ *

+ * + * + * @author Mark Hadley + * @author Gerard Davison (gerard.davison at oracle.com) + * @see LinkHeader + */ + +class RequestLinkFilter implements ContainerRequestFilter { + + @Context + private UriInfo uriInfo; + +// public void filter(ContainerRequestContext request) { +// final Object entity = response.getEntity(); +// +// if (entity != null && !uriInfo.getMatchedResources().isEmpty()) { +// Class entityClass = entity.getClass(); +// HeaderProcessor lhp = new HeaderProcessor(entityClass); +// lhp.processLinkHeaders(entity, uriInfo, response.getHeaders()); +// FieldProcessor lp = new FieldProcessor(entityClass); +// lp.processLinks(entity, uriInfo); +// } +// +// } + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + + } +} \ No newline at end of file diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java similarity index 95% rename from incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkFilter.java rename to incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java index 2e5a47c433..eccca017c4 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java @@ -58,7 +58,7 @@ *

  *     <init-param>
  *         <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
- *         <param-value>com.sun.jersey.server.linking.LinkFilter</param-value>
+ *         <param-value>com.sun.jersey.server.linking.ResponseLinkFilter</param-value>
  *     </init-param>
  * 
*

@@ -69,8 +69,7 @@ * @see LinkHeader */ -@Provider -class LinkFilter implements ContainerResponseFilter { +class ResponseLinkFilter implements ContainerResponseFilter { @Context private UriInfo uriInfo; diff --git a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java index 4d80968d12..9c7ae96b18 100644 --- a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java +++ b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java @@ -55,6 +55,7 @@ import java.util.zip.ZipEntry; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.*; import static org.junit.Assert.*; @@ -509,6 +510,63 @@ public void testSubresource() { assertEquals("/application/resources/a/b", testClass.uri); } + + @Path("a") + public static class QueryResource { + + @Path("b") + @GET + public String getB(@QueryParam("query") String query, @QueryParam("query2") String query2) { + return "hello world"; + } + } + + public static class QueryResourceBean { + + public String getQueryParam() { + return queryExample; + } + private String queryExample; + + public QueryResourceBean(String queryExample, String queryExample2) { + this.queryExample = queryExample; + this.queryExample2 = queryExample2; + } + + public String getQueryParam2() { + return queryExample2; + } + private String queryExample2; + + + @InjectLink(resource=QueryResource.class, method="getB", + bindings = { + @Binding(name="query", value = "${instance.queryParam}"), + @Binding(name="query2", value = "${instance.queryParam2}") + }) + public String uri; + } + + + @Test + public void testQueryResource() { + System.out.println("QueryResource"); + FieldProcessor instance = new FieldProcessor(QueryResourceBean.class); + QueryResourceBean testClass = new QueryResourceBean("queryExample",null); + instance.processLinks(testClass, mockUriInfo); + assertEquals("/application/resources/a/b?query=queryExample", testClass.uri); + } + + @Test + public void testDoubleQueryResource() { + System.out.println("QueryResource"); + FieldProcessor instance = new FieldProcessor(QueryResourceBean.class); + QueryResourceBean testClass = new QueryResourceBean("queryExample","queryExample2"); + instance.processLinks(testClass, mockUriInfo); + assertEquals("/application/resources/a/b?query=queryExample&query2=queryExample2", testClass.uri); + } + + public static class TestClassK { public static final ZipEntry zipEntry = new ZipEntry("entry"); } From a5eb074fb705841f5b305bc3d2a08ae3fd6cccf6 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Fri, 21 Feb 2014 17:42:30 +0000 Subject: [PATCH 2/9] The last group of the PathPattern wasn't matching the remaining path, causing problems in later module, added test in the core moudle to make sure that is it picked up properly. --- .../org/glassfish/jersey/uri/PathPattern.java | 11 +++--- .../jersey/uri/PatternWithGroups.java | 8 +--- .../org/glassfish/jersey/uri/UriTemplate.java | 39 +++++++++++++++---- .../uri/internal/UriTemplateParser.java | 28 +++++++++---- .../glassfish/jersey/uri/PathPatternTest.java | 25 ++++++++++++ .../glassfish/jersey/uri/UriTemplateTest.java | 20 +++++++++- .../linking/DeclarativeLinkingFeature.java | 3 +- .../jersey/linking/RequestLinkFilter.java | 16 -------- .../jersey/linking/ResponseLinkFilter.java | 1 - .../jersey/linking/FieldProcessorTest.java | 4 -- 10 files changed, 105 insertions(+), 50 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java b/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java index 518a88a29d..efdc7e1bb8 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java @@ -40,6 +40,7 @@ package org.glassfish.jersey.uri; import java.util.Comparator; +import java.util.regex.MatchResult; /** * A path pattern that is a regular expression generated from a URI path @@ -149,7 +150,7 @@ public PathPattern(String template) { */ public PathPattern(PathTemplate template) { super(postfixWithCapturingGroup(template.getPattern().getRegex()), - addIndexForRightHandPathCapturingGroup(template.getPattern().getGroupIndexes())); + addIndexForRightHandPathCapturingGroup(template.getNumberOfRegexGroups(), template.getPattern().getGroupIndexes())); this.template = template; } @@ -172,7 +173,7 @@ public PathPattern(String template, RightHandPath rhpp) { */ public PathPattern(PathTemplate template, RightHandPath rhpp) { super(postfixWithCapturingGroup(template.getPattern().getRegex(), rhpp), - addIndexForRightHandPathCapturingGroup(template.getPattern().getGroupIndexes())); + addIndexForRightHandPathCapturingGroup(template.getNumberOfRegexGroups(), template.getPattern().getGroupIndexes())); this.template = template; } @@ -193,15 +194,15 @@ private static String postfixWithCapturingGroup(String regex, RightHandPath rhpp return regex + rhpp.getRegex(); } - private static int[] addIndexForRightHandPathCapturingGroup(int[] indexes) { + private static int[] addIndexForRightHandPathCapturingGroup(int numberOfGroups, int[] indexes) { if (indexes.length == 0) { return indexes; } - + int[] cgIndexes = new int[indexes.length + 1]; System.arraycopy(indexes, 0, cgIndexes, 0, indexes.length); - cgIndexes[indexes.length] = cgIndexes[indexes.length - 1] + 1; + cgIndexes[indexes.length] = numberOfGroups +1; return cgIndexes; } } diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java b/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java index 910734c865..7a204eff6b 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -54,6 +54,7 @@ * #renamed com.sun.jersey.api.uri.UriPattern * * @author Paul Sandoz + * @author Gerard Davison (gerard.davison at oracle.com) */ public class PatternWithGroups { @@ -370,11 +371,6 @@ public final boolean match(final CharSequence cs, final List groupNames, groupValues.clear(); - for (int count = 0; count <= m.groupCount(); ++count) - { - System.out.println(count + " " + m.group(count)); - } - for (int i = 0; i < groupNames.size(); i++) { String name = groupNames.get(i); String currentValue = m.group((groupIndexes.length > 0) ? groupIndexes[i] : i + 1); diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java index 9e3e7db7f6..74b3d46213 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java @@ -41,7 +41,6 @@ import java.net.URI; import java.util.ArrayDeque; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Deque; @@ -61,6 +60,7 @@ * * @author Paul Sandoz * @author Martin Matula (martin.matula at oracle.com) + * @author Gerard Davison (gerard.davison at oracle.com) */ public class UriTemplate { private static String[] EMPTY_VALUES = new String[0]; @@ -132,6 +132,17 @@ public int compare(UriTemplate o1, UriTemplate o2) { return o2.pattern.getRegex().compareTo(o1.pattern.getRegex()); } }; + + + /** + * A strategy interface for processing parameters, should be replaced with + * a JDK 8 one day in the future. + */ + private interface NameStrategy + { + public String getValueFor(String tVariable, String matchedGroup); + } + /** * The regular expression for matching URI templates and names. */ @@ -167,6 +178,12 @@ public int compare(UriTemplate o1, UriTemplate o2) { * variables. */ private final int numOfExplicitRegexes; + + /** + * The number regular expression groups in this pattern + */ + private final int numOfRegexGroups; + /** * The number of characters in the regular expression not resulting * from conversion of template variables. @@ -181,7 +198,7 @@ private UriTemplate() { this.pattern = PatternWithGroups.EMPTY; this.endsWithSlash = false; this.templateVariables = Collections.emptyList(); - this.numOfExplicitRegexes = this.numOfCharacters = 0; + this.numOfExplicitRegexes = this.numOfCharacters = this.numOfRegexGroups = 0; } /** @@ -233,6 +250,8 @@ protected UriTemplate(UriTemplateParser templateParser) throws this.pattern = initUriPattern(templateParser); this.numOfExplicitRegexes = templateParser.getNumberOfExplicitRegexes(); + + this.numOfRegexGroups = templateParser.getNumberOfRegexGroups(); this.numOfCharacters = templateParser.getNumberOfLiteralCharacters(); @@ -453,6 +472,16 @@ public final int getNumberOfExplicitRegexes() { return numOfExplicitRegexes; } + /** + * Get the number of regular expression groups + * + * @return the number of regular expressions groups + */ + public final int getNumberOfRegexGroups() { + return numOfRegexGroups; + } + + /** * Get the number of characters in the regular expression not resulting * from conversion of template variables. @@ -562,12 +591,6 @@ public final String createURI(String... values) { return createURI(values, 0, values.length); } - - private interface NameStrategy - { - public String getValueFor(String tVariable, String matchedGroup); - } - /** * Create a URI by substituting any template variables * for corresponding template values. diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java index fde2cf84cb..968e08e2b0 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -58,6 +58,7 @@ * A URI template parser that parses JAX-RS specific URI templates. * * @author Paul Sandoz + * @author Gerard Davison (gerard.davison at oracle.com) */ public class UriTemplateParser { /* package */ static final int[] EMPTY_INT_ARRAY = new int[0]; @@ -89,6 +90,8 @@ private static Set initReserved() { private final List groupCounts = new ArrayList(); private final Map nameToPattern = new HashMap(); private int numOfExplicitRegexes; + private int skipGroup; + private int literalCharacters; /** @@ -186,16 +189,10 @@ public final int[] getGroupIndexes() { } int[] indexes = new int[names.size()]; -// indexes[0] = 1; for (int i = 0; i < indexes.length; i++) { indexes[i] = (i == 0 ? 0 : indexes[i - 1]) + groupCounts.get(i); } -// for (int i = 0; i < indexes.length; i++) { -// if (indexes[i] != i + 1) { -// return indexes; -// } -// } return indexes; } @@ -208,6 +205,22 @@ public final int getNumberOfExplicitRegexes() { return numOfExplicitRegexes; } + /** + * Get the number of regular expression groups + * + * @return the number of regular expressions groups + */ + public final int getNumberOfRegexGroups() { + if (groupCounts.isEmpty()) { + return 0; + } + else { + int groupIndex[] = getGroupIndexes(); + return groupIndex[groupIndex.length-1] + skipGroup; + } + } + + /** * Get the number of literal characters. * @@ -229,7 +242,6 @@ protected String encodeLiteralCharacters(final String characters) { private void parse(final CharacterIterator ci) { try { - int skipGroup = 0; while (ci.hasNext()) { char c = ci.next(); if (c == '{') { diff --git a/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java b/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java index 2b35cc8f0b..c1c5c8a98f 100644 --- a/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java @@ -39,7 +39,12 @@ */ package org.glassfish.jersey.uri; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.MatchResult; +import java.util.regex.Matcher; import org.junit.Assert; import org.junit.Test; @@ -51,6 +56,7 @@ * Tests {@link PathTemplate}. * * @author Marek Potociar (marek.potociar at oracle.com) + * @author Gerard Davison (gerard.davison at oracle.com) */ public class PathPatternTest { @@ -125,4 +131,23 @@ public void testSetsAndGetsUriTemplate() throws Exception { pattern.getTemplate() ); } + + + + @Test + public void testLastElementOfMatchIsRestOfPath() throws Exception { + PathPattern path = new PathPattern("{a: (\\d)(\\d*)}-{b: (\\d)(\\d*)}-{c: (\\d)(\\d*)}"); + + + MatchResult m = path.match("/123-456-789/d"); + String value = m.group(m.groupCount()); + + assertEquals( + "Last value should match all of the trailing part", + "/d", + value + ); + + } + } diff --git a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java index 5235c1fc0d..13357fde5e 100644 --- a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java @@ -60,6 +60,7 @@ * Taken from Jersey 1: jersey-tests: com.sun.jersey.impl.uri.UriTemplateTest * * @author Paul.Sandoz at Sun.Com + * @author Gerard Davison (gerard.davison at oracle.com) */ public class UriTemplateTest { @@ -583,7 +584,24 @@ public void testSettingTwoMatrixParameter() throws Exception { ); } - + + @Test + public void testSettingTwoSeperatedMatrixParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{;matrix}/other{;other}"); + + Map values = new HashMap(); + values.put("matrix", "example"); + values.put("other", "otherExample"); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is not set", + "/test;matrix=example/other;other=otherExample", + uri + ); + + } + @Test public void testNotSettingQueryParameter() throws Exception { diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java index a7e4713dc3..32343cdc8c 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java @@ -65,7 +65,8 @@ public boolean configure(FeatureContext context) { if (!config.isRegistered(ResponseLinkFilter.class)) { context.register(ResponseLinkFilter.class); - context.register(RequestLinkFilter.class); + // Todo map values back? +// context.register(RequestLinkFilter.class); return true; } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java index 95bfd9f622..f8e7c419a1 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java @@ -46,10 +46,7 @@ import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.Link; -import javax.ws.rs.ext.Provider; /** * Filter that processes {@link Link} annotated fields in returned response @@ -76,19 +73,6 @@ class RequestLinkFilter implements ContainerRequestFilter { @Context private UriInfo uriInfo; -// public void filter(ContainerRequestContext request) { -// final Object entity = response.getEntity(); -// -// if (entity != null && !uriInfo.getMatchedResources().isEmpty()) { -// Class entityClass = entity.getClass(); -// HeaderProcessor lhp = new HeaderProcessor(entityClass); -// lhp.processLinkHeaders(entity, uriInfo, response.getHeaders()); -// FieldProcessor lp = new FieldProcessor(entityClass); -// lp.processLinks(entity, uriInfo); -// } -// -// } - @Override public void filter(ContainerRequestContext requestContext) throws IOException { diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java index eccca017c4..333b498bad 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java @@ -47,7 +47,6 @@ import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.Link; -import javax.ws.rs.ext.Provider; /** * Filter that processes {@link Link} annotated fields in returned response diff --git a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java index 9c7ae96b18..703ced1131 100644 --- a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java +++ b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java @@ -40,10 +40,6 @@ package org.glassfish.jersey.linking; -import org.glassfish.jersey.linking.FieldDescriptor; -import org.glassfish.jersey.linking.FieldProcessor; -import org.glassfish.jersey.linking.Binding; -import org.glassfish.jersey.linking.InjectLink; import java.net.URI; import java.util.Arrays; From a19243ab7f23b142a6692cb70ac1f61434059370 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Tue, 8 Apr 2014 14:43:48 +0100 Subject: [PATCH 3/9] Working towards better support for Resource links and query paramters. Exmaple updated to show both working. --- .../representation/ItemsRepresentation.java | 125 ++++++++ .../linking/resources/ItemsResource.java | 110 +++++++ .../mapping/NaiveResourceMappingContext.java | 288 ++++++++++++++++++ .../mapping/ResourceMappingContext.java | 62 ++++ 4 files changed, 585 insertions(+) create mode 100644 examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java create mode 100644 examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java create mode 100644 incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/NaiveResourceMappingContext.java create mode 100644 incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/ResourceMappingContext.java diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java new file mode 100644 index 0000000000..bdb27e1e15 --- /dev/null +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java @@ -0,0 +1,125 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package org.glassfish.jersey.examples.linking.representation; + +import org.glassfish.jersey.examples.linking.resources.ItemResource; +import org.glassfish.jersey.linking.Binding; +import org.glassfish.jersey.linking.InjectLinks; +import org.glassfish.jersey.linking.InjectLink; +import org.glassfish.jersey.linking.InjectLink.Style; + +import java.util.List; +import javax.ws.rs.core.Link; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +/** + * JAXB representation of an item + * + * + * @author Mark Hadley + * @author Gerard Davison (gerard.davison at oracle.com) + */ +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "item") +@InjectLinks({ + @InjectLink( + resource = ItemResource.class, + style = Style.ABSOLUTE, + condition = "${resource.next}", + bindings = @Binding(name = "id", value = "${resource.nextId}"), + rel = "next" + ), + @InjectLink( + resource = ItemResource.class, + style = Style.ABSOLUTE, + condition = "${resource.prev}", + bindings = @Binding(name = "id", value = "${resource.prevId}"), + rel = "prev" + ) +}) +public class ItemRepresentation { + + @XmlElement + private String name; + + @InjectLink( + resource = ItemResource.class, + style = Style.ABSOLUTE, + bindings = @Binding(name = "id", value = "${resource.id}"), + rel = "self" + ) + @XmlJavaTypeAdapter(Link.JaxbAdapter.class) + @XmlElement(name="link") + Link self; + + @InjectLinks({ + @InjectLink( + resource = ItemResource.class, + style = Style.ABSOLUTE, + condition = "${resource.next}", + bindings = @Binding(name = "id", value = "${resource.nextId}"), + rel = "next" + ), + @InjectLink( + resource = ItemResource.class, + style = Style.ABSOLUTE, + condition = "${resource.prev}", + bindings = @Binding(name = "id", value = "${resource.prevId}"), + rel = "prev" + )}) + @XmlElement(name="link") + @XmlElementWrapper(name = "links") + @XmlJavaTypeAdapter(Link.JaxbAdapter.class) + List links; + + public ItemRepresentation() { + this.name = ""; + } + + public ItemRepresentation(String name) { + this.name = name; + } + +} diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java new file mode 100644 index 0000000000..cc93ee96e5 --- /dev/null +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.examples.linking.resources; + +import org.glassfish.jersey.examples.linking.model.ItemModel; +import org.glassfish.jersey.examples.linking.model.ItemsModel; +import org.glassfish.jersey.examples.linking.representation.ItemRepresentation; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Resource that provides access to one item from a set of items managed + * by ItemsModel + * + * @author Mark Hadley + * @author Gerard Davison (gerard.davison at oracle.com) + */ +@Path("items/{id}") +@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) +public class ItemResource { + + private ItemsModel itemsModel; + private ItemModel itemModel; + private String id; + + public ItemResource(@PathParam("id") String id) { + this.id = id; + itemsModel = ItemsModel.getInstance(); + try { + itemModel = itemsModel.getItem(id); + } catch (IndexOutOfBoundsException ex) { + throw new NotFoundException(); + } + } + + @GET + public ItemRepresentation get() { + return new ItemRepresentation(itemModel.getName()); + } + + /** + * Determines whether there is a next item. + * @return + */ + public boolean isNext() { + return itemsModel.hasNext(id); + } + + /** + * Determines whether there is a previous item + * @return + */ + public boolean isPrev() { + return itemsModel.hasPrev(id); + } + + public String getNextId() { + return itemsModel.getNextId(id); + } + + public String getPrevId() { + return itemsModel.getPrevId(id); + } + + public String getId() { + return id; + } +} diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/NaiveResourceMappingContext.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/NaiveResourceMappingContext.java new file mode 100644 index 0000000000..0b7a72f75d --- /dev/null +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/NaiveResourceMappingContext.java @@ -0,0 +1,288 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.linking.mapping; + +import java.lang.reflect.Type; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.Context; +import org.glassfish.jersey.process.Inflector; +import org.glassfish.jersey.server.ExtendedResourceContext; +import org.glassfish.jersey.server.model.HandlerConstructor; +import org.glassfish.jersey.server.model.Invocable; +import org.glassfish.jersey.server.model.MethodHandler; +import org.glassfish.jersey.server.model.Resource; +import org.glassfish.jersey.server.model.ResourceMethod; +import org.glassfish.jersey.server.model.ResourceModel; +import org.glassfish.jersey.server.model.ResourceModelComponent; +import org.glassfish.jersey.server.model.ResourceModelVisitor; +import org.glassfish.jersey.server.model.RuntimeResource; +import org.glassfish.jersey.uri.PathPattern; +import org.glassfish.jersey.uri.UriTemplate; + +/** + * This implementation of the resource mapping context assumed resource are + * of simple a simple type with a statically defined structure. + * @author Gerard Davison (gerard.davison at oracle.com) + */ + + +public class NaiveResourceMappingContext + implements ResourceMappingContext { + + + private ExtendedResourceContext erc; + + private Map, ResourceMappingContext.Mapping> mappings; + + + public NaiveResourceMappingContext(@Context ExtendedResourceContext erc) + { + this.erc = erc; + } + + + + @Override + public Mapping getMapping(Class resource) { + buildMappings(); + return mappings.get(resource); + } + + + + + + private void buildMappings() { + + if (mappings!=null) { + return; + } + + mappings + = new HashMap, ResourceMappingContext.Mapping>(); + + + // + + erc.getResourceModel().accept(new ResourceModelVisitor() { + + StringBuffer prefix = new StringBuffer(); + Deque stack = new LinkedList(); + + + private void processComponents(ResourceModelComponent component) { + + List components = component.getComponents(); + if (components!=null) + { + for (ResourceModelComponent rc : components) { + rc.accept(this); + } + } + } + + @Override + public void visitInvocable(Invocable invocable) { + processComponents(invocable); + } + + @Override + public void visitRuntimeResource(RuntimeResource runtimeResource) { + processComponents(runtimeResource); + } + + @Override + public void visitResourceModel(ResourceModel resourceModel) { + processComponents(resourceModel); + } + + + + @Override + public void visitResourceHandlerConstructor(HandlerConstructor handlerConstructor) { + processComponents(handlerConstructor); + } + + @Override + public void visitMethodHandler(MethodHandler methodHandler) { + processComponents(methodHandler); + } + + @Override + public void visitChildResource(Resource resource) { + visitResourceIntl(resource, false); + } + + @Override + public void visitResource(Resource resource) { + + visitResourceIntl(resource, true); + } + + + private void visitResourceIntl(Resource resource, boolean isRoot) { + try { + stack.addLast(resource.getPathPattern()); + processComponents(resource); + + if (isRoot) { + Class likelyToBeRoot = null; + for (Class next : resource.getHandlerClasses()) { + if (!(Inflector.class.isAssignableFrom(next))) { + likelyToBeRoot = next; + } + } + + if (likelyToBeRoot!=null){ + mappings.put(likelyToBeRoot, getMapping(getTemplate())); + } + } + } + finally { + stack.removeLast(); + } + } + + + + @Override + public void visitResourceMethod(ResourceMethod resourceMethod) { + + if (resourceMethod.isExtended()) { + return; + } + + if (ResourceMethod.JaxrsType.SUB_RESOURCE_LOCATOR.equals(resourceMethod.getType())) { + if (resourceMethod.getInvocable()!=null) { + Invocable i = resourceMethod.getInvocable(); + + final Type type = i.getResponseType(); + final StringBuilder template = getTemplate(); + + mappings.put((Class)type, getMapping(template)); + + // Process sub resources ? + + try { + Resource.Builder builder = Resource + .builder(i.getRawResponseType()); + if (builder == null) { + // for example in the case the return type of the sub resource locator is Object + builder = Resource.builder().path(resourceMethod.getParent().getPath()); + } + Resource subResource = builder.build(); + + visitChildResource(subResource); + } + finally { + + } + + } + + + + } + + + processComponents(resourceMethod); + } + + private StringBuilder getTemplate() { + final StringBuilder template = new StringBuilder(); + for (PathPattern pp : stack) { + String ppTemplate = pp.getTemplate().getTemplate(); + + int tlength = template.length(); + if (tlength > 0) + { + if (template.charAt(tlength -1) == '/') { + if (ppTemplate.startsWith("/")) { + template.append(ppTemplate, 1, ppTemplate.length()); + } + else { + template.append(ppTemplate); + } + } + else { + if (ppTemplate.startsWith("/")) { + template.append(ppTemplate); + } + else { + template.append("/"); + template.append(ppTemplate); + } + } + } + else { + template.append(ppTemplate); + } + + + + } + return template; + } + + }); + + + + } + + private Mapping getMapping(final StringBuilder template) { + return new Mapping() + { + UriTemplate uriTemplate = new UriTemplate(template.toString()); + + @Override + public UriTemplate getTemplate() { + return uriTemplate; + } + + }; + } + +} diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/ResourceMappingContext.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/ResourceMappingContext.java new file mode 100644 index 0000000000..ba17542759 --- /dev/null +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/mapping/ResourceMappingContext.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.linking.mapping; + +import org.glassfish.jersey.uri.UriTemplate; + +/** + * This service tries to work out the UriTemplate required for a particular + * resource class. + * + * @author Gerard Davison (gerard.davison at oracle.com) + */ + + +public interface ResourceMappingContext { + + public interface Mapping + { + public UriTemplate getTemplate(); + } + + + public Mapping getMapping(Class resource); +} From 6e7d95a00fb50da3184843c9efac7621984cad34 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Tue, 8 Apr 2014 15:18:53 +0100 Subject: [PATCH 4/9] Refresh up to latest 2.8-SNAPSHOT code --- .../uri/internal/UriTemplateParser.java | 2 +- .../jersey/examples/linking/App.java | 9 +- .../examples/linking/model/ItemsModel.java | 15 ++- .../representation/ItemRepresentation.java | 62 +++++++-- .../representation/ItemsRepresentation.java | 122 ++++++++++++++---- .../linking/resources/ItemResource.java | 34 +---- .../linking/resources/ItemsResource.java | 78 +++++------ .../examples/linking/LinkWebAppTest.java | 13 +- .../linking/DeclarativeLinkingFeature.java | 27 +++- .../jersey/linking/ELLinkBuilder.java | 31 ++++- .../jersey/linking/EntityDescriptor.java | 2 +- .../jersey/linking/FieldProcessor.java | 26 ++-- .../jersey/linking/HeaderProcessor.java | 15 ++- .../jersey/linking/InjectLinkDescriptor.java | 3 +- .../linking/InjectLinkFieldDescriptor.java | 50 ++++--- .../jersey/linking/LinkHeaderDescriptor.java | 5 +- .../jersey/linking/RequestLinkFilter.java | 4 +- .../jersey/linking/ResponseLinkFilter.java | 13 +- .../jersey/linking/EntityDescriptorTest.java | 16 ++- .../jersey/linking/FieldProcessorTest.java | 106 ++++++++++++--- .../jersey/linking/HeaderProcessorTest.java | 82 ++++++++++-- 21 files changed, 514 insertions(+), 201 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java index 968e08e2b0..dece0d5c89 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java @@ -103,7 +103,7 @@ private static Set initReserved() { */ public UriTemplateParser(final String template) throws IllegalArgumentException { if (template == null || template.length() == 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Template is null or has zero length"); } this.template = template; diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java index 77cd9bbd35..83288796dc 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java @@ -45,13 +45,14 @@ import java.net.URI; import java.util.logging.Level; import java.util.logging.Logger; +import org.glassfish.grizzly.http.server.HttpHandler; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.jersey.linking.DeclarativeLinkingFeature; -import org.glassfish.jersey.examples.linking.resources.ItemResource; +import org.glassfish.jersey.examples.linking.resources.ItemsResource; /** * Show link injection in action @@ -66,12 +67,16 @@ public class App { public static void main(String[] args) { try { + Logger.getLogger(HttpHandler.class.getName()).setLevel(Level.ALL); + System.out.println("\"Declarative Linking\" Jersey Example App"); - final ResourceConfig resourceConfig = new ResourceConfig(ItemResource.class); + final ResourceConfig resourceConfig = new ResourceConfig(ItemsResource.class); + resourceConfig.register(DeclarativeLinkingFeature.class); final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig); + System.out.println(String.format("Application started.\nTry out %s%s\nHit enter to stop it...", BASE_URI, ROOT_PATH)); System.in.read(); diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java index fd9c9a8d02..ef1bd983fc 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java @@ -64,11 +64,10 @@ public static synchronized ItemsModel getInstance() { private ItemsModel() { items = new ArrayList(); - items.add(new ItemModel("Item 0")); - items.add(new ItemModel("Item 1")); - items.add(new ItemModel("Item 2")); - items.add(new ItemModel("Item 3")); - items.add(new ItemModel("Item 4")); + for(int i=0; i < 100; i++) + { + items.add(new ItemModel("Item " + i)); + } } public boolean hasNext(String currentId) { @@ -94,4 +93,10 @@ public String getPrevId(String id) { private int getIndex(String id) { return Integer.parseInt(id); } + + + public int getSize() + { + return items.size(); + } } diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java index bdb27e1e15..76aba52da7 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java @@ -52,7 +52,9 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.glassfish.jersey.examples.linking.model.ItemsModel; /** * JAXB representation of an item @@ -67,15 +69,15 @@ @InjectLink( resource = ItemResource.class, style = Style.ABSOLUTE, - condition = "${resource.next}", - bindings = @Binding(name = "id", value = "${resource.nextId}"), + condition = "${instance.next}", + bindings = @Binding(name = "id", value = "${instance.nextId}"), rel = "next" ), @InjectLink( resource = ItemResource.class, style = Style.ABSOLUTE, - condition = "${resource.prev}", - bindings = @Binding(name = "id", value = "${resource.prevId}"), + condition = "${instance.prev}", + bindings = @Binding(name = "id", value = "${instance.prevId}"), rel = "prev" ) }) @@ -84,10 +86,16 @@ public class ItemRepresentation { @XmlElement private String name; + @XmlTransient + private String id; + @XmlTransient + private ItemsModel itemsModel; + + @InjectLink( resource = ItemResource.class, style = Style.ABSOLUTE, - bindings = @Binding(name = "id", value = "${resource.id}"), + bindings = @Binding(name = "id", value = "${instance.id}"), rel = "self" ) @XmlJavaTypeAdapter(Link.JaxbAdapter.class) @@ -98,28 +106,56 @@ public class ItemRepresentation { @InjectLink( resource = ItemResource.class, style = Style.ABSOLUTE, - condition = "${resource.next}", - bindings = @Binding(name = "id", value = "${resource.nextId}"), + condition = "${instance.next}", + bindings = @Binding(name = "id", value = "${instance.nextId}"), rel = "next" ), @InjectLink( resource = ItemResource.class, style = Style.ABSOLUTE, - condition = "${resource.prev}", - bindings = @Binding(name = "id", value = "${resource.prevId}"), + condition = "${instance.prev}", + bindings = @Binding(name = "id", value = "${instance.prevId}"), rel = "prev" )}) @XmlElement(name="link") @XmlElementWrapper(name = "links") @XmlJavaTypeAdapter(Link.JaxbAdapter.class) List links; + - public ItemRepresentation() { - this.name = ""; + public ItemRepresentation() + { + } - - public ItemRepresentation(String name) { + + public ItemRepresentation(ItemsModel itemsModel, String id, String name) { + this.itemsModel = itemsModel; this.name = name; + this.id = id; + } + + + + public String getId() { + return id; + } + + + public boolean isNext() { + return itemsModel.hasNext(id); + } + + public boolean isPrev() { + return itemsModel.hasPrev(id); + } + + public String getNextId() { + return itemsModel.getNextId(id); + } + + public String getPrevId() { + return itemsModel.getPrevId(id); } + } diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java index bdb27e1e15..edc82de181 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java @@ -39,6 +39,7 @@ */ package org.glassfish.jersey.examples.linking.representation; +import java.util.ArrayList; import org.glassfish.jersey.examples.linking.resources.ItemResource; import org.glassfish.jersey.linking.Binding; import org.glassfish.jersey.linking.InjectLinks; @@ -52,61 +53,96 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.glassfish.jersey.examples.linking.model.ItemsModel; +import org.glassfish.jersey.examples.linking.resources.ItemsResource; /** - * JAXB representation of an item + * JAXB representation of a sublist of items * * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ @XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "item") +@XmlRootElement(name = "items") @InjectLinks({ @InjectLink( - resource = ItemResource.class, + resource = ItemsResource.class, style = Style.ABSOLUTE, - condition = "${resource.next}", - bindings = @Binding(name = "id", value = "${resource.nextId}"), + method = "query", + condition = "${instance.offset + instance.limit < instance.modelLimit}", + bindings = { + @Binding(name = "offset", value = "${instance.offset + instance.limit}"), + @Binding(name = "limit", value = "${instance.limit}") + }, rel = "next" ), @InjectLink( - resource = ItemResource.class, + resource = ItemsResource.class, style = Style.ABSOLUTE, - condition = "${resource.prev}", - bindings = @Binding(name = "id", value = "${resource.prevId}"), + method = "query", + condition = "${instance.offset - instance.limit >= 0}", + bindings = { + @Binding(name = "offset", value = "${instance.offset - instance.limit}"), + @Binding(name = "limit", value = "${instance.limit}") + }, rel = "prev" - ) -}) -public class ItemRepresentation { + )}) + +public class ItemsRepresentation { - @XmlElement - private String name; + @XmlElement(name="items") + private List items; + + @XmlTransient + private int offset, limit; + + @XmlTransient + private ItemsModel itemsModel; + + + + + @InjectLink( - resource = ItemResource.class, + resource = ItemsResource.class, + method = "query", style = Style.ABSOLUTE, - bindings = @Binding(name = "id", value = "${resource.id}"), + bindings = {@Binding(name = "offset", value="${instance.offset}"), + @Binding(name = "limit", value="${instance.limit}") + }, rel = "self" ) @XmlJavaTypeAdapter(Link.JaxbAdapter.class) @XmlElement(name="link") Link self; + + @InjectLinks({ @InjectLink( - resource = ItemResource.class, + resource = ItemsResource.class, style = Style.ABSOLUTE, - condition = "${resource.next}", - bindings = @Binding(name = "id", value = "${resource.nextId}"), + method = "query", + condition = "${instance.offset + instance.limit < instance.modelLimit}", + bindings = { + @Binding(name = "offset", value = "${instance.offset + instance.limit}"), + @Binding(name = "limit", value = "${instance.limit}") + }, rel = "next" ), @InjectLink( - resource = ItemResource.class, + resource = ItemsResource.class, style = Style.ABSOLUTE, - condition = "${resource.prev}", - bindings = @Binding(name = "id", value = "${resource.prevId}"), + method = "query", + condition = "${instance.offset - instance.limit >= 0}", + bindings = { + @Binding(name = "offset", value = "${instance.offset - instance.limit}"), + @Binding(name = "limit", value = "${instance.limit}") + }, rel = "prev" )}) @XmlElement(name="link") @@ -114,12 +150,46 @@ public class ItemRepresentation { @XmlJavaTypeAdapter(Link.JaxbAdapter.class) List links; - public ItemRepresentation() { - this.name = ""; - } - public ItemRepresentation(String name) { - this.name = name; + + + + public ItemsRepresentation() { + offset = 0; + limit = 10; + } + + public ItemsRepresentation(ItemsModel itemsModel, int offset, int limit) { + + this.offset = offset; + this.limit = limit; + this.itemsModel = itemsModel; + + items = new ArrayList(); + for (int i = offset; i < (offset + limit) && i < itemsModel.getSize(); i++) + { + items.add(new ItemRepresentation( + itemsModel, + Integer.toString(i), + itemsModel.getItem(Integer.toString(i)).getName())); + } + } + + + public int getOffset() + { + return offset; + } + + public int getLimit() + { + return limit; + } + + public int getModelLimit() + { + return itemsModel.getSize(); + } } diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java index cc93ee96e5..701391f564 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java @@ -57,7 +57,6 @@ * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ -@Path("items/{id}") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public class ItemResource { @@ -65,9 +64,13 @@ public class ItemResource { private ItemModel itemModel; private String id; - public ItemResource(@PathParam("id") String id) { + public ItemResource() { + throw new IllegalStateException("Only for JAX-B dressing"); + } + + public ItemResource(ItemsModel itemsModel, String id) { this.id = id; - itemsModel = ItemsModel.getInstance(); + this.itemsModel = itemsModel; try { itemModel = itemsModel.getItem(id); } catch (IndexOutOfBoundsException ex) { @@ -77,32 +80,9 @@ public ItemResource(@PathParam("id") String id) { @GET public ItemRepresentation get() { - return new ItemRepresentation(itemModel.getName()); - } - - /** - * Determines whether there is a next item. - * @return - */ - public boolean isNext() { - return itemsModel.hasNext(id); - } - - /** - * Determines whether there is a previous item - * @return - */ - public boolean isPrev() { - return itemsModel.hasPrev(id); - } - - public String getNextId() { - return itemsModel.getNextId(id); + return new ItemRepresentation(itemsModel, id, itemModel.getName()); } - public String getPrevId() { - return itemsModel.getPrevId(id); - } public String getId() { return id; diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java index cc93ee96e5..ea902f289d 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java @@ -40,71 +40,63 @@ package org.glassfish.jersey.examples.linking.resources; -import org.glassfish.jersey.examples.linking.model.ItemModel; +import javax.ws.rs.DefaultValue; import org.glassfish.jersey.examples.linking.model.ItemsModel; -import org.glassfish.jersey.examples.linking.representation.ItemRepresentation; import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glassfish.jersey.examples.linking.representation.ItemsRepresentation; /** - * Resource that provides access to one item from a set of items managed - * by ItemsModel + * Resource that provides access to the entire list of items * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ -@Path("items/{id}") +@Path("items") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) -public class ItemResource { +public class ItemsResource { private ItemsModel itemsModel; - private ItemModel itemModel; - private String id; - public ItemResource(@PathParam("id") String id) { - this.id = id; + public ItemsResource() { itemsModel = ItemsModel.getInstance(); - try { - itemModel = itemsModel.getItem(id); - } catch (IndexOutOfBoundsException ex) { - throw new NotFoundException(); - } } + @GET - public ItemRepresentation get() { - return new ItemRepresentation(itemModel.getName()); - } + public ItemsRepresentation query( + @Context javax.ws.rs.core.UriInfo info, + @QueryParam("offset") @DefaultValue("-1") int offset, @DefaultValue("-1") @QueryParam("limit") int limit) { + - /** - * Determines whether there is a next item. - * @return - */ - public boolean isNext() { - return itemsModel.hasNext(id); - } - - /** - * Determines whether there is a previous item - * @return - */ - public boolean isPrev() { - return itemsModel.hasPrev(id); - } - - public String getNextId() { - return itemsModel.getNextId(id); + if (offset==-1 || limit == -1) + { + offset = offset == -1 ? 0 : offset; + limit = limit == -1 ? 10 : limit; + + throw new WebApplicationException( + Response.seeOther(info.getRequestUriBuilder().queryParam("offset", offset) + .queryParam("limit", limit).build()) + .build() + ); + } + + + + return new ItemsRepresentation(itemsModel, offset, limit ); } - - public String getPrevId() { - return itemsModel.getPrevId(id); + + + @Path("{id}") + public ItemResource get(@PathParam("id") String id ) { + return new ItemResource(itemsModel, id); } - public String getId() { - return id; - } } diff --git a/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java b/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java index 9c3f6a285b..ec9131ea88 100644 --- a/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java +++ b/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java @@ -41,11 +41,12 @@ package org.glassfish.jersey.examples.linking; import java.util.List; +import javax.ws.rs.core.MediaType; -import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientResponse; -import org.glassfish.jersey.examples.linking.resources.ItemResource; +import org.glassfish.jersey.examples.linking.resources.ItemsResource; import org.glassfish.jersey.linking.DeclarativeLinkingFeature; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; @@ -63,7 +64,7 @@ public class LinkWebAppTest extends JerseyTest { @Override protected ResourceConfig configure() { enable(TestProperties.LOG_TRAFFIC); - ResourceConfig rc = new ResourceConfig(ItemResource.class); + ResourceConfig rc = new ResourceConfig(ItemsResource.class); rc.register(DeclarativeLinkingFeature.class); return rc; } @@ -73,7 +74,11 @@ protected ResourceConfig configure() { */ @Test public void testLinks() throws Exception { - Response response = target().path("items/1").request().get(Response.class); + + String wadl = target().path("application.wadl").request().get(String.class); + Response response = target().path("items/1").request().accept(MediaType.APPLICATION_XML_TYPE).get(Response.class); + final Response.StatusType statusInfo = response.getStatusInfo(); + Object content = response.readEntity(String.class); List linkHeaders = response.getHeaders().get("Link"); assertEquals(2, linkHeaders.size()); diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java index 32343cdc8c..71483f23e6 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java @@ -40,10 +40,17 @@ package org.glassfish.jersey.linking; +import javax.inject.Singleton; import javax.ws.rs.core.Configuration; import javax.ws.rs.core.Feature; import javax.ws.rs.core.FeatureContext; +import org.glassfish.hk2.api.PerThread; +import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.Beta; +import org.glassfish.jersey.internal.inject.Injections; +import org.glassfish.jersey.linking.mapping.NaiveResourceMappingContext; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.process.internal.RequestScoped; /** * A feature to enable the declarative linking functionality @@ -64,8 +71,26 @@ public boolean configure(FeatureContext context) { if (!config.isRegistered(ResponseLinkFilter.class)) { + // + context.register(new AbstractBinder() + { + + @Override + protected void configure() { + bindAsContract(NaiveResourceMappingContext.class).to( + ResourceMappingContext.class).in(Singleton.class); + } + }); + + + +// Injections.newBinder(NaiveResourceMappingContext.class).to( +// ResourceMappingContext.class).in(Singleton.class)); + context.register(ResponseLinkFilter.class); - // Todo map values back? + + +// Todo map values back? // context.register(RequestLinkFilter.class); return true; } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java index c17c870253..8f98e94ac7 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java @@ -44,10 +44,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.el.ELException; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.uri.UriTemplate; import org.glassfish.jersey.uri.internal.UriTemplateParser; /** @@ -68,13 +71,22 @@ public static boolean evaluateCondition(String condition, Object entity, LinkELContext context = new LinkELContext(entity, resource, instance); ValueExpression expr = expressionFactory.createValueExpression(context, condition, boolean.class); - Object result = expr.getValue(context).toString(); - return result.equals("true"); + try + { + Object result = expr.getValue(context).toString(); + return result.equals("true"); + } + catch (ELException ex) + { + throw ex; + } } public static URI buildURI(InjectLinkDescriptor link, Object entity, Object resource, Object instance, - UriInfo uriInfo) { - String template = link.getLinkTemplate(); + UriInfo uriInfo, ResourceMappingContext rmc) { + String template = link.getLinkTemplate(rmc); + + // first process any embedded EL expressions LinkELContext context = new LinkELContext(entity, resource, instance); @@ -114,8 +126,15 @@ private static Map getParameterValues(List parameterName String elExpression = getEL(name, linkField); ValueExpression expr = expressionFactory.createValueExpression(context, elExpression, String.class); - Object value = expr.getValue(context); - values.put(name, value); + try + { + Object value = expr.getValue(context); + values.put(name, value!=null ? value.toString() : null); + } + catch (ELException ex) + { + throw ex; + } } return values; } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java index 3b83321c09..b69c446e08 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java @@ -9,7 +9,7 @@ * may not use this file except in compliance with the License. You can * obtain a copy of the License at * http://glassfish.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific + * or packager/legal/gLICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/FieldProcessor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/FieldProcessor.java index 9f2a6bc534..00c603bad8 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/FieldProcessor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/FieldProcessor.java @@ -49,10 +49,10 @@ import java.util.List; import java.util.Set; import javax.ws.rs.core.Link; - import javax.ws.rs.core.UriInfo; -import org.glassfish.jersey.linking.LinkMessages; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.server.ExtendedUriInfo; /** * Utility class that can inject links into {@link com.sun.jersey.server.linking.Link} annotated fields in @@ -76,10 +76,10 @@ public FieldProcessor(Class c) { * @param entity the entity object returned by the resource method * @param uriInfo the uriInfo for the request */ - public void processLinks(T entity, UriInfo uriInfo) { + public void processLinks(T entity, UriInfo uriInfo, ResourceMappingContext rmc) { Set processed = new HashSet(); Object resource = uriInfo.getMatchedResources().get(0); - processLinks(entity, resource, entity, processed, uriInfo); + processLinks(entity, resource, entity, processed, uriInfo, rmc); } /** @@ -91,7 +91,8 @@ public void processLinks(T entity, UriInfo uriInfo) { * @param uriInfo */ private void processLinks(Object entity, Object resource, Object instance, - Set processed, UriInfo uriInfo) { + Set processed, UriInfo uriInfo, + ResourceMappingContext rmc) { try { if (instance == null || processed.contains(instance)) @@ -110,7 +111,7 @@ private void processLinks(Object entity, Object resource, Object instance, { InjectLinkFieldDescriptor linkField = (InjectLinkFieldDescriptor) field; if (ELLinkBuilder.evaluateCondition(linkField.getCondition(), entity, resource, instance)) { - URI uri = ELLinkBuilder.buildURI(linkField, entity, resource, instance, uriInfo); + URI uri = ELLinkBuilder.buildURI(linkField, entity, resource, instance, uriInfo, rmc); linkField.setPropertyValue(instance, uri); } } else if (field instanceof InjectLinksFieldDescriptor) { @@ -120,7 +121,7 @@ private void processLinks(Object entity, Object resource, Object instance, for (InjectLinkFieldDescriptor linkField : linksField.getLinksToInject()) { if (ELLinkBuilder.evaluateCondition(linkField.getCondition(), entity, resource, instance)) { - URI uri = ELLinkBuilder.buildURI(linkField, entity, resource, instance, uriInfo); + URI uri = ELLinkBuilder.buildURI(linkField, entity, resource, instance, uriInfo, rmc); Link link = linkField.getLink(uri); list.add(link); } @@ -137,26 +138,27 @@ private void processLinks(Object entity, Object resource, Object instance, if (instanceClass.isArray() && Object[].class.isAssignableFrom(instanceClass)) { Object array[] = (Object[]) instance; for (Object member : array) { - processMember(entity, resource, member, processed, uriInfo); + processMember(entity, resource, member, processed, uriInfo,rmc); } } else if (instance instanceof Collection) { Collection collection = (Collection) instance; for (Object member : collection) { - processMember(entity, resource, member, processed, uriInfo); + processMember(entity, resource, member, processed, uriInfo,rmc); } } // Recursively process all member fields for (FieldDescriptor member : instanceDescriptor.getNonLinkFields()) { - processMember(entity, resource, member.getFieldValue(instance), processed, uriInfo); + processMember(entity, resource, member.getFieldValue(instance), processed, uriInfo,rmc); } } - private void processMember(Object entity, Object resource, Object member, Set processed, UriInfo uriInfo) { + private void processMember(Object entity, Object resource, Object member, Set processed, UriInfo uriInfo, + ResourceMappingContext rmc) { if (member != null) { FieldProcessor proc = new FieldProcessor(member.getClass()); - proc.processLinks(entity, resource, member, processed, uriInfo); + proc.processLinks(entity, resource, member, processed, uriInfo, rmc); } } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/HeaderProcessor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/HeaderProcessor.java index 0622b2870a..8216f3e254 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/HeaderProcessor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/HeaderProcessor.java @@ -47,6 +47,8 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.server.ExtendedUriInfo; /** @@ -70,14 +72,14 @@ public HeaderProcessor(Class c) { * @param uriInfo the uriInfo for the request * @param headers the map into which the headers will be added */ - public void processLinkHeaders(T entity, UriInfo uriInfo, MultivaluedMap headers) { - List headerValues = getLinkHeaderValues(entity, uriInfo); + public void processLinkHeaders(T entity, UriInfo uriInfo, ResourceMappingContext rmc, MultivaluedMap headers) { + List headerValues = getLinkHeaderValues(entity, uriInfo,rmc); for (String headerValue: headerValues) { headers.add("Link", headerValue); } } - List getLinkHeaderValues(Object entity, UriInfo uriInfo) { + List getLinkHeaderValues(Object entity, UriInfo uriInfo, ResourceMappingContext rmc) { final List matchedResources = uriInfo.getMatchedResources(); if (!matchedResources.isEmpty()) { @@ -86,7 +88,7 @@ List getLinkHeaderValues(Object entity, UriInfo uriInfo) { for (LinkHeaderDescriptor desc: instanceDescriptor.getLinkHeaders()) { if (ELLinkBuilder.evaluateCondition(desc.getCondition(), entity, resource, entity)) { - String headerValue = getLinkHeaderValue(desc, entity, resource, uriInfo); + String headerValue = getLinkHeaderValue(desc, entity, resource, uriInfo, rmc); headerValues.add(headerValue); } } @@ -96,8 +98,9 @@ List getLinkHeaderValues(Object entity, UriInfo uriInfo) { return Collections.emptyList(); } - static String getLinkHeaderValue(LinkHeaderDescriptor desc, Object entity, Object resource, UriInfo uriInfo) { - URI uri = ELLinkBuilder.buildURI(desc, entity, resource, entity, uriInfo); + static String getLinkHeaderValue(LinkHeaderDescriptor desc, Object entity, Object resource, UriInfo uriInfo, + ResourceMappingContext rmc) { + URI uri = ELLinkBuilder.buildURI(desc, entity, resource, entity, uriInfo, rmc); InjectLink link = desc.getLinkHeader(); return InjectLink.Util.buildLinkFromUri(uri, link).toString(); } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkDescriptor.java index e4d5188b59..58d88b0c42 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkDescriptor.java @@ -41,6 +41,7 @@ package org.glassfish.jersey.linking; import org.glassfish.jersey.linking.InjectLink; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; /** * Utility for working with @Ref annotations @@ -60,7 +61,7 @@ interface InjectLinkDescriptor { * @Path of the class referenced in resource() * @return the link template */ - String getLinkTemplate(); + String getLinkTemplate(ResourceMappingContext rmc); /** * Get the binding as an EL expression for a particular URI template parameter diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java index bca271cde5..52f06c447c 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java @@ -48,9 +48,11 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import javax.ws.rs.HttpMethod; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Link; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; import org.glassfish.jersey.server.model.AnnotatedMethod; import org.glassfish.jersey.server.model.MethodList; @@ -115,33 +117,51 @@ public InjectLink.Style getLinkStyle() { return link.style(); } - public String getLinkTemplate() { - return getLinkTemplate(link); + public String getLinkTemplate(ResourceMappingContext rmc) { + return getLinkTemplate(rmc, link); } - public static String getLinkTemplate(InjectLink link) { + + public static String getLinkTemplate(ResourceMappingContext rmc, InjectLink link) { String template = null; if (!link.resource().equals(Class.class)) { + + + ResourceMappingContext.Mapping map = rmc.getMapping(link.resource()); + if (map!=null) + { + template = map.getTemplate().getTemplate().toString(); + } + else + { + // extract template from specified class' @Path annotation + Path path = link.resource().getAnnotation(Path.class); + template = path==null ? "" : path.value(); + } + // extract template from specified class' @Path annotation - Path path = link.resource().getAnnotation(Path.class); - template = path==null ? "" : path.value(); if (link.method().length() > 0) { // append value of method's @Path annotation MethodList methods = new MethodList(link.resource()); - methods = methods.withAnnotation(Path.class); + methods = methods.withMetaAnnotation(HttpMethod.class); Iterator iterator = methods.iterator(); while (iterator.hasNext()) { AnnotatedMethod method = iterator.next(); if (!method.getMethod().getName().equals(link.method())) continue; - Path methodPath = method.getAnnotation(Path.class); - String methodTemplate = methodPath.value(); StringBuilder builder = new StringBuilder(); builder.append(template); - if (!(template.endsWith("/") || methodTemplate.startsWith("/"))) - builder.append("/"); - builder.append(methodTemplate); - + + Path methodPath = method.getAnnotation(Path.class); + if (methodPath!=null) + { + String methodTemplate = methodPath.value(); + + if (!(template.endsWith("/") || methodTemplate.startsWith("/"))) + builder.append("/"); + builder.append(methodTemplate); + } + // append query parameters StringBuilder querySubString = new StringBuilder(); for (Annotation paramAnns[] : method.getParameterAnnotations()){ @@ -152,15 +172,15 @@ public static String getLinkTemplate(InjectLink link) { } } } - + if (querySubString.length() > 0) { builder.append("{?"); builder.append(querySubString.subSequence(0, querySubString.length()-1)); builder.append("}"); } - - + + template = builder.toString(); break; } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkHeaderDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkHeaderDescriptor.java index 374dbb57fc..a4d2c6f6ec 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkHeaderDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/LinkHeaderDescriptor.java @@ -43,6 +43,7 @@ import org.glassfish.jersey.linking.InjectLink.Style; import java.util.HashMap; import java.util.Map; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; /** * Utility class for working with {@link LinkHeader} annotations @@ -67,8 +68,8 @@ public InjectLink getLinkHeader() { return linkHeader; } - public String getLinkTemplate() { - return InjectLinkFieldDescriptor.getLinkTemplate(linkHeader); + public String getLinkTemplate(ResourceMappingContext rmc) { + return InjectLinkFieldDescriptor.getLinkTemplate(rmc, linkHeader); } public Style getLinkStyle() { diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java index f8e7c419a1..8edad40c39 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java @@ -42,11 +42,11 @@ import java.io.IOException; import javax.ws.rs.core.Context; -import javax.ws.rs.core.UriInfo; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.Link; +import org.glassfish.jersey.server.ExtendedUriInfo; /** * Filter that processes {@link Link} annotated fields in returned response @@ -71,7 +71,7 @@ class RequestLinkFilter implements ContainerRequestFilter { @Context - private UriInfo uriInfo; + private ExtendedUriInfo uriInfo; @Override public void filter(ContainerRequestContext requestContext) throws IOException { diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java index 333b498bad..db28610744 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java @@ -41,12 +41,14 @@ package org.glassfish.jersey.linking; import javax.ws.rs.core.Context; -import javax.ws.rs.core.UriInfo; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.Link; +import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.server.ExtendedUriInfo; /** * Filter that processes {@link Link} annotated fields in returned response @@ -73,15 +75,20 @@ class ResponseLinkFilter implements ContainerResponseFilter { @Context private UriInfo uriInfo; + @Context + private ResourceMappingContext rmc; + public void filter(ContainerRequestContext request, ContainerResponseContext response) { final Object entity = response.getEntity(); if (entity != null && !uriInfo.getMatchedResources().isEmpty()) { Class entityClass = entity.getClass(); HeaderProcessor lhp = new HeaderProcessor(entityClass); - lhp.processLinkHeaders(entity, uriInfo, response.getHeaders()); + lhp.processLinkHeaders(entity, uriInfo, rmc, response.getHeaders()); FieldProcessor lp = new FieldProcessor(entityClass); - lp.processLinks(entity, uriInfo); + lp.processLinks(entity, uriInfo, rmc); + + int i = 0; } } diff --git a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/EntityDescriptorTest.java b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/EntityDescriptorTest.java index 45aff29bd8..fb70345874 100644 --- a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/EntityDescriptorTest.java +++ b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/EntityDescriptorTest.java @@ -44,6 +44,7 @@ import java.util.Iterator; import javax.ws.rs.Path; import javax.ws.rs.core.UriBuilder; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; import org.junit.Test; import static org.junit.Assert.*; @@ -65,6 +66,15 @@ public static class TestClassA { public String baz; } + + ResourceMappingContext mockRmc = new ResourceMappingContext() { + + @Override + public ResourceMappingContext.Mapping getMapping(Class resource) { + return null; + } + }; + /** * Test for declared properties @@ -111,7 +121,7 @@ public void testResourceLink() { assertEquals(1, instance.getLinkFields().size()); assertEquals(0, instance.getNonLinkFields().size()); InjectLinkFieldDescriptor linkDesc = (InjectLinkFieldDescriptor)instance.getLinkFields().iterator().next(); - assertEquals(TEMPLATE_A, linkDesc.getLinkTemplate()); + assertEquals(TEMPLATE_A, linkDesc.getLinkTemplate(mockRmc)); assertEquals("baz", linkDesc.getBinding("bar")); } @@ -132,7 +142,7 @@ public void testStringLink() { Iterator i = instance.getLinkFields().iterator(); while (i.hasNext()) { InjectLinkFieldDescriptor linkDesc = (InjectLinkFieldDescriptor)i.next(); - assertEquals(TEMPLATE_A, linkDesc.getLinkTemplate()); + assertEquals(TEMPLATE_A, linkDesc.getLinkTemplate(mockRmc)); } } @@ -144,7 +154,7 @@ public void testSetLink() { TestClassD testClass = new TestClassD(); while (i.hasNext()) { InjectLinkFieldDescriptor linkDesc = (InjectLinkFieldDescriptor)i.next(); - URI value = UriBuilder.fromPath(linkDesc.getLinkTemplate()).build(); + URI value = UriBuilder.fromPath(linkDesc.getLinkTemplate(mockRmc)).build(); linkDesc.setPropertyValue(testClass, value); } assertEquals(TEMPLATE_A, testClass.res1); diff --git a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java index 703ced1131..cab405c6ff 100644 --- a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java +++ b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java @@ -48,11 +48,18 @@ import java.util.logging.Filter; import java.util.logging.LogRecord; import java.util.logging.Logger; +import java.util.regex.MatchResult; import java.util.zip.ZipEntry; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.*; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.server.ExtendedUriInfo; +import org.glassfish.jersey.server.model.Resource; +import org.glassfish.jersey.server.model.ResourceMethod; +import org.glassfish.jersey.server.model.RuntimeResource; +import org.glassfish.jersey.uri.UriTemplate; import static org.junit.Assert.*; import org.junit.Test; @@ -64,7 +71,7 @@ */ public class FieldProcessorTest { - UriInfo mockUriInfo = new UriInfo() { + ExtendedUriInfo mockUriInfo = new ExtendedUriInfo() { private final static String baseURI = "http://example.com/application/resources"; @@ -147,7 +154,66 @@ public URI relativize(URI uri) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + @Override + public Throwable getMappedThrowable() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedResults() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedTemplates() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getPathSegments(String name) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getPathSegments(String name, boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedRuntimeResources() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ResourceMethod getMatchedResourceMethod() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Resource getMatchedModelResource() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedResourceLocators() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getLocatorSubResources() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + + ResourceMappingContext mockRmc = new ResourceMappingContext() { + + @Override + public ResourceMappingContext.Mapping getMapping(Class resource) { + return null; + } + }; + private final static String TEMPLATE_A = "foo"; @@ -165,7 +231,7 @@ public void testProcessLinks() { System.out.println("Links"); FieldProcessor instance = new FieldProcessor(TestClassD.class); TestClassD testClass = new TestClassD(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals(TEMPLATE_A, testClass.res1); assertEquals(TEMPLATE_A, testClass.res2.toString()); } @@ -192,7 +258,7 @@ public void testProcessLinksWithFields() { System.out.println("Links from field values"); FieldProcessor instance = new FieldProcessor(TestClassE.class); TestClassE testClass = new TestClassE("10"); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("widgets/10", testClass.link); } @@ -219,7 +285,7 @@ public void testNesting() { FieldProcessor instance = new FieldProcessor(TestClassF.class); TestClassE nested = new TestClassE("10"); TestClassF testClass = new TestClassF("20", nested); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("widgets/20", testClass.thelink); assertEquals("widgets/10", testClass.nested.link); } @@ -231,7 +297,7 @@ public void testArray() { TestClassE item1 = new TestClassE("10"); TestClassE item2 = new TestClassE("20"); TestClassE array[] = {item1, item2}; - instance.processLinks(array, mockUriInfo); + instance.processLinks(array, mockUriInfo, mockRmc); assertEquals("widgets/10", array[0].link); assertEquals("widgets/20", array[1].link); } @@ -243,7 +309,7 @@ public void testCollection() { TestClassE item1 = new TestClassE("10"); TestClassE item2 = new TestClassE("20"); List list = Arrays.asList(item1, item2); - instance.processLinks(list, mockUriInfo); + instance.processLinks(list, mockUriInfo, mockRmc); assertEquals("widgets/10", list.get(0).link); assertEquals("widgets/20", list.get(1).link); } @@ -277,7 +343,7 @@ public void testLinkStyles() { System.out.println("Link styles"); FieldProcessor instance = new FieldProcessor(TestClassG.class); TestClassG testClass = new TestClassG("10"); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("widgets/10", testClass.relativePath); assertEquals("/application/resources/widgets/10", testClass.absolutePath); assertEquals("/application/resources/widgets/10", testClass.defaultStyle); @@ -298,7 +364,7 @@ public void testComputedProperty() { System.out.println("Computed property"); FieldProcessor instance = new FieldProcessor(TestClassH.class); TestClassH testClass = new TestClassH(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/widgets/10", testClass.link); } @@ -316,7 +382,7 @@ public void testEL() { System.out.println("EL link"); FieldProcessor instance = new FieldProcessor(TestClassI.class); TestClassI testClass = new TestClassI(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/widgets/10", testClass.link); } @@ -334,7 +400,7 @@ public void testMixed() { System.out.println("Mixed EL and template vars link"); FieldProcessor instance = new FieldProcessor(TestClassJ.class); TestClassJ testClass = new TestClassJ(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/widgets/10/widget/10", testClass.link); } @@ -360,7 +426,7 @@ public void testELScopes() { System.out.println("EL scopes"); FieldProcessor instance = new FieldProcessor(OuterBean.class); OuterBean testClass = new OuterBean(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/inner", testClass.inner.innerUri); assertEquals("/application/resources/outer", testClass.inner.outerUri); } @@ -379,7 +445,7 @@ public void testELBinding() { System.out.println("EL binding"); FieldProcessor instance = new FieldProcessor(BoundLinkBean.class); BoundLinkBean testClass = new BoundLinkBean(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/name", testClass.uri); } @@ -399,7 +465,7 @@ public void testELBindingOnLink() { System.out.println("EL binding"); FieldProcessor instance = new FieldProcessor(BoundLinkOnLinkBean.class); BoundLinkOnLinkBean testClass = new BoundLinkOnLinkBean(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/name", testClass.link.getUri().toString()); assertEquals("self", testClass.link.getRel()); } @@ -440,7 +506,7 @@ public void testELBindingOnLinks() { System.out.println("EL binding"); FieldProcessor instance = new FieldProcessor(BoundLinkOnLinksBean.class); BoundLinkOnLinksBean testClass = new BoundLinkOnLinksBean(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/name", testClass.links.get(0).getUri().toString()); assertEquals("self", testClass.links.get(0).getRel()); assertEquals("other", testClass.links.get(1).getRel()); @@ -478,7 +544,7 @@ public void testCondition() { System.out.println("Condition"); FieldProcessor instance = new FieldProcessor(ConditionalLinkBean.class); ConditionalLinkBean testClass = new ConditionalLinkBean(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/name", testClass.uri1); assertEquals(null, testClass.uri2); } @@ -502,7 +568,7 @@ public void testSubresource() { System.out.println("Subresource"); FieldProcessor instance = new FieldProcessor(SubResourceBean.class); SubResourceBean testClass = new SubResourceBean(); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/a/b", testClass.uri); } @@ -549,7 +615,7 @@ public void testQueryResource() { System.out.println("QueryResource"); FieldProcessor instance = new FieldProcessor(QueryResourceBean.class); QueryResourceBean testClass = new QueryResourceBean("queryExample",null); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/a/b?query=queryExample", testClass.uri); } @@ -558,7 +624,7 @@ public void testDoubleQueryResource() { System.out.println("QueryResource"); FieldProcessor instance = new FieldProcessor(QueryResourceBean.class); QueryResourceBean testClass = new QueryResourceBean("queryExample","queryExample2"); - instance.processLinks(testClass, mockUriInfo); + instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/a/b?query=queryExample&query2=queryExample2", testClass.uri); } @@ -596,13 +662,13 @@ public void testKL() { FieldProcessor instanceK = new FieldProcessor(TestClassK.class); TestClassK testClassK = new TestClassK(); - instanceK.processLinks(testClassK, mockUriInfo); + instanceK.processLinks(testClassK, mockUriInfo, mockRmc); assertTrue(lf.getCount() == 0); FieldProcessor instanceL = new FieldProcessor(TestClassL.class); TestClassL testClassL = new TestClassL(); - instanceL.processLinks(testClassL, mockUriInfo); + instanceL.processLinks(testClassL, mockUriInfo, mockRmc); assertTrue(lf.getCount() == 0); diff --git a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/HeaderProcessorTest.java b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/HeaderProcessorTest.java index 7779f42573..664a7bf85b 100644 --- a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/HeaderProcessorTest.java +++ b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/HeaderProcessorTest.java @@ -44,10 +44,16 @@ import java.net.URI; import java.util.Collections; import java.util.List; +import java.util.regex.MatchResult; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.linking.mapping.ResourceMappingContext; +import org.glassfish.jersey.server.ExtendedUriInfo; +import org.glassfish.jersey.server.model.Resource; +import org.glassfish.jersey.server.model.ResourceMethod; +import org.glassfish.jersey.server.model.RuntimeResource; +import org.glassfish.jersey.uri.UriTemplate; import static org.junit.Assert.*; import org.junit.Test; @@ -60,7 +66,7 @@ */ public class HeaderProcessorTest { - UriInfo mockUriInfo = new UriInfo() { + ExtendedUriInfo mockUriInfo = new ExtendedUriInfo() { private final static String baseURI = "http://example.com/application/resources"; @@ -143,8 +149,68 @@ public URI relativize(URI uri) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + @Override + public Throwable getMappedThrowable() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedResults() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedTemplates() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getPathSegments(String name) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getPathSegments(String name, boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedRuntimeResources() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ResourceMethod getMatchedResourceMethod() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Resource getMatchedModelResource() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getMatchedResourceLocators() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List getLocatorSubResources() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + + ResourceMappingContext mockRmc = new ResourceMappingContext() { + + @Override + public ResourceMappingContext.Mapping getMapping(Class resource) { + return null; + } + }; + + @InjectLink(value="A") public static class EntityA { @@ -155,7 +221,7 @@ public void testLiteral() { System.out.println("Literal"); HeaderProcessor instance = new HeaderProcessor(EntityA.class); EntityA testClass = new EntityA(); - List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo); + List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc); assertEquals(1, headerValues.size()); String headerValue = headerValues.get(0); assertEquals("", headerValue); @@ -173,7 +239,7 @@ public void testEL() { System.out.println("EL"); HeaderProcessor instance = new HeaderProcessor(EntityB.class); EntityB testClass = new EntityB(); - List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo); + List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc); assertEquals(1, headerValues.size()); String headerValue = headerValues.get(0); assertEquals("", headerValue); @@ -191,7 +257,7 @@ public void testTemplateLiteral() { System.out.println("Template Literal"); HeaderProcessor instance = new HeaderProcessor(EntityC.class); EntityC testClass = new EntityC(); - List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo); + List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc); assertEquals(1, headerValues.size()); String headerValue = headerValues.get(0); assertEquals("", headerValue); @@ -209,7 +275,7 @@ public void testMultiple() { System.out.println("Multiple Literal"); HeaderProcessor instance = new HeaderProcessor(EntityD.class); EntityD testClass = new EntityD(); - List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo); + List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc); assertEquals(2, headerValues.size()); // not sure if annotation order is supposed to be preserved but it seems // to work as expected @@ -240,7 +306,7 @@ public void testParameters() { System.out.println("Parameters"); HeaderProcessor instance = new HeaderProcessor(EntityE.class); EntityE testClass = new EntityE(); - List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo); + List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc); assertEquals(1, headerValues.size()); String headerValue = headerValues.get(0); assertTrue(headerValue.contains("")); @@ -279,7 +345,7 @@ public void testConditional() { System.out.println("EL"); HeaderProcessor instance = new HeaderProcessor(EntityF.class); EntityF testClass = new EntityF(); - List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo); + List headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc); assertEquals(1, headerValues.size()); String headerValue = headerValues.get(0); assertEquals("", headerValue); From db98401a7dce432eee8c70795ffff99dfbd003ee Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Tue, 8 Apr 2014 17:31:20 +0100 Subject: [PATCH 5/9] Put in better unit test for example --- .../org/glassfish/jersey/examples/linking/App.java | 4 ++-- .../jersey/examples/linking/LinkWebAppTest.java | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java index 83288796dc..868bcc52ce 100644 --- a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java +++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java @@ -63,7 +63,7 @@ public class App { private static final URI BASE_URI = URI.create("http://localhost:8080/"); - public static final String ROOT_PATH = "items/0"; + public static final String ROOT_PATH = "items"; public static void main(String[] args) { try { @@ -77,7 +77,7 @@ public static void main(String[] args) { final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig); - System.out.println(String.format("Application started.\nTry out %s%s\nHit enter to stop it...", + System.out.println(String.format("Application started.\nTry out curl -L %s%s\nHit enter to stop it...", BASE_URI, ROOT_PATH)); System.in.read(); server.shutdownNow(); diff --git a/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java b/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java index ec9131ea88..fb559b95a2 100644 --- a/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java +++ b/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java @@ -54,6 +54,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * @author Naresh (Srinivas.Bhimisetty@Sun.Com) @@ -75,12 +76,15 @@ protected ResourceConfig configure() { @Test public void testLinks() throws Exception { - String wadl = target().path("application.wadl").request().get(String.class); - Response response = target().path("items/1").request().accept(MediaType.APPLICATION_XML_TYPE).get(Response.class); + Response response = target().path("items").queryParam("offset", 10).queryParam("limit", "10").request().accept(MediaType.APPLICATION_XML_TYPE).get(Response.class); final Response.StatusType statusInfo = response.getStatusInfo(); - Object content = response.readEntity(String.class); + assertEquals("Should have succeeded", 200, statusInfo.getStatusCode()); + + + String content = response.readEntity(String.class); List linkHeaders = response.getHeaders().get("Link"); - assertEquals(2, linkHeaders.size()); + assertEquals("Should have two link headers", 2, linkHeaders.size()); + assertTrue("Content should contain next link",content.contains("http://localhost:9998/items?offset=20&limit=10")); } } From 461db69704f62469b6d11eea15b3e172fce420a5 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Tue, 6 May 2014 11:35:44 +0100 Subject: [PATCH 6/9] Apply changes from Marek's final review patch to branch. Happy with changes. --- .../org/glassfish/jersey/uri/PathPattern.java | 18 +- .../jersey/uri/PatternWithGroups.java | 8 +- .../org/glassfish/jersey/uri/UriTemplate.java | 316 ++++++++-------- .../uri/internal/UriTemplateParser.java | 106 +++--- .../glassfish/jersey/uri/PathPatternTest.java | 9 - .../glassfish/jersey/uri/UriTemplateTest.java | 273 ++++++++++---- .../linking/DeclarativeLinkingFeature.java | 40 +- .../jersey/linking/ELLinkBuilder.java | 96 +++-- .../linking/InjectLinkFieldDescriptor.java | 109 +++--- .../linking/InjectLinksFieldDescriptor.java | 60 +-- .../jersey/linking/RequestLinkFilter.java | 10 +- .../jersey/linking/ResponseLinkFilter.java | 19 +- .../jersey/linking/FieldProcessorTest.java | 353 +++++++++--------- 13 files changed, 764 insertions(+), 653 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java b/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java index efdc7e1bb8..a0c63fa2e5 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java @@ -40,7 +40,6 @@ package org.glassfish.jersey.uri; import java.util.Comparator; -import java.util.regex.MatchResult; /** * A path pattern that is a regular expression generated from a URI path @@ -65,11 +64,11 @@ public final class PathPattern extends PatternWithGroups { * Path pattern matching the end of a URI path. Can be either empty {@code ""} * or contain a trailing slash {@code "/"}. */ - public static final PathPattern END_OF_PATH_PATTERN = new PathPattern("", PathPattern.RightHandPath.capturingZeroSegments); + public static final PathPattern END_OF_PATH_PATTERN = new PathPattern("", PathPattern.RightHandPath.capturingZeroSegments); /** * Path pattern matching the any URI path. */ - public static final PathPattern OPEN_ROOT_PATH_PATTERN = new PathPattern("", RightHandPath.capturingZeroOrMoreSegments); + public static final PathPattern OPEN_ROOT_PATH_PATTERN = new PathPattern("", RightHandPath.capturingZeroOrMoreSegments); /** * Path pattern comparator that defers to {@link UriTemplate#COMPARATOR comparing * the templates} associated with the patterns. @@ -120,6 +119,7 @@ private String getRegex() { public static PathPattern asClosed(PathPattern pattern) { return new PathPattern(pattern.getTemplate().getTemplate(), RightHandPath.capturingZeroSegments); } + // private final UriTemplate template; @@ -133,7 +133,6 @@ private PathPattern() { * {@link RightHandPath#capturingZeroOrMoreSegments}. * * @param template the path template. - * * @see #PathPattern(String, PathPattern.RightHandPath) */ public PathPattern(String template) { @@ -145,7 +144,6 @@ public PathPattern(String template) { * {@link RightHandPath#capturingZeroOrMoreSegments}. * * @param template the path template - * * @see #PathPattern(PathTemplate, PathPattern.RightHandPath) */ public PathPattern(PathTemplate template) { @@ -159,7 +157,7 @@ public PathPattern(PathTemplate template) { * Create a path pattern and post fix with a right hand path pattern. * * @param template the path template. - * @param rhpp the right hand path pattern postfix. + * @param rhpp the right hand path pattern postfix. */ public PathPattern(String template, RightHandPath rhpp) { this(new PathTemplate(template), rhpp); @@ -169,7 +167,7 @@ public PathPattern(String template, RightHandPath rhpp) { * Create a path pattern and post fix with a right hand path pattern. * * @param template the path template. - * @param rhpp the right hand path pattern postfix. + * @param rhpp the right hand path pattern postfix. */ public PathPattern(PathTemplate template, RightHandPath rhpp) { super(postfixWithCapturingGroup(template.getPattern().getRegex(), rhpp), @@ -194,15 +192,15 @@ private static String postfixWithCapturingGroup(String regex, RightHandPath rhpp return regex + rhpp.getRegex(); } - private static int[] addIndexForRightHandPathCapturingGroup(int numberOfGroups, int[] indexes) { + private static int[] addIndexForRightHandPathCapturingGroup(int numberOfGroups, int[] indexes) { if (indexes.length == 0) { return indexes; } - + int[] cgIndexes = new int[indexes.length + 1]; System.arraycopy(indexes, 0, cgIndexes, 0, indexes.length); - cgIndexes[indexes.length] = numberOfGroups +1; + cgIndexes[indexes.length] = numberOfGroups + 1; return cgIndexes; } } diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java b/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java index dfdc6e8039..8b609abe58 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/PatternWithGroups.java @@ -97,7 +97,8 @@ public PatternWithGroups(final String regex) throws PatternSyntaxException { /** * Construct a new pattern. * - * @param regex the regular expression. If the expression is {@code null} or an empty string then the pattern will only + * @param regex the regular expression. If the expression is {@code null} or an empty string then the pattern will + * only * match * a {@code null} or empty string. * @param groupIndexes the array of group indexes to capturing groups. @@ -351,7 +352,7 @@ public final boolean match(final CharSequence cs, final List groupValues * @throws IllegalArgumentException if group values is {@code null}. */ public final boolean match(final CharSequence cs, final List groupNames, final Map groupValues) throws IllegalArgumentException { + String> groupValues) throws IllegalArgumentException { if (groupValues == null) { throw new IllegalArgumentException(); } @@ -371,8 +372,7 @@ public final boolean match(final CharSequence cs, final List groupNames, // Assign the matched group values to group names groupValues.clear(); - - + for (int i = 0; i < groupNames.size(); i++) { String name = groupNames.get(i); String currentValue = m.group((groupIndexes.length > 0) ? groupIndexes[i] : i + 1); diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java index 64bff85f84..45299374e4 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java @@ -63,7 +63,7 @@ * @author Gerard Davison (gerard.davison at oracle.com) */ public class UriTemplate { - private static String[] EMPTY_VALUES = new String[0]; + private static final String[] EMPTY_VALUES = new String[0]; /** * Order the templates according to JAX-RS specification. @@ -132,24 +132,32 @@ public int compare(UriTemplate o1, UriTemplate o2) { return o2.pattern.getRegex().compareTo(o1.pattern.getRegex()); } }; - - + /** * A strategy interface for processing parameters, should be replaced with * a JDK 8 one day in the future. */ - private interface NameStrategy - { - public String getValueFor(String tVariable, String matchedGroup); + private static interface TemplateValueStrategy { + /** + * Get a value for a given template variable. + * + * @param templateVariable template variable. + * @param matchedGroup matched group string for a given template variable. + * @return template value. + * + * @throws java.lang.IllegalArgumentException in case no value has been found and the strategy + * does not support {@code null} values. + */ + public String valueFor(String templateVariable, String matchedGroup); } /** * The regular expression for matching URI templates and names. */ private static final Pattern TEMPLATE_NAMES_PATTERN = Pattern.compile("\\{([\\w\\?;][-\\w\\.,]*)\\}"); - + /** - * The empty URI template that matches the null or empty URI path. + * The empty URI template that matches the {@code null} or empty URI path. */ public static final UriTemplate EMPTY = new UriTemplate(); /** @@ -180,10 +188,10 @@ private interface NameStrategy private final int numOfExplicitRegexes; /** - * The number regular expression groups in this pattern + * The number of regular expression groups in this pattern. */ private final int numOfRegexGroups; - + /** * The number of characters in the regular expression not resulting * from conversion of template variables. @@ -219,8 +227,7 @@ private UriTemplate() { * an empty string. */ @SuppressWarnings("DuplicateThrows") - public UriTemplate(String template) throws - PatternSyntaxException, IllegalArgumentException { + public UriTemplate(String template) throws PatternSyntaxException, IllegalArgumentException { this(new UriTemplateParser(template)); } @@ -241,8 +248,7 @@ public UriTemplate(String template) throws * an empty string. */ @SuppressWarnings("DuplicateThrows") - protected UriTemplate(UriTemplateParser templateParser) throws - PatternSyntaxException, IllegalArgumentException { + protected UriTemplate(UriTemplateParser templateParser) throws PatternSyntaxException, IllegalArgumentException { this.template = templateParser.getTemplate(); this.normalizedTemplate = templateParser.getNormalizedTemplate(); @@ -250,7 +256,7 @@ protected UriTemplate(UriTemplateParser templateParser) throws this.pattern = initUriPattern(templateParser); this.numOfExplicitRegexes = templateParser.getNumberOfExplicitRegexes(); - + this.numOfRegexGroups = templateParser.getNumberOfRegexGroups(); this.numOfCharacters = templateParser.getNumberOfLiteralCharacters(); @@ -277,6 +283,7 @@ private static PatternWithGroups initUriPattern(UriTemplateParser templateParser * @param baseUri base URI to be used for resolution. * @param refUri reference URI string to be resolved against the base URI. * @return resolved URI. + * * @throws IllegalArgumentException If the given string violates the URI specification RFC. */ public static URI resolve(final URI baseUri, String refUri) { @@ -327,6 +334,7 @@ public static URI resolve(final URI baseUri, URI refUri) { * * @param uri the original URI string. * @return the URI with dot and dot-dot segments resolved. + * * @throws IllegalArgumentException If the given string violates the URI specification RFC. * @see java.net.URI#normalize() */ @@ -346,6 +354,7 @@ public static URI normalize(final String uri) { * * @param uri the original URI. * @return the URI with dot and dot-dot segments resolved. + * * @see java.net.URI#normalize() */ public static URI normalize(final URI uri) { @@ -361,7 +370,7 @@ public static URI normalize(final URI uri) { final Deque resolvedSegments = new ArrayDeque(segments.length); for (final String segment : segments) { - if ((segment.length() == 0) || (".".equals(segment))) { + if (segment.isEmpty() || ".".equals(segment)) { // skip } else if ("..".equals(segment)) { resolvedSegments.pollLast(); @@ -449,8 +458,7 @@ public final List getTemplateVariables() { * template. * * @param name name The template variable. - * @return {@code true} if the template variable is a member of the template, otherwise - * false. + * @return {@code true} if the template variable is a member of the template, otherwise {@code false}. */ @SuppressWarnings("UnusedDeclaration") public final boolean isTemplateVariablePresent(String name) { @@ -464,9 +472,9 @@ public final boolean isTemplateVariablePresent(String name) { } /** - * Get the number of explicit regexes declared in template variables. + * Get the number of explicit regular expressions declared in the template variables. * - * @return the number of explicit regexes. + * @return the number of explicit regular expressions in the template variables. */ public final int getNumberOfExplicitRegexes() { return numOfExplicitRegexes; @@ -480,8 +488,7 @@ public final int getNumberOfExplicitRegexes() { public final int getNumberOfRegexGroups() { return numOfRegexGroups; } - - + /** * Get the number of characters in the regular expression not resulting * from conversion of template variables. @@ -514,6 +521,7 @@ public final int getNumberOfTemplateVariables() { * and template values (as values). The map is cleared before any * entries are put. * @return true if the URI matches the template, otherwise false. + * * @throws IllegalArgumentException if the uri or * templateVariableToValue is null. */ @@ -538,6 +546,7 @@ public final boolean match(CharSequence uri, Map templateVariabl * capturing groups is matching is successful. The values are stored * in the same order as the pattern's capturing groups. * @return true if the URI matches the template, otherwise false. + * * @throws IllegalArgumentException if the uri or * templateVariableToValue is null. */ @@ -561,19 +570,14 @@ public final boolean match(CharSequence uri, List groupValues) throws * @return the URI. */ public final String createURI(final Map values) { - NameStrategy ns = new NameStrategy() - { - public String getValueFor(String tVariable, String matchedGroup) - { - return values.get(tVariable); - + final StringBuilder sb = new StringBuilder(); + resolveTemplate(normalizedTemplate, sb, new TemplateValueStrategy() { + @Override + public String valueFor(String templateVariable, String matchedGroup) { + return values.get(templateVariable); } - - }; - - final StringBuilder sb = new StringBuilder(); - applyTemplateStrategy(normalizedTemplate, sb, ns); - return sb.toString(); + }); + return sb.toString(); } /** @@ -584,13 +588,13 @@ public String getValueFor(String tVariable, String matchedGroup) * empty string. * * @param values the array of template values. The values will be - * substituted in order of occurence of unique template variables. + * substituted in order of occurrence of unique template variables. * @return the URI. */ public final String createURI(String... values) { return createURI(values, 0, values.length); } - + /** * Create a URI by substituting any template variables * for corresponding template values. @@ -599,113 +603,125 @@ public final String createURI(String... values) { * empty string. * * @param values the array of template values. The values will be - * substituted in order of occurence of unique template variables. - * @param offset the offset into the array - * @param length the length of the array + * substituted in order of occurrence of unique template variables. + * @param offset the offset into the template value array. + * @param length the length of the template value array. * @return the URI. */ public final String createURI(final String[] values, final int offset, final int length) { - - NameStrategy ns = new NameStrategy() - { - int lengthPlusOffset = length + offset; - int v = offset; - - - Map mapValues = new HashMap(); - - - public String getValueFor(String tVariable, String matchedGroup) - { + + TemplateValueStrategy ns = new TemplateValueStrategy() { + private final int lengthPlusOffset = length + offset; + private int v = offset; + private final Map mapValues = new HashMap(); + + @Override + public String valueFor(String templateVariable, String matchedGroup) { // Check if a template variable has already occurred // If so use the value to ensure that two or more declarations of // a template variable have the same value - String tValue = mapValues.get(tVariable); + String tValue = mapValues.get(templateVariable); if (tValue == null) { if (v < lengthPlusOffset) { tValue = values[v++]; if (tValue != null) { - mapValues.put(tVariable, tValue); + mapValues.put(templateVariable, tValue); } } } - + return tValue; - } - - }; + }; - final StringBuilder sb = new StringBuilder(); - applyTemplateStrategy(normalizedTemplate, sb, ns); - return sb.toString(); + final StringBuilder sb = new StringBuilder(); + resolveTemplate(normalizedTemplate, sb, ns); + return sb.toString(); } /** - * Build a URI based on the parameters provided by the strategy - * @param ns The naming strategy to use - * @return the URI + * Build a URI based on the parameters provided by the variable name strategy. + * + * @param normalizedTemplate normalized URI template. A normalized template is a template without any explicit regular + * expressions. + * @param builder URI string builder to be used. + * @param valueStrategy The template value producer strategy to use. */ - private static void applyTemplateStrategy( - String normalizedTemplate, - StringBuilder b, - NameStrategy ns) { + private static void resolveTemplate( + String normalizedTemplate, + StringBuilder builder, + TemplateValueStrategy valueStrategy) { // Find all template variables Matcher m = TEMPLATE_NAMES_PATTERN.matcher(normalizedTemplate); - - int i = 0; while (m.find()) { - b.append(normalizedTemplate, i, m.start()); - String tVariable = m.group(1); + builder.append(normalizedTemplate, i, m.start()); + String variableName = m.group(1); // TODO matrix - if (tVariable.startsWith("?") || tVariable.startsWith(";")) { - - char seperator = tVariable.startsWith("?") ? '&' : ';'; - char prefix = tVariable.startsWith("?") ? '?' : ';'; - - boolean found = false; - int index = b.length(); - String variables[] = tVariable.substring(1).split(", ?"); + char firstChar = variableName.charAt(0); + if (firstChar == '?' || firstChar == ';') { + final char prefix; + final char separator; + final String emptyValueAssignment; + if (firstChar == '?') { + // query + prefix = '?'; + separator = '&'; + emptyValueAssignment = "="; + } else { + // matrix + prefix = ';'; + separator = ';'; + emptyValueAssignment = ""; + } + + int index = builder.length(); + String[] variables = variableName.substring(1).split(", ?"); for (String variable : variables) { - String tValue = ns.getValueFor(variable, m.group()); - if (tValue!=null && tValue.length() > 0) { - if (index != b.length()) { - b.append(seperator); + try { + String value = valueStrategy.valueFor(variable, m.group()); + if (value != null) { + if (index != builder.length()) { + builder.append(separator); + } + + builder.append(variable); + if (value.isEmpty()) { + builder.append(emptyValueAssignment); + } else { + builder.append('='); + builder.append(value); + } } - - b.append(variable); - b.append('='); - b.append(tValue); + } catch (IllegalArgumentException ex) { + // no value found => ignore the variable } } - - if (index!= b.length()) { - b.insert(index, prefix); + + if (index != builder.length() && (index == 0 || builder.charAt(index - 1) != prefix)) { + builder.insert(index, prefix); } - } - else { - String tValue = ns.getValueFor(tVariable, m.group()); + } else { + String value = valueStrategy.valueFor(variableName, m.group()); - if (tValue!=null) { - b.append(tValue); + if (value != null) { + builder.append(value); } } - + i = m.end(); } - b.append(normalizedTemplate, i, normalizedTemplate.length()); + builder.append(normalizedTemplate, i, normalizedTemplate.length()); } - @Override public final String toString() { return pattern.toString(); } /** - * Hashcode is calculated from String of the regular expression + * Hash code is calculated from String of the regular expression * generated from the template. * * @return the hash code. @@ -900,7 +916,7 @@ private static String createURIWithStringValues( int offset = 0; if (scheme != null) { - offset = createURIComponent(UriComponent.Type.SCHEME, scheme, values, + offset = createUriComponent(UriComponent.Type.SCHEME, scheme, values, offset, false, mapValues, sb); sb.append(':'); } @@ -909,26 +925,26 @@ private static String createURIWithStringValues( sb.append("//"); if (notEmpty(userInfo)) { - offset = createURIComponent(UriComponent.Type.USER_INFO, userInfo, values, + offset = createUriComponent(UriComponent.Type.USER_INFO, userInfo, values, offset, encode, mapValues, sb); sb.append('@'); } if (notEmpty(host)) { // TODO check IPv6 address - offset = createURIComponent(UriComponent.Type.HOST, host, values, + offset = createUriComponent(UriComponent.Type.HOST, host, values, offset, encode, mapValues, sb); } if (notEmpty(port)) { sb.append(':'); - offset = createURIComponent(UriComponent.Type.PORT, port, values, + offset = createUriComponent(UriComponent.Type.PORT, port, values, offset, false, mapValues, sb); } } else if (notEmpty(authority)) { sb.append("//"); - offset = createURIComponent(UriComponent.Type.AUTHORITY, authority, values, + offset = createUriComponent(UriComponent.Type.AUTHORITY, authority, values, offset, encode, mapValues, sb); } @@ -942,19 +958,19 @@ private static String createURIWithStringValues( // path template values are treated as path segments unless encodeSlashInPath is false. UriComponent.Type t = (encodeSlashInPath) ? UriComponent.Type.PATH_SEGMENT : UriComponent.Type.PATH; - offset = createURIComponent(t, path, values, + offset = createUriComponent(t, path, values, offset, encode, mapValues, sb); } if (notEmpty(query)) { sb.append('?'); - offset = createURIComponent(UriComponent.Type.QUERY_PARAM, query, values, + offset = createUriComponent(UriComponent.Type.QUERY_PARAM, query, values, offset, encode, mapValues, sb); } if (notEmpty(fragment)) { sb.append('#'); - createURIComponent(UriComponent.Type.FRAGMENT, fragment, values, + createUriComponent(UriComponent.Type.FRAGMENT, fragment, values, offset, encode, mapValues, sb); } } @@ -966,9 +982,10 @@ private static boolean notEmpty(String string) { } @SuppressWarnings("unchecked") - private static int createURIComponent(final UriComponent.Type t, + private static int createUriComponent(final UriComponent.Type componentType, String template, - final String[] values, final int offset, + final String[] values, + final int valueOffset, final boolean encode, final Map _mapValues, final StringBuilder b) { @@ -977,42 +994,39 @@ private static int createURIComponent(final UriComponent.Type t, if (template.indexOf('{') == -1) { b.append(template); - return offset; + return valueOffset; } // Find all template variables template = new UriTemplateParser(template).getNormalizedTemplate(); - - class CountingStrategy implements NameStrategy - { - int v = offset; + + + class ValuesFromArrayStrategy implements TemplateValueStrategy { + private int offset = valueOffset; @Override - public String getValueFor(String tVariable, String matchedGroup) { + public String valueFor(String templateVariable, String matchedGroup) { - Object tValue = mapValues.get(tVariable); - if (tValue == null && v < values.length) { - tValue = values[v++]; + Object value = mapValues.get(templateVariable); + if (value == null && offset < values.length) { + value = values[offset++]; + mapValues.put(templateVariable, value); } - if (tValue != null) { - mapValues.put(tVariable, tValue); - if (encode) { - tValue = UriComponent.encode(tValue.toString(), t); - } else { - tValue = UriComponent.contextualEncode(tValue.toString(), t); - } + if (value == null) { + throw new IllegalArgumentException( + String.format("The template variable '%s' has no value", templateVariable)); + } + if (encode) { + return UriComponent.encode(value.toString(), componentType); } else { - throw templateVariableHasNoValue(tVariable); + return UriComponent.contextualEncode(value.toString(), componentType); } - - return tValue!=null ? tValue.toString() : null; } - } - CountingStrategy cs = new CountingStrategy(); - applyTemplateStrategy(template, b, cs); + ValuesFromArrayStrategy cs = new ValuesFromArrayStrategy(); + resolveTemplate(template, b, cs); - return cs.v; + return cs.offset; } @@ -1025,6 +1039,7 @@ public String getValueFor(String tVariable, String matchedGroup) { * @param encode True if template values from {@code _mapValues} should be percent encoded. * @param _mapValues Map with template variables as keys and template values as values. None of them should be null. * @return String with resolved template variables. + * * @throws IllegalArgumentException when {@code _mapValues} value is null. */ @SuppressWarnings("unchecked") @@ -1038,42 +1053,37 @@ public static String resolveTemplateValues(final UriComponent.Type type, } final Map mapValues = (Map) _mapValues; - StringBuilder sb = new StringBuilder(); // Find all template variables template = new UriTemplateParser(template).getNormalizedTemplate(); - - NameStrategy ns = new NameStrategy() - { + + StringBuilder sb = new StringBuilder(); + resolveTemplate(template, sb, new TemplateValueStrategy() { @Override - public String getValueFor(String tVariable, String matchedGroup) { + public String valueFor(String templateVariable, String matchedGroup) { - Object tValue = mapValues.get(tVariable); + Object value = mapValues.get(templateVariable); - if (tValue != null) { + if (value != null) { if (encode) { - tValue = UriComponent.encode(tValue.toString(), type); + value = UriComponent.encode(value.toString(), type); } else { - tValue = UriComponent.contextualEncode(tValue.toString(), type); + value = UriComponent.contextualEncode(value.toString(), type); } - return tValue.toString(); + return value.toString(); } else { - if (mapValues.containsKey(tVariable)) { - throw new IllegalArgumentException("The value associated of the template value map for key + " + tVariable - + " is null."); + if (mapValues.containsKey(templateVariable)) { + throw new IllegalArgumentException( + String.format("The value associated of the template value map for key '%s' is 'null'.", + templateVariable) + ); } return matchedGroup; } } - }; - - applyTemplateStrategy(template, sb, ns); - return sb.toString(); - } + }); - private static IllegalArgumentException templateVariableHasNoValue(String tVariable) { - return new IllegalArgumentException("The template variable, " - + tVariable + ", has no value"); + return sb.toString(); } } diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java index dece0d5c89..ae19e24f6e 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/internal/UriTemplateParser.java @@ -99,10 +99,10 @@ private static Set initReserved() { * * @param template the template. * @throws IllegalArgumentException if the template is null, an empty string - * or does not conform to a JAX-RS URI template. + * or does not conform to a JAX-RS URI template. */ public UriTemplateParser(final String template) throws IllegalArgumentException { - if (template == null || template.length() == 0) { + if (template == null || template.isEmpty()) { throw new IllegalArgumentException("Template is null or has zero length"); } @@ -113,7 +113,8 @@ public UriTemplateParser(final String template) throws IllegalArgumentException } catch (PatternSyntaxException ex) { throw new IllegalArgumentException("Invalid syntax for the template expression '" + regex + "'", - ex); + ex + ); } } @@ -189,10 +190,11 @@ public final int[] getGroupIndexes() { } int[] indexes = new int[names.size()]; - for (int i = 0; i < indexes.length; i++) { - indexes[i] = (i == 0 ? 0 : indexes[i - 1]) + groupCounts.get(i); + indexes[0] = 0 + groupCounts.get(0); + for (int i = 1; i < indexes.length; i++) { + indexes[i] = indexes[i - 1] + groupCounts.get(i); } - + return indexes; } @@ -209,18 +211,18 @@ public final int getNumberOfExplicitRegexes() { * Get the number of regular expression groups * * @return the number of regular expressions groups + * + * @since 2.9 */ public final int getNumberOfRegexGroups() { if (groupCounts.isEmpty()) { return 0; - } - else { - int groupIndex[] = getGroupIndexes(); - return groupIndex[groupIndex.length-1] + skipGroup; + } else { + int[] groupIndex = getGroupIndexes(); + return groupIndex[groupIndex.length - 1] + skipGroup; } } - - + /** * Get the number of literal characters. * @@ -297,29 +299,28 @@ private static String[] initHexToUpperCaseRegex() { for (char c = 'a'; c <= 'f'; c++) { // initialize table values: table[a] = ([aA]) ... - table[c] = new StringBuffer().append("[").append(c).append((char) (c - 'a' + 'A')).append("]").toString(); + table[c] = "[" + c + (char) (c - 'a' + 'A') + "]"; } for (char c = 'A'; c <= 'F'; c++) { // initialize table values: table[A] = ([aA]) ... - table[c] = new StringBuffer().append("[").append((char) (c - 'A' + 'a')).append(c).append("]").toString(); + table[c] = "[" + (char) (c - 'A' + 'a') + c + "]"; } return table; } - private int parseName(final CharacterIterator ci, int skipGroup) { char c = consumeWhiteSpace(ci); char paramType = 'p'; // Normal path param unless otherwise stated StringBuilder nameBuffer = new StringBuilder(); - + // Look for query or matrix types - if (c == '?' || c==';') { + if (c == '?' || c == ';') { paramType = c; c = ci.next(); - } - + } + if (Character.isLetterOrDigit(c) || c == '_') { // Template name character nameBuffer.append(c); @@ -335,10 +336,10 @@ private int parseName(final CharacterIterator ci, int skipGroup) { if (Character.isLetterOrDigit(c) || c == '_' || c == '-' || c == '.') { // Template name character nameBuffer.append(c); - } else if (paramType!='p' && c == ',' ) { - // separator allowed + } else if (c == ',' && paramType != 'p') { + // separator allowed for non-path parameter names nameBuffer.append(c); - } else if (c == ':' && paramType=='p') { + } else if (c == ':' && paramType == 'p') { nameRegexString = parseRegex(ci); break; } else if (c == '}') { @@ -353,77 +354,71 @@ private int parseName(final CharacterIterator ci, int skipGroup) { break; } else { // Error - throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_ILLEGAL_CHAR_AFTER_NAME(c, - ci.pos(), template)); + throw new IllegalArgumentException( + LocalizationMessages.ERROR_TEMPLATE_PARSER_ILLEGAL_CHAR_AFTER_NAME(c, ci.pos(), template)); } } else { - throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_ILLEGAL_CHAR_PART_OF_NAME(c, - ci.pos(), template)); + throw new IllegalArgumentException( + LocalizationMessages.ERROR_TEMPLATE_PARSER_ILLEGAL_CHAR_PART_OF_NAME(c, ci.pos(), template)); } } - + String name = nameBuffer.toString(); Pattern namePattern; - try { if (paramType == '?' || paramType == ';') { - String subNames[] = name.split(",\\s?"); - + String[] subNames = name.split(",\\s?"); + // Build up the regex for each of these properties - StringBuilder regexBuilder = new StringBuilder(paramType == '?' ? "\\?" : ";"); - String seperator = paramType == '?' ? "\\&" : ";/\\?"; - - // Start a group becuase each parameter could repeat - + String separator = paramType == '?' ? "\\&" : ";/\\?"; + + // Start a group because each parameter could repeat // names.add("__" + (paramType == '?' ? "query" : "matrix")); - + boolean first = true; - + regexBuilder.append("("); for (String subName : subNames) { regexBuilder.append("(&?"); regexBuilder.append(subName); regexBuilder.append("(=([^"); - regexBuilder.append(seperator); + regexBuilder.append(separator); regexBuilder.append("]*))?"); regexBuilder.append(")"); if (!first) { regexBuilder.append("|"); } - + names.add(subName); groupCounts.add( - first ? 5 : 3); + first ? 5 : 3); first = false; } // groupCounts.add(1); skipGroup = 1; - // Knock of last bar regexBuilder.append(")*"); - + namePattern = Pattern.compile(regexBuilder.toString()); - // Make sure we display something useful name = paramType + name; } else { - names.add(name); - // groupCounts.add(1 + skipGroup); + // groupCounts.add(1 + skipGroup); - if (nameRegexString.length() > 0) { + if (!nameRegexString.isEmpty()) { numOfExplicitRegexes++; } - namePattern = (nameRegexString.length() == 0) + namePattern = (nameRegexString.isEmpty()) ? TEMPLATE_VALUE_PATTERN : Pattern.compile(nameRegexString); if (nameToPattern.containsKey(name)) { if (!nameToPattern.get(name).equals(namePattern)) { - throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_NAME_MORE_THAN_ONCE(name, - template)); + throw new IllegalArgumentException( + LocalizationMessages.ERROR_TEMPLATE_PARSER_NAME_MORE_THAN_ONCE(name, template)); } } else { nameToPattern.put(name, namePattern); @@ -434,26 +429,21 @@ private int parseName(final CharacterIterator ci, int skipGroup) { int g = m.groupCount(); groupCounts.add(1 + skipGroup); skipGroup = g; - } - - regex.append('('). append(namePattern). append(')'); - - normalizedTemplate.append('{'). append(name). append('}'); } catch (PatternSyntaxException ex) { - throw new IllegalArgumentException(LocalizationMessages.ERROR_TEMPLATE_PARSER_INVALID_SYNTAX(nameRegexString, name, - template), ex); + throw new IllegalArgumentException( + LocalizationMessages.ERROR_TEMPLATE_PARSER_INVALID_SYNTAX(nameRegexString, name, template), ex); } - - // Tell the next time though the loop how many to skip + + // Tell the next time through the loop how many to skip return skipGroup; } diff --git a/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java b/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java index c1c5c8a98f..e3da83b6e6 100644 --- a/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/uri/PathPatternTest.java @@ -39,12 +39,7 @@ */ package org.glassfish.jersey.uri; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.regex.MatchResult; -import java.util.regex.Matcher; import org.junit.Assert; import org.junit.Test; @@ -132,8 +127,6 @@ public void testSetsAndGetsUriTemplate() throws Exception { ); } - - @Test public void testLastElementOfMatchIsRestOfPath() throws Exception { PathPattern path = new PathPattern("{a: (\\d)(\\d*)}-{b: (\\d)(\\d*)}-{c: (\\d)(\\d*)}"); @@ -147,7 +140,5 @@ public void testLastElementOfMatchIsRestOfPath() throws Exception { "/d", value ); - } - } diff --git a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java index df00465337..b9176438f3 100644 --- a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java @@ -41,6 +41,7 @@ import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -54,7 +55,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import org.junit.Ignore; /** * Taken from Jersey 1: jersey-tests: com.sun.jersey.impl.uri.UriTemplateTest @@ -275,16 +275,10 @@ void _testMatching(String template, String uri, String... values) { UriTemplate t = new UriTemplate(template); Map m = new HashMap(); - System.out.println("TEMPLATE: " + template); - System.out.println("REGEX: " + t.getPattern().getRegex()); - System.out.println("TEMPLATE NAMES: " + t.getTemplateVariables()); - boolean isMatch = t.match(uri, m); assertTrue(isMatch); assertEquals(values.length, t.getTemplateVariables().size()); - System.out.println("MAP: " + m); - Iterator names = t.getTemplateVariables().iterator(); for (String value : values) { String mapValue = m.get(names.next()); @@ -297,8 +291,6 @@ void _testMatching(String template, String uri, String... values) { assertTrue(isMatch); assertEquals(values.length, matchedValues.size()); - System.out.println("LIST: " + matchedValues); - for (int i = 0; i < values.length; i++) { assertEquals(values[i], matchedValues.get(i)); } @@ -314,8 +306,8 @@ void _testMatching(String template, String uri, String... values) { assertEquals(0, mr.start(0)); assertEquals(uri.length(), mr.end(0)); for (int i = 0; i < mr.groupCount(); i++) { - assertEquals(values[i], mr.group(i+1)); - assertEquals(values[i], uri.substring(mr.start(i+1), mr.end(i+1))); + assertEquals(values[i], mr.group(i + 1)); + assertEquals(values[i], uri.substring(mr.start(i + 1), mr.end(i + 1))); } } @@ -465,21 +457,19 @@ private void validateNormalize(String path, String expected) throws Exception { assertEquals(expected, result.toString()); } - - @Test public void testSingleQueryParameter() throws Exception { UriTemplate tmpl = new UriTemplate("/test{?query}"); - Map result = new HashMap(); + Map result = new HashMap(); tmpl.match("/test?query=x", result); - + assertEquals( "incorrect size for match string", 1, result.size() ); - + assertEquals( "query parameter is not matched", "x", @@ -487,23 +477,22 @@ public void testSingleQueryParameter() throws Exception { ); } - @Test public void testDoubleQueryParameter() throws Exception { UriTemplate tmpl = new UriTemplate("/test{?query,secondQuery}"); List list = new ArrayList(); tmpl.match("/test?query=x&secondQuery=y", list); - - Map result = new HashMap(); + + Map result = new HashMap(); tmpl.match("/test?query=x&secondQuery=y", result); - + assertEquals( "incorrect size for match string", 2, result.size() ); - + assertEquals( "query parameter is not matched", "x", @@ -516,121 +505,245 @@ public void testDoubleQueryParameter() throws Exception { ); } - + @Test public void testSettingQueryParameter() throws Exception { UriTemplate tmpl = new UriTemplate("/test{?query}"); - - Map values = new HashMap(); + + Map values = new HashMap(); values.put("query", "example"); - + String uri = tmpl.createURI(values); assertEquals( "query string is not set", "/test?query=example", uri ); - - } + } @Test - public void testSettingMatrixParameter() throws Exception { - UriTemplate tmpl = new UriTemplate("/test{;matrix}/other"); - - Map values = new HashMap(); - values.put("matrix", "example"); - + public void testSettingTwoQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query,other}"); + + Map values = new HashMap(); + values.put("query", "example"); + values.put("other", "otherExample"); + String uri = tmpl.createURI(values); assertEquals( "query string is not set", - "/test;matrix=example/other", + "/test?query=example&other=otherExample", uri ); - - } - - - + + } + @Test - public void testSettingTwoQueryParameter() throws Exception { - UriTemplate tmpl = new UriTemplate("/test{?query,other}"); - - Map values = new HashMap(); - values.put("query", "example"); - values.put("other", "otherExample"); - + public void testNotSettingQueryParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{?query}"); + + Map values = new HashMap(); + + String uri = tmpl.createURI(values); + assertEquals( + "query string is set", + "/test", + uri + ); + + } + + @Test + public void testSettingMatrixParameter() throws Exception { + UriTemplate tmpl = new UriTemplate("/test{;matrix}/other"); + + Map values = new HashMap(); + values.put("matrix", "example"); + String uri = tmpl.createURI(values); assertEquals( "query string is not set", - "/test?query=example&other=otherExample", + "/test;matrix=example/other", uri ); - - } + + } @Test public void testSettingTwoMatrixParameter() throws Exception { UriTemplate tmpl = new UriTemplate("/test{;matrix,other}/other"); - - Map values = new HashMap(); + + Map values = new HashMap(); values.put("matrix", "example"); values.put("other", "otherExample"); - + String uri = tmpl.createURI(values); assertEquals( "query string is not set", "/test;matrix=example;other=otherExample/other", uri ); - - } + + } @Test public void testSettingTwoSeperatedMatrixParameter() throws Exception { UriTemplate tmpl = new UriTemplate("/test{;matrix}/other{;other}"); - - Map values = new HashMap(); + + Map values = new HashMap(); values.put("matrix", "example"); values.put("other", "otherExample"); - + String uri = tmpl.createURI(values); assertEquals( "query string is not set", "/test;matrix=example/other;other=otherExample", uri ); - - } - - - @Test - public void testNotSettingQueryParameter() throws Exception { - UriTemplate tmpl = new UriTemplate("/test{?query}"); - - Map values = new HashMap(); - - String uri = tmpl.createURI(values); - assertEquals( - "query string is set", - "/test", - uri - ); - - } + } @Test public void testNotSettingMatrixParameter() throws Exception { UriTemplate tmpl = new UriTemplate("/test{;query}/other"); - - Map values = new HashMap(); - + + Map values = new HashMap(); + String uri = tmpl.createURI(values); assertEquals( "query string is set", "/test/other", uri ); - - } + } + + /* + RFC 6570, section 3.2: + + count := ("one", "two", "three") + dom := ("example", "com") + dub := "me/too" + hello := "Hello World!" + half := "50%" + var := "value" + who := "fred" + base := "http://example.com/home/" + path := "/foo/bar" + list := ("red", "green", "blue") + keys := [("semi",";"),("dot","."),("comma",",")] + v := "6" + x := "1024" + y := "768" + empty := "" + empty_keys := [] + undef := null + */ + private static final List count = Arrays.asList("one", "two", "three"); + private static final List dom = Arrays.asList("example", "com"); + private static final String dub = "me/too"; + private static final String hello = "Hello World!"; + private static final String half = "50%"; + private static final String var = "value"; + private static final String who = "fred"; + private static final String base = "http://example.com/home/"; + private static final String path = "/foo/bar"; + private static final List list = Arrays.asList("red", "green", "blue"); + private static final Map keys = new HashMap() {{ + put("semi", ";"); + put("dot", "."); + put("comma", ","); + }}; + private static final String v = "6"; + private static final String x = "1024"; + private static final String y = "768"; + private static final String empty = ""; + private static final Map emptyKeys = Collections.emptyMap(); + + @Test + public void testRfc6570QueryTemplateExamples() { + /* + RFC 6570, section 3.2.8: + + {?who} ?who=fred + {?half} ?half=50%25 + {?x,y} ?x=1024&y=768 + {?x,y,empty} ?x=1024&y=768&empty= + {?x,y,undef} ?x=1024&y=768 + {?var:3} ?var=val + {?list} ?list=red,green,blue + {?list*} ?list=red&list=green&list=blue + {?keys} ?keys=semi,%3B,dot,.,comma,%2C + {?keys*} ?semi=%3B&dot=.&comma=%2C + */ + assertEncodedQueryTemplateExpansion("?who=fred", "{?who}", who); + assertEncodedQueryTemplateExpansion("?half=50%25", "{?half}", half); + assertEncodedQueryTemplateExpansion("?x=1024&y=768", "{?x,y}", x, y); + assertEncodedQueryTemplateExpansion("?x=1024&y=768&empty=", "{?x,y,empty}", x, y, empty); + assertEncodedQueryTemplateExpansion("?x=1024&y=768", "{?x,y,undef}", x, y); + // TODO assertEncodedQueryTemplateExpansion("?var=val", "{?var:3}", var); + // TODO assertEncodedQueryTemplateExpansion("?list=red,green,blue", "{?list}", list); + // TODO assertEncodedQueryTemplateExpansion("?list=red&list=green&list=blue", "{?list*}", list); + // TODO assertEncodedQueryTemplateExpansion("?keys=semi,%3B,dot,.,comma,%2C", "{?keys}", keys); + // assertEncodedQueryTemplateExpansion("?semi=%3B&dot=.&comma=%2C", "{?keys*}", keys); + } + + private void assertEncodedQueryTemplateExpansion(String expectedExpansion, String queryTemplate, Object... values) { + assertEquals("Unexpected encoded query template expansion result.", + expectedExpansion, + UriTemplate.createURI(null, null, null, null, null, null, queryTemplate, null, values, true, false)); + } + private void assertEncodedQueryTemplateExpansion(String expectedExpansion, String queryTemplate, Map values) { + assertEquals("Unexpected encoded query template expansion result.", + expectedExpansion, + UriTemplate.createURI(null, null, null, null, null, null, queryTemplate, null, values, true, false)); + } + + @Test + public void testRfc6570MatrixTemplateExamples() { + /* + RFC 6570, section 3.2.7: + + {;who} ;who=fred + {;half} ;half=50%25 + {;empty} ;empty + {;v,empty,who} ;v=6;empty;who=fred + {;v,bar,who} ;v=6;who=fred + {;x,y} ;x=1024;y=768 + {;x,y,empty} ;x=1024;y=768;empty + {;x,y,undef} ;x=1024;y=768 + {;hello:5} ;hello=Hello + {;list} ;list=red,green,blue + {;list*} ;list=red;list=green;list=blue + {;keys} ;keys=semi,%3B,dot,.,comma,%2C + {;keys*} ;semi=%3B;dot=.;comma=%2C + */ + assertEncodedPathTemplateExpansion(";who=fred", "{;who}", who); + assertEncodedPathTemplateExpansion(";half=50%25", "{;half}", half); + assertEncodedPathTemplateExpansion(";empty", "{;empty}", empty); + assertEncodedPathTemplateExpansion(";v=6;empty;who=fred", "{;v,empty,who}", v, empty, who); + assertEncodedPathTemplateExpansion(";v=6;who=fred", "{;v,bar,who}", new HashMap() {{ + put("v", v); + put("who", who); + }}); + assertEncodedPathTemplateExpansion(";x=1024;y=768", "{;x,y}", x, y); + assertEncodedPathTemplateExpansion(";x=1024;y=768;empty", "{;x,y,empty}", x, y, empty); + assertEncodedPathTemplateExpansion(";x=1024;y=768", "{;x,y,undef}", x, y); + // TODO assertEncodedPathTemplateExpansion(";hello=Hello", "{;hello:5}", hello); + // TODO assertEncodedPathTemplateExpansion(";list=red,green,blue", "{;list}", list); + // TODO assertEncodedPathTemplateExpansion(";list=red;list=green;list=blue", "{;list*}", list); + // TODO assertEncodedPathTemplateExpansion(";keys=semi,%3B,dot,.,comma,%2C", "{;keys}", keys); + // TODO assertEncodedPathTemplateExpansion(";semi=%3B;dot=.;comma=%2C", "{;keys*}", keys); + } + + private void assertEncodedPathTemplateExpansion(String expectedExpansion, String pathTemplate, Object... values) { + assertEquals("Unexpected encoded matrix parameter template expansion result.", + expectedExpansion, + UriTemplate.createURI(null, null, null, null, null, pathTemplate, null, null, values, true, false)); + } + + private void assertEncodedPathTemplateExpansion(String expectedExpansion, String pathTemplate, Map values) { + assertEquals("Unexpected encoded matrix parameter template expansion result.", + expectedExpansion, + UriTemplate.createURI(null, null, null, null, null, pathTemplate, null, null, values, true, false)); + } } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java index 71483f23e6..465faf9ae8 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java @@ -40,62 +40,48 @@ package org.glassfish.jersey.linking; -import javax.inject.Singleton; import javax.ws.rs.core.Configuration; import javax.ws.rs.core.Feature; import javax.ws.rs.core.FeatureContext; -import org.glassfish.hk2.api.PerThread; -import org.glassfish.hk2.utilities.binding.AbstractBinder; + +import javax.inject.Singleton; + import org.glassfish.jersey.Beta; -import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.linking.mapping.NaiveResourceMappingContext; import org.glassfish.jersey.linking.mapping.ResourceMappingContext; -import org.glassfish.jersey.process.internal.RequestScoped; + +import org.glassfish.hk2.utilities.binding.AbstractBinder; /** - * A feature to enable the declarative linking functionality - * + * A feature to enable the declarative linking functionality. + * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ @Beta -public class DeclarativeLinkingFeature - implements Feature -{ +public class DeclarativeLinkingFeature implements Feature { @Override public boolean configure(FeatureContext context) { - + Configuration config = context.getConfiguration(); - - if (!config.isRegistered(ResponseLinkFilter.class)) - { - // - context.register(new AbstractBinder() - { + if (!config.isRegistered(ResponseLinkFilter.class)) { + context.register(new AbstractBinder() { @Override protected void configure() { - bindAsContract(NaiveResourceMappingContext.class).to( - ResourceMappingContext.class).in(Singleton.class); + bindAsContract(NaiveResourceMappingContext.class) + .to(ResourceMappingContext.class).in(Singleton.class); } }); - - - -// Injections.newBinder(NaiveResourceMappingContext.class).to( -// ResourceMappingContext.class).in(Singleton.class)); context.register(ResponseLinkFilter.class); - // Todo map values back? // context.register(RequestLinkFilter.class); return true; } - return false; } - } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java index 8f98e94ac7..3f85d5e67e 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java @@ -44,49 +44,58 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.el.ELException; -import javax.el.ExpressionFactory; -import javax.el.ValueExpression; + import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; + +import javax.el.ExpressionFactory; +import javax.el.ValueExpression; + import org.glassfish.jersey.linking.mapping.ResourceMappingContext; -import org.glassfish.jersey.uri.UriTemplate; import org.glassfish.jersey.uri.internal.UriTemplateParser; /** - * A helper class to build links from EL expressions - * + * A helper class to build links from EL expressions. + * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ -class ELLinkBuilder { +final class ELLinkBuilder { + + private ELLinkBuilder() { + } - private static ExpressionFactory expressionFactory = + private static final ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); - public static boolean evaluateCondition(String condition, Object entity, - Object resource, Object instance) { - if (condition==null || condition.length()==0) + /** + * TODO javadoc. + */ + static boolean evaluateCondition(String condition, + Object entity, + Object resource, + Object instance) { + + if (condition == null || condition.isEmpty()) return true; LinkELContext context = new LinkELContext(entity, resource, instance); - ValueExpression expr = expressionFactory.createValueExpression(context, - condition, boolean.class); - try - { - Object result = expr.getValue(context).toString(); - return result.equals("true"); - } - catch (ELException ex) - { - throw ex; - } + ValueExpression expr = expressionFactory.createValueExpression(context, condition, boolean.class); + + Object result = expr.getValue(context).toString(); + return "true".equals(result); } - public static URI buildURI(InjectLinkDescriptor link, Object entity, Object resource, Object instance, - UriInfo uriInfo, ResourceMappingContext rmc) { + /** + * TODO javadoc. + */ + static URI buildURI(InjectLinkDescriptor link, + Object entity, + Object resource, + Object instance, + UriInfo uriInfo, + ResourceMappingContext rmc) { + String template = link.getLinkTemplate(rmc); - - // first process any embedded EL expressions LinkELContext context = new LinkELContext(entity, resource, instance); @@ -95,16 +104,15 @@ public static URI buildURI(InjectLinkDescriptor link, Object entity, Object reso template = expr.getValue(context).toString(); // now process any embedded URI template parameters - UriBuilder ub=applyLinkStyle(template, link.getLinkStyle(), uriInfo); + UriBuilder ub = applyLinkStyle(template, link.getLinkStyle(), uriInfo); UriTemplateParser parser = new UriTemplateParser(template); List parameterNames = parser.getNames(); Map valueMap = getParameterValues(parameterNames, link, context); - URI uri = ub.buildFromMap(valueMap); - return uri; + return ub.buildFromMap(valueMap); } private static UriBuilder applyLinkStyle(String template, InjectLink.Style style, UriInfo uriInfo) { - UriBuilder ub=null; + UriBuilder ub = null; switch (style) { case ABSOLUTE: ub = uriInfo.getBaseUriBuilder().path(template); @@ -121,35 +129,23 @@ private static UriBuilder applyLinkStyle(String template, InjectLink.Style style } private static Map getParameterValues(List parameterNames, InjectLinkDescriptor linkField, LinkELContext context) { - Map values = new HashMap(); - for (String name: parameterNames) { + Map values = new HashMap<>(); + for (String name : parameterNames) { String elExpression = getEL(name, linkField); ValueExpression expr = expressionFactory.createValueExpression(context, elExpression, String.class); - try - { - Object value = expr.getValue(context); - values.put(name, value!=null ? value.toString() : null); - } - catch (ELException ex) - { - throw ex; - } + + Object value = expr.getValue(context); + values.put(name, value != null ? value.toString() : null); } return values; } private static String getEL(String name, InjectLinkDescriptor linkField) { String binding = linkField.getBinding(name); - if (binding != null) + if (binding != null) { return binding; - StringBuilder builder = new StringBuilder(); - builder.append("${"); - builder.append(ResponseContextResolver.INSTANCE_OBJECT); - builder.append("."); - builder.append(name); - builder.append("}"); - return builder.toString(); + } + return "${" + ResponseContextResolver.INSTANCE_OBJECT + "." + name + "}"; } - } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java index 52f06c447c..444cc14d74 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinkFieldDescriptor.java @@ -48,18 +48,19 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; + import javax.ws.rs.HttpMethod; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Link; + import org.glassfish.jersey.linking.mapping.ResourceMappingContext; import org.glassfish.jersey.server.model.AnnotatedMethod; - import org.glassfish.jersey.server.model.MethodList; - + /** - * Utility class for working with {@link InjectLink} annotated fields - * + * Utility class for working with {@link InjectLink} annotated fields. + * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ @@ -69,118 +70,118 @@ class InjectLinkFieldDescriptor extends FieldDescriptor implements InjectLinkDes private Class type; private Map bindings; + /** + * TODO javadoc. + */ public InjectLinkFieldDescriptor(Field f, InjectLink l, Class t) { super(f); link = l; type = t; - bindings = new HashMap(); - for (Binding binding: l.bindings()) { + bindings = new HashMap<>(); + for (Binding binding : l.bindings()) { bindings.put(binding.name(), binding.value()); } } - + + /** + * TODO javadoc. + */ public void setPropertyValue(Object instance, URI uri) { setAccessibleField(field); try { - - Object value = null; + + Object value; if (URI.class.equals(type)) { value = uri; - } - else if (Link.class.isAssignableFrom(type)) { + } else if (Link.class.isAssignableFrom(type)) { // Make a link with the correct bindings - // - value = getLink(uri); - } - else if (String.class.equals(type)) { + } else if (String.class.equals(type)) { value = uri.toString(); + } else { + throw new IllegalArgumentException("Field type " + type + " not one of supported String,URI and Link"); } - else { - throw new IllegalArgumentException("Field type " + type + " not one of supported String,URI and Link"); - } - + field.set(instance, value); - - - - - } catch (IllegalArgumentException ex) { - Logger.getLogger(InjectLinkFieldDescriptor.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalAccessException ex) { + } catch (IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(InjectLinkFieldDescriptor.class.getName()).log(Level.SEVERE, null, ex); } } + /** + * TODO javadoc. + */ public InjectLink.Style getLinkStyle() { return link.style(); } + /** + * TODO javadoc. + */ public String getLinkTemplate(ResourceMappingContext rmc) { return getLinkTemplate(rmc, link); } - + + /** + * TODO javadoc. + */ public static String getLinkTemplate(ResourceMappingContext rmc, InjectLink link) { String template = null; if (!link.resource().equals(Class.class)) { - - + + ResourceMappingContext.Mapping map = rmc.getMapping(link.resource()); - if (map!=null) - { - template = map.getTemplate().getTemplate().toString(); - } - else - { + if (map != null) { + template = map.getTemplate().getTemplate().toString(); + } else { // extract template from specified class' @Path annotation Path path = link.resource().getAnnotation(Path.class); - template = path==null ? "" : path.value(); + template = path == null ? "" : path.value(); } // extract template from specified class' @Path annotation - if (link.method().length() > 0) { + if (link.method().length() > 0) { // append value of method's @Path annotation MethodList methods = new MethodList(link.resource()); methods = methods.withMetaAnnotation(HttpMethod.class); Iterator iterator = methods.iterator(); while (iterator.hasNext()) { AnnotatedMethod method = iterator.next(); - if (!method.getMethod().getName().equals(link.method())) + if (!method.getMethod().getName().equals(link.method())) { continue; + } StringBuilder builder = new StringBuilder(); builder.append(template); Path methodPath = method.getAnnotation(Path.class); - if (methodPath!=null) - { + if (methodPath != null) { String methodTemplate = methodPath.value(); - if (!(template.endsWith("/") || methodTemplate.startsWith("/"))) + if (!(template.endsWith("/") || methodTemplate.startsWith("/"))) { builder.append("/"); + } builder.append(methodTemplate); } // append query parameters StringBuilder querySubString = new StringBuilder(); - for (Annotation paramAnns[] : method.getParameterAnnotations()){ + for (Annotation paramAnns[] : method.getParameterAnnotations()) { for (Annotation ann : paramAnns) { if (ann.annotationType() == QueryParam.class) { - querySubString.append(((QueryParam)ann).value()); + querySubString.append(((QueryParam) ann).value()); querySubString.append(','); } } } - if (querySubString.length() > 0) - { + if (querySubString.length() > 0) { builder.append("{?"); - builder.append(querySubString.subSequence(0, querySubString.length()-1)); + builder.append(querySubString.subSequence(0, querySubString.length() - 1)); builder.append("}"); } - template = builder.toString(); break; } @@ -188,17 +189,27 @@ public static String getLinkTemplate(ResourceMappingContext rmc, InjectLink link } else { template = link.value(); } + return template; } - + + /** + * TODO javadoc. + */ public Link getLink(URI uri) { return InjectLink.Util.buildLinkFromUri(uri, link); } - + + /** + * TODO javadoc. + */ public String getBinding(String name) { return bindings.get(name); } + /** + * TODO javadoc. + */ public String getCondition() { return link.condition(); } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java index bcd544e643..b5e7a769d2 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/InjectLinksFieldDescriptor.java @@ -45,60 +45,62 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; + import javax.ws.rs.core.Link; - + /** - * Utility class for working with {@link InjectLinks} annotated fields - * + * Utility class for working with {@link InjectLinks} annotated fields. + * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ -class InjectLinksFieldDescriptor extends FieldDescriptor { +class InjectLinksFieldDescriptor extends FieldDescriptor { - private InjectLinks link; - private Class type; + private final InjectLinks link; + private final Class type; + /** + * TODO javadoc. + */ public InjectLinksFieldDescriptor(Field f, InjectLinks l, Class t) { super(f); link = l; type = t; } - + + /** + * TODO javadoc. + */ public void setPropertyValue(Object instance, List list) { setAccessibleField(field); try { - - Object value = null; + + Object value; if (List.class.equals(type)) { value = list; + } else if (type.isArray()) { + value = list.toArray((Object[]) Array.newInstance(type.getComponentType(), list.size())); + } else { + throw new IllegalArgumentException("Field type " + type + " not one of supported List or List[]"); } - else if (type.isArray()) { - value = list.toArray((Object[])Array.newInstance(type.getComponentType(), list.size())); - } - else { - throw new IllegalArgumentException("Field type " + type + " not one of supported List or List[]"); - } - + field.set(instance, value); - - - - - } catch (IllegalArgumentException ex) { - Logger.getLogger(InjectLinksFieldDescriptor.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalAccessException ex) { + + + } catch (IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(InjectLinksFieldDescriptor.class.getName()).log(Level.SEVERE, null, ex); } } - - public InjectLinkFieldDescriptor[] getLinksToInject() - { + + /** + * TODO javadoc. + */ + public InjectLinkFieldDescriptor[] getLinksToInject() { final InjectLink[] listOfLinks = link.value(); InjectLinkFieldDescriptor[] fields = new InjectLinkFieldDescriptor[listOfLinks.length]; - for (int i =0; i < fields.length; i ++) - { + for (int i = 0; i < fields.length; i++) { fields[i] = new InjectLinkFieldDescriptor(field, listOfLinks[i], Link.class); } - return fields; + return fields; } } diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java index 8edad40c39..69902d46c2 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/RequestLinkFilter.java @@ -37,15 +37,15 @@ * only if the new code is made subject to such option by the copyright * holder. */ - package org.glassfish.jersey.linking; import java.io.IOException; -import javax.ws.rs.core.Context; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Link; + import org.glassfish.jersey.server.ExtendedUriInfo; /** @@ -62,10 +62,9 @@ * *

* - * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) - * @see LinkHeader + * @see Link */ class RequestLinkFilter implements ContainerRequestFilter { @@ -76,6 +75,5 @@ class RequestLinkFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { - } -} \ No newline at end of file +} diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java index db28610744..06114e34c1 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ResponseLinkFilter.java @@ -37,18 +37,16 @@ * only if the new code is made subject to such option by the copyright * holder. */ - package org.glassfish.jersey.linking; -import javax.ws.rs.core.Context; - import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Link; import javax.ws.rs.core.UriInfo; + import org.glassfish.jersey.linking.mapping.ResourceMappingContext; -import org.glassfish.jersey.server.ExtendedUriInfo; /** * Filter that processes {@link Link} annotated fields in returned response @@ -64,10 +62,9 @@ * *

* - * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) - * @see LinkHeader + * @see Link */ class ResponseLinkFilter implements ContainerResponseFilter { @@ -77,7 +74,9 @@ class ResponseLinkFilter implements ContainerResponseFilter { @Context private ResourceMappingContext rmc; - + + @Override + @SuppressWarnings("unchecked") public void filter(ContainerRequestContext request, ContainerResponseContext response) { final Object entity = response.getEntity(); @@ -86,10 +85,8 @@ public void filter(ContainerRequestContext request, ContainerResponseContext res HeaderProcessor lhp = new HeaderProcessor(entityClass); lhp.processLinkHeaders(entity, uriInfo, rmc, response.getHeaders()); FieldProcessor lp = new FieldProcessor(entityClass); - lp.processLinks(entity, uriInfo, rmc); - - int i = 0; + lp.processLinks(entity, uriInfo, rmc); } } -} \ No newline at end of file +} diff --git a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java index cab405c6ff..f83418ee2d 100644 --- a/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java +++ b/incubator/declarative-linking/src/test/java/org/glassfish/jersey/linking/FieldProcessorTest.java @@ -40,7 +40,6 @@ package org.glassfish.jersey.linking; - import java.net.URI; import java.util.Arrays; import java.util.Collections; @@ -50,10 +49,15 @@ import java.util.logging.Logger; import java.util.regex.MatchResult; import java.util.zip.ZipEntry; + import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; -import javax.ws.rs.core.*; +import javax.ws.rs.core.Link; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.PathSegment; +import javax.ws.rs.core.UriBuilder; + import org.glassfish.jersey.linking.mapping.ResourceMappingContext; import org.glassfish.jersey.server.ExtendedUriInfo; import org.glassfish.jersey.server.model.Resource; @@ -61,168 +65,184 @@ import org.glassfish.jersey.server.model.RuntimeResource; import org.glassfish.jersey.uri.UriTemplate; -import static org.junit.Assert.*; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** - * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */ public class FieldProcessorTest { - + ExtendedUriInfo mockUriInfo = new ExtendedUriInfo() { - private final static String baseURI = "http://example.com/application/resources"; + private final static String baseURI = "http://example.com/application/resources"; - public String getPath() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public String getPath() { + throw new UnsupportedOperationException("Not supported yet."); + } - public String getPath(boolean decode) { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public String getPath(boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); + } - public List getPathSegments() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public List getPathSegments() { + throw new UnsupportedOperationException("Not supported yet."); + } - public List getPathSegments(boolean decode) { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public List getPathSegments(boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); + } - public URI getRequestUri() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public URI getRequestUri() { + throw new UnsupportedOperationException("Not supported yet."); + } - public UriBuilder getRequestUriBuilder() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public UriBuilder getRequestUriBuilder() { + throw new UnsupportedOperationException("Not supported yet."); + } - public URI getAbsolutePath() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public URI getAbsolutePath() { + throw new UnsupportedOperationException("Not supported yet."); + } - public UriBuilder getAbsolutePathBuilder() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public UriBuilder getAbsolutePathBuilder() { + throw new UnsupportedOperationException("Not supported yet."); + } - public URI getBaseUri() { - return getBaseUriBuilder().build(); - } + @Override + public URI getBaseUri() { + return getBaseUriBuilder().build(); + } - public UriBuilder getBaseUriBuilder() { - return UriBuilder.fromUri(baseURI); - } + @Override + public UriBuilder getBaseUriBuilder() { + return UriBuilder.fromUri(baseURI); + } - public MultivaluedMap getPathParameters() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public MultivaluedMap getPathParameters() { + throw new UnsupportedOperationException("Not supported yet."); + } - public MultivaluedMap getPathParameters(boolean decode) { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public MultivaluedMap getPathParameters(boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); + } - public MultivaluedMap getQueryParameters() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public MultivaluedMap getQueryParameters() { + throw new UnsupportedOperationException("Not supported yet."); + } - public MultivaluedMap getQueryParameters(boolean decode) { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public MultivaluedMap getQueryParameters(boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); + } - public List getMatchedURIs() { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public List getMatchedURIs() { + throw new UnsupportedOperationException("Not supported yet."); + } - public List getMatchedURIs(boolean decode) { - throw new UnsupportedOperationException("Not supported yet."); - } + @Override + public List getMatchedURIs(boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); + } - public List getMatchedResources() { - Object dummyResource = new Object(){}; - return Collections.singletonList(dummyResource); - } + @Override + public List getMatchedResources() { + Object dummyResource = new Object() { + }; + return Collections.singletonList(dummyResource); + } - @Override - public URI resolve(URI uri) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public Throwable getMappedThrowable() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public URI relativize(URI uri) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getMatchedResults() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public Throwable getMappedThrowable() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getMatchedTemplates() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getMatchedResults() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getPathSegments(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getMatchedTemplates() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getPathSegments(String name, boolean decode) { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getPathSegments(String name) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getMatchedRuntimeResources() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getPathSegments(String name, boolean decode) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public ResourceMethod getMatchedResourceMethod() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getMatchedRuntimeResources() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public Resource getMatchedModelResource() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public ResourceMethod getMatchedResourceMethod() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getMatchedResourceLocators() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public Resource getMatchedModelResource() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public List getLocatorSubResources() { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getMatchedResourceLocators() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public URI resolve(URI uri) { + throw new UnsupportedOperationException("Not supported yet."); + } - @Override - public List getLocatorSubResources() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } + @Override + public URI relativize(URI uri) { + throw new UnsupportedOperationException("Not supported yet."); + } + }; - }; - - ResourceMappingContext mockRmc = new ResourceMappingContext() { + private final ResourceMappingContext mockRmc = new ResourceMappingContext() { @Override public ResourceMappingContext.Mapping getMapping(Class resource) { return null; } }; - private final static String TEMPLATE_A = "foo"; public static class TestClassD { - @InjectLink(value=TEMPLATE_A, style=InjectLink.Style.RELATIVE_PATH) + @InjectLink(value = TEMPLATE_A, style = InjectLink.Style.RELATIVE_PATH) private String res1; - @InjectLink(value=TEMPLATE_A, style=InjectLink.Style.RELATIVE_PATH) + @InjectLink(value = TEMPLATE_A, style = InjectLink.Style.RELATIVE_PATH) private URI res2; } @@ -239,7 +259,7 @@ public void testProcessLinks() { private final static String TEMPLATE_B = "widgets/{id}"; public static class TestClassE { - @InjectLink(value=TEMPLATE_B, style=InjectLink.Style.RELATIVE_PATH) + @InjectLink(value = TEMPLATE_B, style = InjectLink.Style.RELATIVE_PATH) private String link; private String id; @@ -263,7 +283,7 @@ public void testProcessLinksWithFields() { } public static class TestClassF { - @InjectLink(value=TEMPLATE_B, style=InjectLink.Style.RELATIVE_PATH) + @InjectLink(value = TEMPLATE_B, style = InjectLink.Style.RELATIVE_PATH) private String thelink; private String id; @@ -315,13 +335,13 @@ public void testCollection() { } public static class TestClassG { - @InjectLink(value=TEMPLATE_B, style=InjectLink.Style.RELATIVE_PATH) + @InjectLink(value = TEMPLATE_B, style = InjectLink.Style.RELATIVE_PATH) private String relativePath; - @InjectLink(value=TEMPLATE_B, style=InjectLink.Style.ABSOLUTE_PATH) + @InjectLink(value = TEMPLATE_B, style = InjectLink.Style.ABSOLUTE_PATH) private String absolutePath; - @InjectLink(value=TEMPLATE_B, style=InjectLink.Style.ABSOLUTE) + @InjectLink(value = TEMPLATE_B, style = InjectLink.Style.ABSOLUTE) private String absolute; @InjectLink(TEMPLATE_B) @@ -409,6 +429,7 @@ public static class DependentInnerBean { public String outerUri; @InjectLink("${instance.id}") public String innerUri; + public String getId() { return "inner"; } @@ -416,6 +437,7 @@ public String getId() { public static class OuterBean { public DependentInnerBean inner = new DependentInnerBean(); + public String getId() { return "outer"; } @@ -432,7 +454,7 @@ public void testELScopes() { } public static class BoundLinkBean { - @InjectLink(value="{id}", bindings={@Binding(name="id", value="${instance.name}")}) + @InjectLink(value = "{id}", bindings = {@Binding(name = "id", value = "${instance.name}")}) public String uri; public String getName() { @@ -449,10 +471,10 @@ public void testELBinding() { assertEquals("/application/resources/name", testClass.uri); } - public static class BoundLinkOnLinkBean { - @InjectLink(value="{id}", - bindings={@Binding(name="id", value="${instance.name}")}, - rel="self") + public static class BoundLinkOnLinkBean { + @InjectLink(value = "{id}", + bindings = {@Binding(name = "id", value = "${instance.name}")}, + rel = "self") public Link link; public String getName() { @@ -470,31 +492,30 @@ public void testELBindingOnLink() { assertEquals("self", testClass.link.getRel()); } - - public static class BoundLinkOnLinksBean { + + public static class BoundLinkOnLinksBean { @InjectLinks({ - @InjectLink(value="{id}", - bindings={@Binding(name="id", value="${instance.name}")}, - rel="self"), - @InjectLink(value="{id}", - bindings={@Binding(name="id", value="${instance.name}")}, - rel="other"), - - }) + @InjectLink(value = "{id}", + bindings = {@Binding(name = "id", value = "${instance.name}")}, + rel = "self"), + @InjectLink(value = "{id}", + bindings = {@Binding(name = "id", value = "${instance.name}")}, + rel = "other"), + + }) public List links; - + @InjectLinks({ - @InjectLink(value="{id}", - bindings={@Binding(name="id", value="${instance.name}")}, - rel="self"), - @InjectLink(value="{id}", - bindings={@Binding(name="id", value="${instance.name}")}, - rel="other"), - - }) + @InjectLink(value = "{id}", + bindings = {@Binding(name = "id", value = "${instance.name}")}, + rel = "self"), + @InjectLink(value = "{id}", + bindings = {@Binding(name = "id", value = "${instance.name}")}, + rel = "other"), + + }) public Link[] linksArray; - - + public String getName() { return "name"; @@ -514,16 +535,15 @@ public void testELBindingOnLinks() { assertEquals("/application/resources/name", testClass.linksArray[0].getUri().toString()); assertEquals("self", testClass.linksArray[0].getRel()); assertEquals("other", testClass.linksArray[1].getRel()); - + } - - - + + public static class ConditionalLinkBean { - @InjectLink(value="{id}", condition="${entity.uri1Enabled}") + @InjectLink(value = "{id}", condition = "${entity.uri1Enabled}") public String uri1; - @InjectLink(value="{id}", condition="${entity.uri2Enabled}") + @InjectLink(value = "{id}", condition = "${entity.uri2Enabled}") public String uri2; public String getId() { @@ -559,7 +579,7 @@ public String getB() { } public static class SubResourceBean { - @InjectLink(resource=SubResource.class, method="getB") + @InjectLink(resource = SubResource.class, method = "getB") public String uri; } @@ -572,10 +592,8 @@ public void testSubresource() { assertEquals("/application/resources/a/b", testClass.uri); } - @Path("a") public static class QueryResource { - @Path("b") @GET public String getB(@QueryParam("query") String query, @QueryParam("query2") String query2) { @@ -588,6 +606,7 @@ public static class QueryResourceBean { public String getQueryParam() { return queryExample; } + private String queryExample; public QueryResourceBean(String queryExample, String queryExample2) { @@ -598,37 +617,36 @@ public QueryResourceBean(String queryExample, String queryExample2) { public String getQueryParam2() { return queryExample2; } + private String queryExample2; - - - @InjectLink(resource=QueryResource.class, method="getB", - bindings = { - @Binding(name="query", value = "${instance.queryParam}"), - @Binding(name="query2", value = "${instance.queryParam2}") - }) + + + @InjectLink(resource = QueryResource.class, method = "getB", + bindings = { + @Binding(name = "query", value = "${instance.queryParam}"), + @Binding(name = "query2", value = "${instance.queryParam2}") + }) public String uri; } - @Test public void testQueryResource() { System.out.println("QueryResource"); FieldProcessor instance = new FieldProcessor(QueryResourceBean.class); - QueryResourceBean testClass = new QueryResourceBean("queryExample",null); + QueryResourceBean testClass = new QueryResourceBean("queryExample", null); instance.processLinks(testClass, mockUriInfo, mockRmc); - assertEquals("/application/resources/a/b?query=queryExample", testClass.uri); + assertEquals("/application/resources/a/b?query=queryExample&query2=", testClass.uri); } - + @Test public void testDoubleQueryResource() { System.out.println("QueryResource"); FieldProcessor instance = new FieldProcessor(QueryResourceBean.class); - QueryResourceBean testClass = new QueryResourceBean("queryExample","queryExample2"); + QueryResourceBean testClass = new QueryResourceBean("queryExample", "queryExample2"); instance.processLinks(testClass, mockUriInfo, mockRmc); assertEquals("/application/resources/a/b?query=queryExample&query2=queryExample2", testClass.uri); } - - + public static class TestClassK { public static final ZipEntry zipEntry = new ZipEntry("entry"); } @@ -639,9 +657,10 @@ public static class TestClassL { private class LoggingFilter implements Filter { private int count = 0; + @Override public synchronized boolean isLoggable(LogRecord logRecord) { - if(logRecord.getThrown() instanceof IllegalAccessException) { + if (logRecord.getThrown() instanceof IllegalAccessException) { count++; return false; } From 19acd3c0d9e2a244e27b86308bba7edd0e49c398 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Wed, 7 May 2014 11:02:39 +0100 Subject: [PATCH 7/9] Fix snagged copyright headers --- .../org/glassfish/jersey/uri/PathPattern.java | 2 +- .../glassfish/jersey/uri/UriTemplateTest.java | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java b/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java index a0c63fa2e5..2915e82f8e 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/PathPattern.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development diff --git a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java index 57df0261a8..ffda1fba6a 100644 --- a/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/uri/UriTemplateTest.java @@ -59,7 +59,7 @@ /** * Taken from Jersey 1: jersey-tests: com.sun.jersey.impl.uri.UriTemplateTest * - * @author Paul.Sandoz at Sun.Com + * @author Paul.Sandoz * @author Gerard Davison (gerard.davison at oracle.com) */ public class UriTemplateTest { @@ -398,31 +398,31 @@ public void testGroupIndexes() throws Exception { assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[0])); template = new UriTemplate("/{a}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1})); template = new UriTemplate("/{a}/b"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1})); template = new UriTemplate("/{a}/{b}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2, 3})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2})); template = new UriTemplate("/{a}/{b}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2, 3})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2})); template = new UriTemplate("/{a}/b/{c}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2, 3})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 2})); template = new UriTemplate("/{a: (abc)+}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 3})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1})); template = new UriTemplate("/{a: (abc)+}/b"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 3})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1})); template = new UriTemplate("/{a: (abc)+}/{b}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 3, 4})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 3})); template = new UriTemplate("/{a: (abc)+}/b/{c}"); - assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 3, 4})); + assertThat(template.getPattern().getGroupIndexes(), equalTo(new int[] {1, 3})); } void _testSubstitutionArray(String template, String uri, String... values) { From 7605b2549000291a45f1a5f0428dc02a17e0cb92 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Wed, 7 May 2014 11:20:01 +0100 Subject: [PATCH 8/9] Try again, should be a clean copyright header --- .../java/org/glassfish/jersey/linking/EntityDescriptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java index b69c446e08..3b83321c09 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java @@ -9,7 +9,7 @@ * may not use this file except in compliance with the License. You can * obtain a copy of the License at * http://glassfish.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/gLICENSE.txt. See the License for the specific + * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each From 3419b4e251c4788ec552daa56c3d374f0688e7b8 Mon Sep 17 00:00:00 2001 From: Gerard Davison Date: Wed, 7 May 2014 13:58:57 +0100 Subject: [PATCH 9/9] Trigger another validation build, just add a space --- .../org/glassfish/jersey/linking/DeclarativeLinkingFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java index 465faf9ae8..d89fa4aa09 100644 --- a/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java +++ b/incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java @@ -54,7 +54,7 @@ /** * A feature to enable the declarative linking functionality. - * + * * @author Mark Hadley * @author Gerard Davison (gerard.davison at oracle.com) */