brush clojure

Thursday, October 23, 2008

Measuring user session size with HtmlUnit and remote JMX

Keeping the user session small is a very important requirement for the application I am working on. The application architecture has tried to keep the size as small as possible, but how big is it eventually? The best way to know is to measure how much memory each new session costs. This turned out pretty straightforward with standard Java and open source tools:
- HtmlUnit provides a compact Web client. With little programming you can emulate some user action. An example follows which logs a new user.
- Start the Application Server in a separate JVM. Allow for remote JMX Monitoring.
- Start thousands of new sessions en follow what happens to memory usage in the server JVM using MemoryMXBean. JConsole is the easiest way to monitor the memory usage, but if you want to do some calculations or logging you'll have to gain access to the remote MXBean programmatically.

The method is independent of any server technology. You don't even need the application sources. A pic to illustrate what is going on:



and some implementation details:

Start the server and allow for remote JMX monitoring
Add the following to the java arguments:

-Dcom.sun.management.jmxremote.port=1444
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false


Program a simple web client to perform some user action

HtmlUnit's Webclient is a beauty. Here's an example of logging in in the Pluto portlet container an clicking on a link:


public void loginAndStartUseCase() throws Exception {
final WebClient webClient = new WebClient();
HtmlPage page = (HtmlPage) webClient.getPage("http://localhost:8080/pluto/portal/MyPage");
List forms = page.getForms();
boolean loginFormSubmitted = false;
for (HtmlForm htmlForm : forms) {
if (htmlForm.getActionAttribute().equals("j_security_check")) {
htmlForm.getInputByName("j_username").setValueAttribute("pluto");
htmlForm.getInputByName("j_password").setValueAttribute("pluto");
htmlForm.submit(htmlForm.getInputByName("login"));
loginFormSubmitted = true;
break;
}
}
if (!loginFormSubmitted) {
throw new RuntimeException("Login Form expected here");
}
page = (HtmlPage) webClient.getPage("http://localhost:8080/pluto/portal/MyPage");
page = clickLink(page, "MyUseCase");

page.getAllHtmlChildElements();
}

private static HtmlPage clickLink(HtmlPage page, String linkText) throws IOException {
LOG.debug("click " + linkText);
List anchors = page.getAnchors();
for (HtmlAnchor htmlAnchor : anchors) {
if (htmlAnchor.getFirstChild().asText().equals(linkText)) {
return (HtmlPage) htmlAnchor.click();
}
}
throw new RuntimeException("No link " + linkText);
}


Obtain a remote memory monitoring MXBean proxy

private static Object getMXBean(String beanType, Class beanClass, int port) throws MalformedURLException,
IOException, MalformedObjectNameException {
JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
JMXConnector jmxConnector = JMXConnectorFactory.connect(u);
MBeanServerConnection beanServerConnection = jmxConnector.getMBeanServerConnection();

Hashtable props = new Hashtable();
props.put("type", beanType);
ObjectName on = new ObjectName("java.lang", props);

Set found = beanServerConnection.queryMBeans(on, null);
if (found.size() != 1) {
throw new RuntimeException("1 bean expected");
}

ObjectInstance objectInstance = found.iterator().next();

Object mxBean = ManagementFactory.newPlatformMXBeanProxy(beanServerConnection, objectInstance.getObjectName()
.toString(), beanClass);
return mxBean;
}


Start measuring!

Now you can start a new user session (or 10, or 1000), measure the memory impact and calculate whatever you want


