You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2012/08/17 15:48:25 UTC

svn commit: r1374255 - in /cxf/trunk/rt/frontend/jaxrs/src: main/java/org/apache/cxf/jaxrs/impl/ test/java/org/apache/cxf/jaxrs/impl/

Author: sergeyb
Date: Fri Aug 17 13:48:25 2012
New Revision: 1374255

URL: http://svn.apache.org/viewvc?rev=1374255&view=rev
Log:
[CXF-4455] Initial support for Link header

Added:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java   (with props)
Modified:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/RuntimeDelegateImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java?rev=1374255&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java Fri Aug 17 13:48:25 2012
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.impl;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Link;
+import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
+
+public class LinkHeaderProvider implements HeaderDelegate<Link> {
+
+    private static final String REL = "rel";
+    private static final String TYPE = "type";
+    private static final String TITLE = "title";
+    
+    private static final Set<String> KNOWN_PARAMETERS;
+    static {
+        KNOWN_PARAMETERS = new HashSet<String>(Arrays.asList(REL, TYPE, TITLE));
+    }
+    
+    public Link fromString(String value) {
+        
+        if (value == null) {
+            throw new IllegalArgumentException("Link value can not be null");
+        }
+        value = value.trim();
+        int closeIndex = value.indexOf('>');
+        if (!value.startsWith("<") || closeIndex < 2) {
+            throw new IllegalArgumentException("Link URI is missing");
+        }
+        Link.Builder builder = new Link.Builder();
+        builder.uri(value.substring(1, closeIndex).trim());
+        if (closeIndex < value.length() - 1) {
+            
+            String[] tokens = value.substring(closeIndex + 1).split(";");
+            for (String token : tokens) {
+                String theToken = token.trim();
+                if (theToken.isEmpty()) {
+                    continue;
+                }
+                String paramName = null;
+                String paramValue = null;
+                int i = token.indexOf('=');
+                if (i != -1) {
+                    paramName = theToken.substring(0, i).trim();
+                    paramValue = i == theToken.length() - 1 ? "" : theToken.substring(i + 1).trim();
+                }
+                if (REL.equals(paramName)) {
+                    String[] rels = removeQuotesIfNeeded(paramValue).split(",");
+                    for (String rel : rels) {
+                        builder.rel(rel.trim());
+                    }
+                } else if (TYPE.equals(paramName)) {
+                    builder.type(removeQuotesIfNeeded(paramValue));
+                } else if (TITLE.equals(paramName)) {
+                    builder.title(removeQuotesIfNeeded(paramValue));
+                } else {
+                    builder.param(paramName, paramValue);
+                }
+            }
+        }
+        return builder.build();
+        
+    }
+
+    private String removeQuotesIfNeeded(String value) {
+        if (value.length() > 1 && value.startsWith("\"") && value.endsWith("\"")) {
+            return value.substring(1, value.length() - 1);
+        }  else {
+            return value;
+        }
+    }
+    
+    public String toString(Link link) {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append('<');
+        sb.append(link.getUri());
+        sb.append('>');
+        
+        List<String> rels = link.getRel();
+        if (!rels.isEmpty()) {
+            sb.append(";").append(REL).append('=');
+            writeListParamValues(sb, rels);
+        }
+        if (link.getTitle() != null) {
+            sb.append(";").append(TITLE).append("=\"").append(link.getTitle()).append('"');
+        }
+        if (link.getType() != null) {
+            sb.append(";").append(TYPE).append('=').append(link.getType());
+        }
+        for (Map.Entry<String, List<String>> entry : link.getParams().entrySet()) {
+            if (KNOWN_PARAMETERS.contains(entry.getKey())) {
+                continue;
+            }
+            sb.append(";").append(entry.getKey()).append('=');
+            writeListParamValues(sb, entry.getValue());
+        }
+        
+        return sb.toString();
+        
+    }
+
+    private void writeListParamValues(StringBuilder sb, List<String> values) {
+        if (values.size() > 1) {
+            sb.append('"');
+        }
+        for (int i = 0; i < values.size(); i++) {
+            sb.append(values.get(i));
+            if (i < values.size() - 1) { 
+                sb.append(',');    
+            }
+        }
+        if (values.size() > 1) {
+            sb.append('"');
+        }
+    }
+}

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkHeaderProvider.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java?rev=1374255&r1=1374254&r2=1374255&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java Fri Aug 17 13:48:25 2012
@@ -39,7 +39,6 @@ import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Link;
-import javax.ws.rs.core.Link.Builder;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.NewCookie;
@@ -217,26 +216,42 @@ public final class ResponseImpl extends 
         return header == null ? null : MediaType.valueOf(header);
     }
     
+    public boolean hasLink(String relation) {
+        return getLink(relation) != null;
+    }
+    
     public Link getLink(String relation) {
-        // TODO Auto-generated method stub
+        Set<Map.Entry<List<String>, Link>> entries = getAllLinks().entrySet();
+        for (Map.Entry<List<String>, Link> entry : entries) {
+            if (entry.getKey().contains(relation)) {
+                return entry.getValue();
+            }
+        }
         return null;
     }
 
