/*
 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

/*
 * $Id: JAXRSCommonClient.java 75004 2017-05-30 16:04:30Z jsupol $
 */
package com.sun.ts.tests.jaxrs.common;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpState;

import com.sun.javatest.Status;
import com.sun.ts.lib.harness.ServiceEETest;
import com.sun.ts.lib.util.TestUtil;
import com.sun.ts.tests.common.webclient.TestFailureException;
import com.sun.ts.tests.common.webclient.WebTestCase;
import com.sun.ts.tests.common.webclient.http.HttpRequest;
import com.sun.ts.tests.common.webclient.validation.CheckOneOfStatusesTokenizedValidator;
import com.sun.ts.tests.jaxrs.common.util.JaxrsUtil;
import com.sun.ts.tests.servlet.common.util.Data;

/**
 * 
 * @author dianne jiao
 * @author jan supol
 */
public abstract class JAXRSCommonClient extends ServiceEETest {

	private static final long serialVersionUID = 1L;

	/**
	 * TS Webserver host property
	 */
	protected static final String SERVLETHOSTPROP = "webServerHost";
	/**
	 * TS Webserver port property
	 */
	protected static final String SERVLETPORTPROP = "webServerPort";
	/**
	 * TS home property
	 */
	protected static final String TSHOME = "ts_home";
	/**
	 * Test properties
	 */
	protected static final Hashtable<Property, String> TEST_PROPS = new Hashtable<Property, String>();
	/**
	 * StatusCode property
	 */
	protected static final String STATUS_CODE = "STATUS_CODE";
	/**
	 * Request property
	 */
	protected static final String REQUEST = "REQUEST";
	/**
	 * Request headers property
	 */
	protected static final String REQUEST_HEADERS = "REQUEST_HEADERS";
	/**
	 * Goldenfile property
	 */
	protected static final String GOLDENFILE = "goldenfile";
	/**
	 * Search string property
	 */
	protected static final String SEARCH_STRING = "SEARCH_STRING";
	/**
	 * Search string case insensitive property
	 */
	protected String TESTDIR = null;
	/**
	 * Goldenfile directory
	 */
	protected String GOLDENFILEDIR = "/src/web";
	/**
	 * Default request method
	 */
	protected static final String GET = "GET ";
	/**
	 * HTTP 1.0
	 */
	protected static final String HTTP10 = " HTTP/1.0";
	/**
	 * HTTP 1.1
	 */
	protected static final String HTTP11 = " HTTP/1.1";
	/**
	 * Forward slash
	 */
	protected static final String SL = "/";
	/**
	 * Goldenfile suffix
	 */
	protected static final String GF_SUFFIX = ".gf";
	/**
	 * JSP suffix
	 */
	/**
	 * Current test name
	 */
	protected String _testName = null;
	/**
	 * location of _tsHome
	 */
	protected String _tsHome = null;
	/**
	 * Context root of target tests
	 */
	public String _contextRoot = null;
	/**
	 * General file/request URI for both gfiles and tests
	 */
	protected String _generalURI = null;
	/**
	 * Target webserver hostname
	 */
	protected String _hostname = null;
	/**
	 * Target webserver port
	 */
	protected int _port = 0;
	/**
	 * HttpState that may be used for multiple invocations requiring state.
	 */
	protected HttpState _state = null;
	/**
	 * Test case.
	 */
	protected WebTestCase _testCase = null;
	/**
	 * Use saved state.
	 */
	protected boolean _useSavedState = false;
	/**
	 * Save state.
	 */
	protected boolean _saveState = false;
	protected boolean _redirect = false;

	public static final String newline = System.getProperty("line.separator");
	public static final String indent = "    ";

	/**
	 * List of possible requests
	 */
	protected enum Request {
		GET, PUT, POST, HEAD, OPTIONS, DELETE, TRACE
	}

