You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wink.apache.org by ro...@apache.org on 2009/12/18 00:20:44 UTC

svn commit: r891982 - in /incubator/wink/trunk: wink-client/src/test/java/org/apache/wink/client/ wink-common/src/main/java/org/apache/wink/common/internal/model/ wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/ wink-com...

Author: rott
Date: Thu Dec 17 23:20:44 2009
New Revision: 891982

URL: http://svn.apache.org/viewvc?rev=891982&view=rev
Log:
support usage of custom providers during AtomEntry->AtomContent->value retrieval when outside the scope of the thread local store of the client-server thread.

Added:
    incubator/wink/trunk/wink-client/src/test/java/org/apache/wink/client/ClientAtomTest.java
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntryProvider.java
Modified:
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/model/ModelUtils.java
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntrySyndEntryProvider.java
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/xml/AbstractJAXBProvider.java
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/model/atom/AtomContent.java
    incubator/wink/trunk/wink-common/src/main/resources/META-INF/core/wink-providers

Added: incubator/wink/trunk/wink-client/src/test/java/org/apache/wink/client/ClientAtomTest.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-client/src/test/java/org/apache/wink/client/ClientAtomTest.java?rev=891982&view=auto
==============================================================================
--- incubator/wink/trunk/wink-client/src/test/java/org/apache/wink/client/ClientAtomTest.java (added)
+++ incubator/wink/trunk/wink-client/src/test/java/org/apache/wink/client/ClientAtomTest.java Thu Dec 17 23:20:44 2009
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * 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.wink.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.wink.common.internal.providers.entity.xml.JAXBXmlProvider;
+import org.apache.wink.common.model.atom.AtomEntry;
+
+public class ClientAtomTest extends BaseTest {
+    
+//    @Path("atomresource")
+//    private class MyAtomResource {
+//        @Path("entry")
+//        @GET
+//        @Produces("application/atom+xml")
+//        public Response getEntry() {
+//            MyPojo myPojo = new MyPojo();
+//            myPojo.setTitle("wheeee!!!");
+//            // wrap POJO (JAXB object) in AtomContent, AtomEntry
+//            return Response.status(Status.OK).entity(wrapInAtom(myPojo)).build();
+//        }
+//        
+//        /*
+//         * utility method to wrap responses in AtomEntry
+//         */
+//        private AtomEntry wrapInAtom(Object obj) {
+//            AtomContent atomContent = new AtomContent();
+//            atomContent.setType(MediaType.APPLICATION_XML);
+//            atomContent.setValue(obj);
+//            AtomEntry retAtomEntry = new AtomEntry();
+//            retAtomEntry.setContent(atomContent);
+//            return retAtomEntry;
+//        }
+//    }
+    
+    @XmlRootElement(name = "mypojo", namespace = "http://mypojons/")
+    @XmlType(name = "mypojo", propOrder = {"title"})
+    protected static class MyPojo {
+
+        private String title;
+
+        public void setTitle(String title) {
+            this.title = title;
+        }
+        
+        public String getTitle() {
+            return title;
+        }
+
+    }
+
+    private RestClient getRestClient() {
+        return new RestClient(new ClientConfig().applications(new Application() {
+            
+            @Override
+            public Set<Object> getSingletons() {
+                return null;
+            }
+
+            @Override
+            public Set<Class<?>> getClasses() {
+                Set<Class<?>> set = new HashSet<Class<?>>();
+                set.add(MyProvider.class);
+                return set;
+            }
+
+        }));
+    }
+    
+    @Provider
+    @Consumes( {MediaType.TEXT_XML, MediaType.APPLICATION_XML, MediaType.WILDCARD})
+    @Produces( {MediaType.TEXT_XML, MediaType.APPLICATION_XML, MediaType.WILDCARD})
+    public static class MyProvider extends JAXBXmlProvider {
+        
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType,
+                Annotation[] annotations, MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public Object readFrom(Class<Object> type, Type genericType,
+                Annotation[] annotations, MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException,
+                WebApplicationException {
+            MyPojo myPojo = (MyPojo)super.readFrom(type, genericType, annotations, mediaType, httpHeaders,
+                    entityStream);
+            String oldTitle = myPojo.getTitle();
+            myPojo.setTitle(oldTitle + " -- MyProvider was here.");
+            return myPojo;
+        }
+        
+    }
+    
+    private final static String responseString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
+    "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:ns2=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns:ns3=\"http://www.w3.org/1999/xhtml\">" +
+    "<content type=\"application/xml\">" +
+    "<ns2:mypojo xmlns:ns2=\"http://mypojons/\">" +
+    "<title xmlns:ns5=\"http://www.w3.org/2005/Atom\" xmlns=\"\">wheeee!!!</title>" +
+    "</ns2:mypojo>" +
+    "</content>" +
+    "</entry>";
+    
+    public void testAtomContentRetrieval() {
+        server.setMockResponseCode(200);
+        server.setMockResponseContentType(MediaType.APPLICATION_ATOM_XML);
+        server.setMockResponseContent(responseString);
+        RestClient client = getRestClient();
+        Resource resource = client.resource(serviceURL + "/atomresource/entry");
+        
+        // do get with response
+        ClientResponse clientResponse = resource.get();
+        // unwrap the AtomEntry, AtomContent value
+        MyPojo myPojo = (MyPojo)clientResponse.getEntity(AtomEntry.class).getContent().getValue(MyPojo.class);
+        
+        // Confirm that the custom MyProvider is used during AtomContent.getValue(MyPojo.class) call.
+        // Custom providers are stored on the client-server transaction's thread local store.  This assertion
+        // ensures that the custom providers are held long enough for a client application to use them during
+        // retrieval of the value from the AtomContent object, which occurs in its own thread.
+        
+        assertEquals("wheeee!!! -- MyProvider was here.", myPojo.getTitle());
+
+    }
+    
+}

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/model/ModelUtils.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/model/ModelUtils.java?rev=891982&r1=891981&r2=891982&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/model/ModelUtils.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/model/ModelUtils.java Thu Dec 17 23:20:44 2009
@@ -22,6 +22,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.lang.annotation.Annotation;
@@ -39,6 +40,7 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.Providers;
+import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
@@ -60,6 +62,7 @@
 import org.apache.wink.common.internal.lifecycle.ScopeLifecycleManager;
 import org.apache.wink.common.internal.registry.ProvidersRegistry;
 import org.apache.wink.common.internal.registry.metadata.ProviderMetadataCollector;
+import org.apache.wink.common.internal.runtime.AbstractRuntimeContext;
 import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
 import org.apache.wink.common.internal.utils.UnmodifiableMultivaluedMap;
 import org.apache.wink.common.model.atom.AtomContent;
@@ -215,6 +218,7 @@
                                   Annotation[] annotations,
                                   MultivaluedMap<String, String> httpHeaders,
                                   MediaType mediaType) throws IOException {
+        
         if (list == null || list.isEmpty()) {
             return null;
         }
@@ -293,10 +297,37 @@
                     providers = new ProvidersImpl(providersRegistry, runtimeContext);
                 }
             }
