Spring REST webservice serializing to multiple JSON formats - spring

I have a Spring REST web service which populates a generic object based on data we have in a database, the goal is to have the users pass a parameter to the web service to to indicate the format they want the output to be in. Based on their input we will use the correct JSONSerializer to give them what they want.
I have set up my webservice as follows, in my spring-ws-servlet.xml I have set our company ObjectMapper to be used by the mvc:message-converters, I have also set it on the RestController so that it can adjust the ObjectMapper to register the serializer. It looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="endpoint" class="org.company.Controller">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.company.CompanyObjectMapper" />
</beans>
The controller looks like this:
#RestController
public class Controller {
private ObjectMapper objectMapper;
#RequestMapping(...)
public GenericObject getObject(#PathVariables ...) {
//Get Object from database, just creating an object for example
GenericObject object = new GenericObject();
//Based on the user input we will pick out
//a Serializer that extends JsonSerializer<GenericObject>
BaseSerializer serializer = getSerializer();
//Create a simpleModule and use it to register our serializer
SimpleModule module = new SimpleModule();
module.addSerializer(GenericObject.class, serializer);
//get module and register the serializer
ObjectMapper mapper = getObjectMapper();
mapper.registerModule(module);
return object;
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
The issue is that when I publish my webapp, the first query works correctly, if I specify format=format1, I will get the output in format1. However, after that I can only receive format1. I may specify format=format2, but still get the output in format1. I believe the issue is that the ObjectMapper still has the module registered to it from the first query. I have read that I can avoid this problem by creating a new ObjectMapper every time, but I am not sure how to set that to be used by Spring when it outputs the JSON.
Could someone help me come up with a solution to either create a new ObjectMapper every time I run the code and set that ObjectMapper to the Spring rest service, or help me figure out how I can "unregister" any modules that are registered on the object mapper before setting the latest desired serializer?

An idea could be to create and configure all the mappers you need at startup time as a spring beans.
Then create the default object mapper that will work as a dispatcher for other object mappers (or as the fallback one), and it may be aware of the current http request.
You can register all the mappers in this object mapper, register this mapper to be used as the default one in spring.
Something like this maybe :
public class RequestAwareObjectMapper extends ObjectMapper{
private Map<String, ObjectMapper > mappers = new HashMap<>();
#Override
public String writeValueAsString(Object value) throws JsonProcessingException{
HttpServletRequest req = null;//get request from spring context, if any, this is a managed spring bean it wont be a prorblem
String param = null; // read the param from the query
ObjectMapper mapper = mappers.get(param);
if(mapper == null){
mapper = this;
}
return mapper.writeValueAsString(value);
}
public void registerMapper(String key, ObjectMapper mapper){...}
}
in this way you are not going to pollute your controller with references to the object mapper and you can carry on using #ResponseBody (thanks to #RestController)..
I am sure there's a cleaner way to achieve the same result integrating a similar solution in the spring flow, can't look on something better right now.

Create your customObjectMapper class and auto wire it to your controller using #Autowire annotation. You can then create different methods to create different formatted objects.
You can also send serialiser as parameters.
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
super.setSerializationInclusion(JsonInclude.Include.ALWAYS);
super.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
..... etc.....
super.setDateFormat(df);
}
public byte[] generateJsonFormat1(Object value, BaseSerializer serializer) throws IOException, JsonGenerationException, JsonMappingException {
Hibernate4Module hm = new Hibernate4Module();
hm.configure(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION, false);
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
.....
.....
hm.addSerializer(Object.class, serializer);
return super.registerModule(hm).writeValueAsBytes(value);
}
public byte[] generateJsonFormat2(Object value, BaseSerializer serialiser) throws IOException, JsonGe nerationException, JsonMappingException {
SimpleModule sm = new SimpleModule();
sm.addSerializer(Object.class, serialiser);
return super.registerModule(hm).writeValueAsBytes(value);
}
}
Above code is a snippet from my own application. I hope it gives the idea.

Related

Spring Batch CompositeItemProcessor get value from other delegates

