How is Spring's DataBinder used to convert a plain parameter value? - java

I've been digging through the Spring DataBinder code and docs while answering this question and i've noticed the DataBinder constructor docs mentionioning :
target - the target object to bind onto (or null if the binder is just
used to convert a plain parameter value)
I've been searching around and haven't found such a usage and it really made me curious. Would appreciate any insight related to :
How would such a data binder be used with a null target to convert a plain parameter value?
Or what does it actually mean to convert a plain parameter value in this context?
Is it also applicable to Spring MVC? (since i noticed it mentioned in WebDataBinder's constructor docs as well).

I have been done some digging in the Spring Framework source, searching for usages with null parameter as you described. Since I was not aware of this kind of usage either, looking at the testcases to understand things better was my way to go.
The test class for DataBinder is (not surprisingly) DataBinderTests.
I am going to paste a usage example here, along with the link to github where I found the code, for reference:
For your first question, the answer seems if you use a DataBinder with null constructor parameter, it means that you just want to use the conversion facility, without the data binding mechanism (since we have not passed an object to bind values to).
This pretty explanatory testcase shows this type of usage, creating a DataBinder with null, setting a DefaultFormattingConversionService to the dataBinder and registering a custom editor after that.
You can add a custom converter with the addConverter method if you want a different String representation after converting your bean.
#Test
public void testConversionWithInappropriateStringEditor() {
DataBinder dataBinder = new DataBinder(null);
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
dataBinder.setConversionService(conversionService);
dataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
NameBean bean = new NameBean("Fred");
assertEquals("ConversionService should have invoked toString()", "Fred", dataBinder.convertIfNecessary(bean, String.class));
conversionService.addConverter(new NameBeanConverter());
assertEquals("Type converter should have been used", "[Fred]", dataBinder.convertIfNecessary(bean, String.class));
}
For reference, here is the code of NameBeanConverter (also from the test class)
public static class NameBeanConverter implements Converter<NameBean, String> {
#Override
public String convert(NameBean source) {
return "[" + source.getName() + "]";
}
}
Source: https://github.com/spring-projects/spring-framework/blob/d5ee787e1e6653257720afe31ee3f8819cd4605c/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java#L598-L609
I think the above explanation was an answer for the first two questions:
How would such a data binder be used with a null target to convert a plain parameter value?
Or what does it actually mean to convert a plain parameter value in this context?
For your third question, unfortunately, I have not found any usable testcase like above, but going through the WebDataBinder's code make me think of there is no "added value" compared to DataBinder in terms of a null constructor parameter, so you can use the conversion facility through the WebDataBinder as well.
You probably know that you can bind your web request fields to your backing bean, adding conversion (e.g. converting a particularly formatted date to an actual Date field object on the backing bean) etc etc, but I don't want to describe this in more detail as this was not your point with the question I guess.
Lastly, I found this article very useful about how the WebDataBinder could be used: http://www.intertech.com/Blog/spring-frameworks-webdatabinder/

You can think of DataBinder as filter or handler that all request parameters go thorough before they get consumed. To make use of the DataBinder functionality you need to implement a method in your controller class with "#InitBinder" annotation. For example:
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
// bind empty strings as null
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
As you see, all you need to do in the method is register CustomEditor for a specific data type.

In Spring MVC, a DataBinder with a null target is automatically used in a case like this :
#RequestMapping(method = RequestMethod.GET)
public String showHome(#RequestParam("date") Date date) {
return "home";
}
In this case, the created DataBinder has an objectName "date" and a null target.

Related

Map a collection with parameter with mapstruct

To map a certain object with mapstruct I need some custom post processing which needs an additional parameter to do it's work:
#Mapper
public abstract class AlertConfigActionMapper {
#Mappings({ #Mapping(target = "label", ignore = true)})
public abstract AlertConfigActionTO map (AlertConfigAction action, Locale userLanguage);
#AfterMapping
public void setLabel (AlertConfigAction action, #MappingTarget AlertConfigActionTO to, Locale userLanguage) {
for (AlertConfigActionLabel label : action.getAlertConfigActionLabels()) {
if (label.getLanguage().equals(userLanguage)) {
to.setLabel(label.getLabel());
break;
} else if (label.getLanguage().equals(Locale.ENGLISH)) {
to.setLabel(label.getLabel());
}
}
}
}
This works just fine.
The problem starts when I add following method to this mapper:
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, Locale userLanguage);
I need to pass this parameter (userLanguage) as well but mapstruct seems to 'break down' in this case: I generates following code for this part (which naturally gives a compilation error):
#Override
public List<AlertConfigActionTO> mapList(List<AlertConfigAction> actions, Locale userLanguage) {
if ( actions == null && userLanguage == null ) {
return null;
}
List<AlertConfigActionTO> list = new List<AlertConfigActionTO>();
return list;
}
I'm sure it is related to the parameter since if I remove it (from all mapping methods) then the mapList method is generated correctly.
What is needed to be done to allow custom parameters in this case?
What you describe is not possible (yet). Could you open a feature request in our issue tracker? We should provide means of denoting parameters as some sort of "context" which is passed down the call stack.
As a work-around for the time being, you might take a look at using a ThreadLocal which you set before invoking the mapping routine and which you access in your after-mapping customization. It's not elegant - and you need to make sure to clean up the thread local to avoid memory leaks - but it should do the trick.
I know that this question is quiet old, but I run into this issue, and starting at version 1.2 of mapstruct you can resolve it using #Context
So declaring the mapping for the list need to be like this :
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, #Context Locale userLanguage);
Now, you juste need to add another non abstract mapping like this :
public AlertConfigActionTO mapConcrete (AlertConfigAction action, #Context Locale userLanguage){
return map (action, userLanguage);
}
I don't think it is possible. At least not that way. Problem is that you prepare interface/abstract class - and rest is done by the engine. And that engine expects methods with one parameter... There are decorators, but they go the same way. I would try to inject language. Create bean, mark it as session scoped, and find out. With Spring, you would use ScopedProxyMode for that... Not sure how that goes with CDI.
Other option is more workaround, then solution - maybe that AlertConfigAction can pass that information?

