Spring custom converter gets overwritten - java

This is my first post, so please excuse me if I miss to follow any convention of posting a good question.
I am coding a RESTful service using Spring REST. The signature of the service is as below.
#RestController
#RequestMapping(value = "/{param1}/{param2}/myservice", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyService {
#RequestMapping(value = "/{param3}", method = GET)
public MyResponseObj getMyResponseDetails(MyRequestObject request){
//Service logic code
}
}
In above example, MyRequestObject contains a custom date object with a specific format. For the reference of this question, the name of the custom date object of MyDate in MyRequestObject is myDate.
My objective here is to convert the string value of myDate coming as a query parameter with name myDate. I have purposefully kept the query parameter name the same as of the inner object name within MyRequestObject class, so that Spring can implicitly assign the value to the myDate attribute of the MyRequestObject instance of the method argument. To make this auto assignment, I ensured to keep a constructor for MyDate with a String parameter. But, Spring does not do this auto-assignment for myDate value.
So, after reading several posts on this forum, I created a custom converter to convert a String object to MyDate object. Below is my code for the same.
#Component
public class StringToMyDateConverter implements Converter<String, MyDate> {
public MyDate convert(String s){
//MyDate.parseData(String) is a custom method to convert a String to MyDate object
return MyDate.parseData(s);
}
}
Then, I had added below configuration into my Spring config.xml file to add this custom converter class into Spring's default conversion service.
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.my.service.rest.util.conversion.StringToMyDateConverter"/>
</set>
</property>
</bean>
During application startup while debug, I have found that my custom converter code is getting executed and the Spring's default converter service registers my custom converter. However, I have also noticed that the Spring's internal code to initialize FormattingConversionServiceFactoryBean is getting executed multiple times during start up of the service. And that eventually resulted into overwriting of the converter service listing, which did not have my custom converter that got registered before.
So, when I invoke the REST service URL mentioned below, I get below mentioned error.
URL
http://localhost:7880/project/json/value1/value2/myservice/value3?myDate=2017-09-12
Exception
org.springframework.validation.BindException:
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'request' on field 'myDate': rejected value [2017-09-12]; codes [typeMismatch.request.myDate,typeMismatch.myDate,typeMismatch.com.my.service.xml.datatype.MyDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [request.myDate,myDate]; arguments []; default message [myDate]]; default message [Failed to convert property value of type [java.lang.String] to required type [com.my.service.xml.datatype.MyDate] for property 'myDate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.my.service.xml.datatype.MyDate] for property 'myDate': no matching editors or conversion strategy found]
It is important to mention here that my project has a complex module structure having multiple Spring configurations.
I think, because of that Spring is initializing the default conversion service multiple times. Though, I am not able to determine the order of configuration invocations so that I can prevent Spring overwriting my customer configuration added in the list. I have also tried the annotation based configuration class as listed below to achieve this result. But that also resulted in same issue.
#Configuration
public class ConversionServiceProvider {
#Autowired
private StringToMyDateConverter stringToMyDateConverter;
#Bean
public ConversionService getConversionService() {
ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
bean.setConverters(getConverters());
bean.afterPropertiesSet();
ConversionService object = bean.getObject();
return object;
}
private Set<Converter<?, ?>> getConverters() {
Set<Converter<?, ?>> converters = new HashSet<>();
converters.add(stringToMyDateConverter);
// add here more custom converters, either as spring bean references or directly instantiated
return converters;
}
}
Can you please suggest me anything that I can try here to solve this issue? Please excuse me for the lengthy question, but I think that would help to understand the situation and problem better.
Thanks!!

Related

Spring - Values from property file not loaded while consuming Web Service

I consume a web service in spring and get values. To do that I use GenericHandler Class to set headers for web service XML, to fill up credentials and other links in the XML I use property file and load them. However, I am not able to load values into the variables. Here are my code,
#Component
Class WSAuthentication extends GenericHandler
{
#Value("${webservice.consumerId}")
private String consumerIdString;
#Override
public QName[] getHeaders()
{
return null;
}
public boolean handleRequest(MessageContext context)
{
.... // SOAP Message context codes
System.out.println(consumerIdString);
// the above line Prints null
}
}
Web Service Handler class as follows:
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.HandlerRegistry;
import javax.xml.rpc.ServiceException;
public class WSHandler
{
public StubClass addHandlerToGetInfo()
{
....// Web Service stub codes
HandlerRegistry handlerRegistryObject = locatorStubObject.getHandlerRegistry();
List chain = handlerRegistryObject.getHandlerChain((QName)locatorStubObject.getPorts().next());
HandlerInfo handlerInfoObject = new HandlerInfo();
handlerInfoObject.setHandlerClass(WSAuthentication.class);
chain.add(handlerInfoObject);
return stubObject;
}
}
In another class I use this web service to get the code, I invoke this code in a method annotated as #PostProcess of another bean,
....// Consuming code goes here
WSHandler wsHandlerObj = new WSHandler();
StubClass stubObj = wsHandlerObj.addHandlerToGetInfo();
// Invoking WS Stub methods to get values
WSResponseClass responseObj = stubObj.getProfile(id);
Here I am not able to get consumerIdString object from properties, on the other hand I am able to hard code the value in the WSAuthentication class and it goes good when I try executing that way. Loading from property file gives a null object when I tried to access that member variable.
My questions:
Will an instance of class WSAuthentication be created by
HandlerInfo? Or How does it access the WSAuthentication class?
Does the HandlerInfo gets the web service header
through Class instance of WSAuthentication?
Is there any other way
to do this?
Or shall I use reflection to initialize the member variables of the class?
Please help me out, Thank you!
According to Spring Reference Manual, appendix E on XML Schema-based configuration,
<util:properties id="env" location="classpath:application.properties">
is a shortcut for :
<!-- creates a java.util.Properties instance with values loaded from supplied location -->
<bean id="env" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:application.properties"/>
</bean>
That means that those properties should be accessed via their containing bean.
I think that what you want to achieve is using a PropertyPlaceholderConfigurer, which should be declared as :
<context:property-placeholder location="classpath:/application.properties"/>
shortcut for :
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:/application.properties"/>
</bean>
Because here properties are directly available to configure other beans.
You are setting WSAuthentication as Handler as follows:
handlerInfoObject.setHandlerClass(WSAuthentication.class);
I think it is taking it as a plain bean instead of getting from Spring ApplicationContext, and hence no value is set for #Value annotated property.
Just to confirm inject any other Spring bean and see its value. If it is null means the instance for WSAuthentication is being created using Reflection instead of using Spring.

which context.getBean to use when get the bean from spring

In the following link
http://docs.spring.io/spring-amqp/reference/html/quick-tour.html
It defined a bean in XML like
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>
Then in the calling code, it used
AmqpTemplate template = context.getBean(AmqpTemplate.class);
Why it used
context.getBean(AmqpTemplate.class);
instead of
context.getBean("amqpTemplate");
What's the getBean(AmqpTemplate.class) means? I can't find it defined by xml.
getBean()
is an overloaded method. You can call with the bean name or a bean type. Calling with the class returns the single instance of this class type. If there are more than one throws an exception. If there are none again throws an exception.
See here.
If you used context.getBean("amqpTemplate") you would need to cast the result to AmqpTemplate while getBean(AmqpTemplate.class) does it automatically. Note that getBean(AmqpTemplate.class) can only work if you have only one bean with AmqpTemplate class in the context

jackson spring controller serialization

I have a Java Spring 3.2 + Hibernate project.
I used jackson2 annotations (com.fasterxml.jackson.annotation) in the model, and I (guess) the spring rest controller should use jackson2 (aka com.fasterxml.jackson) when serializing the requested objects.
I configured the application with:
<!-- Use the HibernateAware mapper instead of the default -->
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="myProj.util.HibernateAwareObjectMapper">
<property name="serializationInclusion">
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
The HibernateAwareObjectMapper is defined this way:
package myProj.util;
import com.fasterxml.jackson.databind.ObjectMapper;
public class HibernateAwareObjectMapper extends ObjectMapper {
private static final long serialVersionUID = -5002954669679467811L;
public HibernateAwareObjectMapper() {
Hibernate4Module hbm = new Hibernate4Module();
hbm.enable(Hibernate4Module.Feature.FORCE_LAZY_LOADING);
registerModule(hbm);
}
}
so I can state that it extends the com.fasterxml ObjectMapper (OTOH I'm not sure why it was added, since I just inherited the code from other developers).
Note that from what I know spring3.2 should use jackson2 by default.
This is mostly working fine but then I have a serialization issue which only happens with a specific service/controller. I have an object which defines a parent, containing the same object as a child. This is resulting in a serialization loop, which ends with an exception on the server side:
[...]
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
[...]
and an incomplete JSON sent to the client.
This is the controller code:
#RequestMapping(value = "/getReleases", method = RequestMethod.POST)
public Map<String, Object> getReleases(#RequestBody Project project) {
Map<String, Object> subProjectsMap = new HashMap<String, Object>();
List<Release> releaseList = null;
try {
releaseList = jiraService.getReleases(project);
subProjectsMap.put("success", (releaseList.size() > 0) ? true : false);
subProjectsMap.put("data", releaseList);
} catch (Exception e) {
e.printStackTrace();
}
return subProjectsMap;
}
Serialization is performed implicitly by the framework.
The question is: why is spring apparently using org.codehaus.jackson rather than com.fasterxml.jackson as I would expect? Note that the model describing the serialized object is using jackson2 annotations (in particular #JsonBackReference and #JsonIgnore), so they are possibly ignored when using jackson1, which (I think) may result in the loop issue.
After many hours spent banging my head I still don't know why this is happening. Can you provide any hint?
Well, it turned out the problem was due to the missing #ResponseBody annotation in the controller code.
#RequestMapping(value = "/getReleases", method = RequestMethod.POST)
public #ResponseBody Map<String, Object> getReleases(#RequestBody Project project) {
[...]
}
Adding the #ResponseBody annotation magically fixed the problem for me.
I realized the problem by comparing this controller code with the code from similar controllers which were not showing the issue (silly of me not doing this before), after several vain attempts at digging through the Spring code.
Thanks all for the answers!
Spring automatically configures MappingJackson2HttpMessageConverter when Jackson2 is on the classpath. However, you override this behavior by defining your own bean of type MappingJackson2HttpMessageConverter. First of all, check if it is your bean being used for serialization (e.g. via debugging). Then check its configuration. It seems the constructor of the Hibernate extension to ObjectMapper is not calling super(), which means that configuration of the default SerializerProvider and BeanSerializerFactory is missing. See http://fasterxml.github.io/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/ObjectMapper.html for more details.

Bind UUID in Spring MVC

What is the easiest way to bind a UUID in Spring MVC, such that this works:
#RequestMapping("/MyController.myAction.mvc")
#ResponseBody
public String myAction(UUID id, String myParam)...
Using the above I currentely get the following exception:
org.springframework.beans.BeanInstantiationException:
Could not instantiate bean class [java.util.UUID]:
No default constructor found;
nested exception is java.lang.NoSuchMethodException: java.util.UUID.<init>()
There are other questions on SO that skirt around this, but none seem to answer it. I'm using Spring 3.latest (4 EA actually). I'm after the latest, simplest way to achieve this.
UUID is a class that cannot simply be instantiated. Assuming that it comes as a request parameter you should first annotate the argument with #RequestParam.
#RequestMapping("/MyController.myAction.mvc")
#ResponseBody
public String myAction(#RequestParam UUID id, String myParam)...
Now this expects a request parameter with the name id to be available in the request. The parameter will be converted to a UUID by the StringToUUIDConverter which is automatically registered by Spring.
Prior to the Spring 3.2
there was no StringToUUIDConverter so additionally you have to write and register converter by your own.
public class StringToUUIDConverter implements Converter<String, UUID> {
public UUID convert(String source) {
return UUID.fromString(source);
}
}
Hook this class up to the ConversionService and you should have UUID conversion for request parameters. (This would also work if it was a request header, basically for everything that taps into the ConversionService). You also might want to have a Converter for the other-way (UUID -> String).
Hooking it up to Spring MVC is nicely explained in the reference guide (assuming you use xml config). But in short:
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="org.company.converter.StringToUUIDConverter"/>
</set>
</property>
</bean>
The converter below is available in Spring Framework (core) since version 3.2.
org.springframework.core.convert.support.StringToUUIDConverter<String, java.util.UUID>
If its coming as a Header parameter use
#RequestHeader(value="UUID") String id
If its coming in a model
#ModelAttribute(value="ModelName") Entity modelName

ways to inject a object of a class in spring controller?

I need to inject a object of a java class in spring controller through applicaionContext.xml. My controller will be ,
#Controller
public class SpringController{
private MyClass obj;
}
I know I can do it with #Autowired annotation.
Is this really good to create a object for a controller through applicaionContext.xml ? Also can I inject a object of a class in controller using the <property> tag inside a <bean> tag ?
Is this really possible ? or please forgive me if it is a stupid question.
I need to know the possible ways for how to inject a object of a class in Spring controller ?
You can of course use #Autowired annotation to autowire the relationships, which can reduce the need to define the properties and constructor arguments for the controller in your applicationContext.xml file. And also to add a dependency to a class, you don't need to modify the configuration files.
But it has some disadvantages too, like if you use #Autowired, there will not be any explicit documentation for the wiring details between Spring managed beans. And to know the relationships between the beans, you have to go through your managed beans. But, if you use configuration files to define the relationships, the relationship details can be found in one place.
You can inject an object of a class into your controller through your applicaionContext.xml as below:
Constructor based injection:
#Controller
public class SpringController{
private MyClass obj;
public SpringController(MyClass obj){
this.obj=obj;
}
}
<bean id="myClassImpl" class="x.y.z.MyClassImpl"></bean>
<bean id="springController" class="x.y.z.web.controllers.SpringController">
<constructor-arg ref="myClassImpl"></constructor-arg>
</bean>
Setter based injection:
#Controller
public class SpringController{
private MyClass obj;
public void setObj(MyClass obj){
this.obj=obj;
}
public MyClass getObj(){
return obj;
}
}
<bean id="myClassImpl" class="x.y.z.MyClassImpl"></bean>
<bean id="springController" class="x.y.z.web.controllers.SpringController">
<property name="obj" ref="myClassImpl"></property>
</bean>
If you want to inject an object in a controller and you particularly want to you use xml,then instead of component scanning of Controller you should create a bean of the controller class of singleton scope in the application context.
Your controller class need not be annotated with #Controller.
you then have to you extend some Controller also like AbstractCommandController, AbstractController, AbstractFormController, AbstractWizardFormController, BaseCommandController, CancellableFormController, MultiActionController SimpleFormController, UrlFilenameViewController
Now to inject a particular object you can use Either Constructor and Setter based injection.
or you can use Autowring by name or type to auto inject the object.
Make sure that you have also declared the bean of that object also in Application Context.
After a DispatcherServlet has received a request and has done its work to resolve locales, themes and suchlike, it then tries to resolve a Controller, using a HandlerMapping. When a Controller has been found to handle the request, the handleRequest method of the located Controller will be invoked; the located Controller is then responsible for handling the actual request and - if applicable - returning an appropriate ModelAndView.
Thats it.
Actually, injection with xml and annotation is same behind the scene. Xml is old fashion while annotations are newer.
Basically, there are 2 types of injection types.
byName
Autowiring by property name. Spring container looks at the properties
of the beans on which autowire attribute is set to byName in the XML
configuration file. It then tries to match and wire its properties
with the beans defined by the same names in the configuration file.
You can give explicit names to beans both with xml and annotation.
#Service("BeanName")
#Component("BeanName")
#Controller("BeanName")
<bean name="BeanName" class="someclass"></bean>
and inject beans by using #Qualifier annotation.
#Autowired
#Qualifier("BeanName")
and with xml
<bean id="MyBean2" class="MyBean2 class">
<property name="Property of MyBean2 which refers to injecting bean" ref="BeanName" />
</bean>
byType
Autowiring by property datatype. Spring container looks at the
properties of the beans on which autowire attribute is set to byType
in the XML configuration file. It then tries to match and wire a
property if its type matches with exactly one of the beans name in
configuration file. If more than one such beans exists, a fatal
exception is thrown.
Default auto wiring mode is byType, so spring will look for matching type in auto wiring. However, older versions of Spring has default behavior none on injection. If you want to inject byType using xml, you should tell spring contaioner explicitly.
For example MyBean2 has a reference to MyBean, by setting autowired attribute to byType it handles injection automatically.
<bean id="MyBean" class="MyBean class">
<property name="Property of MyBean2 which refers to injecting bean" ref="BeanName" />
</bean>
<bean id="MyBean2" class="MyBean2 class"
autowire="byType">
</bean>
It also depends on where the injection take place in your code. There are 2 types, setter getter injection and constructor injection.
Note : There is no difference in #Controller since they are already in spring context.
See also
Spring Beans Auto wiring
I ran into such problem. I was getting "Ambiguous mapping found". (I use xml configuration as well and i am injecting a bean into my controller)
Then looking at my console i realized that my controller was being instantiated twice.
In more detailed look i noticed that my annotation
#Controller(value = "aController")
(Note value = "aController")
was different from my xml configuration where i was instatiating the same controller with different bean id
<bean id="aControleRRRRR" class="package.ControllerClassName"
p:property-ref="beanToInject" />
(Note id="aControleRRRRR")
So in conclusion your #Controller name (value = "aController") needs to be exactly the same as the name you give in the XML configuration (id="aControleRRRRR"), so that Spring can manage to distinct that they refer to the same bean (instance)
Hope this helps

Categories