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 2010/02/08 17:41:12 UTC

svn commit: r907718 [1/2] - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/ rt/frontend/jaxrs/src/...

Author: sergeyb
Date: Mon Feb  8 16:41:11 2010
New Revision: 907718

URL: http://svn.apache.org/viewvc?rev=907718&view=rev
Log:
JAXRS : WADLgen enhancements, case-insensitive query lookups plus few minor fixes

Added:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLName.java   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/ElementQNameResolver.java   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/test/resources/book1.xsd   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/test/resources/book2.xsd   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/test/resources/books.xsd   (with props)
Modified:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriInfoImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/xml/XMLSourceTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestPreprocessorTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/BookStore.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/WadlGeneratorTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/jaxb/Book.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/jaxb/Chapter.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/JAXBElementProviderTest.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/JAXRSUtilsTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLName.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLName.java?rev=907718&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLName.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLName.java Mon Feb  8 16:41:11 2010
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.ext.xml;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Can be used to assign qualified XML names to arbitrary classes
+ * for the purpose of matching them with external schema definitions 
+ * 
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XMLName {
+    /**
+     * Qualified name, example {http://books}book
+     */
+    String value() default "";
+    /**
+     * Optional prefix 
+     **/
+    String prefix() default "";
+}

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

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

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/xml/XMLSource.java Mon Feb  8 16:41:11 2010
@@ -18,9 +18,12 @@
  */
 package org.apache.cxf.jaxrs.ext.xml;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Array;
+import java.lang.reflect.Method;
 import java.net.URI;
 import java.util.Collections;
 import java.util.Iterator;
@@ -32,6 +35,7 @@
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.Unmarshaller;
 import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
 import javax.xml.transform.Source;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.xpath.XPath;
@@ -46,9 +50,12 @@
 
 import org.apache.cxf.common.i18n.BundleUtils;
 import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PrimitiveUtils;
 import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.helpers.XMLUtils;
 import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
 
 /**
  * Utility class for manipulating XML response using XPath and XSLT
@@ -62,6 +69,7 @@
     
     private InputStream stream; 
     private boolean buffering;
+    private boolean markFailed;
     
     public XMLSource(InputStream is) {
         stream = is;
@@ -83,17 +91,14 @@
     }
     
     public <T> T getNode(String expression, Map<String, String> namespaces, Class<T> cls) {
-        XPath xpath = XPathFactory.newInstance().newXPath();
-        xpath.setNamespaceContext(new NamespaceContextImpl(namespaces));
-        try {
-            Node node = (Node)xpath.evaluate(expression, getSource(), XPathConstants.NODE);
-            if (node == null) {
-                return null;
-            }
-            DOMSource ds = new DOMSource(node);
-            return readFromSource(ds, cls);
-        } catch (XPathExpressionException ex) {
-            throw new IllegalArgumentException("Illegal XPath expression '" + expression + "'", ex);
+        Node node = (Node)evaluate(expression, namespaces, XPathConstants.NODE);
+        if (node == null) {
+            return null;
+        }
+        if (cls.isPrimitive() || cls == String.class) {
+            return readPrimitiveValue(node, cls);    
+        } else {
+            return readFromSource(new DOMSource(node), cls);
         }
     }
     
@@ -103,25 +108,21 @@
     
     @SuppressWarnings("unchecked")
     public <T> T[] getNodes(String expression, Map<String, String> namespaces, Class<T> cls) {
-        XPath xpath = XPathFactory.newInstance().newXPath();
-        xpath.setNamespaceContext(new NamespaceContextImpl(namespaces));
-        try {
-            NodeList nodes = (NodeList)xpath.evaluate(expression, getSource(), XPathConstants.NODESET);
-            if (nodes == null || nodes.getLength() == 0) {
-                return null;
+        
+        NodeList nodes = (NodeList)evaluate(expression, namespaces, XPathConstants.NODESET);
+        if (nodes == null || nodes.getLength() == 0) {
+            return null;
+        }
+        T[] values = (T[])Array.newInstance(cls, nodes.getLength());
+        for (int i = 0; i < nodes.getLength(); i++) {
+            Node node = nodes.item(i);
+            if (InjectionUtils.isPrimitive(cls)) {
+                values[i] = readPrimitiveValue(node, cls);
+            } else {
+                values[i] = readFromSource(new DOMSource(node), cls);
             }
-            T[] values = (T[])Array.newInstance(cls, nodes.getLength());
- 
-            for (int i = 0; i < nodes.getLength(); i++) {
-                DOMSource ds = new DOMSource(nodes.item(i));
-                values[i] = readFromSource(ds, cls);
-            }
-                  
-            return values;
-            
-        } catch (XPathExpressionException ex) {
-            throw new IllegalArgumentException("Illegal XPath expression '" + expression + "'", ex);
         }
+        return values;
     }
 
     public URI getLink(String expression) {
@@ -144,15 +145,34 @@
     }
     
     public String getValue(String expression, Map<String, String> namespaces) {
+        return getValue(expression, namespaces, String.class);
+    }
+    
+    public <T> T getValue(String expression, Map<String, String> namespaces, Class<T> cls) {
+        Object result = evaluate(expression, namespaces, XPathConstants.STRING);
+        return result == null ? null : convertStringToPrimitive(result.toString(), cls); 
+    }
+    
+    
+    private Object evaluate(String expression, Map<String, String> namespaces, QName type) {
         XPath xpath = XPathFactory.newInstance().newXPath();
         xpath.setNamespaceContext(new NamespaceContextImpl(namespaces));
         try {
-            return (String)xpath.evaluate(expression, getSource(), XPathConstants.STRING);
+            return xpath.evaluate(expression, getSource(), type);
         } catch (XPathExpressionException ex) {
             throw new IllegalArgumentException("Illegal XPath expression '" + expression + "'", ex);
         }
     }
     
+    
+    public String[] getValues(String expression) {
+        return getValues(expression, CastUtils.cast(Collections.emptyMap(), String.class, String.class));
+    }
+    
+    public String[] getValues(String expression, Map<String, String> namespaces) {
+        return getNodes(expression, namespaces, String.class);
+    }
+    
     private static class NamespaceContextImpl implements NamespaceContext {
         
         private Map<String, String> namespaces;
@@ -183,6 +203,41 @@
         }
     }
     
+    private <T> T readPrimitiveValue(Node node, Class<T> cls) {
+        if (String.class == cls) {
+            if (node.getNodeType() == Node.ELEMENT_NODE) {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                XMLUtils.writeTo(new DOMSource(node), bos, 0, "", "yes");
+                try {
+                    return cls.cast(bos.toString("UTF-8"));
+                } catch (UnsupportedEncodingException ex) {
+                    // won't happen
+                }
+            } else {
+                return cls.cast(node.getNodeValue());
+            }
+        } 
+        
+        return convertStringToPrimitive(node.getNodeValue(), cls);
+    }
+    
+    private <T> T convertStringToPrimitive(String value, Class<T> cls) {
+        if (String.class == cls) {
+            return cls.cast(value);
+        }
+        if (cls.isPrimitive()) {
+            return cls.cast(PrimitiveUtils.read(value, cls));
+        } else {
+            try {
+                Method m = cls.getMethod("valueOf", new Class[]{String.class});
+                return cls.cast(m.invoke(null, value));
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+    
+    
     private <T> T readFromSource(Source s, Class<T> cls) {
         try {
             JAXBElementProvider provider = new JAXBElementProvider();
@@ -199,9 +254,15 @@
     
     private InputSource getSource() {
         try {
-            if (buffering) {
-                stream.reset();
-                stream.mark(stream.available());
+            if (!markFailed && buffering) {
+                try {
+                    stream.reset();
+                    stream.mark(stream.available());
+                } catch (IOException ex) {
+                    markFailed = true;
+                    LOG.warning(new org.apache.cxf.common.i18n.Message("NO_SOURCE_MARK", BUNDLE).toString());
+                    stream = IOUtils.loadIntoBAIS(stream);
+                }
             }
         } catch (IOException ex) {
             LOG.warning(new org.apache.cxf.common.i18n.Message("NO_SOURCE_MARK", BUNDLE).toString());

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriInfoImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriInfoImpl.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriInfoImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriInfoImpl.java Mon Feb  8 16:41:11 2010
@@ -43,18 +43,16 @@
 
 public class UriInfoImpl implements UriInfo {
     private static final Logger LOG = LogUtils.getL7dLogger(UriInfoImpl.class);
+    private static final String CASE_INSENSITIVE_QUERIES = "org.apache.cxf.http.case_insensitive_queries";
 
     private MultivaluedMap<String, String> templateParams;
     private Message message;
     private OperationResourceInfoStack stack;
+    private boolean caseInsensitiveQueries;
 
     @SuppressWarnings("unchecked")
     public UriInfoImpl(Message m) {
-        this.message = m;
-        this.templateParams = (MultivaluedMap<String, String>)m.get(URITemplate.TEMPLATE_PARAMETERS);
-        if (m != null) {
-            this.stack = m.get(OperationResourceInfoStack.class);
-        }
+        this(m, (MultivaluedMap<String, String>)m.get(URITemplate.TEMPLATE_PARAMETERS));
     }
     
     public UriInfoImpl(Message m, MultivaluedMap<String, String> templateParams) {
@@ -62,6 +60,8 @@
         this.templateParams = templateParams;
         if (m != null) {
             this.stack = m.get(OperationResourceInfoStack.class);
+            this.caseInsensitiveQueries = 
+                MessageUtils.isTrue(m.getContextualProperty(CASE_INSENSITIVE_QUERIES));
         }
     }
 
@@ -109,7 +109,15 @@
     }
 
     public MultivaluedMap<String, String> getQueryParameters(boolean decode) {
-        return JAXRSUtils.getStructuredParams((String)message.get(Message.QUERY_STRING), "&", decode);
+        
+        if (!caseInsensitiveQueries) {
+            return JAXRSUtils.getStructuredParams((String)message.get(Message.QUERY_STRING), "&", decode);
+        }
+        
+        MultivaluedMap<String, String> queries = new MetadataMap<String, String>(false, true);
+        JAXRSUtils.getStructuredParams(queries, (String)message.get(Message.QUERY_STRING), "&", decode);
+        return queries;
+        
     }
 
     public URI getRequestUri() {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java Mon Feb  8 16:41:11 2010
@@ -336,12 +336,15 @@
             Response excResponse = JAXRSUtils.convertFaultToResponse(ex, message);
             if (excResponse != null) {
                 serializeMessage(message, excResponse, ori, false);
+                return;
+            } else {
+                ex.printStackTrace();
             }
-        } else {
-            message.put(Message.RESPONSE_CODE, 500);
-            writeResponseErrorMessage(out, "SERIALIZE_ERROR", 
-                                      responseObj.getClass().getSimpleName()); 
-        }    
+        }
+        message.put(Message.RESPONSE_CODE, 500);
+        writeResponseErrorMessage(out, "SERIALIZE_ERROR", 
+                                  responseObj.getClass().getSimpleName()); 
+            
     }
     
     

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/ElementQNameResolver.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/ElementQNameResolver.java?rev=907718&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/ElementQNameResolver.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/ElementQNameResolver.java Mon Feb  8 16:41:11 2010
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.jaxrs.model.wadl;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+public interface ElementQNameResolver {
+    QName resolve(Class<?> type, Annotation[] annotations, Map<Class<?>, QName> clsMap);
+}

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

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

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java Mon Feb  8 16:41:11 2010
@@ -18,10 +18,16 @@
  */
 package org.apache.cxf.jaxrs.model.wadl;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringReader;
 import java.io.StringWriter;
 import java.lang.annotation.Annotation;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -45,8 +51,10 @@
 import javax.xml.namespace.QName;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -68,6 +76,8 @@
 import org.apache.cxf.jaxrs.JAXRSServiceImpl;
 import org.apache.cxf.jaxrs.ext.Description;
 import org.apache.cxf.jaxrs.ext.RequestHandler;