+            
+            /*
+             * Need to set a temporary RuntimeContextTLS just in case we're already outside of the runtime context.
+             * This may occur when a client app is retrieving the AtomContent value, expecting it to be unmarshalled
+             * automatically, but we are already outside of the client-server thread, and thus no longer have a
+             * RuntimeContextTLS from which to retrieve or inject providers.
+             */
+            
+            RuntimeContext tempRuntimeContext = RuntimeContextTLS.getRuntimeContext();
+            if (tempRuntimeContext == null) {
+                final Providers p = providers;
+                RuntimeContextTLS.setRuntimeContext(new AbstractRuntimeContext() {
+                    {
+                        setAttribute(Providers.class, p);
+                    }
+
+                    public OutputStream getOutputStream() throws IOException {
+                        return null;
+                    }
+
+                    public InputStream getInputStream() throws IOException {
+                        return null;
+                    }
+                });
+            }
+            
             MessageBodyReader<T> reader =
                 providers.getMessageBodyReader(type, type, EMPTY_ARRAY, mediaType);
             if (reader == null)
                 throw new WebApplicationException(Response.Status.UNSUPPORTED_MEDIA_TYPE);
+
             T read =
                 reader.readFrom(type,
                                 type,
@@ -304,6 +335,10 @@
                                 mediaType,
                                 httpHeaders,
                                 new ByteArrayInputStream((byte[])value));
