Chaining a method on dynamic call - java

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 {
}

Related

JAX-RS via Jersey -> No Content Returned In HTTP Body (Method Completes Without Errors)

I am using JAX-RS via Jersey and I have hit a "bump in the road". I have a method that is supposed to return a JSON object following an HTTP POST. It does execute successfully, but does not return the JSON Object (Unless I do a work around). I am hoping someone can tell me why this does not work as I expect it to. See the following code:
#Path("chatroom")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ChatroomResource {
ChatroomService service = new ChatroomService();
//this works properly and returns the object as json
#GET
public List<Chatroom> getChatrooms() {
return service.getChatrooms();
}
/**********
* This works, but does not return any content in response body
*******/
#POST
public Chatroom addRoom(Chatroom room) {
return service.addChatroom(room);
/*
* This one does produce content body
* service.addChatroom(room);
* return room;
*/
}
}
The following Method is in the service object:
public Chatroom addChatroom(Chatroom room) {
return Cache.getChatrooms().put(room.getRoomName(), room);
}
What might be wrong and how to fix it
Based on the superficial details you've provided, I believe the following instruction is returning null:
return Cache.getChatrooms().put(room.getRoomName(), room);
In the put(String, Chatroom) method, I guess you are adding the Chatroom instance to the cache, but you are returning null instead of the Chatroom instance.
The following should work:
public Chatroom addChatroom(Chatroom room) {
Cache.getChatrooms().put(room.getRoomName(), room);
return room;
}
Update 1
As you mentioned in the comments, you are using a Hashtable to implement your cache.
Be aware the put(K, V) method returns the previous value of the specified key in the hashtable, or null if it did not have one. For more details, consider reading the documentation.
Update 2
Have you ever consider using a HashMap instead of a Hashtable?
If synchronization becomes an issue, you might be interested in a ConcurrentHashMap.

Spring MVC #RequestParam and #SessionAttribute

I'm new to Spring and Portlet development.
I'm using the spring-webmvc-portlet but I think the workflow is almost the same with servlets.
My use case is pretty common but it's still a bit of a struggle to make everything work the way i would like to.
I got a controller like this :
#Controller
#RequestMapping(value = "VIEW")
#SessionAttributes(value = "persons")
public class MyController
// Display a list of person in personList.jsp
#RenderMapping(params = "actionPerson=query")
public String showList() {
return "personList";
}
#ActionMapping(params = "actionPerson=query"){
// Here is a trick I use to avoid the MissingPortletRequestParameterException
// to be raised
response.setRenderParameter("query","")
response.setRenderParameter("actionPerson","query");
}
// Return a list of person and store it in the model
#ModelAttribute("persons")
public List<Person> getPersons(#RequestParam(value="query") String query) {
List<Person> personList = personService.SearchByName(query);
return personList;
}
So far i understand that methods annoted with #ModelAttribute are invoked every time to generate the model before any rendering process.
I don't want this method to invoked every time. I tried to use the annotation #SessionAttributes to store my list of person in the handler’s conversational state.
My problem is the annotation #RequestParam raise the following expection :
org.springframework.web.portlet.bind.MissingPortletRequestParameterException: Required String parameter 'query' is not present
at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter$PortletHandlerMethodInvoker.raiseMissingParameterException(AnnotationMethodHandlerAdapter.java:559)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveRequestParam(HandlerMethodInvoker.java:514)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:353)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:155)
at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:369)
at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.doHandle(AnnotationMethodHandlerAdapter.java:356)
at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.handleRender(AnnotationMethodHandlerAdapter.java:296)
at org.springframework.web.portlet.DispatcherPortlet.doRenderService(DispatcherPortlet.java:764)
at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:537)
at org.springframework.web.portlet.FrameworkPortlet.doDispatch(FrameworkPortlet.java:483)
at javax.portlet.GenericPortlet.render(GenericPortlet.java:248)
My guess is even if the method getPersons is not invoked. The #RequestParam(value="query) is still evaluated.
When I display my list of person, users can select a person to get detailed informations. I would like them to go back to the list of result without submitting a new query.
How can i handle this case properly ? Is there a way to skip #RequestParam to be evaluated ?
Thanks !
You could make #RequestParam as not required. This would allow your request to succeed, but query would then be null.
#ModelAttribute("persons")
public List<Person> getPersons(#RequestParam(value="query" , required=false) String query) {
List<Person> personList = personService.SearchByName(query);
return personList;
}
For more details on RequestParam

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.

What is the best way to call one #RequestMapping method from another in Java?

I have 3 methods in a controller, looking something like this.
#RequestMapping(value="export-data")
public ExcelView exportData(String urlForData, String paramToPass) {
List<Data> data; //WHAT SHOULD I DO HERE?
return new ExcelView(data);
}
#RequestMapping(value="get-data-1")
public List<Data> getData1(String param) {
//return the data based on param
}
#RequestMapping(value="get-data-2")
public List<Data> getData2(String param) {
//return the data based on param
}
The value for urlForData will be either get-data-1 or get-data-2. And I'll need to call the specified method with the given paramToPass.
Is there a way to call the methods via Spring (versus just doing a String equality check)? Notice that this is not a redirect as I want to remain in the exportData method (versus giving up control to the other methods).

Spring AOP not being applied when I split the method

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
}
}

Categories