Spring Cloud Gateway - Unable to find GatewayFilterFactory with name [Filter_Name] - spring-boot

I have a spring cloud gateway application. I am trying to setup a gateway filter. The Spring Boot version is 2.3.4.RELEASE Here are the dependencies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation platform(SpringBootPlugin.BOM_COORDINATES)
implementation platform('org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8')
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}
Here is the configutraion for the gateway client
server:
port: 8081
spring:
cloud:
gateway:
routes:
- id: onboard_redirect
uri: http://localhost:8080/api/v1/onboard
predicates:
- Path=/api/v1/onboard
filters:
- name: MyLogging
args:
baseMessage: My Custom Message
preLogger: true
postLogger: true
Here is my filter class:
#Component
public class MyLoggingGatewayFilterFactory extends AbstractGatewayFilterFactory<MyLoggingGatewayFilterFactory.Config> {
final Logger logger =
LoggerFactory.getLogger(MyLoggingGatewayFilterFactory.class);
public MyLoggingGatewayFilterFactory() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// Pre-processing
if (config.preLogger) {
logger.info("Pre GatewayFilter logging: "
+ config.baseMessage);
}
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
// Post-processing
if (config.postLogger) {
logger.info("Post GatewayFilter logging: "
+ config.baseMessage);
}
}));
};
}
public static class Config {
public String baseMessage;
public boolean preLogger;
public boolean postLogger;
}
}
Everything works without configuring the filter but when I configure the filter I get follwing error:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name MyLogging
Caused by: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name MyLogging
what I am doing wrong here?

The filter class is MyLoggingGatewayFilterFactory, not MyLogging as you set in your properties.
Try to the following modification in your application.yml file:
filters:
- name: MyLoggingGatewayFilterFactory

Add this dependency in the application.properties file.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-
resilience4j</artifactId>
</dependency>

Related

Error while using custom loadbalancer for spring cloud loadbalancer with healthcheck configuration

