Spring AOP not being applied when I split the method - java

This is my first post on stackoverflow...
Well here it goes.
I have a custom spring AOP annotation which works fine for this method
#testInterceptor
public MyObjList getMyObjList( List qlist,Context cntxt){
//some processing
List<MyObj> myObjList= getMyObjs(qlist,cntxt);
//Some more processing
return myObjList;
}
public List<MyObj> getMyObjs( List qlist,Context cntxt){
List<MyObj> myObjList= new ArrayList<MyObj>();
//Some more processing
return myObjList;
}
I realized that this annotation should actually be at the getMyObjs() method.
So I moved the annotation to the getMyObjs() but for some reason now the aspect is not being applied.
I have no idea why.
#testInterceptor
public List<MyObj> getMyObjs( List qlist,Context cntxt){
List<MyObj> myObjList= new ArrayList<MyObj>();
//Some more processing
return myObjList;
}

Due to how Spring uses AOP, in order for #testInterceptor to work on getMyObjs, that method needs to be called from outside the class. Calling it from getMyObjList will not get the interceptor involved.
Check out this blog post for more details.
To clarify what I above with an example:
Let's say you have another class
class Foo {
#Autowired
private MyObjList myObjList;
//this will invode the interceptor
public void willWork() {
myObjList.getMyObjs();
}
public void willNotWork() {
myObjList.getMyObjList(); //will not invoke interceptor since `getMyObjs` is being invoked from inside the class that it's defined
}
}

Related

Is it possible to initialize some of the fields in a mock object

I have a code that I cannot correctly cover with tests.
I am using the Mockito library.
And I had difficulty at the moment of starting the test.
Below is the test code:
#Test
public void testLoadCar() {
when(remoteService.loadData()).thenReturn(new DataResult<DataCar>("", "", new DataCar()));
when(dataResult.hasError()).thenReturn(true);
when(dataResult.response.hasHeaders()).thenReturn(true);
requestNetwork = new RequestNetwork(remoteService);
Response<DataCar> response = requestNetwork.load(request);
}
These are objects in the test class: remoteService, dataResult, request.
I am concerned about the moment where I am trying to implement the when method:
when(dataResult.response.hasHeaders()).thenReturn(true);
I would like to know if such a recording will work.
If it doesn't work, then how can we handle this moment:
protected Response createResponse(DataResult<T> dataResult) {
if (dataResult.hasError() || !dataResult.response.hasHeaders()) {
return dataResult.getErrorMessage());
} else {
return Response.data(dataResult.value);
}
}
This is a method on the system under test (SUT) that has a createResponse() method. This method contains a call to the mock method of the DataResult object.
To implement dataResult.hasError () I got it:
when (dataResult.hasError ()). thenReturn (true);
Then with! DataResult.response.hasHeaders () I have a problem. Since I don't understand how to substitute the value I need.
Not all objects that your object under test interacts with need to be mocks.
Remember that you can use POJOs as well.
DataResult looks like a perfect candidate for a POJO.
You gain nothing by using a mock objet if you can create a POJO with desired state and behaviour.
Looking at the posted code, it looks like it is easy to create:
new DataResult<DataCar>("", "", new DataCar())
On top of that:
Your code looks suspicious to me.
when stubbing remoteService.loadData() you create a new instance of DataResult
subsequently, you stub some calls on dataResult, which is not an object returned from remoteService.loadData()
And to answer original post:
You can set fields on mocks (directly if access modifiers allow it, or via reflection otherwise). Note that this is highly not-idiomatic and surprising use of mocks.
class A {
B b;
}
class B {
boolean hasHeaders() {
return true;
}
}
#ExtendWith(MockitoExtension.class)
public class AAATest {
#Mock
A aMock;
#Mock
B bMock;
#BeforeEach
void setupMocks() {
aMock.b = bMock;
}
#Test
void testFieldInMockIsInitialized() {
Assertions.assertEquals(bMock, aMock.b);
}
}

Chaining a method on dynamic call

