[
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