	/**
	 * the list of properties to be put into a property table
	 */
	protected enum Property {
		APITEST, BASIC_AUTH_PASSWD, BASIC_AUTH_REALM, BASIC_AUTH_USER, //
		CONTENT, DONOTUSEServletName, EXPECT_RESPONSE_BODY, EXPECTED_HEADERS, //
		FOLLOW_REDIRECT, GOLDENFILE, IGNORE_BODY, IGNORE_STATUS_CODE, //
		REASON_PHRASE, REQUEST, REQUEST_HEADERS, RESPONSE_MATCH, SAVE_STATE, //
		SEARCH_STRING, SEARCH_STRING_IGNORE_CASE, STANDARD, STATUS_CODE, //
		STRATEGY, TEST_NAME, UNEXPECTED_HEADERS, UNEXPECTED_RESPONSE_MATCH, //
		UNORDERED_SEARCH_STRING, USE_SAVED_STATE;
	}

	/*
	 * public methods
	 * ========================================================================
	 */
	/**
	 * <code>setTestDir</code> sets the current test directory.
	 * 
	 * @param testDir
	 *            a <code>String</code> value
	 */
	public void setTestDir(String testDir) {
		TestUtil.logTrace("[JAXRSCommonClient] setTestDir");
		TESTDIR = testDir;
	}

	public void setContextRoot(String root) {
		TestUtil.logTrace("[JAXRSCommonClient] Contextroot set at " + root);
		_contextRoot = root;
	}

	public String getContextRoot() {
		TestUtil.logTrace("[JAXRSCommonClient]getContextRoot");
		return _contextRoot;
	}

	/**
	 * <code>setup</code> is by the test harness to initialize the tests.
	 * 
	 * @param args
	 *            a <code>String[]</code> value
	 * @param p
	 *            a <code>Properties</code> value
	 * @exception Fault
	 *                if an error occurs
	 */
	public void setup(String[] args, Properties p) throws Fault {
		TestUtil.logTrace("setup method JAXRSCommonClient");

		String hostname = p.getProperty(SERVLETHOSTPROP);
		String portnum = p.getProperty(SERVLETPORTPROP);
		String tshome = p.getProperty(TSHOME);

		assertFault(!isNullOrEmpty(hostname),
				"[JAXRSCommonClient] 'webServerHost' was not set in the build.properties.");
		_hostname = hostname.trim();
		assertFault(!isNullOrEmpty(portnum),
				"[JAXRSCommonClient] 'webServerPort' was not set in the build.properties.");
		_port = Integer.parseInt(portnum.trim());
		assertFault(!isNullOrEmpty(tshome),
				"[JAXRSCommonClient] 'tshome' was not set in the build.properties.");
		_tsHome = tshome.trim();

		TestUtil.logMsg("[JAXRSCommonClient] Test setup OK");
	}

	/**
	 * <code>cleanup</code> is called by the test harness to cleanup after text
	 * execution
	 * 
	 * @exception Fault
	 *                if an error occurs
	 */
	public void cleanup() throws Fault {
		TestUtil.logMsg("[JAXRSCommonClient] Test cleanup OK");
	}

	/*
	 * protected methods
	 * ========================================================================
	 */
	/**
	 * <PRE>
	 * Invokes a test based on the properties
	 * stored in TEST_PROPS.  Once the test has completed,
	 * the properties in TEST_PROPS will be cleared.
	 * </PRE>
	 * 
	 * @throws Fault
	 *             If an error occurs during the test run
	 */
	protected void invoke() throws Fault {
		TestUtil.logTrace("[JAXRSCommonClient] invoke");
		try {
			_testCase = new WebTestCase();
			setTestProperties(_testCase);
			TestUtil.logTrace("[JAXRSCommonClient] EXECUTING");
			if (_useSavedState && _state != null) {
				_testCase.getRequest().setState(_state);
			}
			if (_redirect != false) {
				TestUtil.logTrace("##########Call setFollowRedirects");
				_testCase.getRequest().setFollowRedirects(_redirect);
			}
			_testCase.execute();
			if (_saveState) {
				_state = _testCase.getResponse().getState();
			}
		} catch (TestFailureException tfe) {
			Throwable t = tfe.getRootCause();
			if (t != null) {
				TestUtil.logErr("Root cause of Failure: " + t.getMessage(), t);
			}
			throw new Fault("[JAXRSCommonClient] " + _testName
					+ " failed!  Check output for cause of failure.", tfe);
		} finally {
			_useSavedState = false;
			_saveState = false;
			_redirect = false;
			clearTestProperties();
		}
	}