I have a compositeItemProcessor as below
<bean id="compositeItemProcessor" class="org.springframework.batch.item.support.CompositeItemProcessor">
<property name="delegates">
<list>
<bean class="com.example.itemProcessor1"/>
<bean class="com.example.itemProcessor2"/>
<bean class="com.example.itemProcessor3"/>
<bean class="com.example.itemProcessor4"/>
</list>
</property>
</bean>
The issue i have is that within itemProcessor4 i require values from both itemProcessor1 and itemProcessor3.
I have looked at using the Step Execution Context but this does not work as this is within one step. I have also looked at using #AfterProcess within ItemProcessor1 but this does not work as it isn't called until after ItemProcessor4.
What is the correct way to share data between delegates in a compositeItemProcessor?
Is a solution of using util:map that is updated in itemProcessor1 and read in itemProcessor4 under the circumstances that the commit-interval is set to 1?
Using the step execution context won't work as it is persisted at chunk boundary, so it can't be shared between processors within the same chunk.
AfterProcess is called after the registered item processor, which is the composite processor in your case (so after ItemProcessor4). This won't work neither.
The only option left is to use some data holder object that you share between item processors.
Hope this helps.
This page seems to state that there are two types of ExecutionContexts, one at step-level, one at job-level.
https://docs.spring.io/spring-batch/trunk/reference/html/patterns.html#passingDataToFutureSteps
You should be able to get the job context and set keys on that, from the step context
I had a similar requirement in my application too. I went with creating a data transfer object ItemProcessorDto which will be shared by all the ItemProcessors. You can store data in this DTO object in first processor and all the remaining processors will get the information out of this DTO object. In addition to that any ItemProcessor could update or retrieve the data out of the DTO.
Below is a code snippet:
#Bean
public ItemProcessor1<ItemProcessorDto> itemProcessor1() {
log.info("Generating ItemProcessor1");
return new ItemProcessor1();
}
#Bean
public ItemProcessor2<ItemProcessorDto> itemProcessor2() {
log.info("Generating ItemProcessor2");
return new ItemProcessor2();
}
#Bean
public ItemProcessor3<ItemProcessorDto> itemProcessor3() {
log.info("Generating ItemProcessor3");
return new ItemProcessor3();
}
#Bean
public ItemProcessor4<ItemProcessorDto> itemProcessor4() {
log.info("Generating ItemProcessor4");
return new ItemProcessor4();
}
#Bean
#StepScope
public CompositeItemProcessor<ItemProcessorDto> compositeItemProcessor() {
log.info("Generating CompositeItemProcessor");
CompositeItemProcessor<ItemProcessorDto> compositeItemProcessor = new CompositeItemProcessor<>();
compositeItemProcessor.setDelegates(Arrays.asList(itemProcessor1(), itemProcessor2(), itemProcessor3), itemProcessor4()));
return compositeItemProcessor;
}
#Data
public class ItemProcessorDto {
private List<String> sharedData_1;
private Map<String, String> sharedData_2;
}

Spring REST - binding GET parameters to nested objects

I know you can bind get request parameters to a pojo like:
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)
return service.loadReservations(criteria);
}
Using a pojo like:
public class ReservationCriteria {
String hotelName;
DateRange reservationDateRange;
//getters-setters omitted
}
With a request: /reservation?hotelName=myHotel
myHotel will be bound to hotelName in ReservationCriteria object.
But how can I bind parameters to the nested object DateRange? Which defined like:
public class DateRange {
Date from;
Date to;
//getters-setters omitted
}
Is there a URL pattern which allows that kind of binding something like:
/reservation?hotelName=myHotel&reservationDateRange={fromDate=14.04.2016,toDate=15.04.2016}
Or do I have to declare seperate request parameters and bind them manually?
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(
ReservationCriteria criteria,
#RequestParam Date from,
#RequestParam Date to)
DateRange range = new DateRange();
range.setFrom(from);
range.setTo(to);
criteria.setDateRange(range);
return service.loadReservations(criteria);
}
I would prefer not to modify ReservationCriteria class because it is used in many other projects, which would cause alot of refactoring to be made.
Since at least Spring 4 you can pass in nested objects separated with "." in the url.
In the OP case it would be for query parameters:
?reservationDateRange.from=2019-04-01&reservationDateRange.to=2019-04-03
This assumes that Date can be parsed from the given string. This may not work to an arbitrary level of nesting but I've tested it works with one additional nested object.
When you pass a POJO as container of data, Spring use the name of the properties for build the query string and with the data that you pass build the pojo through an appropriated converter. This works for planar pojo or in other words without nesting, for this purpose you have provide the your converter. for this reason you cold have a think like below:
public class ReservationCriteria {
String hotelName;
Date from;
Date to;
//getters-setters omitted
}
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)
return service.loadReservations(criteria);
}
/reservation?hotelName=value&from=val&to=val
in this way you can benefit of standard converter of SpringMVC.
the your attempt to use a sort of json for codificate the inner object didn't work because Spring by default in query string don't understand this presentation you have provide a converter for this purpose.
Update for answer to Ben's comment:
If you want implement a custom Converter you had implements the org.springframework.core.convert.converter.Converter<S, T> and then register the your new Converter on the Spring Conversion Service.
On xml configuration you can use FormattingConversionServiceFactoryBean and register it on mvc namespace like below:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<mvc:annotation-driven conversion-service="conversionService"/>
<context:component-scan base-package="com.springapp.mvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<util:list>
<bean class="com.springapp.mvc.DateRangeToStringConverter"/>
<bean class="com.springapp.mvc.StringToDateRangeConverter"/>
</util:list>
</property>
</bean>
</beans>
on java config you can extends WebMvcConfigurerAdapter and add you bena like below:
#Configuration
#EnableWebMvc
public class YourWebConfigurationClass extends WebMvcConfigurerAdapter{
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addConverter(yourConverter());
}
...
}
the your converter can be like below:
public class DateRangeToStringConverter implements Converter<DateRange,String> {
#Override
public String convert(DateRange dateRange) {
return Json.createObjectBuilder().add("fromDate",DateFormatData.DATE_FORMAT.format(dateRange.getFrom()))
.add("toDate", DateFormatData.DATE_FORMAT.format(dateRange.getTo()))
.build()
.toString();
}
}
public class StringToDateRangeConverter implements Converter<String,DateRange> {
#Override
public DateRange convert(String dateRange) {
DateRange range = new DateRange();
JsonObject jsonObject = Json.createReader(new StringReader(dateRange)).readObject();
try {
range.setFrom(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("fromDate")));
} catch (ParseException e) {
e.printStackTrace();
}
try {
range.setTo(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("toDate")));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(range);
return range;
}
}
in this way you can listgening on the url: http://localhost:8080/reservation?hotelName=myHotel&reservationDateRange={"fromDate":"14.04.2016","toDate":"15.04.2016"}
pleas pay attenction on reservation DateRange field because I encoded it like a json.
I hope that it can help you

