Access Spring Service name in the service itself - java

I am having some difficulties to do something I thought would be easy.
I have a service:
#Service("tr_v1_mapper")
#RequiredArgsConstructor
#Slf4j
public class VoneMapper implements GenericMapper{
// Mapping code ...
// Service name
String serviceName = "tr_v1_mapper"
}
Basically, I want to stock my service name in a String variable inside my class VoneMapper without harcoding it.
is it something possible ?
Thanks by advance for your help.
PM

What about using a constant in the annotation? More info here: https://www.baeldung.com/java-annotation-attribute-value-restrictions
#Service(VoneMapper.SERVICE_NAME)
#RequiredArgsConstructor
#Slf4j
public class VoneMapper implements GenericMapper {
// Mapping code ...
// Service name
static final String SERVICE_NAME = "tr_v1_mapper";
}

Related

SpringBootRequestHandler for a GET call

I am currently using Spring Cloud Function, and I want to deploy my GET function on AWS Lambda, using the adapter for AWS.
Till now all the Spring Cloud Function implemented was a single POST function with the following structure.
This was done using the
SpringBootRequestHandler.
The DemoRequest class was a POJO class defined to accept the request body and DemoResponse is for the response.
I understand that this part of the code is Depricated.
Interface to define the Endpoint:
#SuppressWarnings("hiding")
#FunctionalInterface
public interface Demoapi<DemoRequest, DemoResponse>{
#PostMapping(value = "/v1/demo")
public ResponseEntity<DemoResponse> demo(#RequestBody DemoRequest demoInfo);
}
Followed by the controller where the #RestController was defined
Part of the controller:
#RestController
public class Democontroller implements Demoapi\<DemoRequest, DemoResponse\>{
static final Logger LOGGER = LogManager.getLogger(Democontroller.class);
#Autowired
private DemoService demoService;
#Override
public ResponseEntity<DemoResponse> demo(#RequestBody DemoRequest demoInfo) {
DemoResponse demoResponse=new DemoResponse();
try {
demoResponse = demoService.demofun(demoInfo);
//logic
}
Now I want to modify this for the GET call and this is the modification I am using.
#SuppressWarnings("hiding")
#FunctionalInterfacepublic interface Demoapi<Void, DemoResponse>{ // as there is no request #GetMapping(value = "/v1/demo")public ResponseEntity<DemoResponse> demo(#RequestParam(name="q", required = false defaultValue="...") String name);// the get call has an optional query param}
Followed by the controller where the #RestController was defined
Part of the controller:
#RestControllerpublic class Democontroller implements Demoapi<Void, DemoResponse>{static final Logger LOGGER = LogManager.getLogger(Democontroller.class);
#Autowiredprivate DemoService demoService;
#Overridepublic ResponseEntity<DemoResponse> demo(#RequestParam(name="q", required = false , defaultValue="...") String name) {DemoResponse demoResponse=new DemoResponse();try {demoResponse = demoService.demofun(name);//logic}
This set up works fine when tested in local but when deployed as Lambda ( I provide Handler as com.demo.DemoHandler in the AWS console, and in the Environment Variable under FUNCTION_NAME I give the DemoController class with the Starting letter in small caps i.e demoController ) and the lambda throws the following error when tested in the console as well as directed from an API Gateway:
\*2023-02-17 11:24:04.080 INFO 10 --- \[ main\] lambdainternal.AWSLambda : Started AWSLambda in 2.649 seconds (JVM running for 3.349)
2023-02-17 11:24:04.329 ERROR 10 --- \[ main\] c.f.c.c.BeanFactoryAwareFunctionRegistry : Failed to invoke function 'domainController'
java.lang.IllegalArgumentException: argument type mismatch
\*
As per error seems like Handler going to find #RestController 'domainController'
In your code there is all Democontroller
The bean for the demoController not defined properly.

Spring Boot can't access REST Controller

I have a simple problem - SpringBootApplication doesn't see my controller - what's more weird - only one of three.
I have UserController, WalletController and DashboardController - this one is not visible for my application.
What I have already done is:
Every package with controller is under the main package, where my SpringBootApplication.class is,
I tried annotate main SpringBootApplication.class with #ComponentScan both with basePackages and basePackageClasses,
There is no other beans - which should be annotated #Component, I removed them and moved methods to my DashboardService.class
This is my controller, which is not visible:
DashboardController
And this is my package structure(seems to be right): Package Structure
Thank You for help!
EDIT:
It might be important, that I use the third-party api to get the data I need
In that methods I use url:
private String getNbpJson(String url) {
return new RestTemplate().getForObject(url, String.class);
}
private CurrentRateDTO getCurrentExchangeRate(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
String code = node.get("code").toString();
String date = node.get("rates").get(0).get("effectiveDate").toString();
double bid = node.get("rates").get(0).get("bid").asDouble();
double ask = node.get("rates").get(0).get("ask").asDouble();
return new CurrentRateDTO(code, date, bid, ask);
}
And then in ExchangeService this is my url
#Service
public class ExchangeRateService implements IExchangeRateService {
private static final Logger logger = LoggerFactory.getLogger(ExchangeRateService.class);
private String NBP_CURRENT_RATE_URL = "http://api.nbp.pl/api/exchangerates/rates/c/eur/2020-12-11/?format=json";
What's more... when I move methods from DashboardController to WalletController (which works)
Another thing that I have found out is that only methods, which make use of the third-party api don't work.
Basically, I retrieve data from the url above - I get the specific fields, create an objects with filled fields.
May it be a problem with retrieving data from the third-party and then implementing it in my app?
I have no more ideas for now...
Maybe your controller is registered but you type a slightly different url. Try this property logging.level.org.springframework.web.servlet.mvc.method.annotation: TRACE
and check on application startup if the controller is registered under some other url.
Try to add this to your controller
#RestController()
#RequestMapping("exchangerates")
Okay, I probably found out the problem... Before I have 3 controllers:
#RestController("/api")
public class UserController {}
#RestController("/wallets")
public class WalletController {}
#RestController("/exchangerates")
public class DashboardController {}
I changed the above to
#RestController("/api")
public class UserController {}
#RestController
public class WalletController {}
#RestController
public class DashboardController {}
So, basically I removed base ?endpoint? for each controller and now it works... It seems like basic endpoints in three RestControllers is too much and the third one is not available, but I don't know is it truth (I bet that it's not).
Why that happened then, can anybody explain that behaviour of controllers?
Thank You for explanantion.

Decide which of multiple implementations to use in Spring Boot app

Given two (or more) implementations of a particular service API, what's the best way to pick which one to use at runtime in my app based on an application property?
Example API:
public interface Greeting {
String sayHello(String username);
}
Implementations:
public class FriendlyGreeting implements Greeting {
public String sayHello(String username) {
return "Hello, " + username;
}
}
public class HostileGreeting implements Greeting {
public String sayHello(String username) {
return "Go away, " + username;
}
}
I've got a separate service class with an #Autowired constructor that takes an instance of Greeting. What I want, is based upon a configuration property, to decide which greeting implementation gets injected and used. I came up with using a configuration class to make that decision:
#Configuration
public class GreetingConfiguration {
private String selection;
#Autowired
public GreetingConfiguration(#Value("${greeting.type}") String type) {
this.selection = type;
}
#Bean
public Greeting provideGreeting() {
if ("friendly".equals(selection)) {
return new FriendlyGreeting();
} else {
return new HostileGreeting();
}
}
}
Is this the right way to do what I want? I went down the road of using #Qualifier on the implementations, and ended up with a mess where Spring saw 3 instances of my Greeting API, and I needed a configuration anyway to pick which implementation to use and return it with a unique qualifier name on it, and that feels worse than what I settled on.
You can mark both Greeting as #Service and select the chosen one with #Qualifier("yourServiceHere") like this:
#Autowired
#Qualifier("friendlyGreeting")
private Greeting greeting;
Another way you can do it is with profile. You can mark your FriendlyGreeting service with #Service and #Profile("friendly") and the HostileGreeting service with #Service and #Profile("hostileGreeting") and just put in the application.properties the following:
spring.profiles.active=friendly
Answering my own question.
#Compass and #user268396 were correct - using Profiles got this working as expected.
I created both implementations, annotated with #Service and #Profile("friendly") or #Profile("hostile"), and could change the property spring.profiles.active to dev,friendly for example, and get what I wanted.
You can use #Conditional annotations described at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html and https://reflectoring.io/spring-boot-conditionals/
#Profile annotations mentioned above are based upon #Conditional(from Spring Framework); see also Spring Boot: org.springframework.boot.autoconfigure.condition
Here is a full solution using ideas mentioned by David and Vitor above with #Profile and #Qualifer annotations.
Two beans with same name but Only one is activated based on which profile is defined.
#Profile("profile1")
#Bean("greeting")
public class FriendlyGreeting implements Greeting {
---
#Profile("profile2")
#Bean("greeting")
public class HostileGreeting implements Greeting {
---
#Configuration
public class GreetingConfiguration {
private Greeting greeting;
#Autowired
public GreetingConfiguration(#Qualifier("greeting") Greeting greeting) {
this.greeting = greeting;
}
}
Notes:
you can remove the intermediate class GreetingConfiguration and stick the "greeting" bean wherever you need
i prefer the #Autowired on the constructor instead of the class member to make it easier for unit testing.

Property values in yml file not loading into a class in springboot project

Please find the code below for application.yml
decrypt: /Users/Blahblah/Bleh
The above property we're trying to read into a Class please find the code for PropertyLoader.java
#Configuration
#Component
public class PropertyLoader implements InitializingBean{
#Value("${decrypt}")
private String decryptPath;
<--->
}
the value decryptPath is always null. Can anyone tell me what is wrong with the code?
Firstly application.yml should be under the src/main/resources/application.yml.
If you want to use this variables in constructor, you don't. Because spring inject #Value annotated variables after the construction.But if you want to do in constructer you can do like :
public class PropertyLoader implements InitializingBean{
private String decryptPath;
public PropertyLoader(#Value("${decrypt}") decrypPath) {
this.decryptPath = decryptPath;
}
}
It turns out since this class is implementing InitializingBean, the properties object won't be initialized until this class has completed execution. The #Value will always return null.

Spring DI with dynamic constructor arguments

Most of my classes wired up look something like:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
#Autowired
public MyClass(MyService service) {
this.service = service;
}
}
So that makes sense to me, but if I want to do something like this:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
private String id; // this is what I need
#Autowired
public MyClass(MyService service, String id) {
this.service = service;
this.id = id;
}
}
But the problem is String id is not known until runtime. Is there any way to do this? From what I can tell, Spring checks all the dependencies by default at runtime so if I try the second example, it complains about the constructor arguments.
I've seen some examples where you can use a factory to create the actual value later down the line. Or I can create getters and setters in order to set the id when I need it, but that would also mean I'd need to add those getters and setters to the interface MyClass as well. Is there a cleaner way to do this?
You can create a #Bean method for id with logic needed to calculate it.
#Configuration
public class Config {
#Bean
public String idForService() {
return calculateId();
}
}
And then your service constructor will be look like this
#Autowired
public MyClass(MyService service, #Qualifier("idForService") String id) {
this.service = service;
this.id = id;
}
But as others have already mentioned it looks like a bad practice so you'd better consider to redesign your service class.
I am afraid what you are trying to do is not in the spirit of what Spring was created for. Please do have a look at: Runtime dependency injection with Spring
Having said that, there might be a workaround to your problem.
If you have some class that calculates that ID for you, just inject that class in a constructor and bind the result of its calculation to the field id in your class.

Categories

Resources