	/**
	 * <PRE>
	 * Sets the appropriate test properties based
	 * on the values stored in TEST_PROPS
	 * </PRE>
	 */
	protected void setTestProperties(WebTestCase testCase) {
		TestUtil.logTrace("[JAXRSCommonClient] setTestProperties");

		setStandardProperties(TEST_PROPS.get(Property.STANDARD), testCase);
		setApiTestProperties(TEST_PROPS.get(Property.APITEST), testCase);

		if (TEST_PROPS.get(Property.STATUS_CODE) == null)
			setProperty(Property.STATUS_CODE, getStatusCode(Response.Status.OK));

		HttpRequest req = testCase.getRequest();

		// Check for a request object. If doesn't exist, then
		// check for a REQUEST property and create the request object.
		if (req == null)
			req = setWebTestCaseRequest(testCase, null);
		setWebTestCaseProperties(testCase, req);
	}

	protected HttpRequest setWebTestCaseRequest(WebTestCase testCase,
			HttpRequest req) {
		String request = TEST_PROPS.get(Property.REQUEST);
		boolean isRequest = false;
		for (Request r : Request.values())
			if (request.startsWith(r.name()))
				isRequest = true;
		if (request.endsWith(HTTP10) || request.endsWith(HTTP11))
			isRequest = true;
		if (isRequest) {
			// user has overridden default request behavior
			req = createHttpRequest(request, _hostname, _port);
			testCase.setRequest(req);
		} else {
			req = createHttpRequest(getTSRequest(request), _hostname, _port);
			testCase.setRequest(req);
		}
		return req;
	}

