#FeignClient name parameter (service id) with relative url at the end - spring-cloud

I have started using Feign in my spring boot project recently.
I have a question about scenario when my feign client uses service discovery and load balancing with eureka and ribbon (the target service is spread across the multiple nodes in a cluster). I want to have one feign client interface like this:
#FeignClient(name="targetServiceId")
public interface MyFeignClientRelatedToCommonUrlPart {
#RequestMapping(value="/common-url-part/rest-of-the-url-1", ...)
.....method signature....
#RequestMapping(value="/common-url-part/rest-of-the-url-2", ...)
.....method signature....
......
#RequestMapping(value="/common-url-part/rest-of-the-url-N", ...)
.....method signature....
}
Is there any way to "mix" the value of the name parameter (targetServiceId) with "/common-url-part" to avoid replacing the same url part in annotation for every method in my interface?

You could try path property like this:
#FeignClient(name="targetServiceId", path = "/common-url-part/")
It should do the trick, at least the description seems to be promising: FeignClient.

Related

Setting queueSize option on SEDA

I have a seda queue where i set queueSize option according to the camel documentation
The route i have looks like:
from("seda:someQueue?concurrentConsumers=10&queueSize=10")
.process(someProcessor);
I'm getting the following error due to the queueSize option:
org.apache.camel.FailedToCreateRouteException: Failed to create route....bla bla bla..
There are 1 parameters that couldn't be set on the endpoint. Check the uri if the parameters are spelt correctly and that they are properties of the endpoint. Unknown parameters=[{queueSize=10}].....
[stacktrace continues here]
Can anyone point out what's wrong?
I'm using Java 8, Camel 2.9.13
Notice that the documentation says that the option queueSize is component only, which mean you need to configure it on the SedaComponent instead. In other words you cannot configure it on the endpoint as you do in your route above.
For up to date documentation and better docs on Camel components, then browse github pages at: https://github.com/apache/camel/blob/master/components/camel-seda/src/main/docs/seda-component.adoc
Those docs are up to date and show both component vs endpoint options in different tables, so its easier to know the difference.
For those who have the same question, this is how i use the queueSize now
Initialize a new seda component,
SedaComponent sedaComponent = new SedaComponent();
sedaComponent.setQueueSize(3);
context.addComponent("sedaComponent", sedaComponent);
then use this component at the route like,
from("seda:someEndPoint?concurrentConsumers=5")
.to("sedaComponent:someOtherSedaEndPoint?blockWhenFull=true");
Create a specific queue . it is quarkus example replace Named as bean and ApplicationScoped to Configuration for Spring boot
#ApplicationScoped
public class ConnectionConf {
#Named("NonLimitQueue")
#Produces
public BlockingQueue arrayDeque(){
return new ArrayBlockingQueue(30000);
}
}
camel side
from("seda:queue=#NonLimitQueue")
.convertBodyTo(String.class).log("${body}")
Replace queueSize
with
size(query param in apache document)
from("seda:someQueue?concurrentConsumers=10&queueSize=10")
.process(someProcessor);

Spring WS remove flexible URL, Restricting WSDL URL and service URL

