Archive for the ‘Java’ Category

Displaytag + Struts 2 localization

August 12th, 2009

Displaytag 1.2 (current latest release) provides several LocaleResolver, but none works with the latest version of Struts 2.1.X. There was a small change in Xwork API breaking the I18nWebworkadapter. ActionContext#getValueStack() now returns a ValueStack instead of OgnlValueStack.

Here is the updated LocaleResolver for Struts 2.1.6 :
/**
* Licensed under the Artistic License; you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://displaytag.sourceforge.net/license.html
*
* THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.displaytag.localization;

import java.util.Iterator;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.views.jsp.TagUtils;
import org.displaytag.Messages;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.util.ValueStack;

/**
* Struts2 implementation of a resource provider and locale resolver.
* @author Richard HALLIER
* @author Fabrizio Giustina
* @author Nicolas Richeton - Fix for Struts 2.1.6
* @version $Revision: 1081 $ ($Author: fgiust $)
*/
public class I18nStruts2Adapter implements LocaleResolver, I18nResourceProvider
{

/**
* prefix/suffix for missing entries.
*/
public static final String UNDEFINED_KEY = "???"; //$NON-NLS-1$

/**
* logger.
*/
private static Log log = LogFactory.getLog(I18nStruts2Adapter.class);

/**
* @see LocaleResolver#resolveLocale(HttpServletRequest)
*/
public Locale resolveLocale(HttpServletRequest request)
{

Locale result = null;
ValueStack stack = ActionContext.getContext().getValueStack();

Iterator iterator = stack.getRoot().iterator();
while (iterator.hasNext())
{
Object o = iterator.next();

if (o instanceof LocaleProvider)
{
LocaleProvider lp = (LocaleProvider) o;
result = lp.getLocale();

break;
}
}

if (result == null)
{
log.debug("Missing LocalProvider actions, init locale to default");
result = Locale.getDefault();
}

return result;
}

/**
* @see I18nResourceProvider#getResource(String, String, Tag, PageContext)
*/
public String getResource(String resourceKey, String defaultValue, Tag tag, PageContext pageContext)
{

// if resourceKey isn't defined either, use defaultValue
String key = (resourceKey != null) ? resourceKey : defaultValue;

String message = null;
ValueStack stack = TagUtils.getStack(pageContext);
Iterator iterator = stack.getRoot().iterator();

while (iterator.hasNext())
{
Object o = iterator.next();

if (o instanceof TextProvider)
{
TextProvider tp = (TextProvider) o;
message = tp.getText(key);

break;
}
}

// if user explicitely added a titleKey we guess this is an error
if (message == null && resourceKey != null)
{
log.debug(Messages.getString("Localization.missingkey", resourceKey)); //$NON-NLS-1$
message = UNDEFINED_KEY + resourceKey + UNDEFINED_KEY;
}

return message;
}

}

Fixing Struts2 initialization error on Jetty restart

August 12th, 2009

[ERROR] Error reconfiguring/restarting webapp after change in watched files
Caught exception while loading file struts-default.xml – [unknown location]

Caused by: java.lang.ClassCastException: org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration

This issue seems to only occur with Java 6 and is related to the Xalan version included in this JDK.

Add xalan to your pom or in an endorsed/shared directory and the problem goes away.

<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.1</version>
</dependency>

More details there : JIRA item.

Struts 2 tips

August 11th, 2009

Filters
org.apache.struts2.dispatcher.FilterDispatcher and org.apache.struts2.dispatcher.ActionContextCleanUp filters are now deprecated (Struts 2.1.x), but most wiki resources and maven archetypes are not updated yet.

According to WW-2167, these 2 filters are broken and can even create memory leaks.

The new filters are : org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter and org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter

Security
Don’t forget to look at security bulletins. There are some pretty bad vulnerabilities issues on versions < 2.0.12, including a “remote code exploit” and a “Directory traversal vulnerability“.