	protected void setWebTestCaseProperties(WebTestCase testCase,
			HttpRequest req) {
		Property key = null;
		String value = null;
		// process the remainder of the properties
		for (Enumeration<Property> e = TEST_PROPS.keys(); e.hasMoreElements();) {
			key = e.nextElement();
			value = TEST_PROPS.get(key);
			switch (key) {
			case APITEST:
				break;
			case BASIC_AUTH_PASSWD:
			case BASIC_AUTH_REALM:
			case BASIC_AUTH_USER:
				String user = TEST_PROPS.get(Property.BASIC_AUTH_USER);
				String password = TEST_PROPS.get(Property.BASIC_AUTH_PASSWD);
				String realm = TEST_PROPS.get(Property.BASIC_AUTH_REALM);
				req.setAuthenticationCredentials(user, password,
						HttpRequest.BASIC_AUTHENTICATION, realm);
				break;
			case CONTENT:
				req.setContent(value);
				break;
			case DONOTUSEServletName:
				break;
			case EXPECT_RESPONSE_BODY:
				// FIXME
				// setExpectResponseBody(false);
				break;
			case EXPECTED_HEADERS:
				testCase.addExpectedHeader(value);
				break;
			case FOLLOW_REDIRECT:
				TestUtil.logTrace("##########Found redirect Property");
				_redirect = true;
				break;
			case GOLDENFILE:
				StringBuffer sb = new StringBuffer(50);
				sb.append(_tsHome).append(GOLDENFILEDIR);
				sb.append(_generalURI).append(SL);
				sb.append(value);
				testCase.setGoldenFilePath(sb.toString());
				break;
			case IGNORE_BODY:
				// FIXME
				// setIgnoreResponseBody(true);
				testCase.setGoldenFilePath(null);
				break;
			case IGNORE_STATUS_CODE:
				testCase.setExpectedStatusCode("-1");
				break;
			case REASON_PHRASE:
				testCase.setExpectedReasonPhrase(value);
				break;
			case REQUEST:
				break;
			case REQUEST_HEADERS:
				req.addRequestHeader(value);
				break;
			case RESPONSE_MATCH:
				// setResponseMatch(TEST_PROPS.getProperty(key));
				break;
			case SAVE_STATE:
				_saveState = true;
				break;
			case SEARCH_STRING:
				testCase.setResponseSearchString(value);
				break;
			case SEARCH_STRING_IGNORE_CASE:
				testCase.setResponseSearchStringIgnoreCase(value);
				break;
			case STANDARD:
				break;
			case STATUS_CODE:
				if (value.contains("|"))
					testCase.setStrategy(CheckOneOfStatusesTokenizedValidator.class
							.getName());
				testCase.setExpectedStatusCode(value);
				break;
			case STRATEGY:
				testCase.setStrategy(value);
				break;
			case TEST_NAME:
				// testName = TEST_PROPS.getProperty(key);
				break;
			case UNEXPECTED_HEADERS:
				testCase.addUnexpectedHeader(value);
				break;
			case UNEXPECTED_RESPONSE_MATCH:
				testCase.setUnexpectedResponseSearchString(value);
				break;
			case UNORDERED_SEARCH_STRING:
				testCase.setUnorderedSearchString(value);
				break;
			case USE_SAVED_STATE:
				_useSavedState = true;
				break;
			}
		}
	}

	/**
	 * Create request <type> /<contextroot>/<path> HTTP/1.1. ContextRoot is
	 * defined in every client.
	 * 
	 * @param type
	 *            PUT, GET, POST, ...
	 * @param path
	 *            path defined in a servlet
	 * @return String representing HTTP request
	 */
	protected String buildRequest(String type, String... path) {
		StringBuilder sb = new StringBuilder();
		sb.append(type).append(" ").append(_contextRoot).append(SL);
		for (String segment : path)
			sb.append(segment);
		sb.append(HTTP11);
		return sb.toString();
	}

	protected String buildRequest(Request type, String... path) {
		return buildRequest(type.name(), path);
	}

	protected String buildRequest10(Request type, String... path) {
		return buildRequest(type, path).replace(HTTP11, HTTP10);
	}

	/**
	 * Create counterpart to @Produces
	 * 
	 * @param type
	 * @return Accept:{@code type}.{@link #toString()}
	 */
	protected static String buildAccept(MediaType type) {
		return buildHeaderMediaType("Accept", type);
	}

	/**
	 * Create counterpart to @Consumes
	 * 
	 * @param type
	 * @return
	 */
	protected static String buildContentType(MediaType type) {
		return buildHeaderMediaType("Content-Type", type);
	}

	protected static String buildHeaderMediaType(String header, MediaType type) {
		StringBuilder sb = new StringBuilder();
		sb.append(header).append(":").append(type.getType()).append(SL);
		sb.append(type.getSubtype());
		return sb.toString();
	}

	/**
	 * @return http response body as string
	 * @throws Fault
	 *             when an error occur
	 */
	protected String getResponseBody() throws Fault {
		try {
			com.sun.ts.tests.common.webclient.http.HttpResponse response;
			response = _testCase.getResponse();
			boolean isNull = response.getResponseBodyAsRawStream() == null;
			return isNull ? null : response.getResponseBodyAsString();
		} catch (IOException e) {
			throw new Fault(e);
		}
	}

