Springboot Hystrix properties are not working - java

I am integrating Hystrix with Springboot application but somehow hystrix properties are not effective i.e. whenever I am calling getSchoolDetails(http://localhost:9003//getSchoolDetails/dummyschool), it's directly going into fallBack method which I want to control based on few Hystrix properties e.g. - requestVolumeThreshold, sleepWindowInMilliseconds etc.
But none of the hystrix properties taking effect.
Please let me know what else I need to do to make Hystrix properties working in springboot application.
Given below is my sample code.
#EnableCircuitBreaker
#EnableHystrix
#SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
System.setProperty("security.basic.enabled", "false");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
log.debug("Spring context loaded");
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
/*LogbackValve logbackValve = new LogbackValve();
// point to logback-access.xml
logbackValve.setFilename("logback-access.xml");
tomcat.addContextValves(logbackValve);*/
return tomcat;
}
}
#RestController
public class SchoolServiceController {
#Autowired
StudentServiceDelegate studentServiceDelegate;
#RequestMapping(value = "/getSchoolDetails/{schoolname}", method = RequestMethod.GET)
public String getStudents(#PathVariable String schoolname) {
System.out.println("Going to call student service to get data!");
return studentServiceDelegate.callStudentServiceAndGetData(schoolname);
}
}
#Service
public class StudentServiceDelegate {
#Autowired
RestTemplate restTemplate;
#HystrixCommand(fallbackMethod = "callStudentServiceAndGetData_Fallback", commandKey = "StudentServiceDelegate")
public String callStudentServiceAndGetData(String schoolname){
System.out.println("Getting School details for " + schoolname);
//"http://localhost:8098/getStudentDetailsForSchool/{schoolname}"
HttpHeaders headers = new HttpHeaders();
headers.add("Accept","application/json");
HttpEntity<String> postEntity = new HttpEntity<>(null, headers);
String response = restTemplate
.exchange("http://localhost:9001/v1/browse/catalog"
, HttpMethod.GET
, postEntity
, new ParameterizedTypeReference<String>() {
}, schoolname).getBody();
System.out.println("Response Received as " + response + " - " + new Date());
return "NORMAL FLOW !!! - School Name - " + schoolname + " ::: " +
" Student Details " + response + " - " + new Date();
}
private String callStudentServiceAndGetData_Fallback(String schoolname, Throwable t) {
System.out.println("Student Service is down!!! fallback route enabled...");
return "CIRCUIT BREAKER ENABLED!!! No Response From Student Service at this moment. " +
" Service will be back shortly - " + new Date();
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
application.properties
server.port=9003
hystrix.command.StudentServiceDelegate.circuitBreaker.requestVolumeThreshold=5
hystrix.command.StudentServiceDelegate.circuitBreaker.sleepWindowInMilliseconds=60000
hystrix.command.StudentServiceDelegate.metrics.rollingStats.timeInMilliseconds=60000
hystrix.command.StudentServiceDelegate.circuitBreaker.errorThresholdPercentage=50
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kohls</groupId>
<artifactId>msp-hystrix-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</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</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Newly created service method at 9001 port which is taking more than 5 seconds to respond:
#RequestMapping(value = "/v1/browse/catalog", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<String> getCatalog(HttpServletRequest httpRequest) {
long startTime = System.currentTimeMillis();
MSPResponse response = null;
try {
Thread.sleep(5000);
response = catalogService.getCatalog(httpRequest);
} catch (Exception e) {
System.out.println("Error "+e);
}
long endTime = System.currentTimeMillis() - startTime;
System.out.println("API Time taken "+endTime);
return browseUtil.forwardResponse(response);
}

Related

How to have custom context Path for Swagger Url in OpenApi3 Springboot Application

I am migrating from springfox to OpenApi3 in my Springboot project as we have a requirement to upgrade to latest springboot with version 2.7.0
I need to configure custom contextPath for different environments like below
dev - https://qa.swagger.com/dev/api/myApp/swagger-ui/index.html
qa - https://qa.swagger.com/api/myApp/swagger-ui/index.html
uat - https://uat.swagger.com/api/myApp/swagger-ui/index.html#/
// pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>1.6.8</version>
</dependency>
// SwaggerConfig class
#Configuration
#Profile({ "local", "dev", "qat", "uat" })
public class SwaggerConfig {
#Bean
public OpenAPI openAPI() {
return new OpenAPI().info(info());
}
private Info info() {
return new Info()
.title(title)
.version(version)
.license(new License().name(licenseName).url(licenseUrl));
}
}
//application.properties
spring.application.name=myApp
server.servlet.context-path=/api/${spring.application.name}
With the above configuration, I am able to run swagger using below url and getting all required response from controller apis
http://localhost:8082/api/myApp/swagger-ui/index.html#/
For configuring the Swagger url for other environments,I tried to create a listener configuration class like below which didn't work
#Component
public class SwaggerListener implements ApplicationListener<ApplicationPreparedEvent> {
final ApplicationPreparedEvent event = null;
#Override
public void onApplicationEvent(final ApplicationPreparedEvent event) {
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
Properties properties = new Properties();
properties.put("springdoc.swagger-ui.path", swaggerPath(event));
environment.getPropertySources().addFirst(new PropertiesPropertySource("programmatically", properties));
}
private String swaggerPath(final ApplicationPreparedEvent event) {
String basePath = null;
String swagger = "swagger-ui/index.html";
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
String[] profilesList = environment.getActiveProfiles();
List<String> profiles = Arrays.asList(profilesList);
String contextPath = environment.getProperty("server.servlet.context-path");
if (profiles != null && (profiles.contains("local"))) {
basePath = swagger;
} else if (profiles != null && profiles.contains("dev")) {
basePath = "/dev/api/myApp/" + swagger;
} else if (profiles != null && (profiles.contains("qat") || profiles.contains("uat"))) {
basePath = "/api/myApp/";
}
return basePath;
}
}
Adding above listener to main class
#SpringBootApplication(scanBasePackages = { "com.myApp.controller" })
#OpenAPIDefinition
public class myApi {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(myApi.class);
springApplication.addListeners(new SwaggerListener());
springApplication.run(args);
}
}
The above listener configuration is not working
Can anyone help me out here and let me know what am I missing here ?
I have been looking the same. This documentation help me:
To enable the support of multiple OpenAPI definitions, a bean of type
GroupedOpenApi needs to be defined.
For the following Group definition(based on package path), the OpenAPI
description URL will be : /v3/api-docs/stores
#Bean
public GroupedOpenApi storeOpenApi() {
String paths[] = {"/store/**"};
return GroupedOpenApi.builder().group("stores").pathsToMatch(paths)
.build();
}
For the following Group definition (based on package name), the
OpenAPI description URL will be: /v3/api-docs/users
#Bean
public GroupedOpenApi userOpenApi() {
String packagesToscan[] = {"test.org.springdoc.api.app68.api.user"};
return GroupedOpenApi.builder().group("users").packagesToScan(packagesToscan)
.build();
}
For the following Group definition(based on path), the OpenAPI
description URL will be: /v3/api-docs/pets
#Bean
public GroupedOpenApi petOpenApi() {
String paths[] = {"/pet/**"};
return GroupedOpenApi.builder().group("pets").pathsToMatch(paths)
.build();
}
For the following Group definition (based on package name and path),
the OpenAPI description URL will be: /v3/api-docs/groups
#Bean
public GroupedOpenApi groupOpenApi() {
String paths[] = {"/v1/**"};
String packagesToscan[] = {"test.org.springdoc.api.app68.api.user", "test.org.springdoc.api.app68.api.store"};
return GroupedOpenApi.builder().group("groups").pathsToMatch(paths).packagesToScan(packagesToscan)
.build();
}
Source: SPRINDOC