+            
+            // Reset RuntimeContext from temporary above.  tempRuntimeContext may be null here, which is ok.
+            RuntimeContextTLS.setRuntimeContext(tempRuntimeContext);
+            
             return read;
         }
         throw new ClassCastException("Cannot cast " + value.getClass().getName()

Added: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntryProvider.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntryProvider.java?rev=891982&view=auto
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntryProvider.java (added)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntryProvider.java Thu Dec 17 23:20:44 2009
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * 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.wink.common.internal.providers.entity.atom;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+import org.apache.wink.common.internal.providers.entity.xml.JAXBXmlProvider;
+import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
+import org.apache.wink.common.internal.utils.MediaTypeUtils;
+import org.apache.wink.common.model.atom.AtomContent;
+import org.apache.wink.common.model.atom.AtomEntry;
+
+@Provider
+@Consumes( {MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaTypeUtils.JAVASCRIPT})
+@Produces( {MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaTypeUtils.JAVASCRIPT})
+public class AtomEntryProvider extends JAXBXmlProvider {
+
+    @Override
+    public boolean isReadable(Class<?> type,
+                              Type genericType,
+                              Annotation[] annotations,
+                              MediaType mediaType) {
+        return type == AtomEntry.class;
+    }
+
+    @Override
+    public Object readFrom(Class<Object> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+            throws IOException, WebApplicationException {
+
+
+        AtomEntry atomEntry = (AtomEntry) super.readFrom(type, genericType, annotations, mediaType, httpHeaders,
+                entityStream);
+
+        /*
+         * The value in the AtomContent object is arbitrary, set by the server.  We want to use the
+         * built-in AND client application supplied providers.  Because these providers are collected
+         * on the thread local store, and the unmarshalling of the AtomContent value is done lazily
+         * (after the client-server context has expired), we need some way to hold onto the Providers
+         * long enough for the client app to be able to seamlessly get the value off the AtomContent
+         * object.  Thus the need to set the *real* providers list on the AtomContent object, so it
+         * can use the list to pass to ModelUtils when it needs to retrieve and unmarshal the AtomContent
+         * value.
+         * 
+         * We have to be careful to use the real Providers list instead of the one from the injected
+         * local providers field in this class.  The injected object may be a "proxy" to the real
+         * providers, and could cause an infinite loop when ModelUtils.readValue calls back through
+         * the providers.
+         */
+        AtomContent content = atomEntry.getContent();
+        if (content != null) {
+            content.setProviders(RuntimeContextTLS.getRuntimeContext().getAttribute(Providers.class));
+        }
+
+        return atomEntry;
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return false;  // so JAXBXmlProvider will do the write
+    }
+
+}

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntrySyndEntryProvider.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntrySyndEntryProvider.java?rev=891982&r1=891981&r2=891982&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntrySyndEntryProvider.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/atom/AtomEntrySyndEntryProvider.java Thu Dec 17 23:20:44 2009
@@ -37,7 +37,9 @@
 import javax.ws.rs.ext.Provider;
 import javax.ws.rs.ext.Providers;
 
+import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
 import org.apache.wink.common.internal.utils.MediaTypeUtils;
+import org.apache.wink.common.model.atom.AtomContent;
 import org.apache.wink.common.model.atom.AtomEntry;
 import org.apache.wink.common.model.synd.SyndEntry;
 
@@ -72,6 +74,27 @@
                             mediaType,
                             httpHeaders,
                             entityStream);
+        
+        /*
+         * The value in the AtomContent object is arbitrary, set by the server.  We want to use the
+         * built-in AND client application supplied providers.  Because these providers are collected
+         * on the thread local store, and the unmarshalling of the AtomContent value is done lazily
+         * (after the client-server context has expired), we need some way to hold onto the Providers
+         * long enough for the client app to be able to seamlessly get the value off the AtomContent
+         * object.  Thus the need to set the *real* providers list on the AtomContent object, so it
+         * can use the list to pass to ModelUtils when it needs to retrieve and unmarshal the AtomContent
+         * value.
+         * 
+         * We have to be careful to use the real Providers list instead of the one from the injected
+         * local providers field in this class.  The injected object may be a "proxy" to the real
+         * providers, and could cause an infinite loop when ModelUtils.readValue calls back through
+         * the providers.
+         */
+        AtomContent content = entry.getContent();
+        if (content != null) {
+            content.setProviders(RuntimeContextTLS.getRuntimeContext().getAttribute(Providers.class));
+        }
+        
         return entry.toSynd(new SyndEntry());
     }
 

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/xml/AbstractJAXBProvider.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/xml/AbstractJAXBProvider.java?rev=891982&r1=891981&r2=891982&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/xml/AbstractJAXBProvider.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/providers/entity/xml/AbstractJAXBProvider.java Thu Dec 17 23:20:44 2009
@@ -63,7 +63,7 @@
                                                                                                 new SoftConcurrentMap<Class<?>, JAXBContext>();
 
     @Context