+import org.apache.cxf.jaxrs.ext.xml.XMLName;
+import org.apache.cxf.jaxrs.ext.xml.XMLSource;
 import org.apache.cxf.jaxrs.impl.HttpHeadersImpl;
 import org.apache.cxf.jaxrs.impl.UriInfoImpl;
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
@@ -75,12 +85,14 @@
 import org.apache.cxf.jaxrs.model.Parameter;
 import org.apache.cxf.jaxrs.model.ParameterType;
 import org.apache.cxf.jaxrs.model.URITemplate;
+import org.apache.cxf.jaxrs.utils.AnnotationUtils;
 import org.apache.cxf.jaxrs.utils.InjectionUtils;
 import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 import org.apache.cxf.jaxrs.utils.ResourceUtils;
 import org.apache.cxf.jaxrs.utils.schemas.SchemaHandler;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.service.Service;
+import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;
 import org.apache.cxf.staxutils.StaxUtils;
 import org.apache.ws.commons.schema.XmlSchema;
 
@@ -98,7 +110,14 @@
     private String wadlNamespace;
     private boolean ignoreMessageWriters = true;
     private boolean singleResourceMultipleMethods = true;
-    private boolean useSingleSlashResource = true;
+    private boolean useSingleSlashResource;
+    
+    private boolean useJaxbContextForQnames = true;
+    
+    private List<String> externalSchemasCache;
+    private List<URI> externalSchemaLinks;
+    private Map<String, List<String>> externalQnamesMap; 
+    private ElementQNameResolver resolver;
     
     public Response handleRequest(Message m, ClassResourceInfo resource) {
         
@@ -122,25 +141,26 @@
         
         List<ClassResourceInfo> cris = getResourcesList(m, resource);
         
-        Set<Class<?>> jaxbTypes = ResourceUtils.getAllRequestResponseTypes(cris, true).keySet();
-        JAXBContext context = createJaxbContext(jaxbTypes);
-        SchemaCollection coll = getSchemaCollection(context);
-        JAXBContextProxy proxy = null;
-        if (coll != null) {
-            proxy = ReflectionInvokationHandler.createProxyWrapper(context, JAXBContextProxy.class);
-        }
+        Set<Class<?>> allTypes = 
+            ResourceUtils.getAllRequestResponseTypes(cris, useJaxbContextForQnames).keySet();
+        
+        JAXBContext context = createJaxbContext(allTypes);
+        SchemaWriter schemaWriter = createSchemaWriter(context, ui);
+        ElementQNameResolver qnameResolver = 
+            schemaWriter == null ? null : createElementQNameResolver(context);
+        
         Map<Class<?>, QName> clsMap = new IdentityHashMap<Class<?>, QName>();
         Set<ClassResourceInfo> visitedResources = new HashSet<ClassResourceInfo>();
         for (ClassResourceInfo cri : cris) {
             String path = cri.getURITemplate().getValue();
             sbResources.append("<resource path=\"").append(path).append("\">");
             handleDocs(cri.getServiceClass().getAnnotations(), sbResources);
-            handleResource(sbResources, jaxbTypes, proxy, clsMap, cri, visitedResources);
+            handleResource(sbResources, allTypes, qnameResolver, clsMap, cri, visitedResources);
             sbResources.append("</resource>");
         }
         sbResources.append("</resources>");
         
-        handleGrammars(sbMain, sbGrammars, coll, clsMap);
+        handleGrammars(sbMain, sbGrammars, schemaWriter, clsMap);
         
         sbGrammars.append("</grammars>");
         sbMain.append(">");
@@ -157,7 +177,11 @@
     }
 
     private void handleGrammars(StringBuilder sbApp, StringBuilder sbGrammars, 
-                                SchemaCollection coll, Map<Class<?>, QName> clsMap) {
+                                SchemaWriter writer, Map<Class<?>, QName> clsMap) {
+        if (writer == null) {
+            return;
+        }
+        
         Map<String, String> map = new HashMap<String, String>();
         for (QName qname : clsMap.values()) {
             map.put(qname.getPrefix(), qname.getNamespaceURI());
@@ -167,25 +191,12 @@
                  .append(entry.getValue()).append("\"");
         }
         
-        
-        writeSchemas(sbGrammars, coll);
+        writer.write(sbGrammars);
     }
     
-    private void writeSchemas(StringBuilder sb, SchemaCollection coll) {
-        if (coll == null) {
-            return;
-        }
-        for (XmlSchema xs : coll.getXmlSchemas()) {
-            if (xs.getItems().getCount() == 0) {
-                continue;
-            }
-            StringWriter writer = new StringWriter();
-            xs.write(writer);
-            sb.append(writer.toString());
-        }
-    }
     