	/**
	 * @return http response body as string
	 * @throws Fault
	 *             when an error occur
	 */
	protected String[] getResponseHeaders() throws Fault {
		Header[] headerEntities = _testCase.getResponse().getResponseHeaders();
		String[] headers = new String[headerEntities.length];
		for (int i = 0; i != headerEntities.length; i++)
			headers[i] = headerEntities[i].toString();
		return headers;
	}

	/**
	 * @param s
	 *            the header to search
	 * @throws Fault
	 *             when header not found
	 */
	protected void assertResponseHeadersContain(String s) throws Fault {
		boolean found = false;
		for (String header : getResponseHeaders())
			if (header.contains(s)) {
				found = true;
				break;
			}
		assertFault(found, "Response headers do not contain", s);
	}

	/**
	 * @param s
	 *            the entity to search
	 * @throws Fault
	 *             when entity not found
	 */
	protected void assertResponseBodyContain(String s) throws Fault {
		boolean found = getResponseBody().contains(s);
		assertFault(found, "Response body does not contain", s);
	}

	/**
	 * get HttpResponse#statusCode
	 * 
	 * @return JAXRS Response.Status equivalent of HttpResponse#statusCode
	 */
	protected Response.Status getResponseStatusCode() {
		String status = _testCase.getResponse().getStatusCode();
		return Response.Status.fromStatusCode(Integer.parseInt(status));
	}

	/**
	 * Set TEST_PROPS property value. If it already exists, the value is
	 * appended
	 */
	protected void setProperty(String key, String value) {
		Property property = Property.valueOf(key);
		setProperty(property, value);
	}
	
	/*
	 * @since 2.0.1
	 */
	protected void setProperty(Property key, String... value) {
		setProperty(key, objectsToString("", (Object[])value));
	}	

	protected void setProperty(Property key, String value) {
		String oldValue = TEST_PROPS.get(key);
		if (oldValue == null) {
			TEST_PROPS.put(key, value);
		} else {
			int len = value.length() + oldValue.length() + 1;
			StringBuilder combinedValue = new StringBuilder(len);
			combinedValue.append(oldValue).append("|").append(value);
			TEST_PROPS.put(key, combinedValue.toString());
		}
	}

	protected void clearProperty(Property key) {
		TEST_PROPS.remove(key);
	}

	/**
	 * This pattern is used in all subclasses
	 */
	protected Status run(String[] args) {
		Status s;
		s = run(args, new PrintWriter(System.out), new PrintWriter(System.err));
		s.exit();
		return s;
	}

	/**
	 * Asserts that a condition is true.
	 * 
	 * @param conditionTrue
	 *            tested condition
	 * @param message
	 *            a space separated message[i].toString() compilation for
	 *            i=<0,message.length)
	 * @throws Fault
	 *             when conditionTrue is not met with message provided
	 */
	public static void //
	assertFault(boolean conditionTrue, Object... message) throws Fault {
		assertTrue(conditionTrue, message);
	}

	/**
	 * Asserts that a condition is true.
	 * 
	 * @param condition
	 *            tested condition
	 * @param message
	 *            a space separated message[i].toString() compilation for
	 *            i=<0,message.length)
	 * @throws Fault
	 *             when conditionTrue is not met with message provided
	 */
	public static void //
	assertTrue(boolean condition, Object... message) throws Fault {
		if (!condition)
			fault(message);
	}

	/**
	 * Asserts that a condition is false.
	 * 
	 * @param condition
	 *            tested condition
	 * @param message
	 *            a space separated message[i].toString() compilation for
	 *            i=<0,message.length)
	 * @throws Fault
	 *             when condition is not false with message provided
	 */
	public static void //
	assertFalse(boolean condition, Object... message) throws Fault {
		assertTrue(!condition, message);
	}

