Invoke controller method from java class - java

I just want to know whether controller class method is accessible from another java class.
following is my controller and its method.
#Controller
public class TestResultUploadController {
#RequestMapping(method = RequestMethod.POST,value="/uploadTestResult")
public #ResponseBody
String uploadTestResult(String testResultBarcode,int deviceLoc) {
//some code goes here
return something;
}
I just want to call this controller method from another java class.
How can I make it work?
please suggest..

Short answer: yes, it is possible. In your other class/thread, you can do
// this will create a new instance of that controller where no fields are wired/injected
TestResultUploadController controller = new TestResultUploadController();
controller.uploadTestResult("someString", 1234);
However, keep in mind that your setup is highly unusual and all your autowired fields wouldn't be wired correctly. If you obtain your controller from the context, you'd be able to have your fields wired/injected properly:
// obtain controller bean from context, should have fields wired properly
TestResultUploadController controller = ctx.getBean(TestResultUploadController.class);
controller.uploadTestResult("someString", 1234);
or you can, in your other class, have:
#Autowired private TestResultUploadController controller;
....
public void doStuff(){
controller.uploadTestResult("someString", 1234);
}
Again, this is highly unusual, but highly possible. However, just cause something is possible to be done, doesn't mean you should do it. I would recommend the more common Spring/MVC approach in which you outsource the business logic to Services. Basically, to have something like this:
#Controller
public class TestResultUploadController {
#Autowired private UploadTestResultService uploadTestResultService;
#RequestMapping(method = RequestMethod.POST,value="/uploadTestResult")
public #ResponseBody String uploadTestResult(String testResultBarcode,int deviceLoc) {
return uploadTestResultService.uploadTestResult(testResultBarcode, deviceLoc);
}
}
And in your thread:
//somehow get the service
UploadTestResultService uploadTestResultService = //somehowGetTheService (whether from context or in some other way)
uploadTestResultService.uploadTestResult(testResultBarcode, deviceLoc);
That way, you'd be able to mock the UploadTestResultService in your tests of the controller, and you'd also be able to test the uploadTestResult method of that service on its own without it being in a controller.
EDIT:
How to obtain the Spring context is outside of the scope of this question. I assume you know basic Spring and also basic java.

Related

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.

How to pass an argument to a Spring Boot REST Controller?

I am trying to pass an argument to my #RESTController Spring Boot class.
In the #POSTMapping method I want to use a method of a self defined Java class for processing the received body and returning a response.
The Spring application is launched in Application.java. The Controller-Object seems to get created implicitly.
I already tried adding a constructor to my RESTController class. But I couldn't find a way to call that constructor with an argument.
// Application.java
public static void main (String[] args) {
SpringApplication.run(Application.class, args);
}
//ConnectorService.java
#RestController
public class ConnectorService {
private Solveable solver;
public ConnectorService() {}
public ConnectorService (Solveable solveable) {
this.solver = solveable;
}
#CrossOrigin(origins = "http://localhost:3000")
#PostMapping(path = "/maze")
public Solution Test(#RequestBody Test test) {
return solver.solve(test);
}
}
Even though i could define a second constructor, i didn't find any way to call it with my Object.
Use #RequestParam annotation to pass an argument
You can pass parameter with #RequestParam annotation like this:
#CrossOrigin(origins = "http://localhost:3000")
#PostMapping(path = "/maze")
public Solution Test(#RequestParam("paramName") String param, #RequestBody Test test) {
return solver.solve(test);
}
And you can put it with http request:
http://localhost:3000/maze?paramName=someValue
Assuming that you have POST request, there may be different ways to build this request, depending on the API testing tools you use.
#RestController follows the same rules for dependency injection as any other #Component in Spring framework.
If you have a single constructor, Spring will try to „inject” the parameters while instantiating the controller.
You need to register your dependency as a Spring bean.
It seems that you are new to Spring and you are starting with advanced topics like Spring Boot and rest controllers. Please find some time to read about the basics.
Yo can create a Bean configuration file to initialize your objects like:
#Configuration
#ComponentScan("com.xxx.xxx") // the base package you want to scan
public class Config {
#Bean
//where Solveable is a class and is annotated with an Spring's annotation
public Solveable solveable() {
return new Solveable();
}
}
And use the #Autowired annotation to inject the object in:
#Autowired
public ConnectorService (Solveable solveable) {
this.solver = solveable;
}
This last block will initialize or pass(what you want) the object to the ConnectorService class.

Service injected into spring controller is not available in one of the functions

