Showing posts with label struts. Show all posts
Showing posts with label struts. Show all posts

Tuesday, September 25, 2007

Testing HTTPS Secured Actions with StrutsTestCase

If you have secured some Struts actions using SSLEXT's Struts plugin (org.apache.struts.action.SecurePlugIn) you will have to invoke the action over https, else SSLEXT will redirect to the configured HTTPS port.

So in order to test secured actions using MockStruts test cases you need to inform SecurePlugIn that the request came over https. This is quite easy as the HttpServletRequestSimulator class that's used by MockStruts has a setScheme method. All you have to do is set the scheme to HTTPS in the inherited request object before calling the actionPerform method in the test class.

e.g.


public class LoginTest extends MockStrutsTestCase {
public void testlogin() {
setConfigFile("/WEB-INF/struts-config.xml");
setRequestPathInfo("/login");
addRequestParameter("username", "test");
addRequestParameter("password", "test1234");

//set the request's scheme to HTTPS
request.setScheme("HTTPS");
//invoke action
actionPerform();

verifyNoActionErrors();
verifyForward("success");
}
}

Don' forget to click the +1 button below if this post was helpful.

Friday, September 7, 2007

Speed up MockStruts Test Execution by Caching Spring Context

If you are using MockStruts to test your Struts/Spring application most of your test execution time would be spent on initializing or trying to initialize the Spring context for each test method.

This is true if you use Spring's ContextLoaderPlugIn, where it tries to initialize a custom spring context using the parent context.

Spring will look for the parent context in the ServletContext object where it store it. Since MockStruts will create a new ServletContext for each test method Spring will try to create the context each time. This results in addition of about 4 second per test method (on my workstation).

Note: I'm using MappingDispatchAction with multiple actions in a single Action class, therefore I have mutiple test methods in a single test class too.

The above 4 second penalty per test method can be removed by simply caching and storing the Spring context in ServletContext object before each test execution. This reduced the execution time of the whole test suite drastically in the application I was working on.

In order to achieve the above I used a base class extending MockStrutsTestCase as shown below and used it as the parent class for all the Struts test cases


import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import servletunit.struts.MockStrutsTestCase;

public abstract class BaseStrutsTestCase extends MockStrutsTestCase {

/** Spring context */
private static ApplicationContext applicationContext;

/**
* Setup Spring context
*
* @throws Exception
* exception
*/
protected void setUp() throws Exception {
super.setUp();

if (applicationContext == null) {
// this is the first time. Initialize the Spring context.
applicationContext = (new ContextLoader()).initWebApplicationContext(getRequest().getSession()
.getServletContext());
} else {
// Spring context is already initialized. Set it in servlet context
// so that Spring's ContextLoaderPlugIn will not initialize it again
getRequest().getSession().getServletContext().setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
}
}

/**
* Returns the Spring application context
*
* @return initialized Spring application context
*/
public ApplicationContext getApplicationContext() {
return applicationContext;
}

}


Also remember to set forkMode to perBatch or once in ants junit task as shown below if you have set the fork attribute to true.


<junit fork="true" forkmode="once" haltonfailure="">
...

</junit>

Don' forget to click the +1 button below if this post was helpful.

Thursday, May 17, 2007

Global Exception Handling for Struts and Tiles Applications

Update: The solution explained in this post has been tested only on Weblogic 8.1 and 9.2. On Tomcat 5.x this will not work and please post a comment with the results if you happen to test this on any other server or find a work around for the Tomcat issue.

I was looking at implementing a global error page for unhandled exceptions in a Struts / Tiles application. Normally this was achieved by adding a section in struts-config.xml with a custom exception handler to log the exceptions and the url to the global error page.

This would forward to the error page if an unhandled exception was thrown from the Struts action but will not do so if the exception occurred in one of the tiles JSPs. An exception in a tile would result in a partly rendered page but this was fine in previous app as there were hardly any logic in JSP files and it was very rare to have an exception at the JSP level in production.

In the new app though this wasn’t the case as there were lot of logic that went in to JSPs in the form of custom tags etc… that could potentially throw all sorts of exceptions at run time and I wanted to handle all these exceptions in a unified manner.


New Solution

I abandoned the global-exceptions approach in struts-config altogether and added a <error-page> section to the web.xml file which instructed the app server to forward the request to the specified URL on all unhandled exceptions.

<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error </location>
</error-page>

Then added a servlet and a servlet mapping to the /error URL

<servlet>
<servlet-name>exceptionHandler</servlet-name>
<servlet-class>com.test.ExceptionHandlerServlet</servlet-class>
<init-param>
<param-name>errorPageURL</param-name>
<param-value>error.html</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>exceptionHandler</servlet-name>
<url-pattern>/error</url-pattern>
</servlet-mapping>

The ExceptionHandlerServlet would log the exception and redirect the browser to the URL defined in the servlet configuration (error.html).

ExceptionHandlerServlet Code:





protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
logger.debug("Handling exception in ErrorHandlerServlet");

Throwable exception = null;

// Check if struts has placed an exception object in request
Object obj = request.getAttribute(Globals.EXCEPTION_KEY);

if (obj == null) {
// Since no struts exception is found,
// check if a JSP exception is available in request.
obj = request.getAttribute("javax.servlet.jsp.jspException");
}

if ((obj != null) && (obj instanceof Throwable)) {
exception = (Throwable) obj;
}


if (logger.isDebugEnabled()) {
logger.debug("Request URI: " + request.getAttribute("javax.servlet.forward.request_uri"));
}

// request uri containing the original URL value will be available
// only on servers implementing servlet 2.4 spec
String requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri");

logger.error("Exception while handling request: " + requestURI, exception);
response.sendRedirect(errorPageURL);
} catch (Exception e) {
// Throwing exceptions from this method can result in request
// going in to an infinite loop forwarding to the error servlet recursively.
e.printStackTrace();
}
}


Now on all unhandled exceptions the servlet will log the exception. Yet on some occasions where the exception was thrown in one of the tile JSPs the browser would display a partially rendered page and would not redirect to the error page.


This was due to tiles flushing content to the response buffer before the whole page was rendered. In such a situation the browser will happily display what it received and the redirect to the error page will have no effect.

In order to prevent or minimize the flushing of response buffer:

1. Set flush attribute to false in all tiles insert tags
2. Increase the response buffer size to minimize auto flushing

This was achieved by modifying the tiles layout jsp page.

<!-- sets the response buffer size to 64kb -->
<%@ page buffer="64kb"%>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles"%>
…
<tiles:insert attribute="header" flush="false" />
<tiles:insert attribute="leftnav" flush="false" />
<tiles:insert attribute="body" flush="false" />
<tiles:insert attribute="footer" flush="false" />


In the above code segment all html formatting was removed for clarity. The response buffer was set to 64 kilobytes and this value should be decide based on the average page size of the application and the performance impact of the increased memory usage.


Don' forget to click the +1 button below if this post was helpful.