You are viewing a plain text version of this content. The canonical link for it is here.
Posted to watchdog-dev@jakarta.apache.org by rl...@apache.org on 2002/02/02 04:22:53 UTC
cvs commit: jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task CookieController.java GTest.java HttpCookie.java RfcDateParser.java
rlubke 02/02/01 19:22:52
Added: src/tools/org/apache/watchdog/task CookieController.java
GTest.java HttpCookie.java RfcDateParser.java
Log:
- Moved classes to new package
- Added Copyright headers
- Formatted code
Revision Changes Path
1.1 jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/CookieController.java
Index: CookieController.java
===================================================================
/*
* $Header: /home/cvs/jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/CookieController.java,v 1.1 2002/02/02 03:22:52 rlubke Exp $
* $Revision: 1.1 $
* $Date: 2002/02/02 03:22:52 $
*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" 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"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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.watchdog.task;
import java.util.*;
import java.io.*;
import java.net.URLConnection;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.IOException;
import java.net.URL;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
/**
* Represents a collection of Cookie instances.
* <p>
* Fires events when the cookies have been changed internally. Deals
* with management of cookies in terms of saving and loading them,
* and disabling them.
*
* @author Ramesh.Mandava
*/
public class CookieController {
private VetoableChangeSupport vceListeners;
private int listenerNum = 0;
private static Hashtable cookieJar;
private static boolean initialized = false;
/* public no arg constructor for bean */
public CookieController() {
if (!initialized) {
vceListeners = new VetoableChangeSupport(this);
cookieJar = new Hashtable();
initialized = true;
}
}
/////////////////////////////////////////////////////////////
/**
* Records any cookies which have been sent as part of an HTTP response.
* The connection parameter must be already have been opened, so that
* the response headers are available. It's ok to pass a non-HTTP
* URL connection, or one which does not have any set-cookie headers.
*/
public void recordAnyCookies(Vector rcvVectorOfCookies , URL url ) {
if ((rcvVectorOfCookies == null) || ( rcvVectorOfCookies.size()== 0) ) {
// no headers here
return;
}
try {
/*
Properties properties = new Properties();
FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
properties.load(fin);
String cookiepolicy = properties.getProperty("cookie.acceptpolicy");
if (cookiepolicy == null || cookiepolicy.equals("none")) {
return;
}
*/
for (int hi = 0; hi<rcvVectorOfCookies.size(); hi++) {
String cookieValue = (String)rcvVectorOfCookies.elementAt(hi) ;
recordCookie(url, cookieValue); // What to do here
}
}
catch( Exception e )
{
System.out.println("exception : " + e );
}
}
/**
* Create a cookie from the cookie, and use the HttpURLConnection to
* fill in unspecified values in the cookie with defaults.
*/
public void recordCookie(URL url,
String cookieValue) {
HttpCookie cookie = new HttpCookie(url, cookieValue);
// First, check to make sure the cookie's domain matches the
// server's, and has the required number of '.'s
String twodot[]=
{"com", "edu", "net", "org", "gov", "mil", "int"};
String domain = cookie.getDomain();
if( domain == null )
return;
int index = domain.indexOf(':');
if (index != -1) {
int portCookie;
try {
portCookie = (Integer.valueOf(domain.substring(index+1,domain.length()))).intValue();
} catch (Exception e) {
return;
}
portCookie = ( portCookie == -1 ) ? 80 : portCookie;
domain=domain.substring(0,index);
}
domain.toLowerCase();
String host = url.getHost();
host.toLowerCase();
boolean domainOK = host.equals(domain);
if( !domainOK && host.endsWith( domain ) ) {
int dotsNeeded = 2;
for( int i = 0; i < twodot.length; i++ ) {
if( domain.endsWith( twodot[i] ) ) {
dotsNeeded = 1;
}
}
int lastChar = domain.length();
for( ; lastChar > 0 && dotsNeeded > 0; dotsNeeded-- ) {
lastChar = domain.lastIndexOf( '.', lastChar-1 );
}
if( lastChar > 0 )
domainOK = true;
}
if( domainOK ) {
recordCookie(cookie);
}
}
/**
* Record the cookie in the in-memory container of cookies. If there
* is already a cookie which is in the exact same domain with the
* exact same
*/
public void recordCookie(HttpCookie cookie) {
if (!checkIfCookieOK(cookie)) {
return;
}
synchronized (cookieJar) {
String domain = cookie.getDomain().toLowerCase();
Vector cookieList = (Vector)cookieJar.get(domain);
if (cookieList == null) {
cookieList = new Vector();
}
addOrReplaceCookie(cookieList, cookie);
cookieJar.put(domain, cookieList);
}
}
public boolean checkIfCookieOK(HttpCookie cookie) {
try {
if (vceListeners != null) {
vceListeners.fireVetoableChange("cookie", null, cookie );
}
} catch (PropertyVetoException ex) {
return false;
}
return true;
}
/**
* Scans the vector of cookies looking for an exact match with the
* given cookie. Replaces it if there is one, otherwise adds
* one at the end. The vector is presumed to have cookies which all
* have the same domain, so the domain of the cookie is not checked.
* <p>
* <p>
* If this is called, it is assumed that the cookie jar is exclusively
* held by the current thread.
*
*/
private void addOrReplaceCookie(Vector cookies,
HttpCookie cookie) {
int numCookies = cookies.size();
String path = cookie.getPath();
String name = cookie.getName();
HttpCookie replaced = null;
int replacedIndex = -1;
for (int i = 0; i < numCookies; i++) {
HttpCookie existingCookie = (HttpCookie)cookies.elementAt(i);
String existingPath = existingCookie.getPath();
if (path.equals(existingPath)) {
String existingName = existingCookie.getName();
if (name.equals(existingName)) {
// need to replace this one!
replaced = existingCookie;
replacedIndex = i;
break;
}
}
}
// Do the replace - if cookie has already expired, remove
// the replaced cookie.
if (replaced != null) {
if (cookie.isSaveableInMemory()) {
cookies.setElementAt(cookie, replacedIndex);
//System.out.println("REPLACED existing cookie with " + cookie);
} else {
cookies.removeElementAt(replacedIndex);
//System.out.println("Removed cookie b/c or expr " + cookie);
}
} else { // only save the cookie in memory if it is non persistent
// or not expired.
if (cookie.isSaveableInMemory()) {
cookies.addElement(cookie);
//System.out.println("RECORDED new cookie " + cookie);
}
}
}
public String applyRelevantCookies(URL url ) {
try {
/*
Properties properties = new Properties();
FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
properties.load(fin);
// check current accept policy instead enableCookies
String cookiepolicy = properties.getProperty("cookie.acceptpolicy");
if (cookiepolicy == null || cookiepolicy.equals("none")) {
return null;
}
*/
return applyCookiesForHost(url);
}
catch ( Exception e )
{
System.out.println("Exception : " +e );
return null;
}
}
/**
* Host may be a FQDN, or a partial domain name starting with a dot.
* Adds any cookies which match the host and path to the
* cookie set on the URL connection.
*/
private String applyCookiesForHost(URL url ){
String cookieString = null;
Vector cookieVector = getAllRelevantCookies(url);
if (cookieVector != null) {
for (Enumeration e = cookieVector.elements(); e.hasMoreElements();) {
HttpCookie cookie = (HttpCookie)e.nextElement();
if( cookieString == null ) {
cookieString = cookie.getNameValue();
} else {
cookieString = cookieString + "; " + cookie.getNameValue();
}
}
/*
if( cookieString != null ) {
httpConn.setRequestProperty("Cookie", cookieString);
// System.out.println("Returned cookie string: " + cookieString + " for HOST = " + host);
}
*/
}
// System.out.println("Returned cookie string: " + cookieString + " for HOST = " + host);
return cookieString;
}
private Vector getAllRelevantCookies(URL url) {
String host = url.getHost();
Vector cookieVector = getSubsetRelevantCookies(host, url);
Vector tempVector;
int index;
while ((index = host.indexOf('.', 1)) >= 0) {
// trim off everything up to, and including the dot.
host = host.substring(index+1);
// add onto cookieVector
tempVector = getSubsetRelevantCookies(host,url);
if (tempVector != null ) {
for (Enumeration e = tempVector.elements(); e.hasMoreElements(); ) {
if (cookieVector == null) {
cookieVector = new Vector(2);
}
cookieVector.addElement(e.nextElement());
}
}
}
return cookieVector;
}
private Vector getSubsetRelevantCookies(String host, URL url) {
Vector cookieList = (Vector)cookieJar.get(host);
// System.out.println("getRelevantCookies() .. for host, url " + host +", "+url);
Vector cookiePortList = (Vector)cookieJar.get(host+":"+((url.getPort() == -1) ? 80 : url.getPort()));
if (cookiePortList != null) {
if (cookieList == null) {
cookieList = new Vector(10);
}
Enumeration cookies = cookiePortList.elements();
while (cookies.hasMoreElements()) {
cookieList.addElement(cookies.nextElement());
}
}
if (cookieList == null) {
return null;
}
String path = url.getFile();
// System.out.println(" path is " + path + "; protocol = " + url.getProtocol());
int queryInd = path.indexOf('?');
if (queryInd > 0) {
// strip off the part following the ?
path = path.substring(0, queryInd);
}
Enumeration cookies = cookieList.elements();
Vector cookiesToSend = new Vector(10);
while (cookies.hasMoreElements()) {
HttpCookie cookie = (HttpCookie)cookies.nextElement();
String cookiePath = cookie.getPath();
if (path.startsWith(cookiePath)) {
// larrylf: Actually, my documentation (from Netscape)
// says that /foo should
// match /foobar and /foo/bar. Yuck!!!
if (!cookie.hasExpired()) {
cookiesToSend.addElement(cookie);
}
/*
We're keeping this piece of commented out code around just in
case we decide to put it back. the spec does specify the above,
but it is so disgusting!
int cookiePathLen = cookiePath.length();
// verify that /foo does not match /foobar by mistake
if ((path.length() == cookiePathLen)
|| (path.length() > cookiePathLen &&
path.charAt(cookiePathLen) == '/')) {
// We have a matching cookie!
if (!cookie.hasExpired()) {
cookiesToSend.addElement(cookie);
}
}
*/
}
}
// Now, sort the cookies in most to least specific order
// Yes, its the deaded bubblesort!! Wah Ha-ha-ha-ha....
// (it should be a small vector, so perf is not an issue...)
if( cookiesToSend.size() > 1 ) {
for( int i = 0; i < cookiesToSend.size()-1; i++ ) {
HttpCookie headC = (HttpCookie)cookiesToSend.elementAt(i);
String head = headC.getPath();
// This little excercise is a cheap way to get
// '/foo' to read more specfic then '/'
if( !head.endsWith("/") ) {
head = head + "/";
}
for( int j = i+1; j < cookiesToSend.size(); j++ ) {
HttpCookie scanC = (HttpCookie)cookiesToSend.elementAt(j);
String scan = scanC.getPath();
if( !scan.endsWith("/") ) {
scan = scan + "/";
}
int headCount = 0;
int index = -1;
while( (index=head.indexOf('/', index+1)) != -1 ) {
headCount++;
}
index = -1;
int scanCount = 0;
while( (index=scan.indexOf('/', index+1)) != -1 ) {
scanCount++;
}
if( scanCount > headCount ) {
cookiesToSend.setElementAt(headC, j);
cookiesToSend.setElementAt(scanC, i);
headC = scanC;
head = scan;
}
}
}
}
return cookiesToSend;
}
/*
* Writes cookies out to PrintWriter if they are persistent
* (i.e. have a expr date)
* and haven't expired. Will remove cookies that have expired as well
*/
private void saveCookiesToStream(PrintWriter pw) {
Enumeration cookieLists = cookieJar.elements();
while (cookieLists.hasMoreElements()) {
Vector cookieList = (Vector)cookieLists.nextElement();
Enumeration cookies = cookieList.elements();
while (cookies.hasMoreElements()) {
HttpCookie cookie = (HttpCookie)cookies.nextElement();
if (cookie.getExpirationDate() != null) {
if (cookie.isSaveable()) {
pw.println(cookie);
} else { // the cookie must have expired,
//remove from Vector cookieList
cookieList.removeElement(cookie);
}
}
}
}
// Must print something to the printwriter in the case that
// the cookieJar has been cleared - otherwise the old cookie
// file will continue to exist.
pw.print("");
}
/////////////////////////////////////////////////////////////
/* adds cookieList to the existing cookie jar*/
public void addToCookieJar(HttpCookie[] cookieList) {
if (cookieList != null) {
for (int i = 0; i < cookieList.length; i++) {
recordCookie(cookieList[i]);
}
}
}
/*adds one cookie to the Cookie Jar */
public void addToCookieJar(String cookieString, URL docURL) {
recordCookie(new HttpCookie(docURL, cookieString));
}
/* loads the cookies from the given filename */
public void loadCookieJarFromFile(String cookieFileName) {
try {
FileReader fr = new FileReader(cookieFileName);
BufferedReader in = new BufferedReader(fr);
try {
String cookieString;
while ((cookieString = in.readLine()) != null) {
HttpCookie cookie = new HttpCookie(cookieString);
// Record the cookie, without notification. We don't
// do a notification for cookies that are read at
// program start-up.
recordCookie(cookie);
}
} finally {
in.close();
}
} catch (IOException e) {
// do nothing; it's not an error not to have persistent cookies
}
}
/* saves the cookies to the given file specified by fname */
public void saveCookieJarToFile(String cookieFileName) {
try {
FileWriter fw = new FileWriter(cookieFileName);
PrintWriter pw = new PrintWriter(fw, false);
try {
saveCookiesToStream(pw);
} finally {
pw.close();
}
} catch (IOException e) {
// REMIND: I18N
System.err.println("Saving cookies failed " + e.getMessage());
}
}
/**
* Return an array with all of the cookies represented by this
* jar. This is useful when the bean is shutting down, and the client
* wants to make the cookie jar persist.
*/
public HttpCookie[] getAllCookies() {
Vector result = new Vector();
Hashtable jar;
jar = (Hashtable) cookieJar.clone();
synchronized (jar) {
for (Enumeration e = jar.elements(); e.hasMoreElements() ;) {
Vector v = (Vector) e.nextElement();
for (int i = 0; i < v.size(); i++) {
HttpCookie hc = (HttpCookie) v.elementAt(i);
result.addElement(hc);
}
}
}
HttpCookie[] resultA = new HttpCookie[result.size()];
for (int i = 0; i < result.size(); i++) {
resultA[i] = (HttpCookie) result.elementAt(i);
}
return resultA;
}
/* Gets all cookies that applies for the URL */
public HttpCookie[] getCookiesForURL(URL url) {
Vector cookieVector = getAllRelevantCookies(url);
if (cookieVector == null) {
return null;
}
int i = 0;
HttpCookie[] cookieArr = new HttpCookie[cookieVector.size()];
for (Enumeration e = cookieVector.elements(); e.hasMoreElements(); ) {
cookieArr[i++] = (HttpCookie)e.nextElement();
// System.out.println("cookieArr["+(i-1)+"] = " +cookieArr[i-1].toString());
}
return cookieArr;
}
/* this will set the property of enableCookies to isDisabled */
public void setCookieDisable(boolean isDisabled) {
// Pending visit back this again
try {
Properties properties = new Properties();
properties.load(new FileInputStream("ServerAutoRun.properties") );
properties.put("enableCookies", isDisabled ? "false" : "true");
properties.store(new FileOutputStream("ServerAutoRun.properties"),"comments");
}
catch ( Exception e )
{
System.out.println("Exception : " + e );
}
}
public void discardAllCookies() {
try {
if (vceListeners != null) {
vceListeners.fireVetoableChange("cookie", null, null);
}
} catch (PropertyVetoException ex) {
}
cookieJar.clear();
}
/*
* purges any expired cookies in the Cookie hashtable.
*/
public void purgeExpiredCookies() {
Enumeration cookieLists = cookieJar.elements();
while (cookieLists.hasMoreElements()) {
Vector cookieList = (Vector)cookieLists.nextElement();
Enumeration cookies = cookieList.elements();
while (cookies.hasMoreElements()) {
HttpCookie cookie = (HttpCookie)cookies.nextElement();
if (cookie.hasExpired()) {
cookieList.removeElement(cookie);
}
}
}
}
/*********************
* Listener methods
*********************/
/* Add listener to CookieJar. If the first listener registers,
* add CookieJar's listener to sunw.hotjava.misc.Cookies. This
* management done to avoid having unnecessary object retention*/
public void addVetoableChangeListener(VetoableChangeListener l) {
vceListeners.addVetoableChangeListener(l);
}
/* Removes listener to CookieJar. If there are no more listeners
* to CookieJar, remove CookieJar's listener to sunw.hotjava.misc.Cookies.
*/
public void removeVetoableChangeListener(VetoableChangeListener l) {
vceListeners.removeVetoableChangeListener(l);
}
/*********
* Older code that could serve as a reminder for future
*********/
/**
* Predicate function which returns true if the cookie appears to be
* invalid somehow and should not be added to the cookie set.
*/
/* This code used to be called in recordCookie(HttpCookie cookie),
however, I couldn't figure out what it would be good for, so
I took it out for now. See the misc.Cookies in deleted file
for more context*/
private boolean shouldRejectCookie(HttpCookie cookie) {
// REMIND: implement per http-state-mgmt Internet Draft
return false;
}
}
1.1 jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/GTest.java
Index: GTest.java
===================================================================
/*
* $Header: /home/cvs/jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/GTest.java,v 1.1 2002/02/02 03:22:52 rlubke Exp $
* $Revision: 1.1 $
* $Date: 2002/02/02 03:22:52 $
*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" 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"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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/>.
*
*/
/**
* @Author Costin, Ramesh.Mandava
*/
package org.apache.watchdog.task;
import java.net.*;
import java.io.*;
import java.util.*;
import java.net.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
// derived from Jsp
public class GTest extends Task implements TaskContainer {
private static final String ZEROS = "00000000";
private static final String CRLF = "\r\n";
private static final int SHORTPADSIZE = 4;
private static final int BYTEPADSIZE = 2;
private static final int CARRIAGE_RETURN = 13;
private static final int LINE_FEED = 10;
private static int failureCount = 0;
private static int passCount = 0;
private static boolean hasFailed = false;
String prefix = "http";
String host = "localhost";
int port = 8080;
int debug = 0;
private ArrayList children = new ArrayList();
String description = "No description";
String request;
HashMap requestHeaders = new HashMap();
String content;
// true if task is nested
private boolean nested = false;
// Expected response
boolean magnitude = true;
boolean exactMatch = false;
// expect a response body
boolean expectResponseBody = true;
// Match the body against a golden file
String goldenFile;
// Match the body against a string
String responseMatch;
// the response should include the following headers
HashMap expectHeaders = new HashMap();
// Headers that should not be found in response
HashMap unexpectedHeaders = new HashMap();
// Match request line
String returnCode = "";
String returnCodeMsg = "";
// Actual response
String responseLine;
byte[] responseBody;
HashMap headers;
// For Report generation
static String resultFileName = null;
static FileOutputStream resultOut = null;
boolean firstTask = false;
boolean lastTask = false;
String expectedString;
String actualString;
String testName;
String assertion;
String testStrategy;
// For Session Tracking
static Hashtable sessionHash;
static Hashtable cookieHash;
String testSession;
Vector cookieVector;
URL requestURL;
CookieController cookieController ;
/**
* Creates a new <code>GTest</code> instance.
*
*/
public GTest() {
}
/**
* <code>setTestSession</code> adds a
* CookieController for the value of sessionName
*
* @param sessionName a <code>String</code> value
*/
public void setTestSession( String sessionName ) {
testSession = sessionName;
if ( sessionHash == null ) {
sessionHash = new Hashtable();
} else if ( sessionHash.get( sessionName ) == null ) {
sessionHash.put ( sessionName, new CookieController() );
}
}
/**
* <code>setTestName</code> sets the current test name.
*
* @param tn current testname.
*/
public void setTestName ( String tn ) {
testName = tn;
}
/**
* <code>setAssertion</code> sets the assertion text
* for the current test.
*
* @param assertion assertion text
*/
public void setAssertion ( String assertion ) {
this.assertion = assertion;
}
/**
* <code>setTestStrategy</code> sets the test strategy
* for the current test.
*
* @param strategy test strategy text
*/
public void setTestStrategy ( String strategy ) {
testStrategy = strategy;
}
/**
* <code>getTestName</code> returns the current
* test name.
*
* @return a <code>String</code> value
*/
public String getTestName( ) {
return testName;
}
/**
* <code>getAssertion</code> returns the current
* assertion text.
*
* @return a <code>String</code> value
*/
public String getAssertion( ) {
return assertion;
}
/**
* <code>getTestStrategy</code> returns the current
* test strategy test.
*
* @return a <code>String</code> value
*/
public String getTestStrategy( ) {
return testStrategy;
}
/**
* <code>setResultFileName</code> allows the user
* to set the filename in which to write test results
* to.
*
* @param fileName result filename
* @exception IOException if an error occurs
*/
public void setResultFileName( String fileName )
throws IOException {
if ( firstTask ) {
resultFileName = fileName;
File passedFile = new File( fileName );
System.out.println( "Full Path of Result File-> " + passedFile.getAbsolutePath() );
resultOut = new FileOutputStream( passedFile );
if ( resultOut == null ) {
System.out.println( "ERROR: Not able to create FileOutputStream for result" );
} else {
resultOut.write( "<root>\n".getBytes() );
}
}
}
/**
* <code>setFirstTask</code> denotes that current task
* being executed is the first task within the list.
*
* @param a <code>boolean</code> value
*/
public void setFirstTask( boolean val ) {
firstTask = val;
}
/**
* <code>setLastTask</code> denotes that the current task
* being executed is the last task within the list.
*
* @param a <code>boolean</code> value
*/
public void setLastTask ( boolean val ) {
lastTask = val;
}
/**
* <code>setPrefix</code> sets the protocol
* prefix. Defaults to "http"
*
* @param prefix Either http or https
*/
public void setPrefix( String prefix ) {
this.prefix = prefix;
}
/**
* <code>setHost</code> sets hostname where
* the target server is running. Defaults to
* "localhost"
*
* @param h a <code>String</code> value
*/
public void setHost( String h ) {
this.host = h;
}
/**
* <code>setPort</code> sets the port
* that the target server is listening on.
* Defaults to "8080"
*
* @param portS a <code>String</code> value
*/
public void setPort( String portS ) {
this.port = Integer.valueOf( portS ).intValue();
}
/**
* <code>setExactMatch</code> determines if a
* byte-by-byte comparsion is made of the server's
* response and the test's goldenFile, or if
* a token comparison is made. By default, only
* a token comparison is made ("false").
*
* @param exact a <code>String</code> value
*/
public void setExactMatch( String exact ) {
exactMatch = Boolean.valueOf( exact ).booleanValue();
}
/**
* <code>setContent</code> String value upon which
* the request header Content-Length is based upon.
*
* @param s a <code>String</code> value
*/
public void setContent( String s ) {
this.content = s;
}
/**
* <code>setDebug</code> enables debug output.
* By default, this is disabled ( value of "0" ).
*
* @param debugS a <code>String</code> value
*/
public void setDebug( String debugS ) {
debug = Integer.valueOf( debugS ).intValue();
}
/**
* <code>setMagnitude</code> Expected return
* value of the test execution.
* Defaults to "true"
*
* @param magnitudeS a <code>String</code> value
*/
public void setMagnitude( String magnitudeS ) {
magnitude = Boolean.valueOf( magnitudeS ).booleanValue();
}
/**
* <code>setGoldenFile</code> Sets the goldenfile
* that will be used to validate the server's response.
*
* @param s fully qualified path and filename
*/
public void setGoldenFile( String s ) {
this.goldenFile = s;
}
/**
* <code>setExpectResponseBody</code> sets a flag
* to indicate if a response body is expected from the
* server or not
*
* @param b a <code>boolean</code> value
*/
public void setExpectResponseBody( boolean b ) {
this.expectResponseBody = b;
}
/**
* <code>setExpectHeaders</code> Configures GTest
* to look for the header passed in the server's
* response.
*
* @param s a <code>String</code> value in the
* format of <header-field>:<header-value>
*/
public void setExpectHeaders( String s ) {
this.expectHeaders = new HashMap();
StringTokenizer tok = new StringTokenizer( s, "|" );
while ( tok.hasMoreElements() ) {
String header = (String) tok.nextElement();
setHeaderDetails( header, expectHeaders, false );
}
}
/**
* <code>setUnexpectedHeaders</code> Configures GTest
* to look for the header passed to validate that it
* doesn't exist in the server's response.
*
* @param s a <code>String</code> value in the
* format of <header-field>:<header-value>
*/
public void setUnexpectedHeaders( String s ) {
this.unexpectedHeaders = new HashMap();
setHeaderDetails( s, unexpectedHeaders, false );
}
public void setNested( String s ) {
nested = Boolean.valueOf( s ).booleanValue();
}
/**
* <code>setResponseMatch</code> Match the
* passed value in the server's response.
*
* @param s a <code>String</code> value
*/
public void setResponseMatch( String s ) {
this.responseMatch = s;
}
/**
* <code>setRequest</code> Sets the HTTP/HTTPS
* request to be sent to the target server
* Ex.
* GET /servlet_path/val HTTP/1.0
*
* @param s a <code>String</code> value in the form
* of METHOD PATH HTTP_VERSION
* @exception Exception if an error occurs
*/
public void setRequest ( String s ) throws Exception {
this.request = s;
String addressString = request.substring( request.indexOf( "/" ), request.indexOf( "HTTP" ) ).trim();
if ( addressString.indexOf( "?" ) > -1 ) {
addressString = addressString.substring( 0, addressString.indexOf( "?" ) ) ;
}
requestURL = new URL( "http", host, port, addressString );
}
/**
* <code>setReturnCode</code> Sets the expected
* return code from the server's response.
*
* @param code a valid HTTP response status code
*/
public void setReturnCode( String code ) {
this.returnCode = code;
}
/**
* Describe <code>setReturnCodeMsg</code> Sets the expected
* return message to be found in the server's
* response.
*
* @param code a valid HTTP resonse status code
* @param message a <code>String</code> value
*/
public void setReturnCodeMsg( String message ) {
this.returnCodeMsg = message;
}
/**
* <code>setRequestHeaders</code> Configures the request
* headers GTest should send to the target server.
*
* @param s a <code>String</code> value in for format
* of <field-name>:<field-value>
*/
public void setRequestHeaders( String s ) {
requestHeaders = new HashMap();
StringTokenizer tok = new StringTokenizer( s, "|" );
while ( tok.hasMoreElements() ) {
String header = (String) tok.nextElement();
setHeaderDetails( header, requestHeaders, true );
}
}
/**
* Add a Task to this container
*
* @param Task to add
*/
public void addTask(Task task) {
children.add(task);
}
/**
* <code>execute</code> Executes the test.
*
* @exception BuildException if an error occurs
*/
public void execute() throws BuildException {
try {
if ( resultOut != null && !nested ) {
resultOut.write( "<test>".getBytes() );
resultOut.write( ( "\n<testName>" + testName + "</testName>" ).getBytes() );
resultOut.write( ( "\n<assertion>" + assertion + "</assertion>" ).getBytes() );
resultOut.write( ( "\n<testStrategy>" + testStrategy + "</testStrategy>\n" ).getBytes() );
}
dispatch( request, requestHeaders );
boolean result = checkResponse( magnitude );
if ( !result ) {
hasFailed = true;
}
if ( !children.isEmpty() ) {
Iterator iter = children.iterator();
while (iter.hasNext()) {
Task task = (Task) iter.next();
task.perform();
}
}
if ( !hasFailed && !nested ) {
passCount++;
if ( resultOut != null ) {
resultOut.write( "<result>PASS</result>\n".getBytes() );
}
if ( testName != null ) {
System.out.println( " PASSED " + testName + "\n (" + request + ")" );
} else {
System.out.println( " PASSED " + request );
}
} else if ( hasFailed && !nested ){
failureCount++;
if ( resultOut != null ) {
resultOut.write( "<result>FAIL</result>\n".getBytes() );
}
if ( testName != null ) {
System.out.println( " FAILED " + testName + "\n (" + request + ")" );
} else {
System.out.println( " FAILED " + request );
}
}
if ( resultOut != null && !nested ) {
resultOut.write( "</test>\n".getBytes() );
if ( lastTask ) {
resultOut.write( "</root>\n".getBytes() );
resultOut.close();
}
}
if ( lastTask ) {
System.out.println( "\n\n------- TEST SUMMARY -------\n" );
System.out.println( "*** " + passCount + " TEST(S) PASSED! ***" );
System.out.println( "*** " + failureCount + " TEST(S) FAILED! ***" );
}
} catch ( Exception ex ) {
failureCount++;
if ( "No description".equals( description ) ) {
System.out.println( " FAIL " + request );
} else
System.out.println( " FAIL " + description + " (" + request + ")" );
ex.printStackTrace();
} finally {
if ( !nested ) {
hasFailed = false;
}
}
}
/**
* <code>checkResponse</code> Executes various response
* checking mechanisms against the server's response.
* Checks include:
* <ul>
* <li>expected headers
* <li>unexpected headers
* <li>return codes and messages in the Status-Line
* <li>response body comparison againt a goldenfile
* </ul>
*
* @param testCondition a <code>boolean</code> value
* @return a <code>boolean</code> value
* @exception Exception if an error occurs
*/
private boolean checkResponse( boolean testCondition )
throws Exception {
boolean match = false;
if ( responseLine != null ) {
// If returnCode doesn't match
if ( responseLine.indexOf( "HTTP/1." ) > -1 ) {
if ( !returnCode.equals( "" ) ) {
boolean resCode = ( responseLine.indexOf( returnCode ) > -1 );
boolean resMsg = ( responseLine.indexOf( returnCodeMsg ) > -1 );
if ( returnCodeMsg.equals( "" ) ) {
match = resCode;
} else {
match = ( resCode && resMsg );
}
if ( match != testCondition ) {
System.out.println( " Error in: " + request );
System.out.println( " Expected Status-Line with one or all of the following values:" );
System.out.println( " Status-Code: " + returnCode );
System.out.println( " Reason-Phrase: " + returnCodeMsg );
System.out.println( " Received: " + responseLine );
if ( resultOut != null ) {
String expectedStatusCode = "<expectedStatusCode>" + returnCode + "</expectedReturnCode>\n";
String expectedReasonPhrase = "<expectedReasonPhrase>" + returnCodeMsg + "</expectedReasonPhrase>";
actualString = "<actualStatusLine>" + responseLine + "</actualStatusLine>\n";
resultOut.write( expectedStatusCode.getBytes() );
resultOut.write( expectedReasonPhrase.getBytes() );
resultOut.write( actualString.getBytes() );
}
return false;
} else {
if ( debug > 0 ) {
System.out.println( " Expected values found in Status-Line" );
}
}
}
} else {
System.out.println( " Error: Received invalid HTTP version in response header from target Server" );
System.out.println( " Target server must support HTTP 1.0 or HTTP 1.1" );
System.out.println( " Response from server: " + responseLine );
return false;
}
} else {
System.out.println( " Error in: " + request );
System.out.println( " Expecting response from server, received null" );
return false;
}
/*
* Check for headers the test expects to be in the server's response
*/
// Duplicate set of response headers
HashMap copiedHeaders = cloneHeaders( headers );
// used for error reporting
String currentHeaderField = null;
String currentHeaderValue = null;
if ( !expectHeaders.isEmpty() ) {
boolean found = false;
String expHeader = null;
if ( debug > 0 ) {
System.out.println( " Looking for expected response headers..." );
}
if ( !headers.isEmpty() ) {
Iterator expectIterator = expectHeaders.keySet().iterator();
while ( expectIterator.hasNext() ) {
found = false;
String expFieldName = (String) expectIterator.next();
currentHeaderField = expFieldName;
ArrayList expectValues = (ArrayList) expectHeaders.get( expFieldName );
Iterator headersIterator = copiedHeaders.keySet().iterator();
while( headersIterator.hasNext() ) {
String headerFieldName = (String) headersIterator.next();
ArrayList headerValues = (ArrayList) copiedHeaders.get( headerFieldName );
// compare field names and values in an HTTP 1.x compliant fashion
if ( ( headerFieldName.equalsIgnoreCase( expFieldName ) ) ) {
int hSize = headerValues.size();
int eSize = expectValues.size();
// number of expected headers found in server response
int numberFound = 0;
for ( int i = 0; i < eSize; i++ ) {
currentHeaderValue = (String) expectValues.get( i );
/*
* Handle the Content-Type header appropriately
* based on the the test is configured to look for.
*/
if ( currentHeaderField.equalsIgnoreCase( "content-type" ) ) {
String resVal = (String) headerValues.get( 0 );
if ( currentHeaderValue.indexOf( ';' ) > -1 ) {
if ( debug > 0 ) {
System.out.println( " Exact match for Content-Type header required." );
}
if ( currentHeaderValue.equals( resVal ) ) {
numberFound++;
headerValues.remove( 0 );
}
} else if ( resVal.indexOf( currentHeaderValue ) > -1 ) {
if ( debug > 0 ) {
System.out.println( " Approximate match for Content-Type header required." );
}
numberFound++;
headerValues.remove( 0 );
}
} else if ( headerValues.contains( currentHeaderValue ) ) {
numberFound++;
headerValues.remove( headerValues.indexOf( currentHeaderValue ) );
}
}
if ( numberFound == eSize ) {
found = true;
}
}
}
if ( !found ) {
/*
* Expected headers not found in server response.
* Break the processing loop.
*/
break;
}
}
}
if ( !found ) {
StringBuffer actualBuffer = new StringBuffer( 128 );
System.out.println( " Unable to find the expected header: '" + currentHeaderField + ": " + currentHeaderValue + "' in the server's response." );
if ( resultOut != null ) {
expectedString = "<expectedHeader>" + currentHeaderField + ": " + currentHeaderValue + "</expectedHeader>\n";
}
if ( !headers.isEmpty() ) {
System.out.println( " The following headers were received: " );
Iterator iter = headers.keySet().iterator();
while ( iter.hasNext() ) {
String headerName = (String) iter.next();
ArrayList vals = (ArrayList) headers.get( headerName );
String[] val = (String[]) vals.toArray( new String[ vals.size() ] );
for ( int i = 0; i < val.length; i++ ) {
System.out.println( "\tHEADER -> " + headerName + ": " + val[ i ] );
if ( resultOut != null ) {
actualBuffer.append( "<actualHeader>" + headerName + ": " + val[ i ] + "</actualHeader>\n" );
}
}
}
if ( resultOut != null ) {
resultOut.write( expectedString.getBytes() );
resultOut.write( actualBuffer.toString().getBytes() );
}
}
return false;
}
}
/*
* Check to see if we're looking for unexpected headers.
* If we are, compare the values in the unexectedHeaders
* ArrayList against the headers from the server response.
* if the unexpected header is found, then return false.
*/
if ( !unexpectedHeaders.isEmpty() ) {
boolean found = false;
String unExpHeader = null;
if ( debug > 0 ) {
System.out.println( " looking for unexpected headers..." );
}
// Check if we got any unexpected headers
if ( !copiedHeaders.isEmpty() ) {
Iterator unexpectedIterator = unexpectedHeaders.keySet().iterator();
while ( unexpectedIterator.hasNext() ) {
found = false;
String unexpectedFieldName = (String) unexpectedIterator.next();
ArrayList unexpectedValues = (ArrayList) unexpectedHeaders.get( unexpectedFieldName );
Iterator headersIterator = copiedHeaders.keySet().iterator();
while ( headersIterator.hasNext() ) {
String headerFieldName = (String) headersIterator.next();
ArrayList headerValues = (ArrayList) copiedHeaders.get( headerFieldName );
// compare field names and values in an HTTP 1.x compliant fashion
if ( ( headerFieldName.equalsIgnoreCase( unexpectedFieldName ) ) ) {
int hSize = headerValues.size();
int eSize = unexpectedValues.size();
int numberFound = 0;
for ( int i = 0; i < eSize; i++ ) {
if ( headerValues.contains( unexpectedValues.get( i ) ) ) {
numberFound++;
headerValues.remove( headerValues.indexOf( headerFieldName ) );
}
}
if ( numberFound == eSize ) {
found = true;
}
}
}
if ( !found ) {
/*
* Expected headers not found in server response.
* Break the processing loop.
*/
break;
}
}
}
if ( found ) {
System.out.println( " Unexpected header received from server: " + unExpHeader );
return false;
}
}
if ( responseMatch != null ) {
// check if we got the string we wanted
if ( expectResponseBody && responseBody == null ) {
System.out.println( " ERROR: got no response, expecting " + responseMatch );
return false;
}
String responseBodyString = new String( responseBody );
if ( responseBodyString.indexOf( responseMatch ) < 0 ) {
System.out.println( " ERROR: expecting match on " + responseMatch );
System.out.println( "Received: " );
System.out.println( responseBodyString );
}
}
if ( !expectResponseBody && responseBody != null ) {
if ( debug > 0 ) {
System.out.println( "Received a response body from the server where none was expected" );
}
return false;
}
// compare the body
if ( goldenFile == null )
return true;
// Get the expected result from the "golden" file.
byte[] expResult = getExpectedResult();
// Compare the results and set the status
boolean cmp = true;
if ( exactMatch ) {
if ( debug > 0 ) {
System.out.println( " Performing exact match of server response and goldenfile" );
}
cmp = compare( responseBody, expResult );
} else {
if ( debug > 0 ) {
System.out.println( " Performing token match of server response and goldenfile" );
}
cmp = compareWeak( responseBody, expResult );
}
if ( cmp != testCondition ) {
if ( resultOut != null ) {
expectedString = "<expectedBody>" + new String( expResult ) + "</expectedBody>\n";
actualString = "<actualBody>" + new String( responseBody ) + "</actualBody>\n";
resultOut.write( expectedString.getBytes() );
resultOut.write( actualString.getBytes() );
}
return false;
}
return true;
}
/**
* <code>dispatch</code> sends the request and any
* configured request headers to the target server.
*
* @param request a <code>String</code> value
* @param requestHeaders a <code>HashMap</code> value
*/
private void dispatch( String request, HashMap requestHeaders )
throws Exception {
// XXX headers are ignored
Socket socket = new Socket( host, port );
InputStream in = new CRBufferedInputStream( socket.getInputStream() );
// Write the request
socket.setSoLinger( true, 1000 );
OutputStream out = new BufferedOutputStream(
socket.getOutputStream() );
StringBuffer reqbuf = new StringBuffer( 128 );
// set the Host header
setHeaderDetails( "Host:" + host, requestHeaders, true );
// set the Content-Length header
if ( content != null ) {
setHeaderDetails( "Content-Length:" + content.length(),
requestHeaders, true );
}
// set the Cookie header
if ( testSession != null ) {
cookieController = ( CookieController ) sessionHash.get( testSession );
if ( cookieController != null ) {
String releventCookieString = cookieController.applyRelevantCookies( requestURL );
if ( ( releventCookieString != null ) && ( !releventCookieString.trim().equals( "" ) ) ) {
setHeaderDetails( "Cookie:" + releventCookieString, requestHeaders, true );
}
}
}
if ( debug > 0 ) {
System.out.println( " REQUEST: " + request );
}
reqbuf.append( request ).append( CRLF );;
// append all rquest headers
if ( !requestHeaders.isEmpty() ) {
Iterator iter = requestHeaders.keySet().iterator();
while ( iter.hasNext() ) {
String headerKey = ( String ) iter.next();
ArrayList values = (ArrayList) requestHeaders.get( headerKey );
String[] value = (String[]) values.toArray( new String[ values.size() ] );
for ( int i = 0; i < value.length; i++ ) {
reqbuf.append( headerKey ).append( ": " ).append( value[ i ] ).append( CRLF );
if ( debug > 0 ) {
System.out.println( " REQUEST HEADER: " + headerKey + ": " + value[ i ] );
}
}
}
}
/*
if ( ( testSession != null ) && ( sessionHash.get( testSession ) != null ) ) {
System.out.println("Sending Session Id : " + (String)sessionHash.get( testSession ) );
pw.println("JSESSIONID:" + (String)sessionHash.get( testSession) );
}
*/
if ( request.indexOf( "HTTP/1." ) > -1 ) {
reqbuf.append( "" ).append( CRLF );
}
// append request content
if ( content != null ) {
reqbuf.append( content );
// XXX no CRLF at the end -see HTTP specs!
}
byte[] reqbytes = reqbuf.toString().getBytes();
try {
// write the request
out.write( reqbytes, 0, reqbytes.length );
out.flush();
reqbuf = null;
} catch ( Exception ex1 ) {
System.out.println( " Error writing request " + ex1 );
if ( debug > 0 ) {
System.out.println( "Message: " + ex1.getMessage() );
ex1.printStackTrace();
}
}
// read the response
try {
responseLine = read( in );
if ( debug > 0 ) {
System.out.println( " RESPONSE STATUS-LINE: " + responseLine );
}
headers = parseHeaders( in );
byte[] result = readBody( in );
if ( result != null ) {
responseBody = result;
if ( debug > 0 ) {
System.out.println( " RESPONSE BODY:\n" + new String( responseBody ) );
}
}
} catch ( SocketException ex ) {
System.out.println( " Socket Exception: " + ex );
ex.printStackTrace();
} finally {
if ( debug > 0 ) {
System.out.println( " closing socket" );
}
socket.close();
socket = null;
}
}
/**
* <code>getExpectedResult</code> returns a byte array
* containing the content of the configured goldenfile
*
* @return goldenfile as a byte[]
* @exception IOException if an error occurs
*/
private byte[] getExpectedResult()
throws IOException {
byte[] expResult = { 'N','O',' ',
'G','O','L','D','E','N','F','I','L','E',' ',
'F','O','U','N','D' };
try {
InputStream in = new BufferedInputStream(
new FileInputStream( goldenFile ) );
return readBody ( in );
} catch ( Exception ex ) {
System.out.println( "Golden file not found: " + goldenFile );
return expResult;
}
}
/**
* <code>compare</code> compares the two byte arrays passed
* in to verify that the lengths of the arrays are equal, and
* that the content of the two arrays, byte for byte are equal.
*
* @param fromServer a <code>byte[]</code> value
* @param fromGoldenFile a <code>byte[]</code> value
* @return <code>boolean</code> true if equal, otherwise false
*/
private boolean compare( byte[] fromServer, byte[] fromGoldenFile ) {
if ( fromServer == null || fromGoldenFile == null ) {
return false;
}
/*
* Check to see that the respose and golden file lengths
* are equal. If they are not, dump the hex and don't
* bother comparing the bytes. If they are equal,
* iterate through the byte arrays and compare each byte.
* If the bytes don't match, dump the hex representation
* of the server response and the goldenfile and return
* false.
*/
if ( fromServer.length != fromGoldenFile.length ) {
StringBuffer sb = new StringBuffer( 50 );
sb.append( " Response and golden files lengths do not match!\n" );
sb.append( " Server response length: " );
sb.append( fromServer.length );
sb.append( "\n Goldenfile length: " );
sb.append( fromGoldenFile.length );
System.out.println( sb.toString() );
sb = null;
// dump the hex representation of the byte arrays
dumpHex( fromServer, fromGoldenFile );
return false;
} else {
int i = 0;
int j = 0;
while ( ( i < fromServer.length ) && ( j < fromGoldenFile.length ) ) {
if ( fromServer[ i ] != fromGoldenFile[ j ] ) {
System.out.println( " Error at position " + ( i + 1 ) );
// dump the hex representation of the byte arrays
dumpHex( fromServer, fromGoldenFile );
return false;
}
i++;
j++;
}
}
return true;
}
/**
* <code>compareWeak</code> creates new Strings from the passed arrays
* and then uses a StringTokenizer to compare non-whitespace tokens.
*
* @param fromServer a <code>byte[]</code> value
* @param fromGoldenFile a <code>byte[]</code> value
* @return a <code>boolean</code> value
*/
private boolean compareWeak( byte[] fromServer, byte[] fromGoldenFile ) {
if ( fromServer == null || fromGoldenFile == null ) {
return false;
}
boolean status = true;
String server = new String( fromServer );
String golden = new String( fromGoldenFile );
StringTokenizer st1 = new StringTokenizer( server );
StringTokenizer st2 = new StringTokenizer( golden );
while ( st1.hasMoreTokens() && st2.hasMoreTokens() ) {
String tok1 = st1.nextToken();
String tok2 = st2.nextToken();
if ( !tok1.equals( tok2 ) ) {
System.out.println( "\t FAIL*** : Rtok1 = " + tok1
+ ", Etok2 = " + tok2 );
status = false;
}
}
if ( st1.hasMoreTokens() || st2.hasMoreTokens() ) {
status = false;
}
if ( !status ) {
StringBuffer sb = new StringBuffer( 255 );
sb.append( "ERROR: Server's response and configured goldenfile do not match!\n" );
sb.append( "Response received from server:\n" );
sb.append( "---------------------------------------------------------\n" );
sb.append( server );
sb.append( "\nContent of Goldenfile:\n" );
sb.append( "---------------------------------------------------------\n" );
sb.append( golden );
sb.append( "\n" );
System.out.println( sb.toString() );
}
return status;
}
/**
* <code>readBody</code> reads the body of the response
* from the InputStream.
*
* @param input an <code>InputStream</code>
* @return a <code>byte[]</code> representation of the response
*/
private byte[] readBody( InputStream input ) {
StringBuffer sb = new StringBuffer( 255 );
while ( true ) {
try {
int ch = input.read();
if ( ch < 0 ) {
if ( sb.length() == 0 ) {
return ( null );
} else {
break;
}
}
sb.append( ( char ) ch );
} catch ( IOException ex ) {
return null;
}
}
return sb.toString().getBytes();
}
/**
* <code>setHeaderDetails</code> Wrapper method for parseHeader.
* Allows easy addition of headers to the specified
* HashMap
*
* @param line a <code>String</code> value
* @param headerMap a <code>HashMap</code> value
* @param isRequest a <code>boolean</code> indicating if the passed Header
* HashMap is for request headers
*/
private void setHeaderDetails( String line, HashMap headerHash, boolean isRequest ) {
StringTokenizer stk = new StringTokenizer( line, "##" );
while ( stk.hasMoreElements( ) ) {
String presentHeader = stk.nextToken();
parseHeader( presentHeader, headerHash, isRequest );
}
}
// ==================== Code from JSERV !!! ====================
/**
* Parse the incoming HTTP request headers, and set the corresponding
* request properties.
*
*
* @exception IOException if an input/output error occurs
*/
private HashMap parseHeaders( InputStream is ) throws IOException {
HashMap headers = new HashMap();
cookieVector = new Vector();
while ( true ) {
// Read the next header line
String line = read( is );
if ( ( line == null ) || ( line.length() < 1 ) ) {
break;
}
parseHeader( line, headers, false );
if ( debug > 0 ) {
System.out.println( " RESPONSE HEADER: " + line );
}
}
if ( testSession != null ) {
cookieController = ( CookieController ) sessionHash.get( testSession );
if ( cookieController != null ) {
cookieController.recordAnyCookies( cookieVector, requestURL );
}
}
return headers;
}
/**
* <code>parseHeader</code> parses input headers in format of "key:value"
* The parsed header field-name will be used as a key in the passed
* HashMap object, and the values found will be stored in an ArrayList
* associated with the field-name key.
*
* @param line String representation of an HTTP header line.
* @param headers a<code>HashMap</code> to store key/value header objects.
* @param isRequest set to true if the headers being processed are
* requestHeaders.
*/
private void parseHeader( String line, HashMap headerMap, boolean isRequest ) {
// Parse the header name and value
int colon = line.indexOf( ":" );
if ( colon < 0 ) {
System.out.println( " ERROR: Header is in incorrect format: " + line );
return ;
}
String name = line.substring( 0, colon ).trim();
String value = line.substring( colon + 1 ).trim();
if ( ( cookieVector != null ) && ( name.equalsIgnoreCase( "Set-Cookie" ) ) ) {
cookieVector.addElement( value );
/*
if ( ( value.indexOf("JSESSIONID") > -1 ) || (value.indexOf("jsessionid") > -1 ) )
{
String sessionId= value.substring( value.indexOf("=")+1);
if ( testSession != null )
{
sessionHash.put( testSession, sessionId );
}
System.out.println("Got Session-ID : " + sessionId );
}
*/
}
// System.out.println("HEADER: " +name + " " + value);
ArrayList values = (ArrayList) headerMap.get( name );
if ( values == null ) {
values = new ArrayList();
}
// HACK
if ( value.indexOf( ',' ) > -1 && !isRequest && !name.equalsIgnoreCase( "Date" ) ) {
StringTokenizer st = new StringTokenizer( value, "," );
while ( st.hasMoreElements() ) {
values.add( st.nextToken() );
}
} else {
values.add( value );
}
headerMap.put( name, values );
}
/**
* Read a line from the specified servlet input stream, and strip off
* the trailing carriage return and newline (if any). Return the remaining
* characters that were read as a string.7
*
* @returns The line that was read, or <code>null</code> if end of file
* was encountered
*
* @exception IOException if an input/output error occurred
*/
private String read( InputStream input ) throws IOException {
// Read the next line from the input stream
StringBuffer sb = new StringBuffer();
while ( true ) {
try {
int ch = input.read();
// System.out.println("XXX " + (char)ch );
if ( ch < 0 ) {
if ( sb.length() == 0 ) {
if ( debug > 0 )
System.out.println( " Error reading line " + ch + " " + sb.toString() );
return "";
} else {
break;
}
} else if ( ch == LINE_FEED ) {
break;
}
sb.append( ( char ) ch );
} catch ( IOException ex ) {
System.out.println( " Error reading : " + ex );
debug = 1;
if ( debug > 0 ) {
System.out.println( "Partial read: " + sb.toString() );
ex.printStackTrace();
}
}
}
return sb.toString();
}
/**
* <code>dumpHex</code> helper method to dump formatted
* hex output of the server response and the goldenfile.
*
* @param serverResponse a <code>byte[]</code> value
* @param goldenFile a <code>byte[]</code> value
*/
private void dumpHex( byte[] serverResponse, byte[] goldenFile ) {
StringBuffer outBuf = new StringBuffer( ( serverResponse.length + goldenFile.length ) * 2 );
String fromServerString = getHexValue( serverResponse, 0, serverResponse.length );
String fromGoldenFileString = getHexValue( goldenFile, 0, goldenFile.length );
outBuf.append( " Hex dump of server response and goldenfile below.\n\n### RESPONSE FROM SERVER ###\n" );
outBuf.append( "----------------------------\n" );
outBuf.append( fromServerString );
outBuf.append( "\n\n### GOLDEN FILE ###\n" );
outBuf.append( "-------------------\n" );
outBuf.append( fromGoldenFileString );
outBuf.append( "\n\n### END OF DUMP ###\n" );
System.out.println( outBuf.toString() );
}
/**
* <code>getHexValue</code> displays a formatted hex
* representation of the passed byte array. It also
* allows for only a specified offset and length of
* a particular array to be returned.
*
* @param bytes <code>byte[]</code> array to process.
* @param pos <code>int</code> specifies offset to begin processing.
* @param len <code>int</code> specifies the number of bytes to process.
* @return <code>String</code> formatted hex representation of processed
* array.
*/
private String getHexValue( byte[] bytes, int pos, int len ) {
StringBuffer outBuf = new StringBuffer( bytes.length * 2 );
int bytesPerLine = 36;
int cnt = 1;
int groups = 4;
int curPos = pos;
int linePos = 1;
boolean displayOffset = true;
while ( len-- > 0 ) {
if ( displayOffset ) {
outBuf.append( "\n" + paddedHexString( pos, SHORTPADSIZE,
true ) + ": " );
displayOffset = false;
}
outBuf.append(
paddedHexString( ( int ) bytes[ pos ], BYTEPADSIZE, false ) );
linePos += 2; // Byte is padded to 2 characters
if ( ( cnt % 4 ) == 0 ) {
outBuf.append( " " );
linePos++;
}
// Now display the characters that are printable
if ( ( cnt % ( groups * 4 ) ) == 0 ) {
outBuf.append( " " );
while ( curPos <= pos ) {
if ( !Character.isWhitespace( ( char ) bytes[ curPos ] ) ) {
outBuf.append( ( char ) bytes[ curPos ] );
} else {
outBuf.append( "." );
}
curPos++;
}
curPos = pos + 1;
linePos = 1;
displayOffset = true;
}
cnt++;
pos++;
}
// pad out the line with spaces
while ( linePos++ <= bytesPerLine ) {
outBuf.append( " " );
}
outBuf.append( " " );
// Now display the printable characters for the trailing bytes
while ( curPos < pos ) {
if ( !Character.isWhitespace( ( char ) bytes[ curPos ] ) ) {
outBuf.append( ( char ) bytes[ curPos ] );
} else {
outBuf.append( "." );
}
curPos++;
}
return outBuf.toString();
}
/**
* <code>paddedHexString</code> pads the passed value
* based on the specified wordsize and the value of the
* prefixFlag.
*
* @param val an <code>int</code> value
* @param wordsize an <code>int</code> value
* @param prefixFlag a <code>boolean</code> value
* @return a <code>String</code> value
*/
private String paddedHexString( int val, int wordsize,
boolean prefixFlag ) {
String prefix = prefixFlag ? "0x" : "" ;
String hexVal = Integer.toHexString( val );
if ( hexVal.length() > wordsize )
hexVal = hexVal.substring( hexVal.length() - wordsize );
return ( prefix + ( wordsize > hexVal.length() ?
ZEROS.substring( 0, wordsize - hexVal.length() ) : "" ) + hexVal );
}
/**
* <code>cloneHeaders</code> returns a "cloned"
* HashMap of the map passed in.
*
* @param map a <code>HashMap</code> value
* @return a <code>HashMap</code> value
*/
private HashMap cloneHeaders( HashMap map ) {
HashMap dupMap = new HashMap();
Iterator iter = map.keySet().iterator();
while ( iter.hasNext() ) {
String key = new String( (String) iter.next() );
ArrayList origValues = (ArrayList) map.get( key );
ArrayList dupValues = new ArrayList();
String[] dupVal = (String[]) origValues.toArray( new String[ origValues.size() ] );
for ( int i = 0; i < dupVal.length; i++ ) {
dupValues.add( new String( dupVal[ i ] ) );
}
dupMap.put( key, dupValues );
}
return dupMap;
}
/**
* <code>CRBufferedInputStream</code> is a modified version of
* the java.io.BufferedInputStream class. The fill code is
* the same, but the read is modified in that if a carriage return
* is found in the response stream from the target server,
* it will skip that byte and return the next in the stream.
*/
private class CRBufferedInputStream extends BufferedInputStream {
private static final int DEFAULT_BUFFER = 2048;
/**
* Creates a new <code>CRBufferedInputStream</code> instance.
*
* @param in an <code>InputStream</code> value
*/
public CRBufferedInputStream( InputStream in ) {
super( in, DEFAULT_BUFFER );
}
/**
* <code>read</code> reads a single byte value per call.
* If, the byte read, is a carriage return, the next byte
* in the stream in returned instead.
*
* @return an <code>int</code> value
* @exception IOException if an error occurs
*/
public int read() throws IOException {
if ( in == null ) {
throw new IOException ( "Stream closed" );
}
if ( pos >= count ) {
fill();
if ( pos >= count ) {
return -1;
}
}
int val = buf[pos++] & 0xff;
if ( val == CARRIAGE_RETURN ) {
return buf[pos++] & 0xff;
}
return val;
}
/**
* <code>fill</code> is used to fill the internal
* buffer used by this BufferedInputStream class.
*
* @exception IOException if an error occurs
*/
private void fill() throws IOException {
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buf.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buf, markpos, buf, 0, sz);
pos = sz;
markpos = 0;
} else if (buf.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else { /* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buf, 0, nbuf, 0, pos);
buf = nbuf;
}
count = pos;
int n = in.read(buf, pos, buf.length - pos);
if (n > 0)
count = n + pos;
}
}
}
1.1 jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/HttpCookie.java
Index: HttpCookie.java
===================================================================
/*
* $Header: /home/cvs/jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/HttpCookie.java,v 1.1 2002/02/02 03:22:52 rlubke Exp $
* $Revision: 1.1 $
* $Date: 2002/02/02 03:22:52 $
*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" 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"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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.watchdog.task;
import java.util.Properties;
import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.StringTokenizer;
/**
* An object which represents an HTTP cookie. Can be constructed by
* parsing a string from the set-cookie: header.
*
* Syntax: Set-Cookie: NAME=VALUE; expires=DATE;
* path=PATH; domain=DOMAIN_NAME; secure
*
* All but the first field are optional.
*
* @author Ramesh.Mandava
*/
public class HttpCookie {
private Date expirationDate = null;
private String nameAndValue;
private String path;
private String domain;
private boolean isSecure = false;
private static boolean defaultSet = true;
private static long defExprTime = 100;
public HttpCookie(String cookieString) {
/*
System.out.println("Calling default expiration :");
getDefaultExpiration();
*/
parseCookieString(cookieString);
}
//
// Constructor for use by the bean
//
public HttpCookie(Date expirationDate,
String nameAndValue,
String path,
String domain,
boolean isSecure) {
this.expirationDate = expirationDate;
this.nameAndValue = nameAndValue;
this.path = path;
this.domain = domain;
this.isSecure = isSecure;
}
public HttpCookie(URL url, String cookieString) {
parseCookieString(cookieString);
applyDefaults(url);
}
/**
* Fills in default values for domain, path, etc. from the URL
* after creation of the cookie.
*/
private void applyDefaults(URL url) {
if (domain == null) {
domain = url.getHost()+":"+((url.getPort() == -1) ? 80 : url.getPort());
}
if (path == null) {
path = url.getFile();
// larrylf: The documentation for cookies say that the path is
// by default, the path of the document, not the filename of the
// document. This could be read as not including that document
// name itself, just its path (this is how NetScape intrprets it)
// so amputate the document name!
int last = path.lastIndexOf("/");
if( last > -1 ) {
path = path.substring(0, last);
}
}
}
/**
* Parse the given string into its individual components, recording them
* in the member variables of this object.
*/
private void parseCookieString(String cookieString) {
StringTokenizer tokens = new StringTokenizer(cookieString, ";");
if (!tokens.hasMoreTokens()) {
// REMIND: make this robust against parse errors
nameAndValue="=";
return;
}
nameAndValue = tokens.nextToken().trim();
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (token.equalsIgnoreCase("secure")) {
isSecure = true;
} else {
int equIndex = token.indexOf("=");
if (equIndex < 0) {
continue;
// REMIND: malformed cookie
}
String attr = token.substring(0, equIndex);
String val = token.substring(equIndex+1);
if (attr.equalsIgnoreCase("path")) {
path = val;
} else if (attr.equalsIgnoreCase("domain")) {
if( val.indexOf(".") == 0 ) {
// spec seems to allow for setting the domain in
// the form 'domain=.eng.sun.com'. We want to
// trim off the leading '.' so we can allow for
// both leading dot and non leading dot forms
// without duplicate storage.
domain = val.substring(1);
} else {
domain = val;
}
} else if (attr.equalsIgnoreCase("expires")) {
expirationDate = parseExpireDate(val);
} else {
// unknown attribute -- do nothing
}
}
}
// commented the following out, b/c ok to have no expirationDate
// that means that the cookie should last only for that particular
// session.
// if (expirationDate == null) {
// expirationDate = getDefaultExpiration();
// }
}
/* Returns the default expiration, which is the current time + default
expiration as specified in the properties file.
This uses reflection to get at the properties file, since Globals is
not in the utils/ directory
*/
private Date getDefaultExpiration() {
if (defaultSet == false) {
Properties props = new Properties();
try {
FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
props.load( fin );
System.out.println("Got properties from ServerAutoRun.properties");
props.list(System.out);
} catch (IOException ex) {
System.out.println("HttpCookie getDefaultExpiration : ServerAutoRun.properties not found!" + ex);
}
// defExprTime = props.getProperty("cookies.default.expiration");
defExprTime = Long.parseLong( props.getProperty("cookies.default.expiration") );
}
defaultSet = true;
return (new Date(System.currentTimeMillis() + defExprTime));
}
//======================================================================
//
// Accessor functions
//
public String getNameValue() {
return nameAndValue;
}
/**
* Returns just the name part of the cookie
*/
public String getName() {
// it probably can't have null value, but doesn't hurt much
// to check.
if (nameAndValue == null) {
return "=";
}
int index = nameAndValue.indexOf("=");
return (index < 0) ? "=" : nameAndValue.substring(0, index);
}
/**
* Returns the domain of the cookie as it was presented
*/
public String getDomain() {
// REMIND: add port here if appropriate
return domain;
}
public String getPath() {
return path;
}
public Date getExpirationDate() {
return expirationDate;
}
public boolean hasExpired() {
if(expirationDate == null) {
return false;
}
return (expirationDate.getTime() <= System.currentTimeMillis());
}
/**
* Returns true if the cookie has an expiration date (meaning it's
* persistent), and if the date nas not expired;
*/
public boolean isSaveable() {
return (expirationDate != null)
&& (expirationDate.getTime() > System.currentTimeMillis());
}
public boolean isSaveableInMemory() {
return ((expirationDate == null) ||
(expirationDate != null && expirationDate.getTime() > System.currentTimeMillis()));
}
public boolean isSecure() {
return isSecure;
}
private Date parseExpireDate(String dateString) {
// format is wdy, DD-Mon-yyyy HH:mm:ss GMT
RfcDateParser parser = new RfcDateParser(dateString);
Date theDate = parser.getDate();
if (theDate == null) {
// Expire in some intelligent default time
theDate = getDefaultExpiration();
}
return theDate;
}
public String toString() {
String result = (nameAndValue == null) ? "=" : nameAndValue;
if (expirationDate != null) {
result += "; expires=" + expirationDate;
}
if (path != null) {
result += "; path=" + path;
}
if (domain != null) {
result += "; domain=" + domain;
}
if (isSecure) {
result += "; secure";
}
return result;
}
}
1.1 jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/RfcDateParser.java
Index: RfcDateParser.java
===================================================================
/*
* $Header: /home/cvs/jakarta-watchdog-4.0/src/tools/org/apache/watchdog/task/RfcDateParser.java,v 1.1 2002/02/02 03:22:52 rlubke Exp $
* $Revision: 1.1 $
* $Date: 2002/02/02 03:22:52 $
*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" 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"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 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.watchdog.task;
import java.util.Date;
import java.text.DateFormat;
import java.util.Locale;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TimeZone;
/**
* A parser for date strings commonly found in http and email headers that
* follow various RFC conventions. Given a date-string, the parser will
* attempt to parse it by trying matches with a set of patterns, returning
* null on failure, a Date object on success.
*
* @author Ramesh.Mandava
*/
public class RfcDateParser {
private static final String debugProp = "hotjava.debug.RfcDateParser";
private boolean isGMT = false;
private static boolean usingJDK = false;
private static DateFormat dateFormat = DateFormat.getInstance();
static final String[] standardFormats = {
"EEEE', 'dd-MMM-yy HH:mm:ss z", // RFC 850 (obsoleted by 1036)
"EEEE', 'dd-MMM-yy HH:mm:ss", // ditto but no tz. Happens too often
"EEE', 'dd-MMM-yyyy HH:mm:ss z", // RFC 822/1123
"EEE', 'dd MMM yyyy HH:mm:ss z", // REMIND what rfc? Apache/1.1
"EEEE', 'dd MMM yyyy HH:mm:ss z", // REMIND what rfc? Apache/1.1
"EEE', 'dd MMM yyyy hh:mm:ss z", // REMIND what rfc? Apache/1.1
"EEEE', 'dd MMM yyyy hh:mm:ss z", // REMIND what rfc? Apache/1.1
"EEE MMM dd HH:mm:ss z yyyy", // Date's string output format
"EEE MMM dd HH:mm:ss yyyy", // ANSI C asctime format()
"EEE', 'dd-MMM-yy HH:mm:ss", // No time zone 2 digit year RFC 1123
"EEE', 'dd-MMM-yyyy HH:mm:ss" // No time zone RFC 822/1123
};
/* because there are problems with JDK1.1.6/SimpleDateFormat with
* recognizing GMT, we have to create this workaround with the following
* hardcoded strings */
static final String[] gmtStandardFormats = {
"EEEE',' dd-MMM-yy HH:mm:ss 'GMT'", // RFC 850 (obsoleted by 1036)
"EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'", // RFC 822/1123
"EEE',' dd MMM yyyy HH:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
"EEEE',' dd MMM yyyy HH:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
"EEE',' dd MMM yyyy hh:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
"EEEE',' dd MMM yyyy hh:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
"EEE MMM dd HH:mm:ss 'GMT' yyyy" // Date's string output format
};
String dateString;
public RfcDateParser(String dateString) {
this.dateString = dateString.trim();
if (this.dateString.indexOf("GMT") != -1) {
isGMT = true;
}
// use java.text.SimpleDateFormat if present
try {
Class c = Class.forName("java.text.SimpleDateFormat");
usingJDK = true;
} catch (ClassNotFoundException e) {
}
}
public Date getDate() {
if (usingJDK == true) {
int arrayLen = isGMT ? gmtStandardFormats.length : standardFormats.length;
for (int i = 0; i < arrayLen; i++) {
Date d = null;
if (isGMT) {
d = tryParsing(gmtStandardFormats[i]);
} else {
d = tryParsing(standardFormats[i]);
}
if (d != null) {
return d;
}
}
return null;
} // end clause "if (usingJDK)"
return parseNoJDKDate();
}
private Date tryParsing(String format) {
java.text.SimpleDateFormat df = new java.text.SimpleDateFormat(format, Locale.US);
if (isGMT) {
df.setTimeZone(TimeZone.getTimeZone("GMT"));
}
try {
return df.parse(dateString);
} catch (Exception e) {
return null;
}
}
private Date parseNoJDKDate() {
// format is wdy, DD-Mon-yyyy HH:mm:ss GMT
// or
// format is wdy, DD-Mon-yy HH:mm:ss GMT
Date fInternalDate = null;
try {
fInternalDate = dateFormat.parse( dateString );
}
catch (Exception ex) {
}
// Apply emergency parsing measures to work around
// Y2K 2 digit year date parse bug in java.util.Date
// format is wdy, DD-Mon-yy HH:mm:ss GMT
if ( fInternalDate == null ) {
String newString = new String();
StringTokenizer spaces = new StringTokenizer(dateString, " ");
if ( spaces.countTokens() >= 3 ) {
newString = newString.concat(spaces.nextToken());
String DDMonyy = spaces.nextToken();
int idx = DDMonyy.lastIndexOf('-');
String DDMon = DDMonyy.substring(0, idx);
if ( idx >= 0 ) {
String year = DDMonyy.substring(idx+1);
if ( year.length() == 2 ) {
try {
int yearInt = Integer.parseInt(year);
if ( yearInt < 70 ) {
yearInt = yearInt+2000;
String newYY = Integer.toString(yearInt);
newString = newString.concat(" " + DDMon + "-" + newYY);
}
else {
return(null);
}
}
catch ( Exception ex ) {
return(null);
}
}
else {
return(null);
}
}
else {
return(null);
}
while ( spaces.hasMoreTokens() ) {
newString = newString.concat( " " + spaces.nextToken() );
}
try {
fInternalDate = dateFormat.parse( newString );
}
catch (Exception ex) {
}
} // end of if (spaces.countTokens() >= 3)
else {
return(null);
}
} // end of if (fInternalDate == null)
return fInternalDate;
}
} /* class RfcDateParser */
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>