Can I control how spring controller method arguments are instantiated?

Consider the following interface/object hierarchy in a spring project:
public interface MyInterface {
//method defenitions
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class MyClass implements MyInterface {
//method implementations
}
I use MyClass in a controller method where it is read from the requests body:
#RequestMapping(method = POST, value = "/posturi", consumes = "application/json")
public void createEntity(#RequestBody MyClass myClass) {
//handle request
}
The jackson library is used to read json data and convert it to a java object.
I would like to change the type of the parameter in the controller method from MyClass to MyInterface. This does not seem to work since the interface can't be instantiated with the new operator. But it could be created like this:
MyInterface instance = applicationContext.getBean(MyInterface.class);
Is it possible to make spring/jackson instantiate the object this way? I would like to do this so that my controller does not need to be aware of what implementation is used.
It should be possible with Converters. See documentation http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html. Question is, how do you know which class you return by converter? Rather rethink your design to use POJOs in input.
I have solved this now and the concept is quite simple but the implementation can be a bit tricky. As I understand it, you can annotate any type with #RequestBody as long as you provide a HttpMessageConverter that can convert from a http request to your desired type.
So the solution is:
Implement a HttpMessageConverter
Configure spring so that your HttpMessageConverter is used.
The second part can be a bit tricky. This is because spring adds a bunch of default HttpMessageConverter that can handle common types such as strings, integers, dates and I want these to continue to function as usual. Another problem is that if jackson is on the path, spring also adds a MappingJackson2HttpMessageConverter for generic json handling such as converting to concrete objects, maps and so on. Spring will use the first HttpMessageConverter it finds that claims to be able to convert to your type. The MappingJackson2HttpMessageConverter claims to be able to do so for my objects, but it is not able to, so it fails and the request fails. This could be considered a bug...
The chain that I wanted was:
Springs default HttpMessageConverters.
My own HttpMessageConverter
The MappingJackson2HttpMessageConverter
I found two ways to acheive this. First, you can declare this explicitly through xml.
<mvc:annotation-driven>
<mvc:message-converters>
<!-- All converters in specific order here -->
</mvc:message-converters>
</mvc:annotation-driven>
The downside of this is that if the default HttpMessageConverter chain changes in later releases, it will not change for your configuration.
Another way to do it is to programatically insert your own HttpMessageConverter before the MappingJackson2HttpMessageConverter.
#Configuration
public class MyConfiguration {
#Autowired
private RequestMappingHandlerAdapter adapter;
#Autowired
private MyHttpMessageConverter myHttpMessageConverter;
#PostConstruct
private void modify() {
List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
int insertLocation = messageConverters.size() - 1;
for (int i = 0; i < messageConverters.size(); i++) {
Object messageConverter = messageConverters.get(i);
if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
insertLocation = i;
}
}
messageConverters.add(insertLocation, myHttpMessageConverter);
}
}
The second alternative will continue to use the "default configuration" even if it changes in later releases. I consider it a bit hacky and not at all elegant but the reason I think it is a valid soulution is that there seems to be flaws in the MappingJackson2HttpMessageConverter claiming to be able to convert to types it cannot convert to. And also that you cannot explicitly add a HttpMessageConverter to a specific position in the chain.
For now I am going with the second option but how you do is up to you...

