I am learning microservices. Therefore to ensure fault tolerance I wanted to use hystrix.
I use springboot with maven for this implementation. I followed a tutorial to make the implementation and have tried adding various dependencies in the pom.xml. However it wasn't successful. The #HystrixCommand is unable to be imported for the usage. I have attached the sample error shown here below. My micro service application code looks like below.
My Pom.XML file looks like this:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.javabrains</groupId>
<artifactId>movie-catalog-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>movie-catalog-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2020.0.3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<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-compiler-plugin</artifactId>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
The main method
package io.javabrains.moviecatalogservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
#SpringBootApplication
#EnableEurekaClient
#EnableHystrix
public class MovieCatalogServiceApplication {
#Bean
#LoadBalanced
public RestTemplate getRestTemplate() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(3000);
return new RestTemplate(clientHttpRequestFactory);
}
public static void main(String[] args) {
SpringApplication.run(MovieCatalogServiceApplication.class, args);
}
}
The controller/ resource class
package io.javabrains.moviecatalogservice.resources;
import com.netflix.discovery.DiscoveryClient;
import io.javabrains.moviecatalogservice.models.CatalogItem;
import io.javabrains.moviecatalogservice.models.Movie;
import io.javabrains.moviecatalogservice.models.Rating;
import io.javabrains.moviecatalogservice.models.UserRating;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
//How to make it REST... Just add the annotation
//So whenever a request is made, it checks the controller for any action to be done
#RestController
//Further to say springboot to treat this as api which is accessible at /catalog/something
#RequestMapping("/catalog")
public class MovieCatalogResource {
#Autowired //I am basically telling spring that somebody has a BEAN somewhere, offsitre restTempltae//GET ME THT THING
private RestTemplate restTemplate;
//
// #Autowired
// private DiscoveryClient client;
//FOR WEB CLIENT
#Autowired
private WebClient.Builder webClientBuilder;
#RequestMapping("/{userId}") //userId is a variable and it will passed
#HystrixCommand(fallbackMethod = "getFallBackCatalog")//teliing hsyutrix that getCatalog shouldnt brak the limit. If it breaks, call getFallBackCatalog
public List<CatalogItem> getCatalog(#PathVariable("userId") String userId){
UserRating ratings = restTemplate.getForObject("http://RATING-DATA-SERVICE/ratingsdata/user/" + userId, UserRating.class);
return ratings.getRatings().stream().map(rating -> {
System.out.println(rating.getMovieId());
Movie movie = restTemplate.getForObject("http://MOVIE-INFO-SERVICE/movies/"+rating.getMovieId(), Movie.class);
return new CatalogItem(movie.getName() , "Test", rating.getRating());
})
.collect(Collectors.toList());
}
public List<CatalogItem> getFallBackCatalog(#PathVariable("userId") String userId){
return Arrays.asList(new CatalogItem("No movie", "", 0));//returning default list
}
}
However, the problem that I get is that I am unable to import the #HystrixCommand as its not getting recognized
Add version in this dependency. Just like this
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
Related
I'm making a MicroServices based project so I have more the one Spring Boot projects in my workspace. I need to configure restOperations in some of then but I want to configure once for all the project that needs. So I'm trying to add my #Configuration class to a jar and import in each MS projects.
The problem is, when I execute the MS project in my server, I receive this error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field restOperations in com.epavanellio.base.business.controller.BusinessController required a bean of type 'org.springframework.web.client.RestOperations' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.web.client.RestOperations' in your configuration.
Here I have my Rest configuration class:
package com.epavanellio.base.restConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
//#Component
#Configuration
public class SimpleRestConfiguration {
final CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
.build();
#Bean
public RestOperations createRestTemplate(final ClientHttpRequestFactory clientHttpRequestFactory){
return new RestTemplate(clientHttpRequestFactory);
}
#Bean
public ClientHttpRequestFactory createHttpRequestFactory (#Value("${rest.connect.timeout}") final int connectTimeout,
#Value("${rest.read.timeout}") final int readTimeout) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(connectTimeout);
clientHttpRequestFactory.setReadTimeout(readTimeout);
clientHttpRequestFactory.setHttpClient(httpClient);
return clientHttpRequestFactory;
}
}
I imported the .jar (dpdc-rest) with has the SimpleRestConfiguration class in my MS project POM:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.epavanellio.base</groupId>
<artifactId>ms-manager-business</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ms-manager-business</name>
<description>Validate business logic. A microservice based project. </description>
<packaging>war</packaging>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.epavanellio.base</groupId>
<artifactId>domain</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.epavanellio.base</groupId>
<artifactId>dpdc-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.epavanellio.base</groupId>
<artifactId>dpdc-custom-exception-handler</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</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-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>ms-manager-business</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and in my MS application class is like this:
package com.epavanellio.base.business;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import com.epavanellio.base.restConfig.SimpleRestConfiguration;
//#SpringBootApplication(scanBasePackages={"com.epavanellio.base", "com.epavanellio.base.restConfig"})
//#Import(SimpleRestConfiguration.class)
//#ComponentScan({"com.epavanellio.base", "com.epavanellio.base.restConfig"})
#ComponentScan("com.epavanellio.base")
#EntityScan("com.epavanellio.base.domain")
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
#SpringBootApplication
public class BusinessApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(BusinessApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BusinessApplication.class);
}
}
as you can see commented, I already tried to my make my runtime "see" my configuration class in diffrent ways:
First I tried to add (scanBasePackages={"com.epavanellio.base", "com.epavanellio.base.restConfig"}) after my annotation #SpringBootApplication, but the same error occurs. Then I tried to add specifically the SimpleRestConfiguration class package to the #ComponentScan annotation(for this, I uncommented the #Component annotation in SimpleRestConfiguration class), but the same error occurs. At least I tried to use #Import, but in this case I receive the error:
java.io.FileNotFoundException: class path resource [com/epavanellio/base/restConfig/SimpleRestConfiguration.class] cannot be opened because it does not exist
does any one know how can I make my application class to "see" my
#Configuration class?
The problem was Maven, for some reason maven was no recognizing my jar.
so I made a new dependency project, with a new name but same same SimpleRestConfiguration class. I imported the new .Jar to my MS and then works fine.
My application become like this:
#ComponentScan("com.epavanellio.base")
#EntityScan("com.epavanellio.base.domain")
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
#SpringBootApplication
public class UserApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(UserApplication.class);
}
}
I am new to Resilience4j and circuit breaker pattern.
I write a sample for resilience4j. Details is as below:
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ir.co.isc</groupId>
<artifactId>circuit-breaker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>circuit-breaker</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- resilience4j dependency-->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.5.0</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</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>
application.yml:
resilience4j:
circuitbreaker:
configs:
default:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
waitDurationInOpenState: 5s
failureRateThreshold: 20
eventConsumerBufferSize: 10
instances:
mainService:
baseConfig: default
Controller class:
package ir.co.isc.circuitbreaker;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class MainServiceController {
#Autowired
private MainService mainService;
#GetMapping("/getSleuthTest")
#ResponseStatus(HttpStatus.OK)
public ResponseEntity<String> getSleuthTest(){
String response = mainService.getResponse();
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
Service class:
package ir.co.isc.circuitbreaker;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
#Service
public class MainService {
private static final String MAIN_SERVICE = "mainService";
#Autowired
private RestTemplate restTemplate;
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
#CircuitBreaker(name = MAIN_SERVICE, fallbackMethod="testFallBack")
public String getResponse(){
return restTemplate.getForObject("http://localhost:8081/serviceOne", String.class);
}
private ResponseEntity<String> testFallBack(Exception e){
return new ResponseEntity<>("In fallback method", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
SpringBootApplication class:
package ir.co.isc.circuitbreaker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CircuitBreakerApplication {
public static void main(String[] args) {
SpringApplication.run(CircuitBreakerApplication.class, args);
}
}
I use postman runner to call my API. I set runner iterations to 200.
after 10 successful API call I stop third-party in this url :
http://localhost:8081/serviceOne
As I understood, after stopping third party API and record minimum number of successful call, resilience4j start to calculate fault rate and when fault rate is more than failureRateThreshold, fallback method (here testFallBack in service class) called, circuit status change from close to open mode and return my desire answer which describe in testFallBack() method.
But this is never happen (testFallBack() method never called). What is wrong with my application?
Overall after so much Google, i have feeling that fallBack can only be called from Controller. I have tried multiple times, but from Service it didn't work.
Since you are also calling it from Service, that's why you are facing this issue, instead if you call it from controller it should work for you.
I tried to change an web application from simple broker to an embedded ActiveMq Broker with stomp using Spring boot 1.5.4 but always getting an error on start up
Caused by: java.lang.IllegalArgumentException: No handlers
at org.springframework.util.Assert.isTrue(Assert.java:92) ~[spring-core-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.start(SubProtocolWebSocketHandler.java:244) ~[spring-websocket-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:175) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
... 15 common frames omitted
I reduced the failure with an simple example
POM File *
<?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.example</groupId>
<artifactId>websocket</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>websocket</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</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-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-stomp</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-net</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application Class
package com.example.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class WebsocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebsocketApplication.class, args);
}
}
WebSocketConfig Class
package com.example.websocket;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurationSupport;
#Configuration
#EnableWebSocket
#EnableWebSocketMessageBroker
#EnableJms
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {
#Value("${spring.activemq.user}")
private String mqUser;
#Value("${spring.activemq.password}")
private String mqPasword;
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/topic") //
.setRelayHost("localhost") //
.setRelayPort(61613) //
.setClientLogin(mqUser) //
.setClientPasscode(mqPasword) //
;
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
application.yml
spring:
activemq:
broker-url: stomp://localhost:61613
user: user
password: pass
Someone knows my mistake?
I found the solution. My problem was the EnableWebSocketMessageBroker Annotation and missing deployment of ActiveMQ Broker
Remove the application.yml and change WebSocketConfig class to
package com.example.websocket;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.jmx.ManagementContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurationSupport;
#Configuration
#EnableWebSocket
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/topic") //
.setRelayHost("localhost") //
.setRelayPort(61613);
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
#Bean(initMethod = "start", destroyMethod = "stop")
public BrokerService broker() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("stomp://localhost:61613");
broker.setPersistent(false);
final ManagementContext managementContext = new ManagementContext();
managementContext.setCreateConnector(true);
broker.setManagementContext(managementContext);
return broker;
}
}
works for me.
I'm trying to configure Autowired in non-spring managed class under spring boot application.
I run successfully this under web application deployed under tomcat server.
But when i want run this under spring boot nothing works.
I made very simple app to check this functionality:
Console result from web app int tomcat:
...:::TEST CONTROLLER
...:::TEST autowired in cotrnoller: com.mycompany.test_aop.Test#30ea7445
...:::NEW
...:::Check: com.mycompany.test_aop.Test#30ea7445
Console result from spring boot app:
...:::TEST CONTROLLER
...:::TEST autowired in cotrnoller: com.mycompany.test_aop.Test#627ae77d
...:::NEW
...:::Check: null
I read a lot of topics hot to configure spring boot app but nothing works.
I trying set param -javaagent to spring-instrument.jar but no effect.
I struggle with this for three days but no effect
App:
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>pl.com.eurohost</groupId>
<artifactId>aop_test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>unknown-jars-temp-repo</id>
<name>A temporary repository created by NetBeans for libraries and jars it could not identify. Please replace the dependencies in this repository with correct ones and delete this repository.</name>
<url>file:${project.basedir}/lib</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.5.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-agent</artifactId>
<version>2.5.6.SEC03</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.3.3.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Main.class:
package com.mycompany.test_aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
#SpringBootApplication
#EnableSpringConfigured
#EnableAspectJAutoProxy
#EnableLoadTimeWeaving
#ComponentScan(value = "com.mycompany.test_aop")
public class Main {
public static void main(String args[]) {
SpringApplication.run(Main.class, args);
}
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
}
Web.java:
package com.mycompany.test_aop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class web {
#Autowired
Test t;
#RequestMapping(value = "/")
public #ResponseBody String root(){
System.out.println("...:::TEST CONTROLLER");
System.out.println("...:::TEST autowired in cotrnoller: "+t);
Test2 a = new Test2();
a.check();
return "HI!";
}
}
Test.java:
package com.mycompany.test_aop;
import org.springframework.stereotype.Service;
#Service
public class Test {
public void display() {
System.out.println("...:::TEST CLASS CALL DISPLAY");
}
}
Test2.java
package com.mycompany.test_aop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
#Configurable
public class Test2 {
#Autowired
private Test t;
public Test2() {
System.out.println("...:::NEW");
}
public void check() {
System.out.println("...:::Check: "+t);
}
}
Webapp has different pom, addittional file app-servlet.xml and no main method:
Main.java
package com.mycompany.test_aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#EnableSpringConfigured
#EnableLoadTimeWeaving
#ComponentScan(value = "com.mycompany.test_aop")
#Configuration
#EnableWebMvc
public class Main extends WebMvcConfigurerAdapter {
}
app-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:default-servlet-handler/>
<context:annotation-config/>
<bean class="com.mycompany.test_aop.Main"/>
<mvc:annotation-driven>
</mvc:annotation-driven>
</beans>
By using Autowire Annotations.
#Autowired
Test2 a;
#RequestMapping(value = "/")
public #ResponseBody String root(){
System.out.println("...:::TEST CONTROLLER");
System.out.println("...:::TEST autowired in cotrnoller: "+t);
Test2 a = new Test2();//instead of this use autowired of test2
a.check();
return "HI!";
If you give thisTest2 a=new Test2();It will initiate with new Object then it will show only NULL
---UPDATE---
As it turns out the heap is getting emptied after some time. However the number of threads just grows without end. On my mac with 8Gb of RAM I am fine, but on a production machine with 1Gb I am getting:
Exception in thread "Thread-341" java.lang.OutOfMemoryError: unable to create new native thread
I did write a simple app using Spring Boot (1.2.7.RELEASE) and Apache Camel (2.15.0). The app is simple and has only 1 route: a timer will invoke a method on a bean every 1s. The method invoked will use ProducerTemplate to ssh into a remote machine, execute a small script, and print out the output to the console. Simple, right?
However, when profiling this, I can see the number of threads, and heap go through the roof! It seems like any threads created for the ssh are never killed, but parked instead. Because of that I run OOM pretty quickly.
Let me show you some profiler output:
As you can see the threads/heap go up and up very quickly.
The app code is minimal, so I will provide it all here for reference.
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>tests</groupId>
<artifactId>camel-producer-template-testing</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<start-class>app.Application</start-class>
<camel.version>2.15.0</camel.version>
<spring-boot.version>1.2.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-ftp</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-ssh</artifactId>
<version>${camel.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Application.java:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.util.TimeZone;
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
SpringApplication application = new SpringApplication(Application.class);
application.run(args);
}
}
MyAppContext.java:
import org.apache.camel.CamelContext;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spring.SpringCamelContext;
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("application.properties")
public class MyAppContext {
private final String sshKeyPath = "/Users/gruszd/.ssh/id_rsa";
#Autowired
private ApplicationContext applicationContext;
#Bean
public CamelContext camelContext() {
return new SpringCamelContext(applicationContext);
}
#Bean
FileKeyPairProvider keyPairProvider() {
return new FileKeyPairProvider(new String[]{sshKeyPath});
}
#Bean
RoutesBuilder myRouter() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer://foo?period=1000").to("bean:sftpStager?method=stage");
}
};
}
}
SftpStager.java:
import org.apache.camel.ProducerTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class SftpStager {
#Autowired
private ProducerTemplate producerTemplate;
public void stage() throws Exception {
String response = producerTemplate.requestBody(
"ssh://_remote.machine.url.here_?username=_username_&keyPairProvider=#keyPairProvider",
"/home/_username_/some_temp_script.sh",
String.class);
System.out.println("----");
System.out.println(response);
System.out.println("----");
}
}
As you can see the app is very minimal, and it works (I can see the output of the remote script in my console where the app is running). But like I said, it eats up memory like fresh cookies!
Now I did read this . However, in my app the ProducerTemplate is a bean instantiated by the Camelcontext itself. Therefore I can't producerTemplate.stop() because the next trigger would throw an exception saying the template is not started...
So my main question is: am I using the ProducerTemplate in a wrong way? And if I do, how should I use it?
If I am not doing anything wrong, is that a bug? Should I report it?
As noted by the original poster:
Turns out it is a bug in Apache Camel itself, should be [and was] fixed in 2.16.2: Jira Issue here
You must stop / clear the state of the producerTemplate.
There are in-built methods like producerTemplate.stop() or in your case, since you had autowired the Producer template, you could try producerTemplate.cleanUp()