0% found this document useful (0 votes)
93 views34 pages

Testing, Debugging, Logging, Performance Tuning Struts Applications

Sang Shin is a full-time employee of sun microsystems, but the contents here are created as their own personal endeavor. First, we will talk about logging, then Unit Testing and debugging, and performance tuning and best practice guidelines.

Uploaded by

api-3760916
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
93 views34 pages

Testing, Debugging, Logging, Performance Tuning Struts Applications

Sang Shin is a full-time employee of sun microsystems, but the contents here are created as their own personal endeavor. First, we will talk about logging, then Unit Testing and debugging, and performance tuning and best practice guidelines.

Uploaded by

api-3760916
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

07/21/2005

Testing, Debugging,
Logging,
Performance tuning
Struts Applications

1
07/21/2005

Sang Shin
[email protected]
www.javapassion.com
Java™ Technology Evangelist
Sun Microsystems, Inc.
2

2
07/21/2005

Disclaimer & Acknowledgments


? Even though Sang Shin is a full-time employees of Sun
Microsystems, the contents here are created as their own personal
endeavor and thus does not reflect any official stance of Sun
Microsystems.
? Sun Microsystems is not responsible for any inaccuracies in the
contents.
? Acknowledgments:
– The slides from this presentation is made from StrutsTestCase tutorial from
http://strutstestcase.sourceforge.net/ written by Deryl Seale

3
07/21/2005

Revision History
? 12/01/2003: version 1: created by Sang Shin
? Things to do
– Contents on performance tuning, debugging still
need to be added

4
07/21/2005

Topics
? StrutsTestCase Unit testing
? Logging

This is the topics we will discuss in this session. First, we will talk about
logging, then unit testing and debugging, and performance tuning and best
practice guidelines.

5
07/21/2005

Unit Testing

6
07/21/2005

Unit Testing Tools


? JUnit
– For testing JavaBeans
– http://www.junit.org
? Cactus
– For testing JSP, taglibs and conventional servlet
components
– http://jakarta.apache.org/cactus/
? StrutsTestCase
– For testing Action servlets
– http://strutstestcase.sourceforge.net
7

1. Unit test normal java beans with JUnit. (more...)


2. Unit test JSP, taglibs and conventional servlet components with Cactus.
(more...)
3. Unit test Action servlets with StrutsTestCase. (more...)

7
07/21/2005

StrutsTestCase
? Extends JUnit framework to allow testing the
Action class
? Because StrutsTestCase uses the ActionServlet
controller to test your code, you can test not only
the implementation of your Action objects, but
also your mappings, form beans, and forwards
declarations
? Offers two approaches for testing
– Mock Object approach
– In-container testing approach

StrutsTestCase for JUnit is an extension of the standard JUnit TestCase class that
provides facilities for testing code based on the Struts framework. StrutsTestCase
provides both a Mock Object approach and a Cactus approach to actually run the
Struts ActionServlet, allowing you to test your Struts code with or without a
running servlet engine. Because StrutsTestCase uses the ActionServlet controller
to test your code, you can test not only the implementation of your Action objects,
but also your mappings, form beans, and forwards declarations. And because
StrutsTestCase already provides validation methods, it's quick and easy to write
unit test cases.

8
07/21/2005

Two Testing Approaches


? Mock objects approach
– test classes by simulating the server container
? In-container testing approach
– tests classes running in the actual server container
? StrutsTestCase for JUnit allows you to use
either approach, with very minimal impact
on your actual unit test code
– setup and validation methods are exactly the same for
both approaches
– choosing one approach over the other simply effects
which base class you use
9

There are two popular approaches to testing server-side classes: mock objects,
which test classes by simulating the server container, and in-container testing,
which tests classes running in the actual server container. StrutsTestCase for JUnit
allows you to use either approach, with very minimal impact on your actual unit
test code. In fact, because the StrutsTestCase setup and validation methods are
exactly the same for both approaches, choosing one approach over the other
simply effects which base class you use!

9
07/21/2005

Base Classes
? Mock objects
– MockStrutsTestCase uses a set of HttpServlet
mock objects to simulate the container
environment without requiring a running servlet
engine
? In-container
– CactusStrutsTestCase uses the Cactus testing
framework to test Struts classes in the actual
server container

10