Mixing <mvc:annotation-driven conversion-service with #Autowired dependency coming back as null

I have a spring mvc 3.0 web application and am using a conversion-service as follows...
<bean id="applicationConversionService"
class="com.myer.reporting.converter.ApplicationConversionServiceFactoryBean"/>
<context:component-scan base-package="com.myer.reporting.controller" />
<context:component-scan base-package="com.myer.reporting.manager" />
<context:component-scan base-package="com.myer.reporting.dao"/>
<context:component-scan base-package="com.myer.reporting.dao.mapper"/>
<mvc:annotation-driven conversion-service="applicationConversionService"/>
In my controllers when i use the conversion service it all works fine.
#Autowired
private ApplicationConversionServiceFactoryBean applicationConversionService; '
But for the first time I am now trying to use the applicationConversionService in my mapper using the same method but the applicationConversionService is always coming back as null.
package com.myer.reporting.dao.mapper;
public class ImportHistoryRowMapper implements RowMapper<ImportHistory> {
private static final String BUSINESS_DATE = "businessdate";
private static final String TOTAL_TRAN_COUNT = "total_tran_count";
#Autowired
private ApplicationConversionServiceFactoryBean applicationConversionService;
public ImportHistory mapRow(ResultSet rs, int rowNum) throws SQLException {
Date businessDate = rs.getDate(BUSINESS_DATE);
Integer totalTranCount = rs.getInt(TOTAL_TRAN_COUNT);
ImportHistory importHistory = new ImportHistory();
importHistory.setBusinessDate(businessDate);
importHistory.setTransactionCount(totalTranCount);
// status
TrafficLightStatus status =
applicationConversionService.
getImportThresholdToStatusConverter()
.convert(totalTranCount);
importHistory.setStatus(status);
return importHistory;
}
}
I tried to add #Component to the top of the class but that doesn't seem to work either. I think I am lacking some understanding of the #Autowired annotation and therefore can't get this to work.
Can someone give me some advice on this please.
thanks
You need to make the ImportHistoryRowMapper spring managed for this to work.
I believe your dao class making the reference to this mapper class is singleton. For every request (i.e., call to method getJdbcTemplate().query ( sql, object array, new instance of ImportHistoryRowMapper);), you need to create new instance of ImportHistoryRowMapper i.e., it should be prototype.
You can inject prototype bean into singleton in 2 ways:
1) Lookup Method injection
2) Scoped proxies
Take a look at this.

Spring Data Solr multiple cores and repository

