1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.ws.server;
  27 
  28 import com.sun.istack.internal.Nullable;
  29 import com.sun.xml.internal.ws.api.server.*;
  30 import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;
  31 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
  32 import com.sun.xml.internal.ws.wsdl.SDDocumentResolver;
  33 import com.sun.xml.internal.ws.util.RuntimeVersion;
  34 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderToXMLStreamWriter;
  35 import com.sun.xml.internal.ws.wsdl.parser.ParserUtil;
  36 import com.sun.xml.internal.ws.wsdl.parser.WSDLConstants;
  37 import com.sun.xml.internal.ws.wsdl.writer.DocumentLocationResolver;
  38 import com.sun.xml.internal.ws.wsdl.writer.WSDLPatcher;
  39 
  40 import javax.xml.namespace.QName;
  41 import javax.xml.stream.*;
  42 import javax.xml.ws.WebServiceException;
  43 import java.io.IOException;
  44 import java.io.OutputStream;
  45 import java.net.MalformedURLException;
  46 import java.net.URL;
  47 import java.util.HashSet;
  48 import java.util.List;
  49 import java.util.Set;
  50 
  51 /**
  52  * {@link SDDocument} implmentation.
  53  *
  54  * <p>
  55  * This extends from {@link SDDocumentSource} so that
  56  * JAX-WS server runtime code can use {@link SDDocument}
  57  * as {@link SDDocumentSource}.
  58  *
  59  * @author Kohsuke Kawaguchi
  60  * @author Jitendra Kotamraju
  61  */
  62 public class SDDocumentImpl extends SDDocumentSource implements SDDocument {
  63 
  64     private static final String NS_XSD = "http://www.w3.org/2001/XMLSchema";
  65     private static final QName SCHEMA_INCLUDE_QNAME = new QName(NS_XSD, "include");
  66     private static final QName SCHEMA_IMPORT_QNAME = new QName(NS_XSD, "import");
  67     private static final QName SCHEMA_REDEFINE_QNAME = new QName(NS_XSD, "redefine");
  68     private static final String VERSION_COMMENT =
  69         " Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is "+RuntimeVersion.VERSION+". ";
  70 
  71     private final QName rootName;
  72     private final SDDocumentSource source;
  73 
  74     /**
  75      * Set when {@link ServiceDefinitionImpl} is constructed.
  76      */
  77     @Nullable List<SDDocumentFilter> filters;
  78     @Nullable SDDocumentResolver sddocResolver;
  79 
  80 
  81     /**
  82      * The original system ID of this document.
  83      *
  84      * When this document contains relative references to other resources,
  85      * this field is used to find which {@link com.sun.xml.internal.ws.server.SDDocumentImpl} it refers to.
  86      *
  87      * Must not be null.
  88      */
  89     private final URL url;
  90     private final Set<String> imports;
  91 
  92     /**
  93      * Creates {@link SDDocument} from {@link SDDocumentSource}.
  94      * @param src WSDL document infoset
  95      * @param serviceName wsdl:service name
  96      * @param portTypeName
  97      *      The information about the port of {@link WSEndpoint} to which this document is built for.
  98      *      These values are used to determine which document is the concrete and abstract WSDLs
  99      *      for this endpoint.
 100      *
 101      * @return null
 102      *      Always non-null.
 103      */
 104     public static SDDocumentImpl create(SDDocumentSource src, QName serviceName, QName portTypeName) {
 105         URL systemId = src.getSystemId();
 106 
 107         try {
 108             // RuntimeWSDLParser parser = new RuntimeWSDLParser(null);
 109             XMLStreamReader reader = src.read();
 110             try {
 111                 XMLStreamReaderUtil.nextElementContent(reader);
 112 
 113                 QName rootName = reader.getName();
 114                 if(rootName.equals(WSDLConstants.QNAME_SCHEMA)) {
 115                     String tns = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_TNS);
 116                     Set<String> importedDocs = new HashSet<String>();
 117                     while (XMLStreamReaderUtil.nextContent(reader) != XMLStreamConstants.END_DOCUMENT) {
 118                          if (reader.getEventType() != XMLStreamConstants.START_ELEMENT)
 119                             continue;
 120                         QName name = reader.getName();
 121                         if (SCHEMA_INCLUDE_QNAME.equals(name) || SCHEMA_IMPORT_QNAME.equals(name) ||
 122                                 SCHEMA_REDEFINE_QNAME.equals(name)) {
 123                             String importedDoc = reader.getAttributeValue(null, "schemaLocation");
 124                             if (importedDoc != null) {
 125                                 importedDocs.add(new URL(src.getSystemId(), importedDoc).toString());
 126                             }
 127                         }
 128                     }
 129                     return new SchemaImpl(rootName,systemId,src,tns,importedDocs);
 130                 } else if (rootName.equals(WSDLConstants.QNAME_DEFINITIONS)) {
 131                     String tns = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_TNS);
 132 
 133                     boolean hasPortType = false;
 134                     boolean hasService = false;
 135                     Set<String> importedDocs = new HashSet<String>();
 136                     Set<QName> allServices = new HashSet<QName>();
 137 
 138                     // if WSDL, parse more
 139                     while (XMLStreamReaderUtil.nextContent(reader) != XMLStreamConstants.END_DOCUMENT) {
 140                          if(reader.getEventType() != XMLStreamConstants.START_ELEMENT)
 141                             continue;
 142 
 143                         QName name = reader.getName();
 144                         if (WSDLConstants.QNAME_PORT_TYPE.equals(name)) {
 145                             String pn = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
 146                             if (portTypeName != null) {
 147                                 if(portTypeName.getLocalPart().equals(pn)&&portTypeName.getNamespaceURI().equals(tns)) {
 148                                     hasPortType = true;
 149                                 }
 150                             }
 151                         } else if (WSDLConstants.QNAME_SERVICE.equals(name)) {
 152                             String sn = ParserUtil.getMandatoryNonEmptyAttribute(reader, WSDLConstants.ATTR_NAME);
 153                             QName sqn = new QName(tns,sn);
 154                             allServices.add(sqn);
 155                             if(serviceName.equals(sqn)) {
 156                                 hasService = true;
 157                             }
 158                         } else if (WSDLConstants.QNAME_IMPORT.equals(name)) {
 159                             String importedDoc = reader.getAttributeValue(null, "location");
 160                             if (importedDoc != null) {
 161                                 importedDocs.add(new URL(src.getSystemId(), importedDoc).toString());
 162                             }
 163                         } else if (SCHEMA_INCLUDE_QNAME.equals(name) || SCHEMA_IMPORT_QNAME.equals(name) ||
 164                                 SCHEMA_REDEFINE_QNAME.equals(name)) {
 165                             String importedDoc = reader.getAttributeValue(null, "schemaLocation");
 166                             if (importedDoc != null) {
 167                                 importedDocs.add(new URL(src.getSystemId(), importedDoc).toString());
 168                             }
 169                         }
 170                     }
 171                     return new WSDLImpl(
 172                         rootName,systemId,src,tns,hasPortType,hasService,importedDocs,allServices);
 173                 } else {
 174                     return new SDDocumentImpl(rootName,systemId,src);
 175                 }
 176             } finally {
 177                 reader.close();
 178             }
 179         } catch (WebServiceException e) {
 180             throw new ServerRtException("runtime.parser.wsdl", systemId,e);
 181         } catch (IOException e) {
 182             throw new ServerRtException("runtime.parser.wsdl", systemId,e);
 183         } catch (XMLStreamException e) {
 184             throw new ServerRtException("runtime.parser.wsdl", systemId,e);
 185         }
 186     }
 187 
 188     protected SDDocumentImpl(QName rootName, URL url, SDDocumentSource source) {
 189         this(rootName, url, source, new HashSet<String>());
 190     }
 191 
 192     protected SDDocumentImpl(QName rootName, URL url, SDDocumentSource source, Set<String> imports) {
 193         if (url == null) {
 194             throw new IllegalArgumentException("Cannot construct SDDocument with null URL.");
 195         }
 196         this.rootName = rootName;
 197         this.source = source;
 198         this.url = url;
 199         this.imports = imports;
 200     }
 201 
 202     void setFilters(List<SDDocumentFilter> filters) {
 203         this.filters = filters;
 204     }
 205 
 206     void setResolver(SDDocumentResolver sddocResolver) {
 207         this.sddocResolver = sddocResolver;
 208     }
 209 
 210     public QName getRootName() {
 211         return rootName;
 212     }
 213 
 214     public boolean isWSDL() {
 215         return false;
 216     }
 217 
 218     public boolean isSchema() {
 219         return false;
 220     }
 221 
 222     public URL getURL() {
 223         return url;
 224     }
 225 
 226     public XMLStreamReader read(XMLInputFactory xif) throws IOException, XMLStreamException {
 227         return source.read(xif);
 228     }
 229 
 230     public XMLStreamReader read() throws IOException, XMLStreamException {
 231         return source.read();
 232     }
 233 
 234     public URL getSystemId() {
 235         return url;
 236     }
 237 
 238     public Set<String> getImports() {
 239         return imports;
 240     }
 241 
 242     public void writeTo(OutputStream os) throws IOException {
 243         XMLStreamWriter w = null;
 244         try {
 245             //generate the WSDL with utf-8 encoding and XML version 1.0
 246             w = XMLStreamWriterFactory.create(os, "UTF-8");
 247             w.writeStartDocument("UTF-8", "1.0");
 248             new XMLStreamReaderToXMLStreamWriter().bridge(source.read(), w);
 249             w.writeEndDocument();
 250         } catch (XMLStreamException e) {
 251             IOException ioe = new IOException(e.getMessage());
 252             ioe.initCause(e);
 253             throw ioe;
 254         } finally {
 255             try {
 256                 if (w != null)
 257                     w.close();
 258             } catch (XMLStreamException e) {
 259                 IOException ioe = new IOException(e.getMessage());
 260                 ioe.initCause(e);
 261                 throw ioe;
 262             }
 263         }
 264     }
 265 
 266 
 267     public void writeTo(PortAddressResolver portAddressResolver, DocumentAddressResolver resolver, OutputStream os) throws IOException {
 268         XMLStreamWriter w = null;
 269         try {
 270             //generate the WSDL with utf-8 encoding and XML version 1.0
 271             w = XMLStreamWriterFactory.create(os, "UTF-8");
 272             w.writeStartDocument("UTF-8", "1.0");
 273             writeTo(portAddressResolver,resolver,w);
 274             w.writeEndDocument();
 275         } catch (XMLStreamException e) {
 276             IOException ioe = new IOException(e.getMessage());
 277             ioe.initCause(e);
 278             throw ioe;
 279         } finally {
 280             try {
 281                 if (w != null)
 282                     w.close();
 283             } catch (XMLStreamException e) {
 284                 IOException ioe = new IOException(e.getMessage());
 285                 ioe.initCause(e);
 286                 throw ioe;
 287             }
 288         }
 289     }
 290 
 291     public void writeTo(PortAddressResolver portAddressResolver, DocumentAddressResolver resolver, XMLStreamWriter out) throws XMLStreamException, IOException {
 292         if (filters != null) {
 293             for (SDDocumentFilter f : filters) {
 294                 out = f.filter(this,out);
 295             }
 296         }
 297 
 298         XMLStreamReader xsr = source.read();
 299         try {
 300             out.writeComment(VERSION_COMMENT);
 301             new WSDLPatcher(portAddressResolver, new DocumentLocationResolverImpl(resolver)).bridge(xsr,out);
 302         } finally {
 303             xsr.close();
 304         }
 305     }
 306 
 307 
 308     /**
 309      * {@link SDDocument.Schema} implementation.
 310      *
 311      * @author Kohsuke Kawaguchi
 312      */
 313     private static final class SchemaImpl extends SDDocumentImpl implements SDDocument.Schema {
 314         private final String targetNamespace;
 315 
 316         public SchemaImpl(QName rootName, URL url, SDDocumentSource source, String targetNamespace,
 317                           Set<String> imports) {
 318             super(rootName, url, source, imports);
 319             this.targetNamespace = targetNamespace;
 320         }
 321 
 322         public String getTargetNamespace() {
 323             return targetNamespace;
 324         }
 325 
 326         public boolean isSchema() {
 327             return true;
 328         }
 329     }
 330 
 331 
 332     private static final class WSDLImpl extends SDDocumentImpl implements SDDocument.WSDL {
 333         private final String targetNamespace;
 334         private final boolean hasPortType;
 335         private final boolean hasService;
 336         private final Set<QName> allServices;
 337 
 338         public WSDLImpl(QName rootName, URL url, SDDocumentSource source, String targetNamespace, boolean hasPortType,
 339                         boolean hasService, Set<String> imports,Set<QName> allServices) {
 340             super(rootName, url, source, imports);
 341             this.targetNamespace = targetNamespace;
 342             this.hasPortType = hasPortType;
 343             this.hasService = hasService;
 344             this.allServices = allServices;
 345         }
 346 
 347         public String getTargetNamespace() {
 348             return targetNamespace;
 349         }
 350 
 351         public boolean hasPortType() {
 352             return hasPortType;
 353         }
 354 
 355         public boolean hasService() {
 356             return hasService;
 357         }
 358 
 359         public Set<QName> getAllServices() {
 360             return allServices;
 361         }
 362 
 363         public boolean isWSDL() {
 364             return true;
 365         }
 366     }
 367 
 368     private class DocumentLocationResolverImpl implements DocumentLocationResolver {
 369         private DocumentAddressResolver delegate;
 370 
 371         DocumentLocationResolverImpl(DocumentAddressResolver delegate) {
 372             this.delegate = delegate;
 373         }
 374 
 375         public String getLocationFor(String namespaceURI, String systemId) {
 376             if (sddocResolver == null) {
 377                 return systemId;
 378             }
 379             try {
 380                 URL ref = new URL(getURL(), systemId);
 381                 SDDocument refDoc = sddocResolver.resolve(ref.toExternalForm());
 382                 if (refDoc == null)
 383                     return systemId;  // not something we know. just leave it as is.
 384 
 385                 return delegate.getRelativeAddressFor(SDDocumentImpl.this, refDoc);
 386             } catch (MalformedURLException mue) {
 387                 return null;
 388             }
 389         }
 390     }
 391 
 392 }