StrutsTestCase for JUnit provides two base classes, both of which are extensions
of the standard JUnit TestCase. MockStrutsTestCase uses a set of HttpServlet
mock objects to simulate the container environment without requiring a running
servlet engine. CactusStrutsTestCase uses the Cactus testing framework to test
Struts classes in the actual server container, allowing for a testing environment
more in line with the actual deployment environment.

Please note that while the following examples use the MockStrutsTestCase
approach, you could choose to use the Cactus approach by simply subclassing
from CactusStrutsTestCase without changing another line of code!
How does it work?

10
07/21/2005

Example
public class LoginAction extends Action {

public ActionForward perform(ActionMapping mapping,


ActionForm form,
HttpServletRequest request,
HttpServletResponse response){

String username = ((LoginForm) form).getUsername();


String password = ((LoginForm) form).getPassword();

ActionErrors errors = new ActionErrors();

if ((!username.equals("deryl")) || (!password.equals("radar")))
errors.add("password",new ActionError("error.password.mismatch"));

if (!errors.empty()) {
saveErrors(request,errors);
return mapping.findForward("login");
}

11

So, what are we doing here? Well, we receive an ActionForm bean which should
contain login information. First, we try to get the username and password
information, and then check to see if it is valid. If there is a mismatch in the
username or password values, we then create an ActionError message with a key
to a message catalogue somewhere, and then try to forward to the login screen so
we can log in again. If the username and password match, however, we store some
authentication information in the session, and we try to forward to the next page.

11
07/21/2005

Example
// store authentication info on the session
HttpSession session = request.getSession();
session.setAttribute("authentication", username);

// Forward control to the specified success URI


return mapping.findForward("success");
}

12

12
07/21/2005

Things That Can to be Tested


for the Sample Action
? Does the LoginForm bean work properly? If we
place the appropriate parameters in the request,
does this bean get instantiated correctly?
? If the username or password doesn't match, do
the appropriate errors get saved for display on
the next screen? Are we sent back to the login
page?
? If we supply the correct login information, do we
get to the correct page? Are we sure there are no
errors reported? Does the proper authentication
information get saved in the session?

13

There are several things we can test here:

* Does the LoginForm bean work properly? If we place the appropriate


parameters in the request, does this bean get instantiated correctly?
* If the username or password doesn't match, do the appropriate errors get
saved for display on the next screen? Are we sent back to the login page?
* If we supply the correct login information, do we get to the correct page? Are
we sure there are no errors reported? Does the proper authentication information
get saved in the session?

StrutsTestCase gives you the ability to test all of these conditions within the
familiar JUnit framework. All of the Struts setup -- which really amounts to
starting up the ActionServlet -- is taken care of for you.

13
07/21/2005

Example: Empty Test Case


public class TestLoginAction extends MockStrutsTestCase {

public void setUp() { super.setUp(); }

public void tearDown() { super.tearDown(); }

public TestLoginAction(String testName) { super(testName); }

public void testSuccessfulLogin() {}


}

14

So, how do we actually do it? Let's start by creating an empty test case, which we
extend from the base StrutsTestCase class.

If you choose to override the setUp() method, you must explicitly call
super.setUp(). This method performs some important initialization routines, and
StrutsTestCase will not work if it is not called.

14
07/21/2005

Step 1: Tell Struts which mapping


to use in this test
public class TestLoginAction extends MockStrutsTestCase {

public TestLoginAction(String testName) { super(testName); }

public void testSuccessfulLogin() {


// Tell Struts which mapping to use in this test.
// To do so, we specify a path that is associated with
// a Struts mapping; this is the same mechanism that
// the Struts tag library method uses.
setRequestPathInfo("/login");
}
}

15

The first thing we need to do is to tell Struts which mapping to use in this test. To
do so, we specify a path that is associated with a Struts mapping; this is the same
mechanism that the Struts tag library method uses.

15
07/21/2005

Step 3: Make Action Class do its work


public class TestLoginAction extends MockStrutsTestCase {

public TestLoginAction(String testName) { super(testName); }


public void testSuccessfulLogin() {

setRequestPathInfo("/login");

addRequestParameter("username","deryl");
addRequestParameter("password","radar");

// Make the Action to do its thing, which just involves


// executing the actionPerform method
actionPerform();
}
}

16

Finally, we need to get the Action to do its thing, which just involves executing
the actionPerform method:

16
07/21/2005

Step 4: Verify that everything happened


as expected
public class TestLoginAction extends MockStrutsTestCase {
public TestLoginAction(String testName) { super(testName); }
public void testSuccessfulLogin() {

setRequestPathInfo("/login");

addRequestParameter("username","deryl");
addRequestParameter("password","radar");

actionPerform();

// verify that everything happened as we expected it to


verifyForward("success");
}
}

17

That's all you have to do to get the ActionServlet to process your request, and if
all goes well, then nothing will happen. But we're not done yet -- we still need to
verify that everything happened as we expected it to. First, we want to make sure
we got to the right page:

It's worth noting here that when you verify which page you ended up at, you can
use the Struts forward mapping. You don't have to hard code filenames -- the
StrutsTestCase framework takes care of this for you. Thus, if you were to change
where "success" pointed to, your tests would still work correctly. All in the spirit
of Struts.

17
07/21/2005

Step 5: Make sure that authentication


information was stored properly:
public class TestLoginAction extends MockStrutsTestCase {
public TestLoginAction(String testName) { super(testName); }
public void testSuccessfulLogin() {

setRequestPathInfo("/login");
addRequestParameter("username","deryl");
addRequestParameter("password","radar");
actionPerform();
verifyForward("success");

// Make sure that authentication information was stored properly


assertEquals("deryl",(String) getSession().getAttribute("authentication"));

}
}

18

Next, we want to make sure that authentication information was stored properly:

Here we're getting the session object from the request, and checking to see if it has
the proper attribute and value. You could just as easily place an object on the
session that your Action object expects to find. All of the servlet classes available
in the StrutsTestCase base classes are fully functioning objects.

18
07/21/2005

Step 6: Verify No ActionError messages


were sent along
public class TestLoginAction extends MockStrutsTestCase {
public TestLoginAction(String testName) { super(testName); }
public void testSuccessfulLogin() {

setRequestPathInfo("/login");
addRequestParameter("username","deryl");
addRequestParameter("password","radar");
actionPerform();
verifyForward("success");
assertEquals("deryl",(String) getSession().getAttribute("authentication"));

// Make sure that no ActionError messages were sent along.


// We can use a built in method to make sure of this condition:
verifyNoActionErrors();

}
}

19

Finally, we want to make sure that no ActionError messages were sent along. We
can use a built in method to make sure of this condition:

19
07/21/2005

Example 2: test the case where a user


supplies incorrect login information
public class TestLoginAction extends MockStrutsTestCase {
public TestLoginAction(String testName) { super(testName); }
public void testSuccessfulLogin() {

addRequestParameter("username","deryl");
addRequestParameter("password","express");
setRequestPathInfo("/login");
actionPerform();

// Test the case where a user supplies incorrect login information


verifyForward("login");
verifyActionErrors(new String[] {"error.password.mismatch"});
assertNull((String) getSession().getAttribute("authentication"));

}
}

20

So, now that we've written one test case, it's easy to write another. For example,
we'd probably want to test the case where a user supplies incorrect login
information. We'd write such a test case like the following:

Now, this looks quite similar to our first test case, except that we're passing
incorrect information. Also, we're checking to make sure we used a different
forward, namely one that takes us back to the login page, and that the
authentication information is not on the session.

We're also verifying that the correct error messages were sent. Note that we used
the symbolic name, not the actual text. Because the verifyActionErrors() method
takes a String array, we can verify more than one error message, and
StrutsTestCase will make sure there is an exact match. If the test produced more
error messages than we were expecting, it will fail; if it produced fewer, it will
also fail. Only an exact match in name and number will pass.

It's that easy! As you can see, StrutsTestCase not only tests the implementation of
your Action objects, but also the mappings that execute them, the ActionForm
beans that are passed as arguments, and the error messages and forward
statements that result from execution. It's the whole enchilada!
20
07/21/2005

StrutsTestCase
Testing Tiles

21

21
07/21/2005

Test If Correct Tile Definition is


Used for Forwarding
public class TestLoginAction extends MockStrutsTestCase {

public TestLoginAction(String testName) { super(testName); }

public void testSuccessfulLogin() {


setConfigFile("/WEB-INF/struts-config.xml");
setRequestPathInfo("/login.do");
addRequestParameter("username","deryl");
addRequestParameter("password","radar");
actionPerform();

verifyTilesForward("success","success.tiles.def");
}
}

22

The Tiles framework, which is integrated into Struts 1.2, is a flexible templating
mechanism designed to easily re-use common user experience elements.
StrutsTestCase now provides support for testing applications that use Tiles,
allowing any test case to verify that an Action object uses the correct Tiles
definition. Tiles testing is similar to calling verifyActionForward, with a twist:

This is similar to our previous test cases, except that we're additionally passing in
a definition name to verify that this Action uses a given Tiles definition when
resolving the expected forward. If it uses a different Tiles definition, or if the
expected definition does not exist, then this test will fail.

22
07/21/2005

Test an Action uses a Correct Tiles


Definition as an input mapping
public class TestLoginAction extends MockStrutsTestCase {

public TestLoginAction(String testName) { super(testName); }

public void testSuccessfulLogin() {


setConfigFile("/WEB-INF/struts-config.xml");
setRequestPathInfo("/login.do");
addRequestParameter("username","deryl");
addRequestParameter("password","radar");
actionPerform();
verifyInputTilesForward("success.tiles.def");
}
}

23

Additionally, you can call verifyInputTilesForward to verify that an Action uses


an input mapping, and that input mapping is the expected Tiles definition:

23
07/21/2005

Logging

24

24
07/21/2005

Logging Schemes for Web


Applications
? Using log() method of ServletConext object
? Jakarta commons logging

25

In Struts 1.0, the logging functionality was fairly limited. You could set a
debugging detail level with a servlet initialization parameter, and all log
messages were written to wherever ServletContext.log() output is sent by your
servlet container. WIth Struts 1.1, however, all logging messages written by
Struts itself, as well as the commons librarires that it utilizes, flow through an
abstract wrapper called Commons Logging, which can be used as a wrapper
around any logging implementation. The most common implementations used
are simple logging to System.err, the Apache Log4J package, or the built-in
logging capabilities of JDK 1.4 or later in the java.util.logging package.

Commons Logging provides fine-grained control over the logging messages


created by a Log instance. By convention, the Log instances for Struts (and the
Commons packages in general) are named the fully qualified class name of the
class whose messages are being logged. Therefore, log messages created by the
RequestProcessor class are, naturally enough, directed to a logger named
org.apache.struts.action.RequestProcessor.

25
07/21/2005

Log() method of ServletContext

? Every container has to support this method


of logging
? Name and location of the log file is
container specific
? ServletContext class has two log() methods
– public void log (String msg);
– public void log (String msg, Throwable throwable);

26

In Struts 1.0, the logging functionality was fairly limited. You could set a
debugging detail level with a servlet initialization parameter, and all log
messages were written to wherever ServletContext.log() output is sent by your
servlet container. WIth Struts 1.1, however, all logging messages written by
Struts itself, as well as the commons librarires that it utilizes, flow through an
abstract wrapper called Commons Logging, which can be used as a wrapper
around any logging implementation. The most common implementations used
are simple logging to System.err, the Apache Log4J package, or the built-in
logging capabilities of JDK 1.4 or later in the java.util.logging package.

Commons Logging provides fine-grained control over the logging messages


created by a Log instance. By convention, the Log instances for Struts (and the
Commons packages in general) are named the fully qualified class name of the
class whose messages are being logged. Therefore, log messages created by the
RequestProcessor class are, naturally enough, directed to a logger named
org.apache.struts.action.RequestProcessor.

26
07/21/2005

What is Jakarta Commons Logging?


? Provides common logging API
– API has no dependency to any logging
implementations
? Insulates applications from actual logging
implementations
– Applications do not have to change when logging
implementation is changed
? Developers declaratively configure logging
implementation
– Logging implementation is dynamically discovered
at runtime
27

In Struts 1.0, the logging functionality was fairly limited. You could set a
debugging detail level with a servlet initialization parameter, and all log
messages were written to wherever ServletContext.log() output is sent by your
servlet container. WIth Struts 1.1, however, all logging messages written by
Struts itself, as well as the commons librarires that it utilizes, flow through an
abstract wrapper called Commons Logging, which can be used as a wrapper
around any logging implementation. The most common implementations used
are simple logging to System.err, the Apache Log4J package, or the built-in
logging capabilities of JDK 1.4 or later in the java.util.logging package.

Commons Logging provides fine-grained control over the logging messages


created by a Log instance. By convention, the Log instances for Struts (and the
Commons packages in general) are named the fully qualified class name of the
class whose messages are being logged. Therefore, log messages created by the
RequestProcessor class are, naturally enough, directed to a logger named
org.apache.struts.action.RequestProcessor.

27
07/21/2005

Possible Logging Implementations


? log4j
? JDK 1.4 logging
? LogKit
? SimpleLog
– available with Struts distribution
– writes log messages to stdout and stderr
? NoOpLog
– available with Struts distribution
– log messages are ignored
28

In Struts 1.0, the logging functionality was fairly limited. You could set a
debugging detail level with a servlet initialization parameter, and all log
messages were written to wherever ServletContext.log() output is sent by your
servlet container. WIth Struts 1.1, however, all logging messages written by
Struts itself, as well as the commons librarires that it utilizes, flow through an
abstract wrapper called Commons Logging, which can be used as a wrapper
around any logging implementation. The most common implementations used
are simple logging to System.err, the Apache Log4J package, or the built-in
logging capabilities of JDK 1.4 or later in the java.util.logging package.

Commons Logging provides fine-grained control over the logging messages


created by a Log instance. By convention, the Log instances for Struts (and the
Commons packages in general) are named the fully qualified class name of the
class whose messages are being logged. Therefore, log messages created by the
RequestProcessor class are, naturally enough, directed to a logger named
org.apache.struts.action.RequestProcessor.

28
07/21/2005

Example: How to Use Logging API?


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public final class EditSubscriptionAction extends Action {

private Log log =


LogFactory.getLog("org.apache.struts.webapp.Example");

public ActionForward execute(ActionMapping mapping, ..)


throw Exception{
....
if (log.isDebugEnabled()) {
log.debug("EditSubscriptionAction: Processing " + action + " action");
}
....
if (user == null) {
if (log.isTraceEnabled()) {
log.trace(" User is not logged on in session " + session.getId());
}
return (mapping.findForward("logon"));
} 29

Using commons-logging in your own code is very simple - all you need are two
imports and a declaration for a logger. Let's take a look:

The general idea is to instantiate a single logger per class and to use a name for
the logger which reflects where it's being used. The example is constructed with
the class itself. This gives the logger the name of
“org.apache.struts.webapp.Example”. Doing things this way lets you easily see
where the output is coming from, so you can quickly pin-point problem areas. In
addition, you are able to enable/disable logging in a very fine-grained way.

29
07/21/2005

Example: How to Use Logging API?


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public final class EditSubscriptionAction extends Action {

private Log log =


LogFactory.getLog("org.apache.struts.webapp.Example");

public ActionForward execute(ActionMapping mapping, ..)


throw Exception{
....
if (log.isDebugEnabled()) {
log.debug("EditSubscriptionAction: Processing " + action + " action");
}
....
if (user == null) {
if (log.isTraceEnabled()) {
log.trace(" User is not logged on in session " + session.getId());
}
return (mapping.findForward("logon"));
} 30

Using commons-logging in your own code is very simple - all you need are two
imports and a declaration for a logger. Let's take a look:

The general idea is to instantiate a single logger per class and to use a name for
the logger which reflects where it's being used. The example is constructed with
the class itself. This gives the logger the name of
“org.apache.struts.webapp.Example”. Doing things this way lets you easily see
where the output is coming from, so you can quickly pin-point problem areas. In
addition, you are able to enable/disable logging in a very fine-grained way.

30
07/21/2005

Example2: init() method of


MemoryDatabasePlugin in struts-example1
public final class MemoryDatabasePlugIn implements PlugIn {
...
/**
* Initialize and load our initial database from persistent
storage.
*
* @param servlet The ActionServlet for this web application
* @param config The ApplicationConfig for our owning module
*
* @exception ServletException if we cannot configure ourselves
correctly
*/
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {

log.info("Initializing memory database plug in from '" +


pathname + "'");

// Remember our associated configuration and servlet


this.config = config;
this.servlet = servlet;

31

This is another example of how logging API is used. This is the init() method of
MemoryDatabasePlugin in struts-example1 sample code. Here we have a single
INFO line that will be logged.

31
07/21/2005

Example2: init() method of


MemoryDatabasePlugin in struts-example1

32

Because the init() method of the plugin should be called when the application
gets installed and loaded, I did “ant install”.

32
07/21/2005

<jwsdp-install>/logs/launcher.server.log

33

This is theout from launcher.server.log file. As you see the line that is pointed
by the arrow, the information that we saw in the previous slide is wrtten to a log
file here.

33
07/21/2005

Passion!

34

34

You might also like