I have apache solr with multiple cores e.g. currency, country etc... So using Spring Data Solr I can retrieve information from one core. I have got this XML configuration right now queries against 'currency' core. If I wanted to query against 'country' core how can I set this up?
<!-- Enable Solr repositories and configure repository base package -->
<solr:repositories base-package="com.acme.repository" solr-template-ref="solrCurrencyTemplate"/>
<solr:solr-server id="solrCurrencyServer" url="http://localhost:8983/solr/currency"/>
<bean id="solrCurrencyTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrCurrencyServer" />
</bean>
and have the repository defined as
#Repository
public interface CurrencyRepository extends SolrCrudRepository<Currency, String> {
}
and from my service I can do this
#Override
public List<Currency> getCurrencies() {
Page<Currency> currencies = (Page<Currency>) currencyRepository.findAll();
return currencies.getContent();
}
I have also tried using #SolrDocument(solrCoreName = "currency") but this din't work.
#SolrDocument(solrCoreName = "currency")
public class Currency {
public static final String FIELD_CURRENCY_NAME = "currency_name";
public static final String FIELD_CURRENCY_CODE = "currency_code";
public static final String FIELD_DECIMALS = "decimals";
#Id
#Field(value = FIELD_CURRENCY_CODE)
private String currencyCode;
//currency_name,decimals
#Field(value = FIELD_CURRENCY_NAME)
private String currencyName;
#Field(value = FIELD_DECIMALS)
private String decimals;
...
...
...
}
I need help on this asap... otherwise I will have to go back to the RestTemplate Solution :-(
Hope someone can help.
Thanks
GM
Thought I would share, We spend lot of time recently configuring multiple cores. We did in java, not xml.
As part of spring #configuration add following.
#Bean(name="solrCore1Template")
public SolrTemplate solrCore1Template() throws Exception {
EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(getCoreContainer(), "core1");
return new SolrTemplate(embeddedSolrServer);
}
#Bean(name="solrCore2Template")
public SolrTemplate solrCore2Template() throws Exception {
EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(getCoreContainer(), "core2");
return new SolrTemplate(embeddedSolrServer);
}
#Bean
#Scope
public CoreContainer getCoreContainer() throws FileNotFoundException{
String dir = <path_to_solr_home>;
System.setProperty("solr.solr.home", dir);
CoreContainer.Initializer initializer = new CoreContainer.Initializer();
return initializer.initialize();
}
And to use each template use like below in service classes.
#Resource
private SolrTemplate solrCore1Template;
Embedded server can be relaced with HTTP using below code.
HttpSolrServer httpSolrServer = new HttpSolrServer(getSolrURL());
return new SolrTemplate(httpSolrServer, "core1");
Hope this helps. I know it's a very late reply for the question asked.
multicore support via namespace config is unfortunately an open issue. You'll need to have a separate SolrTemplate for each core and create repositories manually.
#Autowired
#Qualifier("solrCurrencyTemplate")
private SolrTemplate solrCurrencyTemplate;
#Autowired
#Qualifier("solrCountryTemplate")
private SolrTemplate solrCountryTemplate;
//...
CurrencyRepository currencyRepo = new SolrRepositoryFactory(this.solrCurrencyTemplate)
.getRepository(CurrencyRepository.class);
CountryRepository countryRepo = new SolrRepositoryFactory(this.solrCountryTemplate)
.getRepository(CountryRepository.class);
Spring Data now supports multiple cores with their respective repositories.
The multicoreSupport flag needs to be true in #EnableSolrRepositories annotation and the corresponding document needs to be told what core they belong to. Like:
#SolrDocument(solrCoreName = "currency")
public class Currency
{
// attributes
}
the other class should be
#SolrDocument(solrCoreName = "country")
public class Country
{
// attributes
}
The respective repositories should know what pojo they are working with.
public interface CurrencyRepository extends SolrCrudRepository<Currency,String>
{
}
and
public interface CountryRepository extends SolrCrudRepository<Country,String>
{
}
and configuration should be
#Configuration
#EnableSolrRepositories(value = "com.package.name",multicoreSupport = true)
public class SolrConfig
{
#Bean
public SolrServer solrServer() throws Exception
{
HttpSolrServerFactoryBean f = new HttpSolrServerFactoryBean();
f.setUrl("http://localhost:8983/solr");
f.afterPropertiesSet();
return f.getSolrServer();
}
#Bean
public SolrTemplate solrTemplate(SolrServer solrServer) throws Exception
{
return new SolrTemplate(solrServer());
}
}
With Spring Data Solr 1.1.0.RC1 multiple cores works as described by Christoph Strobl with #EnableSolrRepositories. It works also with an XML configuration by set multicore-support="true".
<solr:repositories base-package="your.solr.repo.package" repository-impl-postfix="Impl" multicore-support="true"/>
<solr:solr-server id="solrServer" url="${solr.server.base.connection.url}" />
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg index="0" ref="solrServer" />
</bean>
<solr:solr-server id="solrServer" timeout="1000" maxConnections="1000" url="${solr.server.1},${solr.server.2}"/>
<bean id="solrServerFactory" class="org.springframework.data.solr.server.support.MulticoreSolrServerFactory">
<constructor-arg ref="solrServer" />
<constructor-arg name="cores">
<list>
<value>${solr.index.customer}</value>
<value>${solr.index.task}</value>
</list>
</constructor-arg>
</bean>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrServerFactory" />
</bean>
<solr:repositories base-package="com.deve.pig.solr" multicore-support="true" solr-template-ref="solrTemplate" />

Internationalized drop downs using Spring 3

Story
I have a select control that represents user access level. I'm looking for a way to internationalize it. The label should be loaded from a message resource and the value should be used as is. I prepare all my drop down lists in controllers using a simple SelectOption class that has a label and a value properties. This way, my select's look consistent accross all jsp's.
Problem
I've found some examples but they are based on logic within jsp. Developer loops through his labels and manually constructs the option tag using a message resource. While this works, there just has to be a better way. I've also found some comments that Spring 3 will have support for internationalizing option labels but I can't find anything concrete on that.
Controller logic
Collection<SelectOption> optionList = new ArrayList<SelectOption>();
optionList.add(new SelectOption("-SELECT-", "-"));
optionList.add(new SelectOption("Administrator", "ADMIN"));
optionList.add(new SelectOption("Editor", "EDIT"));
bean.setFilterUserAccessLevelOptionList(optionList);
JSP logic
<form:select path="filterUserAccessLevel" items="${bean.filterUserAccessLevelOptionList}" itemLabel="label" itemValue="value"/>
Questions
I would like to add options in my controller in this way: optionList.add(new SelectOption("userAccessLevelAdministratorLabel", "ADMIN")); and have Spring convert userAccessLevelAdministratorLabel to a value from a message resource. Is this possible?
If Spring 3 cannot do this for me, how else can this be achieved without manually constructing the option tag within jsp?
=== 2012-01-15 ==============================================================
Still trying to work out a solution using aweigold's idea.
Controller
#Controller
public class UserController {
#Autowired
private UserService userService;
#Autowired
SelectOptionListBuilder listBuilder;
#RequestMapping("/userIndex/{pageNumber}")
public ModelAndView getUserList(#PathVariable Integer pageNumber, #ModelAttribute("userIndexBean") UserIndexBean phantomBean, Locale locale, Model model) {
UserIndexBean bean = new UserIndexBean();
// prepare filter form
Collection<SelectOption> optionList = listBuilder.getUserAccessLevelOptionList(true, SortOrder.NONE, locale);
bean.setFilterUserAccessLevelOptionList(optionList);
SelectOptionListBuilderImpl
#Component
public class SelectOptionListBuilderImpl implements SelectOptionListBuilder, MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#Override
public List<SelectOption> getUserAccessLevelOptionList(boolean addSelectPrompt, SortOrder sortOrder, Locale locale) {
List<SelectOption> optionList = new ArrayList<SelectOption>();
if(addSelectPrompt) {
optionList.add(new SelectOption(messageSource.getMessage("common.selectPromptLabel", null, locale), "-"));
}
messageSource mapping
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/i18n/messages" />
<property name="defaultEncoding" value="UTF-8"/>
<property name="UseCodeAsDefaultMessage" value="true"/>
</bean>
Exception
org.springframework.context.NoSuchMessageException: No message found under code 'common.selectPromptLabel' for locale 'en_CA'
When I need to do operations like this in a Controller outside of a jsp, I've been making my Controllers MessageSourceAware. Spring will then inject a new MessageSource when they are swapped, and you can interrogate it much like Spring does. In your example, you would do something like this:
#Controller
public class someController implements MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping
// Pass in the locale from the LocaleResolver
public void someMapping(Locale locale){
optionList.add(new SelectOption(
messageSource.getMessage("userAccessLevelAdministratorLabel", null, locale),
"ADMIN"))
}
}
Have a look at a spring roo project. They managed this kind of problem by creating tagx tags. This tags do what you already descibed (it contains a litte logic to load the messages from ressources and build the option tags). But because the logic is witten once and you can use this tags like normal tags in you jspx files, it feels like a tag that do what you want to have.

Resources