Marshalling List<String> with JAX-RS - rest

I'm used to working with jax-ws where a wsdl file is generated, and a client can then be generated based on this wsdl file and its xsd(s) using a maven plugin. Using this client is no hassle at at, and you don't have to really think about what happens in the background, like marshalling, http transfer and such.
I'm currently working on a jax-rs project using jaxb to unmarshal objects. One of the methods there returns a list of strings, but it seems that jaxb does not know how to marshal this, which is kinda surprising as it does know how to marshal a list of entities (ex, customers).
Also, I have written a client for the jax-rs service on my own, handling both http responses and unmarshalling of the payload using jaxb. Marshalling and unmarshalling with jaxb is a real hassle since it cannot automatically marshall or unmarshall list of entities that is added to its context, even less lists of strings.
I would like to know if there is some neat way to get all of this for free using restful webservices? This would have to be quite lightweight, and the clients must be easy to distribute.
Thanks!
Runar
The service method that is not working using jaxrs and jaxb:
#GET
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_XML)
public List<String> isCustomerLocked(#PathParam("customerId") Long customerId) {
}
Client code that attempts to marshall/unmarshall text payload. Classes added to the jaxbcontext not shown:
javax.xml.bind.Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(obj, stringwriter)
javax.xml.bind.Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.unmarshal(inputstream)