Supposed I have a User list on Post entity
private List<User> users = new ArrayList<>();
and I can clear it using
post.getUsers().clear();
and can add to it with
post.getUsers().addAll(Something);
how can I do the same if use to call the function getUsers dynamically? I tried
post.getClass().getMethod("getUsers").invoke(post).getClass().getMethod("clear").invoke(new ArrayList<>());
and also I tried
ArrayList.class.getMethod("clear").invoke(post);
but im getting a
WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [java.lang.IllegalArgumentException: object is not an instance of declaring class]
Any idea on how can I do it?
take note that this is working
post.getClass().getMethod("getUsers").invoke(post); //get the users
I just have no idea how can I chained the clear method or the addAll on it.
Method.invoke returns an Object and the Object class does not have a clear() method, so you need to cast the returned object into a List.
Post post = new Post();
Method m = post.getClass().getMethod("getUsers");
List<User> users = (List<User>)m.invoke(post);
users.clear();
And if you want it in one line, it is ugly, but can be done:
((List<User>)(post.getClass().getMethod("getUsers").invoke(post))).clear();
This also works:
List.class.cast(post.getClass().getMethod("getUsers").invoke(post)).clear();
Just check the JavaDocs of ArrayList.
ArrayList.clear() returns nothing because it's a void method, ArrayList.addAll() returns a boolean.
So to allow for method chaining, you'd have to wrap the calls in your Post entity to return the post entity itself. Then you could get method chaining.
So, in your Post entity, you'd add:
public Post clearUsers() {
users.clear();
return this;
}
and
public Post addAllUsers(List<User> usersToAdd) {
users.addAll(usersToAdd);
return this;
}
and invoke these methods instead of getUsers().xyz() (which is very bad style anyway. The posobject owns these users. it should be the only one to manipulate these).
You need to invoke those methods on the array list object contained by post.
For example, you can call clear on post.getUsers() as
ArrayList.class.getMethod("clear").invoke(
// this will give you reference to array list contained by post
post.getClass().getMethod("getUsers").invoke(post)
);
Tested above code as following
public class DynamicInvocation {
public static void main(String[] args) throws Exception {
Post post = new Post();
post.getUsers().add(new User());
System.out.println(post.getUsers());
ArrayList.class.getMethod("clear").invoke(
post.getClass().getMethod("getUsers").invoke(post)
);
System.out.println(post.getUsers());
}
}
class Post {
private List<User> users = new ArrayList<>();
public List<User> getUsers() {
return users;
}
}
class User {
}

Exposing hypermedia links on collection even it's empty using Spring Data Rest

First at all I read the previous question: Exposing link on collection entity in spring data REST
But the issue still persist without trick.
Indeed if I want to expose a link for a collections resources I'm using the following code:
#Component
public class FooProcessor implements ResourceProcessor<PagedResources<Resource<Foo>>> {
private final FooLinks fooLinks;
#Inject
public FooProcessor(FooLinks fooLinks) {
this.FooLinks = fooLinks;
}
#Override
public PagedResources<Resource<Foo>> process(PagedResources<Resource<Foo>> resource) {
resource.add(fooLinks.getMyCustomLink());
return resource;
}
}
That works correctly except when collection is empty...
The only way to works is to replace my following code by:
#Component
public class FooProcessor implements ResourceProcessor<PagedResources> {
private final FooLinks fooLinks;
#Inject
public FooProcessor(FooLinks fooLinks) {
this.FooLinks = fooLinks;
}
#Override
public PagedResources process(PagedResources resource) {
resource.add(fooLinks.getMyCustomLink());
return resource;
}
}
But by doing that the link will be exposed for all collections.
I can create condition for exposing only for what I want but I don't think is clean.
I think spring does some magic there trying to discover the type of the collection - on an empty collection you cannot tell which type it is of - so spring-data-rest cannot determine which ResourceProcessor to use.
I think I have seen in
org.springframework.data.rest.webmvc.ResourceProcessorHandlerMethodReturnValueHandler.ResourcesProcessorWrapper#isValueTypeMatch that they try to determine the type by looking at the first element in the collection and otherwise just stop processing:
if (content.isEmpty()) {
return false;
}
So I think you cannot solve this using spring-data-rest. For your controller you could fall back to writing a custom controller and use spring hateoas and implement your own ResourceAssemblerSupport to see the link also on empty collections.

Is there any #PostConstruct equivalent for #RequestBody requests?