I am using a static list (SimpleDiscoveryClient) to loadbalance using spring cloud loadbalancer. Using StickySession Loadbalancer rule in https://github.com/fitzoh/spring-cloud-commons/blob/e10997b6141ff560479ef7065c3547f1f59360c8/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/StickySessionLoadBalancer.java.
My WebClientConfig class:
#Configuration
#LoadBalancerClient(name = "testservice", configuration = CustomLoadBalancerConfiguration.class)
public class WebClientConfig {
#LoadBalanced
#Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
Custom LoadBalancer Configuration class:
public class CustomLoadBalancerConfiguration {
#Bean
ReactorLoadBalancer<ServiceInstance> StickySessionLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new StickySessionLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
#Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
Posting my yml here :
spring:
application:
name: sample
cloud:
discovery:
client:
health-indicator:
enabled: false
simple:
instances:
testservice:
- uri: http://localhost:8082
- uri: http://localhost:8081
loadbalancer:
configurations: health-check
cache:
enabled: false
health-check:
path:
default: /actuator/health
interval: 10000
gateway:
routes:
- id: testrouting
path: /user/*
uri: lb://testservice
predicates:
- Method=GET,POST
- Path=/user/**
It's all according to the official documentation. But with the customloadbalancer rule (Stickysession Loadbalancer), the healthchecks to servers are not happening to checkif the servers are alive or not. The server list is always empty (all servers are marked as not alive).

hystrix dashboard doesnt load

I can see hystrix dashboard with : http://localhost:8081/hystrix
Also I see my circuits break and fallback methods working as well
Yet I can't see my application's data at : http://localhost:8081/actuator/hystrix.stream
When I look at /actuator/conditions I see following error for Hystrix
"HystrixAutoConfiguration.HystrixServletAutoConfiguration": {
"notMatched": [
{
"condition": "OnClassCondition",
"message": "#ConditionalOnClass did not find required class 'com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet'"
}
],
"matched": []
}
Code
pom.xml
Spring Boot parent version : 2.2.6.RELEASE
Spring Cloud version : Hoxton.SR3
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
application.properties
management.endpoints.web.exposure.include = hystrix.stream, conditions
MainApp
#SpringBootApplication
#EnableEurekaClient
#EnableCircuitBreaker
#EnableHystrixDashboard
#EnableHystrix
public class MovieCatalogServiceApplication {}
CatalogRestController
#RestController
#RequestMapping("/catalog")
public class CatalogRestController {
#Autowired
MovieInfoService movieInfo;
#Autowired
UserRatingsService userRatingInfo;
#RequestMapping("/{userId}")
public List<Catalog> getCatalogsByUserId(#PathVariable("userId") int userId){
UserRatings ratings = userRatingInfo.getUserRating(userId);
return ratings.getUserRatings().stream().map(oneOfRatings -> {
return movieInfo.getCatalog(oneOfRatings);
}).collect(Collectors.toList());
}
}
UserRatingsService
#Service
public class UserRatingsService {
#Autowired
private WebClient webClient;
#HystrixCommand(fallbackMethod="getFallbackUserRating")
public UserRatings getUserRating(int userId) {
return webClient
.get()
.uri("http://user-rated-movies-service/ratingsdata/users/"+ userId)
.retrieve()
.bodyToMono(UserRatings.class)
.block();
}
public UserRatings getFallbackUserRating(int userId) {
UserRatings userRatings = new UserRatings();
userRatings.setUserRatings(Arrays.asList(
new Rating("0", "fallback method of user rating", 0)
));
return userRatings;
}
}
Your Hixtrix port is 8081, so you need to use your endpoint on that 8081 port for metrics work.
E.G.
API-GATEWAY where Hystrix is used on port: 8081
USER-SERVICE port: 9001
Problem:
API-GATEWAY - http://localhost:8081/acturator/hystrix.stream
USER-SERVICE save user - http://localhost:9001/user/
Solution:
API-GATEWAY - http://localhost:8081/acturator/hystrix.stream
USER-SERVICE save user - http://localhost:8081/user/
Metrics now work well and the Hystrix Dashboard can work correctly.

Upload to S3 with resourceLoader - Spring Boot

I'm trying to upload a file to my s3 bucket without AWS SDK, using only Spring cloud with resourceLoader bean.
I have this code:
private fun uploadS3(awsFileName: String, content: String): String {
val writableResource = resourceLoader.getResource(awsFileName) as WritableResource
writableResource.outputStream.use { it.write(content.toByteArray()) }
return writableResource.url.toString()
}
My application.yml has this configuration:
cloud:
aws:
credentials:
accessKey: XXXXX
secretKey: XXXXX
instanceProfile: false
region:
static: us-east-1
auto: false
s3:
default-bucket: XXXXXX
My Spring Boot version is:
springBootVersion = '2.0.2.RELEASE'
But all I get is this error:
There is no EC2 meta data available, because the application is not running in the EC2 environment. Region detection is only possible if the application is running on a EC2 instance
And I just don't know how to solve this issue. Please, help me!
You could use Spring Content S3 which uses the SimpleStorageResourceLoader underneath the covers.
Add the following dependencies to your pom.xml
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>content-s3-spring-boot-starter</artifactId>
<version>0.1.0</version>
</dependency>
Add the following configuration that creates a SimpleStorageResourceLoader bean:
#Autowired
private Environment env;
public Region region() {
return Region.getRegion(Regions.fromName(env.getProperty("AWS_REGION")));
}
#Bean
public BasicAWSCredentials basicAWSCredentials() {
return new BasicAWSCredentials(env.getProperty("AWS_ACCESS_KEY_ID"), env.getProperty("AWS_SECRET_KEY"));
}
#Bean
public AmazonS3 client(AWSCredentials awsCredentials) {
AmazonS3Client amazonS3Client = new AmazonS3Client(awsCredentials);
amazonS3Client.setRegion(region());
return amazonS3Client;
}
#Bean
public SimpleStorageResourceLoader simpleStorageResourceLoader(AmazonS3 client) {
return new SimpleStorageResourceLoader(client);
}
Create a "Store":
S3Store.java
public interface S3Store extends Store<String> {
}
Autowire this store where you need to upload resource:
#Autowired
private S3Store store;
WritableResource r = (WritableResource)store.getResource(getId());
InputStream is = // your input stream
OutputStream os = r.getOutputStream();
IOUtils.copy(is, os);
is.close();
os.close();
When your application starts it will see the dependency on spring-content-s3 and your S3Store interface and inject an implementation for you therefore you don't need to worry about implementing it yourself.
HTH

New Functional Web Framework with jetty

I wanted to setup an example for New in Spring 5: Functial Web Framework
So I set up a RouteConfiguration:
#Configuration
public class RouteConfiguration {
#Autowired
private MyService myService;
#Bean
public RouterFunction<?> routerFunction() {
return route(
GET("/first")
, myService::getItemsFirst)
.and(route(
GET("/second")
, myService::getItemsSecond));
}
}
I started my application using jetty and at first it seemed to work... until I wanted to call one of my methods: localhost:8080/first and it returned a 404.
Did I define my route configuration wrong or why arent the routes accessible?
EDIT
With netty you need to provide a Server Configuration Like the following:
#Configuration
public class HttpServerConfiguration {
#Autowired
private Environment environment;
#Bean
public HttpServer httpServer(final RouterFunction<?> routerFunction) {
final HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);
final ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
final HttpServer server = HttpServer.create("localhost", Integer.valueOf(this.environment.getProperty("server.port")));
server.newHandler(adapter);
return server;
}
}
But I could not find something like this for jetty.
EDIT 2
My Dependencies:
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencyManagement {
dependencies {
dependency (group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '2.0.0.M1')
dependencySet (group: 'org.hibernate', version: '5.2.8.Final') {
entry 'hibernate-core'
entry 'hibernate-entitymanager'
entry 'hibernate-spatial'
}
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-hateoas')
compile('org.springframework.boot:spring-boot-starter-jetty')
compile('org.springframework.boot:spring-boot-starter-webflux') {
exclude module: 'spring-boot-starter-reactor-netty'
}
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-autoconfigure')
compile('org.springframework.boot:spring-boot-actuator')
compile('org.springframework.cloud:spring-cloud-starter-consul')
compile('org.springframework.cloud:spring-cloud-starter-consul-discovery')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('junit:junit')
}
Spring-Boot Version: 2.0.0.M3
Reading the comments, it seems this was an issue with dependencies bringing spring-boot-starter-web; if it is present, a Spring MVC application is started by Spring Boot.
There's a way to explicitly tell Spring Boot the type of the application, in the main Application class:
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AgentApplication.class);
application.setWebApplicationType(WebApplicationType.REACT‌​IVE);
application.run(args);
}

How to expose Hystrix Stream on Spring Actuator port?

I am using Jetty embedded server in the Spring Boot application.
To handle requests I provide my custom handler like that.
#Slf4j
#Configuration
#EnableWebMvc
#SpringBootApplication
public class Main {
public static void main(String... args) {
new SpringApplicationBuilder().sources(Main.class).run(args);
}
#Bean
public EmbeddedServletContainerCustomizer customizer(JettyRequestHandler myCustomHandler) throws MalformedURLException {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
customizeJetty((JettyEmbeddedServletContainerFactory) container);
}
}
private void customizeJetty(JettyEmbeddedServletContainerFactory jetty) {
jetty.addServerCustomizers((JettyServerCustomizer) server -> {
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.setHandlers(new Handler[]{myCustomHandler, server.getHandler()});
server.setHandler(handlerCollection);
});
}
};
}
}
I am listening for a requests on a standard 8080 port. I included also Spring Boot Actuator into my project to get some production endpoints (health, etc.). It starts on another port: 8181.
Additionally I am using Hystrix for circuit breaking purposes.
My question is how to enable Hystrix Stream to be exposed on actuator port?
Currently I managed only to expose it on standard port 8080 with following piece of code:
#Bean
public ServletRegistrationBean hystrixStreamServlet(){
return new ServletRegistrationBean(new HystrixMetricsStreamServlet(), "/hystrix.stream");
}
But I would like to expose it on another, to have the default one only for application purposes.
Those are some of my dependecies:
compile 'com.netflix.hystrix:hystrix-core:1.5.3'
compile 'com.netflix.hystrix:hystrix-metrics-event-stream:1.5.3'
compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.5.RELEASE'
I would like NOT to use Spring Cloud where is #EnableHystrix that gives the stream on the actuator port actually.
Actually I did what #m-deinum proposed and it worked. I used Spring Cloud Stack.
To achieve Hystrix Stream on actuator I added dependecies:
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.1.RELEASE' // spring cloud starter
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.1.3.RELEASE' // spring cloud hystrix starter
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-ribbon', version: '1.1.3.RELEASE' // spring ribbon starter
And the annotation on the Main class:
#EnableCircuitBreaker
#SpringBootApplication
public class Main {
public static void main(String... args) {
new SpringApplicationBuilder().sources(Main.class).run(args);
}
// ...
}

Resources