Spring Cloud Feign/Ribbon with corporate proxy - spring-boot

I want to consume a REST service from the outside world behind a corporate proxy with authentication.
How do I configure Spring Boot + Spring Cloud Feign/Ribbon to use our proxy?

I believe you're looking for something like this:
import feign.Feign;
import okhttp3.OkHttpClient;
import java.net.InetSocketAddress;
import java.net.Proxy;
...
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy-url", 1234));
OkHttpClient okHttpClient = new OkHttpClient.Builder().proxy(proxy).build();
Feign.builder()
.client(new feign.okhttp.OkHttpClient(okHttpClient))
.target(...);
You just have to additionally add compile 'io.github.openfeign:feign-okhttp:9.5.0' to your project.
The target clause contains your defined Interface. Further reference: https://github.com/OpenFeign/feign

Turns out there is actually a much easier solution.
The following information will be helpful (also for more advanced use cases):
Spring Cloud Commons HTTP Factories
Overriding Feign Defaults
OpenFeign Client can run with several HTTP Clients.
By default it uses java.net.URLConnection, but you can also use ApacheHttpClient or OkHttpClient.
Using Apache Http Client
Here is what you can do to set a proxy using ApacheHttpClient:
Add the following two dependencies to your pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Dependency to switch HttpClient implementation from java.net.URLConnection to Apache HTTP Client -->
<!-- See also: FeignAutoConfiguration for details. -->
<!-- See also: https://cloud.spring.io/spring-cloud-commons/reference/html/#http-clients -->
<!-- See also: https://cloud.spring.io/spring-cloud-openfeign/reference/html/#spring-cloud-feign-overriding-defaults -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
In your app expose the following bean:
// see: https://cloud.spring.io/spring-cloud-commons/reference/html/#http-clients
#Bean
public HttpClientBuilder proxiedHttpClient() {
String proxyHost = "client-envoy";
Integer proxyPort = 80
String proxyScheme = "http";
return HttpClientBuilder.create()
.setProxy(new HttpHost(proxyHost, proxyPort, proxyScheme));
}
That's it - nothing else needs to be configured in application.yaml since ApacheHttpClient will be used by default, if it is on the classpath.
Using Ok Http Client
To set a proxy using OkHttpClient you do a similar thing:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
In your application.yml make sure to enable OkHttpClient and disable ApacheHttpClient:
spring:
cloud:
httpclientfactories:
ok:
enabled: true
apache:
enabled: false
feign:
okhttp:
enabled: true
httpclient:
enabled: false
Instead of HttpClientBuilder expose a bean of type OkHttpClient.Builder.

