/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.jmeter.protocol.http.sampler;
import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
import org.apache.jmeter.protocol.http.config.*;
import org.apache.jmeter.samplers.*;
import java.util.*;
import java.net.*;
import javax.swing.ImageIcon;
import org.apache.log4j.*;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.framework.TestResult;
// required to use to tidy parser
import java.io.*;
import org.w3c.dom.*;
import org.w3c.tidy.Tidy;
import org.xml.sax.SAXException;
// required for TestCase
import org.apache.jmeter.protocol.http.config.UrlConfigFull;
/**
* A sampler that downloads all downloadable components e.g. images,
* applets etc.
*
* @author Khor Soon Hin
* @version 1.0
* @created $Date: 2002/03/07 00:00:37 $
*/
public class HTTPSamplerFull extends HTTPSampler
{
public final static String IMAGE = "httpSamplerFull.IMAGE";
protected static Category catClass =
Category.getInstance(HTTPSamplerFull.class.getName());
protected static String utfEncodingName;
protected static String HTTP = "http://";
protected URL baseUrl;
public HTTPSamplerFull()
{
super();
catClass.debug("Start : HTTPSamplerFull1");
catClass.debug("End : HTTPSamplerFull1");
}
/**
* Gets the UrlConfig attribute of the HTTPSampler object
*
* @param e Description of Parameter
* @return The UrlConfig value
*/
/*public UrlConfig getUrlConfig(Entry e)
{
UrlConfig urlConfig = (UrlConfig)
e.getConfigElement(UrlConfigFull.class);
if(catClass.isDebugEnabled())
{
catClass.debug("getUrlConfig1 : Returning urlConfig - " + urlConfig.toString());
}
return urlConfig;
}*/
/**
* Samples the <code>Entry</code> passed in and stores the result in
* <code>SampleResult</code>
*
* @param entry an entry to be sampled
* @param redirected whether redirection is turned on
* @return resuls of the sampling
*/
public SampleResult sample(Entry e)
{
catClass.debug("Start : sample2");
long totalTime = (long)0;
SampleResult res = super.sample(e);
String displayName = (String)res.getValue(SampleResult.SAMPLE_LABEL);
res.putValue(SampleResult.DISPLAY_NAME, displayName);
List loadBinaryList = new ArrayList();
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : main page loading time - " + res.getTime());
}
totalTime += res.getTime();
Document html = null;
try
{
baseUrl = new URL((String)res.getValue(SampleResult.SAMPLE_LABEL));
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : baseUrl - " + baseUrl.toString());
}
// now use parser to look for image tags
html = (Document)getDOM(new String((byte[])res.getValue(SampleResult.TEXT_RESPONSE)));
}
catch(SAXException exception)
{
catClass.error("sample2 : Error parsing document - " + exception);
catClass.error("sample2 : Setting SampleResult SUCCESS value to false");
res.putValue(SampleResult.TEXT_RESPONSE, exception.toString().getBytes());
res.putValue(RESPONSE_CODE, NON_HTTP_RESPONSE_CODE);
res.putValue(RESPONSE_MESSAGE, NON_HTTP_RESPONSE_MESSAGE);
res.putValue(SampleResult.SUCCESS, new Boolean(false));
}
catch(MalformedURLException exception)
{
catClass.error("sample2 : Error creating URL - " + exception);
catClass.error("sample2 : Setting SampleResult SUCCESS value to false");
res.putValue(SampleResult.TEXT_RESPONSE, exception.toString().getBytes());
res.putValue(RESPONSE_CODE, NON_HTTP_RESPONSE_CODE);
res.putValue(RESPONSE_MESSAGE, NON_HTTP_RESPONSE_MESSAGE);
res.putValue(SampleResult.SUCCESS, new Boolean(false));
}
// sample result to store image from <img...> tags
SampleResult imgRes = null;
NodeList nodeList = html.getElementsByTagName("img");
for(int i = 0; i < nodeList.getLength(); i++)
{
imgRes = new SampleResult();
try
{
Node tempNode = nodeList.item(i);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Image tags - " + tempNode);
}
// get the url of those images
NamedNodeMap nnm = tempNode.getAttributes();
Node namedItem = nnm.getNamedItem("src");
String imgUrlStr = namedItem.getNodeValue();
// set the baseUrl and imgUrl so that if error occurs
// due to MalformedException then at least the values will be
// visible to the user to aid correction
imgRes.putValue(SampleResult.DISPLAY_NAME, baseUrl + "," + imgUrlStr);
// download those images
URL imgUrl = new URL(baseUrl, imgUrlStr);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Image url without baseUrl - " + imgUrlStr);
catClass.debug("sample2 : Image url with baseUrl - " + imgUrl);
}
imgRes.putValue(SampleResult.DISPLAY_NAME, imgUrl.toString());
loadBinary(imgUrl, e, imgRes);
ImageIcon image = new ImageIcon(imgUrl, imgUrl.toString());
imgRes.putValue(IMAGE, image);
totalTime += imgRes.getTime();
imgRes.remove(SampleResult.TEXT_RESPONSE);
}
catch(IOException exception)
{
catClass.error("sample2 : Error reading from URL - " + exception);
catClass.error("sample2 : Setting SampleResult SUCCESS value to false");
imgRes.putValue(SampleResult.TEXT_RESPONSE, exception.toString().getBytes());
imgRes.putValue(RESPONSE_CODE, NON_HTTP_RESPONSE_CODE);
imgRes.putValue(RESPONSE_MESSAGE, NON_HTTP_RESPONSE_MESSAGE);
imgRes.putValue(SampleResult.SUCCESS, new Boolean(false));
}
loadBinaryList.add(imgRes);
}
SampleResult appRes = null;
// use parser to look for applet tags
nodeList = html.getElementsByTagName("applet");
for(int i = 0; i < nodeList.getLength(); i++)
{
appRes = new SampleResult();
try
{
Node tempNode = nodeList.item(i);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Applet tags - " + tempNode);
}
// get the url of those applets
NamedNodeMap nnm = tempNode.getAttributes();
Node namedItem = nnm.getNamedItem("code");
String appletUrlStr = namedItem.getNodeValue() + ".class";
// set the baseUrl and imgUrl so that if error occurs
// due to MalformedException then at least the values will be
// visible to the user to aid correction
appRes.putValue(SampleResult.DISPLAY_NAME, baseUrl + "," + appletUrlStr);
// download those applet
URL appletUrl = new URL(baseUrl, appletUrlStr);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Applet url without baseUrl - " + appletUrlStr);
catClass.debug("sample2 : Applet url with baseUrl - " + appletUrl);
}
appRes.putValue(SampleResult.DISPLAY_NAME, appletUrl.toString());
appRes.putValue(SampleResult.TEXT_RESPONSE, loadBinary(appletUrl, e, appRes));
totalTime += appRes.getTime();
}
catch(IOException exception)
{
catClass.error("sample2 : Error reading from URL - " + exception);
catClass.error("sample2 : Setting SampleResult SUCCESS value to false");
appRes.putValue(SampleResult.TEXT_RESPONSE, exception.toString().getBytes());
appRes.putValue(RESPONSE_CODE, NON_HTTP_RESPONSE_CODE);
appRes.putValue(RESPONSE_MESSAGE, NON_HTTP_RESPONSE_MESSAGE);
appRes.putValue(SampleResult.SUCCESS, new Boolean(false));
}
loadBinaryList.add(appRes);
}
// use parser to look for input tags with image types as well
nodeList = html.getElementsByTagName("input");
for(int i = 0; i < nodeList.getLength(); i++)
{
try
{
Node tempNode = nodeList.item(i);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Input tags - " + tempNode);
}
// get the url of those images
NamedNodeMap nnm = tempNode.getAttributes();
Node namedItem = nnm.getNamedItem("type");
if(namedItem != null)
{
String inputType = namedItem.getNodeValue();
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Input type - " + inputType);
}
if(inputType != null && inputType.equalsIgnoreCase("image"))
{
imgRes = new SampleResult();
namedItem = nnm.getNamedItem("src");
String imgUrlStr = namedItem.getNodeValue();
// set the baseUrl and imgUrl so that if error occurs
// due to MalformedException then at least the values will be
// visible to the user to aid correction
imgRes.putValue(SampleResult.DISPLAY_NAME, baseUrl + "," +
imgUrlStr);
// download those images
URL imgUrl = new URL(baseUrl, imgUrlStr);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : Image url without baseUrl - " +
imgUrlStr);
catClass.debug("sample2 : Image url with baseUrl - " +
imgUrl);
}
loadBinary(imgUrl, e, imgRes);
ImageIcon image = new ImageIcon(imgUrl, imgUrl.toString());
imgRes.putValue(IMAGE, image);
imgRes.putValue(SampleResult.DISPLAY_NAME, imgUrl.toString());
totalTime += imgRes.getTime();
imgRes.remove(SampleResult.TEXT_RESPONSE);
}
}
}
catch(IOException exception)
{
catClass.error("sample2 : Error reading from URL - " + exception);
catClass.error("sample2 : Setting SampleResult SUCCESS value to false");
imgRes.putValue(SampleResult.TEXT_RESPONSE, exception.toString().getBytes());
imgRes.putValue(RESPONSE_CODE, NON_HTTP_RESPONSE_CODE);
imgRes.putValue(RESPONSE_MESSAGE, NON_HTTP_RESPONSE_MESSAGE);
imgRes.putValue(SampleResult.SUCCESS, new Boolean(false));
}
loadBinaryList.add(imgRes);
}
res.putValue(SampleResult.RESULT_LIST, loadBinaryList);
if(catClass.isDebugEnabled())
{
catClass.debug("sample2 : total time - " + totalTime);
}
res.putValue(SampleResult.TOTAL_TIME, new Long(totalTime));
res.setTime(totalTime);
catClass.debug("End : sample2");
return res;
}
/**
* Download the binaries from given <code>URL</code>
*
* @param url <code>URL</code> from where binary is to be downloaded
* @param e <code>Entry</code> being sampled
* @param res <code>SampleResult</code> to store sampling results
* @return binary downloaded
*/
protected byte[] loadBinary(URL url, Entry e, SampleResult res)
throws IOException
{
catClass.debug("Start : loadBinary1");
byte[] ret = new byte[0];
// Create a dummy UrlConfig and set only the method. Set the method to
// GET. Pass this UrlConfig to setupConnection() 'cos it uses UrlConfig
// to determine the HTTP method to get the url. In this case, we are
// loading binaries so the method has to be GET.
UrlConfig urlConfig = new UrlConfig();
urlConfig.setMethod(UrlConfig.GET);
HttpURLConnection conn = null;
try
{
conn = setupConnection(url, urlConfig, e);
conn.connect();
}
catch(IOException exception)
{
// don't do anything 'cos persumably the connection will return the
// correct http response codes
if(catClass.isDebugEnabled())
{
catClass.debug("loadBinary1 : error in setupConnection " + exception);
}
}
long time = System.currentTimeMillis();
if(catClass.isDebugEnabled())
{
catClass.debug("loadBinary1 : start sampling time - " + time);
}
int errorLevel = getErrorLevel(conn, res);
if (errorLevel == 2)
{
ret = readResponse(conn);
res.putValue(SampleResult.SUCCESS, new Boolean(true));
long endTime = System.currentTimeMillis();
if(catClass.isDebugEnabled())
{
catClass.debug("loadBinary1 : end sampling time - " + endTime);
}
res.setTime(endTime - time);
}
else
{
res.putValue(SampleResult.SUCCESS, new Boolean(false));
int responseCode = ((HttpURLConnection)conn).getResponseCode();
String responseMessage = ((HttpURLConnection)conn).getResponseMessage();
catClass.error("loadBinary1 : failed response code - " +
responseCode);
catClass.error("loadBinary1 : failed response message - " +
responseMessage);
}
if(catClass.isDebugEnabled())
{
catClass.debug("loadBinary1 : binary - " + ret[0]+ret[1]);
catClass.debug("loadBinary1 : loadTime - " + res.getTime());
}
catClass.debug("End : loadBinary1");
return ret;
}
/**
* Get the response code of the URL connection and divide it by 100 thus
* returning 2(for 2xx response codes), 3(for 3xx reponse codes), etc
*
* @param conn <code>HttpURLConnection</code> of URL request
* @param res where all results of sampling will be stored
* @return HTTP response code divided by 100
*/
protected int getErrorLevel(HttpURLConnection conn, SampleResult res)
{
catClass.debug("Start : getErrorLevel1");
int errorLevel = 2;
try
{
int responseCode = ((HttpURLConnection) conn).getResponseCode();
String responseMessage = ((HttpURLConnection) conn).getResponseMessage();
errorLevel = responseCode/100;
res.putValue(RESPONSE_CODE, String.valueOf(responseCode));
res.putValue(RESPONSE_MESSAGE, responseMessage);
if(catClass.isDebugEnabled())
{
catClass.debug("getErrorLevel1 : responseCode - " + responseCode);
catClass.debug("getErrorLevel1 : responseMessage - " +
responseMessage);
}
}
catch (Exception e2)
{
System.out.println("getErrorLevel1 : " + conn.getHeaderField(0));
System.out.println("getErrorLevel1 : " + conn.getHeaderFieldKey(0));
catClass.error("getErrorLevel1 : " +
"Error getting response code for HttpUrlConnection - " + e2);
catClass.error("getErrorLevel1 : " +
"Setting SampleResult SUCCESS value to false");
res.putValue(SampleResult.TEXT_RESPONSE, e2.toString().getBytes());
res.putValue(RESPONSE_CODE, NON_HTTP_RESPONSE_CODE);
res.putValue(RESPONSE_MESSAGE, NON_HTTP_RESPONSE_MESSAGE);
res.putValue(SampleResult.SUCCESS, new Boolean(false));
}
catClass.debug("End : getErrorLevel1");
return errorLevel;
}
/**
* Returns <code>tidy</code> as HTML parser
*
* @return a <code>tidy</code> HTML parser
*/
protected static Tidy getParser()
{
catClass.debug("Start : getParser1");
Tidy tidy = new Tidy();
tidy.setCharEncoding(org.w3c.tidy.Configuration.UTF8);
tidy.setQuiet(true);
if(catClass.isDebugEnabled())
{
catClass.debug("getParser1 : tidy parser created - " + tidy);
}
catClass.debug("End : getParser1");
return tidy;
}
/**
* Returns a node representing a whole xml given an xml document
*
* @param text an xml document
* @return a node representing a whole xml
*/
protected static Node getDOM(String text) throws SAXException
{
catClass.debug("Start : getDOM1");
try
{
Node node = getParser().parseDOM(new
ByteArrayInputStream(text.getBytes(getUTFEncodingName())), null);
if(catClass.isDebugEnabled())
{
catClass.debug("node : " + node);
}
catClass.debug("End : getDOM1");
return node;
}
catch(UnsupportedEncodingException e)
{
catClass.error("getDOM1 : Unsupported encoding exception - " + e);
catClass.debug("End : getDOM1");
throw new RuntimeException("UTF-8 encoding failed");
}
}
/**
* Returns the encoding type which is different for different jdks
* even though the mean the same thing i.e. UTF8 or UTF-8
*
* @return either UTF8 or UTF-8 depending on the jdk version
*/
protected static String getUTFEncodingName()
{
catClass.debug("Start : getUTFEncodingName1");
if (utfEncodingName == null)
{
String versionNum = System.getProperty( "java.version" );
if(catClass.isDebugEnabled())
{
catClass.debug("getUTFEncodingName1 : versionNum - " + versionNum);
}
if (versionNum.startsWith( "1.1" ))
{
utfEncodingName = "UTF8";
}
else
{
utfEncodingName = "UTF-8";
}
}
if(catClass.isDebugEnabled())
{
catClass.debug("getUTFEncodingName1 : Returning utfEncodingName - " +
utfEncodingName);
}
catClass.debug("End : getUTFEncodingName1");
return utfEncodingName;
}
public static class Test extends TestCase
{
private HTTPSamplerFull hsf;
private Entry entry;
private static Category catClass =
Category.getInstance(Test.class.getName());
public Test(String name)
{
super(name);
}
protected void setUp()
{
catClass.debug("Start : setUp1");
hsf = new HTTPSamplerFull();
entry = new Entry();
UrlConfigFull urlConfigFull = new UrlConfigFull();
urlConfigFull.setMethod(UrlConfig.GET);
urlConfigFull.setProtocol("file");
// urlConfigFull.setPort(8080);
// urlConfigFull.setDomain("jakarta.apache.org");
urlConfigFull.setPath("HTTPSamplerFullTestFile.txt");
entry.setSamplerClass(HTTPSamplerFull.class);
entry.addConfigElement(urlConfigFull,UrlConfig.class);
catClass.debug("End : setUp1");
}
public void testGetUTFEncodingName()
{
catClass.debug("Start : testGetUTFEncodingName1");
String javaVersion = System.getProperty("java.version");
System.setProperty("java.version", "1.1");
assertEquals("UTF8", HTTPSamplerFull.getUTFEncodingName());
// need to clear utfEncodingName variable first 'cos
// getUTFEncodingName checks to see if it's null
utfEncodingName = null;
System.setProperty("java.version", "1.2");
assertEquals("UTF-8", HTTPSamplerFull.getUTFEncodingName());
System.setProperty("java.version", javaVersion);
catClass.debug("End : testGetUTFEncodingName1");
}
public void testGetUrlConfig()
{
catClass.debug("Start : testGetUrlConfig1");
UrlConfig urlConfig = hsf.getUrlConfig(entry);
assertEquals(UrlConfig.GET, urlConfig.getMethod());
assertEquals("file", urlConfig.getProtocol());
// assertEquals(8080, urlConfig.getPort());
// assertEquals("jakarta.apache.org", urlConfig.getDomain());
assertEquals("HTTPSamplerFullTestFile.txt", urlConfig.getPath());
catClass.debug("End : testGetUrlConfig1");
}
// Can't think of a self-contained way to test this 'cos it requires
// http server. Tried to use file:// but HTTPSampler's sample
// specifically requires http.
public void testSampleMain()
{
catClass.debug("Start : testSampleMain1");
// !ToDo : Have to wait till the day SampleResult is extended to
// store results of all downloaded stuff e.g. images, applets etc
String fileInput = "<html>\n\n" +
"<title>\n" +
" A simple applet\n" +
"</title>\n" +
"<body background=\"back.jpg\" vlink=\"#dd0000\" link=\"#0000ff\">\n" +
"<center>\n" +
"<h2> A simple applet\n" +
"</h2>\n" +
"<br>\n" +
"<br>\n" +
"<table>\n" +
"<td width = 20>\n" +
"<td width = 500 align = left>\n" +
"<img src=\"/tomcat.gif\">\n" +
"<a href=\"Time.java\"> Read my code <a>\n" +
"<p><applet code=Time width = 400 height = 200>\n" +
"</applet>\n" +
"</table>\n" +
"<form>\n" +
" <input type=\"image\" src=\"/tomcat-power.gif\">\n" +
"</form>\n" +
"</body>\n" +
"</html>\n";
byte[] bytes = fileInput.getBytes();
try
{
FileOutputStream fos =
new FileOutputStream("HTTPSamplerFullTestFile.txt");
fos.write(bytes);
fos.close();
}
catch(IOException e)
{
fail("Cannot create HTTPSamplerFullTestFile.txt in current directory " +
"for testing - " + e);
}
// !ToDo
// hsf.sample(entry);
assertNull("Cannot think of way to test sample", null);
catClass.debug("End : testSampleMain1");
}
protected void tearDown()
{
catClass.debug("Start : tearDown1");
hsf = null;
entry = null;
catClass.debug("End : tearDown1");
}
public static void main(String[] args)
{
BasicConfigurator.configure();
TestSuite suite = new TestSuite(Test.class);
junit.textui.TestRunner runner = new junit.textui.TestRunner();
runner.run(suite);
}
}
}