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