Doctypes
Doc types headers are necessary to have Struts pick up your xml configuration files. Even with the right name and location, it will not work without the right doctype. Beware of copying/pasting without it.

Accessing page context data in struts tags
Use #attr . This is usefull when you want to get values created by jstl or display tags. See this post. The Struts2 -> Displaytag -> Struts2 round tip can be done this way :

<s:set name="user" value="#session.user" scope="request" />
<display:table uid="profile" name="user.profiles" />
<s:property value="#attr.profile.label"/>
</display:table>

Paris JUG : Soirée RIA (FR)

July 4th, 2009

Le Paris JUG organise une soirée RIA (Flex, JavaFX), le mardi 7 juillet.

Note : L’inscription est obligatoire.

Pour ma part, j’y vais. Rendez-vous là-bas, cela peut aussi être une occasion de discuter Eclipse et RCP :)

Memory analysis with Eclipse 3.5

June 12th, 2009

Eclipse Galileo (3.5) will be released on June 24, 2009 and comes with a really good and easy-to-use JVM Memory Analyzer.

Memory chart

Memory chart

Here is a quick memory analysis walkthrough :

Creating a heap dump

You can dump the memory of a Java process anytime using jmap which is included in the JDK.

To ensure jmap is working for you, get your java process ID :
ps -Af | grep java

or, if you want to dump eclipse memory :
ps -Af | grep eclipse

Then ask jmap to output a memory summary :

jmap <pid>

Jmap output :
Attaching to process ID 356, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.5.0_16-133

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
(...)

If you get an error, take a look at the end of this post for a few tips.

Ok so jmap works for you. Now dump the complete memory in a file :
jmap -heap:format=b <pid>

There is now a file named heap.bin in your current directory.

Installing Memory Analyzer
Start Eclipse, use Help->Install new software… and select Memory Analyzer from the Galileo update site :

Installing Memory Analyzer

Installing Memory Analyzer

Opening the dump

Switch to Memory Analysis perspective

Perspectives : select Memory Analysis

Perspectives : select Memory Analysis


Use File->Open Head Dump… and select heap.bin
Memory summary

Memory summary

You can now digg into your application heap memory and ensure that no objects is using more memory than it should. Memory Analyzer also includes really nice features like an automatic leak search :

Leak suspects report

Leak suspects report

Have fun and catch the leaks :-)

jmap tips
If you have problems running jmap, try the following tips :

  • Be sure to use the jmap binary packaged with the JVM you are running : jmap from a Java 5 JDK will not be able to dump memory from a Java 6 VM : you’ll get this stacktrace :

    Attaching to process ID 704, please wait...
    Exception in thread "main" java.lang.RuntimeException: gHotSpotVMTypes was not initialized properly in the remote process; can not continue
    at sun.jvm.hotspot.HotSpotTypeDataBase.readVMTypes(HotSpotTypeDataBase.java:111)
    at sun.jvm.hotspot.HotSpotTypeDataBase.(HotSpotTypeDataBase.java:68)
    at sun.jvm.hotspot.MacOSXTypeDataBase.(MacOSXTypeDataBase.java:35)
    at sun.jvm.hotspot.bugspot.BugSpotAgent.setupVM(BugSpotAgent.java:560)
    at sun.jvm.hotspot.bugspot.BugSpotAgent.go(BugSpotAgent.java:481)
    at sun.jvm.hotspot.bugspot.BugSpotAgent.attach(BugSpotAgent.java:319)
    at sun.jvm.hotspot.tools.Tool.start(Tool.java:146)
    at sun.jvm.hotspot.tools.JMap.main(JMap.java:128)

  • jmap needs high system privileges. If you can’t attach to a java process with the following error :

    Attaching to process ID 704, please wait...
    attach: task_for_pid(704) failed (5)
    Error attaching to process: Error attaching to process, or no such process

    …, try to use sudo : sudo jmap <pid>. (See Ken Sipe's blog for additional information)