MemoryMXBean memoryBean = (MemoryMXBean) getMXBean("Memory", MemoryMXBean.class, 1444);
for (many times) {
for(many_times) { loginAndStartUseCase(); }
memoryBean.gc();
long used = memoryBean.getHeapMemoryUsage().getUsed();
. . .



That's what my memory usage looks like after 10000 sessions:



This is an Excel chart built from comma-separated logging output.

The blue line shows the total memory usage - it usage grows consistently, it looks like no sessions have been released. The red line shows the calculated average session size. The average is pretty unreliable in the begin but stabilizes after a while - the cost of a new user just logging in and starting the use case seems to be around 3k.

Few remarks:
- Programming the Html WebClient for a more complex use case can be time consuming. Is there a good scripting alternative? Selenium was great, but starting a real browser for each user new session is not an option
- Make sure no sessions are released during the measurement. The test goes pretty fast, a normal session timeout of 15-30 min will be more than enough to reach your users limit.

Tuesday, October 7, 2008

Annotated controllers in Spring Portlet MVC

Usually trying out something new with the Spring framework goes smoothly. Reading the reference documentation provides me with the right amount of information to start and soon things work as expected - by me.

That is not what happened when I started using the annotated controllers in Spring Portlet MVC. I took me some time to get my first portlet working at all, and even more time to figure out how our team should write our portlets.

To sort things out, I started with a simple annotated controller that would allow me to collect a first and second name from the user, with few validations. A screenshot of two portlets on one Pluto page to get the idea:


That shouldn't be very difficult, right?

For me, it was. Here is a quick link to the final version.

The documentation is very clear about the design decision that all portlet details should be exposed to the portlet developer:

Most other portlet MVC frameworks attempt to completely hide the two phases from the developer and make it look as much like traditional servlet development as possible - we think this approach removes one of the main benefits of using portlets. So, the separation of the two phases is preserved throughout the Spring Portlet MVC framework.


Right, the portlet dispatcher has 2 phases, but the portlet annotations do not. You have to annotate your portlet's two phase controllers with the servlet annotation designed for one phase processing, so now and then you'll be short of words.

A small remark before we start: in your spring configuration, add the following interceptor to the DefalutAnnotationHandlerMapping:


<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<bean class="org.springframework.web.portlet.handler.ParameterMappingInterceptor" />
</property>
</bean>


This will copy the action parameter from the ActionRequest to the ActionResponse, so that the same parameter is present in the subsequent RenderRequest.

Let's start with a simple controller:

@Controller
@RequestMapping("VIEW")
public class PersonController {

@RequestMapping(params = "action=editPerson")
public void action(@ModelAttribute("person")
Person person) {
. . .
}

@RequestMapping(params = "action=editPerson")
public ModelAndView render(@ModelAttribute("person")
Person person) {
return . . .
}
}
and a url to kick off the controller action:

<portlet:actionURL>
<portlet:param name="action" value="person" />
</portlet:actionURL>


Here comes the first surprise: only the action method is called in both phases, render is never called. And the first lesson learned is:

@RequestMapping - argument types determine the phase mapping

The implementation of AnnotationMethodHandlerAdapter gives the details: each annotated method can be marked as a 'render' and/or 'action' method, or none. Action methods do not compete to handle render requests and vice versa. A method is marked as an action method mapping, if one of its parameters is of type ActionRequest, ActionResponse, InputStream, Reader. The coresponding render method argument types are RenderRequest, RenderResponse, OutputStream, Writer.
So in the example above both methods are neither action nor render and the best match in both cases is the first method in the list: action.

To avoid such surprises, don't forget to *always* include one the above classes in the annotated function arguments. My personal rule is always to include a request argument. Here comes the next controller version:


@RequestMapping(params = "action=editPerson")
public void action(ActionRequest request, @ModelAttribute("person") Person person);

@RequestMapping(params = "action=editPerson")
public ModelAndView render(RenderRequest request, @ModelAttribute("person") Person person)


This is not very nice; it is not always clear why this finction argument is there and a perfectionist reviewer could even remove it. Support for portlet specifics in the annotation would be better:

@RenderMapping(params = "action=editPerson", phase="ACTION")
public void action(@ModelAttribute("person") Person person)


Let's do validations. The submitted form is handled in the action phase, all subsequent renders should show the same validation errors. The next controller version:


@Controller
@RequestMapping("VIEW")
public class PersonController {

@RequestMapping(params = "action=person")
public void action(ActionRequest request, Person person, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "firstName", "firstName.empty", "First name cannot be empty");
}

@RequestMapping(params = "action=person")
public ModelAndView render(RenderRequest request, Person person, Errors errors) {
return new ModelAndView("person");
}
}


The result is frightening: the error message is never shown from the following JSP fragment:


First Name
<form:input path="firstName"/>
<form:errors path="firstName"/>

The reason is the command object present in the arguments list of the render method; remove it an you'll see the validation warning. Here comes the next lesson:

Don't pass command objects as render parameters

Each time a command object argument is needed, it's bound again with the current request, no matter action or render request.
The next version of our controller is:


@RequestMapping(params = "action=person")
public ModelAndView render(RenderRequest request) {
. . .

With no command object as render parameter, the errors are preserved from the action phase to all subsequent render requests.

Now we have a non-empty implicit model which Spring holds for us in the session, hence the extra render parameter org.springframework.web.portlet.mvc.ImplicitModel=true in the request. Model data created in the ACTION phase is available in the RENDER phase.

Where to create your command object?

In the servlet example at http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-requestmapping the bean is prepared in the GET request processing, bind and validate at following POSTs, in two separate methods. I like the idea of having two different methods for the first call when the bean is created, and for subsequent calls when it is filled and validated:

@RequestMapping(params = "action=startPerson")
public ModelAndView renderFormFirst(RenderRequest request) {
ModelAndView modelAndView = new ModelAndView("/WEB-INF/jsp/person.jsp");
Person person = new Person();
// initialize from model...
person.setFirstName("John");
modelAndView.addObject("person", person);
return modelAndView;
}

@RequestMapping(params = "action=person")
public ModelAndView renderForm(RenderRequest request, ModelMap modelMap) {
return new ModelAndView("/WEB-INF/jsp/person.jsp");
}


Having only a render handler for starting the process is fine: normally an action handler will set render parameters for the next render, like in:

@RequestMapping(params = "action=person")
public void action(ActionRequest request, ActionResponse response, Person person, Errors errors, SessionStatus sessionStatus) {
ValidationUtils.rejectIfEmpty(errors, "firstName", "firstName.empty", "Please, fill in your first name");
ValidationUtils.rejectIfEmpty(errors, "lastName", "lastName.empty", "Please, fill in your last name");
if (!errors.hasErrors()) {
response.setRenderParameter("action", "hello");
}
}


The next inconsstiency in the annotated portlet controllers:
@ModelAttribute methods called for both action and render phase

The @ModelAttribute annotated methods, as can be expected, is handled the same in both phases. Chances are you won't need most of them in the action phase, like the following example:

@ModelAttribute("countries")
public List getCountries() {
. . .
}


Once again, a better annotation would be:


@ModelAttribute("countries", phase="RENDER")
public List getCountries() {
. . .
}


As this is not available, my rule is: don't use Model attribute. Call it explicitly in the needed (probably render) method.