1 /* 2 * Copyright (c) 1997, 2010, 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.tools.internal.ws.wsdl.parser; 27 28 import com.sun.istack.internal.NotNull; 29 import com.sun.istack.internal.Nullable; 30 import com.sun.tools.internal.ws.resources.WscompileMessages; 31 import com.sun.tools.internal.ws.resources.WsdlMessages; 32 import com.sun.tools.internal.ws.wscompile.AbortException; 33 import com.sun.tools.internal.ws.wscompile.DefaultAuthenticator; 34 import com.sun.tools.internal.ws.wscompile.ErrorReceiver; 35 import com.sun.tools.internal.ws.wscompile.WsimportOptions; 36 import com.sun.tools.internal.ws.wsdl.document.WSDLConstants; 37 import com.sun.tools.internal.ws.wsdl.document.schema.SchemaConstants; 38 import com.sun.tools.internal.ws.wsdl.framework.ParseException; 39 import com.sun.xml.internal.ws.api.wsdl.parser.MetaDataResolver; 40 import com.sun.xml.internal.ws.api.wsdl.parser.MetadataResolverFactory; 41 import com.sun.xml.internal.ws.api.wsdl.parser.ServiceDescriptor; 42 import com.sun.xml.internal.ws.util.DOMUtil; 43 import com.sun.xml.internal.ws.util.JAXWSUtils; 44 import com.sun.xml.internal.ws.util.ServiceFinder; 45 import org.w3c.dom.Document; 46 import org.w3c.dom.Element; 47 import org.w3c.dom.Node; 48 import org.w3c.dom.NodeList; 49 import org.xml.sax.EntityResolver; 50 import org.xml.sax.InputSource; 51 import org.xml.sax.SAXException; 52 import org.xml.sax.SAXParseException; 53 54 import javax.net.ssl.HostnameVerifier; 55 import javax.net.ssl.HttpsURLConnection; 56 import javax.net.ssl.SSLSession; 57 import javax.xml.transform.Source; 58 import javax.xml.transform.dom.DOMSource; 59 import java.io.FileNotFoundException; 60 import java.io.IOException; 61 import java.io.InputStream; 62 import java.net.*; 63 import java.util.*; 64 65 /** 66 * @author Vivek Pandey 67 */ 68 public final class MetadataFinder extends DOMForest{ 69 70 public boolean isMexMetadata; 71 private String rootWSDL; 72 private Set<String> rootWsdls = new HashSet<String>(); 73 74 public MetadataFinder(InternalizationLogic logic, WsimportOptions options, ErrorReceiver errReceiver) { 75 super(logic, new WSEntityResolver(options,errReceiver), options, errReceiver); 76 77 } 78 79 public void parseWSDL(){ 80 // parse source grammars 81 for (InputSource value : options.getWSDLs()) { 82 String systemID = value.getSystemId(); 83 errorReceiver.pollAbort(); 84 85 Document dom ; 86 Element doc = null; 87 88 try { 89 //if there is entity resolver use it 90 if (options.entityResolver != null) 91 value = options.entityResolver.resolveEntity(null, systemID); 92 if (value == null) 93 value = new InputSource(systemID); 94 dom = parse(value, true); 95 96 doc = dom.getDocumentElement(); 97 if (doc == null) { 98 continue; 99 } 100 //if its not a WSDL document, retry with MEX 101 if (doc.getNamespaceURI() == null || !doc.getNamespaceURI().equals(WSDLConstants.NS_WSDL) || !doc.getLocalName().equals("definitions")) { 102 throw new SAXParseException(WsdlMessages.INVALID_WSDL(systemID, 103 com.sun.xml.internal.ws.wsdl.parser.WSDLConstants.QNAME_DEFINITIONS, doc.getNodeName(), locatorTable.getStartLocation(doc).getLineNumber()), locatorTable.getStartLocation(doc)); 104 } 105 } catch(FileNotFoundException e){ 106 errorReceiver.error(WsdlMessages.FILE_NOT_FOUND(systemID), e); 107 return; 108 } catch (IOException e) { 109 doc = getFromMetadataResolver(systemID, e); 110 } catch (SAXParseException e) { 111 doc = getFromMetadataResolver(systemID, e); 112 } catch (SAXException e) { 113 doc = getFromMetadataResolver(systemID, e); 114 } 115 116 if (doc == null) { 117 continue; 118 } 119 120 NodeList schemas = doc.getElementsByTagNameNS(SchemaConstants.NS_XSD, "schema"); 121 for (int i = 0; i < schemas.getLength(); i++) { 122 if(!inlinedSchemaElements.contains(schemas.item(i))) 123 inlinedSchemaElements.add((Element) schemas.item(i)); 124 } 125 } 126 identifyRootWsdls(); 127 } 128 129 public static class WSEntityResolver implements EntityResolver { 130 EntityResolver parentResolver; 131 WsimportOptions options; 132 ErrorReceiver errorReceiver; 133 134 public WSEntityResolver(WsimportOptions options, ErrorReceiver errReceiver) { 135 this.parentResolver = options.entityResolver; 136 this.options = options; 137 this.errorReceiver = errReceiver; 138 } 139 140 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { 141 InputSource inputSource = null; 142 143 if(options.entityResolver != null ) { 144 inputSource = options.entityResolver.resolveEntity(null, systemId); 145 } 146 if (inputSource == null) { 147 inputSource = new InputSource(systemId); 148 InputStream is = null; 149 int redirects = 0; 150 boolean redirect; 151 URL url = JAXWSUtils.getFileOrURL(inputSource.getSystemId()); 152 URLConnection conn = url.openConnection(); 153 do { 154 if (conn instanceof HttpsURLConnection) { 155 if (options.disableSSLHostnameVerification) { 156 ((HttpsURLConnection) conn).setHostnameVerifier(new HttpClientVerifier()); 157 } 158 } 159 redirect = false; 160 if (conn instanceof HttpURLConnection) { 161 ((HttpURLConnection) conn).setInstanceFollowRedirects(false); 162 } 163 164 try { 165 is = conn.getInputStream(); 166 //is = sun.net.www.protocol.http.HttpURLConnection.openConnectionCheckRedirects(conn); 167 } catch (IOException e) { 168 if (conn instanceof HttpURLConnection) { 169 HttpURLConnection httpConn = ((HttpURLConnection) conn); 170 int code = httpConn.getResponseCode(); 171 if (code == 401) { 172 errorReceiver.error(new SAXParseException(WscompileMessages.WSIMPORT_AUTH_INFO_NEEDED(e.getMessage(), 173 systemId, DefaultAuthenticator.defaultAuthfile), null, e)); 174 throw new AbortException(); 175 } 176 //FOR other code we will retry with MEX 177 } 178 throw e; 179 } 180 181 //handle 302 or 303, JDK does not seem to handle 302 very well. 182 //Need to redesign this a bit as we need to throw better error message for IOException in this case 183 if (conn instanceof HttpURLConnection) { 184 HttpURLConnection httpConn = ((HttpURLConnection) conn); 185 int code = httpConn.getResponseCode(); 186 if (code == 302 || code == 303) { 187 //retry with the value in Location header 188 List<String> seeOther = httpConn.getHeaderFields().get("Location"); 189 if (seeOther != null && seeOther.size() > 0) { 190 URL newurl = new URL(url, seeOther.get(0)); 191 if (!newurl.equals(url)) { 192 errorReceiver.info(new SAXParseException(WscompileMessages.WSIMPORT_HTTP_REDIRECT(code, seeOther.get(0)), null)); 193 url = newurl; 194 httpConn.disconnect(); 195 if (redirects >= 5) { 196 errorReceiver.error(new SAXParseException(WscompileMessages.WSIMPORT_MAX_REDIRECT_ATTEMPT(), null)); 197 throw new AbortException(); 198 } 199 conn = url.openConnection(); 200 inputSource.setSystemId(url.toExternalForm()); 201 redirects++; 202 redirect = true; 203 } 204 } 205 } 206 } 207 } while (redirect); 208 inputSource.setByteStream(is); 209 } 210 211 return inputSource; 212 } 213 214 } 215 216 // overide default SSL HttpClientVerifier to always return true 217 // effectively overiding Hostname client verification when using SSL 218 private static class HttpClientVerifier implements HostnameVerifier { 219 public boolean verify(String s, SSLSession sslSession) { 220 return true; 221 } 222 } 223 224 /** 225 * Gives the root wsdl document systemId. A root wsdl document is the one which has wsdl:service. 226 * @return null if there is no root wsdl 227 */ 228 public @Nullable 229 String getRootWSDL(){ 230 return rootWSDL; 231 } 232 233 /** 234 * Gives all the WSDL documents. 235 */ 236 public @NotNull 237 Set<String> getRootWSDLs(){ 238 return rootWsdls; 239 } 240 241 242 /** 243 * Identifies WSDL documents from the {@link DOMForest}. Also identifies the root wsdl document. 244 */ 245 private void identifyRootWsdls(){ 246 for(String location: rootDocuments){ 247 Document doc = get(location); 248 if(doc!=null){ 249 Element definition = doc.getDocumentElement(); 250 if(definition == null || definition.getLocalName() == null || definition.getNamespaceURI() == null) 251 continue; 252 if(definition.getNamespaceURI().equals(WSDLConstants.NS_WSDL) && definition.getLocalName().equals("definitions")){ 253 rootWsdls.add(location); 254 //set the root wsdl at this point. Root wsdl is one which has wsdl:service in it 255 NodeList nl = definition.getElementsByTagNameNS(WSDLConstants.NS_WSDL, "service"); 256 257 //TODO:what if there are more than one wsdl with wsdl:service element. Probably such cases 258 //are rare and we will take any one of them, this logic should still work 259 if(nl.getLength() > 0) 260 rootWSDL = location; 261 } 262 } 263 } 264 //no wsdl with wsdl:service found, throw error 265 if(rootWSDL == null){ 266 StringBuffer strbuf = new StringBuffer(); 267 for(String str : rootWsdls){ 268 strbuf.append(str); 269 strbuf.append('\n'); 270 } 271 errorReceiver.error(null, WsdlMessages.FAILED_NOSERVICE(strbuf.toString())); 272 } 273 } 274 275 /* 276 * If source and target namespace are also passed in, 277 * then if the mex resolver is found and it cannot get 278 * the data, wsimport attempts to add ?wsdl to the 279 * address and retrieve the data with a normal http get. 280 * This behavior should only happen when trying a 281 * mex request first. 282 */ 283 private @Nullable Element getFromMetadataResolver(String systemId, Exception ex) { 284 //try MEX 285 MetaDataResolver resolver; 286 ServiceDescriptor serviceDescriptor = null; 287 for (MetadataResolverFactory resolverFactory : ServiceFinder.find(MetadataResolverFactory.class)) { 288 resolver = resolverFactory.metadataResolver(options.entityResolver); 289 try { 290 serviceDescriptor = resolver.resolve(new URI(systemId)); 291 //we got the ServiceDescriptor, now break 292 if (serviceDescriptor != null) 293 break; 294 } catch (URISyntaxException e) { 295 throw new ParseException(e); 296 } 297 } 298 299 if (serviceDescriptor != null) { 300 errorReceiver.warning(new SAXParseException(WsdlMessages.TRY_WITH_MEX(ex.getMessage()), null, ex)); 301 return parseMetadata(systemId, serviceDescriptor); 302 } else { 303 errorReceiver.error(null, WsdlMessages.PARSING_UNABLE_TO_GET_METADATA(ex.getMessage(), WscompileMessages.WSIMPORT_NO_WSDL(systemId)), ex); 304 } 305 return null; 306 } 307 308 private Element parseMetadata(@NotNull String systemId, @NotNull ServiceDescriptor serviceDescriptor) { 309 List<? extends Source> mexWsdls = serviceDescriptor.getWSDLs(); 310 List<? extends Source> mexSchemas = serviceDescriptor.getSchemas(); 311 Document root = null; 312 for (Source src : mexWsdls) { 313 if (src instanceof DOMSource) { 314 Node n = ((DOMSource) src).getNode(); 315 Document doc; 316 if (n.getNodeType() == Node.ELEMENT_NODE && n.getOwnerDocument() == null) { 317 doc = DOMUtil.createDom(); 318 doc.importNode(n, true); 319 } else { 320 doc = n.getOwnerDocument(); 321 } 322 323 // Element e = (n.getNodeType() == Node.ELEMENT_NODE)?(Element)n: DOMUtil.getFirstElementChild(n); 324 if (root == null) { 325 //check if its main wsdl, then set it to root 326 NodeList nl = doc.getDocumentElement().getElementsByTagNameNS(WSDLConstants.NS_WSDL, "service"); 327 if (nl.getLength() > 0) { 328 root = doc; 329 rootWSDL = src.getSystemId(); 330 } 331 } 332 NodeList nl = doc.getDocumentElement().getElementsByTagNameNS(WSDLConstants.NS_WSDL, "import"); 333 for(int i = 0; i < nl.getLength(); i++){ 334 Element imp = (Element) nl.item(i); 335 String loc = imp.getAttribute("location"); 336 if (loc != null) { 337 if (!externalReferences.contains(loc)) 338 externalReferences.add(loc); 339 } 340 } 341 if (core.keySet().contains(systemId)) 342 core.remove(systemId); 343 core.put(src.getSystemId(), doc); 344 resolvedCache.put(systemId, doc.getDocumentURI()); 345 isMexMetadata = true; 346 } 347 348 //TODO:handle SAXSource 349 //TODO:handler StreamSource 350 } 351 352 for (Source src : mexSchemas) { 353 if (src instanceof DOMSource) { 354 Node n = ((DOMSource) src).getNode(); 355 Element e = (n.getNodeType() == Node.ELEMENT_NODE) ? (Element) n : DOMUtil.getFirstElementChild(n); 356 inlinedSchemaElements.add(e); 357 } 358 //TODO:handle SAXSource 359 //TODO:handler StreamSource 360 } 361 return root.getDocumentElement(); 362 } 363 }