KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jmeter > protocol > http > sampler > WebServiceSampler


1 // $Header: /home/cvs/jakarta-jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/WebServiceSampler.java,v 1.15 2004/03/13 19:44:14 sebb Exp $
2
/*
3  * Copyright 2003-2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17 */

18
19 package org.apache.jmeter.protocol.http.sampler;
20
21 import java.io.BufferedReader JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.StringReader JavaDoc;
26 import java.net.HttpURLConnection JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.util.Random JavaDoc;
29
30 import javax.xml.parsers.DocumentBuilder JavaDoc;
31
32 import org.xml.sax.InputSource JavaDoc;
33
34 import org.apache.jorphan.io.TextFile;
35
36 import org.apache.jmeter.gui.JMeterFileFilter;
37 import org.apache.jmeter.protocol.http.util.DOMPool;
38 import org.apache.jmeter.samplers.Entry;
39 import org.apache.jmeter.samplers.SampleResult;
40 import org.apache.jmeter.util.JMeterUtils;
41 import org.apache.soap.Envelope;
42 import org.apache.soap.messaging.Message;
43 import org.apache.soap.transport.SOAPTransport;
44 import org.apache.soap.transport.http.SOAPHTTPConnection;
45 import org.apache.soap.util.xml.XMLParserUtils;
46 import org.w3c.dom.Document JavaDoc;
47
48 /**
49  * Sampler to handle Web Service requests. It uses Apache SOAP drivers to
50  * perform the XML generation, connection, SOAP encoding and other SOAP
51  * functions.
52  * <p>
53  * Created on: Jun 26, 2003
54  *
55  * @version $Revision: 1.15 $
56  */

