Switch Schema based on call from rest controller - java

We have two products: Product A and Product B and both have different rest controllers. Both of these controllers are calling the same common service and the common service calling the common dao methods. I want to select the schema in the common daos methods based on which products the call has been made. So for example, if the call is coming from the Product A controller then select schema as A or else B.
I wanted to use the same database connection and change the schema based on which controller the call is made. One possible solution would be to pass the schema name from the controller layer to the service layer which in turn passes it to the dao layer. I want to avoid having two different data sources and then switching between them dynamically.
So is there any other better way that we could do this?
Note: We are using Mybatis as our persistence layer.
Please find the sample code :
Controller for ProductA
#Component
public class ProductA{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
// Calling common service method here to get total products
}
}
Controller for ProductB
#Component
public class ProductB{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
// Calling common service method here to get total products
}
}
FYI: We are using Jersey

Thank you #ave for the response. I am able to solve it by using ThreadLocal as you mentioned. By setting the tenant id in the controller, I am able to get the particular tenant in the common service.
Please find the updated code below:
Controller for Product A
#Component
public class ProductA{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
public void setSchema(){
TenantContext.setCurrentTenant("ProductA");}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
setSchema();
// Calling common service method here to get total products
}}
Controller for Product B
#Component
public class ProductB{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
public void setSchema(){
TenantContext.setCurrentTenant("ProductA");}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
setSchema();
// Calling common service method here to get total products
}
}

Related

Spring MVC Controller implementation advice

Currently I have a domainObjectPersistenceService which calls DAO functions on the Domain Object, and I have a separate domainObjectDomainService which processes Business Logic. For example, userPersistenceService and userDomainService.
I am unsure whether to handle the initial call to the domainObjectPersistenceService from the Controller directly, or call it from inside the domainObjectDomainService.
What is the preferred way?
#Controller
public class Controller {
public controllerMethod(int fileId) {
domainObject = domainObjectPersistenceService.getFile(fileId);
data = domainObjectDomainService.processFile(domainObject);
// convert data into DTO
return dataDTO;
}
}
or
#Controller
public class Controller {
public controllerMethod(int fileId) {
// domainObjectDomainService handles persistence layer calls.
data = domainObjectDomainService.processFile(fileId);
// convert data into DTO
return dataDTO;
}
}
Many thanks!
See Domain Driven Design: Domain Service, Application Service
In your case Controller is Application Service. It is an orchestrator, that retrieves objects from the repository and pass to the Domain Service.
First way is correct.

Chain of REST path in spring boot controller