I am writing spring controller, which injects a bean.
The bean is added in config(we use java config for everything):
#Bean
public NotificationService notificationService() {
return new NotificationService();
}
The service itself has few injected dependencies and few functions:
public class NotificationService {
#Inject
NotificationRepository notificationRepository;
#Inject
ProjectRepository projectRepository;
#Inject
ModelMapper modelMapper;
public NotificationDto create(NotificationDto notificationDto) {
//convert to domain object, save, return dto with updated ID
return notificationDto;
}
public void markAsRead(Long id, String recipientNip) {
//find notification, update status
}
}
Model mapper has almost no configuration, is only set to strict. Meanwhile repositoriers are interfaces extending JpaRepository with no custom functions. They are found by #EnableJpaRepositories.
Finally I have controller that tries to use the code above:
#RestController
#RequestMapping("/notifications")
public class NotificationController extends ExceptionHandlerController {
#Autowired
private NotificationService notificationService;
#PreAuthorize("isFullyAuthenticated() and hasRole('create_notification')")
#RequestMapping(method = RequestMethod.POST, consumes = MediaTypeExtension.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> createNotification(#Valid #RequestBody(required = true) final NotificationDto notification) {
this.notificationService.create(notification);
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
#PreAuthorize("isFullyAuthenticated() and hasRole('update_notification')")
#RequestMapping(value = "/{id}/read", method = RequestMethod.PUT)
private ResponseEntity<?> markNotificationAsRead(#PathVariable("id") Long id, #AuthenticatedContractor ContractorDto contractor) {
this.notificationService.markAsRead(id, contractor.getNip());
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.OK);
}
}
All controllers are added trough #ComponentScan, based on their package.
As you can see both functions use notificationService. When I send POST for create on /notifications the notificationService is properly injected. In the same controller, when I do PUT request on /{id}/read, the notificationService is null.
I think it has something to do with spring putting things into its container, and for some reason not being able to do it for that one function. I have few more functions in the controller and in all of them notificationService is properly injected. I don't see any real difference between createNotification and markNotificationAsRead functions and I couldn't find anything even remotely related on google/stack. In all cases the service wouldn't inject at all because of configuration mistake.
Edit
I have tried changing things around in the function until it has started working. My final code looks like this:
#PreAuthorize("isFullyAuthenticated() and hasRole('update_notification')")
#RequestMapping(value = "{id}/read", method = RequestMethod.PUT)
public ResponseEntity<?> read(#PathVariable("id") Long id, #AuthenticatedContractor ContractorDto contractor) {
this.notificationService.markAsRead(id, contractor.getNip());
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.OK);
}
and it works. Honestly I can't see any difference from my original code, and I have been staring at it for last hour or so. The imports are the same too.
I have also noticed(on unworking code) that while all functions from the controller on debug stack were marked as
NotificationController.functionName(arguments) line: x
The non working function was:
NotificationController$$EnhancerBySpringCGLIB$$64d88bfe(NotificationController).‌​markNotificationAsRead(ContractorDto) line: 86
Why this single function was enhanced by spring CGLIB I have no idea. I have tried looking it up, but for now I came empty handed. Even though the code started to work I am leaving the question open in order to find the underlying cause.
Your method markNotificationAsRead is private and that probably causes the issue. I've just had same issue with final method - this message appeared in log:
2016-11-28 17:19:14.186 INFO 97079 --- [ main] o.s.aop.framework.CglibAopProxy : Unable to proxy method [public final java.util.Map com.package.controller.MyController.someMethod(javax.servlet.http.HttpServletResponse)] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
Looks like in one case we see a CGLib proxy, and in another - the actual class. Only one of those has all the fields injected, looks like the proxy has all fields nulls. But it doesn't matter - the point is your method should be public and not final in order to be proxied properly by #PreAuthorize methods.
I was also facing the same issue. It was all due to the private access modifier used and #PreAuthorize. Making the controller method private does not make an issue if you do not make it secure. But, to make secure, make it public.

Singleton implementation of a Spring bean

