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 2010/04/22 17:53:19 UTC

svn commit: r936913 - in /incubator/wink/trunk: wink-common/ wink-common/src/main/java/org/apache/wink/common/internal/contexts/ wink-common/src/main/java/org/apache/wink/common/internal/utils/ wink-common/src/test/java/org/apache/wink/common/internal/...

Author: rott
Date: Thu Apr 22 15:53:19 2010
New Revision: 936913

URL: http://svn.apache.org/viewvc?rev=936913&view=rev
Log:
WINK-262: followup for perf improvements, make default response Content-Type header charset attribute concatenation behavior configurable

Added:
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/contexts/MediaTypeCharsetAdjuster.java
    incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjuster.java
    incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjusterTest.java
Modified:
    incubator/wink/trunk/wink-common/pom.xml
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/utils/MediaTypeUtils.java
    incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/jaxb/ProvidersJAXBTest.java
    incubator/wink/trunk/wink-server/pom.xml
    incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/DeploymentConfiguration.java
    incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/FlushResultHandler.java
    incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/ServerMessageContext.java
    incubator/wink/trunk/wink-server/src/main/resources/META-INF/wink-default.properties

Modified: incubator/wink/trunk/wink-common/pom.xml
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/pom.xml?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/pom.xml (original)
+++ incubator/wink/trunk/wink-common/pom.xml Thu Apr 22 15:53:19 2010
@@ -69,6 +69,12 @@
             <groupId>javax.ws.rs</groupId>
             <artifactId>jsr311-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.jmock</groupId>
+            <artifactId>jmock-junit3</artifactId>
+            <version>2.5.1</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