	/**
	 * Asserts that two objects are equal. When instances of Comparable, such as
	 * String, compareTo is used.
	 * 
	 * @param first
	 *            first object
	 * @param second
	 *            second object
	 * @param message
	 *            a space separated message[i].toString() compilation for
	 *            i=<0,message.length)
	 * @throws Fault
	 *             when objects are not equal with message provided
	 */
	@SuppressWarnings("unchecked")
	public static <T> void //
	assertEquals(T first, T second, Object... message) throws Fault {
		if (first == null && second == null)
			return;
		assertFalse(first == null && second != null, message);
		assertFalse(first != null && second == null, message);
		if (first instanceof Comparable)
			assertTrue(((Comparable<T>) first).compareTo(second) == 0, message);
		else
			assertTrue(first.equals(second), message);
	}

	public static <T> void //
	assertEqualsInt(int first, int second, Object... message) throws Fault {
		assertTrue(first == second, message);
	}

	public static <T> void //
	assertEqualsLong(long first, long second, Object... message) throws Fault {
		assertTrue(first == second, message);
	}

	public static <T> void //
	assertEqualsBool(boolean first, boolean second, Object... message)
			throws Fault {
		assertTrue(first == second, message);
	}

	/**
	 * Asserts that an object is null.
	 * 
	 * @param object
	 *            Assert that object is not null
	 * @param message
	 *            a space separated message[i].toString() compilation for
	 *            i=<0,message.length)
	 * @throws Fault
	 *             when condition is not met with message provided
	 */
	public static void //
	assertNull(Object object, Object... message) throws Fault {
		assertTrue(object == null, message);
	}

	/**
	 * Asserts that an object is not null.
	 * 
	 * @param object
	 *            Assert that object is not null
	 * @param message
	 *            a space separated message[i].toString() compilation for
	 *            i=<0,message.length)
	 * @throws Fault
	 *             when condition is not met with message provided
	 */
	public static void //
	assertNotNull(Object object, Object... message) throws Fault {
		assertTrue(object != null, message);
	}

	/**
	 * Throws Fault with space separated objects[1],object[2],...,object[n]
	 * message
	 * 
	 * @param objects
	 *            objects whose toString() results will be added to Fault
	 *            message
	 * @throws Fault
	 *             fault with space separated objects.toString values
	 */
	public static void fault(Object... objects) throws Fault {
		throw new Fault(objectsToString(objects));
	}

	/**
	 * Assert that given substring is a substring of given string
	 * 
	 * @param string
	 *            the string to search substring in
	 * @param substring
	 *            the substring to be searched in a given string
	 * @param message
	 *            space separated message values to be thrown
	 * @throws Fault
	 *             throws
	 */
	public static void assertContains(String string, String substring,
			Object... message) throws Fault {
		assertTrue(string.contains(substring), message);
	}

	/**
	 * Assert that given substring is a substring of given string, case
	 * insensitive
	 * 
	 * @param string
	 *            the string to search substring in
	 * @param substring
	 *            the substring to be searched in a given string
	 * @param message
	 *            space separated message values to be thrown
	 * @throws Fault
	 */
	public static void assertContainsIgnoreCase(String string,
			String substring, Object... message) throws Fault {
		assertTrue(string.toLowerCase().contains(substring.toLowerCase()),
				message);
	}

	/**
	 * Assert that given subtext.toString() subject is a substring of given text
	 * 
	 * @param text
	 *            the text.toString() object to search subtext.toString() in
	 * @param subtext
	 *            the subtext.toString() to be searched in a given
	 *            text.toString()
	 * @param message
	 *            space separated message values to be thrown
	 * @throws Fault
	 */
	public static <T> void assertContains(T text, T subtext, Object... message)
			throws Fault {
		assertContains(text.toString(), subtext.toString(), message);
	}

	/**
	 * Assert that given subtext.toString() subject is a substring of given
	 * text, case insensitive
	 * 
	 * @param text
	 *            the text.toString() object to search subtext.toString() in
	 * @param subtext
	 *            the subtext.toString() to be searched in a given
	 *            text.toString()
	 * @param message
	 *            space separated message values to be thrown
	 * @throws Fault
	 */
	public static <T> void assertContainsIgnoreCase(T text, T subtext,
			Object... message) throws Fault {
		assertContainsIgnoreCase(text.toString(), subtext.toString(), message);
	}

