1/*
2 * Copyright (c) 1997, 2014, 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
26package com.sun.xml.internal.ws.server;
27
28import com.sun.istack.internal.Nullable;
29import com.sun.xml.internal.ws.api.server.*;
30import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;
31import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
32import com.sun.xml.internal.ws.wsdl.SDDocumentResolver;
33import com.sun.xml.internal.ws.util.RuntimeVersion;
34import com.sun.xml.internal.org.jvnet.staxex.util.XMLStreamReaderToXMLStreamWriter;
35import com.sun.xml.internal.ws.wsdl.parser.ParserUtil;
36import com.sun.xml.internal.ws.wsdl.parser.WSDLConstants;
37import com.sun.xml.internal.ws.wsdl.writer.DocumentLocationResolver;
38import com.sun.xml.internal.ws.wsdl.writer.WSDLPatcher;
39
40import javax.xml.namespace.QName;
41import javax.xml.stream.*;
42import javax.xml.ws.WebServiceException;
43import java.io.IOException;
44import java.io.OutputStream;
45import java.net.MalformedURLException;
46import java.net.URL;
47import java.util.HashSet;
48import java.util.List;
49import 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 */
62public 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 (http://jax-ws.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}
393