I'm trying to make a Spring Boot Soap WebService application, and was following the Get Started (https://spring.io/guides/gs/producing-web-service/) example to learn how to do this.
I've created what I want, but I have two URL problems with this setup and I could not find what configuration should I change to fix this :
WSDL URL basic is localhost:8080/ws/countries.wsdl but anything like localhost:8080/ws/whatever/countries.wsdl is correct
service URL for SoapUI request is localhost:8080/ws but anything like localhost:8080/ws/whatever is correct
I know that this is a feature for Spring WS, but I want a fixed URL (without 'whatever' in both cases) and could not find what to change for this
There is no straight forward way to restrict the way you want.
SOAP service is not URL based.
SOAP message body describe the endpoint.
The thing you wanted is possible following way.
Changing URL mapping in ServletRegistrationBean to restrict URL access
Existing /ws/* mapping is the reason why all the /ws/whatever url successfully responded.
Change as new ServletRegistrationBean(servlet, "/ws");
Effect will be you can not request other than /ws URL
Now the problem is, you can not get WSDL by this mapping.
Solution to get WSDL
The DefaultWsdl11Definition is actually generating WSDL from XSD on every request.
Save countries.wsdl to resource folder as static WSDL file.
Remove DefaultWsdl11Definition bean.
Create a new SimpleWsdl11Definition bean as like
#Bean(name = "countries")
public SimpleWsdl11Definition orders() {
SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
wsdl11Definition.setWsdl(new ClassPathResource("countries.wsdl"));
return wsdl11Definition;
}
Now add another static URL mapping in ServletRegistrationBean. As it will be finally look like new ServletRegistrationBean(servlet, "/ws", "/ws/countries.wsdl");
This practice is good for development phase as you can publish easily the changed definition. But it is recommended to use static-wsdl for production environment. Details ** here
Just change
return new ServletRegistrationBean(servlet, "/ws/*");
for example to
return new ServletRegistrationBean(servlet, new String[]{
"/ws/v1/countries.wsdl",
"/ws/v2/countries.wsdl"
});

spring cloud programmatic metadata generation

Is there anyway that I can generate some metadata to add to the service when it registers.
We are moving from Eureka to Consul and I need to add a UUID value to the registered metadata when a service starts. So that later I can get this metadata value when I retrieve the service instances by name.
Some background: We were using this excellent front end UI from https://github.com/VanRoy/spring-cloud-dashboard. It is set to use the Eureka model for services in which you have an Application with a name. Each application will have multiple instances each with an instance id.
So with the eureka model there is a 2 level service description whereas the spring cloud model is a flat one where n instances each of which have a service id.
The flat model won't work with the UI that I referenced above since there is no distinction between application name and instance id which is the spring model these are the same.
So if I generate my own instance id and handle it through metadata then I can preserve some of the behaviour without rewriting the ui.
See the documentation on metadata and tags in spring cloud consul. Consul doesn't support metadata on service discovery yet, but spring cloud has a metadata abstraction (just a map of strings). In consul tags created with key=value style are parsed into that metadata map.
For example in, application.yml:
spring:
cloud:
consul:
discovery:
tags: foo=bar, baz
The above configuration will result in a map with foo→bar and baz→baz.
Based on Spencer's answer I added an EnvironmentPostProcessor to my code.
It works and I am able to add the metadata tag I want programmatically but it is a complement to the "tags: foo=bar, baz" element so it overrides that one. I will probably figure a way around it in the next day or so but I thougth I would add what I did for other who look at this answer and say, so what did you do?
first add a class as follows:
#Slf4j
public class MetaDataEnvProcessor implements EnvironmentPostProcessor, Ordered {
// Before ConfigFileApplicationListener
private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1;
private UUID instanceId = UUID.randomUUID();
#Override
public int getOrder() {
return this.order;
}
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.consul.discovery.tags", "instanceId="+instanceId.toString());
MapPropertySource propertySource = new MapPropertySource("springCloudConsulTags", map);
environment.getPropertySources().addLast(propertySource);
}
}
then add a spring.factories in resources/META-INF with eht following line to add this processor
org.springframework.boot.env.EnvironmentPostProcessor=com.example.consul.MetaDataEnvProcessor
This works fine except for the override of what is in your application.yml file for tags

Questions about combining Hystrix with Feign

I am trying to use the new HystrixFeign support in Feign. Here is what my code looks like
route66Client =
HystrixFeign.builder()
.logger(new Slf4jLogger())
.encoder(new GsonEncoder())
.target(Route66Client.class, "http://route66/");
The Route66Client is defined as
public interface Route66Client {
#RequestLine("POST /route")
ApiResponse getRoute(
RouteRequest request);
}
When i try to run the code. I get UnknownHostException. As it is not able to resolve route66 for its hostname. Anyone knows what i could be missing ?
Further i had this working with regular Feign ( not HystrixFeign ). All i did was to annotate my Route66Client class with #FeignClient("...") and then injecting Route66Client in the calling class ( So no Feign.builder() was used )
But i couldn't find some #HystrixFeignClient annotation. So i went ahead and started using the HystrixFeign.builder(). But then when i did that the serviceName->Address resolution stopped working.
If you want the benefits of eureka, don't use the builder directly. Put #EnableFeignClients on an #Configuration class (usually your application). Then label Route66Client with #FeignClient("route66") and inject Route66Client. This is only available in Brixton's 2nd Milestone. See the documentation.

Can I configure a #FeignClient url using a properties/yml file?

My goal is to create a strategy of different steps to get from a point-to-point communication between 2 components to a "full blown netflix" style of communication using eureka, ribbon, hystrix. With each iteration I want to add more while I try to limit the amount of changes to the actual code. Feign is my preferred client side framework to make this happen. First step is to create a FeignClient to communicate to the server:
#FeignClient(url = "http://localhost:9000")
interface Client {
#RequestMapping(method = RequestMethod.GET, value = "/author/{author}/addedValue/{addedValue}")
Result addToTotal(#RequestParam(value="author") String author, #RequestParam(value="addedValue") long addedValue);
}
This works but I don't want the URL to be hardcoded in the annotation. I would like to have this: #FeignClient()
and have a properties construct like: client.url: http://localhost:9000
So far I couldn't find any clues on how to configure that and I couldn't find a solution in the spring-cloud sources.
Can it be done and if yes; how?
It can be done with a "serviceId" instead of a "url". E.g.
#FeignClient("foo")
interface Client { ... }
and
foo.ribbon.listOfServers: localhost:9000
e.g. see http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-ribbon-without-eureka for docs.
This can be done like this:
#FeignClient(name="fd-mobileapi-service",url="${fdmobile.ribbon.listOfServers}")
Where fdmobile.ribbon.listOfServers : value is a property in application.properties.
I have tested it and it is working.
I got a way to pass the environment variables in a very simple way interface FeignClient,
#FeignClient(url = "https://"+"\${url}")
interface Client {
#RequestMapping(method = RequestMethod.GET, value = "/author/{author}/addedValue/{addedValue}")
Result addToTotal(#RequestParam(value="author") String author, #RequestParam(value="addedValue") long addedValue);
properties
#URL
url.client=${URL}
.env
URL=https:localhost:9000