Mockito Mocking any I18n instance created runtime

I have a scenario ,like i am creating i18n object instance dynamically in code. I am using the following code:
public String getLocaleString(Locale locale, SlingHttpServletRequest request){
final ResourceBundle bundle = request.getResourceBundle(locale);
I18n i18n = new I18n(bundle);
return i18n.get("local");
}
Here locale, request I have mocked. But i18n is created dynamically. so am not able to mock i18n.get("local").
I tried using:
when(any(I18n.class).get("local")).thenReturn("localizedString")
But I am not able to. I am getting NullPointerException in this line.
I would like to use Mockito for mocking this scenario. Can you guys help me please? Thanks.
Mockito has suggesting refactoring to get around this https://code.google.com/p/mockito/wiki/MockingObjectCreation?ts=1332544670&updated=MockingObjectCreation
I normally avoid testing internals of the method.
But in some cases when I really need I use PowerMockito https://code.google.com/p/powermock/wiki/MockConstructor
I don't think it's possible to use any() in that way, as it's an argument matcher, you should only specify a mock object with a method invocation, e.g.:
when(mock(i18n).get("local")).thenReturn("localizedString");
I think that might be the source of your NullPointerException.
But to solve your problem, I think you have two options:
The first is to use a factory to create the I18n object, and then mock the factory:
...
private I18nFactory factory;
...
public String getLocaleString(Locale locale, SlingHttpServletRequest request){
final ResourceBundle bundle = request.getResourceBundle(locale);
I18n i18n = factory.get(bundle);
return i18n.get("local");
}
Then in your test, set up the factory to produce the object you want:
// Mock I18n, locale, request etc...
final I18nFactory factory = mock(I18nFactory.class);
when(factory.get(bundle)).thenReturn(i81n);
// Assign 'factory' to your Controller(?)
controller.setI18nFactory(factory);
// act, assert etc...
The second approach is to set up the locale and request mocks/objects in such a way that new I18n(...) creates a valid object which meets your expectations.
On balance, I think I would prefer to use the second approach, especially so if I18n is a 3rd-party class. Although without more information on the aim of your test, this answer is somewhat speculative. Anyway, I hope this helps.

Conception problem on generic implementation