	/**
	 * Searches an encapsulated exception cause in parent exception
	 */
	protected static <T extends Throwable> T assertCause(Throwable parent,
			Class<T> wrapped, Object... msg) throws Fault {
		T t = hasCause(parent, wrapped);
		assertNotNull(t, msg);
		return t;
	}

	@SuppressWarnings("unchecked")
	private static <T extends Throwable> T //
	hasCause(Throwable parent, Class<? extends Throwable> cause) {
		while (parent != null) {
			if (cause.isInstance(parent))
				return (T) parent;
			parent = parent.getCause();
		}
		return null;
	}

	public static String getStatusCode(Response.Status status) {
		return String.valueOf(status.getStatusCode());
	}

	public static void logMsg(Object... msg) {
		TestUtil.logMsg(objectsToString(msg));
	}
	
	public static void logTrace(Object... msg) {
		TestUtil.logTrace(objectsToString(msg));
	}	

	/**
	 * Use rather this method than
	 * {@link JaxrsUtil#iterableToString(String, Iterable)} since not all wars
	 * (for servlet vehicle, api) do contain JaxrsUtil
	 * 
	 * @param objects
	 * @return objects in a single string , each object separated by " "
	 */
	protected static String objectsToString(Object... objects) {
		return objectsToString(" ", objects);
	}

	/**
	 * @since 2.0.1 
	 */
	protected static String objectsToString(String delimiter, Object... objects) {
		StringBuilder sb = new StringBuilder();
		for (Object o : objects)
			sb.append(o).append(delimiter);
		return sb.toString().trim();
	}

	/*
	 * private methods
	 * ========================================================================
	 */
	private String getTSRequest(String request) {
		TestUtil.logTrace("[JAXRSCommonClient] getTSRequest");
		StringBuffer finReq = new StringBuffer(50);
		finReq.append(GET).append(_contextRoot).append(SL).append(_generalURI);
		finReq.append(SL).append(request).append(HTTP11);
		return finReq.toString();
	}

	/**
	 * Clears the contents of TEST_PROPS
	 */
	protected void clearTestProperties() {
		TEST_PROPS.clear();
	}

	protected boolean isNullOrEmpty(String val) {
		return val == null || val.trim().equals("");
	}

	private InetAddress[] _addrs = null;
	protected String _servlet = null;

	/**
	 * Sets the request, testname, and a search string for test passed. A search
	 * is also added for test failure. If found, the test will fail.
	 * 
	 * @param testValue
	 *            - a logical test identifier
	 * @param testCase
	 *            - the current test case
	 */
	private void setApiTestProperties(String testValue, WebTestCase testCase) {
		TestUtil.logTrace("[JAXRSCommonClient] setApiTestProperties");

		if (testValue == null) {
			return;
		}

		// An API test consists of a request with a request parameter of
		// testname, a search string of Test PASSED, and a logical test name.

		// set the testname
		_testName = testValue;

		// set the request
		StringBuffer sb = new StringBuffer(50);
		if ((_servlet != null)
				&& (TEST_PROPS.get(Property.DONOTUSEServletName) == null)) {
			sb.append(GET).append(_contextRoot).append(SL);
			sb.append(_servlet).append("?testname=").append(testValue);
			sb.append(HTTP11);
		} else {
			sb.append(GET).append(_contextRoot).append(SL);
			sb.append(testValue).append(HTTP10);
		}
		System.out.println("REQUEST LINE: " + sb.toString());

		HttpRequest req = createHttpRequest(sb.toString(), _hostname, _port);
		testCase.setRequest(req);

		String value = TEST_PROPS.get(Property.SEARCH_STRING);
		if (isNullOrEmpty(value)) {
			testCase.setResponseSearchString(Data.PASSED);
			testCase.setUnexpectedResponseSearchString(Data.FAILED);
		}
	}
	