I develope RESTful back-end app with spring boot. I find out how to use annotation in the class:
#RestController
#RequestMapping(path = "/users")
public class User{
// rest of code!
}
But every user has orders and any orders has items! So I design rest API like this:
/users /users/{user_id}
/users/{user_id}/orders
/users/{user_id}/orders/{order_id}
/users/{user_id}/orders/{order_id}/items
/users/{user_id}/orders/{order_id}/items/{item_id}
/users/{user_id}/cart
Now, what is best practice or normal implementation for this design in spring boot? How can I handle APIs with Spring Boot?
Continue and use the annotated method inside the class:
#RestController
#RequestMapping(path = "/users")
public class UserController {
#GetMapping("/{user_id}")
public User getUserById(#PathVariable("user_id") String userId) { }
#GetMapping("/{user_id}/orders")
public List<Order> getOrdersByUserId(#PathVariable("user_id") String userId) { }
#GetMapping("/{user_id}/orders/{order_id}")
public List<Order> getOrdersByIdAndUserId(#PathVariable("user_id") String userId, #PathVariable("order_id") String orderId) { }
// ... and so on
}
Don't forget the implementation inside the {} brackets.
The example method getOrdersByIdAndUserId is mapped to the GET method of path /users/{user_id}/orders/{order_id} where /users is a common part defined as the class mapping and the rest with the method.
I suggest you rename the class User to UserController, because the User is a suitable name for the returned entity.

How to invoke Restful WebService in Spring Boot

I have a SB service that is being used to send email. I wanted to use that in my existing application , how can I do that? I am thinking to create a controller that handles incoming HttpRequest and HttpResponse. But still no idea on how my existing application will invoke it. I need some high level overview too on how exactly SB application will run independently with other application.
P.S.- there is no UI interface for the email service so i wont be mapping url like we do in controllers generally.
Here is my sample email service:
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
I created a controller like this earlier to test this out:
#RestController
public class CourseController {
#Autowired
private EmailService emailService;
#RequestMapping(value = "/sendEmail", method = RequestMethod.POST)
public void sendEmail() {
emailService.sendMessage("abc#gmail.com","{client#gmail.com}", "testSubject",new Context);
}
Context has some business data.
I have a jsp that I am using and posting my form through which it is mapping. It all works fine.
But now I want to integrate this with my existing application (its on struts 1)so there wont be any uri to map. There must be some kind of HttpRequest need to be created from the invoking application and my controller should be handling it. How can I achieve this?
You have already this service implemented? Then you need a RestController class that mapps the uri of your choice. In this class you need to inject the service class that realizes your email sending method. Is this class annotated with #Service? Quite difficult to explain without seeing your code. Here an example for a REST-Interface:
#RestController
#RequestMapping("/api/v1/email")
public class RestClass {
private EmailService emailService;
#Autowired
public RestClass(EmailService emailService){
this.emailService = emailService;
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> sendEmail(#RequestBody EmailDTO emailDTO){
String emailAdress = emailDTO.getEmail();
this.emailService.sendEmail(emailAdress);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
So in this case the emailService would be the class that has the method that sends your email. This class should be annotated with #Service. Hope that helps.
Here your existing class:
#Service
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
And in case the injection doesn't work you have to annotate your application class with #ComponentScan({"com.foo.dal.","com.foo.notification."}) replace this packages simply with the package of your service and resource class.
I am not sure about the problem. If I am right that you need to call a rest service from your application. In this case it is lot easier and convenient to use Spring's RestTemplate link
You can get some overview here

Spring: use different beans in one controller

I have following issue: we've been developing website using Spring tools 3.8.3 and one of the option is to allow users to reset their passwords. In order to do that they need to specify their email address and validate captcha. When these steps are completed data is sent to ResetPassword controller which does all necessary checks and work. Controller has following address:
website.com/path/resetPassword/
And has following structure where it extends abstract controller template to define the input and output beans:
#RequestMapping(path="website.com/path/resetPassword/",
consumes = "application/JSON",
produces = "application/JSON",
method = "POST")
public class ResetPassController extends ControllerTemplate<ResetPassCaptchaBean, ResponseBean>{
// Autowired field declaration
// Couple of methods calling services
}
The bean also has only two fields - email and captcha accordingly. Captcha field uses annotation to check if either it's valid or not:
public class ResetPassCaptchaBean extends ResetPassBean {
#CaptchaValid
private String captcha;
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
public String getCaptcha() {
return captcha;
}
}
public class ResetPassBean {
private String email;
public void setEmail(String email) {
this.email= email;
}
public String getCaptcha() {
return email;
}
}
Now we are adding mobile API and it's necessary to do the same procedure but without using captcha validation. Therefore we're simply using the bean without captcha field. Since captcha is validated using the annotation in the bean it's completely not used in the controller itself and therefore we'd like to have the same controller both for handling requests from web site and from mobile API.
I understand that it's possible to create two different controllers and move all the implementation to service layer (read other class) but then there still would be two identical classes with only one line difference
public class ResetPassController extends ControllerTemplate<ResetPassCaptchaBean, ResponseBean>{
//Autowiring same stuff
//Calling same methods
}
and
public class MobileResetPassController extends ControllerTemplate<ResetPassBean, ResponseBean>{
//Autowiring stuff
//Calling same methods
}
So the question is: leave it with two identical controllers or is there other solution?
Thanks!

Call method of Controller1 into another method of Controller2 in Spring

I did Google a lot to find my problem but I couldn't and sorry If this question already on the stack overflow because I have not find it.
First let take a look into the code
#Controller
public class Controller1 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
}
public int calculation(int i){
//Some Calcucation
return i;
}
}
and second controller is
#Controller
public class Controller2 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
//In this I want to call the calculation(1) method of controller1.
}
}
My question is that is there any way to call the method of calculation() of controler1 in to controller2. But remember I don't want to make method static in controller1.Is there anyway to call it without make it static?
Thanks
Yasir
You should create service bean for example in configuration file (or use # one of the annotaions) and inject it into controller. For example ()
#Configuration
public class MyConfig {
#Bean
public MyService myService(){
return new MyService();
}
}
#Controller
public class Controller1 {
#Autowire
private MyService myService;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
#Controller
public class Controller2 {
#Autowire
private MyBean myBean;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
Your controllers should not call each other. If there is a logic which needs to be used by both controllers, it is much better to put that into separate bean, which will be used by both controllers. Then you can simply inject that bean to whicheveer controller neccessary. Try not to put any business logic to controllers, try tu put it to specialized class instead which will be web independent if possible and will accept web agnostic business data as user email, account number etc. No http request or response. This way your class with actual logic is reusable and can be unit tested much more easily. Also, if there is state, it should be contained in your classes outside controllers. Controllers should be stateless and not contail any state at all.
When using MVC pattern and you are deciding where to put your logic, you should separate business logic into model and into controllers you should put only logic regarding user interaction, as explained in this stack overflow post.

Categories