-    public Builder getLinkBuilder(String arg0) {
-        // TODO Auto-generated method stub
-        return null;
+    public Link.Builder getLinkBuilder(String relation) {
+        return Link.fromLink(getLink(relation));
     }
 
     public Set<Link> getLinks() {
-        // TODO Auto-generated method stub
-        return null;
+        return new HashSet<Link>(getAllLinks().values());
     }
 
-    public boolean hasLink(String relation) {
-        // TODO Auto-generated method stub
-        return false;
+    private Map<List<String>, Link> getAllLinks() {
+        List<Object> linkValues = metadata.get(HttpHeaders.LINK);
+        if (linkValues == null) {
+            return Collections.emptyMap();
+        } else {
+            Map<List<String>, Link> links = new HashMap<List<String>, Link>();
+            for (Object o : linkValues) {
+                Link link = Link.valueOf(o.toString());
+                links.put(link.getRel(), link);
+            }
+            return links;
+        }
     }
-
+    
     public <T> T readEntity(Class<T> cls) throws MessageProcessingException, IllegalStateException {
         return readEntity(cls, new Annotation[]{});
     }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/RuntimeDelegateImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/RuntimeDelegateImpl.java?rev=1374255&r1=1374254&r2=1374255&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/RuntimeDelegateImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/RuntimeDelegateImpl.java Fri Aug 17 13:48:25 2012
@@ -26,6 +26,7 @@ import javax.ws.rs.core.Application;
 import javax.ws.rs.core.CacheControl;
 import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.Link;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.NewCookie;
 import javax.ws.rs.core.Response.ResponseBuilder;
@@ -49,6 +50,7 @@ public class RuntimeDelegateImpl extends
         headerProviders.put(EntityTag.class, new EntityTagHeaderProvider());
         headerProviders.put(Cookie.class, new CookieHeaderProvider());
         headerProviders.put(NewCookie.class, new NewCookieHeaderProvider());
+        headerProviders.put(Link.class, new LinkHeaderProvider());
     }
     
     

Added: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java?rev=1374255&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java Fri Aug 17 13:48:25 2012
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.impl;
+
+import java.util.List;
+
+import javax.ws.rs.core.Link;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class LinkHeaderProviderTest extends Assert {
+    
+    @Test
+    public void testFromSimpleString() {
+        Link l = Link.valueOf("<http://bar>");
+        assertEquals("http://bar", l.getUri().toString());
+    }
+    
+    @Test
+    public void testFromSimpleString2() {
+        Link l = Link.valueOf("</>");
+        assertEquals("/", l.getUri().toString());
+    }
+    
+    @Test
+    public void testFromComplexString() {
+        Link l = Link.valueOf("<http://bar>;rel=next;title=\"Next Link\";type=text/xml;method=get");
+        assertEquals("http://bar", l.getUri().toString());
+        List<String> rels = l.getRel();
+        assertEquals(1, rels.size());
+        assertEquals("next", rels.get(0));
+        assertEquals("Next Link", l.getTitle());
+        assertEquals("get", l.getMethod());
+    }
+    
+    @Test
+    public void testToString() {
+        String headerValue = "<http://bar>;rel=next;title=\"Next Link\";type=text/xml;method=get";
+        Link l = Link.valueOf(headerValue);
+        String result = l.toString();
+        assertEquals(result, headerValue);
+    }
+    
+        
+}

Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkHeaderProviderTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java?rev=1374255&r1=1374254&r2=1374255&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java Fri Aug 17 13:48:25 2012
@@ -21,8 +21,10 @@ package org.apache.cxf.jaxrs.impl;
 
 import java.io.ByteArrayInputStream;
 import java.util.Map;
+import java.util.Set;
 
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Link;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.NewCookie;
 import javax.ws.rs.core.Response;
@@ -171,4 +173,34 @@ public class ResponseImplTest extends As
         ri.addMetadata(meta);
         assertEquals("text/xml", ri.getMediaType().toString());
     }
+    
+    @Test
+    public void testGetLinks() {
+        ResponseImpl ri = new ResponseImpl(200);
+        MetadataMap<String, Object> meta = new MetadataMap<String, Object>();
+        ri.addMetadata(meta);
+        assertFalse(ri.hasLink("next"));
+        assertNull(ri.getLink("next"));
+        assertFalse(ri.hasLink("prev"));
+        assertNull(ri.getLink("prev"));
+        
+        meta.add(HttpHeaders.LINK, "<http://next>;rel=next");
+        meta.add(HttpHeaders.LINK, "<http://prev>;rel=prev");
+        
+        assertTrue(ri.hasLink("next"));
+        Link next = ri.getLink("next");
+        assertNotNull(next);
+        assertTrue(ri.hasLink("prev"));
+        Link prev = ri.getLink("prev");
+        assertNotNull(prev);
+        
+        Set<Link> links = ri.getLinks();
+        assertTrue(links.contains(next));
+        assertTrue(links.contains(prev));
+        
+        assertEquals("http://next", next.getUri().toString());
+        assertEquals("next", next.getRel().get(0));
+        assertEquals("http://prev", prev.getUri().toString());
+        assertEquals("prev", prev.getRel().get(0));
+    }
 }