swagger is adding context root twice - spring-boot

I am using swagger 3.0.0-SNAPSHOT with my spring-data-rest. I have context configure in my application property file
server.servlet.context-path=/sample/
my swagger configuration is as follows:
#Configuration
#EnableSwagger2WebMvc
#Import({springfox.documentation.spring.data.rest.configuration.SpringDataRestConfiguration.class})
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
I am accessing my swagger ui as
http://localhost:8080/sample/swagger-ui.html
all end point in swagger appear like
http://locahost:8080/sample/sample/getHello
http://locahost:8080/sample/sample/getName
http://locahost:8080/sample/sample/getAge
these urls should be
http://locahost:8080/sample/getHello
http://locahost:8080/sample/getName
http://locahost:8080/sample/getAge
How do I avoid swagger to add extra context root to endpoint
My RestController looks like
#RestController
public class HelloController {
#RequestMapping("/hello")
public String getHello(){
return "Hello";
}
#RequestMapping("/name")
public String getName(){
return "Sample Name";
}
#RequestMapping("/age")
public Integer getAge(){
return 37;
}
}
I have confirmed with a sample project that it is happening in every case

When we migrate to swagger 3.0.0-SNAPSHOT, it's include context path also in base path. So, we can remove it by using anonymous implementation of PathProvider. I am able to manage swagger work with the below simple configuration.
import io.swagger.annotations.Api;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.PathProvider;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
#Configuration
#EnableSwagger2WebMvc
public class SwaggerConfig extends WebMvcConfigurationSupport {
private static final String DESCRIPTION = "My application description";
private static final String TITLE = " My service title";
private static final String API_VERSION = "1.0";
#Value("${server.servlet.context-path}")
private String contextPath;
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title(TITLE).description(DESCRIPTION).version(API_VERSION).build();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).pathProvider(new PathProvider() {
#Override
public String getOperationPath(String operationPath) {
return operationPath.replace(contextPath, StringUtils.EMPTY);
}
#Override
public String getResourceListingPath(String groupName, String apiDeclaration) {
return null;
}
}).select().apis(RequestHandlerSelectors.withClassAnnotation(Api.class)).build().apiInfo(apiInfo());
}
#Bean
public UiConfiguration uiConfig() {
return UiConfigurationBuilder.builder().displayRequestDuration(true).validatorUrl(StringUtils.EMPTY).build();
}
}

Related

Swagger UI doesn't show my APIs when using paths regex

I hava a problem with swagger UI. When I use paths regex in my swagger configuration class, this doesn't find my APIs. When I use the value PathsSelectors.any() this find my APIs but show also my models. I should show only my API so I have decided to use the regex.
This is my Swagger config:
package com.my.project.configurations;
#org.springframework.context.annotation.Configuration
#PropertySource(value = "classpath:application.properties")
#EnableAutoConfiguration
public class Configuration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
#Bean
public Docket productApi(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(
withClassAnnotation(RestController.class)
.and(RequestHandlerSelectors.basePackage("com.my.project.controller"))
)
.paths(regex("/rest.*"))
.build();
}
this is my Main:
package com.my.project;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication ( exclude = {SecurityAutoConfiguration.class} )
#EnableJpaRepositories("com.my.project.repository")
#EntityScan(basePackages= {"com.my.project.entities"})
public class MyProjectApplication {
public static void main(String[] args) {
SpringApplication.run(MyProjectApplication.class, args);
}
}
this is my restcontroller:
package com.my.project.controller;
#RestController
#RequestMapping(value = "/rest/pattern")
public class PatternController {
#Autowired
private IPattern patternSvc;
#GetMapping(value = { "/get-all-pattern" }, produces = { MediaType.APPLICATION_JSON_VALUE })
#ApiOperation(value = "get all pattern", tags = "pattern")
public ResponseEntity<BaseAjaxResponse<List<PatternDto>>> findAllPattern(){
HttpStatus status = HttpStatus.OK;
BaseAjaxResponse.BaseAjaxResponseBuilder<List<PatternDto>> builder = BaseAjaxResponse.<List<PatternDto>>builder();
try{
List<PatternDto> listPattern = this.patternSvc.findAllPattern();
if(!listPattern.isEmpty()){
builder
.description("Success")
.size(listPattern.size())
.code(status.value())
.payload(listPattern);
}
else{
builder
.description("Hidden list")
.code(status.value())
.size(0);
}
}catch(Exception e){
status = HttpStatus.INTERNAL_SERVER_ERROR;
builder
.description("No patterns found")
.size(0)
.code(status.value())
.errors(ApiError.builder()
.error(MyProjectInternalError.GENERIC_ERROR.keySet().toArray()[0].toString())
.datails(Collections.singletonList(
ErrorDetail
.builder()
.code(String.valueOf(MyProjectInternalError.GENERIC_ERROR.get("GENERIC ERROR")))
.field("generic")
.message(e.getMessage())
.build()
))
.build());
}
return ResponseEntity.status(status).body(builder.build());
}
}
what am i doing wrong?
I solved adding .ignoredParameterTypes(MyClass1.class, Myclass2.class, ...)
to my Docket bean and setting paths with PathSelectors.any() value