Context : Java using Guice 3.0
'Hi everybody, I'm trying to construct a decent converter library for my web application.
Converter allow me to convert from an unknown type to another type.
To convert type, I'm using a converter registry with Guice MultiBinder like this one (following #Ivan Sopov's implementation)
An example might be more explicit :
**NOTICE : this is not a working example**
//"obj" is an object with unknowed type, and we want to convert it to another type)
Converter c = converterRegistry.getConverter(obj.getClass(),type.getClass());
Object t = c.convert(obj); //obj is now converted !
I can have many kind of Converter : StringToInteger, StringToDouble, etc ; for those simple one I have no problem.
public interface Converter<F,T>{
public T convert(F from);
}
public class StringToInteger implements Converter<String,Integer>{
public Integer convert(String from){
return Integer.valueOf(from);
}
}
Problems come when I have to implement Converter that needs some more parameters (specific information to convert a type).
For example : DateToString converter
In this case I would have the current user Locale to format the Date into String correctly.
Currently I'm badly solving the problem using another parameter to my Converter interface like that :
public interface Converter<F,T>{
public T convert(F from, ConverterData data);
}
public class ConverterData{
private Locale locale;
//... many other object that can be usefull for other converter...
}
public class DateToString implements Converter<Date,String>{
public String convert(Date from, ConverterData data){
//using data.getLocale() to convert the Date to String and return it
}
}
Now, I'm searching a better solution to improve the code. I thought about some possible solution using Guice but I'm not totally convinced...
Here is one of them :
public class DateToString implements Converter<Date,String>{
private Provider<Locale> providerLocale;
#Inject
public DateToString(Provider<Locale> providerLocale){
this.providerLocale = providerLocale;
}
public String convert(Date from){
//using providerLocale.get() to convert the Date
}
}
Here, I'm using a Provider cause a converter could be in a #Singleton...
(maybe I'm wrong in using a Provider ?)
- How should I do this ?
Well, I tried to explain my problem at best. I hope someone will take time to read that :)
I'm waiting for your help, Thanks in advance !
PS : Hard to find a title for this post, any suggestion are welcome!
It looks like you want a Converter that is configured by the locale state of the current request.
You are on the right track with the Provider. The reason you give sounds off to me. I'll explain.
Because you will be using your converterRegistry to get the Converter it looks like you'll be using the same object for each request.
A provider is factory that uses guice to supply values. Because you want to configure the DataToString converter with per-request state this allows you to configure the locale for each call to the convert method on the reused object.
Next, you need to create a guice provider class or method of the Local. If you already know how to do that you are done.
To more fully integrate guice I suggest you use Guice-servlet to plug it into your requests: http://code.google.com/p/google-guice/wiki/Servlets

Receiving an array of Object from a web form using Spring framework

I have a form JSP web page, this form contains multiple instances of the same Java object,
I am using Java Spring Framework to control the "communication" with the controller and the view.
My problem is that I would like to be able to receive from the view a simple array containing the instances of my objects which are currently on the page (on which were probably modified).
When I want a specific kind of item, I usually just name it in my controller's method declaration, however for an array (or any Collection), this won't work.
so something like:
#RequestMapping
public String edit(...SomeObject[] objectName, ...){
}
would just return me an error, I can however receive an array of String, so this works:
#RequestMapping
public String edit(...String[] objectString, ...){
}
the goal would be to be able to make Spring automatically map the object.
thanks for your answers!
This is certainly possible; while I've not done it using #RequestMapping, I know that you can retrieve a collection it can be done with a "command" object (or #ModelAttribute)
Define a POJO with a collection attribute as your command
public class FooCommand {
private List<String> myCollection;
// Getter & Setter
}
Then access it in your controller
#RequestMapping(value = "/foo", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("fooCommand") FooCommand fooCmd) {
// do stuff with fooCmd.getMyCollection()
}
That make any sense?
Spring does not know how to create your custom object from a String so you will need to create your own PropertyEditor for your custom object.
Chapter 5 of the Spring Reference explains data binding and there is an example in Chapter 13 of how to register custom property editors in your controller.

Categories

Resources