What is the right approach for extending Spring Cloud Config Client? - java

I want to replace Basic Authentication for Spring Cloud Config Server with oAuth implementation. Let's leave Config Server alone for now and focus on changes for Config Client. Obviously I don't want to write my own implementation for whole thing, but instead execute my own logic and fallback on standard Config Client. Also I have to pack my changes into library since I will use it in multiple micro-services.
Long story short I want to achieve following:
1a. Create custom Starter which will contain Spring Cloud Config Client as dependency. Is it even doable or necessary?
or
1b. Create custom Starter with only my custom logic which will be executed before Spring Cloud Config Client. In this case each micro-service will have Spring Cloud Config Client and custom Starter as dependencies. How can I manage execution order and inject custom logic results into Config Client?
2.Introduce new bootstrap settings. e.g. spring.cloud.config.custom.username and spring.cloud.config.custom.password (Optional).
3.Introduce custom annotation for custom Starter. e.g. #enableCustomConfigClient (Optional).
I started with building custom Starter with following code in /resources/META-INF/spring.factories:
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.example.greeter.config.ConfigClientBootstrapConfiguration
But this code invoked after profile is set, not the first thing like Config Client does.
Any suggestions and especially code samples are appreciated. Thanks!

Posting approach I chose for future reference.
Create new package which will be executed on top of / before Spring Cloud Config Client. Two main features here:
Create file src/main/resources/META-INF/spring.factories with org.springframework.cloud.bootstrap.BootstrapConfiguration={YOUR_CLASS}
In {YOUR_CLASS} apply custom logic. Don't forget to use #org.springframework.core.annotation.Order({YOUR_PRECEDENCE}) and fact that Ordered.LOWEST_PRECEDENCE will be executed first
Build jar from previous step and include it into your project (as local file or via artifactory)
Add Custom logic to Spring Cloud Config Server so it can use JWT.
Working example is here: https://github.com/ka4ok85/spring-cloud-config-client-jwt

Related

How do I connect to multiple databases/instances in GCP Spanner using Spring Cloud GCP Starter?

I am currently building an application that connects to a database on Spanner. The end goal of the application is to be able to connect to multiple databases (and possibly instances) so it can pull data using a GraphQL implementation. I am currently using Spring Cloud GCP Starter and Spring Cloud GCP Starter Data Spanner Maven packages to handle the configuration and data mapping. The Spring Cloud GCP Starter asks me to set up these lines in application.properties:
spring.cloud.gcp.spanner.instance-id=blah
spring.cloud.gcp.spanner.database=blah
spring.cloud.gcp.project-id=blah
Currently the application is set up to have models for each table, a repository (using SpannerRepository), and a controller.
The issue is I haven't been able to figure out how to change the configuration from the initial values when the application is run. Has anyone run into this and figured it out, or is it a limitation of my current implementation in Spring Cloud GCP Starter and I should look for different solution?
What I have tried:
Tried finding someone with the same issue online, nothing similar that I can find currently
Tried looking how to use/change the low level implementations things like SpannerTemplate that the autoconfiguration creates, but wasn't able to figure out how to change/use them
Tried finding a way to change application.properties and reloading during runtime, but after some research this seemed like a horrible idea
Any help would be greatly appreciated, thank you!
I finally found it!
Documentation
By creating a custom bean for DatabaseIdProvider you can set the projectId, instanceId, and database to whatever you would like. Confirmed to be working the SpannerRepository implementation as well.
If anyone else finds this, here is an example of how you would create your own implementation (how you go about feeding it a new database name is up to you):
#Bean
public DatabaseIdProvider databaseIdProvider() {
return () -> DatabaseId.of("projectId", "instanceId", "databaseName");
}
The DatabaseIdProvider interface extends Supplier<DatabaseId>. DatabaseId has a method (of) that creates the new DatabaseId that you need to connect to a different database/instance.

External URL configuration in microservice

