I have a simple Spring MVC application that makes an Ajax call to a ReST service using the RestTemplate class. Some of the elements in the schema start with uppercase letters. The 2 elements (code, message) with lowercase letters cause no problems. The classes generated by JAXB have the #XmlElement annotation and name property. This seems to be ignored. I've read that the JaxbAnnotationIntrospector needs to be used but none of the changes make that take effect. See class below. I've tried adding a bean class definition for RestTemplate in the Spring config, adding an object mapper but nothing helps. See error on OTPRO element in first few lines of stack trace:
[4/18/16 9:52:43:988 EDT] 00000024 SystemErr R org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "OTPRO" (class com.ssdr.rest.message.SSDRResponse), not marked as ignorable (7 known properties: "dt", "ot", "message", "otpro", "otphone", "code", "dtphone"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#4ccbe679; line: 4, column: 14] (through reference chain: com.ssdr.rest.message.SSDRResponse["OTPRO"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "OTPRO" (class com.ssdr.rest.message.SSDRResponse), not marked as ignorable (7 known properties: "dt", "ot", "message", "otpro", "otphone", "code", "dtphone"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#4ccbe679; line: 4, column: 14] (through reference chain: com.ssdr.rest.message.SSDRResponse["OTPRO"])
[4/18/16 9:52:44:019 EDT] 00000024 SystemErr R at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readInternal(MappingJackson2HttpMessageConverter.java:126)
[4/18/16 9:52:44:019 EDT] 00000024 SystemErr R at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:153)
[4/18/16 9:52:44:019 EDT] 00000024 SystemErr R at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:81)
JAXB generated class:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for documentResponse complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType name="documentResponse">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="code" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="message" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="OTPRO" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="OT" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="OTPhone" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="DT" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="DTPhone" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "documentResponse", propOrder = {
"code",
"message",
"otpro",
"ot",
"otPhone",
"dt",
"dtPhone"
})
public class DocumentResponse {
#XmlElement(required = true)
protected String code;
#XmlElement(required = true)
protected String message;
#XmlElement(name = "OTPRO", nillable = true)
protected String otpro;
#XmlElement(name = "OT", nillable = true)
protected String ot;
#XmlElement(name = "OTPhone", nillable = true)
protected String otPhone;
#XmlElement(name = "DT", nillable = true)
protected String dt;
#XmlElement(name = "DTPhone", nillable = true)
protected String dtPhone;
...
Service class:
SSDRResponse resp = null;
RestTemplate restTemplate = new RestTemplate();
HttpEntity<SSDRRequest> httpRequest = new HttpEntity<SSDRRequest>(req, createHeaders());
resp = restTemplate.postForObject(SERVICE_URI, httpRequest, SSDRResponse.class);
Spring config:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:annotation-config />
<!-- Scans the classpath of this application for #Components to deploy as beans -->
<context:component-scan base-package="com.ssdr" />
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven />
<!-- Forwards requests to the "/" resource to the "index" view -->
<!--
<mvc:view-controller path="/" view-name="index" />
-->
<!-- Make webapp/resources directory accessible to web app -->
<mvc:resources location="/resources/" mapping="/resources/**" />
<!-- Resolves view names to protected .jsp resources within the context root directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="annotationIntrospector">
<bean
class="com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector" />
</property>
</bean>
</property>
</bean>
</list>
</property>
</bean>
<!-- <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="prefixJson" value="true"/>
</bean>
</list>
</property>
</bean> -->
</beans>
NOTE: I do not want to simply ignore the upper case elements.
The solution was to use a combination of JacksonAnnotationIntrospector and JaxbAnnotationIntrospector. In this case Jackson was needed to recognize the #JsonIgnoreProperties and #JsonInclude annotations in the request. But Jackson always threw the "Unrecognized field" errors. JAXB could read the response with the uppercase fields but would not recognize the annotations on the request.
To use both introspectors the following code was added to the service class:
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter jaxMsgConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objMapper = new ObjectMapper();
/*
* Jackson introspector needed for #JsonIgnoreProperties and #JsonInclude annotations
* JAXB introspector is needed to handle the uppercase element names in the response
*/
AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);
objMapper.setAnnotationIntrospector(pair);
jaxMsgConverter.setObjectMapper(objMapper);
messageConverters.add(jaxMsgConverter);
SSDRResponse resp = null;
RestTemplate restTemplate = new RestTemplate();
// Set message converter with Jackson and JAXB introspectors in RestTemplate
restTemplate.setMessageConverters(messageConverters);
HttpEntity<SSDRRequest> httpRequest = new HttpEntity<SSDRRequest>(req, createHeaders());
resp = restTemplate.postForObject(SERVICE_URI, httpRequest, SSDRResponse.class);
To use only the JAXB introspector:
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter jaxMsgConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper jaxMapper = new ObjectMapper();
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
jaxMapper.setAnnotationIntrospector(introspector);
jaxMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // ignore unknown properties
jaxMsgConverter.setObjectMapper(jaxMapper);
messageConverters.add(jaxMsgConverter);
My spring boot configuration (ready to use):
#Configuration
#EnableWebMvc
#EnableAutoConfiguration
#ComponentScan(basePackages = {"path.to.your.package"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper jacksonMapper = new ObjectMapper();
AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);
jacksonMapper.setAnnotationIntrospector(pair);
jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
return jacksonMapper;
}
#Bean
public RestTemplate getRestTemplate() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter jaxMsgConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = objectMapper();
jaxMsgConverter.setObjectMapper(objectMapper);
messageConverters.add(jaxMsgConverter);
HttpClientBuilder builder = HttpClientBuilder.create();
HttpClient httpClient = builder.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate template = new RestTemplate(new BufferingClientHttpRequestFactory(factory));
template.setMessageConverters(messageConverters);
return template;
}
}
Related
I am trying to use Spring's ReloadableResourceBundleMessageSource for LocalValidatorFactoryBean so that when I update an error message it should reflect without requiring the server to be restarted. I am using Spring 4.1.4, hibernate-validator 4.3.2.Final.
Below are the code details -
context.xml -
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:../conf/fileapplication</value> <!-- Messages here will override the below properties file-->
<value>/WEB-INF/application</value>
</list>
</property>
<property name="cacheSeconds" value="10"></property> <!-- Will check for refresh every 10 seconds -->
</bean>
<bean name="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource">
<ref bean="messageSource"/>
</property>
</bean>
Model -
import org.hibernate.validator.constraints.NotBlank;
public class InputForm {
#NotBlank ( message = "{required.string.blank}")
String requiredString;
Controller -
#RequestMapping(value = "/check/string", method = RequestMethod.POST)
public String checkString(
#ModelAttribute("formModel") #Valid InputForm inputForm ,
BindingResult result, Model model, HttpServletResponse response,
HttpServletRequest request) {
if (result.hasErrors()) {
model.addAttribute("formModel", inputForm);
return "userInput";
}
// Do some backend validation with String
result.reject("string.not.valid",
"String is Invalid");
model.addAttribute("formModel", inputForm);
return "userInput";
}
application.properties (in /WEB_INF/ folder)
required.string.blank=Please enter the required string.
string.not.valid=Please enter a valid string.
fileapplication.properties (in /conf/ folder. Will override above file)
required.string.blank=You did not enter the required string. #Does not reflect when I change here
string.not.valid=You did not enter a valid string. #Reflects when I change here
Now the problem I am facing is, when I update "string.not.valid" in fileapplication.properties it reflects at runtime and I see the updated message. But when I update "required.string.blank" in fileapplication.properties it does not reflect at runtime.
Note that the overriding part is working fine for both messages upon application start up. But the "reloading" part is not working fine for "required.string.blank".
This is what I figured out based on my research - We need to create our own MessageInterpolator and add it as dependency to the validator instead of message source. Because when we add a messageSource as dependency, it is cached by default by the validator and any message reloads spring does won't take effect in the validator's cached instance of messageSource.
Below are the details:
In context.xml, add the custom MessageInterpolator as dependency to LocalValidatorFactoryBean instead of messageSource:
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:../conf/fileapplication</value> <!-- Messages here will override the below properties file-->
<value>/WEB-INF/application</value>
</list>
</property>
<property name="cacheSeconds" value="10"></property> <!-- Will check for refresh every 10 seconds -->
</bean>
<bean name="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="messageInterpolator">
<ref bean="messageInterpolator"/>
</property>
</bean>
<bean name="messageInterpolator"
class="com.my.org.support.MyCustomResourceBundleMessageInterpolator">
<constructor-arg ref="messageSource" />
</bean>
Create your custom MessageInterpolator by extending Hibernate's org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.
public class MyCustomResourceBundleMessageInterpolator extends
ResourceBundleMessageInterpolator {
public MyCustomResourceBundleMessageInterpolator(MessageSource messageSource)
{
// Passing false for the second argument
// in the super() constructor avoids the messages being cached.
super(new MessageSourceResourceBundleLocator(messageSource), false);
}
}
Model, Controller and properties file can be same as in the question.
I ran the getting started guide (http://www.axonframework.org/axon-2-quickstart-guide/) and it works fine.
I tried to replace the FileSystemEventStore with JpaEventStore, but it doesn't store any event.
Here is my configuration:
public static void main(String[] args) {
CommandBus commandBus = new SimpleCommandBus();
CommandGateway commandGateway = new DefaultCommandGateway(commandBus);
//EventStore eventStore = new FileSystemEventStore(new SimpleEventFileResolver(new File("./events")));
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "cqrsworkshop" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
SimpleEntityManagerProvider entityManagerProvider = new SimpleEntityManagerProvider(entityManager);
EventStore eventStore = new JpaEventStore(entityManagerProvider);
EventBus eventBus = new SimpleEventBus();
EventSourcingRepository<ToDoItem> repository = new EventSourcingRepository<ToDoItem>(ToDoItem.class, eventStore);
repository.setEventBus(eventBus);
AggregateAnnotationCommandHandler.subscribe(ToDoItem.class, repository, commandBus);
AnnotationEventListenerAdapter.subscribe(new ToDoEventHandler(), eventBus);
final String itemId = UUID.randomUUID().toString();
commandGateway.send(new CreateToDoItemCommand(itemId, "Need to do this"));
commandGateway.send(new MarkCompletedCommand(itemId));
entityManagerFactory.close();
}
<persistence-unit name="cqrsworkshop">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- JPA Event Store -->
<class>org.axonframework.eventstore.jpa.DomainEventEntry</class>
<class>org.axonframework.eventstore.jpa.SnapshotEventEntry</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:~/test" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect " />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
ATM to use JPA with Axon requires Spring. The only implementation provided for Axon TransactionManager is SpringTransactionManager which requires an instance of org.springframework.orm.jpa.JpaTransactionManager. See JpaEventStore and resource local transactions: quite old but seems still valid.
By Spring I mean not only Spring-orm but also Spring-context and Spring-aspects. I haven't find out how to use JpaTransactionManager without all the Spring magic.
Two exemples:
a vaadin one
a console one (in Xtend but it's really Java like)
My Xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<bean id="filterBySlic" class="ca.ups.tundra.msg.FilterMessagesBySlic">
<property name="slicList">
<list><value>4196</value><value>1101</value><value>2795</value></list>
</property>
<property name="messageList">
<list><value>7762</value><value>7765</value><value>7766</value><value>7767</value><value>7768</value></list>
</property>
<property name="serviceLevelList">
<list><value>E1</value><value>E3</value><value>E4</value><value>29</value></list>
</property>
<property name="serviceTypeList">
<list><value>029</value><value>096</value></list>
</property>
</bean>
</beans>
this is what I am using in my class:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring-pred-filter.xml"});
FilterMessagesBySlic filterConfig = (FilterMessagesBySlic)context.getBean("filterBySlic");
my condition to access the list of values;
filterConfig.getSiteList().contains(msgSlic)
which is working fine.
Instead I need to use #Autowired to access those list of values! Any suggestion
you can change your lists from being anonymous inner beans to normal beans and inject them in other beans like this:
xml configuration:
<util:list id="slicList" value-type="java.lang.String">
<value>4196</value>
<value>1101</value>
<value>2795</value>
</util:list>
injecting slicList in a bean:
public class Foo {
#Resource(name = "slicList")
List<String> messageList;
}
this implies of course that the instance of Foo is managed by spring.
is it what you are looking for?
I have a fairly simple task that I want to accomplish, but can't seem to find information for Spring MVC routing about it. I have a very simple controller that routes a path to a view:
#Controller
#RequestMapping(value = "/help")
public class HelpController {
private static final String HELP = "help";
#RequestMapping(method = RequestMethod.GET)
public String help(Model model, Locale locale) {
model.addAttribute("locale", locale);
return HELP;
}
}
I would like to throw a 404 if http://mysite.com/help.some.extension.is.entered, but Spring seems to resolve the example to /help. The javadoc says that the #RequestMapping annotation is just a servlet URI mapping, but I thought /help means it needs to be an exact match. Any clarification would be appreciated.
For Spring 4 it's pretty easy to solve:
<mvc:annotation-driven>
<mvc:path-matching suffix-pattern="false" />
</mvc:annotation-driven>
So you still can use mvc:annotation-driven for your config.
You can mention it in the #RequestMapping annotation
it is same as Servlet URL pattern only.
#Controller
public class HelpController {
private static final String HELP = "help";
#RequestMapping(value = "/help" method = RequestMethod.GET)
public String help(Model model, Locale locale) {
model.addAttribute("locale", locale);
return HELP;
}
#RequestMapping(value = "help/*" method = RequestMethod.GET)
public String helpWithExtraWords() {
return "error";
}
}
The best way I can think of is to configure your RequestMappingHandlerMapping explicitly to not consider suffixpaths, this way:
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"></property>
</bean>
However, if you have configured your Spring MVC using mvc:annotation-driven, this will not work, you will have to expand out the entire handlerAdapter definition, which is not that difficult to do, along these lines(this is not complete, you can look through org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser for the entire definition):
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"></property>
</bean>
With Spring 3.0.X You can use the useDefaultSuffixPattern property.
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
You will need to remove </mvc:annotation-driven>
Refer URL Pattern Restricting in SPRING MVC
the default charset of #ResponseBody is iso-8859-1,
how to change to utf8?
configuration below does't seem to work.
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name = "messageConverters">
<list>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name = "supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
You can add produces = "text/plain;charset=UTF-8" to request mapping
#RequestMapping(value = "/rest/create/document", produces = "text/plain;charset=UTF-8")
#ResponseBody
public void create(Document document, HttpServletRespone respone) throws UnsupportedEncodingException {
Document newDocument = DocumentService.create(Document);
return jsonSerializer.serialize(newDocument);
}
If you are using tomcat you also need to specifying URIEncoding="UTF-8" on <Connector> in the Tomcat server.xml config, as described here:
http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q8