Author: sergeyb Date: Wed Apr 29 10:55:04 2009 New Revision: 769741 URL: http://svn.apache.org/viewvc?rev=769741&view=rev Log: JAXRS: Implementing ResponseBuilder.variants() plus various minor updates
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/MetadataMap.java cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProvider.java cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImpl.java cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/VariantListBuilderImpl.java cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/MetadataMapTest.java cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProviderTest.java cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImplTest.java cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookApplication.java Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/MetadataMap.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/MetadataMap.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/MetadataMap.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/MetadataMap.java Wed Apr 29 10:55:04 2009 @@ -42,12 +42,12 @@ this(store, false, false); } + public MetadataMap(boolean readOnly, boolean caseInsensitive) { + this(null, readOnly, caseInsensitive); + } + public MetadataMap(Map<K, List<V>> store, boolean readOnly, boolean caseInsensitive) { - if (!readOnly && caseInsensitive) { - throw new IllegalArgumentException( - "Case-insensitive keys are only supported for read-only maps at the moment"); - } this.caseInsensitive = caseInsensitive; this.m = new LinkedHashMap<K, List<V>>(); @@ -65,7 +65,7 @@ } public void add(K key, V value) { - List<V> data = m.get(key); + List<V> data = this.get(key); if (data == null) { data = new ArrayList<V>(); m.put(key, data); @@ -74,14 +74,14 @@ } public V getFirst(K key) { - List<V> data = get(key); + List<V> data = this.get(key); return data == null ? null : data.get(0); } public void putSingle(K key, V value) { List<V> data = new ArrayList<V>(); data.add(value); - m.put(key, data); + this.put(key, data); } public void clear() { @@ -162,4 +162,5 @@ return m.toString(); } + } Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProvider.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProvider.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProvider.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProvider.java Wed Apr 29 10:55:04 2009 @@ -19,7 +19,6 @@ package org.apache.cxf.jaxrs.impl; -import javax.ws.rs.core.Cookie; import javax.ws.rs.core.NewCookie; import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate; @@ -95,9 +94,7 @@ if (value.isSecure()) { sb.append(';').append(SECURE); } - if (value.getVersion() != Cookie.DEFAULT_VERSION) { - sb.append(';').append(VERSION).append('=').append(1); - } + sb.append(';').append(VERSION).append('=').append(value.getVersion()); return sb.toString(); } Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImpl.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImpl.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImpl.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImpl.java Wed Apr 29 10:55:04 2009 @@ -53,8 +53,8 @@ public Response build() { ResponseImpl r = new ResponseImpl(status, entity); - MetadataMap<String, Object> m = new MetadataMap<String, Object>(); - m.putAll(metadata); + MetadataMap<String, Object> m = + new MetadataMap<String, Object>(metadata, false, true); r.addMetadata(m); reset(); return r; @@ -146,7 +146,46 @@ @Override public ResponseBuilder variants(List<Variant> variants) { - throw new UnsupportedOperationException("Only a single variant option is supported"); + if (variants == null) { + metadata.remove(HttpHeaders.VARY); + return this; + } + String acceptVary = null; + String acceptLangVary = null; + String acceptEncVary = null; + for (Variant v : variants) { + MediaType mt = v.getMediaType(); + if (mt != null) { + acceptVary = HttpHeaders.ACCEPT; + addHeader(HttpHeaders.ACCEPT, mt); + } + Locale l = v.getLanguage(); + if (l != null) { + acceptLangVary = HttpHeaders.ACCEPT_LANGUAGE; + addHeader(HttpHeaders.ACCEPT_LANGUAGE, l); + } + String enc = v.getEncoding(); + if (enc != null) { + acceptEncVary = HttpHeaders.ACCEPT_ENCODING; + addHeader(HttpHeaders.ACCEPT_ENCODING, enc); + } + } + handleVaryValue(acceptVary, acceptLangVary, acceptEncVary); + return this; + } + + private void handleVaryValue(String ...values) { + List<Object> varyValues = metadata.get(HttpHeaders.VARY); + for (String v : values) { + if (v == null) { + metadata.remove(v); + if (varyValues != null) { + varyValues.remove(v); + } + } else { + addHeader(HttpHeaders.VARY, v); + } + } } // CHECKSTYLE:OFF @@ -180,11 +219,18 @@ private ResponseBuilder addHeader(String name, Object... values) { if (values != null && values.length >= 1 && values[0] != null) { for (Object value : values) { - metadata.add(name, value.toString()); + if (!valueExists(name, value)) { + metadata.add(name, value.toString()); + } } } else { metadata.remove(name); } return this; } + + private boolean valueExists(String key, Object value) { + List<Object> values = metadata.get(key); + return values == null ? false : values.contains(value.toString()); + } } Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java Wed Apr 29 10:55:04 2009 @@ -273,6 +273,11 @@ @Override public UriBuilder path(String path) throws IllegalArgumentException { + + if (path == null) { + throw new IllegalArgumentException("path is null"); + } + List<PathSegment> segments = JAXRSUtils.getPathSegments(path, false, false); if (!paths.isEmpty() && !matrix.isEmpty()) { PathSegment ps = paths.remove(paths.size() - 1); Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/VariantListBuilderImpl.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/VariantListBuilderImpl.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/VariantListBuilderImpl.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/VariantListBuilderImpl.java Wed Apr 29 10:55:04 2009 @@ -22,10 +22,8 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Set; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Variant; @@ -33,9 +31,9 @@ public class VariantListBuilderImpl extends VariantListBuilder { - private Set<String> encodings = new HashSet<String>(); - private Set<Locale> languages = new HashSet<Locale>(); - private Set<MediaType> mediaTypes = new HashSet<MediaType>(); + private List<String> encodings = new ArrayList<String>(); + private List<Locale> languages = new ArrayList<Locale>(); + private List<MediaType> mediaTypes = new ArrayList<MediaType>(); private List<Variant> variants = new ArrayList<Variant>(); public VariantListBuilderImpl() { Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java Wed Apr 29 10:55:04 2009 @@ -20,11 +20,15 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -33,6 +37,7 @@ import javax.ws.rs.ext.Provider; import org.apache.commons.lang.ClassUtils; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.lifecycle.PerRequestResourceProvider; import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; @@ -42,6 +47,8 @@ public class CXFNonSpringJaxrsServlet extends CXFNonSpringServlet { + private static final Logger LOG = LogUtils.getL7dLogger(CXFNonSpringJaxrsServlet.class); + private static final String SERVICE_ADDRESS_PARAM = "jaxrs.address"; private static final String SERVICE_CLASSES_PARAM = "jaxrs.serviceClasses"; private static final String PROVIDERS_PARAM = "jaxrs.providers"; @@ -166,7 +173,7 @@ } protected void createServerFromApplication(String cName) throws ServletException { - Class<?> appClass = loadClass(cName); + Class<?> appClass = loadClass(cName, "Application"); Application app = null; try { app = (Application)appClass.newInstance(); @@ -180,14 +187,19 @@ + " can not be instantiated due to IllegalAccessException"); } + verifySingletons(app.getSingletons()); + List<Class> resourceClasses = new ArrayList<Class>(); List<Object> providers = new ArrayList<Object>(); Map<Class, ResourceProvider> map = new HashMap<Class, ResourceProvider>(); // at the moment we don't support per-request providers, only resource classes + // Note, app.getClasse() returns a list of per-resource classes for (Class<?> c : app.getClasses()) { - resourceClasses.add(c); - map.put(c, new PerRequestResourceProvider(c)); + if (isValidPerRequestResourceClass(c, app.getSingletons())) { + resourceClasses.add(c); + map.put(c, new PerRequestResourceProvider(c)); + } } // we can get either a provider or resource class here @@ -212,10 +224,52 @@ } private Class<?> loadClass(String cName) throws ServletException { + return loadClass(cName, "Resource"); + } + + private Class<?> loadClass(String cName, String classType) throws ServletException { try { return ClassUtils.getClass(CXFNonSpringJaxrsServlet.class.getClassLoader(), cName.trim()); } catch (ClassNotFoundException ex) { - throw new ServletException("No resource class " + cName.trim() + " can be found", ex); + throw new ServletException("No " + classType + " class " + cName.trim() + " can be found", ex); + } + } + + private boolean isValidResourceClass(Class<?> c) { + if (c.isInterface() || Modifier.isAbstract(c.getModifiers())) { + LOG.info("Ignoring invalid resource class " + c.getName()); + return false; + } + return true; + } + + private boolean isValidPerRequestResourceClass(Class<?> c, Set<Object> singletons) { + if (!isValidResourceClass(c)) { + return false; + } + for (Object s : singletons) { + if (c == s.getClass()) { + LOG.info("Ignoring per-request resource class " + c.getName() + + " as it is also registered as singleton"); + return false; + } + } + return true; + } + + + private void verifySingletons(Set<Object> singletons) throws ServletException { + if (singletons.isEmpty()) { + return; + } + Set<String> map = new HashSet<String>(); + for (Object s : singletons) { + if (map.contains(s.getClass().getName())) { + throw new ServletException("More than one instance of the same singleton class " + + s.getClass().getName() + " is available"); + } else { + map.add(s.getClass().getName()); + } } } } Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/MetadataMapTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/MetadataMapTest.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/MetadataMapTest.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/MetadataMapTest.java Wed Apr 29 10:55:04 2009 @@ -82,7 +82,8 @@ m2.remove("baz"); } - public void testCaseInsensitive() { + @Test + public void testGetCaseInsensitive() { MetadataMap<String, Object> m = new MetadataMap<String, Object>(); m.add("Baz", "bar"); MetadataMap<String, Object> m2 = new MetadataMap<String, Object>(m, true, true); @@ -90,5 +91,11 @@ assertEquals("bar", m2.getFirst("Baz")); assertTrue(m2.containsKey("BaZ")); assertTrue(m2.containsKey("Baz")); + List<Object> values = m2.get("baz"); + assertEquals(1, values.size()); + assertEquals("bar", values.get(0).toString()); } + + + } Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProviderTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProviderTest.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProviderTest.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/NewCookieHeaderProviderTest.java Wed Apr 29 10:55:04 2009 @@ -69,7 +69,7 @@ @Test public void testToString() { NewCookie c = new NewCookie("foo", "bar", "path", "domain", "comment", 2, true); - assertEquals("foo=bar;Comment=comment;Domain=domain;Max-Age=2;Path=path;Secure", + assertEquals("foo=bar;Comment=comment;Domain=domain;Max-Age=2;Path=path;Secure;Version=1", c.toString()); } Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImplTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImplTest.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImplTest.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseBuilderImplTest.java Wed Apr 29 10:55:04 2009 @@ -22,11 +22,14 @@ import java.net.URI; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; import java.util.Locale; import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Variant; import org.apache.cxf.jaxrs.utils.HttpUtils; @@ -36,7 +39,7 @@ public class ResponseBuilderImplTest extends Assert { - + @Test public void testLanguage() { MetadataMap<String, Object> m = new MetadataMap<String, Object>(); @@ -65,8 +68,8 @@ @Test public void testAddCookie() { MetadataMap<String, Object> m = new MetadataMap<String, Object>(); - m.add("Set-Cookie", "a=b"); - m.add("Set-Cookie", "c=d"); + m.add("Set-Cookie", "a=b;Version=1"); + m.add("Set-Cookie", "c=d;Version=1"); checkBuild(Response.ok().cookie(new NewCookie("a", "b")) .cookie(new NewCookie("c", "d")).build(), 200, null, m); @@ -93,6 +96,20 @@ } @Test + public void testVariant() throws Exception { + + MetadataMap<String, Object> m = new MetadataMap<String, Object>(); + m.putSingle("Content-Type", "text/xml"); + m.putSingle("Content-Language", "en"); + m.putSingle("Content-Encoding", "gzip"); + Variant v = new Variant(MediaType.TEXT_XML_TYPE, new Locale("en"), "gzip"); + + checkBuild(Response.ok().variant(v).build(), + 200, null, m); + } + + + @Test public void testCreatedNoEntity() throws Exception { MetadataMap<String, Object> m = new MetadataMap<String, Object>(); @@ -104,6 +121,7 @@ } + private void checkBuild(Response r, int status, Object entity, MetadataMap<String, Object> meta) { ResponseImpl ri = (ResponseImpl)r; @@ -112,4 +130,27 @@ assertEquals("Wrong meta", ri.getMetadata(), meta); } + @Test + public void testVariants() throws Exception { + + MetadataMap<String, Object> m = new MetadataMap<String, Object>(); + m.add("Accept", "text/xml"); + m.add("Accept", "application/xml"); + m.add("Accept-Language", "en_UK"); + m.add("Accept-Language", "en_GB"); + m.add("Accept-Encoding", "compress"); + m.add("Accept-Encoding", "gzip"); + m.add("Vary", "Accept"); + m.add("Vary", "Accept-Language"); + m.add("Vary", "Accept-Encoding"); + List<Variant> vts = Variant.VariantListBuilder.newInstance() + .mediaTypes(MediaType.TEXT_XML_TYPE, MediaType.APPLICATION_XML_TYPE). + languages(new Locale("en", "UK"), new Locale("en", "GB")).encodings("compress", "gzip"). + add().build(); + + checkBuild(Response.ok().variants(vts).build(), + 200, null, m); + } + + } Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java (original) +++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java Wed Apr 29 10:55:04 2009 @@ -41,6 +41,11 @@ public void testCtorNull() throws Exception { new UriBuilderImpl(null); } + + @Test(expected = IllegalArgumentException.class) + public void testPathStringNull() throws Exception { + new UriBuilderImpl().path((String)null); + } @Test public void testCtorAndBuild() throws Exception { Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookApplication.java URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookApplication.java?rev=769741&r1=769740&r2=769741&view=diff ============================================================================== --- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookApplication.java (original) +++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookApplication.java Wed Apr 29 10:55:04 2009 @@ -29,6 +29,7 @@ public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<Class<?>>(); classes.add(org.apache.cxf.systest.jaxrs.BookStorePerRequest.class); + classes.add(org.apache.cxf.systest.jaxrs.BookStoreJaxrsJaxws.class); return classes; }