Spring Cloud Gateway is not routing (only 404) - spring

I have a Spring Cloud Gateway, a eureka service registry and two microservives. I have no problems with sending the request directely to the sevice with: http://localhost:8081/auth. When i want to use the Gateway http://localhost:8080/auth, i always get a 404 error response. The services and the gateway all connect to the eureka server.
Here is my code:
Gateway
application.properties:
server.port=8080
spring.application.name=gateway-service
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=auth-service
spring.cloud.gateway.routes[0].uri=lb://AUTH-SERVICE
spring.cloud.gateway.routes[0].predicates[0]=Path=/auth/**
spring.cloud.gateway.routes[1].id=songs-service
spring.cloud.gateway.routes[1].uri=lb://SONGS-SERVICE
spring.cloud.gateway.routes[1].predicates[0]=Path=/songs/**
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Main:
package htw.kbe_beleg
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cloud.netflix.eureka.EnableEurekaClient
#SpringBootApplication
#EnableEurekaClient
class GatewayApplication {
companion object {
#JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(GatewayApplication::class.java, *args)
}
}
}
Eureka
application properties:
server.port= 8761
spring.application.name=discovery-service
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
Main:
package htw.kbe_beleg
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer
#SpringBootApplication
#EnableEurekaServer
class EurekaServiceRegistration {
companion object {
#JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(EurekaServiceRegistration::class.java, *args)
}
}
}
Microservice
application.properties:
spring.datasource.url= jdbc:mysql://localhost:3306/authdb
spring.datasource.username= admin
server.port= 8081
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.show_sql= true
spring.jpa.hibernate.naming.implicit-strategy= org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy= org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.application.name= auth-service
eureka.client.service-url.defaultZone= http://localhost:8761/eureka/
Controller :
package htw.kbe_beleg.controller
import htw.kbe_beleg.model.Auth
import htw.kbe_beleg.repository.AuthRepository
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#RestController
class AuthController(val authRepository: AuthRepository) {
#PostMapping("/auth", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.TEXT_PLAIN_VALUE])
fun authenticateUser(#RequestBody authUser: Auth): ResponseEntity<String> {
...........
}
}
Main:
package htw.kbe_beleg
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cloud.netflix.eureka.EnableEurekaClient
#SpringBootApplication
#EnableEurekaClient
class AuthMain {
companion object {
#JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(AuthMain::class.java, *args)
}
}
}
I already gone through nearly every related stackoverflow question, changed a lot with no result and reverted it. I just cant see what am i missing. Hope you can help me.

Shame on me. Problem was that i had the wrong dependencies for my gateway. I only had spring-cloud-starter-config, but i needed spring-cloud-starter-gateway... Took me over a week.

Related

Can't get Spring AOP to log/print in my application

Like the title says, here are my classes (this is just for testing if I can AOP to work):
ApplicationContextConfiguration.kt
package com.fdev.jobby
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.EnableAspectJAutoProxy
#Configuration
#ComponentScan
#EnableAspectJAutoProxy
class ApplicationContextConfiguration {
#Bean
fun dataSource():DataSource{
return DataSource()
}
}
DataSource.kt
package com.fdev.jobby
open class DataSource {
fun getDataFromDB(): String{
// fetching Data
return "data was loaded"
}
fun insertDataToDB(){
// inserting Data
}
}
LoggingDataSource.kt
package com.fdev.jobby
import mu.KotlinLogging
import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.AfterThrowing
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.springframework.stereotype.Component
#Aspect
#Component
class LoggingDataSource {
private val logger = KotlinLogging.logger {}
#Pointcut("execution(* com.fdev.jobby.DataSource.*(..))")
fun getAllMethods(){}
#Before("getAllMethods()")
fun startOfAccessToDB(){
logger.error("Starting access to DB")
}
#AfterReturning("getAllMethods()")
fun endOfAccessToDBSuccess(){
println("Successful access to DB..")
}
#AfterThrowing("getAllMethods()")
fun endOfAccessToDBFailure(){
println("Failed access to DB..")
}
}
AOPController.kt
package com.fdev.jobby
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#RestController
#RequestMapping("aop")
class AOPController(#Autowired val dataSource: DataSource) {
#GetMapping("testing")
fun aopTesting(): String{
val data = dataSource.getDataFromDB()
return data
}
}
So when I call localhost:8080/aop/testing it shows the string "data was loaded" but it wont log or print my advices. What am I doing wrong? I have been reading here but the docs seem very unintuitive and not helpful at all. Please help :(
Edit: my gradle dependencies look like this
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-aop")
implementation("io.github.microutils:kotlin-logging-jvm:2.1.20")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

#RestControllerAdvice not scanned

i have looked at 5 tutorials; more than 10 stackoverflow or similar answers but i still haven't resolved that (apparently common) problem.
All i want to achieve is to set a custom JSON upon when exception are thrown on my API. But the Controller advice is never even instantiated (watch breakpoint never crossed.)
Here are the relevant files:
Main class:
package com.bancarelvalentin.plaxdmin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
#SpringBootApplication(scanBasePackages = ["com.bancarelvalentin.*"])
class Main
fun main(args: Array<String>) {
runApplication<Main>(*args)
}
Sample controller:
package com.bancarelvalentin.plaxdmin.controller
import com.bancarelvalentin.plaxdmin.playground.CustomException
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#RestController
#RequestMapping("/")
class DummyCtrl : PlController() {
#GetMapping
fun throwError(): ResponseEntity<Any> {
throw CustomException()
}
}
Error handler:
package com.bancarelvalentin.plaxdmin.playground;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice;RestControllerAdvice
import org.springframework.web.servlet.configjava.annotationlang.EnableWebMvc;Exception
#RestControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler
public Exceptionfun handleException(Exception ce: Exception): Exception {
return ce;
}
}
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/plaxdmin
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
Here is what i tried:
Adding other anotation to the main and/or the ErrorHandler class (#EnableWebMvc, #Component, some others i don't remember)
Putting the 3 above files in the same package
turning in and off the white label page
add a scanBasePackages attributes in my main class annotaation

How to integrate spring actuator in a non spring boot application?

i have a GWT application which is not a spring boot application and i managed to integrate actuator v1.5.9 with spring v4.3.3 with this configuration class.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricsChannelAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration;
import org.springframework.boot.actuate.endpoint.EndpointProperties;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
#Configuration
#Import({ EndpointWebMvcManagementContextConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
EndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class, MetricExportAutoConfiguration.class,
MetricFilterAutoConfiguration.class, MetricsChannelAutoConfiguration.class,
MetricsDropwizardAutoConfiguration.class, MetricRepositoryAutoConfiguration.class,
PublicMetricsAutoConfiguration.class,EndpointProperties.class, ManagementWebSecurityAutoConfiguration.class })
#PropertySource("classpath:Application.properties")
public class HealthCheckConfiguration {
#Bean
#Autowired
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
return new HealthMvcEndpoint(delegate, true);
}
}
Could you help me to do the same in spring actuator v2.3.1 and spring v5.2.7, because there are many classes not available in the new version.
Thanks,
This configuration worked for me (for a SpringMVC project with spring boot actuator)
#Configuration
#Import({
EndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
})
#EnableConfigurationProperties(CorsEndpointProperties.class)
class ActuatorConfiguration {
#Bean //taken from WebMvcEndpointManagementContextConfiguration.class
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath());
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(),
new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath()));
}
#Bean
DispatcherServletPath dispatcherServletPath() {
return () -> "/";
}
}
I did include
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>2.1.18.RELEASE</version>
</dependency>
for compatibility with the baseline Spring version I've been using (5.1.19.RELEASE)
The actuator endpoints are exposed with /actuator/*

Why does feign client timeout setting (as in documentation) not works?

I try to configure feign client connect timeout, as defined on official documentation, but it does not work.
The app is just simple demo app.
MyFeign.java
package com.example.demo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
#FeignClient(name = "myFeign", url = "localhost:9047/payroll", configuration = FeignConfig.class)
public interface MyFeign {
#GetMapping(value = "/api/master/{employee_id}")
ResponseEntity<String> disableMasterPayroll(#PathVariable(name = "employee_id") String employeeId);
}
1st attempt : using configuration class
FeignConfig.java
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import feign.Request;
#Configuration
public class FeignConfig {
public Request.Options feignRequestOptionsCustom() {
return new Request.Options(5000, 5000);
}
}
2nd attempt, using application.yml as defined on spring doc (copy-paste it)
feign:
hystrix:
enabled: false
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
But none works.
Found several question on stackoverflow (this, this), but none resolve it.
I did not use hystrix. This is my gradle file
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
I'm using boot 2.2 with spring cloud Hoxton.RELEASE.
Any idea how to solve this?

Hystrix Dashboard Issue in Spring Boot

I am new to Hystrix Dashboard. I have written sample application with Hystrix.
I want to see the Hystrix chart (command metric stream). But I am getting the below error:
Circuit: Unable to connect to Command Metric Stream
Thread Pools: Loading...
I am using STS with Maven.
Below is the code used:
Simple server microservice application (Spring boot web running in port 8085)
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
#SpringBootApplication
public class BookstoreApplication {
#RequestMapping(value = "/recommended")
public String readingList(){
return "Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)";
}
public static void main(String[] args) {
SpringApplication.run(BookstoreApplication.class, args);
}
}
Simple client microservice application (Spring boot web running in port 8095) I have included the dependency of Hystrix and Hystrix Dashboard along with Web, so all the Hystrix dependencies are in classpath
package hello;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
#Service
public class BookService {
private final RestTemplate restTemplate;
public BookService(RestTemplate rest) {
this.restTemplate = rest;
}
#HystrixCommand(fallbackMethod = "reliable")
public String readingList() {
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
}
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.web.client.RestTemplate;
#EnableHystrixDashboard
#EnableHystrix
#EnableCircuitBreaker
#RestController
#SpringBootApplication
public class ReadingApplication {
#Autowired
private BookService bookService;
#Bean
public RestTemplate rest(RestTemplateBuilder builder) {
return builder.build();
}
#RequestMapping("/to-read")
public String toRead() {
return bookService.readingList();
}
public static void main(String[] args) {
SpringApplication.run(ReadingApplication.class, args);
}
}
By running the above code, the hystrix is working fine, when the BooKStoreApplication is down, it is going to fallback method.
Both the urls are working fine.
Normal Case:
http://localhost:8085/recommended
Output: Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)
http://localhost:8095/to-read
Output: Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)
When BookStoreApplication is down (http://localhost:8085/recommended) accessing http://localhost:8095/to-read returns "Cloud Native Java (O'Reilly)" as expected.
But when I tried to invoke this url http://localhost:8095/hystrix, I am getting the Hystrix DashBoard Page and asking for the stream value.
I have tried given http://localhost:8095/ or http://localhost:8095/to-read, and clicked "Monitor Stream" and it is going to next page with error:
Circuit: Unable to connect to Command Metric Stream
Thread Pools: Loading...
I've experienced the same. The main problem was, that I didn't have the actuator dependency in my maven pom. So I could not get the hystrix stream.
Include the spring-boot-actuator.
Check if localhost:8085/health is running.
Try to enter localhost:8085/hystrix.stream to stream value in Hystrix Dashboard.
Execute the service few times -> the dashboard should show the monitored method/command.

Resources