oodt-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Ross Laidlaw (JIRA)" <j...@apache.org>
Subject [jira] [Issue Comment Deleted] (OODT-612) Implement a JAX-RS interface to access File Manager product information and metadata as configurable RDF streams
Date Sun, 18 Aug 2013 12:22:48 GMT

     [ 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

Mime
View raw message