I have multiple microservices which communicates with each other through REST calls.
I have used spring boot and spring rest and have configured the URLS of the rest end points in application.properties file.
Now the problems is if the URL for one end point changes then I to have to manually modify all the property files of the services which are calling that particular end point which has got changed.
Is there a workaround for this so that the URLS can be somehow placed in a centralized location so that any modification does not impacts the other services which are using it.
You can use spring-cloud to achieve this. Usual way used in spring-cloud is by configuring the required properties in a git repo. And then those properties can be accessed by any micro-service you want with minimal configurations. You can refer projects in this repo
limits-services acts as a client that needs certain properties those are configured in spring-cloud-config-server. Hope this helps.
In case with microservices you can use Spring Cloud Config (Spring Cloud Config, Spring Cloud Config Server). It's very usefull and you can update your configuration at runtime.
Spring Cloud Config provides server and client-side support for externalized configuration in a distributed system. With the Config Server you have a central place to manage external properties for applications across all environments. The concepts on both client and server map identically to the Spring Environment and PropertySource abstractions, so they fit very well with Spring applications, but can be used with any application running in any language. As an application moves through the deployment pipeline from dev to test and into production you can manage the configuration between those environments and be certain that applications have everything they need to run when they migrate.
As others have mentioned you can use Spring Cloud Config Server to remotly load your application configuration. All you need is git repository containing your configuration.
Spring cloud configuration supporst Git, database as your store for configuration.
Idea is to create an spring-boot app that can provide configuration to other applications.
#SpringBootApplication
#EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
You can configurae port and provide your git repository using key spring.cloud.config.server
server.port: 8888
spring.cloud.config.server.git.uri: file://${user.home}/config-repo
At client side, if you have spring-config in your classpath, application will try to connect to an application runnign at port 8888 to retrieve configuration.
More information can be found here.
may put configuration inside a database.
after that need have one centralize cache service that used by other services, can be .jar service,
then the values can be load inside a cache class in this service,
then in the front end side need have update button for updating the cache after modify the URL value in the database, so then all impacted services can use new value.
and also to be easier may have stand alone UI for update those configuration rather than updating database directly.
You can use Microconfig.IO to manage your service configuration and it's placeholders functionality to reference configuration values of certain services from others. So in your case you configure your deploy url in your server and put placeholders on it in your clients. This allows you to edit value only in one place and then everyone who depend on it will get it automatically.

Is it possible to resolve REST end points of a dependency JAR file in spring Boot

I have a 2 Spring boot jars which work fine as 2 independent applications, however, I have been asked to merge 2 jars into a single application
The easiest thing I thought would be to add app-2 as a maven dependency into app-1 but the problem is that when the app-1 starts it only recognises the app-1 REST endpoints but ignores REST endpoint of app-2 altogether.
I was hoping that when the app-1 starts it will automatically pick the endpoints declared in app-2
#RestController
Class2{
#GetMapping(/hello-from-app2)
public String myapp2(){
return "HELLO FROM APP2"
}
This code gets ignored and at server start up I can only see the endpoints exposed by app-1 are visible.
If you are including App2.jar as a dependency into App1.jar, the best approach would be to import the Configuration of App2. If you start adding scans and stuff you would be tightly coupling you App1 to your App2. App1 would have to know implementation details of App2 that doesn't need to.
If you just import the configuration of App2, the configuration details would remain encapsulated.
I assume you have a Java Config class (or an XML Config file) for App1 and another one for App2. I also assume that the config of App2 contains all the necessary annotations for component scanning and the correct base-packages.
If that's the case, you can add an import like this and it should work right away:
#Configuration
#Import(SpringConfigurationApp2.class)
public class SpringConfigurationApp1 {
//... some beans....
}
I saw answer provided by #Diego but with that user need to make changes in client application. (ex. #Import(SpringConfigurationApp2.class) here).
I have another approach where client (App-1) does not need to make any change in application. It will just work seamlessly. This approach is by use of spring's auto configuration and same feature is used by spring-boot dependency.
Here is my answer to achieve using autoconfiguration.
For App-2,
1) create spring.factories file under resources/META-INF
2) Add org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
path-to-app-2-application/SpringConfigurationApp2
For App-1,
Just include App-2 as maven dependency and you are Done.
Here is a link to get more information about https://dzone.com/articles/what-is-spring-boot-auto-configuration.

Dynamic scheduling with properties in spring boot

I am using Spring Boot and have an issue related #Scheduled.
#Scheduled(fixedRateString = "${timeRate}")
public void SaveStatisticData() {
System.out.println("save Info per "+timeRate+"msec");
...
}
this is a part of my code. this method will run by timeRate value from properties and Properties File is connected with web API.
That means someone can request to change timeRate in Properties File.
I want to know how to reload spring boot on client request which change Properties File.
AFAIK, I can use Hot Swapping in spring boot.
But I think this solution is not good to service.
Because The service have to reload whenever a client request changing timeRate.
In other words, is there another way to apply this client request in real time?

