Spring Mongo converters not registred - spring

I've registered a set of Mongo converters (including Jsr310Converters) but it does not convert LocalDate to Date (since canConvertFromLocalDateToDate is equal to false)
#Configuration
public class MongoConfig {
#Autowired
MongoDbFactory mongoDbFactory;
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory),
new MongoMappingContext());
converter.setMapKeyDotReplacement("_");
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new GeoPointConverter());
converters.addAll(Jsr310Converters.getConvertersToRegister());
converters.addAll(JodaTimeConverters.getConvertersToRegister());
converters.addAll(ThreeTenBackPortConverters.getConvertersToRegister());
converter.setCustomConversions(new CustomConversions(CustomConversions.StoreConversions.NONE, converters));
//is FALSE !
boolean canConvertFromLocalDateToDate = converter.getConversionService().canConvert(LocalDate.class, Date.class);
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
return mongoTemplate;
}
When I try to store some entities with LocalDate fields I get an exception:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.time.LocalDate] to type [java.util.Date]

In fact I found that I had 2 instances of MappingMongoConverter at runtime. So I just reused the instance created by spring instead of instantiating a new one:
#Autowired
MappingMongoConverter converter;
The full snippet is:
#Configuration
public class MongoConfig {
#Autowired
MappingMongoConverter converter;
#Bean
public MongoTemplate mongoTemplate() throws Exception {
// NO NEED TO INSTANTIATE NEW MappingMongoConverter
//MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), new MongoMappingContext());
converter.setMapKeyDotReplacement("_");
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new GeoPointConverter());
converters.addAll(Jsr310Converters.getConvertersToRegister());
converters.addAll(JodaTimeConverters.getConvertersToRegister());
converters.addAll(ThreeTenBackPortConverters.getConvertersToRegister());
converter.setCustomConversions(new CustomConversions(CustomConversions.StoreConversions.NONE, converters));
//is FALSE !
boolean canConvertFromLocalDateToDate = converter.getConversionService().canConvert(LocalDate.class, Date.class);
converter.afterPropertiesSet();
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
return mongoTemplate;
}

Related

Spring MVC 5 Rest API convert XML to Bean using JAXB

We are migrating Jersey Endpoints to Spring MVC Rest endpoints. These endpoints consumes and produces XML. But Spring MVC is unable to convert the XML input bean because XML tag and Bean's property name is different. I have to take request as String and Convert it using JAXB explicitly. Is there any better way to do that?
Web Configuration
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
public static final String JAXB_PACKAGE = "com.comp.platform.dataholder";
#Autowired
private Jackson2ObjectMapperBuilder builder;
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
converters.add(new FormHttpMessageConverter());
converters.add(createXmlHttpMessageConverter());
converters.add(createMappingJacksonHttpMessageConverter(builder));
// converters.add(createMappingJackson2XmlHttpMessageConverter(xmlMapper));
Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter = new Jaxb2RootElementHttpMessageConverter();
jaxb2RootElementHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
converters.add(jaxb2RootElementHttpMessageConverter);
}
#Bean
public Jackson2ObjectMapperBuilder customJson() {
return new Jackson2ObjectMapperBuilder().indentOutput(true).serializationInclusion(JsonInclude.Include.NON_NULL)
.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE).failOnUnknownProperties(false);
}
#Bean
#Primary
public ObjectMapper createObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper mapper = builder.build();
mapper.setSerializationInclusion(Include.NON_NULL);
return mapper;
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(NonFinancialDataHolder.class, JaxbDataHolder.class, ViestimateDataHolder.class,CTRequest.class,com.app.rest.PolicyDetailRequest.class,com.app.rest.TransactionSubmitRequest.class,com.app.rest.AgentSuppressInfoRequest.class);
return marshaller;
}
/**
* #return
*/
#Bean
public MappingJackson2HttpMessageConverter createMappingJacksonHttpMessageConverter(
Jackson2ObjectMapperBuilder builder) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(builder.build());
return converter;
}
#Autowired
Jaxb2Marshaller jaxb2Marshaller;
private HttpMessageConverter<Object> createXmlHttpMessageConverter() {
Assert.notNull(jaxb2Marshaller, "Jaxb Marshaller dependency required");
MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(jaxb2Marshaller);
xmlConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
return xmlConverter;
}
public JAXBContext getMarshaller() throws JAXBException {
return JAXBContext.newInstance(JAXB_PACKAGE);
}
}
Bean
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"_UserId",
"_Password",
"_RequestDateTime",
"_Policy"
})
#XmlRootElement(name = "PolicyDetailRequest")
public class PolicyDetailRequest
{
#XmlElement(name = "UserId")
protected String _UserId;
#XmlElement(name = "Password")
protected String _Password;
#XmlElement(name = "RequestDateTime")
protected String _RequestDateTime;
#XmlElement(name = "Policy")
protected String _Policy;
}
Rest Endpoint
#Slf4j
#RestController
#RequestMapping("/api")
public class api
{
#Autowired
Jaxb2Marshaller jaxb2Marshaller;
#PostMapping(value="/PolicyResponse",produces=MediaType.APPLICATION_XML_VALUE,consumes = MediaType.APPLICATION_XML_VALUE)
#ResponseBody
public PolicyDetailResponse getPolicyResponse(#RequestBody String data)
{
PolicyDetailRequest request = (PolicyDetailRequest) jaxb2Marshaller.unmarshal(new StreamSource(new StringReader(data)));
PolicyDetailResponse response = new PolicyDetailResponse();
InternetRSHandler handler = new InternetRSHandler();
response= handler.getPolicyRSResponse(request);
return response;
}
}
Input Request
<PolicyDetailRequest>
<UserId>1234567890</UserId>
<Password>password</Password>
<RequestDateTime>20200420-15:07:23</RequestDateTime>
<Policy>A76ABCD12</Policy>
</PolicyDetailRequest>

Spring Data: Saving LocalTime to MongoDB using CustomConversions

How do I only convert the string in HH:mm format to LocalTime and the other way?
Here is my mongo config and right now it is converting any fields that are of String type to LocalTime and failed in the process because some of them are not meant to be LocalTime... but some of them are LocalTime in String format, so those actually need to get converted to LocalTime
#Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, this.mongoMappingContext());
converter.setCustomConversions(this.customConversions());
converter.afterPropertiesSet();
return converter;
}
#Bean
public MongoMappingContext mongoMappingContext() {
MongoMappingContext context = new MongoMappingContext();
context.setSimpleTypeHolder(new SimpleTypeHolder(new HashSet<>(Arrays.asList(
LocalTime.class
)), MongoSimpleTypes.HOLDER));
return context;
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new LocalTimeToStringConverter());
converters.add(new StringToLocalTimeConverter());
return new MongoCustomConversions(converters);
}