I'd use JAXB to wrap the data. For a simple List<String> this may look as overkill. But in most cases you want to un-/marshall Resource Representations, not simple objects.
Remember: REST ist not RPC!
If you really want tom un-/marshall List<String> write a JAX-RS Provider. But I'd prefer using JAXB.
S.java
#XmlRootElement
public class S {
private String s;
public S() {
}
public S(String s) {
this.s = s;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
Ss.java
#XmlRootElement(name="ss-wrapper")
public class Ss {
private List<S> ss;
public List<S> getSs() {
return ss;
}
public void setSs(List<S> ss) {
this.ss = ss;
}
public Ss(List<S> ss) {
this.ss = ss;
}
public Ss() {
}
}
JAX-RS class
#Path("/strings")
#GET
#Produces(MediaType.APPLICATION_XML)
public Response getListOfStrings() {
S s1 = new S("foo");
S s2 = new S("bar");
List<S> strings = new ArrayList<S>();
strings.add(s1);
strings.add(s2);
Ss ss = new Ss(strings);
return Response.ok(ss).build();
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ss-wrapper>
<ss>
<s>foo</s>
</ss>
<ss>
<s>bar</s>
</ss>
</ss-wrapper>

Related

Spring MVC 4.3 Rest dynamic filtering of fields

I'm using Spring MVC 4.3.2 with #RestController and I would like to be able to dynamically filter JSON properties specified by our clients in the request.
To keep things easy, let's say I have a Person POJO that contains firstName and lastName fields. This POJO is generated, that's why you will see that I'm using a mixin.
Let's say one client want to retrieve all the persons' firstName, he will call our rest endpoint like this : http://localhost/persons&fields=firstName. And the json will only contains the firstName, not the lastName fields.
Here is my Controller:
#RestController
public class RestPersonController implements PersonApi {
#Autowired
private PersonService personService;
#Autowired
private ObjectMapper objectMapper;
#Override
#GetMapping(path = "/persons")
public ResponseEntity<List<Person>> getPersons(
#RequestParam(required = false) String fields) {
List<Person> persons = this.personService.getPersons();
// Filter fields
if (StringUtils.isNotBlank(fields)) {
configureFilter(fields);
}
return ResponseEntity.status(HttpStatus.OK).body(persons);
}
private void configureFilter(String fields) {
Set<String> fieldsToKeep = new HashSet<>();
fieldsToKeep.add(fields);
objectMapper.addMixIn(Person.class, PersonDynamicFilterMixIn.class);
FilterProvider filterProvider = new SimpleFilterProvider().
addFilter("dynamicFilter", SimpleBeanPropertyFilter.filterOutAllExcept(fieldsToKeep));
objectMapper.setFilterProvider(filterProvider);
}
}
As you can see, the return of the method is ResponseEntity<List<Person>> (I can't change it to JacksonMappingValue, it is from the PersonApi interface) and we are returning with ResponseEntity.status(HttpStatus.OK).body(persons) (so I can't manually write the JSON response with the objectMapper myself);.
Under the hood, Spring is using Jackson ObjectMapper to do the conversion. I would like to configure that ObjectMapper when the fields request parameter is specified.
I also want the ObjectMapper configuration to be the same as Spring's default.
I can't use JsonViews because otherwise it is not dynamic anymore.
Could someone show me how I need to configure Spring to do that ? I tried to use these kind of things in SpringConfiguration, but then I end up with some of my tests not working anymore. For some tests, if I run a single test it passes, if I run the whole test suite that test doesn't pass anymore ...
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(objectMapper()));
}
#Bean
public ObjectMapper objectMapper() {
return Jackson2ObjectMapperBuilder.json().build();
}
Thank you for your help !

JAX-RS - MOXy JAXB - ClassCastException when transforming List<List<Object>> to JSON

In my app I'm using MOXy JAXB with JAX-RS (Jersey) on Glassfish server,
I have the following REST webservice:
#Named
#RequestScoped
#Path("/product")
public class ProductService extends BaseServiceFacade<Product, Integer, ProductVO> {
#EJB(mappedName="java:global/myAppEAR/myAppEJB/ProductServiceRest")
ProductServiceRestRemote productServiceRestRemote;
// ...
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/featuredlists")
public List<List<ProductVO>> featuredlists() {
return productServiceRestRemote.featuredlists();
}
}
When I try to test the REST service accessing:
localhost:8080/atlanteusPortal/rest/product/featuredlists
I get:
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.getDomainClass(MOXyJsonProvider.java:267)
If I put a debug breakpoint before the method return I can see that the List<List<ProductVO>> chunkList is populated but it's not transformed into JSON
Can someone point out a solution to send a List<List<Object>> type via JSON using JAX-RS MOXy and Jersey?
I solved the issue using a workaround encapsulating List of Lists inside an object
called ProductListVO:
#XmlRootElement
public class ProductListVO extends BaseVO<String> {
private List<ProductVO> productVOs;
public List<ProductVO> getProductVOs() {
return productVOs;
}
public void setProductVOs(List<ProductVO> productVOs) {
this.productVOs = productVOs;
}
public static ProductListVO buildVO(List<Product> t) {
ProductListVO vo = new ProductListVO();
List<ProductVO> prodVOs = new ArrayList<ProductVO>();
StringBuilder sb = new StringBuilder();
for (Product product : t) {
sb.append(product.getId()).append('-');
prodVOs.add(ProductVO.buildVO(product));
}
vo.setId(sb.substring(0, sb.length() - 1));
vo.setProductVOs(prodVOs);
return vo;
}
}
in Service method:
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/featured")
public List<ProductListVO> featuredlists() {
return productServiceRestRemote.featuredLists();
}

How do I request a subset of XMLElements using MOXy?

I have a RESTful service that needs to return only a few of the XmlElements if "selectors" are submitted with the request. The URL will take the form of:
/merchants/{merchantId}/profile?selectors=<field1|field2|....|fieldN>
The selectors are optional, and so far I have implemented the service for the full set of elements to be returned for {merchantId} without selectors specified. Now I'm trying to figure out how to add in this added functionality. I'm sure this is covered in documentation but I can't find where. Any RTFM pointers would be appreciated. Thanks.
EclipseLink JAXB (MOXy) does not currently offer a mechanism to selectively indicate which fields/properties are included on a per marshal operation. This sounds like an interesting use case. I would appreciate if you could enter this as an enhancement request using the following link:
https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink
Below is an example of how you could use a stateful XmlAdapter to implement this use case by exploiting the fact that a JAXB (JSR-222) will not marshal an element when the value is null (see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).
FieldAdapter
Since we are going to leverage stateful XmlAdapters we're going to need one per field. Since all our XmlAdapters will perform the same logic we can create a super class that the others can extend from.
package forum13094195;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FieldAdapter<T> extends XmlAdapter<T, T> {
private boolean include;
public FieldAdapter() {
this.include = true;
}
public FieldAdapter(boolean include) {
this.include = include;
}
#Override
public T marshal(T value) throws Exception {
if(include) {
return value;
}
return null;
}
#Override
public T unmarshal(T value) throws Exception {
return value;
}
}
Field1Adapter
package forum13094195;
public class Field1Adapter extends FieldAdapter<String> {
public Field1Adapter() {}
public Field1Adapter(boolean include) {
super(include);
}
}
Field2Adapter
package forum13094195;
public class Field2Adapter extends FieldAdapter<Integer>{
public Field2Adapter() {}
public Field2Adapter(boolean include) {
super(include);
}
}
Field3Adapter
package forum13094195;
public class Field3Adapter extends FieldAdapter<String> {
public Field3Adapter() {}
public Field3Adapter(boolean include) {
super(include);
}
}
Merchant
The #XmlJavaTypeAdapter annotation is used to specify an XmlAdapter on a field/property.
package forum13094195;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Merchant {
#XmlJavaTypeAdapter(Field1Adapter.class)
String field1;
#XmlJavaTypeAdapter(Field2Adapter.class)
int field2;
#XmlJavaTypeAdapter(Field3Adapter.class)
String field3;
}
Demo
The demo code below demonstrates how to set a stateful XmlAdapter on the Marshaller.
package forum13094195;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Merchant.class);
Merchant merchant = new Merchant();
merchant.field1 = "A";
merchant.field2 = 2;
merchant.field3 = "C";
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(merchant, System.out);
marshaller.setAdapter(new Field1Adapter(false));
marshaller.setAdapter(new Field2Adapter(false));
marshaller.setAdapter(new Field3Adapter(true));
marshaller.marshal(merchant, System.out);
}
}
Output
Below is the output from running the demo code. By default the entire object is marshalled out. The second document marshalled does not contain the fields we excluded.
<?xml version="1.0" encoding="UTF-8"?>
<merchant>
<field1>A</field1>
<field2>2</field2>
<field3>C</field3>
</merchant>
<?xml version="1.0" encoding="UTF-8"?>
<merchant>
<field3>C</field3>
</merchant>
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
In EclipseLink 2.5.0 we released a new feature called Object Graphs that enables you to marshal/unmarshal a subset of mapped fields/properties.
// Create the Object Graph
ObjectGraph subset = JAXBHelper.getJAXBContext(jc).createObjectGraph(Merchant.class);
subset.addAttributeNodes("field1", "field1", "fieldN");
// Output XML - Based on Object Graph
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, subset);
marshaller.marshal(customer, System.out);
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-partial-models-on.html