Ease of rolling back from Spring Boot to regular Spring and viewing hybrid of Spring Context and configurations while using Spring Boot

I am assessing whether spring-boot and how I could migrate to using it.
One question I have is whether a project that uses spring boot can be converted easily back to a regular spring project which uses the traditional spring configuration files if that is required. This would be useful in my mind for a few reasons.
1) merging with legacy projects, because as I have read moving from legacy spring to spring-boot is somewhat tedious.
2) Obtaining a view of the spring application context file and webapp configuration files to understand what the actual configurations being used are.
Another question I have is regarding the lack of application-context file, is there a way to have some kind of hybrid where there is still an application-context file that can be seen? Part of my concern is that spring-boot auto configures components without us knowing and learning how they are configured and work together.
Spring Boot provides auto-configuration.
When #SpringBootApplication is encountered, it triggers a search of the CLASSPATH for a file called META-INF/spring.factories which is just a regular text file that enumerates a list of Java configuration classes. Java configuration was introduced in 2006 and then merged into Spring 3 in 2009. So it's been around for a long time. These Java configuration classes define beans in the same way that XML does. Each class is annotated with #Configuration and therein you find beans defined using methods (factory methods, or provider methods) whose return value is managed and exposed via Spring. Each such provider method is annotated with #Bean. This tells Spring to consider the method and its return value the definition of the bean.
Spring Boot tries to launch all the Java configurations it sees enumerated in that text file. It tries to launch RabbitAutoConfiguration.class, which in turn provides beans for connecting to RabbitMQ and so on. Of course, you don't want those beans in certain cases, so Spring Boot takes advantage of Spring framework 4's #Conditional mechanism to conditionally register those beans only if certain conditions are met: is a type on the CLASSPATH, is a property exposed through the environment, has there been another bean of the same type defined by the user, etc. Spring boot uses these to only create the RabbitMQ-specific beans if, for example, the dependencies that you would get from org.springframework.boot:spring-boot-starter-amqp are on the CLASSPATH. It also considers that the user may have provided a different implementation of RabbitTemplate in some othe rbean definition (be it XML or Java configuration) so it uses that if it's there.
These java configuration classes are the same sort of Java configuration classes you would write without Spring Boot. BUT... WHY? 80% of the time, the auto-configuration that Spring Boot provides is going to be as good or better than the configuration you would write yourself. There are only so many ways to configure Spring MVC, Spring Data, Spring Batch, etc., and the wager you take using Spring Boot is that the leaders and engineers on those various projects can provide the most sensible 80%-case configuration that you probably don't care to write, anyway.
So, yes you could use Spring Boot with existing applications, but you'd have to move those existing applications to Spring 4 (which is easy to do if you're using the spring-boot-starter-* dependencies) to take advantage of #Conditional. Spring Boot prefers: NO configuration, Java configuration, XML configuration, in that order.
If you have an existing application, I'd do the following:
find out what dependencies you can remove from your Gradle/Maven build and just have taken care of for you with the various spring-boot-starter- dependencies.
add #SpringBootApplication to a root component class. Eg, if your package is a.b.c, put a class Application in a.Application and annotate that with #SpringBootApplication
You can run it as a Servlet 3 application or in an embedded servlet container. It might be easier to just run in a standard servlet container as you take baby steps. Go to http://start.spring.io and make sure to choose war in the packaging drop down. Copy the ServletInitializer class and the specification from the pom.xml to ensure that your application is a .war, not a .jar. Then, once everything works on Spring Boot, rm -rf the Initializer and then revert the Maven build to a simpler .jar using the Spring Boot plugin for extra win.
If your application has lots of existing configuration, import it using #Import(OldConfig.class) or #ImportResource("old-config.xml") on the a.Application configuration class. The auto-configuration will kick in but it will see, for example, that you may have already defined a DataSource so it'll plug that in in the right places. What I like to do now is just start the application up, see if everything's OK, then start removing code in my old Java or XML configuration. Don't remove your business code, but remove things related to turning on parts of Spring. Anything that looks like #Enable..* or ..:annotation-driven/>. Restart and verify things still work. The goal is to let Spring Boot do as much of the heavy lifting as possible. Viewing the source is very handy here so you can see what Spring Boot will try to do for you. Spring Boot will log information on what #Conditional conditions evaluated to true for you. Simply provide --Ddebug=true wen you start the application to see the output. You could also export DEBUG=true then restart your IDE to run it as long as the environment variable is ivsible in your shell.

Categories