Have a common path with variables in different services - spring

I have two interfaces with the annotation #RequestMapping:
#RestController
#RequestMapping("/users")
public interface UserRestService{}
and
#RestController
#RequestMapping("/clients")
public interface ClientRestService{}
They both have methods to manage users and clients.
I already defined the initial path with the property:
servlet:
context-path: /api
Now, I want to define a common path with a path parameter /v1/tenant/{tenant}/, so the complete URL for the services will be :
/api/v1/tenant/{tenant}/users
/api/v1/tenant/{tenant}/clients
I already tried having those two interfaces extending a common interface and with an annotation but without luck.
How can I do this without define for each interface the complete path?

You can define /v1/tenant/{tenant} as an entry in the properties file. Then, you can inject it like this:
#RestController
#RequestMapping("${my.common.path}/clients")`
public interface ClientRestService{}
#RestController
#RequestMapping("${my.common.path}/users")`
public interface UserRestService{}
Properties file:
my.common.path = /v1/tenant/{tenant}
Finally, to access that path variable inside a method from either controller, you can do this:
#GetMapping("/")
public String test(#PathVariable String tenant) {
return tenant;
}

Related

How to define a class or method after #Getmapping in a Spring Controller

when defining a class after #Getmapping annotation in Spring Controller, are there other ways besides Iterable see code sample below on how to define a class or method after #Getmapping in a Spring Controller,
#RestController
#RequestMapping("/rooms")
public class RoomController {
#Autowired
private RoomRepository roomRepository;
#GetMapping
public Iterable<Room> getRooms(){
return this.roomRepository.findAll();
}
}
Are there other ways to define a class or method after #Getmapping in a Spring Controller
Based on HTTP GET specification:
The GET method means retrieve whatever information (in the form of an
entity) is identified by the Request-URI. If the Request-URI refers
to a data-producing process, it is the produced data which shall be
returned as the entity in the response and not the source text of the
process, unless that text happens to be the output of the process.
So, for GetMapping you can return any type which defines as an entity (i.e. any Class which suits your requirement).

How to access Configuration objects in Spring Boot

Spring Boot has a mechanism for accessing the contents of .properties (or YAML) files that one might want to include in an application.
I currently have a dbase.properties file (residing in src/main/resources) that contains the following information:
app.dbase.name=MyDbase
app.dbase.connect=jdbc:postgresql://localhost:5432
app.dbase.user=auser
app.dbase.password=mypassword
As described in various Spring Boot documents and examples, I have a configuration class that is defined below:
#Configuration
#PropertySource("dbase.properties")
#ConfigurationProperties(prefix = "app.dbase")
public class DbInfo
{
private String name;
private String connect;
private String user;
private String password;
// Getters and setters left out for brevity
}
Unfortunately, while the various documents and examples give good information on how to define a configuration
class, I have been unable to find any description on how to use it! Apparently, a Spring Boot web application
creates an instance of a configuration class upon startup (and it looks like it also initializes them with the
values from the properties file) but my attempts to guess how to access its contents when I need to have failed.
The method of doing so is probably simple, but no one seems to want to describe this method anywhere.
So: how does one access and use one of these configuration classes once they are instantiated?
Note that #ConfigurationProperties would require all of the properties in your file to be prefixed with 'app.dbase', as in 'app.dbase.username' and 'app.dbase.password'. If that's the case, the class you have now should work.
You would call it like this:
#Component
public class Component {
#Autowired DbInfo dbInfo;
public method() {
String username = dbInfo.username();
}
}
If you are having issues, you may be required to add this to a Configuration class:
#Configuration
public class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
...
}
You may also need to add 'classpath:' inside your annotation, as in: #PropertySource("classpath:dbase.properties"), assuming your properties file is in your src/main/resources.

Define different Feign client implementations based on environment