57 public class WebServiceSampler extends HTTPSampler
58 {
59     public static final String JavaDoc XML_DATA = "HTTPSamper.xml_data";
60     public static final String JavaDoc SOAP_ACTION = "Soap.Action";
61     public static final String JavaDoc XML_DATA_FILE =
62         "WebServiceSampler.xml_data_file";
63     public static final String JavaDoc XML_PATH_LOC = "WebServiceSampler.xml_path_loc";
64     public static final String JavaDoc MEMORY_CACHE = "WebServiceSampler.memory_cache";
65     public static final String JavaDoc READ_RESPONSE =
66         "WebServiceSampler.read_response";
67     public static final String JavaDoc USE_PROXY = "WebServiceSampler.use_proxy";
68     public static final String JavaDoc PROXY_HOST = "WebServiceSampler.proxy_host";
69     public static final String JavaDoc PROXY_PORT = "WebServiceSampler.proxy_port";
70
71     /**
72      * The SOAPAction is required by MS
73      * webservices and is defined by the
74      * WSDL.
75      */

76     protected String JavaDoc SOAPACTION = null;
77
78     /**
79      * SampleResult which holds the response
80      */

81     transient SampleResult RESULT = null;
82
83     /**
84      * The XML document
85      */

86     protected Document JavaDoc XMLMSG = null;
87
88     /**
89      * size of File[] array
90      */

91     private int FILE_COUNT = -1;
92
93     /**
94      * List of files that have .xml extension
95      */

96     private File JavaDoc[] FILE_LIST = null;
97
98     /**
99      * Random class for generating random
100      * numbers.
101      */

102     private Random JavaDoc RANDOM = new Random JavaDoc();
103
104     /**
105      * We make DocumentBuilder static. I'm not sure that this is thread safe.
106      * Should investigate this further to make sure it's ok. Making it
107      * non-static could mean a performance hit to get a new DocumentBuilder for
108      * each request. If it's not safe to use static here, then we should
109      * consider using Apache commons pool to create a pool of document builders
110      * or make sure XMLParserUtils creates builders efficiently.
111      */

112     protected static DocumentBuilder JavaDoc XDB = null;
113
114     protected String JavaDoc FILE_CONTENTS = null;
115     
116     /**
117      * Set the path where XML messages are stored for random selection.
118      */

119     public void setXmlPathLoc(String JavaDoc path)
120     {
121         setProperty(XML_PATH_LOC, path);
122     }
123
124     /**
125      * Get the path where XML messages are stored. this is the directory where
126      * JMeter will randomly select a file.
127      */

128     public String JavaDoc getXmlPathLoc()
129     {
130         return getPropertyAsString(XML_PATH_LOC);
131     }
132
133     /**
134      * it's kinda obvious, but we state it anyways. Set the xml file with a
135      * string path.
136      * @param filename
137      */

138     public void setXmlFile(String JavaDoc filename)
139     {
140         setProperty(XML_DATA_FILE, filename);
141     }
142
143     /**
144      * Get the file location of the xml file.
145      * @return String file path.
146      */

147     public String JavaDoc getXmlFile()
148     {
149         return getPropertyAsString(XML_DATA_FILE);
150     }
151
152     /**
153      * Method uses jorphan TextFile class to load the contents of the file. I
154      * wonder if we should cache the DOM Document to save on parsing the
155      * message. Parsing XML is CPU intensive, so it could restrict the number
156      * of threads a test plan can run effectively. To cache the documents, it
157      * may be good to have an external class to provide caching that is
158      * efficient. We could just use a HashMap, but for large tests, it will be
159      * slow. Ideally, the cache would be indexed, so that large tests will
160      * run efficiently.
161      * @return String contents of the file
162      */

163     private File JavaDoc retrieveRuntimeXmlData()
164     {
165         String JavaDoc file = getRandomFileName();
166         if (file.length() > 0)
167         {
168             if (this.getReadResponse()){
169                 TextFile tfile = new TextFile(file);
170                 FILE_CONTENTS = tfile.getText();
171             }
172             return new File JavaDoc(file);
173         } else {
174             return null;
175         }
176     }
177
178     /**
179      * Method is used internally to check if a random file should be used for
180      * the message. Messages must be valid. This is one way to load test with
181      * different messages. The limitation of this approach is parsing XML takes
182      * CPU resources, so it could affect JMeter GUI responsiveness.
183      * @return String filename
184      */

185     protected String JavaDoc getRandomFileName()
186     {
187         if (this.getXmlPathLoc() != null)
188         {
189             File JavaDoc src = new File JavaDoc(this.getXmlPathLoc());
190             if (src.isDirectory() && src.list() != null)
191             {
192                 FILE_LIST =
193                     src.listFiles(
194                         new JMeterFileFilter(new String JavaDoc[] { ".xml" }));
195                 this.FILE_COUNT = FILE_LIST.length;
196                 File JavaDoc one = FILE_LIST[RANDOM.nextInt(FILE_COUNT)];
197                 // return the absolutePath of the file
198
return one.getAbsolutePath();
199             }
200             else
201             {
202                 return getXmlFile();
203             }
204         }
205         else
206         {
207             return getXmlFile();
208         }
209     }
210
211     /**
212      * Set the XML data.
213      * @param data
214      */

215     public void setXmlData(String JavaDoc data)
216     {
217         setProperty(XML_DATA, data);
218     }
219
220     /**
221      * Get the XML data as a string.
222      * @return String data
223      */

224     public String JavaDoc getXmlData()
225     {
226         return getPropertyAsString(XML_DATA);
227     }
228
229     /**
230      * Set the soap action which should be in the form of an URN.
231      * @param data
232      */

233     public void setSoapAction(String JavaDoc data)
234     {
235         setProperty(SOAP_ACTION, data);
236     }
237
238     /**
239      * Return the soap action string.
240      * @return String soap action
241      */

242     public String JavaDoc getSoapAction()
243     {
244         return getPropertyAsString(SOAP_ACTION);
245     }
246
247     /**
248      * Set the memory cache.
249      * @param cache
250      */

251     public void setMemoryCache(boolean cache)
252     {
253         setProperty(MEMORY_CACHE, String.valueOf(cache));
254     }
255
256     /**
257      * Get the memory cache.
258      * @return boolean cache
259      */

260     public boolean getMemoryCache()
261     {
262         return getPropertyAsBoolean(MEMORY_CACHE);
263     }
264
265     /**
266      * Set whether the sampler should read the response or not.
267      * @param read
268      */

269     public void setReadResponse(boolean read)
270     {
271         setProperty(READ_RESPONSE, String.valueOf(read));
272     }
273
274     /**
275      * Return whether or not to read the response.
276      * @return boolean
277      */

278     public boolean getReadResponse()
279     {
280         return this.getPropertyAsBoolean(READ_RESPONSE);
281     }
282
283     /**
284      * Set whether or not to use a proxy
285      * @param proxy
286      */

287     public void setUseProxy(boolean proxy){
288         setProperty(USE_PROXY, String.valueOf(proxy));
289     }
290
291     /**
292      * Return whether or not to use proxy
293      * @return true if should use proxy
294      */

295     public boolean getUseProxy(){
296         return this.getPropertyAsBoolean(USE_PROXY);
297     }
298
299     /**
300      * Set the proxy hostname
301      * @param host
302      */

303     public void setProxyHost(String JavaDoc host){
304         setProperty(PROXY_HOST, host);
305     }
306
307     /**
308      * Return the proxy hostname
309      * @return the proxy hostname
310      */

311     public String JavaDoc getProxyHost(){
312         this.checkProxy();
313         return this.getPropertyAsString(PROXY_HOST);
314     }
315
316     /**
317      * Set the proxy port
318      * @param port
319      */

320     public void setProxyPort(String JavaDoc port){
321         setProperty(PROXY_PORT, port);
322     }
323
324     /**
325      * Return the proxy port
326      * @return the proxy port
327      */

328     public int getProxyPort(){
329         this.checkProxy();
330         return this.getPropertyAsInt(PROXY_PORT);
331     }
332     
333     /**
334      * The method will check to see if JMeter was started
335      * in NonGui mode. If it was, it will try to pick up
336      * the proxy host and port values if they were passed
337      * to JMeter.java.
338      */

339     public void checkProxy(){
340         if (System.getProperty("JMeter.NonGui") != null &&
341             System.getProperty("JMeter.NonGui").equals("true")){
342                 this.setUseProxy(true);
343                 // we check to see if the proxy host and port are set
344
String JavaDoc port = this.getPropertyAsString(PROXY_PORT);
345                 String JavaDoc host = this.getPropertyAsString(PROXY_HOST);
346                 if (host == null || host.length() == 0){
347                     // it's not set, lets check if the user passed
348
// proxy host and port from command line
349
if (System.getProperty("http.proxyHost") != null){
350                         host = System.getProperty("http.proxyHost");
351                         this.setProxyHost(host);
352                     }
353                 }
354                 if (port == null || port.length() == 0){
355                     // it's not set, lets check if the user passed
356
// proxy host and port from command line
357
if (System.getProperty("http.proxyPort") != null){
358                         port = System.getProperty("http.proxyPort");
359                         this.setProxyPort(port);
360                     }
361                 }
362             }
363     }
364     
365     /**
366      * This method uses Apache soap util to create the proper DOM elements.
367      * @return Element
368      */

369     public org.w3c.dom.Element JavaDoc createDocument()
370     {
371         if (getPropertyAsBoolean(MEMORY_CACHE))
372         {
373             String JavaDoc next = this.getRandomFileName();
374             if (DOMPool.getDocument(next) != null)
375             {
376                 return ((Document JavaDoc) DOMPool.getDocument(next))
377                     .getDocumentElement();
378             }
379             else
380             {
381                 return openDocument(next).getDocumentElement();
382             }
383         }
384         else
385         {
386             return openDocument(null).getDocumentElement();
387         }
388     }
389
390     /**
391      * Open the file and create a Document.
392      * @param key
393      * @return Document
394      */

395     protected Document JavaDoc openDocument(String JavaDoc key)
396     {
397         if (XDB == null)
398         {
399             XDB = XMLParserUtils.getXMLDocBuilder();
400         }
401         Document JavaDoc doc = null;
402         // if either a file or path location is given,
403
// get the file object.
404
if (getXmlFile().length() > 0 || getXmlPathLoc().length() > 0){
405             try {
406                 doc = XDB.parse(new FileInputStream JavaDoc(retrieveRuntimeXmlData()));
407             } catch (Exception JavaDoc e){
408                 // there should be a file, if not fail silently
409
}
410         } else {
411             FILE_CONTENTS = getXmlData();
412             if (FILE_CONTENTS != null && FILE_CONTENTS.length() > 0)
413             {
414                 try
415                 {
416                     doc = XDB.parse(
417                         new InputSource JavaDoc(new StringReader JavaDoc(FILE_CONTENTS)));
418                 }
419                 catch (Exception JavaDoc ex)
420                 {
421                     // ex.printStackTrace();
422
}
423             }
424         }
425         if (this.getPropertyAsBoolean(MEMORY_CACHE))
426         {
427             DOMPool.putDocument(key, doc);
428         }
429         return doc;
430     }
431
432     /**
433      * sample(Entry e) simply calls sample().
434      * @param e - ignored
435      * @return the sample Result
436      */

437     public SampleResult sample(Entry e)
438     {
439         return sample();
440     }
441
442     /**
443      * sample() does the following: create a new SampleResult, call
444      * sampleWithApache, and return the result.
445      * @return SampleResult
446      */

447     public SampleResult sample()
448     {
449         RESULT = new SampleResult();
450         sampleWithApache();
451         return RESULT;
452     }
453
454     /**
455      * Sample the URL using Apache SOAP driver. Implementation note for myself
456      * and those that are curious. Current logic marks the end after the
457      * response has been read. If read response is set to false, the buffered
458      * reader will read, but do nothing with it. Essentially, the stream from
459      * the server goes into the ether.
460      */

461     public void sampleWithApache()
462     {
463         try
464         {
465             org.w3c.dom.Element JavaDoc rdoc = createDocument();
466             Envelope msgEnv = Envelope.unmarshall(rdoc);
467             // create a new message
468
Message msg = new Message();
469             RESULT.sampleStart();
470             SOAPHTTPConnection spconn = null;
471             // if a blank HeaderManager exists, try to
472
// get the SOAPHTTPConnection. After the first
473
// request, there should be a connection object
474
// stored with the cookie header info.
475
if (this.getHeaderManager() != null &&
476                 this.getHeaderManager().getSOAPHeader() != null) {
477                 spconn = (SOAPHTTPConnection)this.getHeaderManager().
478                     getSOAPHeader();
479             } else {
480                 spconn = new SOAPHTTPConnection();
481             }
482             // check the proxy
483
String JavaDoc phost = "";
484             int pport = 0;
485             // if use proxy is set, we try to pick up the
486
// proxy host and port from either the text
487
// fields or from JMeterUtil if they were passed
488
// from command line
489
if (this.getUseProxy()){
490                 if (this.getProxyHost().length() > 0 && this.getProxyPort() > 0){
491                     phost = this.getProxyHost();
492                     pport = this.getProxyPort();
493                 } else {
494                     if (System.getProperty("http.proxyHost") != null ||
495                         System.getProperty("http.proxyPort") != null){
496                             phost = System.getProperty("http.proxyHost");
497                             pport = Integer.parseInt(
498                                 System.getProperty("http.proxyPort"));
499                         }
500                 }
501                 // if for some reason the host is blank and the port is
502
// zero, the sampler will fail silently
503
if (phost.length() > 0 && pport > 0){
504                     spconn.setProxyHost(phost);
505                     spconn.setProxyPort(pport);
506                 }
507             }
508             // by default we maintain the session.
509
spconn.setMaintainSession(true);
510             msg.setSOAPTransport(spconn);
511             msg.send(this.getUrl(), this.getSoapAction(), msgEnv);
512
513             if (this.getHeaderManager() != null){
514                 this.getHeaderManager().setSOAPHeader(spconn);
515             }
516
517             SOAPTransport st = msg.getSOAPTransport();
518             BufferedReader JavaDoc br = st.receive();
519             RESULT.setDataType(SampleResult.TEXT);
520             if (this.getPropertyAsBoolean(READ_RESPONSE))
521             {
522                 StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
523                 String JavaDoc line;
524                 while ((line = br.readLine()) != null)
525                 {
526                     buf.append(line);
527                 }
528                 RESULT.sampleEnd();
529                 // set the response
530
RESULT.setResponseData(buf.toString().getBytes());
531             }
532             else
533             {
534                 // by not reading the response
535
// for real, it improves the
536
// performance on slow clients
537
br.read();
538                 RESULT.sampleEnd();
539                 RESULT.setResponseData(
540                     JMeterUtils
541                         .getResString("read_response_message")
542                         .getBytes());
543             }
544             RESULT.setSuccessful(true);
545             // 1-22-04 updated the sampler so that when read
546
// response is set, it also sets SamplerData with
547
// the XML message, so users can see what was
548
// sent. if read response is not checked, it will
549
// not set sampler data with the request message.
550
// peter lin.
551
RESULT.setSamplerData(
552                 getUrl().getProtocol()
553                     + "://"
554                     + getUrl().getHost()
555                     + "/"
556                     + getUrl().getFile()
557                     + "\n"
558                     + FILE_CONTENTS);
559             RESULT.setDataEncoding(
560                 st.getResponseSOAPContext().getContentType());
561             // setting this is just a formality, since
562
// soap will return a descriptive error
563
// message, soap errors within the response
564
// are preferred.
565
RESULT.setResponseCode("200");
566             br.close();
567             msg = null;
568             st = null;
569             // reponse code doesn't really apply, since
570
// the soap driver doesn't provide a
571
// response code
572
}
573         catch (Exception JavaDoc exception)
574         {
575             // exception.printStackTrace();
576
RESULT.setSuccessful(false);
577         }
578     }
579
580     /**
581      * We override this to prevent the wrong encoding and provide no
582      * implementation. We want to reuse the other parts of HTTPSampler, but not
583      * the connection. The connection is handled by the Apache SOAP driver.
584      */

585     public void addEncodedArgument(String JavaDoc name, String JavaDoc value, String JavaDoc metaData)
586     {
587     }
588
589     /**
590      * We override this to prevent the wrong encoding and provide no
591      * implementation. We want to reuse the other parts of HTTPSampler, but not
592      * the connection. The connection is handled by the Apache SOAP driver.
593      */

594     protected HttpURLConnection JavaDoc setupConnection(URL JavaDoc u, String JavaDoc method)
595         throws IOException JavaDoc
596     {
597         return null;
598     }
599
600     /**
601      * We override this to prevent the wrong encoding and provide no
602      * implementation. We want to reuse the other parts of HTTPSampler, but not
603      * the connection. The connection is handled by the Apache SOAP driver.
604      */

605     protected long connect() throws IOException JavaDoc
606     {
607         return -1;
608     }
609 }
610
Popular Tags