-    private void handleResource(StringBuilder sb, Set<Class<?>> jaxbTypes, JAXBContextProxy jaxbProxy,
+    private void handleResource(StringBuilder sb, Set<Class<?>> jaxbTypes, 
+                                ElementQNameResolver qnameResolver,
                                 Map<Class<?>, QName> clsMap, ClassResourceInfo cri, 
                                 Set<ClassResourceInfo> visitedResources) {
         visitedResources.add(cri);
@@ -204,23 +215,23 @@
                     sb.append("<resource path=\"").append(path).append("\">");
                     handleDocs(subcri.getServiceClass().getAnnotations(), sb);
                     handlePathAndMatrixParams(sb, ori);
-                    handleResource(sb, jaxbTypes, jaxbProxy, clsMap, subcri, 
+                    handleResource(sb, jaxbTypes, qnameResolver, clsMap, subcri, 
                                    visitedResources);
                     sb.append("</resource>");
                 } else {
-                    handleDynamicSubresource(sb, jaxbTypes, jaxbProxy, clsMap, ori, subcri);
+                    handleDynamicSubresource(sb, jaxbTypes, qnameResolver, clsMap, ori, subcri);
                 }
                 continue;
             }
             OperationResourceInfo nextOp = i + 1 < sortedOps.size() ? sortedOps.get(i + 1) : null;
-            resourceTagOpened = handleOperation(sb, jaxbTypes, jaxbProxy, clsMap, ori, nextOp, 
+            resourceTagOpened = handleOperation(sb, jaxbTypes, qnameResolver, clsMap, ori, nextOp, 
                                                 resourceTagOpened, i);
         }
     }
     
     //CHECKSTYLE:OFF
     private boolean handleOperation(StringBuilder sb, Set<Class<?>> jaxbTypes, 
-                                 JAXBContextProxy jaxbProxy, 
+                                 ElementQNameResolver qnameResolver, 
                                  Map<Class<?>, QName> clsMap, 
                                  OperationResourceInfo ori,
                                  OperationResourceInfo nextOp,
@@ -250,10 +261,10 @@
         if (ori.getMethodToInvoke().getParameterTypes().length != 0) {
             sb.append("<request>");
             if (isFormRequest(ori)) {
-                handleRepresentation(sb, jaxbTypes, jaxbProxy, clsMap, ori, null, false);
+                handleRepresentation(sb, jaxbTypes, qnameResolver, clsMap, ori, null, false);
             } else {
                 for (Parameter p : ori.getParameters()) {        
-                    handleParameter(sb, jaxbTypes, jaxbProxy, clsMap, ori, p);             
+                    handleParameter(sb, jaxbTypes, qnameResolver, clsMap, ori, p);             
                 }
             }
             sb.append("</request>");
@@ -265,7 +276,7 @@
         }
         sb.append(">");
         if (void.class != ori.getMethodToInvoke().getReturnType()) {
-            handleRepresentation(sb, jaxbTypes, jaxbProxy, clsMap, ori,
+            handleRepresentation(sb, jaxbTypes, qnameResolver, clsMap, ori,
                                  ori.getMethodToInvoke().getReturnType(), false);
         }
         sb.append("</response>");
@@ -315,7 +326,7 @@
     }
     
     protected void handleDynamicSubresource(StringBuilder sb, Set<Class<?>> jaxbTypes, 
-                 JAXBContextProxy jaxbProxy, Map<Class<?>, QName> clsMap, OperationResourceInfo ori,
+                 ElementQNameResolver qnameResolver, Map<Class<?>, QName> clsMap, OperationResourceInfo ori,
                  ClassResourceInfo subcri) {
         
         if (subcri != null) {
@@ -334,11 +345,12 @@
     }
     
     
-    private void handleParameter(StringBuilder sb, Set<Class<?>> jaxbTypes, JAXBContextProxy jaxbProxy, 
+    private void handleParameter(StringBuilder sb, Set<Class<?>> jaxbTypes, 
+                                 ElementQNameResolver qnameResolver, 
                                  Map<Class<?>, QName> clsMap, OperationResourceInfo ori, Parameter pm) {
         Class<?> cls = ori.getMethodToInvoke().getParameterTypes()[pm.getIndex()];
         if (pm.getType() == ParameterType.REQUEST_BODY) {
-            handleRepresentation(sb, jaxbTypes, jaxbProxy, clsMap, ori, cls, true);
+            handleRepresentation(sb, jaxbTypes, qnameResolver, clsMap, ori, cls, true);
             return;
         }
         if (pm.getType() == ParameterType.PATH || pm.getType() == ParameterType.MATRIX) {
@@ -358,6 +370,19 @@
         }
     }
     
+    private Annotation[] getBodyAnnotations(OperationResourceInfo ori, boolean inbound) {
+        if (inbound) {
+            for (Parameter pm : ori.getParameters()) {
+                if (pm.getType() == ParameterType.REQUEST_BODY) {
+                    return ori.getAnnotatedMethod().getParameterAnnotations()[pm.getIndex()];
+                }
+            }
+            return new Annotation[]{};
+        } else {
+            return ori.getAnnotatedMethod().getDeclaredAnnotations();
+        }
+    }
+    
     private void writeParam(StringBuilder sb, Parameter pm, OperationResourceInfo ori) {
         Class<?> type = ori.getMethodToInvoke().getParameterTypes()[pm.getIndex()];
         if (!"".equals(pm.getName())) {
@@ -383,7 +408,9 @@
     }
     
     protected void doWriteParam(StringBuilder sb, Parameter pm, Class<?> type, String paramName) {
-        
+        if (ParameterType.REQUEST_BODY == pm.getType()) {
+            return;
+        }
         sb.append("<param name=\"").append(paramName).append("\" ");
         String style = ParameterType.PATH == pm.getType() ? "template" 
                        : ParameterType.FORM == pm.getType() ? "query"
@@ -399,7 +426,8 @@
         sb.append("/>");
     }
     
-    private void handleRepresentation(StringBuilder sb, Set<Class<?>> jaxbTypes, JAXBContextProxy jaxbProxy,
+    private void handleRepresentation(StringBuilder sb, Set<Class<?>> jaxbTypes, 
+                                      ElementQNameResolver qnameResolver,
                                       Map<Class<?>, QName> clsMap, OperationResourceInfo ori, 
                                       Class<?> type, boolean inbound) {
         List<MediaType> types = inbound ? ori.getConsumeTypes() : ori.getProduceTypes();
@@ -416,8 +444,9 @@
                 }
                 sb.append("<representation");
                 sb.append(" mediaType=\"").append(mt.toString()).append("\"");
-                if (jaxbProxy != null && mt.getSubtype().contains("xml") && jaxbTypes.contains(type)) {
-                    generateQName(sb, jaxbProxy, clsMap, type);
+                if (qnameResolver != null && mt.getSubtype().contains("xml") && jaxbTypes.contains(type)) {
+                    generateQName(sb, qnameResolver, clsMap, type,
+                                  getBodyAnnotations(ori, inbound));
                 }
                 sb.append("/>");
             }
@@ -463,9 +492,10 @@
     
 
     private void generateQName(StringBuilder sb,
-                               JAXBContextProxy jaxbProxy,
+                               ElementQNameResolver qnameResolver,
                                Map<Class<?>, QName> clsMap, 
-                               Class<?> type) {
+                               Class<?> type,
+                               Annotation[] annotations) {
         
         QName typeQName = clsMap.get(type);
         if (typeQName != null) {
@@ -473,7 +503,8 @@
             return;
         }
         
-        QName qname = getQName(jaxbProxy, type, clsMap);
+        QName qname = qnameResolver.resolve(type, annotations,
+                                            Collections.unmodifiableMap(clsMap));
         
         if (qname != null) {
             writeQName(sb, qname);
@@ -513,7 +544,7 @@
         return xmlSchemaCollection;
     }
     
-    private QName getQName(JAXBContextProxy jaxbProxy, Class<?> type, Map<Class<?>, QName> clsMap) {
+    private QName getJaxbQName(JAXBContextProxy jaxbProxy, Class<?> type, Map<Class<?>, QName> clsMap) {
         
         XmlRootElement root = type.getAnnotation(XmlRootElement.class);
         if (root != null) {
@@ -530,7 +561,7 @@
         }
         
         try {
-            JAXBBeanInfo jaxbInfo = JAXBUtils.getBeanInfo(jaxbProxy, type);
+            JAXBBeanInfo jaxbInfo = jaxbProxy == null ? null : JAXBUtils.getBeanInfo(jaxbProxy, type);
             if (jaxbInfo == null) {
                 return null;
             }
@@ -560,7 +591,7 @@
     }
     
     private JAXBContext createJaxbContext(Set<Class<?>> classes) {
-        if (classes.isEmpty()) {
+        if (!useJaxbContextForQnames || classes.isEmpty()) {
             return null;
         }
         Set<Class<?>> classesToBeBound = new HashSet<Class<?>>(classes);
@@ -734,4 +765,309 @@
         this.useSingleSlashResource = useSingleSlashResource;
     }
 
+    public void setSchemaLocations(List<String> locations) {
+        
+        externalQnamesMap = new HashMap<String, List<String>>();
+        externalSchemasCache = new ArrayList<String>(locations.size()); 
+        for (int i = 0; i < locations.size(); i++) {
+            String loc = locations.get(i);
+            try {
+                InputStream is = ResourceUtils.getResourceStream(loc, BusFactory.getDefaultBus());
+                if (is == null) {
+                    return;
+                }
+                ByteArrayInputStream bis = IOUtils.loadIntoBAIS(is);
+                XMLSource source = new XMLSource(bis);
+                source.setBuffering(true);
+                String targetNs = source.getValue("/*/@targetNamespace");
+                
+                Map<String, String> nsMap = 
+                    Collections.singletonMap("xs", XmlSchemaConstants.XSD_NAMESPACE_URI);
+                String[] elementNames = source.getValues("/*/xs:element/@name", nsMap);
+                externalQnamesMap.put(targetNs, Arrays.asList(elementNames));
+                String schemaValue = source.getNode("/xs:schema", nsMap, String.class);
+                externalSchemasCache.add(schemaValue);
+            } catch (Exception ex) {
+                LOG.warning("No schema resource " + loc + " can be loaded : " + ex.getMessage());
+                externalSchemasCache = null;
+                externalQnamesMap = null;
+                return;
+            }
+        }
+         
+    }
+
+    public void setUseJaxbContextForQnames(boolean checkJaxbOnly) {
+        this.useJaxbContextForQnames = checkJaxbOnly;
+    }
+    
+    protected ElementQNameResolver createElementQNameResolver(JAXBContext context) {
+        if (resolver != null) {
+            return resolver;
+        }
+        if (useJaxbContextForQnames) {
+            if (context != null) {
+                JAXBContextProxy proxy = 
+                    ReflectionInvokationHandler.createProxyWrapper(context, JAXBContextProxy.class);
+                return new JaxbContextQNameResolver(proxy);
+            } else {
+                return null;
+            }
+        } else if (externalQnamesMap != null) {
+            return new SchemaQNameResolver(externalQnamesMap);
+        } else {
+            return new XMLNameQNameResolver();
+        }
+    }
+    
+    protected SchemaWriter createSchemaWriter(JAXBContext context, UriInfo ui) {
+        // if neither externalSchemaLinks nor externalSchemasCache is set
+        // then JAXBContext will be used to generate the schema
+        if (externalSchemaLinks != null && externalSchemasCache == null) {
+            return new ExternalSchemaWriter(externalSchemaLinks, ui);
+        } else if (externalSchemasCache != null) {
+            return new StringSchemaWriter(externalSchemasCache, externalSchemaLinks, ui);
+        } else if (context != null) {
+            SchemaCollection coll = getSchemaCollection(context);
+            if (coll != null) {
+                return new SchemaCollectionWriter(coll);
+            }
+        }
+        return null;
+    }
+    
+    public void setExternalLinks(List<String> externalLinks) {
+        externalSchemaLinks = new LinkedList<URI>();
+        for (String s : externalLinks) {
+            try {
+                externalSchemaLinks.add(URI.create(s));
+            } catch (Exception ex) {
+                LOG.warning("Not a valid URI : " + s);
+                externalSchemaLinks = null;
+                break;
+            }
+        }
+    }
+
+    private static interface SchemaWriter {
+        void write(StringBuilder sb);
+    }
+    
+    private class StringSchemaWriter implements SchemaWriter {
+        
+        private List<String> theSchemas;
+        
+        public StringSchemaWriter(List<String> schemas, List<URI> links, UriInfo ui) {
+            
+            this.theSchemas = new LinkedList<String>();
+            // we'll need to do the proper schema caching eventually 
+            for (String s : schemas) {
+                XMLSource source = new XMLSource(new ByteArrayInputStream(s.getBytes()));
+                source.setBuffering(true);
+                Map<String, String> locs = getLocationsMap(source, "import", links, ui);
+                String actualSchema = !locs.isEmpty() ? transformSchema(s, locs) : s;
+                theSchemas.add(actualSchema);
+            }
+        }
+
+        private Map<String, String> getLocationsMap(XMLSource source, String elementName,
+                                                    List<URI> links, UriInfo ui) {
+            Map<String, String> nsMap = 
+                Collections.singletonMap("xs", XmlSchemaConstants.XSD_NAMESPACE_URI);
+            String[] locations = source.getValues("/*/xs:" + elementName + "/@schemaLocation", nsMap);
+            if (locations == null) {
+                return Collections.emptyMap();
+            }
+            
+            Map<String, String> locs = new HashMap<String, String>();
+            for (String loc : locations) {
+                try {
+                    URI uri = URI.create(loc);
+                    if (!uri.isAbsolute()) {
+                        if (links != null) {
+                            for (URI overwriteURI : links) {
+                                if (overwriteURI.toString().endsWith(loc)) {
+                                    if (overwriteURI.isAbsolute()) {
+                                        locs.put(loc, overwriteURI.toString());
+                                    } else {
+                                        locs.put(loc, ui.getBaseUriBuilder().path(
+                                            overwriteURI.toString()).build().toString());
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                        if (!locs.containsKey(loc)) {
+                            locs.put(loc, ui.getBaseUriBuilder().path(
+                                 loc.toString()).build().toString());
+                        }
+                    }
+                } catch (Exception ex) {
+                    // continue
+                }
+            }
+            return locs;
+        }
+        
+        private String transformSchema(String schema, Map<String, String> locs) {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            SchemaConverter sc = new SchemaConverter(StaxUtils.createXMLStreamWriter(bos), locs);
+            try {
+                StaxUtils.copy(new StreamSource(new StringReader(schema)), sc);
+                sc.flush();
+                sc.close();
+                return bos.toString();
+            } catch (Exception ex) {
+                return schema;
+            }
+            
+        }
+        
+        public void write(StringBuilder sb) {
+            for (String s : theSchemas) { 
+                sb.append(s);
+            }
+        }
+    }
+    
+    private class SchemaCollectionWriter implements SchemaWriter {
+        
+        private SchemaCollection coll;
+        
+        public SchemaCollectionWriter(SchemaCollection coll) {
+            this.coll = coll;
+        }
+        
+        public void write(StringBuilder sb) {
+            for (XmlSchema xs : coll.getXmlSchemas()) {
+                if (xs.getItems().getCount() == 0) {
+                    continue;
+                }
+                StringWriter writer = new StringWriter();
+                xs.write(writer);
+                sb.append(writer.toString());
+            }
+        }
+    }
+    
+    private class ExternalSchemaWriter implements SchemaWriter {
+        
+        private List<URI> links;
+        private UriInfo uriInfo; 
+        
+        public ExternalSchemaWriter(List<URI> links, UriInfo ui) {
+            this.links = links;
+            this.uriInfo = ui;
+        }
+        
+        public void write(StringBuilder sb) {
+            for (URI link : links) {
+                try {
+                    URI value = link.isAbsolute() ? link 
+                        : uriInfo.getBaseUriBuilder().path(link.toString()).build(); 
+                    sb.append("<include href=\"").append(value.toString()).append("\"/>");
+                } catch (Exception ex) {
+                    LOG.warning("WADL grammar section will be incomplete, this link is not a valid URI : "
+                                + link.toString());
+                }
+            }
+        }
+    }
+    
+    private class JaxbContextQNameResolver implements ElementQNameResolver {
+
+        private JAXBContextProxy proxy;
+        
+        public JaxbContextQNameResolver(JAXBContextProxy proxy) {
+            this.proxy = proxy;
+        }
+        
+        public QName resolve(Class<?> type, Annotation[] annotations, Map<Class<?>, QName> clsMap) {
+            return getJaxbQName(proxy, type, clsMap);
+        }
+        
+    }
+    
+    private class XMLNameQNameResolver implements ElementQNameResolver {
+
+        public QName resolve(Class<?> type, Annotation[] annotations, Map<Class<?>, QName> clsMap) {
+            XMLName name = AnnotationUtils.getAnnotation(annotations, XMLName.class);
+            if (name == null) {
+                name = type.getAnnotation(XMLName.class);
+            }
+            if (name != null) {
+                QName qname = convertStringToQName(name.value(), name.prefix());
+                if (qname.getPrefix().length() > 0) {
+                    return qname;
+                } else {
+                    return getQNameFromParts(qname.getLocalPart(),
+                                             qname.getNamespaceURI(), clsMap);                    
+                }
+            }
+            return null;
+        }
+        
+    }
+    
+    private class SchemaQNameResolver implements ElementQNameResolver {
+
+        private Map<String, List<String>> map;
+        
+        public SchemaQNameResolver(Map<String, List<String>> map) {
+            this.map = map;
+        }
+        
+        public QName resolve(Class<?> type, Annotation[] annotations, Map<Class<?>, QName> clsMap) {
+            String name = type.getSimpleName();
+            for (Map.Entry<String, List<String>> entry : map.entrySet()) {
+                String elementName = null;  
+                if (entry.getValue().contains(name)) { 
+                    elementName = name;                    
+                } else if (entry.getValue().contains(name.toLowerCase())) { 
+                    elementName = name.toLowerCase();                    
+                }  
+                if (elementName != null) {
+                    return getQNameFromParts(elementName, entry.getKey(), clsMap);
+                }
+            }
+            return null;
+        }
+        
+    }
+    
+    private QName convertStringToQName(String name, String prefix) {
+        int ind1 = name.indexOf('{');
+        if (ind1 != 0) {
+            return null;
+        }
+        
+        int ind2 = name.indexOf('}');
+        if (ind2 <= ind1 + 1 || ind2 >= name.length() - 1) {
+            return null;
+        }
+        String ns = name.substring(ind1 + 1, ind2);
+        String localName = name.substring(ind2 + 1);
+        return new QName(ns, localName, prefix);
+    }
+
+    public void setResolver(ElementQNameResolver resolver) {
+        this.resolver = resolver;
+    }
+    
+    private static class SchemaConverter extends DelegatingXMLStreamWriter {
+        private static final String SCHEMA_LOCATION = "schemaLocation";
+        private Map<String, String> locsMap;    
+        public SchemaConverter(XMLStreamWriter writer, Map<String, String> locsMap) {
+            super(writer);
+            this.locsMap = locsMap;
+        }
+    
+        public void writeAttribute(String local, String value) throws XMLStreamException {
+            if (SCHEMA_LOCATION.equals(local) && locsMap.containsKey(value)) {
+                value = locsMap.get(value);
+            }
+            super.writeAttribute(local, value);
+        }
+    }        
+        
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/AbstractJAXBProvider.java Mon Feb  8 16:41:11 2010
@@ -629,9 +629,9 @@
         return writer == null ? StaxUtils.createXMLStreamWriter(os) : writer;
     }
     
-    protected static void convertToMapOfQNames(Map<String, String> map,
-                                               Map<QName, QName> elementsMap,
-                                               Map<String, String> nsMap) {
+    protected static void convertToQNamesMap(Map<String, String> map,
+                                             QNamesMap elementsMap,
+                                             Map<String, String> nsMap) {
         if (map != null) {
             for (Map.Entry<String, String> entry : map.entrySet()) {
                 QName lname = convertStringToQName(entry.getKey());
@@ -644,6 +644,17 @@
         }
     }
     
+    protected static void convertToMapOfQNames(Map<String, String> map,
+                                               Map<QName, QName> elementsMap) {
+        if (map != null) {
+            for (Map.Entry<String, String> entry : map.entrySet()) {
+                QName lname = convertStringToQName(entry.getKey());
+                QName rname = convertStringToQName(entry.getValue());
+                elementsMap.put(lname, rname);
+            }
+        }
+    }
+    
     
     public void setAttributesToElements(boolean value) {
         this.attributesToElements = value;
@@ -703,7 +714,7 @@
     }
     
     protected static class OutTransformWriter extends DelegatingXMLStreamWriter {
-        private Map<QName, QName> elementsMap = new HashMap<QName, QName>(5);
+        private QNamesMap elementsMap;
         private Map<QName, QName> appendMap = new HashMap<QName, QName>(5);
         private Map<String, String> nsMap = new HashMap<String, String>(5);
         private Set<String> prefixes = new HashSet<String>(2);
@@ -722,8 +733,9 @@
                                   List<String> dropEls,
                                   boolean attributesToElements) {
             super(writer);
-            convertToMapOfQNames(outMap, elementsMap, nsMap);
-            convertToMapOfQNames(append, appendMap, null);
+            elementsMap = new QNamesMap(outMap == null ? 0 : outMap.size());
+            convertToQNamesMap(outMap, elementsMap, nsMap);
+            convertToMapOfQNames(append, appendMap);
             dropElements = convertToSetOfQNames(dropEls);
             this.attributesToElements = attributesToElements;
         }
@@ -869,12 +881,44 @@
         }
     }
     
+    private static class QNamesMap {
+        private QName[] keys;
+        private QName[] values;
+        private int index;
+        
+        public QNamesMap(int size) {
+            keys = new QName[size];
+            values = new QName[size];
+        }
+        
+        public void put(QName key, QName value) {
+            keys[index] = key;
+            values[index] = value;
+            index++;
+        }
+        
+        public QName get(QName key) {
+            for (int i = 0; i < keys.length; i++) {
+                if (keys[i].getNamespaceURI().equals(key.getNamespaceURI())) {
+                    if (keys[i].getLocalPart().equals(key.getLocalPart())) {
+                        return values[i];
+                    } else if ("*".equals(keys[i].getLocalPart())) {
+                        // assume it is something like {somens}* : *
+                        return "*".equals(values[i]) ? new QName(key.getLocalPart()) 
+                            : new QName(values[i].getNamespaceURI(), key.getLocalPart());
+                    }
+                }
+            }
+            return null;    
+        }
+    }
+    
     protected static class InTransformReader extends DepthXMLStreamReader {
         
         private static final String INTERN_NAMES = "org.codehaus.stax2.internNames";
         private static final String INTERN_NS = "org.codehaus.stax2.internNsUris";
         
-        private Map<QName, QName> inElementsMap = new HashMap<QName, QName>(5);
+        private QNamesMap inElementsMap;
         private Map<QName, QName> inAppendMap = new HashMap<QName, QName>(5);
         private Map<String, String> nsMap = new HashMap<String, String>(5);
         private QName currentQName;
@@ -885,8 +929,9 @@
                                  Map<String, String> inMap,
                                  Map<String, String> appendMap) {
             super(reader);
-            convertToMapOfQNames(inMap, inElementsMap, nsMap);
-            convertToMapOfQNames(appendMap, inAppendMap, null);
+            inElementsMap = new QNamesMap(inMap == null ? 0 : inMap.size());
+            convertToQNamesMap(inMap, inElementsMap, nsMap);
+            convertToMapOfQNames(appendMap, inAppendMap);
         }
         
         public int next() throws XMLStreamException {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java Mon Feb  8 16:41:11 2010
@@ -19,8 +19,6 @@
 package org.apache.cxf.jaxrs.provider;
 
 import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
@@ -270,18 +268,8 @@
     
     protected Templates createTemplates(String loc) {
         try {
-            InputStream is = null;
-            if (loc.startsWith("classpath:")) {
-                String path = loc.substring("classpath:".length());
-                is = ResourceUtils.getClasspathResourceStream(path, this.getClass(), this.getBus());
-            } else {
-                File f = new File(loc);
-                if (f.exists()) {
-                    is = new FileInputStream(f);
-                }
-            }
+            InputStream is = ResourceUtils.getResourceStream(loc, this.getBus());
             if (is == null) {
-                LOG.warning("No template is available at : " + loc);
                 return null;
             }
             

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java Mon Feb  8 16:41:11 2010
@@ -872,21 +872,18 @@
                                           Message m, 
                                           String defaultValue,
                                           boolean decode) {
+        
+        MultivaluedMap<String, String> queryMap = new UriInfoImpl(m, null).getQueryParameters(decode);
+        
         if ("".equals(queryName)) {
-            return InjectionUtils.handleBean(paramType, new UriInfoImpl(m, null).getQueryParameters(),
-                                             ParameterType.QUERY, m, decode);
+            return InjectionUtils.handleBean(paramType, queryMap, ParameterType.QUERY, m, false);
         } else {
-            List<String> results = getStructuredParams((String)m.get(Message.QUERY_STRING),
-                                       "&",
-                                       decode).get(queryName);
-    
-            return InjectionUtils.createParameterObject(results, 
+            return InjectionUtils.createParameterObject(queryMap.get(queryName), 
                                                         paramType, 
                                                         genericType,
                                                         defaultValue,
                                                         false,
                                                         ParameterType.QUERY, m);
-             
         }
     }
 
@@ -900,9 +897,18 @@
     public static MultivaluedMap<String, String> getStructuredParams(String query, 
                                                                     String sep, 
                                                                     boolean decode) {
-        MultivaluedMap<String, String> queries = 
+        MultivaluedMap<String, String> map = 
             new MetadataMap<String, String>(new LinkedHashMap<String, List<String>>());
         
+        getStructuredParams(map, query, sep, decode);
+        
+        return map;
+    }
+    
+    public static void getStructuredParams(MultivaluedMap<String, String> queries,
+                                           String query, 
+                                           String sep, 
+                                           boolean decode) {
         if (!StringUtils.isEmpty(query)) {            
             List<String> parts = Arrays.asList(query.split(sep));
             for (String part : parts) {
@@ -919,7 +925,6 @@
                 queries.add(HttpUtils.urlDecode(values[0]), value);
             }
         }
-        return queries;
     }
 
     @SuppressWarnings("unchecked")

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java Mon Feb  8 16:41:11 2010
@@ -78,7 +78,7 @@
     
     private static final Logger LOG = LogUtils.getL7dLogger(ResourceUtils.class);
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(ResourceUtils.class);
-
+    private static final String CLASSPATH_PREFIX = "classpath:";
     
     private ResourceUtils() {
         
@@ -354,18 +354,8 @@
     
     public static List<UserResource> getUserResources(String loc, Bus bus) {
         try {
-            InputStream is = null;
-            if (loc.startsWith("classpath:")) {
-                String path = loc.substring("classpath:".length());
-                is = getClasspathResourceStream(path, ResourceUtils.class, bus);
-            } else {
-                File f = new File(loc);
-                if (f.exists()) {
-                    is = new FileInputStream(f);
-                }
-            }
+            InputStream is = ResourceUtils.getResourceStream(loc, bus);
             if (is == null) {
-                LOG.warning("No user model is available at " + loc);
                 return null;
             }
             return getUserResources(is);
@@ -376,6 +366,26 @@
         return null;
     }
     
+    public static InputStream getResourceStream(String loc, Bus bus) throws Exception {
+        InputStream is = null;
+        if (loc.startsWith(CLASSPATH_PREFIX)) {
+            String path = loc.substring(CLASSPATH_PREFIX.length());
+            is = ResourceUtils.getClasspathResourceStream(path, ResourceUtils.class, bus);
+            if (is == null) {
+                LOG.warning("No classpath resource " + loc + " is available on classpath");
+                return null;
+            }
+        } else {
+            File f = new File(loc);
+            if (!f.exists()) {
+                LOG.warning("No file resource " + loc + " is available on local disk");
+                return null;
+            }
+            is = new FileInputStream(f);
+        }
+        return is;
+    }
+    
     public static InputStream getClasspathResourceStream(String path, Class<?> callingClass, Bus bus) {
         InputStream is = ClassLoaderUtils.getResourceAsStream(path, callingClass);
         if (is == null && bus != null) {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java Mon Feb  8 16:41:11 2010
@@ -20,8 +20,6 @@
 package org.apache.cxf.jaxrs.utils.schemas;
 
 import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -43,7 +41,6 @@
 public class SchemaHandler {
 
     private static final Logger LOG = LogUtils.getL7dLogger(SchemaHandler.class);
-    private static final String CLASSPATH_PREFIX = "classpath:";
     
     private Schema schema;
     private Bus bus;
@@ -71,23 +68,10 @@
         try {
             List<Source> sources = new ArrayList<Source>();
             for (String loc : locations) {
-                InputStream is = null;
-                if (loc.startsWith(CLASSPATH_PREFIX)) {
-                    String path = loc.substring(CLASSPATH_PREFIX.length());
-                    is = ResourceUtils.getClasspathResourceStream(path, SchemaHandler.class, bus);
-                    if (is == null) {
-                        LOG.warning("No schema resource " + loc + " is available on classpath");
-                        return null;
-                    }
-                } else {
-                    File f = new File(loc);
-                    if (!f.exists()) {
-                        LOG.warning("No schema resource " + loc + " is available on local disk");
-                        return null;
-                    }
-                    is = new FileInputStream(f);
+                InputStream is = ResourceUtils.getResourceStream(loc, bus);
+                if (is == null) {
+                    return null;
                 }
-                                
                 Reader r = new BufferedReader(
                                new InputStreamReader(is, "UTF-8"));
                 sources.add(new StreamSource(r));

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/xml/XMLSourceTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/xml/XMLSourceTest.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/xml/XMLSourceTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/xml/XMLSourceTest.java Mon Feb  8 16:41:11 2010
@@ -21,16 +21,79 @@
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 import javax.xml.bind.annotation.XmlRootElement;
 
+import org.apache.cxf.common.xmlschema.XmlSchemaConstants;
+
 import org.junit.Assert;
 import org.junit.Test;
 
 public class XMLSourceTest extends Assert {
 
+    
+    @Test
+    public void testNodeStringValue() {
+        InputStream is = getClass().getResourceAsStream("/book1.xsd");
+        XMLSource xp = new XMLSource(is);
+        Map<String, String> nsMap = 
+            Collections.singletonMap("xs", XmlSchemaConstants.XSD_NAMESPACE_URI);
+        String value = xp.getNode("/xs:schema", nsMap, String.class);
+        assertFalse(value.contains("<?xml"));
+        assertTrue(value.startsWith("<xs:schema"));
+    }
+    
+    @Test
+    public void testAttributeValue() {
+        InputStream is = new ByteArrayInputStream("<foo><bar attr=\"baz\">barValue</bar></foo>".getBytes());
+        XMLSource xp = new XMLSource(is);
+        assertEquals("baz", xp.getValue("/foo/bar/@attr"));
+    }
+    
+    @Test
+    public void testNodeTextValue() {
+        InputStream is = new ByteArrayInputStream("<foo><bar attr=\"baz\">barValue</bar></foo>".getBytes());
+        XMLSource xp = new XMLSource(is);
+        assertEquals("barValue", xp.getValue("/foo/bar"));
+    }
+    
+    @Test
+    public void testAttributeValues() {
+        InputStream is = new ByteArrayInputStream(
+            "<foo><bar attr=\"baz\">bar1</bar><bar attr=\"baz2\">bar2</bar></foo>".getBytes());
+        XMLSource xp = new XMLSource(is);
+        List<String> values = Arrays.asList(xp.getValues("/foo/bar/@attr"));
+        assertEquals(2, values.size());
+        assertTrue(values.contains("baz"));
+        assertTrue(values.contains("baz2"));
+    }
+    
+    @Test
+    public void testNodeTextValues() {
+        InputStream is = new ByteArrayInputStream(
+            "<foo><bar attr=\"baz\">bar1</bar><bar attr=\"baz2\">bar2</bar></foo>".getBytes());
+        XMLSource xp = new XMLSource(is);
+        List<String> values = Arrays.asList(xp.getValues("/foo/bar/text()"));
+        assertEquals(2, values.size());
+        assertTrue(values.contains("bar1"));
+        assertTrue(values.contains("bar2"));
+    }
+    
+    @Test
+    public void testIntegerValues() {
+        InputStream is = new ByteArrayInputStream(
+            "<foo><bar attr=\"1\"/><bar attr=\"2\"/></foo>".getBytes());
+        XMLSource xp = new XMLSource(is);
+        Integer[] values = xp.getNodes("/foo/bar/@attr", Integer.class);
+        assertEquals(2, values.length);
+        assertTrue(values[0] == 1 && values[1] == 2 || values[0] == 2 && values[1] == 1);
+    }
+    
     @Test
     public void testGetNodeNoNamespace() {
         InputStream is = new ByteArrayInputStream("<foo><bar/></foo>".getBytes());

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestPreprocessorTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestPreprocessorTest.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestPreprocessorTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestPreprocessorTest.java Mon Feb  8 16:41:11 2010
@@ -113,6 +113,7 @@
                                 String method,
                                 String methodHeader) {
         Message m = new MessageImpl();
+        m.put("org.apache.cxf.http.case_insensitive_queries", false);
         Exchange e = new ExchangeImpl();
         m.setExchange(e);
         control.reset();

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java Mon Feb  8 16:41:11 2010
@@ -24,6 +24,7 @@
 
 import org.apache.cxf.jaxrs.model.URITemplate;
 import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageImpl;
 import org.apache.cxf.service.model.EndpointInfo;
@@ -87,10 +88,31 @@
         assertEquals("Wrong query value", qps.getFirst("n"), "1%202");
         
         u = new UriInfoImpl(mockMessage("http://localhost:8080/baz", "/bar", 
-                                        "n=1%202&n=3&b=2&a%2Eb=ab"),
+                                        "N=0&n=1%202&n=3&b=2&a%2Eb=ab"),
                             null);
 
         qps = u.getQueryParameters();
+        assertEquals("Number of queiries is wrong", 4, qps.size());
+        assertEquals("Wrong query value", qps.get("N").get(0), "0");
+        assertEquals("Wrong query value", qps.get("n").get(0), "1 2");
+        assertEquals("Wrong query value", qps.get("n").get(1), "3");
+        assertEquals("Wrong query value", qps.get("b").get(0), "2");
+        assertEquals("Wrong query value", qps.get("a.b").get(0), "ab");
+    }
+    
+    @Test
+    public void testGetCaseinsensitiveQueryParameters() {
+        UriInfoImpl u = new UriInfoImpl(mockMessage("http://localhost:8080/baz", "/bar"),
+                                        null);
+        assertEquals("unexpected queries", 0, u.getQueryParameters().size());
+        
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", 
+                                "N=1%202&n=3&b=2&a%2Eb=ab");
+        m.put("org.apache.cxf.http.case_insensitive_queries", "true");
+                
+        u = new UriInfoImpl(m, null);
+
+        MultivaluedMap<String, String> qps = u.getQueryParameters();
         assertEquals("Number of queiries is wrong", 3, qps.size());
         assertEquals("Wrong query value", qps.get("n").get(0), "1 2");
         assertEquals("Wrong query value", qps.get("n").get(1), "3");
@@ -207,11 +229,10 @@
                                 String query, String fragment) {
         Message m = new MessageImpl();
         control.reset();
-        Exchange e = control.createMock(Exchange.class);
+        Exchange e = new ExchangeImpl();
         m.setExchange(e);
         ServletDestination d = control.createMock(ServletDestination.class);
-        e.getDestination();
-        EasyMock.expectLastCall().andReturn(d).anyTimes();
+        e.setDestination(d);
         EndpointInfo epr = new EndpointInfo(); 
         epr.setAddress(baseAddress);
         d.getEndpointInfo();

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/BookStore.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/BookStore.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/BookStore.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/BookStore.java Mon Feb  8 16:41:11 2010
@@ -33,6 +33,7 @@
 import javax.ws.rs.core.HttpHeaders;
 
 import org.apache.cxf.jaxrs.ext.Description;
+import org.apache.cxf.jaxrs.ext.xml.XMLName;
 import org.apache.cxf.jaxrs.fortest.jaxb.packageinfo.Book2;
 import org.apache.cxf.jaxrs.model.wadl.jaxb.Book;
 import org.apache.cxf.jaxrs.model.wadl.jaxb.Chapter;
@@ -72,7 +73,8 @@
                         @CookieParam("cid") int cookieId,
                         @QueryParam("provider.bar") int queryParam,
                         @Context HttpHeaders headers,
-                        Book b) {
+                        @XMLName(value = "{http://books}thesuperbook2", prefix = "p1")
+                        Book2 b) {
         return new Book(1);
     }
     
@@ -105,6 +107,7 @@
     
     @Path("book2")
     @GET
+    @XMLName(value = "{http://books}thesuperbook2", prefix = "p1")
     public Book2 getBook2() {
         return new Book2();
     }

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/WadlGeneratorTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/WadlGeneratorTest.java?rev=907718&r1=907717&r2=907718&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/WadlGeneratorTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/WadlGeneratorTest.java Mon Feb  8 16:41:11 2010
@@ -20,6 +20,7 @@
 
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -64,8 +65,134 @@
     }
     
     @Test
+    public void testCustomSchemaJaxbContextPrefixes() throws Exception {
+        WadlGenerator wg = new WadlGenerator();
+        wg.setSchemaLocations(Collections.singletonList("classpath:/book1.xsd"));
+        
+        ClassResourceInfo cri = 
+            ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
+        
+        Response r = wg.handleRequest(m, cri);
+        checkResponse(r);
+        Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        checkGrammars(doc.getDocumentElement(), "thebook", "thebook2", "thechapter");
+        List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 1);
+        checkBookStoreInfo(els.get(0), "prefix1:thebook", "prefix1:thebook2", "prefix1:thechapter");
+    }
+    
+    @Test
+    public void testCustomSchemaWithImportJaxbContextPrefixes() throws Exception {
+        WadlGenerator wg = new WadlGenerator();
+        wg.setSchemaLocations(Collections.singletonList("classpath:/books.xsd"));
+        
+        ClassResourceInfo cri = 
+            ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
+        
+        Response r = wg.handleRequest(m, cri);
+        checkResponse(r);
+        Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        List<Element> grammarEls = DOMUtils.getChildrenWithName(doc.getDocumentElement(), 
+            WadlGenerator.WADL_NS, "grammars");
+        assertEquals(1, grammarEls.size());
+        List<Element> schemasEls = DOMUtils.getChildrenWithName(grammarEls.get(0), 
+            XmlSchemaConstants.XSD_NAMESPACE_URI, "schema");
+        assertEquals(1, schemasEls.size());
+        assertEquals("http://books", schemasEls.get(0).getAttribute("targetNamespace"));
+        List<Element> elementEls = DOMUtils.getChildrenWithName(schemasEls.get(0), 
+            XmlSchemaConstants.XSD_NAMESPACE_URI, "element");
+        assertEquals(1, elementEls.size());
+        assertTrue(checkElement(elementEls, "books", "tns:books"));
+
+        List<Element> complexTypesEls = DOMUtils.getChildrenWithName(schemasEls.get(0), 
+            XmlSchemaConstants.XSD_NAMESPACE_URI, "complexType");
+        assertEquals(1, complexTypesEls.size());
+        assertTrue(checkComplexType(complexTypesEls, "books"));
+        
+        List<Element> importEls = DOMUtils.getChildrenWithName(schemasEls.get(0), 
+            XmlSchemaConstants.XSD_NAMESPACE_URI, "import");
+        assertEquals(1, importEls.size());
+        assertEquals("http://localhost:8080/baz/book1.xsd",
+                     importEls.get(0).getAttribute("schemaLocation"));
+    }
+    
+    @Test
+    public void testExternalSchemaJaxbContextPrefixes() throws Exception {
+        WadlGenerator wg = new WadlGenerator();
+        wg.setExternalLinks(Collections.singletonList("http://books.xsd"));
+        
+        ClassResourceInfo cri = 
+            ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
+        
+        Response r = wg.handleRequest(m, cri);
+        checkResponse(r);
+        Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        checkGrammarsWithLinks(doc.getDocumentElement(), Collections.singletonList("http://books.xsd"));
+        List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 1);
+        checkBookStoreInfo(els.get(0), "prefix1:thebook", "prefix1:thebook2", "prefix1:thechapter");
+    }
+    
+    @Test
+    public void testExternalRelativeSchemaJaxbContextPrefixes() throws Exception {
+        WadlGenerator wg = new WadlGenerator();
+        wg.setExternalLinks(Collections.singletonList("books.xsd"));
+        
+        ClassResourceInfo cri = 
+            ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
+        
+        Response r = wg.handleRequest(m, cri);
+        checkResponse(r);
+        Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        checkGrammarsWithLinks(doc.getDocumentElement(), 
+                               Collections.singletonList("http://localhost:8080/baz/books.xsd"));
+        List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 1);
+        checkBookStoreInfo(els.get(0), "prefix1:thebook", "prefix1:thebook2", "prefix1:thechapter");
+    }
+    
+    @Test
+    public void testExternalSchemaCustomPrefix() throws Exception {
+        WadlGenerator wg = new WadlGenerator();
+        wg.setExternalLinks(Collections.singletonList("http://books"));
+        wg.setUseJaxbContextForQnames(false);
+        
+        ClassResourceInfo cri = 
+            ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
+        
+        Response r = wg.handleRequest(m, cri);
+        checkResponse(r);
+        Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        checkGrammarsWithLinks(doc.getDocumentElement(), 
+                               Collections.singletonList("http://books"));
+        List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 1);
+        checkBookStoreInfo(els.get(0), "p1:thesuperbook", "p1:thesuperbook2", "p1:thesuperchapter");
+    }
+    
+    @Test
+    public void testCustomSchemaAndSchemaPrefixes() throws Exception {
+        WadlGenerator wg = new WadlGenerator();
+        wg.setSchemaLocations(Collections.singletonList("classpath:/book2.xsd"));
+        wg.setUseJaxbContextForQnames(false);
+        
+        ClassResourceInfo cri = 
+            ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
+        Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
+        
+        Response r = wg.handleRequest(m, cri);
+        checkResponse(r);
+        Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        checkGrammars(doc.getDocumentElement(), "book", "book2", "chapter");
+        List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 1);
+        checkBookStoreInfo(els.get(0), "prefix1:book", "prefix1:book2", "prefix1:chapter");
+    }
+    
+    @Test
     public void testSingleRootResource() throws Exception {
         WadlGenerator wg = new WadlGenerator();
+        
         ClassResourceInfo cri = 
             ResourceUtils.createClassResourceInfo(BookStore.class, BookStore.class, true, true);
         Message m = mockMessage("http://localhost:8080/baz", "/bar", WadlGenerator.WADL_QUERY, null);
@@ -73,8 +200,9 @@
         Response r = wg.handleRequest(m, cri);
         checkResponse(r);
         Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
+        checkGrammars(doc.getDocumentElement(), "thebook", "thebook2", "thechapter");
         List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 1);
-        checkBookStoreInfo(els.get(0));
+        checkBookStoreInfo(els.get(0), "prefix1:thebook", "prefix1:thebook2", "prefix1:thechapter");
         
     }
     
@@ -127,14 +255,14 @@
         Response r = wg.handleRequest(m, null);
         checkResponse(r);
         Document doc = DOMUtils.readXml(new StringReader(r.getEntity().toString()));
-        checkGrammars(doc.getDocumentElement());
+        checkGrammars(doc.getDocumentElement(), "thebook", "thebook2", "thechapter");
         List<Element> els = getWadlResourcesInfo(doc, "http://localhost:8080/baz", 2);
-        checkBookStoreInfo(els.get(0));
+        checkBookStoreInfo(els.get(0), "prefix1:thebook", "prefix1:thebook2", "prefix1:thechapter");
         Element orderResource = els.get(1);
         assertEquals("/orders", orderResource.getAttribute("path"));
     }
 
-    private void checkGrammars(Element appElement) {
+    private void checkGrammars(Element appElement, String bookEl, String book2El, String chapterEl) {
         List<Element> grammarEls = DOMUtils.getChildrenWithName(appElement, WadlGenerator.WADL_NS, 
                                                                 "grammars");
         assertEquals(1, grammarEls.size());
@@ -145,9 +273,9 @@
         List<Element> elementEls = DOMUtils.getChildrenWithName(schemasEls.get(0), 
                             XmlSchemaConstants.XSD_NAMESPACE_URI, "element");
         assertEquals(3, elementEls.size());
-        assertTrue(checkElement(elementEls, "thebook", "tns:book"));
-        assertTrue(checkElement(elementEls, "thebook2", "tns:book2"));
-        assertTrue(checkElement(elementEls, "thechapter", "tns:chapter"));
+        assertTrue(checkElement(elementEls, bookEl, "tns:book"));
+        assertTrue(checkElement(elementEls, book2El, "tns:book2"));
+        assertTrue(checkElement(elementEls, chapterEl, "tns:chapter"));
         
         List<Element> complexTypesEls = DOMUtils.getChildrenWithName(schemasEls.get(0), 
                                         XmlSchemaConstants.XSD_NAMESPACE_URI, "complexType");
@@ -158,6 +286,23 @@
         assertTrue(checkComplexType(complexTypesEls, "chapter"));
     }
     
+    private void checkGrammarsWithLinks(Element appElement, List<String> links) {
+        assertTrue(links.size() > 0);
+        List<Element> grammarEls = DOMUtils.getChildrenWithName(appElement, WadlGenerator.WADL_NS, 
+                                                                "grammars");
+        assertEquals(1, grammarEls.size());
+        List<Element> schemasEls = DOMUtils.getChildrenWithName(grammarEls.get(0), 
+                                                          XmlSchemaConstants.XSD_NAMESPACE_URI, "schema");
+        assertEquals(0, schemasEls.size());
+        
+        List<Element> includeEls = DOMUtils.getChildrenWithName(grammarEls.get(0), WadlGenerator.WADL_NS,
+                                                                "include");
+        assertEquals(links.size(), includeEls.size());
+        for (Element el : includeEls) {
+            assertTrue(links.contains(el.getAttribute("href")));
+        }
+    }
+    
     private boolean checkComplexType(List<Element> els, String name) {
         for (Element e : els) {
             if (name.equals(e.getAttribute("name"))) {
@@ -177,31 +322,30 @@
         return false;
     }
     
-    private void checkBookStoreInfo(Element resource) {
+    private void checkBookStoreInfo(Element resource, String bookEl, String book2El, String chapterEl) {
         assertEquals("/bookstore/{id}", resource.getAttribute("path"));
         
         checkRootDocs(resource);
         
         List<Element> resourceEls = DOMUtils.getChildrenWithName(resource, 
                                          WadlGenerator.WADL_NS, "resource");
-        assertEquals(7, resourceEls.size());        
-        assertEquals("/", resourceEls.get(0).getAttribute("path"));
-        assertEquals("/book2", resourceEls.get(1).getAttribute("path"));
-        assertEquals("/books/{bookid}", resourceEls.get(2).getAttribute("path"));
-        assertEquals("/chapter", resourceEls.get(3).getAttribute("path"));
-        assertEquals("/books/{bookid}", resourceEls.get(4).getAttribute("path"));
-        assertEquals("/booksubresource", resourceEls.get(5).getAttribute("path"));
-        assertEquals("/itself", resourceEls.get(6).getAttribute("path"));
+        assertEquals(6, resourceEls.size());        
+        assertEquals("/book2", resourceEls.get(0).getAttribute("path"));
+        assertEquals("/books/{bookid}", resourceEls.get(1).getAttribute("path"));
+        assertEquals("/chapter", resourceEls.get(2).getAttribute("path"));
+        assertEquals("/books/{bookid}", resourceEls.get(3).getAttribute("path"));
+        assertEquals("/booksubresource", resourceEls.get(4).getAttribute("path"));
+        assertEquals("/itself", resourceEls.get(5).getAttribute("path"));
         
-        // verify repource starting with "/"
+        // verify root resource starting with "/"
         // must have a single template parameter
-        List<Element> paramsEls = DOMUtils.getChildrenWithName(resourceEls.get(0), 
+        List<Element> paramsEls = DOMUtils.getChildrenWithName(resource, 
                                                                WadlGenerator.WADL_NS, "param");
         assertEquals(1, paramsEls.size());
         checkParameter(paramsEls.get(0), "id", "template", "xs:long");
         
         // must have 2 methods, GET and PUT
-        List<Element> methodEls = DOMUtils.getChildrenWithName(resourceEls.get(0), 
+        List<Element> methodEls = DOMUtils.getChildrenWithName(resource, 
                                                                WadlGenerator.WADL_NS, "method");
         assertEquals(2, methodEls.size());
         
@@ -236,10 +380,10 @@
         verifyResponseWithStatus(methodEls.get(1), "204");
         
         // verify resource starting with /book2
-        verifyGetResourceMethod(resourceEls.get(1), "prefix1:thebook2");
+        verifyGetResourceMethod(resourceEls.get(0), book2El);
         
         //verify resource starting with /books/{bookid}
-        paramsEls = DOMUtils.getChildrenWithName(resourceEls.get(2), 
+        paramsEls = DOMUtils.getChildrenWithName(resourceEls.get(1), 
                                                                WadlGenerator.WADL_NS, "param");
         // should have 3 parameters
         assertEquals(3, paramsEls.size());
@@ -248,7 +392,7 @@
         checkParameter(paramsEls.get(2), "mid", "matrix", "xs:int");
         
         // and 2 methods
-        methodEls = DOMUtils.getChildrenWithName(resourceEls.get(2), 
+        methodEls = DOMUtils.getChildrenWithName(resourceEls.get(1), 
                                                  WadlGenerator.WADL_NS, "method");
         assertEquals(2, methodEls.size());
         
@@ -264,29 +408,29 @@
         assertEquals(2, paramsEls.size());
         checkParameter(paramsEls.get(0), "hid", "header", "xs:int");
         checkParameter(paramsEls.get(1), "provider.bar", "query", "xs:int");
-        verifyXmlJsonRepresentations(requestEls.get(0), "prefix1:thebook");
+        verifyXmlJsonRepresentations(requestEls.get(0), book2El);
         
         // PUT
         assertEquals("PUT", methodEls.get(1).getAttribute("name"));
         requestEls = DOMUtils.getChildrenWithName(methodEls.get(1), 
                                                                 WadlGenerator.WADL_NS, "request");
         assertEquals(1, requestEls.size());
-        verifyXmlJsonRepresentations(requestEls.get(0), "prefix1:thebook");
+        verifyXmlJsonRepresentations(requestEls.get(0), bookEl);
         verifyResponseWithStatus(methodEls.get(1), "204");
         
         // verify resource starting with /chapter
-        verifyGetResourceMethod(resourceEls.get(3), "prefix1:thechapter");
+        verifyGetResourceMethod(resourceEls.get(2), chapterEl);
         
         // verify resource starting from /booksubresource
         // should have 2 parameters
-        paramsEls = DOMUtils.getChildrenWithName(resourceEls.get(5), 
+        paramsEls = DOMUtils.getChildrenWithName(resourceEls.get(4), 
                          WadlGenerator.WADL_NS, "param");
         assertEquals(2, paramsEls.size());
         checkParameter(paramsEls.get(0), "id", "template", "xs:int");
         checkParameter(paramsEls.get(1), "mid", "matrix", "xs:int");
         
         // should have 4 child resources
-        List<Element> subResourceEls = DOMUtils.getChildrenWithName(resourceEls.get(5), 
+        List<Element> subResourceEls = DOMUtils.getChildrenWithName(resourceEls.get(4), 
                                          WadlGenerator.WADL_NS, "resource");
         assertEquals(4, subResourceEls.size());        
         assertEquals("/book", subResourceEls.get(0).getAttribute("path"));
@@ -295,7 +439,7 @@
         assertEquals("/chapter/{cid}", subResourceEls.get(3).getAttribute("path"));
         // verify subresource /book
         // GET 
-        verifyGetResourceMethod(subResourceEls.get(0), "prefix1:thebook");
+        verifyGetResourceMethod(subResourceEls.get(0), bookEl);
         
         // verify subresource /chapter/{id}
         List<Element> chapterMethodEls = DOMUtils.getChildrenWithName(subResourceEls.get(3), 
@@ -307,7 +451,7 @@
         assertEquals(1, paramsEls.size());
         checkParameter(paramsEls.get(0), "cid", "template", "xs:int");
         // GET
-        verifyGetResourceMethod(chapterMethodEls.get(0), "prefix1:thechapter");
+        verifyGetResourceMethod(chapterMethodEls.get(0), chapterEl);
     }
     
     private void checkRootDocs(Element el) {