I have a Spring boot application which uses Feign to call an external web service via Eureka. I'd like to be able to run the application using a mocked out implementation of the Feign interface, so I can run the application locally without necessarily having Eureka or the external web service running. I had imagined defining a run configuration that allowed me to do this, but am struggling to get this working. The issue is that the Spring "magic" is defining a bean for the Feign interface no matter what I try.
Feign interface
#FeignClient(name = "http://foo-service")
public interface FooResource {
#RequestMapping(value = "/doSomething", method = GET)
String getResponse();
}
Service
public class MyService {
private FooResource fooResource;
...
public void getFoo() {
String response = this.fooResource.getResponse();
...
}
}
I tried adding a configuration class that conditionally registered a bean if the Spring profile was "local", but that was never called when I ran the application with that Spring profile:
#Configuration
public class AppConfig {
#Bean
#ConditionalOnProperty(prefix = "spring.profile", name = "active", havingValue="local")
public FooResource fooResource() {
return new FooResource() {
#Override
public String getResponse() {
return "testing";
}
};
}
}
At the point my service runs, the FooResource member variable in MyService is of type
HardCodedTarget(type=FoorResource, url=http://foo-service)
according to IntelliJ. This is the type that is automatically generated by the Spring Cloud Netflix framework, and so tries to actually communicate with the remote service.
Is there a way I can conditionally override the implementation of the Feign interface depending on a configuration setting?
the solution is like below:
public interface FeignBase {
#RequestMapping(value = "/get", method = RequestMethod.POST, headers = "Accept=application/json")
Result get(#RequestBody Token common);
}
then define your env based interface:
#Profile("prod")
#FeignClient(name = "service.name")
public interface Feign1 extends FeignBase
{}
#Profile("!prod")
#FeignClient(name = "service.name", url = "your url")
public interface Feign2 extends FeignBase
{}
finally, in your service impl:
#Resource
private FeignBase feignBase;
Having posted the same question on the Spring Cloud Netflix github repository, a useful answer was to use the Spring #Profile annotation.
I created an alternative entry point class that was not annotated with #EnabledFeignClients, and created a new configuration class that defined implementations for my Feign interfaces. This now allows me to run my application locally without the need to have Eureka running, or any dependent services.
I'm using a simpler solution to avoid having multiples interfaces for a variable parameter like url.
#FeignClient(name = "service.name", url = "${app.feign.clients.url}")
public interface YourClient{}
application-{profile}.properties
app.feign.clients.url=http://localhost:9999

Auto-wiring Multiple implementation of a Abstract class

I have been working on a spring MVC project structure where multiple concrete service classes extends from a Abstract Class.
In my controller, i am trying to autowire the abstract class and choose the implementation based on the user choice. Here's the relevant portion of Controller class:
#Controller
#RequestMapping("/")
public class DashboardController {
#Autowired
LogAnalyzerAbstract logAnalyzer;//new LogAnalyzer();
private static final Log logger =LogFactory.getLog(DashboardController.class);
....
logAnalyzer.process();
}
I want to the controller to use the implementation based on the user input and used the Abstract class LogAnalyzer reference in the further implementaion.
Is my logic valid? Can you guide me through?
If I understand what you ask correctly, what you need to do is to create several Spring-MVC controllers, one for each use case, with its own base path, and to autowire there the abstract service class (or the service interface), based on the bean name of the concrete service (if you use the annotation #Service on concrete service classes, the bean name should be the name of the concrete service class, starting with a lowercase letter).
For example, something like the following:
#Service
public class MyFirstUseCaseService extends AbstractService {
...
}
and in the controller
#Controller
#RequestMapping("/firstUseCaseBaseUrl")
MyFirstUseCaseController {
#Autowired
#Qualifier("myFirstUseCaseService")
private AbstractService service;
....
}

Share a common parent path when implementing Spring controllers

I want to be able to define a parent Controller class which will have a mapping of "/api", and then extend that controller with my different implementations.
So ApiController will have:
#Controller
#RequestMapping("/api")
For example, my User controller should extend the base api controller and also add "/users" to the path, so it will answer to "/api/users" requests. So UserController will have:
#Controller
#RequestMapping("/users")
but since it extends ApiController, it will effectively answer to /api/users.
Naturally I can prepend "/api" to all controllers so that this is achieved without the parent class, but I prefer to do it "the right way" if it's possible, so that I can define my api implementations with a cleaner and more visible path.
I tried extending the ApiController base class, but this does not work, UserController still answers to "/users" and ignores the base class "/api".
Hmmm. You can try this, it is what works for me:
#RequestMapping("/abstract")
public abstract class AbstractController {
}
#Controller
public class ExtendedAbstractController extends AbstractController {
#RequestMapping("/another")
public String anotherTest() {
return "another";
}
}
Note, your base class must have no #Controller annotation and must be abstract.
If you try to do extend not abstract class annotated as controller and that has #RequestMappings you get errors on step where RequestMappingHandlerMapping initializing.
You can achieve it with:
create sub-context configuration for controllers with same path prefix
create sub-context with own DispatcherServlet mapped to certain path
Look at this answer. It's similar problem, but for #RestControllers.

Resources