In spring boot, GET request is returning 200 but POST request is returning 403

I've a spring boot application with springdoc-openapi-ui dependency, and without security.
The HTTP GET request is going through (status 200). But the HTTP POST request is not going through (status 403). What could be the reason for the 403 return code on the POST request?
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chartinvest</groupId>
<artifactId>ta</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<properties>
<open-api>1.6.8</open-api>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${open-api}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
</project>
Spring controller class
#RestController
public class TaController {
#RequestMapping(value = "/ping", method = RequestMethod.GET)
public #ResponseBody String pingGet() {
log.info("/ping");
return "ok";
}
#Operation(summary = "test")
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(String string) {
log.info("/test");
return "ok !!";
}
swagger
RequestParam annotation is missing. Please use below method. You need to invoke POST method with request param key string
And ResponseBody annotation is also missing.
#Operation(summary = "test")
#RequestMapping(value = "/test", method = RequestMethod.POST)
public #ResponseBody String test(#RequestParam("string") String string) {
log.info("/test");
return "ok !!";
}
Either you have to use #RequestParam within your test method or you have to remove String string argument :
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#RequestParam(name= "string")String string) {
log.info("/test");
return "ok !!";
}
this works:
#Operation(summary = "test")
#PostMapping("/test")
public String test(#RequestBody String string) {
log.info("/test");
return "ok !!";
}
this will expect the string as a body.
please make sure you refresh the swagger interface after you apply the fix.
for header:
#Operation(summary = "test")
#PostMapping("/test")
public String test(#RequestHeader("someHeader") String string){
log.info("/test");
return "ok !!";
}
for path variable:
#Operation(summary = "test")
#PostMapping("/test/{somePath}")
public String test(#PathVariable("somePath") String string){
log.info("/test");
return "ok !!";
}