Spring cloud feign supports three underlying implementations:
Default
Apache HttpClient
OkHttpClient
If using Default:
Create this spring bean (say by defining inside class with #Configuration annotation), no changes required in application properties/yml:
#Bean
public Client feignClient() {
return new Client.Proxied(
null, null, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
}
If using Apache HttpClient:
that means you have feign.httpclient.enabled: true in application.yml and below in your pom.xml or build.gradle:
pom.xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
build.gradle
implementation 'io.github.openfeign:feign-httpclient'
Create this spring bean (say by defining inside class with #Configuration annotation):
#Bean
public CloseableHttpClient feignClient() {
return HttpClientBuilder.create().setProxy(new HttpHost(proxyHost, proxyPort)).build();
}
If using OkHttpClient:
that means you have feign.okhttp.enabled: true in application.yml and below in your pom.xml or build.gradle:
pom.xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
build.gradle
implementation 'io.github.openfeign:feign-okhttp'
Create this spring bean (say by defining inside class with #Configuration annotation):
#Bean
public OkHttpClient feignClient() {
return new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
.build();
}

Related

Spring boot/Cloud Eureka DiscoveryClient - No such host is known

I want to try out microservices using 2.5.4 spring boot and getting problems in discovery
Steps:
Created a RestTemplate with #LoadBalanced, and trying to call the service using "application name in the url"
I have the register and fetch registry true in properties (thought this should get available services?)
Error: No such host is known
I am trying
#Autowired
private DiscoveryClient discoveryClient;
and do
discoveryClient.getInstances("myappservice-name").forEach((ServiceInstance s) -> {
System.out.println(ToStringBuilder.reflectionToString(s));
});
But all examples tell to use an endpoint? or commandLineRunner. Both I'm looking for auto loading
https://spring.io/guides/gs/service-registration-and-discovery/
https://spring.io/blog/2015/01/20/microservice-registration-and-discovery-with-spring-cloud-and-netflix-s-eureka
Not ok to call the below for each app
#RequestMapping("/service-instances/{applicationName}") public
List<ServiceInstance> serviceInstancesByApplicationName(
How can I register automatically?
EDIT: More detailed question
(1) SERVICE App
bootstrap - spring.application.name=pluralsight-toll-service
application props
server.port=0
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.instance.instance-id=${spring.application.name}:${random.int}
eureka.instance.hostname=localhost
management.endpoints.web.exposure.include=*
App
#SpringBootApplication
#EnableEurekaClient
public class PluralsightEurekaTollrateServiceApplication {
I see app registered in eureka server
(2) Client
bootstrap
spring.application.name=pluralsight-tollrate-billboard
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
application.props
server.port=8081
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
management.endpoints.web.exposure.include=*
App
#SpringBootApplication
#EnableEurekaClient
#EnableDiscoveryClient
public class PluralsightEurekaTollrateBillboardApplication {
Controller
#LoadBalanced
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
#Autowired
RestTemplate restTemplate;
#RequestMapping("/dashboard")
public String GetTollRate(#RequestParam int stationId, Model m) {
TollRate tr = restTemplate.getForObject("http://pluralsight-toll-service/tollrate/" + stationId, TollRate.class);
Error: pluralsight-toll-service host is unknown
How can call service from client using the name
Fixed the issue
Its all down to client project dependencies
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
<version>3.0.3</version>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
The names are too close
spring-cloud-netflix-eureka-client is wrong
spring-cloud-starter-netflix-eureka-client is the right one
Because of the wrong one, I had to add eureka-client of com.netflix.eureka. Cleaned all and it works
(Also I was missing spring-cloud , as I was creating from eclipse. In future will use initializ only )

Configure HTTPS in Spring Boot Apache Camel REST API with keystore having multiple certs using camel-jetty component

I am trying to configure https in my apache camel Spring Boot REST application (using apache-camel v3.11.1, springboot v2.5.3) with keystore having multiple certificates.
Problem:
Application run failed
org.apache.camel.RuntimeCamelException: java.lang.IllegalStateException: KeyStores with multiple certificates are not supported on the base class org.eclipse.jetty.util.ssl.SslContextFactory. (Use org.eclipse.jetty.util.ssl.SslContextFactory$Server or org.eclipse.jetty.util.ssl.SslContextFactory$Client instead)
at org.apache.camel.RuntimeCamelException.wrapRuntimeCamelException(RuntimeCamelException.java:51) ~[camel-api-3.11.1.jar:3.11.1]
Project setup:
pom.xml: (dependencies only, to show that I am not using spring-boot-web-starter)
..
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jetty-starter</artifactId>
</dependency>
..
..<!-- all other required dependencies are in place-->
..
</dependencies>
..
application.properties
#camel.component.jetty.keystore=keystore-with-one-certificate.jks # WORKS
camel.component.jetty.keystore=keystore-with-multiple-certificates.jks # DOESN'T WORK
camel.component.jetty.ssl-key-password=password
camel.component.jetty.ssl-password=password
Rest Route:
restConfiguration()
.component("jetty")
.scheme("https")
.port("8080");
rest()
.path("/api")
.get("/{name}")
..
..
.to("direct:x");
Looked at answers in the below posts, but still not able to resolve the exception that I get,
https://stackoverflow.com/a/60598953/6363894,
https://stackoverflow.com/a/55499113/6363894
I know that exception clearly states to use org.eclipse.jetty.util.ssl.SslContextFactory$Server, but I don't understand how/where to use SslContextFactory.Server object.
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStoreResource(findKeyStorePath());
sslContextFactory.setKeyStorePassword("password");
sslContextFactory.setKeyManagerPassword("password");
sslContextFactory.setNeedClientAuth(true);
Also I've created a bean for sslContextParameters and added that to restConfiguration as below, this time application runs successfully but then when I test, SSL handshake fails.
restConfiguration()
.component("jetty")
.endpointProperty("sslContextParameters", "#sslContextParameters")
.scheme("https")
.port("8080");
#Bean(name = "sslContextParameters")
public SSLContextParameters setSSLContextParameters() {
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("keystore-with-multiple-certificates.jks");
ksp.setPassword("password");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("password");
SSLContextServerParameters scsp = new SSLContextServerParameters();
scsp.setClientAuthentication("REQUIRE");
SSLContextParameters scp = new SSLContextParameters();
scp.setServerParameters(scsp);
scp.setKeyManagers(kmp);
return scp;
}
Any help on how to configure SslContextFactory.Server object with the restConfigurations() or any other way I can achieve this? I'll update the post, if any more details are required.

Load balancer does not contain an instance for the service

I want to use Eureka client with spring-cloud-starter-loadbalancer. But when I added configuration I get error. When I remove #LoadBalancerClient(name = "mail-service", configuration = LoadBalancerConfiguration.class) and LoadBalancerConfiguration class configuration it's working fine. I tried this code:
#FeignClient(name = "mail-service")
#LoadBalancerClient(name = "mail-service", configuration = LoadBalancerConfiguration.class)
public interface EmailClient {
#RequestMapping(method = RequestMethod.POST, value = "/engine/emails/register")
void setUserRegistration(CreateUserDTO createUserDTO);
}
#Configuration
public class LoadBalancerConfiguration {
#Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withBlockingDiscoveryClient()
.withSameInstancePreference()
.withHealthChecks()
.build(context);
}
}
application.yml:
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
fetchRegistry: true
healthcheck:
enabled: true
instance:
preferIpAddress: true
leaseRenewalIntervalInSeconds: 10
POM.xml dependencies
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
I get this error when I have only one target service.
[503] during [POST] to [http://mail-service/engine/emails/register] [EmailClient#setUserRegistration(CreateUserDTO)]: [Load balancer does not contain an instance for the service mail-service]
I use Release Train Version: 2020.0.3
Do you know what could be the problem?
Any application using load balancer should follow the below order
Start the Eureka Server
Start the instances of the Service one by one which have dependency
Any application relies on information from a service registry (i.e. Eureka). Until the application is registered with it's instances by the serviceId , the Eureka server will not be able to pick that instance while load-balancing.
In the code you have shared, the bean ServiceInstanceListSupplier is created in the configuration class along with the health checks ( .withHealthChecks() ). This is causing the application to fail as service has not been registered yet.
Also, the LoadBalancer config should not be in a #Configuration-annotated class instead, it should be a class passed for config via #LoadBalancerClient or #LoadBalancerClients annotation, as described here.
The only bean you need to instantiate is the ServiceInstanceListSupplier (if you add spring-cloud-starter-loadbalancer, LoadBalancerClientFactory etc. will be instantiated by the starter itself).
So your LoadBalancer configuration class should look like code below.
It should not be in the #Configuration class:
public class LoadBalancerConfiguration {
#Bean
public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
As explained in this link, the actual #Configuration class , will have the following annotation: #LoadBalancerClients(defaultConfiguration = LoadBalancerConfiguration .class).
Then, if you enable health-checks in the instances, it should work without any issues.

How to configure Springboot's 2.x actuator in Spring Application(not a SpringBoot Application)? [duplicate]

I've been working on a Spring/Spring MVC application and I'm looking to add performance metrics. I've come across Spring Boot Actuator and it looks like a great solution. However my application is not a Spring Boot application. My application is running in a traditional container Tomcat 8.
I added the following dependencies
// Spring Actuator
compile "org.springframework.boot:spring-boot-starter-actuator:1.2.3.RELEASE"
I created the following config class.
#EnableConfigurationProperties
#Configuration
#EnableAutoConfiguration
#Profile(value = {"dev", "test"})
#Import(EndpointAutoConfiguration.class)
public class SpringActuatorConfig {
}
I even went as far as adding #EnableConfigurationProperties on every configuration class as suggested on another post on StackOverflow. However that didn't do anything. The endpoints are still not being created and return 404s.
First let's clarify that you cannot use Spring Boot Actuator without using Spring Boot.
I was wrong about not being able to it without Spring Boot. See #stefaan-neyts
answer for an example of how to do it.
I created a sample project to show how you could convert a basic SpringMVC application using a minimal amount of Spring Boot auto-configuration.
Original source: http://www.mkyong.com/spring-mvc/gradle-spring-mvc-web-project-example
Converted source: https://github.com/Pytry/minimal-boot-actuator
I could have completely removed the dispatcher-servlet.xml and the web.xml files, but I kept them to show how to perform as minimal a change as possible and to simplify converting more complex projects.
Here is a list of steps I took to convert.
Conversion Process
Add a Java Configuration file annotated with #SpringBootApplication
Add the Application configuration file as a bean to the traditional xml configuration ( added it just after the context scan).
Move view resolvers into Application java configuration.
Alternatively, add the prefix and suffix to application.properties.
You can then inject them with #Value in your application, or delete it entirely and just use the provided spring boot view resolver.
I went with the former.
Removed Default context listener from the spring context xml.
This is important!
Since spring boot will provide one you will get an "Error listener Start" exception if you do not.
Add the spring boot plugin to your build script dependencies (I was using gradle)
Add a mainClassName property to the build file, and set to an empty String (indicates not to create an executable).
Modify dependencies for spring boot actuator
You can use actuator without spring boot.
Add this to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
And then in your config class
#Configuration
#EnableWebMvc
#Import({
EndpointAutoConfiguration.class , PublicMetricsAutoConfiguration.class , HealthIndicatorAutoConfiguration.class
})
public class MyActuatorConfig {
#Bean
#Autowired
public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints);
}
#Bean
#Autowired
public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
return new EndpointMvcAdapter(delegate);
}
}
And then you can see the metrics in your application
http://localhost:8085/metrics
Allthough it is not a good idea to use Spring Boot features without Spring Boot, it is possible!
For example, this Java configuration makes Spring Boot Actuator Metrics available without using Spring Boot:
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration
#Import({ EndpointAutoConfiguration.class, PublicMetricsAutoConfiguration.class })
public class SpringBootActuatorConfig {
#Bean
#Autowired
public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints);
}
#Bean
#Autowired
public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
return new EndpointMvcAdapter(delegate);
}
}
The Maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
Though the answer is already accepted, I thought of updating my experience. I did not want to convert my application to spring boot using #SpringBootApplication. Refer to another question where I have mentioned the bare minimum code required.
As we already have Spring Boot Actuator 2.x, a recipe to include actuator to an existing Spring MVC project can look like this:
#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)
If your objective is to create an endpoint with metrics for Prometheus a.k.a. OpenMetrics, you can use the Prometheus JVM client which is compatible with Spring framework.
Add dependency:
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet</artifactId>
<version>0.16.0</version>
</dependency>
To collect metrics of requests, add as first filter in web-app/WEB-INF/web.xml:
<filter>
<filter-name>prometheusFilter</filter-name>
<filter-class>io.prometheus.client.filter.MetricsFilter</filter-class>
<init-param>
<param-name>metric-name</param-name>
<param-value>webapp_metrics_filter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>prometheusFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
To expose metrics as HTTP endpoint, add servlet:
<servlet>
<servlet-name>prometheus</servlet-name>
<servlet-class>io.prometheus.client.exporter.MetricsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>prometheus</servlet-name>
<url-pattern>/metrics</url-pattern>
</servlet-mapping>
After that you can see the metrics on the /metrics endpoint.
Time passes, we have Spring 6, SpringBoot 3, JakartaEE as a baseline, but people are still looking to add actuator to legacy spring applications. So a small update: spring + actuator without spring-boot. In fact not much changes (and the changes have already been pointed out).
The dependencies
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.3</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>3.0.1</version>
</dependency>
The actuator configuration
#Configuration
#ImportAutoConfiguration({
EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
HealthContributorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
HeapDumpWebEndpointAutoConfiguration.class,
ThreadDumpEndpointAutoConfiguration.class,
LoggersEndpointAutoConfiguration.class,
PrometheusMetricsExportAutoConfiguration.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()),
true);
}
#Bean
DispatcherServletPath dispatcherServletPath() {
return () -> WebInitializer.APPLICATION_ROOT;
}
}
The example is easy to run directly from maven jetty plugin (mvn jetty:run-war).
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
</plugin>
you have made the mistake by not introducing the #springboot annotation in your code.When you add #springboot ot will consider as boot program by the compiler automatically and addd the required dependency file for it and your actuator dependency file

