001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.protocol.http.util;
020:
021: import java.io.IOException;
022: import java.net.HttpURLConnection;
023: import java.net.MalformedURLException;
024: import java.net.URL;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import java.util.Set;
028:
029: import javax.xml.parsers.DocumentBuilder;
030: import javax.xml.parsers.DocumentBuilderFactory;
031: import javax.xml.parsers.ParserConfigurationException;
032:
033: import org.w3c.dom.Document;
034: import org.w3c.dom.Element;
035: import org.w3c.dom.Node;
036: import org.w3c.dom.NodeList;
037: import org.xml.sax.SAXException;
038: import org.apache.jmeter.protocol.http.control.AuthManager;
039:
040: /**
041: * For now I use DOM for WSDLHelper, but it would be more efficient to use JAXB
042: * to generate an object model for WSDL and use it to perform serialization and
043: * deserialization. It also makes it easier to traverse the WSDL to get
044: * necessary information.
045: * <p>
046: * Created on: Jun 3, 2003<br>
047: *
048: * @author Peter Lin
049: * @version $Revision: 596725 $
050: */
051: public class WSDLHelper {
052: /**
053: * -------------------------------------------- The members used by the
054: * class to do its work --------------------------------------------
055: */
056:
057: protected URL WSDLURL = null;
058:
059: protected HttpURLConnection CONN = null;
060:
061: protected Document WSDLDOC = null;
062:
063: protected String SOAPBINDING = null;
064:
065: public String BINDNAME = null;
066:
067: protected URL bindingURL = null;
068:
069: protected Object[] SOAPOPS = null;
070:
071: protected HashMap ACTIONS = new HashMap();
072:
073: protected AuthManager AUTH = null;
074:
075: /**
076: * Default constructor takes a string URL
077: */
078: public WSDLHelper(String url) throws MalformedURLException {
079: try {
080: WSDLURL = new URL(url);
081: } catch (MalformedURLException exception) {
082: throw exception;
083: }
084: }
085:
086: public WSDLHelper(String url, AuthManager auth)
087: throws MalformedURLException {
088: this (url);
089: this .AUTH = auth;
090: }
091:
092: /**
093: * Returns the URL
094: *
095: * @return the URL
096: */
097: public URL getURL() {
098: return this .WSDLURL;
099: }
100:
101: /**
102: * Return the protocol from the URL. this is needed, so that HTTPS works
103: * as expected.
104: */
105: public String getProtocol() {
106: return this .bindingURL.getProtocol();
107: }
108:
109: /**
110: * Return the host in the WSDL binding address
111: */
112: public String getBindingHost() {
113: return this .bindingURL.getHost();
114: }
115:
116: /**
117: * Return the path in the WSDL for the binding address
118: */
119: public String getBindingPath() {
120: return this .bindingURL.getPath();
121: }
122:
123: /**
124: * Return the port for the binding address
125: */
126: public int getBindingPort() {
127: return this .bindingURL.getPort();
128: }
129:
130: /**
131: * Returns the binding point for the webservice. Right now it naively
132: * assumes there's only one binding point with numerous soap operations.
133: *
134: * @return String
135: */
136: public String getBinding() {
137: try {
138: NodeList services = this .WSDLDOC
139: .getElementsByTagName("service");
140: if (services.getLength() == 0) {
141: services = this .WSDLDOC
142: .getElementsByTagName("wsdl:service");
143: }
144: // the document should only have one service node
145: // if it doesn't it may not work!
146: Element node = (Element) services.item(0);
147: NodeList ports = node.getElementsByTagName("port");
148: if (ports.getLength() == 0) {
149: ports = node.getElementsByTagName("wsdl:port");
150: }
151: for (int idx = 0; idx < ports.getLength(); idx++) {
152: Element pnode = (Element) ports.item(idx);
153: // NOTUSED String portname = pnode.getAttribute("name");
154: // used to check binding, but now it doesn't. it was
155: // failing when wsdl did not using binding as expected
156: NodeList servlist = pnode
157: .getElementsByTagName("soap:address");
158: // check wsdlsoap
159: if (servlist.getLength() == 0) {
160: servlist = pnode
161: .getElementsByTagName("wsdlsoap:address");
162: }
163: if (servlist.getLength() == 0) {
164: servlist = pnode
165: .getElementsByTagName("SOAP:address");
166: }
167: Element addr = (Element) servlist.item(0);
168: this .SOAPBINDING = addr.getAttribute("location");
169: this .bindingURL = new URL(this .SOAPBINDING);
170: return this .SOAPBINDING;
171: }
172: return null;
173: } catch (Exception exception) {
174: return null;
175: }
176: }
177:
178: /**
179: * Method is used internally to connect to the URL. It's protected;
180: * therefore external classes should use parse to get the resource at the
181: * given location.
182: *
183: * @throws IOException
184: */
185: protected void connect() throws IOException {
186: try {
187: CONN = (HttpURLConnection) WSDLURL.openConnection();
188: // in the rare case the WSDL is protected and requires
189: // authentication, use the AuthManager to set the
190: // authorization. Basic and Digest authorization are
191: // pretty weak and don't provide real security.
192: if (this .AUTH != null
193: && this .AUTH.getAuthHeaderForURL(this .WSDLURL) != null) {
194: CONN.setRequestProperty("Authorization", this .AUTH
195: .getAuthHeaderForURL(this .WSDLURL));
196: }
197: } catch (IOException exception) {
198: throw exception;
199: }
200: }
201:
202: /**
203: * We try to close the connection to make sure it doesn't hang around.
204: */
205: protected void close() {
206: try {
207: if (CONN != null)
208: CONN.getInputStream().close();
209: } catch (IOException exception) {
210: // do nothing
211: }
212: }
213:
214: /**
215: * Method is used internally to parse the InputStream and build the document
216: * using javax.xml.parser API.
217: */
218: protected void buildDocument() throws ParserConfigurationException,
219: IOException, SAXException {
220: try {
221: DocumentBuilderFactory dbfactory = DocumentBuilderFactory
222: .newInstance();
223: DocumentBuilder docbuild = dbfactory.newDocumentBuilder();
224: WSDLDOC = docbuild.parse(CONN.getInputStream());
225: } catch (ParserConfigurationException exception) {
226: throw exception;
227: } catch (IOException exception) {
228: throw exception;
229: } catch (SAXException exception) {
230: throw exception;
231: }
232: }
233:
234: /**
235: * Call this method to retrieve the WSDL. This method must be called,
236: * otherwise a connection to the URL won't be made and the stream won't be
237: * parsed.
238: */
239: public void parse() throws WSDLException {
240: try {
241: this .connect();
242: this .buildDocument();
243: SOAPOPS = this .getOperations();
244: } catch (IOException exception) {
245: throw (new WSDLException(exception));
246: } catch (SAXException exception) {
247: throw (new WSDLException(exception));
248: } catch (ParserConfigurationException exception) {
249: throw (new WSDLException(exception));
250: } finally {
251: this .close();
252: }
253: }
254:
255: /**
256: * Get a list of the web methods as a string array.
257: */
258: public String[] getWebMethods() {
259: for (int idx = 0; idx < SOAPOPS.length; idx++) {
260: // get the node
261: Node act = (Node) SOAPOPS[idx];
262: // get the soap:operation
263: NodeList opers = ((Element) act)
264: .getElementsByTagName("soap:operation");
265: if (opers.getLength() == 0) {
266: opers = ((Element) act)
267: .getElementsByTagName("wsdlsoap:operation");
268: }
269: if (opers.getLength() == 0) {
270: opers = ((Element) act)
271: .getElementsByTagName("wsdl:operation");
272: }
273: if (opers.getLength() == 0) {
274: opers = ((Element) act)
275: .getElementsByTagName("operation");
276: }
277:
278: // there should only be one soap:operation node per operation
279: Element op = (Element) opers.item(0);
280: String value;
281: if (op != null) {
282: value = op.getAttribute("soapAction");
283: } else {
284: value = "";
285: }
286: String key = ((Element) act).getAttribute("name");
287: this .ACTIONS.put(key, value);
288: }
289: Set keys = this .ACTIONS.keySet();
290: String[] stringmeth = new String[keys.size()];
291: Object[] stringKeys = keys.toArray();
292: System.arraycopy(stringKeys, 0, stringmeth, 0, keys.size());
293: return stringmeth;
294: }
295:
296: /**
297: * Return the soap action matching the operation name.
298: */
299: public String getSoapAction(String key) {
300: return (String) this .ACTIONS.get(key);
301: }
302:
303: /**
304: * Get the wsdl document.
305: */
306: public Document getWSDLDocument() {
307: return WSDLDOC;
308: }
309:
310: /**
311: * Method will look at the binding nodes and see if the first child is a
312: * soap:binding. If it is, it adds it to an array.
313: *
314: * @return Node[]
315: */
316: public Object[] getSOAPBindings() {
317: ArrayList list = new ArrayList();
318: NodeList bindings = WSDLDOC.getElementsByTagName("binding");
319: String soapBind = "soap:binding";
320: if (bindings.getLength() == 0) {
321: bindings = WSDLDOC.getElementsByTagName("wsdl:binding");
322: }
323: if (WSDLDOC.getElementsByTagName(soapBind).getLength() == 0) {
324: soapBind = "wsdlsoap:binding";
325: }
326: if (WSDLDOC.getElementsByTagName(soapBind).getLength() == 0) {
327: soapBind = "SOAP:binding";
328: }
329:
330: for (int idx = 0; idx < bindings.getLength(); idx++) {
331: Element nd = (Element) bindings.item(idx);
332: NodeList slist = nd.getElementsByTagName(soapBind);
333: if (slist.getLength() > 0) {
334: // NOTUSED Element soapbind = (Element) slist.item(0);
335: this .BINDNAME = nd.getAttribute("name");
336: list.add(nd);
337: }
338: }
339: if (list.size() > 0) {
340: return list.toArray();
341: } else {
342: return new Object[0];
343: }
344: }
345:
346: /**
347: * Look at the bindings with soap operations and get the soap operations.
348: * Since WSDL may describe multiple bindings and each binding may have
349: * multiple soap operations, we iterate through the binding nodes with a
350: * first child that is a soap binding. If a WSDL doesn't use the same
351: * formatting convention, it is possible we may not get a list of all the
352: * soap operations. If that is the case, getSOAPBindings() will need to be
353: * changed. I should double check the WSDL spec to see what the official
354: * requirement is. Another option is to get all operation nodes and check to
355: * see if the first child is a soap:operation. The benefit of not getting
356: * all operation nodes is WSDL could contain duplicate operations that are
357: * not SOAP methods. If there are a large number of methods and half of them
358: * are HTTP operations, getting all operations could slow things down.
359: *
360: * @return Node[]
361: */
362: public Object[] getOperations() {
363: Object[] res = this .getSOAPBindings();
364: ArrayList ops = new ArrayList();
365: // first we iterate through the bindings
366: for (int idx = 0; idx < res.length; idx++) {
367: Element one = (Element) res[idx];
368: NodeList opnodes = one.getElementsByTagName("operation");
369: String soapOp = "soap:operation";
370: if (opnodes.getLength() == 0) {
371: opnodes = one.getElementsByTagName("wsdl:operation");
372: }
373: if (one.getElementsByTagName(soapOp).getLength() == 0) {
374: soapOp = "wsdlsoap:operation";
375: }
376: // now we iterate through the operations
377: for (int idz = 0; idz < opnodes.getLength(); idz++) {
378: // if the first child is soap:operation
379: // we add it to the array
380: Element child = (Element) opnodes.item(idz);
381:
382: // TODO - the following code looks wrong - it does the same in both cases
383: NodeList soapnode = child.getElementsByTagName(soapOp);
384: if (soapnode.getLength() > 0) {
385: ops.add(child);
386: } else {
387: ops.add(child);
388: }
389: }
390: }
391: return ops.toArray();
392: }
393:
394: /**
395: * Simple test for the class uses bidbuy.wsdl from Apache's soap driver
396: * examples.
397: *
398: * @param args
399: */
400: public static void main(String[] args) {
401: try {
402: WSDLHelper help =
403: // new WSDLHelper("http://localhost/WSTest/WSTest.asmx?WSDL");
404: // new WSDLHelper("http://localhost/AxisWSDL.xml");
405: new WSDLHelper("http://localhost:8080/ServiceGateway.wsdl");
406: // new WSDLHelper("http://services.bio.ifi.lmu.de:1046/prothesaurus/services/BiologicalNameService?wsdl");
407: long start = System.currentTimeMillis();
408: help.parse();
409: String[] methods = help.getWebMethods();
410: System.out.println("el: "
411: + (System.currentTimeMillis() - start));
412: for (int idx = 0; idx < methods.length; idx++) {
413: System.out.println("method name: " + methods[idx]);
414: }
415: System.out.println("service url: " + help.getBinding());
416: System.out.println("protocol: " + help.getProtocol());
417: System.out.println("port=" + help.getURL().getPort());
418: } catch (Exception exception) {
419: System.out.println("main method catch:");
420: exception.printStackTrace();
421: }
422: }
423: }
|