Jbehave Test configuration and test reporting not working as expected

I am using Jbehave to test some API's. I configured my stories but when i run the configuartion file, i get the following output:
Reports view generated with 0 stories (of which 0 pending) containing 0 scenarios (of which 0 pending)
I assumed that my configuration worked fine, and tried to run my whole test package, but it returned an output saying
No tests found in the package "com.example.prederiq.PrederaAiq"
However, when i ran my PrederaAiqApplicationTests.java file containing the test cases, all the tests passed but no console report or html report was returned to me. I have attached all the concerned files in my test package below and also the project structure below, please help me sort this out
Project Structure:
My Configuration file:
public class StoryConfiguration extends JUnitStories {
#Override
public Configuration configuration() {
return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass())).useStoryReporterBuilder(new StoryReporterBuilder().withCodeLocation(CodeLocations.codeLocationFromClass(this.getClass())).withDefaultFormats().withFormats(StoryReporterBuilder.Format.CONSOLE, StoryReporterBuilder.Format.HTML));
}
#Override
public InjectableStepsFactory stepsFactory() {
return new InstanceStepsFactory(configuration(),new PrederaAiqApplicationTests());
}
#Override
public List<String> storyPaths() {
String codeLoc = codeLocationFromClass(this.getClass()).getFile();
return new StoryFinder().findPaths(codeLoc, asList("**/test*.story"),asList(""),"file"+codeLoc);
}
}
test.story file:
Given: An authorised URL
When: A GET req is made
Then: 200 status code is returned
Given:Unauthorised link
When:GET req is made
Then: 401 status code is returned
Given: An authorised URL.
When: A GET req is made to it
Then: application type is json
Given: An authorised URL.
When: A GET req is made to it
Then: output same as that in documentation
PrederaAiqApplicationsTest file:
class PrederaAiqApplicationTests {
String contentType;
String uri = "https://sandbox.predera.com/aiq/api/projects";
WireMockServer wireMockServer = new WireMockServer(wireMockConfig().dynamicPort().dynamicHttpsPort());
CloseableHttpClient httpClient = HttpClients.createDefault();
String auth = "Bearer " + "eyJraWQiOiJndE1YKzh2bVBaNnk0NElmdllGNDZqVDlvRG5RZWxoeUg4d1JjMVwvWkdBND0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhMGNhZjMyYy0zY2Q0LTQyNzAtYmQ4NC1kOWI4N2Q1NGIyZjAiLCJjdXN0b206dGllciI6IlN0YW5kYXJkIFRpZXIiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtd2VzdC0yLmFtYXpvbmF3cy5jb21cL3VzLXdlc3QtMl8zTVkyM3BMM0YiLCJjb2duaXRvOnVzZXJuYW1lIjoicHBhbGxhdmFsbGlAdW1hc3MuZWR1IiwiY3VzdG9tOnRlbmFudF9pZCI6IlRFTkFOVDU4ZWFjMTM4YzIyMjQ5ZjA5MTA1MDA1Mzk2MGNmMzZhIiwiZ2l2ZW5fbmFtZSI6IlByYW5hdiIsImF1ZCI6IjZibjZrNTk0cmxubXRyamZpYXMxdjQwMGhmIiwiZXZlbnRfaWQiOiJkNDQ2ZTdlNy02MjM5LTRiYWMtYWE0Zi00Y2JjNWQzNDk0YWUiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTYyMzg2NTQzOSwiZXhwIjoxNjIzODY5MDM5LCJjdXN0b206cm9sZSI6IlRlbmFudFVzZXIiLCJpYXQiOjE2MjM4NjU0MzksImZhbWlseV9uYW1lIjoiUGFsbGF2YWxsaSIsImVtYWlsIjoicHBhbGxhdmFsbGlAdW1hc3MuZWR1In0.pcp4KY0HzcAtWIgFfoX5sRwJccQ4GizlbBqoh5GuaoRMkvPrzBtLRf1AwC2tsL8cFDni6whxhClSgW_w1cDQZUUUHQ82svDxiSBLLft1ZAg9VVOlJ1AkKbaZDcoA-4wVAZBdzmmuCiwhNerP9Ask2DiP0slAADwzNMfhlhvlqcqdWbZyEreHMuUkVkGVdSUDK933TRKkKP-x62PTsize6oi-mApmeZY3Qr5AcGHHW3frZE-XuYlLaJzDZH5yJv7qA7pkQ5c05LPlZdWrwTelEdx8GLRKRs-fFnwIquOLkWceqSyYuz3gFalXOG3xfZtVIozNfVfocZzXN54ul7_B-g";
int status = 0;
JSONArray array;
HttpUriRequest httpUriRequest;
#Test
#Given("An authorised URL")
void test(){
httpUriRequest = RequestBuilder.get().setUri(uri).setHeader(HttpHeaders.AUTHORIZATION,auth).build();
}
#When("A GET req is made")
void testGet() throws IOException {
HttpResponse response =httpClient.execute(httpUriRequest);
status = response.getStatusLine().getStatusCode();
}
#Then("200 status code is returned")
void test200() {
assertEquals(200,status);
}
#Test
#Given("Unauthorised link")
void test2() {
httpUriRequest = RequestBuilder.get().setUri(uri).build();
}
#When("GET req is made")
void get2() throws IOException {
HttpResponse response = httpClient.execute(httpUriRequest);
status = response.getStatusLine().getStatusCode();
}
#Then("401 status code is returned")
void statusTest2(){
assertEquals(401,status);
}
#Test
#Given("An authorised URL.")
void test3(){
httpUriRequest = RequestBuilder.get().setUri(uri).setHeader(HttpHeaders.AUTHORIZATION,auth).build();
}
#When("A GET req is made to it")
void getTest3() throws IOException {
HttpResponse response = httpClient.execute(httpUriRequest);
contentType = String.valueOf(response.getEntity().getContentType());
}
#Then("application type is json")
void content(){
assertEquals("application/json",contentType);
}
#Test
#Given("An authorised URL.")
void test4(){
httpUriRequest = RequestBuilder.get().setUri(uri).setHeader(HttpHeaders.AUTHORIZATION,auth).build();
}
#When("A GET req is made to it")
void testGet4() throws IOException, ParseException {
HttpResponse response = httpClient.execute(httpUriRequest);
HttpEntity entity = response.getEntity();
Scanner scanner = new Scanner(entity.getContent());
String content = "";
while(scanner.hasNext()){
content += scanner.nextLine();
}
scanner.close();
JSONParser parser = new JSONParser();
array = (JSONArray) parser.parse(content);
}
#Then("output same as that in documentation")
void content2(){
assertEquals("[{\"owner\":\"ppallavalli#umass.edu\",\"name\":\"churn-juyma\",\"description\":\"Customer Churn Example\",\"last_modified_date\":\"2021-06-09T17:38:06.048Z\",\"id\":\"a-443018111\",\"created_date\":\"2021-06-09T17:38:06.048Z\",\"last_modified_by\":\"ppallavalli#umass.edu\",\"created_by\":\"ppallavalli#umass.edu\",\"users\":[\"ppallavalli#umass.edu\"]}]",array.toString());
}
}
Pom.xml:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.prederiq</groupId>
<artifactId>PrederaAiq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>PrederaAiq</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-core</artifactId>
<version>4.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.28.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>
--illegal-access=permit
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

