Showing posts with label MOXy. Show all posts
Showing posts with label MOXy. Show all posts

Friday, November 9, 2012

J2EE Application with MOXy

As part of my refactoring of my existing J2EE application using JAX-RS, I wanted to try and reduce the JAXB custom code in place. Previously I was using Jackson and Jersey for JSON serialization using the "Mapped" notation. This involved creating my own JAXBContext object and I was required to create some special mapping processing so that I could map an attribute as etiher an array, non-string or other value.

Since I was going to use the latest version of eclipselink with the refactoring (currently EclipseLink 2.4.1) I decided to check out using MOXy which I had always had my eye on. My goal was to avoid the mapping approach (which was inherently buggy since it required me to remember to add any field to a property file for proper serialization and I could not serialize the same named field differently for different types - ie. field X was always to be type Y). MOXy seemed to like it might be able to address these concerns.

To leverage MOXy in your environment, you need to include it as a provider. In my case I included it in my JAX-RS Application class and set some configuration properties on it. Here is an example of my current application (note I am using Package scanning for JAX-RS resources/providers in Jersey)


public class WebApplication extends PackagesResourceConfig {

 @SuppressWarnings("unused")
 private static final Logger logger = Logger.getLogger(WebApplication.class.getName());

 public WebApplication() {
  super("org.javad.web.providers","org.javad.preferences.model.resources","org.javad.stamp.model.resources");
 }

 @Override
 public Set<Object> getSingletons() {
  MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();

  moxyJsonProvider.setAttributePrefix("");
  moxyJsonProvider.setFormattedOutput(true);
  moxyJsonProvider.setIncludeRoot(false);
  moxyJsonProvider.setMarshalEmptyCollections(true);
  moxyJsonProvider.setValueWrapper("$");

  Set<Object> set = new HashSet<Object>();
  set.add(moxyJsonProvider);
  return set;
 }
}

You can read more on the MOXy settings here.

You then need to reference your Application in your web.xml as part of your JAX-RS resource mapping:
<init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>org.javad.web.WebApplication</param-value>
</init-param>
There are other ways to configure the MOXy configuration, but this is the one that I choose and seemed to fit well for my environment (since I was using a PackageResourceConfig from Jersey for finding my resources).

Since several of my JPA entities included a foreign reference to another JPA entity, I wanted only the ID of the foreign entity to be serialized. Previously I had been using the @XmlIDRef to drive this for serialization, but I had difficulty using this with MOXy. Instead, I needed to declare an XML Adapter to do the serialization and deserialization of the field. This is done using the @XmlJavaTypeAdapter annotation. For my album entity, the foreign key reference to the stamp collection entity looks like this:
@XmlJavaTypeAdapter(StampCollectionRefAdapter.class)
@XmlElement(name = StampFormConstants.STAMP_COLLECTION_REF )
@ManyToOne(optional=false,fetch=FetchType.EAGER)
@JoinColumn(name="COLLECTION_ID",nullable=false)
private StampCollection collection;
In the StampCollectionRefAdapter I transfer the StampCollection entity to an ID and vice versa (when deserializing from a JSON posting). One issue I ran into was that I could not inject the StampCollectionService into this class. This is because these classes are initialized by MOXy (it would appear) prior to the CDI and EntityManagerFactory initialization, so I needed to obtain the EntityManagerFactory which was used in my initialization servlet listener (this sets up a few configurations in my environment). I had tried declaring this class as a @Stateless EJB but did not have any luck (likely because it was constructed explicitly). I may look into this further later, but for now the only way I could get access to the service to transfer the ID back to the physical entity was to leveage the entity manager factory from the initialization listener through a static context (note: this would might fail if I tried to perform any transactions in against this, but should work for normal queries)
public class StampCollectionRefAdapter extends XmlAdapter<StampCollectionRefAdapter.StampCollectionRefType, StampCollection> {

 public StampCollectionRefAdapter() {
  super();
 }
 
 @Override
 public StampCollection unmarshal(StampCollectionRefType v) throws Exception {
  StampCollection collection = null;
  EntityManagerFactory emf = SessionInitializer.getEntityManagerFactory();
  if( emf != null ) {
   EntityManager em = emf.createEntityManager();
   if( em != null ) {
    collection = em.find(StampCollection.class, v.id);
   }
  }
  
  return collection;
 }

 @Override
 public StampCollectionRefType marshal(StampCollection v) throws Exception {
  StampCollectionRefType t = new StampCollectionRefType();
  t.id = v.getId().longValue();
  return t;
 }

 public static class StampCollectionRefType {
  @XmlValue
  public long id;
 }
}

I am having a small issue with arrays of Entities. I have a serializable class which is Genericized and has two fields:
  • total - representing the total number of objects (not necessarily the items returned)
  • items - the List of entities to be returned
The first oddity was I could not define a getItems() method on the abstract class. Instead I had to declare it on the concrete class that implements the Generic type. The reason for this is I want my array of items to be the plural of the actual items contained within it. This requires me to declare the collection similiar to the following:
@XmlRootElement(name="list")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class AlbumModelList extends AbstractModelList<Album> {
 @Override
 @XmlElement(name="albums")
 public List<Album> getItems() {
  return items;
 }
}
The resulting JSON will thus look as I intended:
{
    "total": 5,
    "albums": [
        {
            "id": "2000",
            "name": "Test Collection X",
            "countryRefs": [],
            "stampCollectionRef": 1
        },
        {
            "id": "1",
            "name": "Test Collection x2",
            "countryRefs": [],
            "stampCollectionRef": 1
        },
        {
            "id": "2",
            "name": "Test Collection x4",
            "countryRefs": [],
            "stampCollectionRef": 1
        },
        {
            "id": "3",
            "name": "Test Collection x5",
            "countryRefs": [],
            "stampCollectionRef": 1
        },
        {
            "id": "4",
            "name": "Test Collection x6",
            "countryRefs": [],
            "stampCollectionRef": 1
        }
    ]
}
But if I use a content type of XML (which I am not using very often) the Array is not showing up as am <albums/> element that contains <album/> nodes but instead looks like the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list>
    <total>5</total>
    <albums id="2000">
        <name>Test Collection X</name>
        <stampCollectionRef>1</stampCollectionRef>
    </albums>
    <albums id="1">
        <name>Test Collection x2</name>
        <stampCollectionRef>1</stampCollectionRef>
    </albums>
    <albums id="2">
        <name>Test Collection x4</name>
        <stampCollectionRef>1</stampCollectionRef>
    </albums>
    <albums id="3">
        <name>Test Collection x5</name>
        <stampCollectionRef>1</stampCollectionRef>
    </albums>
    <albums id="4">
        <name>Test Collection x6</name>
        <stampCollectionRef>1</stampCollectionRef>
    </albums>
</list>


Not sure what I am missing here, but I need to address this at some point along with modifying my entity IDs from wrapper types to primitives.