I am trying to migrate from Struts2 2.3.36 to Struts2 6.1.2.1 for a legacy multi-module web application.
In every module's init file we register the locale message properties file like so -
//inside init class of XYZ module
static {
LocalizedTextUtil.addDefaultResourceBundle("XYZMessageResources");
}
However, after upgrading the struts2 dependency, I found out that this class has been removed since Struts2 2.5.17 and after going through the source code of the Apache Struts2 I found that StrutsLocalizedTextProviderand GlobalLocalizedTextProvider provides similar methods. So I replaced the above line in all init classes like so -
static {
StrutsLocalizedTextProvider strutsLocalizedTextProvider = new StrutsLocalizedTextProvider();
strutsLocalizedTextProvider.addDefaultResourceBundle("XYZMessageResources");
}
Adding this fixed the compilation issues.
Also, we have a custom TextProviderSupport class, which implements com.opensymphony.xwork2.TextProvider class, and has some extra methods to convert ANSI to UTF8. This class looks something like this -
public class TextProviderSupport implements TextProvider {
private static final Log LOGGER = LogFactory.getLog(TextProviderSupport.class);
final private DefaultTextProvider mProvider =new DefaultTextProvider();
public String getText(final String key, final List args) {
final String msg = mProvider.getText(key, args);
return convertFromANSIToUTF8(key, null, msg, args);
}
//All other overloaded getText methods are overridden similarly which have some business logic
this TextProviderSupport class is being pushed to the invocation stack inside a custom implementation of the I18nInterceptor class :
public class I18nInterceptor implements Interceptor {
//intercept method
public String intercept(final ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(new I18nHandler());
// get requested locale
Object requested_locale = invocation.getInvocationContext().getLocale();
// save it in session
final Map session = invocation.getInvocationContext().getSession();
if (session != null) {
if (requested_locale != null) {
final Locale locale = (requested_locale instanceof Locale) ?
(Locale)requested_locale
:StrutsLocalizedTextProvider.localeFromString(requested_locale.toString(), null);
if (locale != null) {
// put the locale in the session
session.put(attributeName, locale);
// put the javaScript pattern in the session so that
// we can access it in Header.jsp
final String aPattern = getAndUpdateDatePattern(locale);
session.put(patternDate, aPattern);
}
}
// this piece of code checks that a pattern for date has been set
// if not, it sets one according to context locale
if (!session.containsKey(patternDate)) {
final String aPattern = getAndUpdateDatePattern(invocation
.getInvocationContext().getLocale());
session.put(patternDate, aPattern);
}
// set locale for action
final Object locale = session.get(attributeName);
if (locale != null && locale instanceof Locale) {
saveLocale(invocation, (Locale) locale);
}
}
return invocation.invoke();
}
protected void saveLocale(final ActionInvocation invocation, final Locale locale) {
//ActionSupport actionInstance = (ActionSupport) invocation.getAction();
//ActionContext.getContext().setLocale(locale);
invocation.getInvocationContext().withLocale(locale);
}
private static class I18nHandler implements PreResultListener {
public void beforeResult(final ActionInvocation invocation,
final String resultCode) {
// Push com text provider on top of the stack
invocation.getStack().push(new TextProviderSupport());
}
}
and this TextProviderSupport class is added in our own ActionSupport class which extends com.opensymphony.xwork2.ActionSupport class-
public class ActionSupport extends com.opensymphony.xwork2.ActionSupport {
private final transient TextProvider textProvider = new TextProviderSupport();
public TextProvider getTextProviderInstance() {
return textProvider;
}
However, upon debugging, I found that the protected LocalizedTextProvider localizedTextProvider
field is always null and when I try to deploy the application war, i get NPE in line 72 of the getText method in com.opensymphony.xwork2.DefaultTextProvider class-
public String getText(String key, List<?> args) {
Object[] params;
if (args != null) {
params = args.toArray();
} else {
params = EMPTY_ARGS;
}
//NPE on this line as localizedTextProvider is null
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale(), params);
}
the Stacktrace -
Caused by: java.lang.NullPointerException
at com.opensymphony.xwork2.DefaultTextProvider.getText(DefaultTextProvider.java:72)
at com.opensymphony.xwork2.DefaultTextProvider.getText(DefaultTextProvider.java:87)
at com.opensymphony.xwork2.DefaultTextProvider.getText(DefaultTextProvider.java:134)
at XXXXX.XXXX.XXXX.i18n.TextProviderSupport.getText(TextProviderSupport.java:73)
at org.apache.struts2.util.TextProviderHelper.getText(TextProviderHelper.java:55)
at org.apache.struts2.components.Text.end(Text.java:183)
at org.apache.struts2.views.jsp.ComponentTagSupport.doEndTag(ComponentTagSupport.java:40)
at org.apache.jsp.hac.redirectlogin_jsp._jspx_meth_s_005ftext_005f0(redirectlogin_jsp.java:228)
at org.apache.jsp.hac.redirectlogin_jsp._jspService(redirectlogin_jsp.java:112)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:433)
... 44 more
I came across a similar problem on Stackoverflow - Migrate from Struts 2.3.33 to Struts 2.5.12 LocalizedTextUtil is removed
and it suggested implementing a CustomStrutsTextProviderFactory class, but I am not able to understand how to use this class in my application and where I need to wire it.
I used this class ( look at inject
it is from com.opensymphony.xwork2.inject
):
@WebListener
public class CustomStrutsTextProviderFactory extends
StrutsTextProviderFactory implements ServletContextListener{
protected LocalizedTextProvider localizedTextProvider;
@Override
@Inject //this should be from com.opensymphony.xwork2.inject.Inject;
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
localizedTextProvider.addDefaultResourceBundle("messages/resources");
localizedTextProvider.addDefaultResourceBundle("messages/help");
}
@Override
protected TextProvider getTextProvider( Class clazz) {
return new CustomTextProvider(clazz, localeProviderFactory.createLocaleProvider(), localizedTextProvider);
}
@Override
protected TextProvider getTextProvider(ResourceBundle bundle) {
return new CustomTextProvider(bundle, localeProviderFactory.createLocaleProvider(), localizedTextProvider);
}
}
and in struts.xml
<bean type="com.opensymphony.xwork2.TextProviderFactory" name="customStrutsTextProviderFactory" class="foo.bar.CustomStrutsTextProviderFactory" scope="singleton"/>