Unable to consume JSON array using Spring RestTemplate

Am trying to populate a DTO from a published RESTful URL (simple HTTP GET) by using Spring's RestTemplate HTTP Client.
This is the content of the published JSON that I am trying to consume:
[{"startDate":"2017-01-29","cost":"$50000.00","id":1112,"name":"Porsche"},{"startDate":"2017-03-06","cost":"$27000.00","id":38626,"name":"BMW"}]
My DTO:
class DTO {
private String startDate;
private String cost;
private String name;
// Getters and Setters
}
My Response Object:
public class Response {
private static final STRING = "http://www.sample.com/product";
public static List<Object> getCampaigns() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Object[]> responseEntity = (ResponseEntity) restTemplate.getForEntity(URL, Object[].class);
Object[] objects = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();
return Arrays.asList(objects);
}
public void static main (String args []) {
List<Object> dtos = getCampaigns();
for (Object dto : dtos) {
System.out.println(dto.toString());
}
}
}
Here's my pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.7</version>
</dependency>
</dependencies>
When I run the main() method inside Response, I get the following exception:
00:24:14.191 [main] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://www.sample.com/product" resulted in 200 (OK)
Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class [Ljava.lang.Object;] and content type [application/json;charset=utf-8]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:917)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:901)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:312)
What am I possibly doing wrong?
Try this
public class Response {
private static final String URL = "http://www.sample.com/product";
public static List<DTO> getCampaigns() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<DTO[]> responseEntity = restTemplate.getForEntity(URL, DTO[].class);
DTO[] objects = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();
return Arrays.asList(objects);
}
public void static main (String args []) {
List<DTO> dtos = getCampaigns();
for (DTO dto : dtos) {
System.out.println(dto.toString());
}
}
}
use this jackson library instead
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
</dependency>

