How to externalise spring application configuration using config server? - spring

I have written my own config server to centralise configuration management and exposing application configuration using APIs. Now i want to consume configuration properties from spring and spring-boot applications. But i am not able to figure out the correct way for that. I tried placing my config server client code to listen for application context start event and reading configuration from config server. But i am not able to inject this configuration into other beans.
How can i read application configuration from config server (using rest client) and inject this read config to application environment for further processing ?

create an application that for config and your application
#SpringBootApplication
#EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(ConfigApplication.class, args);
}
}
application.yml in config server
spring:
cloud:
config:
server:
native:
search-locations: classpath:/shared
profiles:
active: native
security:
user:
password: pass
server:
port: 8888
and create shared folder in src/main/resources/shared and your-service.yml like this
spring:
security:
basic:
enabled: false
server:
port: 8083
maven pom.xml in config server
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>config</finalName>
</configuration>
</plugin>
</plugins>
</build>
in your-service spring boot application add /src/main/resources/bootstrap.yml
like this
spring:
application:
name: your-service
cloud:
config:
uri: http://localhost:8888
fail-fast: true
username: user
password: pass
and add this dependency to your service
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
first run config server and then your spring boot service

Related

Spring Gateway deployment on external tomcat

I am developing a micro-service solution with spring boot. I have 3 micro services:
authentication services (manages users, roles, permissions)
project service (services used to do CRUD actions on Project entity)
gateway (used as entry point of the solution, also used to validate token and route to services)
I want to deploy these on a tomcat server as war placed in the webapps (knowing that they have an embedded tomcat).
I am able to deploy the authentication and project services and they are reachable over the URI ** localhost:8080/authentication/* ** and ** /project/* *, but I'm unable to reach the gateway on ** localhost:8080/gateway/ **, it returns a 404
I am sure that the gateway is running cause it created the tables in the DB after deployment.
**Knowing that in eclipse it works fine (since i run each service on a port specified in the application.yml) i don't understand why when deploying the gateway on tomcat it is unreachable and always return a 404. I can see it in the tomcat manager as deployed.
Here is the pom.xml of the gateway :
<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.4.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.myProject</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>Gateway</description>
<profiles>
<profile>
<id>dev</id>
<properties>
<activatedProperties>dev</activatedProperties>
</properties>
</profile>
</profiles>
<properties>
<java.version>11</java.version>
<springfox-swagger.version>3.0.0</springfox-swagger.version>
</properties>
<dependencies>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</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>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
and here is my application.yml file :
spring:
cloud:
gateway:
routes:
- id: incident
uri: http://localhost:8080/project
predicates:
- Path=/gateway/v1/project/**
filters:
- RewritePath=/gateway/(?<RID>.*), /$\{RID}
- JwtFilter
- RemoveRequestHeader=Authorization
- id: authentication
uri: http://localhost:8080/auth
predicates:
- Path=/gateway/auth/**
filters:
- RewritePath=/gateway/(?<RID>.*), /$\{RID}
- id: userManagement
uri: http://localhost:8080/auth
predicates:
- Path=/gateway/v1/user/**,/gateway/v1/permission/**,/gateway/v1/role/**,/gateway/v1/role-permission/**,/gateway/v1/user-role/**
filters:
- RewritePath=/gateway/(?<RID>.*), /$\{RID}
- JwtFilter
- RemoveRequestHeader=Authorization
datasource:
url: jdbc:oracle:thin:#//xxx.xxx.xxx.xxx:1521/ORCLCDB.localdomain
username: xxx
password: xxx
jpa:
hibernate:
ddl-auto: update
id:
new_generator_mappings=true
properties:
hibernate:
temp:
use_jdbc_metadata_defaults: false
dialect: org.hibernate.dialect.Oracle12cDialect
physical_naming_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true
logging:
level:
org.springframework: TRACE
org.hibernate.type: TRACE
org.apache.tomcat: INFO
org.apache.catalina: INFO
pattern:
console: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
file: "%d %p %c{1.} [%t] %m%n"
file:
name: ./tomcat-logs/gateway
path: ./tomcat-logs/gateway
Spring Cloud Gateway is itself a Spring Boot WebFlux application. It's support only embedded server. Please check spring documentation
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-traditional-deployment

Getting "Whitelabel Error Page" when opening "/actuator/mappings"

I'm new to spring-cloud, so i'm using a video tutorial, so to read exposed endpoints i tried to open http://localhost:8000/actuator/mappings but it didn't work for me but the /health url gave me the following result:
Here's the written code in the client side:
#RefreshScope
#RestController
class MessageRestController {
#Value("${message}")
private String message;
#RequestMapping("/message")
String getMessage() {
return this.message;
}
}
pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.spring.cloud.reservation</groupId>
<artifactId>reservation-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reservation-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
Here's the client project structure:
bootstrap.properties:
spring.application.name=reservation-service
spring.cloud.config.uri=http://localhost:8888
When executing the Post on the /refresh gives me : Error Not Found
curl -d {} http://localhost:8000/actuator/refresh
spring-boot-actuator jar version = 2.1.3
Default management.endpoints.web.exposure.include, info, health
As actuator/health and actuator/info are provided by default you will get the information
management.endpoints.web.exposure.include = * //will allow all endpoints
to be exposed
management.endpoints.web.exposure.include=health,info # Endpoint IDs that should be included or '*' for all.
management.endpoints.web.exposure.exclude= # Endpoint IDs that should be excluded or '*' for all.
management.endpoints.web.base-path=/actuator # Base path for Web endpoints. Relative to server.servlet.context-path or management.server.servlet.context-path if management.server.port is configured.
management.endpoints.web.path-mapping= # Mapping between endpoint IDs and the path that should expose them.
Securing Endpoint
management.endpoint.health.roles= # Roles used to determine whether or not a user is authorized to be shown details. When empty, all authenticated users are authorized. //for health
management.endpoint.health.show-details=always,never # When to show full health details.
Enable/Disable endpoints
management.endpoint.(endpointName).enabled=true # Whether to enable the health endpoint.
e.g. management.endpoint.health.enabled=true
Spring boot 2.x :: only health and info is exposed by default, In order to expose other actuator endpoint either mention the endpoint name or add following line to expose all endpoints.
management.endpoints.web.exposure.include: '*'
Starting from Spring boot 2.x, all actuator endpoints are disabled except for few non sensitive endpoints like /health and /info by default.
You can enable them using property management.endpoints.web.exposure.include=*
Please refer here for more details.
you have to do post, e.g
curl -X POST xxx.yyy.zzz:8080/actuator/refresh

Trouble when changing Spring Boot version from 2.0.3.RELEASE to 2.1.0.BUILD-SNAPSHOT

I have a working code that stops to work when I change Spring Boot version from 2.0.3.RELEASE to 2.1.0.BUILD-SNAPSHOT.
Sometimes the error is:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'dataSource', defined in BeanDefinition defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
or ...
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-07-16 14:38:18.509 ERROR 604 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'scopedTarget.oauth2ClientContext', defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration$SessionScopedConfiguration$ClientContextConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/security/oauth2/config/annotation/web/configuration/OAuth2ClientConfiguration$OAuth2ClientContextConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
Both cases it is related to duplicated bean os something else.
My POM dependencies are:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version>
<maven.test.skip>true</maven.test.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
and the config:
server:
error:
include-stacktrace: always
whitelabel:
enabled: false
servlet:
session:
cookie:
name: HYDRASSESSION
port: 36205
management:
endpoints:
web:
exposure:
include: "*"
security:
basic:
enabled: false
oauth2:
client:
clientId: atlas
clientSecret: secret
accessTokenUri: http://myserverip:36202/oauth/token
userAuthorizationUri: http://myserverip:36202/oauth/authorize
resource:
userInfoUri: http://myserverip:36202/user/me
spring:
jpa:
properties:
hibernate:
temp:
use_jdbc_metadata_defaults: false
hibernate:
ddl-auto: validate
application:
name: atlas
datasource:
password: admin
username: postgres
url: jdbc:postgresql://myserverip:36211/atlas?ApplicationName=Atlas
guardiao:
logout:
path: http://myserverip:36202/exit
It run fine when using 2.0.3.RELEASE but I'm receiving an error when try to login myserver:36202/oauth/authorize?client_id=: by browser complains ERR_TOO_MANY_REDIRECTS but I have no errors in console. Anyway... my question is: how can I update my SpringBoot to 2.1.0.BUILD-SNAPSHOT ?
So as the exception reports, there are two beans of the same type. Historically Spring would override one bean with the other. That has long been an annoyance since you could get hard to find bugs where a second bean with a completely different type, but with the same bean ID would make your first bean vanish.
Spring Boot 2 now disables that sort of bean overriding by default. You can re-enable it by setting the following property in your application.yml:
spring.main.allow-bean-definition-overriding: true
This re-enables the previous behavior. It doesn't address the root cause that beans are being overridden though and also mean you won't get the benefit of bean override errors. Upgrading the underlying libraries will hopefully clean this up over time.
As noted on other comments, upgrading the spring-security-oauth2-autoconfigure dependency to org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.0.RELEASE may solve it for you.
This answer might be helpful for those who have setup a new project and adding the spring.main.allow-bean-definition-overriding: true property in application.properties file is not fixing the issue:
Check the folder where your application.properties file is present -- it needs to be available directly under the directory: src/main/resources/ and not anywhere else.

Spring Eureka Dashboard not loading

I am trying to start a Eureka server with basic configuration, but the dashboard will not load. Instead, below XML is being returned.
Please find the below code.
In application.yml file
eureka:
environment: qa
client:
fetch-registry: false
register-with-eureka: false
dashboard:
enabled: true
spring:
application:
name: eureka-server
server:
port: 8761
Main class:
#SpringBootApplication
#EnableEurekaServer
public class MygynecologistEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(MygynecologistEurekaServerApplication.class, args);
}
}
pom.xml
<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>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
A similar question was asked here, but it was resolved. Thanks for reading.
UPDATE: with the same code I was able to load the eureka dashboard in a windows machine(windows 8.1) . But it doesn't work in my macbook pro. Should we have to add any extra settings for mac?
I had the same problem. To fix it I had to do the following:
Delete the resources/static and resources/templates folders.
Clean the project by deleting the build (or out) directory.
Once you restart the eureka service, you should be able to see the Eureka dashboard.
I finally found a solution. The issue has got something to do with the versions I used. I made the following changes to fix it
Updated the spring-boot-starter-parent version to 2.0.2.RELEASE
Updated the spring-cloud-dependencies version to Finchley.RC1
Changed the Artifact ID to spring-cloud-starter-netflix-eureka-server

Camel ActiveMQ + Spring boot not reading spring activemq configurations

I am trying a very simple route with Spring Boot 1.5.2.RELEASE + Camel (Spring Boot Starter) + ActiveMQ, which is to read from a particular queue and then log it. However, it looks like it is not picking up my spring.activemq configuration for URL as I see in the log it is trying to connect to a different url and it continues to connect it and the my spring boot app never starts. The questions are based on my configuration that I am providing below how can I do the below:
Fix the configuration to allow spring's activemq configuration
Configure maxReconnectAttempts so that it does not try to connect forever if the URL is not reachable, which could be possible if the ActiveMQ instance goes down
Any assistance would be greatly appreciated. I did search relevant questions on stackoverflow but none gave me a solution to the issue I am facing
Error I am seeing on the console and this continues to like 60-70 attempts and counting. As you can see the broker URL that camel is picking up is some default URL that probably spring has configured by default
Failed to connect to [tcp://localhost:61616] after: 10 attempt(s) continuing to retry.
Here are my current configurations / code:
pom.xml - relevant portion
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud is part of the project where I am configuring camel routes -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-dependencies</artifactId>
<version>2.19.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- I have this as the same project works as a web app as well
and therefore I do not need the
camel.springboot.main-run-controller=true configuration to be set
which is as per camel's spring boot documentation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Camel - start -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-camel</artifactId>
</dependency>
<!-- Camel - end -->
</dependencies>
application.yml (Spring Boot ActiveMQProperties)
spring:
activemq:
brokerUrl: tcp://my.company.host:[port] //This port is up and running
user: user
password: password
Camel Route in JAVA
package com.mycamel.route;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class SampleAmqCamelRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:some.queue").to("log:com.mycamel.route?level=INFO&groupSize=10");
}
}
First you should add the spring-boot-starter-activemq dependency to your pom.xml. Then you can use its AutoConfiguration capabilities which will create a ConnectionFactory based on the properties you have specified in your application.yml.
After that you have to configure Camel's ActiveMQComponent too. If you would like to reuse the ConnectionFactory (which created by the autoconfig) then it can be achievable with the following:
#Configuration
public class ActiveMQComponentConfig {
#Bean(name = "activemq")
public ActiveMQComponent createComponent(ConnectionFactory factory) {
ActiveMQComponent activeMQComponent = new ActiveMQComponent();
activeMQComponent.setConnectionFactory(factory);
return activeMQComponent;
}
}
You can find more information in Camel's ActiveMQ documentation.

Resources