We have a Spring bean implemented as a singleton (default). This bean is used as part of an web-service, and at times when multiple simultaneous requests are triggered, the responseholder (singleton bean) throws a NullPointerException when trying to retrieve. This usually happens when the response is build, and then a new request is triggered before sending the original response back.
Can this be due to the Singletion implementation of the bean? If yes, is there wouldn't changing to prototype solve my problem. What about initiating it with the new operator always? Will there be any performance impacts on doing so? Or is there a better way.
Any help would be greatly appreciated.
Edit:
Code details
public class BuildToolRequestProcessor {
private BuildToolResponse buildToolResponse;
.....
//And it has been referenced in the code in different methods, setting the response details..
public String process(BuildToolRequestXml buildToolRequestXml) throws Exception {
buildToolResponse.setLocation(location);
...
public String handleDetails(BuildToolRequestXml buildToolRequestXml) throws Exception {
buildToolResponse.setSchedule(schedule);
...
// And in another method, when I try to retrieve the Location, it throws a Null Pointer Exception..
buildToolResponse.getLocation().getPinCode()
//Bean configuration
<bean id="buildToolResponse"
class="com.raj.buildTool.processor.BuildToolResponse"/>
Additional Notes: I tried introducing a delay before bulding the response of the first request, shooting another request. The second request resets the Location to NULL, and hence NPE is thrown while trying to retrieve the Location. Could this be because of the singleton? Also I haven't used initialized the buildToolResponse again with the new operator, but the class BuildToolResponse extends from BuildToolResponseBuilder, which I am initializing using 'new' to build the response.
Can this be due to the Singletion implementation of the bean? If yes, is there wouldn't changing to prototype solve my problem.
If you have a singleton bean, make sure that this bean does not maintain any state. This means, it should not have any field that is reinitialized based on some methods, except for the injection of another beans or resources that is done by Spring. This may cause concurrency issues, specially when the bean is used in several threads (in this case, to attend multiple requests done to your web service).
This is an example of a bad design for a Spring bean that will be used on multiple threads:
#Component
public class SingletonByDefaultBean {
private StringBuilder responseBuilder;
#Autowired
private FooService fooService;
public String methodUsedInSeveralThreads() {
//here you will have a concurrency issue
responseBuilder = new StringBuilder();
//write contents into the response
//...
//return the response
return responseBuilder.toString();
}
}
To solve this, you have two approaches:
Remove any state of the bean and move the attributes into method local variables:
#Component
public class SingletonByDefaultBean {
//private StringBuilder responseBuilder;
#Autowired
private FooService fooService;
public String methodUsedInSeveralThreads() {
StringBuilder responseBuilder = new StringBuilder();
//write contents into the response
//...
//return the response
return responseBuilder.toString();
}
}
Change the scope of the bean to prototype
#Component
#Scope("prototype")
public class SingletonByDefaultBean {
private StringBuilder responseBuilder;
#Autowired
private FooService fooService;
public String methodUsedInSeveralThreads() {
responseBuilder = new StringBuilder();
//write contents into the response
//...
//return the response
return responseBuilder.toString();
}
}
What about initiating it with the new operator always?
Refer to this answer to know how you can create instances your classes manually and make them been managed by Spring. It is not that easy and I would recommend using these approaches only if you really understand what you're doing.

Mockito, Testing an object that relies on injected dependencies (Spring)?

I'm new to using Mockito and am trying to understand a way to make a unit test of a class that relies on injected dependencies. What I want to do is to create mock objects of the dependencies and make the class that I am testing use those instead of the regular injected dependencies that would be injected by Spring. I have been reading tutorials but am a bit confused on how to do this.
I have one the class I want to test like this:
package org.rd.server.beans;
import org.springframework.beans.factory.annotation.Autowired;
public class TestBean1 {
#Autowired
private SubBean1 subBean1;
private String helloString;
public String testReturn () {
subBean1.setSomething("its working");
String something = subBean1.getSomething();
helloString = "Hello...... " + something;
return helloString;
}
Then I have the class that I want to use as a mock object (rather than the regular SubBean1 class, like below:
package org.rd.server.beans.mock;
public class SubBean1Mock {
private String something;
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}
I just want to try running a simple test like this:
package test.rd.beans;
import org.rd.server.beans.TestBean1;
import junit.framework.*;
public class TestBean1Test extends TestCase
{
private TestBean1 testBean1;
public TestBean1Test(String name)
{
super(name);
}
public void setUp()
{
testBean1 = new TestBean1();
// Somehow inject the mock dependency SubBean1Mock ???
}
public void test1() {
assertEquals(testBean1.testReturn(),"working");
}
}
I figure there must be some fairly simple way to do this but I can't seem to understand the tutorials as I don't have the context yet to understand everything they are doing / explaining. If anyone could shed some light on this I would appreciate it.
If you're using Mockito you create mocks by calling Mockito's static mock method. You can then just pass in the mock to the class you're trying to test. Your setup method would look something like this:
testBean1 = new TestBean1();
SubBean1 subBeanMock = mock(SubBean1.class);
testBean1.setSubBean(subBeanMock);
You can then add the appropriate behavior to your mock objects for whatever you're trying to test with Mockito's static when method, for example:
when(subBeanMock.getSomething()).thenReturn("its working");
In Mockito you aren't really going to create new "mock" implementations, but rather you are going to mock out the methods on the interface of the injected dependency by telling Mockito what to return when the method is called.
I wrote a test of a Spring MVC Controller using Mockito and treated it just like any other java class. I was able to mock out the various other Spring beans I had and inject those using Spring's ReflectionTestUtils to pass in the Mockito based values. I wrote about it in my blog back in February. It has the full source for the test class and most of the source from the controller, so it's probably too long to put the contents here.
http://digitaljoel.nerd-herders.com/2011/02/05/mock-testing-spring-mvc-controller/
I stumbled on this thread while trying to set up some mocks for a slightly more complicated situation and figured I'd share my results for posterity.
My situation was similar in the fact that I needed to mock dependencies, but I also wanted to mock some of the methods on the class I was testing. This was the solution:
#MockBean
DependentService mockDependentService
ControllerToTest controllerToTest
#BeforeEach
public void setup() {
mockDependentService = mock(DependentService.class);
controllerToTest = mock(ControllerToTest.class);
ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
}
#Test
void test() {
//set up test and other mocks
//be sure to implement the below code that will call the real method that you are wanting to test
when(controllerToTest.methodToTest()).thenCallRealMethod();
//assertions
}
Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.
This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.
Hope this helps someone down the road as it stumped me for a while.

Categories

Resources