Library for distributed spring config (outside springboot)

I am looking for solution for distributed spring configuration. I am thinking of storing it in zookeeper. https://github.com/spring-cloud/spring-cloud-zookeeper does have that functionality but apparently it requires to use spring-boot.
Is there any similar library that I can use outside spring-boot
Consul by HashiCorp
Consul is a popular option because it is:
Open Source
Includes Service Discovery & Configuration
Support Multi-Datacenter out of the box
Etc.
It doesn't require you to use Spring Boot, it just provides the auto-configurations in case you do decide to go with Spring Boot. In other words, if you're not using Spring Boot, none of the configurations will apply automatically, you'll have to provide the configuration yourself.
Zookeeper is a good option, go for it.
EDIT:
To use Zookeeper without Spring Boot, you'd need to register the appropriate beans either manually or by importing the auto-configuration classes that Spring Boot would import for you implicitly. This rule of thumb generally applies to all Spring Boot-enabled modules.
In your case, you'd most likely need to import just the ZookeeperConfigBootstrapConfiguration and ZookeeperConfigAutoConfiguration. The classes are to be found within spring-cloud-zookeeper-config module so no Spring Boot dependencies needed.
Alternatively, you should look at those classes and their #Imports and declare the beans manually.
I found a solution for using spring-cloud-zookeeper without Spring Boot, based on the idea provided here https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
First, create a CloudEnvironement class that will create a PropertySource from Zookeeper :
CloudEnvironement.java
public class CloudEnvironment extends StandardServletEnvironment {
#Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
try {
propertySources.addLast(initConfigServicePropertySourceLocator(this));
}
catch (Exception ex) {
logger.warn("failed to initialize cloud config environment", ex);
}
}
private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) {
ZookeeperConfigProperties configProp = new ZookeeperConfigProperties();
ZookeeperProperties props = new ZookeeperProperties();
props.setConnectString("myzookeeper:2181");
CuratorFramework fwk = curatorFramework(exponentialBackoffRetry(props), props);
ZookeeperPropertySourceLocator propertySourceLocator = new ZookeeperPropertySourceLocator(fwk, configProp);
PropertySource<?> source= propertySourceLocator.locate(environment);
return source ;
}
private CuratorFramework curatorFramework(RetryPolicy retryPolicy, ZookeeperProperties properties) {
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
builder.connectString(properties.getConnectString());
CuratorFramework curator = builder.retryPolicy(retryPolicy).build();
curator.start();
try {
curator.blockUntilConnected(properties.getBlockUntilConnectedWait(), properties.getBlockUntilConnectedUnit());
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return curator;
}
private RetryPolicy exponentialBackoffRetry(ZookeeperProperties properties) {
return new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(),
properties.getMaxRetries(),
properties.getMaxSleepMs());
}
}
Then create a custom XmlWebApplicationContext class : it will enable to load the PropertySource from Zookeeper when your webapplication start and replace the bootsrap magic of Spring Boot:
MyConfigurableWebApplicationContext.java
public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext {
#Override
protected ConfigurableEnvironment createEnvironment() {
return new CloudEnvironment();
}
}
Last, in your web.xml file add the following context-param for using your MyConfigurableWebApplicationContext class and bootstraping your CloudEnvironement.
<context-param>
<param-name>contextClass</param-name>
<param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value>
</context-param>
If you use a standard property file configurer, it should still be loaded so you can have properties in both a local file and Zookeeper.
For all this to work you need to have spring-cloud-starter-zookeeper-config and curator-framework jar in your classpath with their dependancy, if you use maven you can add the following to your pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-dependencies</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
</dependencies>

Resources