spring


15
Sep 07

Restrictive locale resolver

If you make a web application that supports several languages with Spring you are definitely familiar with the handy locale resolvers Spring provides. Unfortunately, none of them allows to restrict the number of locales that can be set. We needed such restriction, because some of our database queries depended on the locale. So I wrote a wrapper locale resolver for it:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.LocaleResolver;
 
/**
 * A wrapper for LocaleResolver. Allows to set only one of the predefined set of locales.
 *
 * @author Ilya Boyandin
 */
public class RestrictiveLocaleResolver implements LocaleResolver {
 
	private final LocaleResolver localeResolver;
	private final Locale defaultLocale;
	private final List<Locale> supportedLocales;
 
	private RestrictiveLocaleResolver(
			LocaleResolver localeResolver, Locale defaultLocale,
			Collection<String> supportedLocales) {
 
		this.localeResolver = localeResolver;
		this.defaultLocale = defaultLocale;
 
		final List<Locale> locales = new ArrayList<Locale>();
		for (String loc : supportedLocales) {
			locales.add(new Locale(loc));
		}
		this.supportedLocales = locales;
	}
 
	public LocaleResolver getLocaleResolver() {
		return localeResolver;
	}
 
	public Locale getDefaultLocale() {
		return defaultLocale;
	}
 
	public Collection<Locale> getSupportedLocales() {
		return Collections.unmodifiableCollection(supportedLocales);
	}
 
	@Override
	public Locale resolveLocale(HttpServletRequest request) {
		final Locale resolved = localeResolver.resolveLocale(request);
		final Locale supported = findSupportedLocale(resolved);	 // this ensures that a locale with one
																 // of the predefined names will be used
		if (supported != null) {
			return supported;
		} else {
			return defaultLocale;
		}
	}
 
	@Override
	public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
		final Locale supported = findSupportedLocale(locale);	// this ensures that a locale with one
																// of the predefined names will be used
		if (supported != null) {
			localeResolver.setLocale(request, response, supported);
		} else {
			localeResolver.setLocale(request, response, defaultLocale);
		}
	}
 
	private Locale findSupportedLocale(Locale locale) {
		for (int i = 0, len = supportedLocales.size(); i < len; i++) {
			final Locale loc = supportedLocales.get(i);
			if (loc.equals(locale)) return loc;
		}
		return null;
	}
 
}

And then you must have something like this in your config file (of course, you can use any other locale resolver instead of SessionLocaleResolver here):

<bean id="localeResolver" class="org.fhj.joanna.web.utils.RestrictiveLocaleResolver">
	<constructor-arg><bean class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/></constructor-arg>
	<constructor-arg value="de"/>
	<constructor-arg><set><value>de</value><value>en</value></set></constructor-arg>
</bean>

3
Sep 07

SQL parameters and query optimization

Few days ago we found out that passing in SQL parameters to a PreparedStatement can affect query optimization when we ran into a problem querying the database from our web app. We constantly got the following error from SQL Server: Adding a value to a ‘datetime’ column caused overflow. In the WHERE clause of the query we had something like DATEADD(month, 2, exam_date) and it went wrong, because there was an invalid exam_date entry in the database table (something like 0095-01-10).

The funny thing is that when we tried to run the very same query in Microsoft Query Analyzer it worked without an error. Then I wrote a short test that queried the database directly using PreparedStatement without Spring and iBatis to localize the problem and found out that when I passed parameters to it the error came, when I put the parameter values into the query itself it didn’t. So it seems that passing the parameters affected the order in which the conditions in the WHERE clause were evaluated: without the parameters another condition was evaluated first so that the errorneous date entry was filtered out before coming to the condition which used DATEADD.