	protected HttpRequest createHttpRequest(String requestLine, String host, int port) {
		return new HttpRequest(requestLine, host, port);
	}

	/**
	 * Consists of a test name, a request, and a goldenfile.
	 * 
	 * @param testValue
	 *            - a logical test identifier
	 * @param testCase
	 *            - the current test case
	 */
	private void setStandardProperties(String testValue, WebTestCase testCase) {
		TestUtil.logTrace("[JAXRSCommonClient] setStandardProperties");

		if (testValue == null) {
			return;
		}
		// A standard test sets consists of a testname
		// a request, and a goldenfile. The URI is not used
		// in this case since the JSP's are assumed to be located
		// at the top of the contextRoot
		String req;

		// set the testname
		_testName = testValue;

		if (_servlet != null) {
			req = buildRequest(Request.GET, _servlet, "?testname=", testValue);
		} else {
			req = buildRequest10(Request.GET, testValue);
		}
		System.out.println("REQUEST LINE: " + req);
		System.out.println("_hostname=" + _hostname);
		HttpRequest httpReq = createHttpRequest(req, _hostname, _port);
		testCase.setRequest(httpReq);

		// set the goldenfile
		StringBuffer sb = new StringBuffer(50);
		sb.append(_tsHome).append(GOLDENFILEDIR);
		sb.append(_generalURI).append(SL);
		sb.append(testValue).append(GF_SUFFIX);
		testCase.setGoldenFilePath(sb.toString());
	}

	/**
	 * Sets the name of the servlet to use when building a request for a single
	 * servlet API test.
	 * 
	 * @param servlet
	 *            - the name of the servlet
	 */
	protected void setServletName(String servlet) {
		TestUtil.logTrace("[JAXRSCommonClient] setServletName");
		_servlet = servlet;
	}

	protected String getServletName() {
		return _servlet;
	}

	protected String getLocalInterfaceInfo(boolean returnAddresses) {
		String result = null;
		initInetAddress();
		if (_addrs.length != 0) {
			StringBuffer sb = new StringBuffer(32);
			if (!returnAddresses) {
				// localhost might not show up if aliased
				sb.append("localhost,");
			} else {
				// add 127.0.0.1
				sb.append("127.0.0.1,");
			}

			for (int i = 0; i < _addrs.length; i++) {
				if (returnAddresses) {
					String ip = _addrs[i].getHostAddress();
					if (!ip.equals("127.0.0.1")) {
						if (ip.contains("%")) {
							int scope_id = ip.indexOf("%");
							ip = ip.substring(0, scope_id);
						}
						sb.append(ip);
					}
				} else {
					String host = _addrs[i].getCanonicalHostName();
					if (!host.equals("localhost")) {
						sb.append(host);
					}
				}
				if (i + 1 != _addrs.length) {
					sb.append(",");
				}
			}
			result = sb.toString();
			TestUtil.logTrace("[AbstractUrlClient] Interface info: " + result);
		}
		return result;
	}

	private void initInetAddress() {
		if (_addrs == null) {
			try {
				_addrs = InetAddress.getAllByName(InetAddress.getLocalHost()
						.getCanonicalHostName());
			} catch (UnknownHostException uhe) {
				TestUtil.logMsg("[AbstractUrlClient][WARNING] Unable to obtain local host information.");
			}
		}
	}
	
	protected String getAbsoluteUrl() {
		return getAbsoluteUrl(null);
	}
	
	protected String getAbsoluteUrl(String method) {
		StringBuilder sb = new StringBuilder();
		sb.append("http://").append(_hostname).append(":").append(_port).append(getContextRoot());
		if (method != null)
			sb.append("/").append(method);
		return sb.toString();
	}	
}
