What is the RESTful way of creating/updating multiple resources - rest

Using Spring MVC on the server, we have your basic REST API:
#Controller
#RequestMapping(value="/entities")
public class EntityController
{
//GET /entities
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public List<Entity> getEntities()
...
//GET /entities/{id}
#RequestMapping(value="/{id}", method=RequestMethod.GET)
#ResponseBody
public Entity getEntity(#PathVariable Long id)
...
//POST /entities
#RequestMapping(method=RequestMethod.POST, consumes="application/json")
#ResponseBody
public Entity createEntity(#RequestBody Entity entity)
...
//PUT /entities
#RequestMapping(method=RequestMethod.PUT, consumes="application/json")
#ResponseBody
public Entity updateEntity(#RequestBody Entity entity)
...
}
This all works just fine. Now I'm wanting to be able to create or update mutliple Entitys with one request. My first thought was to add this:
#RequestMapping(method=RequestMethod.PUT, consumes="application/json")
#ResponseBody
public List<Entity> updateEntities(#RequestBody List<T> entities)
It would have the same URL as the updateEntity but handle lists ([...]). The updateEntity would handle a single object ({...}). However, on server startup I got the following error:
java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'entityController' bean method public java.util.List<foo.bar.Entity> foo.bar.EntityController.updateEntities(java.util.List<foo.bar.Entity>) to {[/entities],methods=[PUT],params=[],headers=[],consumes=[application/json],produces=[],custom=[]}: There is already 'entityController' bean method public foo.bar.Entity foo.bar.EntityController.updateEntity(foo.bar.Entity) mapped.
So, from what I gather, Spring doesn't like two different methods with the same #RequestMapping, even though the #RequestBody is different.
This leads me to two questions. First, am I going about this the correct RESTful way? Am I in line with RESTful principles when I'm doing a PUT to the same URL and just allowing the request body to be a single object or a list? Would there be another correct way of doing this that Spring would like? (Ok, so the first question was actually three...)
The 2nd question is if there is something I can add to the #RequestMapping annotation that would differentiate the two methods enough, but keep the same REST API?
Thanks for any light you can shed on this.

#JsonIgnoreProperties(ignoreUnknown = true) for each model
I have done on this way...with Two model: User and Tree
#RequestMapping(value = "/users/testBean", method = RequestMethod.POST, consumes={"application/json","application/xml"}, produces={"application/json","application/xml"})
public #ResponseBody List<User> testBean(#RequestBody Object object) {
System.out.println("testBean called");
System.out.println(object.toString());
ObjectMapper mapper=new ObjectMapper();
User user =mapper.convertValue(object, User.class);
Tree tree =mapper.convertValue(object, Tree.class);
System.out.println("User:"+user.toString());
System.out.println("Tree:"+tree.toString());
return userService.findAll();
}

Related

how to pass namedQuery parameters in Apache Camel JPA by header?

I have this camel route:
from("direct:getUser")
.pollEnrich("jpa://User?namedQuery=User.findById&consumeDelete=false");
This is my user Entity:
#Entity
#NamedQueries({
#NamedQuery(name="User.findAll", query="SELECT u FROM User u"),
#NamedQuery(name="User.findById", query="SELECT u FROM User u WHERE u.id = :id")
})
public class User{
#Id
private String id;
}
I have tried this route by setting the header:
from("direct:getUser")
.setHeader("id", simple("myid"))
.pollEnrich("jpa://User?namedQuery=User.findById&consumeDelete=false");
But it is not working
Is there any method to set jpa properties by the headers? The camel documentation quote this in parameters option but i don't found the examples
Options: parameters
This option is Registry based which requires the # notation. This
key/value mapping is used for building the query parameters. It is
expected to be of the generic type java.util.Map where
the keys are the named parameters of a given JPA query and the values
are their corresponding effective values you want to select for. Camel
2.19: it can be used for producer as well. When it's used for producer, Simple expression can be used as a parameter value. It
allows you to retrieve parameter values from the message body header
and etc.
I hope it's not too late to answer. In any case I had a similar issue in my project, the client does a HTTP GET with a parameter id, which is used by the JPA query and the result is finally marshalled back to the HTTP client. I'm running camel in a Spring application.
I finally figured out how to achieve it in a reasonably clean way.
This is the RouteBuilder where the route is defined:
#Override
public void configure() throws Exception {
Class dataClass = SomeClass.class;
JacksonDataFormat format = new JacksonDataFormat();
format.setUnmarshalType(dataClass);
String jpaString = String
.format("jpa://%1$s?resultClass=%1$s&namedQuery=q1" +
"&parameters={\"id\":${headers.id}}", dataClass.getName());
from("jetty://http://localhost:8080/test").toD(jpaString) // note the .toD
.marshal(format)
}
And this is the StringToMapTypeConverter class, otherwise camel cannot convert {"id": X} to a map
public class StringToMapTypeConverter implements TypeConverters {
private static final ObjectMapper mapper = new ObjectMapper();
private static JavaType mapType;
static {
mapType = mapper.getTypeFactory().constructMapType(Map.class,
String.class, Object.class);
}
#Converter
public Map<String, Object> toMap(String map) throws IOException {
return mapper.readValue(map, mapType);
}
}
Remember to add it to the context. In Spring is something like:
<bean id="myStringToMapTypeConverter" class="....StringToMapTypeConverter" />
Refs:
http://camel.apache.org/jpa.html
http://camel.apache.org/message-endpoint.html#MessageEndpoint-DynamicTo
http://camel.apache.org/type-converter.html#TypeConverter-Addtypeconverterclassesatruntime

How to obtain Class from JPA entity name?

Is there a way to find out the
java.lang.Class
that is the one having
#Entity(name = "X")?
In other words use the Entity Name to get the classname of the Entity, of course at runtime :)
All registered #Entitys are available by MetaModel#getEntities(). It returns a List<EntityType<?>> whereby the EntityType has a getName() method representing the #Entity(name) and a getJavaType() representing the associated #Entity class. The MetaModel instance is in turn available by EntityManager#getMetaModel().
In other words, here's the approach in flavor of an utility method:
public static Class<?> getEntityClass(EntityManager entityManager, String entityName) {
for (EntityType<?> entity : entityManager.getMetamodel().getEntities()) {
if (entityName.equals(entity.getName())) {
return entity.getJavaType();
}
}
return null;
}
I've raised an issue in the JPA spec to be able to load entity by entity name 4 years ago and still no development on the matter:
https://github.com/javaee/jpa-spec/issues/85