Jaxb in Restful webservices

I have worked on Web services using Jaxb earlier. I geneated Java from xsd, and then I used to post the xml request to the specified URL using HTTP post. Recently I heard about this Restful web services, on reading I felt that what I had been doing earlier is the restful web service only. But, I am not sure about it if its the same thing.
Can anyone explain please.
It sounds like you have been creating the same types of RESTful services. You may be referring to is JAX-RS with is a standard that defines an easier way of creating RESTful services where JAXB is the standard binding layer for the application/xml media type. Below is an example service:
package org.example;
import java.util.List;
import javax.ejb.*;
import javax.persistence.*;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
#Stateless
#LocalBean
#Path("/customers")
public class CustomerService {
#PersistenceContext(unitName="CustomerService",
type=PersistenceContextType.TRANSACTION)
EntityManager entityManager;
#POST
#Consumes(MediaType.APPLICATION_XML)
public void create(Customer customer) {
entityManager.persist(customer);
}
#GET
#Produces(MediaType.APPLICATION_XML)
#Path("{id}")
public Customer read(#PathParam("id") long id) {
return entityManager.find(Customer.class, id);
}
#PUT
#Consumes(MediaType.APPLICATION_XML)
public void update(Customer customer) {
entityManager.merge(customer);
}
#DELETE
#Path("{id}")
public void delete(#PathParam("id") long id) {
Customer customer = read(id);
if(null != customer) {
entityManager.remove(customer);
}
}
}
For More Information
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-45.html
When it comes to say 'RESTful', it's just an convention of HTTP methods and url patterns.
CRUD METHOD URL RESPONSE DESCRIPTION
----------------------------------------------------------------
CREATE POST http://www.doma.in/people 202 Creates a new person with given entity body
READ GET http://www.doma.in/people 200
READ GET http://www.doma.in/people/1 200 404 Reads a single person
UPDATE PUT http://www.doma.in/people/2 204 Updates a single person with given entity body
DELETE DELETE http://www.doma.in/people/1 204 Deletes a person mapped to given id(1)
You can even implement those kind of contracts with Sevlets. Actually I had done with Sevlets before the era of JAX-RS.
And your life will be much more easier when you use JAX-RS.
Here comes a slightly modified version of Mr. Blaise Doughan's.
Nothing's wrong with Mr. Blaise Doughan's code.
I just want to add more for above url patterns.
One of great things that JAX-RS can offer is that you can serve XMLs and JSONs as clients want if you have those fine JAXB classes. See #Producess and #Consumess for those two formats in same method.
When client want to receive as XML with Accept: application/xml, they just get the XML.
When client want to receive as JSON with Accept: application/json, they just get the JSON.
#Path("/customers");
public class CustomersResource {
/**
* Reads all person units.
*/
#POST
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response read() {
final List<Customer> listed = customerBean.list();
final Customers wrapped = Customers.newInstance(listed);
return Response.ok(wrapped).build();
}
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response createCustomer(final Customer customer) {
entityManager.persist(customer);
return Response.created("/" + customer.getId()).build();
}
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/{id: \\d+}")
public Response read(#PathParam("id") final long id) {
final Customer customer = entityManager.find(Customer.class, id);
if (customer == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(customer).build();
}
#PUT
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public void updateCustomer(final Customer customer) {
entityManager.merge(customer);
}
#DELETE
#Path("/{id: \\d+}")
public void deleteCustomer(#PathParam("id") final long id) {
final Customer customer = entityManager.find(Customer.class, id);
if (customer != null) {
entityManager.remove(customer);
}
return Response.status(Status.NO_CONTENT).build();
}
}
Say you want to serve some images?
#GET
#Path("/{id: \\d+}")
#Produces({"image/png", "image/jpeg"})
public Response readImage(
#HeaderParam("Accept") String accept,
#PathParam("id") final long id,
#QueryParam("width") #DefaultValue("160") final int width,
#QueryParam("height") #DefaultValue("160") final int height) {
// get the image
// resize the image
// make a BufferedImage for accept(MIME type)
// rewrite it to an byte[]
return Response.ok(bytes).build();
// you can event send as a streaming outout
return Response.ok(new StreamingOutput(){...}).build();
}

How to access multiple resources in a single request : Jersey Rest

I am trying to a find a good design for the following scenario.
I have a POST rest service which will be given an array of services as data. And which should in turn be calling them one by one to aggregate results on the server and send them back to the client.
#Path("/resource1")
#Path("/resource2")
#Path("/collection")
Post data to /collection
{["serviceName": "resource1", "data":"test1"], ["serviceName":"resource2","data":"test2"]}
The reason i need the resource1 and resource2 are, because those services can be called standalone also. I want to reuse the same setup if possible.
Is there any way to do this.
I am using jersey with spring.
Not sure what these resources have in common. If the post method has the same signature for all of them, you could have an abstract class or interface they implement defining the post method and can try using ResourceContext.matchResource to do this. E.g. something like this:
public abstract class AbstractResource {
public abstract String post(Object data);
}
#Path("/resource1")
public class Resource1 extends AbstractResource {
#POST
public String post(String data) {
// do something
}
}
#Path("/collection")
public class CollectionResource {
#Context
private ResourceContext rc;
#POST
#Consumes("application/json")
public String post(List<PostRequest> postRequests) {
StringBuilder result = new StringBuilder();
for (PostRequest pr : postRequests) {
// should wrap this in try-catch
AbstractResource ar = rc.matchResource(pr.resource,
AbstractResource.class);
sb.append(ar.post(pr.data));
}
return result.toString();
}
}
#XmlRootElement
public class PostRequest {
public String resource;
public String data;
}
Hopefully you got the idea and will be able to play with it and tweak it to fit your needs.