/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.jsfunit.framework;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.TopLevelWindow;
import com.gargoylesoftware.htmlunit.WebWindow;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import org.jboss.jsfunit.seam.SeamRequestListener;
import org.jboss.jsfunit.seam.SeamUtil;
/**
* The WebClientSpec allows configuration of the HtmlUnit WebClient and its
* interaction with JSFUnit.
*
* @author Stan Silvert
* @since 1.0
*/
public class WebClientSpec implements HttpSessionBindingListener
{
public static final String SESSION_KEY = WebClientSpec.class.getName() + "sessionkey";
// IE6 is the HtmlUnit default
private static BrowserVersion DEFAULT_BROWSER_VERSION = BrowserVersion.getDefault();
private String initialPage;
private WebClient webClient;
private BrowserVersion browserVersion;
private String proxyHost;
private int proxyPort;
private Map<String, String> cookies = new HashMap<String, String>();
private InitialRequestStrategy requestStrategy = new SimpleInitialRequestStrategy();
private boolean initialRequestDone = false;
/**
* Create a new WebClientSpec.
*
* Note that the initialPage param should be something that maps into the FacesServlet.
* In the case where the FacesServlet is extension mapped in web.xml, this param will be something
* like "/index.jsf" or "/index.faces". If the FacesServlet is path-mapped then the
* initialPage param will be something like "/faces/index.jsp".
*
* @param initialPage The page used to start a client session with JSF. Example: "/index.jsf"
*/
public WebClientSpec(String initialPage)
{
this(initialPage, DEFAULT_BROWSER_VERSION);
}
/**
* Create a new WebClientSpec.
*
* Note that the initialPage param should be something that maps into the FacesServlet.
* In the case where the FacesServlet is extension mapped in web.xml, this param will be something
* like "/index.jsf" or "/index.faces". If the FacesServlet is path-mapped then the
* initialPage param will be something like "/faces/index.jsp".
*
* @param initialPage The page used to start a client session with JSF. Example: "/index.jsf"
* @param browserVersion The browser version to simulate.
*/
public WebClientSpec(String initialPage, BrowserVersion browserVersion)
{
this(initialPage, browserVersion, null, 0);
}
/**
* Create a new WebClientSpec for use with a proxy server.
*
* Note that the initialPage param should be something that maps into the FacesServlet.
* In the case where the FacesServlet is extension mapped in web.xml, this param will be something
* like "/index.jsf" or "/index.faces". If the FacesServlet is path-mapped then the
* initialPage param will be something like "/faces/index.jsp".
*
* @param initialPage The page used to start a client session with JSF. Example: "/index.jsf"
* @param browserVersion The browser version to simulate.
* @param proxyHost The proxy server.
* @param proxyPort The proxy port.
*/
public WebClientSpec(String initialPage, BrowserVersion browserVersion, String proxyHost, int proxyPort)
{
this.initialPage = initialPage;
this.browserVersion = browserVersion;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
WebConversationFactory.makeWebClient(this);
setFaceletsErrorDetector();
}
private void setFaceletsErrorDetector()
{
JSFUnitWebConnection webConnection = (JSFUnitWebConnection)webClient.getWebConnection();
webConnection.addListener(new FaceletsErrorPageDetector());
}
/**
* Get an immutable Map of all request params sent to the ServletRedirector or ServletTestRunner.
*
* <b>Note that the Map returned is the same one returned from ServletRequest.getParameterMap().
* This Map is defined with a key of type String and a value of type String array - not plain String.</b>
*
* @return The Map of params.
* @since 1.1
*/
public static Map getRedirectorRequestParams()
{
return (Map)WebConversationFactory.getSessionFromThreadLocal()
.getAttribute(JSFUnitFilter.REDIRECTOR_REQUEST_PARAMS_KEY);
}
/**
* Return the initialPage passed into the constructor.
*
* @return The initialPage.
*/
public String getInitialPage()
{
return this.initialPage;
}
/**
* Package-private method to set the WebClient to be used for the JSFSession.
*/
void setWebClient(WebClient webClient)
{
this.webClient = webClient;
}
/**
* Get the WebClient instances used for the JSFSession.
*
* @return The WebClient.
*/
public WebClient getWebClient()
{
return this.webClient;
}
/**
* Get the BrowserVersion to be used by the WebClient.
*
* @return The BrowserVersion.
*/
public BrowserVersion getBrowserVersion()
{
return this.browserVersion;
}
/**
* Get the Proxy Host used by the WebClient.
*
* @return the Proxy Host
*/
public String getProxyHost()
{
return this.proxyHost;
}
/**
* Get the Proxy Port used by the WebClient.
*
* @return The Proxy Port
*/
public int getProxyPort()
{
return this.proxyPort;
}
/**
* Set the strategy to be used when making the initial request to the server.
*
* @param requestStrategy The InitialRequestStrategy implementation.
*/
public void setInitialRequestStrategy(InitialRequestStrategy requestStrategy)
{
this.requestStrategy = requestStrategy;
}
/**
* Add a cookie that will be sent with every request.
*
* @param name The cookie name.
* @param value The cookie value.
*/
public void addCookie(String name, String value)
{
this.cookies.put(name, value);
addCookiesToHeader();
}
/**
* Remove a cookie sent with every request.
*
* @param name The name of the cookie
*
* @return The value of the cookie removed.
*/
public String removeCookie(String name)
{
String value = this.cookies.remove(name);
addCookiesToHeader();
return value;
}
/**
* Get an unmodifiable Map of all the cookies to be sent with each request.
*
* @return The cookie Map.
*/
public Map<String, String> getCookies()
{
return Collections.unmodifiableMap(this.cookies);
}
/**
* Perform the initial request to the server. This is typically only called
* by the JSFSession.
*
* @return The Page created when the initial request is done.
*
* @throws IOException if HtmlUnit encountered an error.
*/
public Page doInitialRequest() throws IOException
{
if (this.initialRequestDone) throw new IllegalStateException("Initial request was already made.");
addCookiesToHeader();
doSeamSetup();
Page page = this.requestStrategy.doInitialRequest(this);
this.initialRequestDone = true;
return page;
}
private void doSeamSetup()
{
if (!SeamUtil.isSeamPresent()) return;
SeamUtil.suppressSeamComponentWarning();
JSFUnitWebConnection webConnection = (JSFUnitWebConnection)this.webClient.getWebConnection();
webConnection.addListener(new SeamRequestListener());
}
protected void addCookiesToHeader()
{
StringBuilder builder = new StringBuilder();
Map<String, String> cookies = getCookies();
for (Iterator<String> i = cookies.keySet().iterator(); i.hasNext();)
{
String key = i.next();
String value = cookies.get(key);
builder.append(key).append("=").append(value);
if (i.hasNext()) builder.append(";");
}
getWebClient().addRequestHeader("Cookie", builder.toString());
}
// -------- implementation of HttpSessionBindingListener -----------
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent)
{
// This is the recommended cleanup code ("close the browser windows")
// as per HtmlUnit issue:
// https://sourceforge.net/tracker/?func=detail&atid=448266&aid=2014629&group_id=47038
// -----------------------------------------------------------------------------------
webClient.closeAllWindows();
}
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent)
{
}
}