Spring AMQP Priority Message

Message priorities in queues in RabbitMQ. It is working with rabbitmq provided java client. But it does not work with spring-rabbit dependency. Please have a look.
RabbitMQ Server Version - 3.6.5
Erlang - OTP 19 (8.0)
Using RabbitMQ Java Client
Pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>RabbitMQ</artifactId>
<version>0.0.1-SNAPSHOT</version>
<developers>
<developer>
<name>Sagar Rout</name>
</developer>
</developers>
<properties>
<!-- Generic properties -->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.3.2.RELEASE</spring-framework.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Spring AMQP -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Publisher.java
public class Publisher {
private final static String QUEUE_NAME = "S1_Priority";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-max-priority", 10);
channel.queueDeclare(QUEUE_NAME, false, false, false, args);
String message = "Hello World!";
for (int i = 0; i < 10; i++) {
channel.basicPublish("", QUEUE_NAME,
new AMQP.BasicProperties.Builder().contentType("text/plain").deliveryMode(2).priority(i).build(),
message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'" + "priority" + i);
}
channel.close();
connection.close();
}}
Consumer.Java
public class Consumer {
private final static String QUEUE_NAME = "S1_Priority";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-max-priority", 10);
channel.queueDeclare(QUEUE_NAME, false, false, false, args);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DefaultConsumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'" + properties.getPriority());
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}}
This is working and the message with higher priority is coming up. But it does not work with Spring-rabbit. Please find the code.
RabbitMQConfig.class
#Configuration
#ComponentScan(basePackages = { "com.blackocean.*" })
#PropertySource("classpath:config.properties")
public class RabbitMQConfig {
#Value("${rabbitmq.host}")
private String host;
#Value("${rabbitmq.port}")
private Integer port;
#Value("${rabbitmq.username}")
private String username;
#Value("${rabbitmq.password}")
private String password;
#Value("${rabbitmq.connection.size}")
private Integer connectionSize ;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
cachingConnectionFactory.setHost(host);
cachingConnectionFactory.setPort(port);
cachingConnectionFactory.setUsername(username);
cachingConnectionFactory.setPassword(password);
cachingConnectionFactory.setConnectionLimit(connectionSize);
return cachingConnectionFactory;
}
#Bean
public RabbitAdmin rabbitAdmin() {
return new RabbitAdmin(connectionFactory());
}
#Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
#Bean
public Queue queue() {
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-priority", 10);
Queue queue = new Queue("myQueue", true, false, false, args) ;
return queue ;
}}
SendUsingJavaConfig
public class Send1UsingJavaConfig {
/**
* #param args
*/
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(RabbitMQConfig.class);
RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
rabbitTemplate.convertAndSend("", "myQueue", "Hi Mr.Ocean 10", new MessagePostProcessor() {
#Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setPriority(9);
return message;
}
});
}
}
ReceiveusingJavaConfig
public class RecvUsingJavaConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(RabbitMQConfig.class);
RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
// Basic Example
String message = (String) rabbitTemplate.receiveAndConvert("myQueue");
System.out.println(message);
}}
Config.properties
#RabbitMQ
rabbitmq.host=localhost
#Always provide port and connection size in numbers
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.connection.size=100
Now I am sending the message with different priority but it always receives the message in the order. Any suggestion will be great !!!
Just a guess here , i tried looking into an old AMQP library i had used (priority queue in an older version of Rabbit MQ).
The priority was set as below
args.put("x-max-priority", 10); , it looks slightly different from args.put("x-priority", 10);.
You could refer the old priority queue repo in the link. You could try to see if that helps
Queue must have arg 'x-max-priority'.
When publishing messageProperties.priority must be none 0.
When using spring-boot amqp, it is important to set
spring.rabbitmq.listener.simple.prefetch=1
Otherwise spring-boot is fetching 250 messages absolutely ignoring priorities.
If anyone has similar requirements on message priority then you need to define priority (Configuration Class) before the queue is created. If you plan to apply the config for existing queues it will not work (from my testing).
#Value("${myApp.rabbitmq.queue}")
private String queueName;
#Bean
Queue queue(){
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-max-priority", 10);
Queue queue = new Queue(queueName, true, false, false, args) ;
return queue ;
}
When you push messages into the queue make sure the priority doesn't exceed 10 as we have defined max priority on the queue as 10.
BR, Santhosh

Categories

Resources