-    private Providers                                             providers;
+    protected Providers                                           providers;
 
     private static final SoftConcurrentMap<Class<?>, Boolean>     jaxbIsXMLRootElementCache =
                                                                                                 new SoftConcurrentMap<Class<?>, Boolean>();

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/model/atom/AtomContent.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/model/atom/AtomContent.java?rev=891982&r1=891981&r2=891982&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/model/atom/AtomContent.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/model/atom/AtomContent.java Thu Dec 17 23:20:44 2009
@@ -199,6 +199,9 @@
     @XmlTransient
     private Object       savedValue = null;
 
+    @XmlTransient
+    private Providers providers;
+    
     public AtomContent() {
     }
 
@@ -224,6 +227,18 @@
         value.setValue(getValue(Object.class));
         return value;
     }
+    
+    /**
+     * Sets the Providers on a local field so that the registry of custom and system
+     * providers is available when a client application retrieves the value, expecting
+     * it to be seamlessly unmarshalled or converted to the expected type declared in
+     * getValue(Class).
+     * 
+     * Client applications should NOT call this method.
+     */
+    public void setProviders(Providers _providers) {
+        providers = _providers;
+    }
 
     /**
      * Gets the value of type.
@@ -339,7 +354,7 @@
         try {
             return getValue(cls,
                             cls,
-                            null,
+                            providers,
                             ModelUtils.EMPTY_ARRAY,
                             ModelUtils.EMPTY_STRING_MAP,
                             ModelUtils.determineMediaType(type));

Modified: incubator/wink/trunk/wink-common/src/main/resources/META-INF/core/wink-providers
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/resources/META-INF/core/wink-providers?rev=891982&r1=891981&r2=891982&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/resources/META-INF/core/wink-providers (original)
+++ incubator/wink/trunk/wink-common/src/main/resources/META-INF/core/wink-providers Thu Dec 17 23:20:44 2009
@@ -38,12 +38,12 @@
 org.apache.wink.common.internal.providers.entity.SourceProvider$DOMSourceProvider
 org.apache.wink.common.internal.providers.entity.StreamingOutputProvider
 
-
 # JAXB Providers
 org.apache.wink.common.internal.providers.entity.xml.JAXBElementXmlProvider
 org.apache.wink.common.internal.providers.entity.xml.JAXBXmlProvider
 
 # Atom 
+org.apache.wink.common.internal.providers.entity.atom.AtomEntryProvider
 org.apache.wink.common.internal.providers.entity.atom.AtomFeedSyndFeedProvider
 org.apache.wink.common.internal.providers.entity.atom.AtomEntrySyndEntryProvider