Spring Boot RabbitMQ Null Pointer Exception Error

I am using RabbitMQ with Spring Boot to broker messages between two services. I am able to receive the message and format it but when I call a service class in the onMessage method, I get a null pointer exception error. Here is my message listener class which receives the message
public class QueueListener implements MessageListener{
#Autowired
private QueueProcessor queueProcessor;
#Override
public void onMessage(Message message) {
String msg = new String(message.getBody());
String output = msg.replaceAll("\\\\", "");
String jsonified = output.substring(1, output.length()-1);
JSONArray obj = new JSONArray(jsonified);
queueProcessor.processMessage(obj);
}
}
Calling the method processMessage throws null pointer exception
Can someone point to me what I ma doing wrong?
I found out the issue was in the RabbitMqConfig class. Here is the code which was causing the error:
#Configuration
public class RabbitMqConfig {
private static final String QUEUE_NAME = "my.queue.name";
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("<url.to.rabbit>");
connectionFactory.setUsername("<username>");
connectionFactory.setPassword("<password>");
return connectionFactory;;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(new QueueListener());
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
The line listenerContainer.setMessageListener(new QueueListener()); was the source of the error. I solved it by Autowiring the class instead of using new. Here is the working code
#Configuration
public class RabbitMqConfig {
private static final String QUEUE_NAME = "my.queue.name";
#Autowired
private QueueListener queueListener;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("<url.to.rabbit>");
connectionFactory.setUsername("<username>");
connectionFactory.setPassword("<password>");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
/*#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(queueListener);
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
Hope this helps someone else
Make sure the QueueListener is a component class or service class that can be managed by the Spring IoC. Otherwise, the config class cannot make this a bean out of the box, since this is just a normal Java class that need to be in the container #runtime.
So when u write new QueueListener() in yr config class, then the Java class is not in the SpringContext at the time when the config class is instantiated and is therefore null.
Hope this helps clear out some of this issue!

Spring Boot & Mongo: custom MappingMongoConverter doesn't work during DotReplacement

I need to achive dotReplacementKey
I'm using MongoDB with such config:
#Configuration
public class MongoTemplateConfig {
#Value("${adserver_mongo_connection_string}")
private String databaseConnectionString;
#Bean
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
MongoClientURI uri = new MongoClientURI(databaseConnectionString.trim());
return new SimpleMongoDbFactory(uri);
}
#Bean
public MongoTemplate mongoTemplate() throws UnknownHostException {
return new MongoTemplate(mongoDbFactory());
}
#Bean
public MappingMongoConverter mongoConverter(MongoDbFactory mongoFactory, MongoMappingContext mongoMappingContext) throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoFactory);
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
mongoConverter.setMapKeyDotReplacement(".");
return mongoConverter;
}
}
I'm doing upsert as follows:
mongoTemplate.bulkOps(...).upsert(...)
but during runtime via debug I found out that different MappingMongoConverter is used rather then this that was configured as a #Bean
btw, if #Inject wherever MappingMongoConverter I get proper bean from config with keyDotReplacement="."
but looks like Spring Boot uses another one under the hood
P.S. I have seen this question without correct answer, but tried with mongoConverter.afterPropertiesSet() and obviously it doesn't work as well
It's a bit ridiculous but the point is that we should pass this custom mappingMongoConverter into MongoTemplate initialization :
#Bean
public MongoTemplate mongoTemplate(MappingMongoConverter mappingMongoConverter) throws UnknownHostException {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter);
}

Autowired VelocityEngine is not working

I have below code to read templates from database using velocityEngine
#Bean
public VelocityEngine velocityEngine() throws VelocityException, IOException {
VelocityEngineFactory factory = new VelocityEngineFactory();
DataSourceResourceLoader ds = new DataSourceResourceLoader();
ds.setDataSource(dataSource);
Properties props = new Properties();
props.put("resource.loader", "ds");
props.put("ds.resource.loader.instance", ds);
props.put("ds.resource.loader.resource.table", "tb_velocity_template");
props.put("ds.resource.loader.resource.keycolumn", "id_template");
props.put("ds.resource.loader.resource.templatecolumn", "template_definition");
props.put("ds.resource.loader.resource.timestampcolumn", "template_timestamp");
factory.setVelocityProperties(props);
return factory.createVelocityEngine();
}
I have another EmailService as below :-
#service
public class emailService{
#Autowired
private VelocityEngine velocityEngine;
public void sendEmail(){
subject =
VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
"WelcomeEmail", "Utf-8", context);
}
}
VelocityEngine is not able find resource WelcomeEmail, eventhough it is present in tb_velocity_template table.
I am not sure, why velocityengine which I have configured in ApplicationConfiguration file is not getting autowired in mailservice.

Resources