Jersey : How to send /pass Custom DTO to a Service class in Jersey

I am using Jersey Framework for developing my Webservices. I have a DTO object named UserInfo with setters and getters inside it. I am setting this DTO value initially when the user logs in. How can I pass this user-specific DTO to a Jersey service class?
I have tried setting them inside MultivaluedMap and Form but I was out of luck making it work.
This is my code :
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.add("queryTerm", "userdto");
Form f = new Form();
f.add("name", "1001D");
And this is the way I am trying to retrieve the Data.
public class HaiService {
#GET
#Produces("application/json")
#Consumes("application/json")
public String sayPlainTextHello(#Context UriInfo ui) {
MultivaluedMap queryParams=ui.getQueryParameters();
Iterator it=queryParams.keySet().iterator();
String theKey=null;
String returnString="";
while(it.hasNext()) {
theKey=(String)it.next();
System.out.println(queryParams.getFirst(theKey));
}
System.out.println("I am called");
return "Hi";
}
But I was out of luck. Typically my requirement is to store user-specific data on logon, and then retrive that inside the service class.
I am avoiding storing data in session because a user might login with multiple ID's under one browser, which produces the same session id, and there is a chance of data being overwritten for the first logged in user.

Spring List of interface type data binding - how?

Tried to find the answer on the Web but failed. Should be simple for pro Spring Devs... so here it comes:
In few words I want to bind the List of interface type: List to the form and get the data back (possibly modified by user via form. The problem is that it doesn't work :(
my code (short version) - command/model class which is passed to the form:
public class RoomsFormSearchResultCommand extends RoomsFormSearchCommand {
#SuppressWarnings("unchecked")
private List<IRoom> roomsList = LazyList.decorate(new ArrayList<Room>(),
FactoryUtils.instantiateFactory(Room.class));
public List<IRoom> getRoomsList() {
return roomsList;
}
public void setRoomsList(final List<IRoom> roomsList) {
this.roomsList = roomsList;
}
(...)
then in the form I use it like that (short version):
<form:form method="post" action="reserve" commandName="roomsResultsCmd">
(...)
<c:forEach var="room" items="${roomsResultsCmd.roomsList}"
varStatus="status">
<tr>
<td><form:input path="roomsList[${status.index}].roomNumber" readonly="true"/>
(...)
The form is displayed fine but after submitting it I get:
2012-01-22 21:31:55 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [wyspa] in context with path [/wyspa] threw exception [Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'roomsList[0]' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Illegal attempt to get property 'roomsList' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'roomsList' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Could not instantiate property type [com.wyspa.entity.IRoom] to auto-grow nested property path: java.lang.InstantiationException: com.wyspa.entity.IRoom] with root cause
org.springframework.beans.NullValueInNestedPathException: Invalid property 'roomsList' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Could not instantiate property type [com.wyspa.entity.IRoom] to auto-grow nested property path: java.lang.InstantiationException: com.wyspa.entity.IRoom
at org.springframework.beans.BeanWrapperImpl.newValue(BeanWrapperImpl.java:633)
at org.springframework.beans.BeanWrapperImpl.growCollectionIfNecessary(BeanWrapperImpl.java:863)
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:770)
at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:555)
(...)
The deal is then when I change the List to "instances" list everything works fine!
public class RoomsFormSearchResultCommand extends RoomsFormSearchCommand {
#SuppressWarnings("unchecked")
//notice that the List is now List<Room>
private List<Room> roomsList = LazyList.decorate(new ArrayList<Room>(),
FactoryUtils.instantiateFactory(Room.class));
In this case data is passed to the controller in proper way.
Since I am used to devlop on interfaces and I am pretty crazy about it I would REALLY prefer not to translate the List<IRoom> (which comes back from services) to List<Room> which seems to suit Spring. Is it possible to work with List<IRoom> in this case or Spring just doesn't support it?
//Of course Room implements IRoom - but I guess you already got that...
I would be VERY happy for any help/suggestions!
Best Regards,
Nirwan
I have exact the same problem. Changing to following won't fix the problem. It looks spring binding ignores the factory utils and tries to instantiate the null object itself:
#SuppressWarnings("unchecked")
private List<IRoom> roomsList = LazyList.decorate(new ArrayList<IRoom>(),
FactoryUtils.instantiateFactory(Room.class));
The workaround is to set auto grow nested path off in your controller:
#InitBinder protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.setAutoGrowNestedPaths(false);
super.initBinder(request, binder);
}
The problem is you'll lose the handy nested path like user.account.address.street. You have to make sure none of user, account, addresss is null. It does cause a lot of problems. That's why I came here, see if I can find better solution.
If you don't actually need the list to auto-grow, you can store the form object in the session to avoid the nasty side effects of disabling auto-growing nested paths.
#Controller
#SessionAttributes(types = RoomsFormSearchResultCommand.class)
public final class SearchController {
#InitBinder
protected void initBinder(final WebDataBinder binder) {
binder.setAutoGrowNestedPaths(false);
}
#RequestMapping(method = RequestMethod.GET)
public String showForm(final Model model) {
RoomsFormSearchResultCommand form = ... // create or load form
model.addAttribute(form);
}
#RequestMapping(method = RequestMethod.POST)
public String onSubmitUpdateCart(
#ModelAttribute final RoomsFormSearchResultCommand form,
final BindingResult result,
final SessionStatus status) {
// if result has no errors, just set status to complete
status.setComplete();
}
}
Try the following lines
#SuppressWarnings("unchecked")
private List<IRoom> roomsList = LazyList.decorate(new ArrayList<IRoom>(),
FactoryUtils.instantiateFactory(Room.class));
don't have time to try that myself, but it would make sense.

Update Entities with currentUser-ID

I'm trying to update each entity with an own #EntityListeners({ ModelListener.class }).
ModelListener implements #PrePersist with a method like this:
#PrePersist
protected void prePersist(final ModelBase modelBase) {
modelBase.setUpdatedBy(currentUser);}
My question is how to get the current user.
I already tried several things:
1) transport the information over EJB
#EJB
private UserModul userModul;
The userModul is always null, when prePersist is called. It seems the EJB-context is not available.
2) Fill a static ThreadLocal
static ThreadLocal<User> currentUser = new ThreadLocal<User>();
Because of the static attribute each classloader (each request in webservers) holds its own instance. I tried with a #WebFilter to fill the ThreadLocal. But it seems I have no faces context.
public void doFilter(final ServletRequest request, ...){
FacesContext.getCurrentInstance();} //always null
Also in request (com.sun.enterprise.web.pwc.connector.coyote.PwcCoyoteRequest) I don't find any information of loggedin user.
What is the most common way to save user information?