[ 
https://issues.apache.org/jira/browse/OODT-612?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Ross Laidlaw updated OODT-612:
------------------------------

    Comment: was deleted

(was: I've been experimenting with a different design that takes advantage of 
some of the built-in features of Apache CXF and capabilities of JAXRS.

h4. Summary

We could annotate our resource classes with JAXB annotations and JAXRS will 
then automatically marshal these classes into appropriate responses.  We can 
map response types to file extensions (used in the URL) with the 
'jaxrs.extensions' property in our web.xml.  CXF will then select the 
appropriate provider to marshal the object into the desired format.  CXF 
already has various data bindings for XML, JSON, etc. but we can also write our 
own custom providers to marshal objects into our desired formats (e.g. RDF, 
RSS).  The providers can be configured using the 'jaxrs.providers' parameter in 
our web.xml.  With XML as our base format, it's also possible for us to use 
XSLT transformations in our providers to transform the XML to other formats.


h4. Details

In our web.xml we can use the 'jaxrs.extensions' parameter to map extensions to 
MIME types, as follows:

{code:xml|borderStyle=solid}
<init-param>
  <param-name>jaxrs.extensions</param-name>
  <param-value>
    xml=application/xml
    json=application/json
    atom=application/atom+xml
    rdf=application/rdf+xml
    rss=application/rss+xml
    zip=application/zip
  </param-value>
</init-param>
{code}

With this setting, we can add file extensions to the URL and JAXRS/CXF will 
attempt to return the MIME type associated with that extension, for example:

{panel:title=Example URLs|borderStyle=solid}
http://host/fmprod/service/reference.zip?...
http://host/fmprod/service/product.xml?...
http://host/fmprod/service/dataset.rdf?...
http://host/fmprod/service/reference.rss?...
http://host/fmprod/service/product.json?...
http://host/fmprod/service/dataset.atom?...
{panel}

Then in our service class, we can annotate our response methods with the same 
MIME types and JAXRS will attempt to select the appropriate provider to provide 
the response:

{code:java|borderStyle=solid}
@GET
@Path("product")
@Produces({"application/xml", "application/json", "application/atom+xml",
  "application/rdf+xml", "application/rss+xml", "application/zip"})
public ProductResource getProduct(...)
{
  ...
}
{code}

Providers can be specified in web.xml using the 'jaxrs.providers' parameter.  
For example, CXF has a 'built-in' JSON capability, which can be enabled using 
the JSONProvider (along with JAXB annotated resources):

{code:xml|borderStyle=solid}
<init-param>
  <param-name>jaxrs.providers</param-name>
  <param-value>
    org.apache.cxf.jaxrs.provider.json.JSONProvider
  </param-value>
</init-param>
{code}

As a side note, the following extra POM dependencies are needed for CXF data 
bindings and for example JSON using Jettison (other providers such as Jackson 
can be used instead):

{code:xml|borderStyle=solid}
<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-extension-providers</artifactId>
  <version>2.6.8</version>
</dependency>
<dependency>
  <groupId>org.codehaus.jettison</groupId>
  <artifactId>jettison</artifactId>
  <version>1.3.4</version>
</dependency>
{code}

We can also write our own providers for different formats (e.g. RDF, RSS, etc) 
and add them to the jaxrs.providers list.  CXF will then link the providers to 
the MIME types and deliver our resources in the desired format.

To write a provider, we should annotate the class with @Provider and 
@Produces("[target_mime_type]") and implement 
javax.ws.rs.ext.MessageBodyWriter<ResourceType>, for example: 

{code:java|title=ProductRdfWriter|borderStyle=solid}
@Provider
@Produces("application/rdf+xml")
public class ProductRdfWriter implements MessageBodyWriter<ProductResource>
{
  @Override
  public long getSize(ProductResource resource, Class<?> type, Type genericType,
    Annotation[] annotations, MediaType mediaType)
  {
    return -1;
  }

  @Override
  public boolean isWriteable(Class<?> type, Type genericType,
    Annotation[] annotations, MediaType mediaType)
  {
    return type.isAnnotationPresent(XmlRootElement.class);
  }

  @Override
  public void writeTo(ProductResource resource, Class<?> type, Type genericType,
    Annotation[] annotations, MediaType mediaType,
    MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
    throws IOException, WebApplicationException
  {
    // write RDF representation to the 'entityStream' OutputStream.
  }
}
{code}

The 'writeTo' method is where we write the desired output to the output stream 
that's sent in the response.  In this method, we can use whatever we want to 
create the format we need.

For CXF/JAXRS to pick up our custom writers, we should add them to the list of 
providers in web.xml, e.g. as follows:

{code:xml|borderStyle=solid}
<init-param>
  <param-name>jaxrs.providers</param-name>
  <param-value>
    org.apache.cxf.jaxrs.provider.json.JSONProvider,
    org.apache.oodt.cas.product.service.ProductRdfWriter
  </param-value>
</init-param>
{code}

To make the connection with our resource classes, I've implemented a single 
service class called 'CasProductService' to contain the @GET methods and handle 
all requests:

{code:java|title=CasProductService.java|borderStyle=solid}
public class CasProductService
{
  @Context
  private ServletContext context;

  @GET
  @Path("product")
  @Produces({"application/xml", "application/json", "application/atom+xml",
   "application/rdf+xml", "application/rss+xml", "application/zip"})
  public ProductResource getProduct(...)
  {
    ...
  }

  @GET
  @Path("dataset")
  @Produces({"application/xml", "application/json", "application/atom+xml",
   "application/rdf+xml", "application/rss+xml", "application/zip"})
  public DatasetResource getDataset(...)
  {
    ...
  } 
}
{code}

This is specified as a service class in web.xml using the 
'jaxrs.serviceClasses' parameter:

{code:xml|borderStyle=solid}
<init-param>
  <param-name>jaxrs.serviceClasses</param-name>
  <param-value>
    org.apache.oodt.cas.product.service.CasProductService
  </param-value>
</init-param>
{code}

Our JAXB annotated resource classes can then be used as the return types for 
the methods.  For example, shown partially below are three JAXRS annotated 
resource classes for products, metadata and references (ProductResource, 
MetadataResource and ReferenceResource):

{code:java|title=ProductResource.java|borderStyle=solid}
@XmlRootElement(name="product")
@XmlAccessorType(XmlAccessType.NONE)
public class ProductResource
{
  private Product product;
  private MetadataResource metadata;
  private List<ReferenceResource> references = new 
ArrayList<ReferenceResource>();

  public ProductResource() { }

  @XmlElement(name="id")
  public String getProductId()
  {
    return product.getProductId();
  }

  @XmlElement(name="name")
  public String getProductName()
  {
    return product.getProductName();
  }

  @XmlElement(name="structure")
  public String getProductStructure()
  {
    return product.getProductStructure();
  }

  @XmlElement
  public MetadataResource getMetadata()
  {
    return metadata;
  }

  @XmlElementWrapper(name="references")
  @XmlElement(name="reference")
  public List<ReferenceResource> getReferences()
  {
    return references;
  }
}
{code}

{code:java|title=ReferenceResource.java|borderStyle=solid}
@XmlRootElement(name="reference")
@XmlAccessorType(XmlAccessType.NONE)
public class ReferenceResource
{
  private Reference reference;

  @XmlElement
  public String getDataStoreReference()
  {
    return reference.getDataStoreReference();
  }

  @XmlElement
  public long getFileSize()
  {
    return reference.getFileSize();
  }

  @XmlElement
  public String getMimeType()
  {
    MimeType m = reference.getMimeType();
    if (m != null)
    {
      return m.getName();
    }
    return null;
  }

  @XmlElement
  public String getOrigReference()
  {
    return reference.getOrigReference();
  }
}
{code}

{code:java|title=MetadataResource.java|borderStyle=solid}
@XmlRootElement(name="metadata")
@XmlAccessorType(XmlAccessType.NONE)
public class MetadataResource
{
  private Metadata metadata;

  @XmlElement(name="metadata")
  public Map<String, String> getMetadatas()
  {
    Map<String, String> map = new HashMap<String, String>();
    List<String> keys = metadata.getAllKeys();
    for (String key : keys)
    {
      map.put(key, metadata.getMetadata(key));
    }
    return map;
  }
}
{code}

Using the above setup, an example output is shown below for a request using 
".xml" as the file extension in the URL (e.g. 
http://host/fmprod/service/product.xml?productID=1787a257-df87-11e2-8a2d-e3f6264e86c5):

{code:xml|borderStyle=solid}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product>
  <id>1787a257-df87-11e2-8a2d-e3f6264e86c5</id>
  <name>test.txt</name>
  <structure>Flat</structure>
  <metadata>
    <entry>
      <key>FileLocation</key>
      <value>/usr/local/oodt/filemgr/repository/test.txt</value>
    </entry>
    <entry>
      <key>ProductType</key>
      <value>GenericFile</value>
    </entry>
    <entry>
      <key>CAS.ProductId</key>
      <value>1787a257-df87-11e2-8a2d-e3f6264e86c5</value>
    </entry>
    <entry>
      <key>ProductStructure</key>
      <value>Flat</value></entry>
    <entry>
      <key>MimeType</key>
      <value>text/plain</value></entry>
    <entry>
      <key>CAS.ProductName</key>
      <value>test.txt</value></entry>
    <entry>
      <key>CAS.ProductReceivedTime</key>
      <value>2013-06-28T01:10:08.308Z</value>
    </entry>
    <entry>
      <key>Filename</key>
      <value>test.txt</value>
    </entry>
  </metadata>
  <references>
    <reference>
      
<dataStoreReference>file:/usr/local/oodt/filemgr/repository/test.txt/test.txt</dataStoreReference>
      <fileSize>10</fileSize>
      <mimeType>text/plain</mimeType>
      <origReference>file:///tmp/test.txt</origReference>
    </reference>
  </references>
</product>
{code}

And here's the output after changing the extension from ".xml" to ".json" (i.e. 
http://host/fmprod/service/product.xml?productID=1787a257-df87-11e2-8a2d-e3f6264e86c5):

{code:borderStyle=solid}
{"product":
  {"id":"1787a257-df87-11e2-8a2d-e3f6264e86c5",
   "name":"test.txt",
   "structure":"Flat",
   "metadata":
    {"entry":[
      
{"key":"FileLocation","value":"\/usr\/local\/oodt\/filemgr\/repository\/test.txt"},
      {"key":"ProductType","value":"GenericFile"},
      {"key":"CAS.ProductId","value":"1787a257-df87-11e2-8a2d-e3f6264e86c5"},
      {"key":"ProductStructure","value":"Flat"},
      {"key":"MimeType","value":"text\/plain"},
      {"key":"CAS.ProductName","value":"test.txt"},
      {"key":"CAS.ProductReceivedTime","value":"2013-06-28T01:10:08.308Z"},
      {"key":"Filename","value":"test.txt"}]
    },
   "references":
    {"reference":
     
{"dataStoreReference":"file:\/usr\/local\/oodt\/filemgr\/repository\/test.txt\/test.txt",
      "fileSize":10,
      "mimeType":"text\/plain",
      "origReference":"file:\/\/\/tmp\/test.txt"}
    }
  }
}
{code}

It looks like it should be possible to use this approach to return files and 
zip archives as well, either by returning File objects directly from the 
service class or returning a resource marshalled via a custom provider (e.g. 
ProductZipWriter).
)
    
> Implement a JAX-RS interface to access File Manager product information and 
> metadata as configurable RDF streams
> ----------------------------------------------------------------------------------------------------------------
>
>                 Key: OODT-612
>                 URL: https://issues.apache.org/jira/browse/OODT-612
>             Project: OODT
>          Issue Type: Sub-task
>          Components: product server
>    Affects Versions: 0.6
>            Reporter: Ross Laidlaw
>            Assignee: Ross Laidlaw
>              Labels: gsoc2013
>             Fix For: 0.7
>
>
> Add Java classes to the org.apache.oodt.cas.product.service package and add 
> XML configuration files to allow File Manager product information and 
> metadata to be accessible as RDF streams via a JAX-RS RESTful interface 
> (URLs).
> The URLs should allow information on single products to be retrieved by 
> product ID, information on product sets to be retrieved by product type and 
> information to be retrieved for any transfers in progress.
> The new JAX-RS interface should also maintain backwards-compatibility with 
> the URLs supported by the servlets in the org.apache.oodt.cas.product.rdf 
> package, as detailed in the user guide: 
> https://cwiki.apache.org/confluence/display/OODT/File+Manager+REST+API.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to