I've a webservice similar to the following:
#RequestMapping(value = "/getMovies", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody ResponseVO getMoviesList(#RequestBody RequestVO vo) { .... }
The RequestVO class is :
public class RequestVO {
private String[] genreList;
public void updateRequest() {
if (genreList != null) {
// remove the duplicates from the list
// or something else
}
}
public String[] getGenreList() {
return genreList;
}
public void setGenreList(String[] genreList) {
this.genreList = genreList;
}
}
Now I want the method updateRequest to be called automatically after the request json is processed as RequestVO. One thing I currently think of is #PostConstruct, but seems to be of no use in this case.
My question is does Spring provide any such annotation or mechanism ? Or #PostConstruct will do the trick ?
NB : I don't need workarounds as I've plenty of them. So please refrain yourself from posting them. Again above codes are mere samples (please ignore minor mistakes).
Couple of thins to consider:
Don't use verbs in Rest Service method names (like getMovies) because you specify action using HTTP verbs like GET, POST and so on.
POST should be used to create a resource on the server not to retrieve them (what is implied by the method name: 'getMovies')
What do you want to achieve is RequestVO.updateRequest() invoked before passing RequestVO instance to getReportData(), is it right? If so, could you elaborate, why can't you invoke this method on the beginning of the getReportData()?
If you want to achieve this kind of functionality despite the fact it's sensible or not, try:
create new aspect which will be invoked before getReportData() and invoke updateRequest()
use #JsonFactory (provided you use Jackson to map JSON to Java objects) like:
public class RequestVO {
private String[] genreList;
public void updateRequest() {
if (genreList != null) {
...
}
}
public String[] getGenreList() {
return genreList;
}
public void setGenreList(String[] genreList) {
this.genreList = genreList;
}
#JsonFactory
public static RequestVO createExample(#JsonProperty("genreList") final String[] genreList) {
RequestVO request = new RequestVO(genreList);
request.updateRequest();
return request;
}
}
As you are saying, #PostConstruct is only call after a bean creation and is of no use here. But you have 2 simple ways of calling a method after the end of another method.
explicit : just wrap your real method in another one, and do all pre- or post-processing there
#RequestMapping(value = "/getMovies", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody ResponseVO getMoviesList(#RequestBody RequestVO vo) {
// pre_processing
ResponseVO resul = doGetMoviesList(vo);
// post_processing
return resul;
}
public ResponseVO doGetMoviesList(RequestVO vo) { ... }
Is is simple to write, even if not very nice.
use Spring AOP. You can define an after returning advice that will be called after the advised method returns normally. The advice can be shared across multiple classes if you need it and write your pointcut accordingly. It is really powerfull, but has one caveat : Spring implementation uses proxies and by default JDK proxies. That means that any advised method should be member of an interface and called through that interface. So it would be much simpler and cleaner to advise a service than a controller. IMHO, if you really need to do AOP on a controller, you should use full AspectJ including class weaving ... In short, it is very nice, very powerfull, but a little harder to implement.

private method inside spring mvc controller is thread safe

As i understand spring mvc controllers are thread safe by default (like servlets). But I just want to know any private helper methods inside the controllers are thread safe ?
I have two mapping in the controller class eg: /test and test/success. Every time user invokes this url I want to check the user status and activation time in the database using a service method ( two different calls ). So I have decided to create a one private helper method to check the status.
So could anyone know that my private method is thread safe ?
All request are handled by one instance of your controller (singleton because it's a spring managed bean). So you need to make sure to not store any state (in a field) related to one request.
So:
#Controller
#RequestMapping("/foo")
public class Foo {
#Autowired
private Something something;
#RequestMapping("/list")
public String foo() {
something.someMethod();
bar();
return "view"
}
private void bar() {
// something
}
}
is OK, but:
#Controller
#RequestMapping("/foo")
public class Foo {
private User theUser; // problem is ALL request share this field
#RequestMapping("/foo/{userId}")
public String foo(#PathVariable final Integer userId) {
if (theUser.getId().equals(userId)) {
// something
} else {
theUser = ...
}
return "view"
}
}
is not.
NB: not tested (typed just here so it can even hurts your compiler)

Categories