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 2009/03/12 19:53:28 UTC

svn commit: r752970 - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/ rt/frontend/jaxrs/src/main/java/org...

Author: sergeyb
Date: Thu Mar 12 18:53:27 2009
New Revision: 752970

URL: http://svn.apache.org/viewvc?rev=752970&view=rev
Log:
JAXRS : encoding related fixes

Modified:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/XMLSource.java
    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/UriBuilderImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/client/WebClientTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/HttpUtilsTest.java
    cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java Thu Mar 12 18:53:27 2009
@@ -81,13 +81,15 @@
     protected List<Interceptor> outInterceptors = new ModCountCopyOnWriteArrayList<Interceptor>();
     protected ConduitSelector conduitSelector;
     protected Bus bus;
-    
+
     private MultivaluedMap<String, String> requestHeaders = new MetadataMap<String, String>();
     private ResponseBuilder responseBuilder;
     
     private URI baseURI;
     private UriBuilder currentBuilder;
 
+    
+    
     protected AbstractClient(URI baseURI, URI currentURI) {
         this.baseURI = baseURI;
         this.currentBuilder = new UriBuilderImpl(currentURI);
@@ -235,7 +237,7 @@
      * {@inheritDoc}
      */
     public URI getCurrentURI() {
-        return getCurrentBuilder().clone().build();
+        return getCurrentBuilder().clone().buildFromEncoded();
     }
 
     /**

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java Thu Mar 12 18:53:27 2009
@@ -53,7 +53,6 @@
 import org.apache.cxf.jaxrs.model.OperationResourceInfo;
 import org.apache.cxf.jaxrs.provider.ProviderFactory;
 import org.apache.cxf.jaxrs.utils.AnnotationUtils;
-import org.apache.cxf.jaxrs.utils.HttpUtils;
 import org.apache.cxf.jaxrs.utils.InjectionUtils;
 import org.apache.cxf.jaxrs.utils.ParameterType;
 import org.apache.cxf.message.Message;
@@ -248,7 +247,7 @@
         List<Parameter> indexList =  getParameters(map, key);
         List<Object> list = new ArrayList<Object>(indexList.size());
         for (Parameter p : indexList) {
-            list.add(encode(p, params[p.getIndex()].toString()));
+            list.add(params[p.getIndex()].toString());
         }
         return list;
     }
@@ -265,7 +264,7 @@
         List<Parameter> qs = getParameters(map, ParameterType.QUERY);
         for (Parameter p : qs) {
             if (params[p.getIndex()] != null) {
-                ub.queryParam(p.getValue(), encode(p, params[p.getIndex()].toString()));
+                ub.queryParam(p.getValue(), params[p.getIndex()].toString());
             }
         }
     }
@@ -275,7 +274,7 @@
         List<Parameter> mx = getParameters(map, ParameterType.MATRIX);
         for (Parameter p : mx) {
             if (params[p.getIndex()] != null) {
-                ub.matrixParam(p.getValue(), encode(p, params[p.getIndex()].toString()));
+                ub.matrixParam(p.getValue(), params[p.getIndex()].toString());
             }
         }
     }
@@ -322,55 +321,43 @@
             reportInvalidResourceMethod(ori.getMethodToInvoke(), "NO_CONTEXT_PARAMETERS");
         }
         
-        boolean isEncoded = AnnotationUtils.isEncoded(anns, ori);
         PathParam a = AnnotationUtils.getAnnotation(anns, PathParam.class); 
         if (a != null) {
-            return new Parameter(ParameterType.PATH, index, a.value(), isEncoded);
+            return new Parameter(ParameterType.PATH, index, a.value());
         } 
         
         QueryParam q = AnnotationUtils.getAnnotation(anns, QueryParam.class);
         if (q != null) {
-            return new Parameter(ParameterType.QUERY, index, q.value(), isEncoded);
+            return new Parameter(ParameterType.QUERY, index, q.value());
         }
         
         MatrixParam m = AnnotationUtils.getAnnotation(anns, MatrixParam.class);
         if (m != null) {
-            return new Parameter(ParameterType.MATRIX, index, m.value(), isEncoded);
+            return new Parameter(ParameterType.MATRIX, index, m.value());
         }  
     
         FormParam f = AnnotationUtils.getAnnotation(anns, FormParam.class);
         if (f != null) {
-            return new Parameter(ParameterType.FORM, index, f.value(), isEncoded);
+            return new Parameter(ParameterType.FORM, index, f.value());
         }  
         
         HeaderParam h = AnnotationUtils.getAnnotation(anns, HeaderParam.class);
         if (h != null) {
-            return new Parameter(ParameterType.HEADER, index, h.value(), isEncoded);
+            return new Parameter(ParameterType.HEADER, index, h.value());
         }  
         
         Parameter p = null;
         CookieParam c = AnnotationUtils.getAnnotation(anns, CookieParam.class);
         if (c != null) {
-            p = new Parameter(ParameterType.COOKIE, index, c.value(), isEncoded);
+            p = new Parameter(ParameterType.COOKIE, index, c.value());
         } else {
-            p = new Parameter(ParameterType.REQUEST_BODY, index, null, isEncoded); 
+            p = new Parameter(ParameterType.REQUEST_BODY, index, null); 
         }
         
         return p;
         
     }
     
-    private static String encode(Parameter p, String value) {
-        if (p.isEncoded()) {
-            return value;
-        }
-        if (p.getType() == ParameterType.PATH || p.getType() == ParameterType.MATRIX) {
-            return HttpUtils.pathEncode(value);
-        } else {
-            return HttpUtils.urlEncode(value); 
-        }
-    }
-    
     private Object doChainedInvocation(URI uri, MultivaluedMap<String, String> headers, 
                           OperationResourceInfo ori, Object[] params, int bodyIndex, 
                           MultivaluedMap<ParameterType, Parameter> types) throws Throwable {
@@ -425,13 +412,11 @@
         private ParameterType type;
         private int ind;
         private String aValue;
-        private boolean isEncoded;
         
-        public Parameter(ParameterType type, int ind, String aValue, boolean encoded) {
+        public Parameter(ParameterType type, int ind, String aValue) {
             this.type = type;
             this.ind = ind;
             this.aValue = aValue; 
-            this.isEncoded = encoded;
         }
         
         public int getIndex() {
@@ -445,10 +430,6 @@
         public ParameterType getType() {
             return type;
         }
-        
-        public boolean isEncoded() {
-            return isEncoded;
-        }
     }
 
     // TODO : what we really need to do is to refactor JAXRSOutInterceptor so that

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/WebClient.java Thu Mar 12 18:53:27 2009
@@ -259,6 +259,7 @@
      *         can be obtained too, see Client.getResponse()
      */
     public <T> T invoke(String httpMethod, Object body, Class<T> responseClass) {
+        
         Response r = doInvoke(httpMethod, body, responseClass);
         
         if (r.getStatus() >= 400) {
@@ -308,6 +309,7 @@
      */
     public WebClient query(String name, Object ...values) {
         getCurrentBuilder().queryParam(name, values);
+        
         return this;
     }
     
@@ -473,6 +475,7 @@
         try {
             ResponseBuilder rb = setResponseBuilder(conn).clone();
             Response currentResponse = rb.clone().build();
+            
             Object entity = readBody(currentResponse, conn, m, responseClass, responseClass,
                                      new Annotation[]{});
             rb.entity(entity);
@@ -484,7 +487,7 @@
     }
     
     protected HttpURLConnection getConnection(String methodName) {
-        return createHttpConnection(getCurrentBuilder().clone().build(), methodName);
+        return createHttpConnection(getCurrentBuilder().clone().buildFromEncoded(), methodName);
     }
     
     private class BodyWriter extends AbstractOutDatabindingInterceptor {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/XMLSource.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/XMLSource.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/XMLSource.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/client/XMLSource.java Thu Mar 12 18:53:27 2009
@@ -46,11 +46,16 @@
 public class XMLSource {
     
     private InputSource source; 
+    private RuntimeException exceptionToThrow;
     
     public XMLSource(InputStream is) {
         source = new InputSource(is);
     }
     
+    public void setException(RuntimeException ex) {
+        exceptionToThrow = ex; 
+    }
+    
     public <T> T getNode(String expression, Class<T> cls) {
         return getNode(expression, CastUtils.cast(Collections.emptyMap(), String.class, String.class), cls);
     }
@@ -61,6 +66,9 @@
         try {
             Node node = (Node)xpath.evaluate(expression, source, XPathConstants.NODE);
             if (node == null) {
+                if (exceptionToThrow != null) {
+                    throw exceptionToThrow;
+                }
                 return null;
             }
             DOMSource ds = new DOMSource(node);

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=752970&r1=752969&r2=752970&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 Thu Mar 12 18:53:27 2009
@@ -33,11 +33,17 @@
     private Map<K, List<V>> m;
     
     public MetadataMap() {
-        this(new LinkedHashMap<K, List<V>>());
+        this.m = new LinkedHashMap<K, List<V>>();
     }
     
     public MetadataMap(Map<K, List<V>> store) {
-        this.m = store;
+        
+        this.m = new LinkedHashMap<K, List<V>>();
+        if (store != null) {
+            for (Map.Entry<K, List<V>> entry : store.entrySet()) {
+                m.put(entry.getKey(), new ArrayList<V>(entry.getValue()));
+            }
+        }
     }
     
     public void add(K key, V value) {

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=752970&r1=752969&r2=752970&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 Thu Mar 12 18:53:27 2009
@@ -30,8 +30,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.ws.rs.Path;
 import javax.ws.rs.core.MultivaluedMap;
@@ -45,8 +43,6 @@
 
 public class UriBuilderImpl extends UriBuilder {
 
-    private static final Pattern DECODE_PATTERN = Pattern.compile("%[0-9a-fA-F][0-9a-fA-F]");
-
     private String scheme;
     private String userInfo;
     private int port;
@@ -79,14 +75,45 @@
 
     private URI doBuild(boolean fromEncoded, Object... values) {
         try {
-            String path = buildPath(fromEncoded);
-            path = substituteVarargs(path, values);
-            return new URI(scheme, userInfo, host, port, path, buildQuery(), fragment);
+            String thePath = buildPath(fromEncoded);
+            thePath = substituteVarargs(thePath, values);
+            return buildURI(fromEncoded, thePath);
         } catch (URISyntaxException ex) {
             throw new UriBuilderException("URI can not be built", ex);
         }
     }
     
+    private URI buildURI(boolean fromEncoded, String thePath) throws URISyntaxException {
+        String theQuery = buildQuery(fromEncoded);
+        // TODO : do encodePartiallyEncoded only once here, do not do it inside buildPath()
+        // buildFromEncoded and buildFromEncodedMap - we'll need to be careful such that 
+        // path '/' seperators are not encoded so probably we'll need to create PathSegments
+        // again if fromEncoded is set
+        if (fromEncoded) {
+            StringBuilder b = new StringBuilder();
+            b.append(scheme).append("://");
+            if (userInfo != null) {
+                b.append(userInfo).append('@');
+            }
+            b.append(host);
+            if (port != -1) {
+                b.append(':').append(port);    
+            }
+            if (thePath != null && thePath.length() > 0) {
+                b.append(thePath.startsWith("/") ? thePath : '/' + thePath);
+            }
+            if (theQuery != null && theQuery.length() != 0) {
+                b.append('?').append(theQuery);
+            }
+            if (fragment != null) {
+                b.append('#').append(fragment);
+            }
+            return new URI(b.toString());
+        } else {
+            return new URI(scheme, userInfo, host, port, thePath, theQuery, fragment);
+        }
+    }
+    
     private String substituteVarargs(String path, Object... values) {
         Map<String, String> varValueMap = new HashMap<String, String>();
         URITemplate templ = new URITemplate(path);
@@ -109,9 +136,13 @@
     public URI buildFromEncoded(Object... values) throws IllegalArgumentException, UriBuilderException {
         // Problem: multi-arg URI c-tor always forces encoding, operation contract would be broken;
         // use os single-arg URI c-tor requires unnecessary concatenate-parse roundtrip.
-        // Solution: decode back given values and pass as non-decoded to regular build() method
+        // While decoding back given values and passing as non-decoded to regular build() method
+        // is promising unfortunatley it causes the loss of encoded reserved values such as +,
+        // which might cause problems if consumers do rely on URLEncoder which would turn '+' into
+        // ' ' or would break the contract in when query parameters are expected to have %2B 
+        
         for (int i = 0; i < values.length; i++) {
-            values[i] = decodePartiallyEncoded(values[i].toString());
+            values[i] = HttpUtils.encodePartiallyEncoded(values[i].toString(), false);
         }
         return doBuild(true, values);
     }
@@ -125,9 +156,9 @@
     private URI doBuildFromMap(Map<String, ? extends Object> map, boolean fromEncoded) 
         throws IllegalArgumentException, UriBuilderException {
         try {
-            String path = buildPath(fromEncoded);
-            path = substituteMapped(path, map);
-            return new URI(scheme, userInfo, host, port, path, buildQuery(), fragment);
+            String thePath = buildPath(fromEncoded);
+            thePath = substituteMapped(thePath, map);
+            return buildURI(fromEncoded, thePath);
         } catch (URISyntaxException ex) {
             throw new UriBuilderException("URI can not be built", ex);
         }
@@ -150,7 +181,8 @@
         // see buildFromEncoded() comment
         Map<String, String> decodedMap = new HashMap<String, String>(map.size());
         for (Map.Entry<String, ? extends Object> entry : map.entrySet()) {
-            decodedMap.put(entry.getKey(), decodePartiallyEncoded(entry.getValue().toString()));
+            decodedMap.put(entry.getKey(), 
+                           HttpUtils.encodePartiallyEncoded(entry.getValue().toString(), false));
         }
         return doBuildFromMap(decodedMap, true);
     }
@@ -158,7 +190,16 @@
     // CHECKSTYLE:OFF
     @Override
     public UriBuilder clone() {
-        return new UriBuilderImpl(build());
+        UriBuilderImpl builder = new UriBuilderImpl();
+        builder.scheme = scheme;
+        builder.userInfo = userInfo;
+        builder.port = port;
+        builder.host = host;
+        builder.paths = new ArrayList<PathSegment>(paths);
+        builder.fragment = fragment;
+        builder.query = new MetadataMap<String, String>(query);
+        builder.matrix = new MetadataMap<String, String>(matrix);
+        return builder;
     }
 
     // CHECKSTYLE:ON
@@ -292,9 +333,9 @@
         scheme = uri.getScheme();
         port = uri.getPort();
         host = uri.getHost();
-        setPathAndMatrix(uri.getPath());
+        setPathAndMatrix(uri.getRawPath());
         fragment = uri.getFragment();
-        query = JAXRSUtils.getStructuredParams(uri.getQuery(), "&", true);
+        query = JAXRSUtils.getStructuredParams(uri.getRawQuery(), "&", false);
         userInfo = uri.getUserInfo();
     }
 
@@ -314,6 +355,7 @@
             PathSegment ps = iter.next();
             String p = ps.getPath();
             if (p.length() != 0 || !iter.hasNext()) {
+                p = fromEncoded ? new URITemplate(p).encodeLiteralCharacters() : p;
                 if (!p.startsWith("/")) {
                     sb.append('/');
                 }
@@ -327,8 +369,8 @@
         return sb.toString();
     }
 
-    private String buildQuery() {
-        return buildParams(query, '&', false);
+    private String buildQuery(boolean fromEncoded) {
+        return buildParams(query, '&', fromEncoded);
     }
 
     @Override
@@ -417,24 +459,6 @@
     }
 
     /**
-     * Decode partially encoded string. Decode only values that matches patter "percent char followed by two
-     * hexadecimal digits".
-     * 
-     * @param encoded fully or partially encoded string.
-     * @return decoded string
-     */
-    private String decodePartiallyEncoded(String encoded) {
-        Matcher m = DECODE_PATTERN.matcher(encoded);
-        StringBuffer sb = new StringBuffer();
-        while (m.find()) {
-            String found = m.group();
-            m.appendReplacement(sb, HttpUtils.pathDecode(found));
-        }
-        m.appendTail(sb);
-        return sb.toString();
-    }
-
-    /**
      * Query or matrix params convertion from object values vararg to list of strings. No encoding is
      * provided.
      * 
@@ -464,13 +488,14 @@
      */
     private String buildParams(MultivaluedMap<String, String> map, char separator,
                                       boolean fromEncoded) {
+        boolean isQuery = separator == '&';
         StringBuilder b = new StringBuilder();
         for (Iterator<Map.Entry<String, List<String>>> it = map.entrySet().iterator(); it.hasNext();) {
             Map.Entry<String, List<String>> entry = it.next();
             for (Iterator<String> sit = entry.getValue().iterator(); sit.hasNext();) {
                 String val = sit.next();
                 if (fromEncoded) {
-                    val = decodePartiallyEncoded(val);
+                    val = HttpUtils.encodePartiallyEncoded(val, isQuery);
                 }
                 b.append(entry.getKey()).append('=').append(val);
                 if (sit.hasNext() || it.hasNext()) {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java Thu Mar 12 18:53:27 2009
@@ -257,6 +257,24 @@
         return sb.toString();
     }
 
+    /**
+     * Encoded literal characters surrounding template variables, 
+     * ex. "a {id} b" will be encoded to "a%20{id}%20b" 
+     * @return encoded value
+     */
+    public String encodeLiteralCharacters() {
+        StringBuilder sb = new StringBuilder();
+        Matcher matcher = TEMPLATE_NAMES_PATTERN.matcher(template);
+        int i = 0;
+        while (matcher.find()) {
+            sb.append(HttpUtils.encodePartiallyEncoded(template.substring(i, matcher.start()), false));
+            sb.append('{').append(matcher.group(1)).append('}');
+            i = matcher.end();
+        }
+        sb.append(HttpUtils.encodePartiallyEncoded(template.substring(i, template.length()), false));
+        return sb.toString();
+    }
+    
     public static URITemplate createTemplate(ClassResourceInfo cri, Path path) {
 
         if (path == null) {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java Thu Mar 12 18:53:27 2009
@@ -45,6 +45,8 @@
 
 import org.w3c.dom.Document;
 
+import org.apache.cxf.jaxrs.client.XMLSource;
+
 @Provider
 @Produces({"application/xml", "text/xml" })
 @Consumes({"application/xml", "text/xml" })
@@ -56,7 +58,7 @@
     }
     
     public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
-        return Source.class.isAssignableFrom(type);
+        return Source.class.isAssignableFrom(type) || XMLSource.class.isAssignableFrom(type);
     }
     
     public Object readFrom(Class<Object> source, Type genericType, Annotation[] annotations, MediaType m,  
@@ -79,6 +81,8 @@
         } else if (StreamSource.class.isAssignableFrom(source)
                    || Source.class.isAssignableFrom(source)) {
             return new StreamSource(is);
+        } else if (XMLSource.class.isAssignableFrom(source)) {
+            return new XMLSource(is);
         }
         
         throw new IOException("Unrecognized source");

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java Thu Mar 12 18:53:27 2009
@@ -29,6 +29,8 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.PathSegment;
@@ -45,7 +47,7 @@
     
     private static final String LOCAL_IP_ADDRESS = "127.0.0.1";
     private static final String LOCAL_HOST = "localhost";
-    
+    private static final Pattern ENCODE_PATTERN = Pattern.compile("%[0-9a-fA-F][0-9a-fA-F]");
     
     private HttpUtils() {
     }
@@ -73,12 +75,42 @@
         
         String result = urlEncode(value);
         // URLEncoder will encode '+' to %2B but will turn ' ' into '+'
+        // We need to retain '+' and encode ' ' as %20
         if (result.indexOf('+') != -1) {
             result = result.replace("+", "%20");
         }
+        if (result.indexOf("%2B") != -1) {
+            result = result.replace("%2B", "+");
+        }
+
         return result;
     }
     
+    /**
+     * Encodes partially encoded string. Encode all values but those matching pattern 
+     * "percent char followed by two hexadecimal digits".
+     * 
+     * @param encoded fully or partially encoded string.
+     * @return fully encoded string
+     */
+    public static String encodePartiallyEncoded(String encoded, boolean query) {
+        if (encoded.length() == 0) {
+            return encoded;
+        }
+        Matcher m = ENCODE_PATTERN.matcher(encoded);
+        StringBuffer sb = new StringBuffer();
+        int i = 0;
+        while (m.find()) {
+            String before = encoded.substring(i, m.start());
+            sb.append(query ? HttpUtils.urlEncode(before) : HttpUtils.pathEncode(before));
+            sb.append(m.group());
+            i = m.end();            
+        }
+        String tail = encoded.substring(i, encoded.length());
+        sb.append(query ? HttpUtils.urlEncode(tail) : HttpUtils.pathEncode(tail));
+        return sb.toString();
+    }
+    
     public static SimpleDateFormat getHttpDateFormat() {
         SimpleDateFormat dateFormat = 
             new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/client/WebClientTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/client/WebClientTest.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/client/WebClientTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/client/WebClientTest.java Thu Mar 12 18:53:27 2009
@@ -27,6 +27,14 @@
 
 public class WebClientTest extends Assert {
 
+    @Test
+    public void testEncoding() {
+        URI u = WebClient.create("http://foo").path("bar+ %2B").matrix("a", "value+ ")
+            .query("b", "bv+ ").getCurrentURI();
+        System.out.println("'" + u.toString() + "'");
+        assertEquals("http://foo/bar+%20%2B;a=value+%20?b=bv%2B+", u.toString());
+    }
+    
     @Test 
     public void testBaseCurrentPath() {
         assertEquals(URI.create("http://foo"), WebClient.create("http://foo").getBaseURI());

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=752970&r1=752969&r2=752970&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 Thu Mar 12 18:53:27 2009
@@ -68,6 +68,47 @@
     }
     
     @Test
+    public void testClone() throws Exception {
+        URI uri = new URI("http://bar");
+        URI newUri = new UriBuilderImpl(uri).clone().build();   
+        assertEquals("URI is not built correctly", "http://bar", newUri.toString());
+    }
+    
+    @Test
+    public void testClonePctEncodedFromUri() throws Exception {
+        URI uri = new URI("http://bar/foo%20");
+        URI newUri = new UriBuilderImpl(uri).clone().buildFromEncoded();   
+        assertEquals("URI is not built correctly", "http://bar/foo%20", newUri.toString());
+    }
+    
+    @Test
+    public void testClonePctEncoded() throws Exception {
+        URI uri = new URI("http://bar");
+        URI newUri = new UriBuilderImpl(uri)
+            .path("{a}").path("{b}")
+            .matrixParam("m", "m1 ", "m2+%20")
+            .queryParam("q", "q1 ", "q2+q3%20").clone().buildFromEncoded("a+ ", "b%2B%20 ");   
+        assertEquals("URI is not built correctly", 
+                     "http://bar/a+%20/b%2B%20%20;m=m1%20;m=m2+%20?q=q1+&q=q2%2Bq3%20", 
+                     newUri.toString());
+    }
+    
+    @Test
+    public void testEncodedPathQueryFromExistingURI() throws Exception {
+        URI uri = new URI("http://bar/foo+%20%2B?q=a+b%20%2B");
+        URI newUri = new UriBuilderImpl(uri).buildFromEncoded();   
+        assertEquals("URI is not built correctly", 
+                     "http://bar/foo+%20%2B?q=a%2Bb%20%2B", newUri.toString());
+    }
+    
+    @Test
+    public void testEncodedAddedQuery() throws Exception {
+        URI uri = new URI("http://bar");
+        URI newUri = new UriBuilderImpl(uri).queryParam("q", "a+b%20%2B").buildFromEncoded();   
+        assertEquals("URI is not built correctly", "http://bar?q=a%2Bb%20%2B", newUri.toString());
+    }
+    
+    @Test
     public void testSchemeSpecificPart() throws Exception {
         URI uri = new URI("http://bar");
         URI newUri = new UriBuilderImpl(uri).scheme("https").schemeSpecificPart("foo/bar").build();
@@ -457,6 +498,7 @@
         assertEquals("URI is not built correctly", new URI("http://foo/bar;p1=v1;p1=v2"), newUri);
     }
 
+    
     @Test
     public void testMatrixParamMultiSameNameNewVals() throws Exception {
         URI uri = new URI("http://foo/bar;p1=v1");

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/HttpUtilsTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/HttpUtilsTest.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/HttpUtilsTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/HttpUtilsTest.java Thu Mar 12 18:53:27 2009
@@ -42,7 +42,12 @@
      
     @Test
     public void testPathEncode() {
-        assertEquals("%2B%20", HttpUtils.pathEncode("+ "));
+        assertEquals("+%20", HttpUtils.pathEncode("+ "));
+    }
+    
+    @Test
+    public void testURLEncode() {
+        assertEquals("%2B+", HttpUtils.urlEncode("+ "));
     }
     
     @Test

Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java?rev=752970&r1=752969&r2=752970&view=diff
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java (original)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSSoapBookTest.java Thu Mar 12 18:53:27 2009
@@ -44,6 +44,7 @@
 import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
 import org.apache.cxf.jaxrs.client.ResponseExceptionMapper;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.client.XMLSource;
 import org.apache.cxf.jaxrs.ext.form.Form;
 import org.apache.cxf.jaxrs.impl.MetadataMap;
 import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
@@ -100,11 +101,11 @@
     }
     
     @Test
-    public void testGetBook123WebClientBean() throws Exception {
+    public void testGetBook123XMLSource() throws Exception {
         String baseAddress = "http://localhost:9092/test/services/rest";
         WebClient client = WebClient.create(baseAddress);
         client.path("/bookstore/123").accept(MediaType.APPLICATION_XML_TYPE);
-        Book b = client.get(Book.class);
+        Book b = client.get(XMLSource.class).getNode("/Book", Book.class);
         assertEquals(123, b.getId());
         assertEquals("CXF in Action", b.getName());
     }