Reactive way of reading YAML with Jackson using Spring boot webflux

The yamlObjectMapper in configuration
#Bean
public ObjectMapper yamlObjectMapper() {
ObjectMapper yamlObjectMapper = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature
.WRITE_DOC_START_MARKER));
yamlObjectMapper.findAndRegisterModules();
return yamlObjectMapper;
}
The Service to parse yaml file
#Service
public class CustomerService {
#Autowired
#Qualifier("yamlObjectMapper")
private ObjectMapper yamlObjectMapper;
public Customer get() {
try {
InputStream inputStream = ResourceUtils.getURL("classpath:/files/test.yaml").openStream();
return yamlObjectMapper.readValue(inputStream, Customer.class);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
#Data
public static class Customer {
private String name;
private String surname;
private String email;
}
}
I guess IO operations are blocking, how this can be done using reactive way?
I would rather use configuration binding since probably you need to read it once.
package com.vob.webflux.webfilter.controller;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#PropertySource(value = "classpath:config.yml", factory= YamlPropertySourceFactory.class)
#Getter
public class YamlFooProperties {
#Value("${test}")
private String test;
}
Factory
package com.vob.webflux.webfilter.controller;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Properties;
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource)
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
Source factory from

Spring Boot: Add a specific HTTP header in a SOAP request based on Web Service Security (WS-Security, WSS) username

I am exposing a SOAP web service using Spring Boot. This web service is secured using Web Service Security (WSS) which is configured with this security_policy.xml:
<xwss:SecurityConfiguration
xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireUsernameToken
passwordDigestRequired="true" nonceRequired="true" />
</xwss:SecurityConfiguration>
Until this point, the application is working just fine. It is able to authenticate successfully.
Now, I need to add a specific HTTP header based on the WSS username. It is, adds the HTTP header "x-auth-type" with the values:
"test-auth-type" when the username is "test"
"production-auth-type" when the username is "production"
"undefined-auth-type" otherwise
I thought it was easy to add an EndpointInterceptor in which I can set the HTTP header based on the user, but is not been possible to me until now.
My Web Service Configuration class looks like this:
package com.godev.soapwebserviceswithspring;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
import org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static final String WS_SCHEMA_PATH = "godev_contract.xsd";
private static final String NAMESPACE_URI = "http://godev.com/soap/webservices/demo";
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(
ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
#Bean(name = "xml_message")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema billsSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("XmlMessagePort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace(NAMESPACE_URI);
wsdl11Definition.setSchema(billsSchema);
return wsdl11Definition;
}
#Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource(WS_SCHEMA_PATH));
}
#Bean
PayloadLoggingInterceptor payloadLoggingInterceptor() {
return new PayloadLoggingInterceptor();
}
#Bean
PayloadValidatingInterceptor payloadValidatingInterceptor() {
final PayloadValidatingInterceptor payloadValidatingInterceptor = new PayloadValidatingInterceptor();
payloadValidatingInterceptor.setSchema(new ClassPathResource(WS_SCHEMA_PATH));
return payloadValidatingInterceptor;
}
#Bean
XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(callbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("security_policy.xml"));
return securityInterceptor;
}
#Bean
SimplePasswordValidationCallbackHandler callbackHandler() {
SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
callbackHandler.setUsersMap(Collections.singletonMap("admin", "pwd123"));
return callbackHandler;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(payloadLoggingInterceptor());
interceptors.add(payloadValidatingInterceptor());
interceptors.add(securityInterceptor());
}
}
My Web Service Endpoint class looks like this:
package com.godev.soapwebserviceswithspring;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import com.godev.soap.webservices.demo.GetXmlMessageRequest;
import com.godev.soap.webservices.demo.GetXmlMessageResponse;
#Endpoint
public class XmlMessageEndpoint {
private static final String NAMESPACE_URI = "http://godev.com/soap/webservices/demo";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getXmlMessageRequest")
#ResponsePayload
public GetXmlMessageResponse getXmlDocument(#RequestPayload GetXmlMessageRequest request) {
GetXmlMessageResponse response = new GetXmlMessageResponse();
response.setXmlMessage("<xml>empty document</xml>");
return response;
}
}
Any advice will be very appreciated!
It works for me:
Inject the Security element present in the SOAP header in the Endpoint:
#Endpoint
public class XmlMessageEndpoint {
private static final String NAMESPACE_URI = "http://godev.com/soap/webservices/demo";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getXmlMessageRequest")
#ResponsePayload
public GetXmlMessageResponse getXmlDocument(#RequestPayload GetXmlMessageRequest request, #SoapHeader("{" + Security.SECURITY_NAMESPACE + "}Security") SoapHeaderElement securityHeader) {
GetXmlMessageResponse response = new GetXmlMessageResponse();
response.setXmlMessage("<xml>empty document</xml>");
return response;
}
In order to parse the securityHeader into something usable, you need to define a couple of POJOs. In my case, I only need the username
POJO for Security element:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = Security.SECURITY_NAMESPACE, name = "Security")
#Getter
#Setter
public class Security {
public static final String SECURITY_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
#XmlElement(namespace = Security.SECURITY_NAMESPACE, name = "UsernameToken")
private UsernameToken usernameToken;
}
POJO for UsernameToken element:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = Security.SECURITY_NAMESPACE, name = "UsernameToken")
#Getter
#Setter
public class UsernameToken {
#XmlElement(namespace = Security.SECURITY_NAMESPACE, name = "Username")
private String username;
}
And finally, you can parse the securityHeader using something like this:
public class SoapParser {
public static Security parseSecurityElement(SoapHeaderElement soapHeaderElement) {
Security securityElement = null;
try {
JAXBContext context = JAXBContext.newInstance(Security.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
securityElement = (Security) unmarshaller.unmarshal(soapHeaderElement.getSource());
} catch (JAXBException e) {
e.printStackTrace();
}
return securityElement;
}
}
I hope, it helps!

Spring boot + redis

I am working demo Spring boot application with integration of Redis.
I have referred various site reference but lastly I preferred to follow this: http://www.baeldung.com/spring-data-redis-tutorial
My code is almost same as given in above link. Only change is that I have autowired StudentRepository in my RestController class.
Now when I try to do maven-install at that time it gives me error that
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'studentRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'studentRepositoryImpl' defined in file [/home/klevu/work/Nimesh/Spring Boot Workspace/bootDemo/target/classes/com/example/demo/redis/repository/StudentRepositoryImpl.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.example.demo.redis.repository.StudentRepositoryImpl]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: No visible constructors in class com.example.demo.redis.repository.StudentRepositoryImpl
When I tried to keep constructor public, it creates build successfully. But I don't know I should do it or not here. I was thinking that rather than Autowiring constructor, I should be able to do setter injection. I also tried below:
#Autowired
private RedisTemplate<String, Student> redisTemplate;
But it is also not working.
package com.example.demo.redis.repository;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.redis.bean.Student;
#Repository
public class StudentRepositoryImpl implements StudentRepository {
private static final String KEY = "Student";
//#Autowired
private RedisTemplate<String, Student> redisTemplate;
private HashOperations<String, String, Student> hashOps;
#Autowired
private StudentRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
#PostConstruct
private void init() {
hashOps = redisTemplate.opsForHash();
}
#Override
public void saveStudent(Student person) {
hashOps.put(KEY, person.getId(), person);
}
#Override
public void updateStudent(Student person) {
hashOps.put(KEY, person.getId(), person);
}
#Override
public Student findStudent(String id) {
return hashOps.get(KEY, id);
}
#Override
public Map<String, Student> findAllStudents() {
return hashOps.entries(KEY);
}
#Override
public void deleteStudent(String id) {
hashOps.delete(KEY, id);
}
}
RedisConfiguration are default and code as below:
package com.example.demo.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
#Configuration
public class RedisConfiguration {
#Bean
JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<String, Object> redisTemplate(){
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
Spring boot main entry point is declared as below:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
#SpringBootApplication
#EnableMongoRepositories(basePackages = {"com.example.demo.mongo.repository"} )
#EnableRedisRepositories(basePackages = {"com.example.demo.redis.repository"})
public class BootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class, args);
}
}
Demo controller to test redis is as below:
package com.example.demo.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.redis.bean.Student;
import com.example.demo.redis.repository.StudentRepository;
#RestController
#RequestMapping("/student")
public class StudentController {
#Autowired
private StudentRepository studentRepository;
#GetMapping
public ResponseEntity<Map<String, Student>> index() {
Map<String, Student> students = studentRepository.findAllStudents();
return new ResponseEntity<Map<String, Student>>(students, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.GET, value = "/{id}")
public ResponseEntity<Student> getStudentById(#PathVariable("id") String id) {
Student student = studentRepository.findStudent(id);
return new ResponseEntity<Student>(student, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Student> saveStudent(#RequestBody Student student) {
studentRepository.saveStudent(student);
return new ResponseEntity<Student>(student, HttpStatus.CREATED);
}
#RequestMapping(method = RequestMethod.PUT, value = "/{id}")
public ResponseEntity<Student> updateStudent(#RequestBody Student student) {
studentRepository.updateStudent(student);
return new ResponseEntity<Student>(student, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.DELETE, value = "/{id}")
public ResponseEntity<Student> deleteMessage(#PathVariable("id") String id) {
studentRepository.deleteStudent(id);
return new ResponseEntity<Student>(HttpStatus.OK);
}
}
You set the constructor as private... change it to public
#Autowired
public StudentRepositoryImpl(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
Change the following redis configuration:
Your original:
#Bean
public RedisTemplate<String, Object> redisTemplate() {
...
}
Change it to:
#Bean
public RedisTemplate<String, ?> redisTemplate(){
...
}
It should work now for you.
You could use Spring Data Redis
Add dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Enable Caching
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
}
Add Cacheable annotation at method to cache in redis
#Cacheable(value = "employee", key = "#id")
public Employee getEmployee(Integer id) {
log.info("Get Employee By Id: {}", id);
Optional<Employee> employeeOptional = employeeRepository.findById(id);
if (!employeeOptional.isPresent()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Id Not foud");
}
return employeeOptional.get();
}

Method with #JmsListener not executed when setting custom message converter

Hy,
Having these three classes I have found a problem:
package xpadro.spring.jms;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MarshallingMessageConverter;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#SpringBootApplication
public class JmsJavaconfigApplication {
public static void main(String[] args) {
SpringApplication.run(JmsJavaconfigApplication.class, args);
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller(){
Jaxb2Marshaller jaxb = new Jaxb2Marshaller();
Class array[] = new Class[1];
array[0]= xpadro.spring.jms.model.Order.class;
jaxb.setClassesToBeBound(array);
return jaxb;
}
#Bean
#Autowired
public MarshallingMessageConverter marshallingMessageConverter( Jaxb2Marshaller marshaller){
MarshallingMessageConverter mc = new MarshallingMessageConverter();
mc.setMarshaller(marshaller);
mc.setUnmarshaller(marshaller);
return mc;
}
#Bean
#Autowired
public JmsTemplate init(MarshallingMessageConverter messageConverter,ConnectionFactory connectionFactory){
JmsTemplate template = new JmsTemplate();
template.setMessageConverter(messageConverter);
template.setConnectionFactory(connectionFactory);
return template;
}
}
Next, the model:
package xpadro.spring.jms.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "order")
#XmlAccessorType(XmlAccessType.FIELD)
public class Order implements Serializable {
private static final long serialVersionUID = -797586847427389162L;
#XmlElement(required = true)
private String id="";
public Order() {
}
public Order(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
Next, the listener:
package xpadro.spring.jms.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import xpadro.spring.jms.model.Order;
import xpadro.spring.jms.service.StoreService;
#Component
public class SimpleListener {
private final StoreService storeService;
#Autowired
public SimpleListener(StoreService storeService) {
this.storeService = storeService;
}
#JmsListener(destination = "simple.queue")
public void receiveOrder(Order order) {
storeService.registerOrder(order);
}
}
The class that sends messages:
package xpadro.spring.jms.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import xpadro.spring.jms.model.Order;
#Service
public class ClientServiceImpl implements ClientService {
private static final String SIMPLE_QUEUE = "simple.queue";
private static final String IN_QUEUE = "in.queue";
private final JmsTemplate jmsTemplate;
#Autowired
public ClientServiceImpl(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
#Override
public void addOrder(Order order) {
//MessageRegistered with a bean
jmsTemplate.convertAndSend(SIMPLE_QUEUE, order);
}
#Override
public void registerOrder(Order order) {
//MessageRegistered with a bean
jmsTemplate.convertAndSend(IN_QUEUE, order);
}
}
The point is that listener(method receiveOrder()) is not executed when I set a custom message converter in the class JmsJavaconfigApplication. When I dont set it, spring boot sets a SimpleMessageConverter and the listener is executed.
Any wrong or missed configuration?
Thanks
You need to provide an marshalling converter to the #JmsListener to unmarshall the message, by configuring the listener container factory see the documentation.

Resources