Added: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/contexts/MediaTypeCharsetAdjuster.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/contexts/MediaTypeCharsetAdjuster.java?rev=936913&view=auto
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/contexts/MediaTypeCharsetAdjuster.java (added)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/contexts/MediaTypeCharsetAdjuster.java Thu Apr 22 15:53:19 2010
@@ -0,0 +1,29 @@
+/*
+ * 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.contexts;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+public interface MediaTypeCharsetAdjuster {
+
+    public MediaType setDefaultCharsetOnMediaTypeHeader(MultivaluedMap<String, Object> httpHeaders,
+                                                        MediaType mediaType);
+}

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/utils/MediaTypeUtils.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/utils/MediaTypeUtils.java?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/utils/MediaTypeUtils.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/utils/MediaTypeUtils.java Thu Apr 22 15:53:19 2010
@@ -24,14 +24,13 @@ import java.util.Comparator;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 
 import org.apache.wink.common.RuntimeContext;
+import org.apache.wink.common.internal.contexts.MediaTypeCharsetAdjuster;
 import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
 import org.apache.wink.common.internal.uri.UriEncoder;
-import org.apache.wink.common.utils.ProviderUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -256,33 +255,14 @@ public class MediaTypeUtils {
      */
     public static MediaType setDefaultCharsetOnMediaTypeHeader(MultivaluedMap<String, Object> httpHeaders,
                                                                MediaType mediaType) {
-        logger.debug("setDefaultCharsetOnMediaTypeHeader({}, {}) entry", httpHeaders, mediaType); //$NON-NLS-1$
-
-        if (httpHeaders != null && httpHeaders.get(HttpHeaders.CONTENT_TYPE) == null) {
-            // only correct the MediaType if the MediaType was not explicitly
-            // set
-            logger
-                .debug("Media Type not explicitly set on Response so going to correct charset parameter if necessary"); //$NON-NLS-1$
-            if (ProviderUtils.getCharsetOrNull(mediaType) == null) { //$NON-NLS-1$
-                try {
-                    RuntimeContext context = RuntimeContextTLS.getRuntimeContext();
-                    HttpHeaders requestHeaders = null;
-                    if (context != null) {
-                        requestHeaders = context.getHttpHeaders();
-                    }
-                    String charsetValue = ProviderUtils.getCharset(mediaType, requestHeaders);
-                    String newMediaTypeStr = mediaType.toString() + ";charset=" + charsetValue;
-                    mediaType = MediaType.valueOf(newMediaTypeStr);
-                    httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE, newMediaTypeStr); //$NON-NLS-1$
-                    logger
-                        .debug("Changed media type to be {} in Content-Type HttpHeader", newMediaTypeStr); //$NON-NLS-1$
-                } catch (Exception e) {
-                    logger.debug("Caught exception while trying to set the charset", e); //$NON-NLS-1$
-                }
+        RuntimeContext context = RuntimeContextTLS.getRuntimeContext();
+        MediaTypeCharsetAdjuster adjuster = null;
+        if (context != null) {
+            adjuster = context.getAttribute(MediaTypeCharsetAdjuster.class);
+            if (adjuster != null) {
+                return adjuster.setDefaultCharsetOnMediaTypeHeader(httpHeaders, mediaType);
             }
         }
-
-        logger.debug("setDefaultCharsetOnMediaTypeHeader() exit returning {}", mediaType); //$NON-NLS-1$
         return mediaType;
     }
 

Modified: incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/jaxb/ProvidersJAXBTest.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/jaxb/ProvidersJAXBTest.java?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/jaxb/ProvidersJAXBTest.java (original)
+++ incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/jaxb/ProvidersJAXBTest.java Thu Apr 22 15:53:19 2010
@@ -19,24 +19,39 @@
  *******************************************************************************/
 package org.apache.wink.common.internal.providers.jaxb;
 
-import static org.junit.Assert.assertTrue;
-
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
+import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.ContextResolver;
 import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Providers;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 
+import org.apache.wink.common.internal.CaseInsensitiveMultivaluedMap;
 import org.apache.wink.common.internal.providers.entity.xml.JAXBXmlProvider;
 import org.apache.wink.common.internal.providers.jaxb.jaxb1.AddNumbers;
+import org.apache.wink.common.model.JAXBUnmarshalOptions;
+import org.apache.wink.common.model.XmlFormattingOptions;
+import org.jmock.Expectations;
+import org.jmock.integration.junit3.MockObjectTestCase;
 import org.junit.Before;
 import org.junit.Test;
 
-public class ProvidersJAXBTest {
+public class ProvidersJAXBTest extends MockObjectTestCase {
     
     public class MyJAXBXmlProvider extends JAXBXmlProvider {
 
+        MyJAXBXmlProvider() {
+            super();
+            providers = ProvidersJAXBTest.this.providers;
+        }
+        
         /* 
          * simulate what would happen if application had supplied a JAXBContext provider
          */
@@ -55,19 +70,66 @@ public class ProvidersJAXBTest {
         "<arg1>2</arg1>" +
         "</ns2:addNumbers>";
     
-    private MessageBodyReader jaxbProvider = null;
+    static final String expectedXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + 
+        "<addNumbers xmlns=\"http://org/apache/wink/common/internal/providers/jaxb/jaxb1\">" +
+        "<arg0>0</arg0>" +
+        "<arg1>0</arg1>" +
+        "</addNumbers>";
+
+    private MessageBodyReader jaxbProviderReader = null;
+    private MessageBodyWriter jaxbProviderWriter = null;
+    private Providers providers;
     
     @Before
     public void setUp() {
-        jaxbProvider = new MyJAXBXmlProvider();
+        providers = mock(Providers.class);
+        checking(new Expectations() {{
+            allowing(providers).getContextResolver(JAXBContext.class, MediaType.TEXT_XML_TYPE); will(returnValue(new MyJAXBContextResolver()));
+            allowing(providers).getContextResolver(XmlFormattingOptions.class, MediaType.TEXT_XML_TYPE); will(returnValue(null));
+            allowing(providers).getContextResolver(JAXBUnmarshalOptions.class, MediaType.TEXT_XML_TYPE); will(returnValue(null));
+        }});
+        jaxbProviderReader = new MyJAXBXmlProvider();
+        jaxbProviderWriter = new MyJAXBXmlProvider();
     }
     
     @Test
     public void testJAXBUnmarshallingWithAlternateContext1() throws Exception {
-        assertTrue(jaxbProvider.isReadable(AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE));
+        assertTrue(jaxbProviderReader.isReadable(AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE));
         ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());
-        Object obj = jaxbProvider.readFrom(AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE, null, bais);
+        Object obj = jaxbProviderReader.readFrom(AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE, null, bais);
         assertTrue(obj instanceof AddNumbers);
     }
     
+    public class MyJAXBContextResolver implements ContextResolver<JAXBContext> {
+
+        public JAXBContext getContext(Class<?> arg0) {
+            try {
+                return JAXBContext.newInstance(arg0);
+            } catch (JAXBException e) {
+                e.printStackTrace();
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testJAXBMarshalling() throws WebApplicationException, IOException {
+        assertTrue(jaxbProviderWriter.isWriteable(AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE));
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        jaxbProviderWriter.writeTo(new AddNumbers(), AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE, null, baos);
+        assertEquals(expectedXml, baos.toString());
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testJAXBMarshallingWithMap() throws WebApplicationException, IOException {
+        assertTrue(jaxbProviderWriter.isWriteable(AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE));
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        MultivaluedMap map = new CaseInsensitiveMultivaluedMap<Object>();
+        jaxbProviderWriter.writeTo(new AddNumbers(), AddNumbers.class, null, null, MediaType.TEXT_XML_TYPE, map, baos);
+        assertEquals(expectedXml, baos.toString());
+    }
+    
+    
 }

Modified: incubator/wink/trunk/wink-server/pom.xml
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/pom.xml?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-server/pom.xml (original)
+++ incubator/wink/trunk/wink-server/pom.xml Thu Apr 22 15:53:19 2010
@@ -100,6 +100,12 @@
             <version>3.2.1</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jmock</groupId>
+            <artifactId>jmock-junit3</artifactId>
+            <version>2.5.1</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

Modified: incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/DeploymentConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/DeploymentConfiguration.java?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/DeploymentConfiguration.java (original)
+++ incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/DeploymentConfiguration.java Thu Apr 22 15:53:19 2010
@@ -92,6 +92,10 @@ public class DeploymentConfiguration {
                                                                               "wink.mediaTypeMapperFactoryClass"; //$NON-NLS-1$
     private static final String       VALIDATE_LOCATION_HEADER            =
                                                                               "wink.validateLocationHeader"; //$NON-NLS-1$
+    private static final String       DEFAULT_RESPONSE_CHARSET            =
+                                                                              "wink.response.defaultCharset"; // $NON-NLS-1$
+    private static final String       USE_ACCEPT_CHARSET                  =
+                                                                              "wink.response.useAcceptCharset"; // $NON-NLS-1$
     // handler chains
     private RequestHandlersChain      requestHandlersChain;
     private ResponseHandlersChain     responseHandlersChain;
@@ -557,5 +561,34 @@ public class DeploymentConfiguration {
     public String[] getHttpMethodOverrideHeaders() {
         return httpMethodOverrideHeaders;
     }
+    
+    /**
+     * isDefaultResponseCharset will write charset=UTF-8 to the response Content-Type header if a response
+     * charset is not already explicitly defined.
+     * @return boolean
+     */
+    public boolean isDefaultResponseCharset() {
+        String val = properties.getProperty(DEFAULT_RESPONSE_CHARSET);
+        return Boolean.valueOf(val).booleanValue();
+    }
+    
+    public void setDefaultResponseCharset(boolean val) {
+        properties.setProperty(DEFAULT_RESPONSE_CHARSET, Boolean.toString(val));
+    }
+    
+    /**
+     * isUseAcceptCharset will use the Accept-Charset header, if present, to write a charset to the response Content-Type
+     * header if a response charset is not already explicitly defined.  This setting will override the isDefaultResponseCharset
+     * setting when the Accept-Charset header is present.
+     * @return
+     */
+    public boolean isUseAcceptCharset() {
+        String val = properties.getProperty(USE_ACCEPT_CHARSET);
+        return Boolean.valueOf(val).booleanValue();
+    }
+    
+    public void setUseAcceptCharset(boolean val) {
+        properties.setProperty(USE_ACCEPT_CHARSET, Boolean.toString(val));
+    }
 
 }

Added: incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjuster.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjuster.java?rev=936913&view=auto
==============================================================================
--- incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjuster.java (added)
+++ incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjuster.java Thu Apr 22 15:53:19 2010
@@ -0,0 +1,96 @@
+/*
+ * 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.server.internal.contexts;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.wink.common.RuntimeContext;
+import org.apache.wink.common.internal.contexts.MediaTypeCharsetAdjuster;
+import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
+import org.apache.wink.common.utils.ProviderUtils;
+import org.apache.wink.server.internal.DeploymentConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ServerMediaTypeCharsetAdjuster implements MediaTypeCharsetAdjuster {
+
+    final private static ServerMediaTypeCharsetAdjuster instance =
+                                                                     new ServerMediaTypeCharsetAdjuster();
+    // enforce singleton
+    private ServerMediaTypeCharsetAdjuster() {
+    }
+    
+    public static ServerMediaTypeCharsetAdjuster getInstance() {
+        return instance;
+    }
+
+    private static final Logger logger =
+                                           LoggerFactory
+                                               .getLogger(ServerMediaTypeCharsetAdjuster.class);
+
+    public MediaType setDefaultCharsetOnMediaTypeHeader(MultivaluedMap<String, Object> httpHeaders,
+                                                        MediaType mediaType) {
+        logger.debug("setDefaultCharsetOnMediaTypeHeader({}, {}) entry", httpHeaders, mediaType); //$NON-NLS-1$
+
+        RuntimeContext context = RuntimeContextTLS.getRuntimeContext();
+        DeploymentConfiguration config = context.getAttribute(DeploymentConfiguration.class);
+        if (config.isDefaultResponseCharset() || config.isUseAcceptCharset()) {
+            if (httpHeaders != null && (httpHeaders.isEmpty() || httpHeaders
+                    .get(HttpHeaders.CONTENT_TYPE) == null)) {
+                // only correct the MediaType if the MediaType was not explicitly
+                // set
+                logger.debug("Media Type not explicitly set on Response so going to correct charset parameter if necessary"); //$NON-NLS-1$
+                if (ProviderUtils.getCharsetOrNull(mediaType) == null) { //$NON-NLS-1$
+                    try {
+                        String charsetValue = "UTF-8";
+                        if (config.isUseAcceptCharset()) {
+                            // configuration says to inspect and use the Accept-Charset header to determine response charset
+                            HttpHeaders requestHeaders = null;
+                            if (context != null) {
+                                requestHeaders = context.getHttpHeaders();
+                            }
+                            charsetValue = ProviderUtils.getCharset(mediaType, requestHeaders);
+                        }
+                        String newMediaTypeStr = mediaType.toString() + ";charset=" + charsetValue;
+                        mediaType = MediaType.valueOf(newMediaTypeStr);
+                        if (context != null) {
+                            HttpServletResponse response =
+                                context.getAttribute(HttpServletResponse.class);
+                            response.setContentType(newMediaTypeStr);
+                        }
+                        logger.debug("Changed media type to be {} in Content-Type HttpHeader", newMediaTypeStr); //$NON-NLS-1$
+                    } catch (Exception e) {
+                        logger.debug("Caught exception while trying to set the charset", e); //$NON-NLS-1$
+                    }
+
+                }
+            }
+        } else {
+            logger.debug("No default charset was applied to the response Content-Type header due to deployment configuration directive.");  // $NON-NLS-1$
+        }
+
+        logger.debug("setDefaultCharsetOnMediaTypeHeader() exit returning {}", mediaType); //$NON-NLS-1$
+        return mediaType;
+    }
+
+}

Modified: incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/FlushResultHandler.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/FlushResultHandler.java?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/FlushResultHandler.java (original)
+++ incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/FlushResultHandler.java Thu Apr 22 15:53:19 2010
@@ -133,12 +133,9 @@ public class FlushResultHandler extends 
             return;
         }
 
-        // we have an entity, set the response media type
+        // we have an entity, get the response media type
+        // the actual writing will take places in the flush
         MediaType responseMediaType = context.getResponseMediaType();
-        if (responseMediaType != null) {
-            logger.debug("Set response Content-Type to: {} ", responseMediaType); //$NON-NLS-1$
-            httpResponse.setContentType(responseMediaType.toString());
-        }
 
         // get the provider to write the entity
         Providers providers = context.getProviders();
@@ -172,7 +169,7 @@ public class FlushResultHandler extends 
             }
 
             FlushHeadersOutputStream outputStream =
-                new FlushHeadersOutputStream(httpResponse, headers);
+                new FlushHeadersOutputStream(httpResponse, headers, responseMediaType);
             if (logger.isDebugEnabled()) {
                 logger
                     .debug("{}@{}.writeTo({}, {}, {}, {}, {}, {}, {}) being called", new Object[] { //$NON-NLS-1$
@@ -218,7 +215,7 @@ public class FlushResultHandler extends 
             .getName());
 
         FlushHeadersOutputStream outputStream =
-            new FlushHeadersOutputStream(httpResponse, httpHeaders);
+            new FlushHeadersOutputStream(httpResponse, httpHeaders, responseMediaType);
         if (logger.isDebugEnabled()) {
             logger.debug("{}.writeTo({}, {}, {}) being called", new Object[] { //$NON-NLS-1$
                          Integer.toHexString(System.identityHashCode(dataContentHandler)), entity,
@@ -267,17 +264,20 @@ public class FlushResultHandler extends 
         // headers that a provider MAY have added to the response, before it
         // actually started writing to stream.
 
-        private boolean                        writeStarted;
-        private HttpServletResponse            httpResponse;
-        private ServletOutputStream            outputStream;
-        private MultivaluedMap<String, Object> headers;
+        private boolean                              writeStarted;
+        final private HttpServletResponse            httpResponse;
+        final private ServletOutputStream            outputStream;
+        final private MultivaluedMap<String, Object> headers;
+        final private MediaType                      responseMediaType;
 
         public FlushHeadersOutputStream(HttpServletResponse httpResponse,
-                                        MultivaluedMap<String, Object> headers) throws IOException {
+                                        MultivaluedMap<String, Object> headers,
+                                        MediaType responseMediaType) throws IOException {
             this.writeStarted = false;
             this.httpResponse = httpResponse;
             this.outputStream = httpResponse.getOutputStream();
             this.headers = headers;
+            this.responseMediaType = responseMediaType;
         }
 
         @Override
@@ -312,6 +312,11 @@ public class FlushResultHandler extends 
 
         private void flushHeaders() {
             if (!writeStarted) {
+                if (httpResponse.getContentType() == null) {
+                    logger.debug("Set response Content-Type to: {} ", responseMediaType); //$NON-NLS-1$
+                    httpResponse.setContentType(responseMediaType.toString());
+                }
+
                 FlushResultHandler.flushHeaders(httpResponse, headers);
                 writeStarted = true;
             }

Modified: incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/ServerMessageContext.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/ServerMessageContext.java?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/ServerMessageContext.java (original)
+++ incubator/wink/trunk/wink-server/src/main/java/org/apache/wink/server/internal/handlers/ServerMessageContext.java Thu Apr 22 15:53:19 2010
@@ -40,6 +40,7 @@ import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Providers;
 
+import org.apache.wink.common.internal.contexts.MediaTypeCharsetAdjuster;
 import org.apache.wink.common.internal.contexts.ProvidersImpl;
 import org.apache.wink.common.internal.registry.ProvidersRegistry;
 import org.apache.wink.common.internal.runtime.AbstractRuntimeContext;
@@ -49,6 +50,7 @@ import org.apache.wink.server.internal.M
 import org.apache.wink.server.internal.contexts.HttpHeadersImpl;
 import org.apache.wink.server.internal.contexts.RequestImpl;
 import org.apache.wink.server.internal.contexts.SecurityContextImpl;
+import org.apache.wink.server.internal.contexts.ServerMediaTypeCharsetAdjuster;
 import org.apache.wink.server.internal.contexts.UriInfoImpl;
 import org.apache.wink.server.internal.registry.ResourceRegistry;
 import org.apache.wink.server.internal.utils.LinkBuildersImpl;
@@ -85,6 +87,7 @@ public class ServerMessageContext extend
         setAttribute(DeploymentConfiguration.class, configuration);
         setAttribute(ResourceRegistry.class, configuration.getResourceRegistry());
         setAttribute(ProvidersRegistry.class, configuration.getProvidersRegistry());
+        setAttribute(MediaTypeCharsetAdjuster.class, ServerMediaTypeCharsetAdjuster.getInstance());
 
         initContexts();
         List<Application> apps = configuration.getApplications();
@@ -224,8 +227,12 @@ public class ServerMessageContext extend
         @Override
         public void setContentType(String type) {
             userContentType = type;
-            MediaType realResponseMimeType = getRealResponseMimeType(type);
-            super.setContentType(realResponseMimeType.toString());
+            if (mediaTypeMapper == null) {
+                super.setContentType(type);
+            } else {
+                MediaType realResponseMimeType = getRealResponseMimeType(type);
+                super.setContentType(realResponseMimeType.toString());
+            }
         }
 
         @Override

Modified: incubator/wink/trunk/wink-server/src/main/resources/META-INF/wink-default.properties
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/main/resources/META-INF/wink-default.properties?rev=936913&r1=936912&r2=936913&view=diff
==============================================================================
--- incubator/wink/trunk/wink-server/src/main/resources/META-INF/wink-default.properties (original)
+++ incubator/wink/trunk/wink-server/src/main/resources/META-INF/wink-default.properties Thu Apr 22 15:53:19 2010
@@ -43,4 +43,15 @@ wink.rootResource=atom+html
 wink.serviceDocumentCssPath=
 
 # Indicates if wink should load all META-INF/wink-application files found in classpath
-wink.loadApplications=true
\ No newline at end of file
+wink.loadApplications=true
+
+# Response Content-Type header should be UTF-8 if none is already specified
+# true: default to UTF-8 (default)
+# false: do not assign a default charset to the Content-Type response header
+wink.response.defaultCharset=true
+
+# Response Content-Type header should default to the highest q-valued Accept-Charset value if Accept-Charset header exists
+# This option overrides wink.response.defaultCharset when the Accept-Charset header is present
+# true: use highest q-valued Accept-Charset value as the charset in the response Content-Type header
+# false: do not use Accept-Charset values as a default on the response Content-Type header
+wink.response.useAcceptCharset=false

Added: incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjusterTest.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjusterTest.java?rev=936913&view=auto
==============================================================================
--- incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjusterTest.java (added)
+++ incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/contexts/ServerMediaTypeCharsetAdjusterTest.java Thu Apr 22 15:53:19 2010
@@ -0,0 +1,276 @@
+/*******************************************************************************
+ * 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.server.internal.contexts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.wink.common.RuntimeContext;
+import org.apache.wink.common.internal.MultivaluedMapImpl;
+import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
+import org.apache.wink.server.internal.DeploymentConfiguration;
+import org.jmock.Expectations;
+import org.jmock.integration.junit3.MockObjectTestCase;
+import org.junit.Test;
+
+public class ServerMediaTypeCharsetAdjusterTest extends MockObjectTestCase {
+
+    RuntimeContext context = null;
+    HttpServletResponse servletResponse = null;
+    DeploymentConfiguration myConfig = new DeploymentConfiguration();
+
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        myConfig.init();
+        // default is true in the real config  
+        myConfig.setDefaultResponseCharset(true);
+        // default is false in the real config  
+        myConfig.setUseAcceptCharset(false);
+        
+        // common expectations
+        context = mock(RuntimeContext.class);
+        checking(new Expectations() {{
+            allowing(context).getAttribute(DeploymentConfiguration.class); will(returnValue(myConfig));
+        }});
+        
+        RuntimeContextTLS.setRuntimeContext(context);
+    }
+
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithDefaultConfig() {
+        
+        final String expected = "application/xml";
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        // null httpHeaders param
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(null, MediaType.APPLICATION_XML_TYPE);
+        assertEquals(expected, mediaType.toString());
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueNullParam() {
+        
+        final String expected = "application/xml";
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        myConfig.setDefaultResponseCharset(true);
+        
+        // null httpHeaders param
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(null, MediaType.APPLICATION_XML_TYPE);
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueAndNullHeadersAndFalseConfig() {
+
+        final String expected = "application/xml";
+        
+        servletResponse = mock(HttpServletResponse.class);
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        // switch off the default
+        myConfig.setDefaultResponseCharset(false);
+        
+        // empty map
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(new MultivaluedMapImpl<String, Object>(), MediaType.APPLICATION_XML_TYPE);
+        // no charset attribute due to setDefaultResponseCharset(false)
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueAndNullHeaders() {
+
+        final String expected = "application/xml;charset=UTF-8";
+        
+        servletResponse = mock(HttpServletResponse.class);
+        checking(new Expectations() {{
+            oneOf(servletResponse).setContentType(expected);
+            oneOf(context).getAttribute(HttpServletResponse.class); will(returnValue(servletResponse));
+        }});
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        // empty map
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(new MultivaluedMapImpl<String, Object>(), MediaType.APPLICATION_XML_TYPE);
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueAndNullAcceptCharsetHeader() {
+
+        final String expected = "application/xml;charset=UTF-8";
+        
+        final HttpHeaders httpHeaders = mock(HttpHeaders.class);
+        servletResponse = mock(HttpServletResponse.class);
+        checking(new Expectations() {{
+            oneOf(servletResponse).setContentType(expected);
+            oneOf(context).getAttribute(HttpServletResponse.class); will(returnValue(servletResponse));
+            oneOf(context).getHttpHeaders(); will(returnValue(httpHeaders));
+            oneOf(httpHeaders).getRequestHeader(HttpHeaders.ACCEPT_CHARSET); will(returnValue(null));
+        }});
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        // exercise code path that reads the Accept-Charset header
+        myConfig.setUseAcceptCharset(true);
+        
+        // empty map
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(new MultivaluedMapImpl<String, Object>(), MediaType.APPLICATION_XML_TYPE);
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueAndAcceptCharsetHeader() {
+        
+        final String expected = "application/xml;charset=ISO-8859-1";
+        
+        final List<String> acceptHeaders = new ArrayList<String>();
+        acceptHeaders.add("UTF-16");
+        
+        final HttpHeaders httpHeaders = mock(HttpHeaders.class);
+        servletResponse = mock(HttpServletResponse.class);
+        checking(new Expectations() {{
+            oneOf(servletResponse).setContentType(expected);
+            oneOf(context).getAttribute(HttpServletResponse.class); will(returnValue(servletResponse));
+            oneOf(context).getHttpHeaders(); will(returnValue(httpHeaders));
+            oneOf(httpHeaders).getRequestHeader(HttpHeaders.ACCEPT_CHARSET); will(returnValue(acceptHeaders));
+        }});
+        
+        // non-empty map, just to make sure production code path is as expected
+        MultivaluedMap<String, Object> map = new MultivaluedMapImpl<String, Object>();
+        map.add("nonsense", null);
+
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        // exercise code path that reads the Accept-Charset header
+        myConfig.setUseAcceptCharset(true);
+        
+        // empty map
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(map, MediaType.APPLICATION_XML_TYPE);
+        // still defaults back to ISO-8859-1 because it is silently added as top q-valued charset on the client-originated Accept-Header.  See HTTP spec.
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueAndAcceptCharsetButFalse() {
+        
+        final String expected = "application/xml;charset=UTF-8";
+        
+        final List<String> acceptHeaders = new ArrayList<String>();
+        acceptHeaders.add("UTF-16;q=1.0");
+        acceptHeaders.add("ISO-8859-1;q=0.5");  // re-prioritize silently added charset to lower q-value than UTF-16
+        
+        final HttpHeaders httpHeaders = mock(HttpHeaders.class);
+        servletResponse = mock(HttpServletResponse.class);
+        checking(new Expectations() {{
+            oneOf(servletResponse).setContentType(expected);
+            oneOf(context).getAttribute(HttpServletResponse.class); will(returnValue(servletResponse));
+        }});
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+        
+        // leave useAcceptHeader to the default of false
+        
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(new MultivaluedMapImpl<String, Object>(), MediaType.APPLICATION_XML_TYPE);
+        // UTF-16 has highest q-value, but Accept-Charset is being ignored due to config, so...
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigTrueAndAcceptCharset() {
+        
+        final String expected = "application/xml;charset=UTF-16";
+        
+        final List<String> acceptHeaders = new ArrayList<String>();
+        acceptHeaders.add("UTF-16;q=1.0");
+        acceptHeaders.add("ISO-8859-1;q=0.5");  // re-prioritize silently added charset to lower q-value than UTF-16
+        
+        final HttpHeaders httpHeaders = mock(HttpHeaders.class);
+        servletResponse = mock(HttpServletResponse.class);
+        checking(new Expectations() {{
+            oneOf(servletResponse).setContentType(expected);
+            oneOf(context).getAttribute(HttpServletResponse.class); will(returnValue(servletResponse));
+            oneOf(context).getHttpHeaders(); will(returnValue(httpHeaders));
+            oneOf(httpHeaders).getRequestHeader(HttpHeaders.ACCEPT_CHARSET); will(returnValue(acceptHeaders));
+        }});
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+
+        // exercise code path that reads the Accept-Charset header
+        myConfig.setUseAcceptCharset(true);
+        
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(new MultivaluedMapImpl<String, Object>(), MediaType.APPLICATION_XML_TYPE);
+        // UTF-16 has highest q-value
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+    @Test
+    public void testSetDefaultCharsetOnMediaTypeHeaderWithConfigFalseAndAcceptCharset() {
+        
+        final String expected = "application/xml;charset=UTF-16";
+        
+        final List<String> acceptHeaders = new ArrayList<String>();
+        acceptHeaders.add("UTF-16;q=1.0");
+        acceptHeaders.add("ISO-8859-1;q=0.5");  // re-prioritize silently added charset to lower q-value than UTF-16
+        
+        final HttpHeaders httpHeaders = mock(HttpHeaders.class);
+        servletResponse = mock(HttpServletResponse.class);
+        checking(new Expectations() {{
+            oneOf(servletResponse).setContentType(expected);
+            oneOf(context).getAttribute(HttpServletResponse.class); will(returnValue(servletResponse));
+            oneOf(context).getHttpHeaders(); will(returnValue(httpHeaders));
+            oneOf(httpHeaders).getRequestHeader(HttpHeaders.ACCEPT_CHARSET); will(returnValue(acceptHeaders));
+        }});
+        
+        ServerMediaTypeCharsetAdjuster serverMediaTypeCharsetAdjuster = ServerMediaTypeCharsetAdjuster.getInstance();
+
+        // make sure when user expects to be using Accept-Header, they really do use it
+        
+        // default is true in the real config.  Resetting to false here to confirm code path is correct
+        myConfig.setUseAcceptCharset(false);
+        // exercise code path that reads the Accept-Charset header
+        myConfig.setUseAcceptCharset(true);
+        
+        MediaType mediaType = serverMediaTypeCharsetAdjuster.setDefaultCharsetOnMediaTypeHeader(new MultivaluedMapImpl<String, Object>(), MediaType.APPLICATION_XML_TYPE);
+        // UTF-16 has highest q-value
+        assertEquals(expected, mediaType.toString());
+        
+    }
+    
+
+}