Author: rwesten
Date: Wed Feb  8 13:09:47 2012
New Revision: 1241888

URL: http://svn.apache.org/viewvc?rev=1241888&view=rev
Log:
STANBOL-481: Implementation of the MessageBodyReader/Writer for ContentItmes 
including unit tests for both

They are not yet used by any Resource but they are fully functionally

Other changes:

* Corrected several Bugs/Issues in the ContentItemHelper utility
* Added required dependencies (see also comments of STANBOL-481) to the parent 
POM and the commons bundle list

Added:
    
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/
    
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/ContentItemReader.java
   (with props)
    
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/
    
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ContentItemWriter.java
   (with props)
    incubator/stanbol/trunk/enhancer/jersey/src/test/java/
    incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/
    incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/
    incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/
    
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/
    
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/
    
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/ContentItemReaderWriterTest.java
   (with props)
Modified:
    
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/ContentItem.java
    
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemHelper.java
    
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemImpl.java
    incubator/stanbol/trunk/enhancer/jersey/pom.xml
    
incubator/stanbol/trunk/launchers/bundlelists/stanbolcommons/src/main/bundles/list.xml
    incubator/stanbol/trunk/parent/pom.xml

Modified: 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/ContentItem.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/ContentItem.java?rev=1241888&r1=1241887&r2=1241888&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/ContentItem.java
 (original)
+++ 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/ContentItem.java
 Wed Feb  8 13:09:47 2012
@@ -78,18 +78,28 @@ public interface ContentItem {
      * A content item may consists of multiple parts, while the part with 
index 0 should always be a blob,
      * higher position may be used by Enhancer to story arbitrary objects, 
such objects can be used for 
      * accessing the precomputations of EnhancementEngines previous in the 
chain.
-     * 
+     * @throws NoSuchPartException if no part with the parsed index exists
+     * @throws ClassCastException if the class of the part is not compatiple 
with
+     * the requested class
+     * @throws IllegalArgumentException if <code>null</code> is parsed as
+     * clazz.
      */
     <T> T getPart(int index, Class<T> clazz) throws NoSuchPartException;
     
     /**
      * Each part of the content item has a URI. EnhancementEngines typically 
access parts by their Uri as the
      * position may vary depending on the chain.
+     * @throws NoSuchPartException if no part with the parsed uri exists
+     * @throws ClassCastException if the class of the part is not compatiple 
with
+     * the requested class
+     * @throws IllegalArgumentException if <code>null</code> is parsed as
+     * uri or clazz.
      */
     <T> T getPart(UriRef uri, Class<T> clazz) throws NoSuchPartException;
     
     /**
      * Get the uri of the part at the specified index
+     * @throws NoSuchPartException if no part with the parsed index exists
      */
     UriRef getPartUri(int index) throws NoSuchPartException;
 
@@ -100,6 +110,8 @@ public interface ContentItem {
      * @param object the part
      * @return the part replaced by the parsed object or <code>null</code> if
      * no part with the parsed URI was present
+     * @throws IllegalArgumentException if <code>null</code> is parsed as
+     * uriRef or object.
      */
     Object addPart(UriRef uriRef, Object object);
 }

Modified: 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemHelper.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemHelper.java?rev=1241888&r1=1241887&r2=1241888&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemHelper.java
 (original)
+++ 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemHelper.java
 Wed Feb  8 13:09:47 2012
@@ -27,6 +27,9 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -230,7 +233,9 @@ public class ContentItemHelper {
      * set only contains lower case values!
      * <li> A read lock on the parsed {@link ContentItem} is applied while
      * searching for a fitting {@link Blob}
-     * </ul>
+     * </ul><p>
+     * In contrast to the contentPart related methods of the {@link 
ContentItem}
+     * this method does NOT throw {@link NoSuchPartException}.
      * @param ci the contentITem
      * @param mimeTypes List of possible mimeTypes
      * @return the {@link UriRef URI} and the {@link Blob content} of the 
content 
@@ -253,15 +258,19 @@ public class ContentItemHelper {
             do {
                 try {
                     cpUri = ci.getPartUri(index);
-                    if(cpUri != null){
+                    index++;
+                    try {
                         Blob blob = ci.getPart(cpUri, Blob.class);
-                        if(blob != null && mimeTypes.contains(
-                            blob.getMimeType().toLowerCase())){
+                        
if(mimeTypes.contains(blob.getMimeType().toLowerCase())){
                             return Collections.singletonMap(cpUri, blob)
                                     .entrySet().iterator().next();
                         } // else no match
-                    } // else no more parts
-                } catch (NoSuchPartException e) {/* ignore*/}
+                    } catch (ClassCastException e) {
+                        // not a Blob -> ignore!
+                    }
+                } catch (NoSuchPartException e) {
+                    cpUri = null; // no more parts
+                }
             } while(cpUri != null);
         } finally {
             ci.getLock().readLock().unlock();
@@ -269,6 +278,47 @@ public class ContentItemHelper {
         return null; // not found
     }
     /**
+     * Returns a Map with the current content parts of the parsed type. future 
+     * changes to the contentParts of the content item will NOT be reflected
+     * within the returned map. The ordering of the {@link Iterator}s over the 
+     * returned map is consistent with the ordering of the contentPart within 
the
+     * {@link ContentItem}. <p> When parsing {@link Object} as class the number
+     * of the element will be equals to the index of that content part.<p>
+     * In contrast to the contentPart related methods of the {@link 
ContentItem}
+     * this method does NOT throw {@link NoSuchPartException}.
+     * @param ci the content item
+     * @param clazz the class of the content part
+     * @return the Map with the {@link UriRef id}s and the content as entries.
+     */
+    public static <T> LinkedHashMap<UriRef,T> getContentParts(ContentItem ci, 
Class<T> clazz){
+        if(ci == null){
+            throw new IllegalArgumentException("The parsed ContentItem MUST 
NOT be NULL!");
+        }
+        LinkedHashMap<UriRef,T> blobs = new LinkedHashMap<UriRef,T>();
+        UriRef cpUri = null;
+        int index = 0;
+        ci.getLock().readLock().lock();
+        try {
+            do {
+                try {
+                    cpUri = ci.getPartUri(index);
+                    index++;
+                    try {
+                        blobs.put(cpUri, ci.getPart(cpUri, clazz));
+                    } catch (ClassCastException e) {
+                        //not of type T -> skip
+                    }
+                } catch (NoSuchPartException e) {
+                    cpUri = null; // no more parts
+                }
+            } while(cpUri != null);
+        } finally {
+            ci.getLock().readLock().unlock();
+        }        
+        return blobs;
+    }
+
+    /**
      * Getter for the Text of an {@link Blob}. This method respects the
      * "charset" if present in the {@link Blob#getParameter() parameter} of the
      * Blob.
@@ -285,4 +335,20 @@ public class ContentItemHelper {
         String charset = blob.getParameter().get("charset");
         return IOUtils.toString(blob.getStream(), charset != null ? charset : 
UTF8);
     }
+    /**
+     * Creates the "{type}/{subtime}; [{param}={value}]+" mime type 
representation
+     * for the {@link Blob#getMimeType()} and {@link Blob#getParameter()} 
values
+     * @param blob the Blob
+     * @return the mime type with parameters (e.g. <code>
+     * text/plain;charset=UTF-8</code>)
+     */
+    public static String getMimeTypeWithParameters(Blob blob) {
+        StringBuilder mimeType = new StringBuilder(blob.getMimeType());
+        //ensure parameters are preserved
+        for(Entry<String,String> param : blob.getParameter().entrySet()){
+           mimeType.append("; 
").append(param.getKey()).append('=').append(param.getValue()); 
+        }
+        return mimeType.toString();
+    }
+
 }

Modified: 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemImpl.java?rev=1241888&r1=1241887&r2=1241888&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemImpl.java
 (original)
+++ 
incubator/stanbol/trunk/enhancer/generic/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/helper/ContentItemImpl.java
 Wed Feb  8 13:09:47 2012
@@ -138,11 +138,17 @@ public abstract class ContentItemImpl im
        public <T> T getPart(UriRef uri, Class<T> clazz) throws 
NoSuchPartException {
         readLock.lock();
         try {
-            if(parts.containsKey(uri)){
-                return (T) parts.get(uri);
+            Object part = parts.get(uri);
+            if(part == null){
+                throw new NoSuchPartException(uri);
+            }
+            if(clazz.isAssignableFrom(part.getClass())){
+                return (T)part;
             } else {
-                   throw new NoSuchPartException(uri);
-               }
+                throw new ClassCastException("The part '"+part+"'(class: "
+                        + part.getClass()+") is not compatiple to the 
requested"
+                        + "type "+clazz);
+            }
         }finally {
             readLock.unlock();
         }

Modified: incubator/stanbol/trunk/enhancer/jersey/pom.xml
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/jersey/pom.xml?rev=1241888&r1=1241887&r2=1241888&view=diff
==============================================================================
--- incubator/stanbol/trunk/enhancer/jersey/pom.xml (original)
+++ incubator/stanbol/trunk/enhancer/jersey/pom.xml Wed Feb  8 13:09:47 2012
@@ -59,7 +59,7 @@
             <Export-Package>
               org.apache.stanbol.enhancer.jersey.*
             </Export-Package>
-            
<Embed-Dependency>*;scope=compile|runtime;inline=false;artifactId=jersey-json|jettison|jackson-core-asl
+            
<Embed-Dependency>*;scope=compile|runtime;inline=false;artifactId=jersey-json|jettison|jackson-core-asl|httpmime
             </Embed-Dependency>
             <Embed-Transitive>true</Embed-Transitive>
             <Import-Package>
@@ -164,7 +164,20 @@
       <groupId>commons-lang</groupId>
       <artifactId>commons-lang</artifactId>
     </dependency>
-
+    <!-- dependencies to read/write ContentItems as multipart mime -->    
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpcore-osgi</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpmime</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>commons-fileupload</groupId>
+        <artifactId>commons-fileupload</artifactId>
+        <!--  <version>1.2.2</version>  -->
+      </dependency>
     <!-- OSGi tax -->
     <dependency>
       <groupId>org.osgi</groupId>

Added: 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/ContentItemReader.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/ContentItemReader.java?rev=1241888&view=auto
==============================================================================
--- 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/ContentItemReader.java
 (added)
+++ 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/ContentItemReader.java
 Wed Feb  8 13:09:47 2012
@@ -0,0 +1,255 @@
+package org.apache.stanbol.enhancer.jersey.reader;
+
+import static 
org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper.randomUUID;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import javax.print.attribute.standard.Media;
+import javax.servlet.ServletContext;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.serializedform.Parser;
+import org.apache.clerezza.rdf.core.serializedform.ParsingProvider;
+import org.apache.clerezza.rdf.jena.parser.JenaParserProvider;
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.FileUpload;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.RequestContext;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.mime.HttpMultipart;
+import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.enhancer.jersey.writers.ContentItemWriter;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryBlob;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap;
+
+public class ContentItemReader implements MessageBodyReader<ContentItem> {
+    
+    private static Logger log = 
LoggerFactory.getLogger(ContentItemReader.class);
+    FileUpload fu = new FileUpload();
+    private ServletContext servletContext;
+    private Parser parser;
+    
+    public static final MediaType MULTIPART = 
MediaType.valueOf(MediaType.MULTIPART_FORM_DATA_TYPE.getType()+"/*");
+
+    public ContentItemReader(@Context ServletContext context) {
+        this.servletContext = context;
+        if(context != null){
+            this.parser = ContextHelper.getServiceFromContext(Parser.class, 
context);
+        } else { //mainly for unit tests we want also allow initialisation 
without context
+            this.parser = new Parser();
+            parser.bindParsingProvider(new JenaParserProvider());
+        }
+    }
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] 
annotations, MediaType mediaType) {
+        return ContentItem.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public ContentItem readFrom(Class<ContentItem> type,
+                                Type genericType,
+                                Annotation[] annotations,
+                                MediaType mediaType,
+                                MultivaluedMap<String,String> httpHeaders,
+                                InputStream entityStream) throws IOException, 
WebApplicationException {
+        //boolean withMetadata = withMetadata(httpHeaders);
+        ContentItem contentItem = null;
+        if(mediaType.isCompatible(MULTIPART)){
+            //try to read ContentItem from "multipart/from-data"
+            MGraph metadata = null;
+            FileItemIterator fileItemIterator;
+            String contentItemId = null;
+            try {
+                fileItemIterator = fu.getItemIterator(new 
MessageBodyReaderContext(entityStream, mediaType));
+                while(fileItemIterator.hasNext()){
+                    FileItemStream fis = fileItemIterator.next();
+                    if(fis.getFieldName().equals("metadata")){
+                        if(contentItem != null){
+                            throw new WebApplicationException(
+                                Response.status(Response.Status.BAD_REQUEST)
+                                .entity("The Multipart MIME part with the 
'metadata' " +
+                                               "MUST BE before the MIME part 
containing the " +
+                                               "'content'!").build());
+                        }
+                        //the metadata may define the ID for the contentItem
+                        if(fis.getName() != null && !fis.getName().isEmpty()){
+                            contentItemId = fis.getName();
+                        }
+                        metadata = new IndexedMGraph();
+                        try {
+                            parser.parse(metadata, fis.openStream(), 
fis.getContentType());
+                        } catch (Exception e) {
+                            throw new WebApplicationException(e, 
+                                Response.status(Response.Status.BAD_REQUEST)
+                                .entity(String.format("Unable to parse 
Metadata " +
+                                               "from Multipart MIME part '%s' 
(" +
+                                               "contentItem: %s| contentType: 
%s)",
+                                               
fis.getFieldName(),fis.getName(),fis.getContentType()))
+                                .build());
+                        }
+                    } else if(fis.getFieldName().equals("content")){
+                        contentItem = createContentItem(contentItemId, 
metadata, fis);
+                    } else { //additional metadata as serialised RDF
+                        if(contentItem == null){
+                            throw new WebApplicationException(
+                                Response.status(Response.Status.BAD_REQUEST)
+                                .entity("Multipart MIME parts for additional " 
+
+                                               "contentParts MUST BE after the 
MIME " +
+                                               "parts for 'metadata' AND 
'content'")
+                                .build());
+                        }
+                        if(fis.getFieldName() == null || 
fis.getFieldName().isEmpty()){
+                            throw new WebApplicationException(
+                                Response.status(Response.Status.BAD_REQUEST)
+                                .entity("Multipart MIME parts representing " +
+                                               "ContentParts for additional 
RDF metadata" +
+                                               "MUST define the contentParts 
URI as" +
+                                               "'name' of the MIME 
part!").build());
+                        }
+                        MGraph graph = new IndexedMGraph();
+                        try {
+                            parser.parse(graph, fis.openStream(), 
fis.getContentType());
+                        } catch (Exception e) {
+                            throw new WebApplicationException(e, 
+                                Response.status(Response.Status.BAD_REQUEST)
+                                .entity(String.format("Unable to parse RDF " +
+                                        "for ContentPart '%s' ( contentType: 
%s)",
+                                        fis.getName(),fis.getContentType()))
+                                .build());
+                        }
+                        contentItem.addPart(new UriRef(fis.getFieldName()), 
graph);
+                    }
+                }
+            } catch (FileUploadException e) {
+                throw new WebApplicationException(e, 
Response.Status.BAD_REQUEST);
+            }
+        } else { //normal content
+            contentItem = new InMemoryContentItem(
+                IOUtils.toByteArray(entityStream), mediaType.toString());
+        }
+        return contentItem;
+    }
+    private ContentItem createContentItem(String id, MGraph metadata, 
FileItemStream content) throws IOException, FileUploadException {
+        MediaType partContentType = 
MediaType.valueOf(content.getContentType());
+        ContentItem contentItem = null;
+        if(partContentType.isCompatible(MULTIPART)){
+            //multiple contentParts are parsed
+            FileItemIterator contentPartIterator = fu.getItemIterator(
+                new MessageBodyReaderContext(
+                    content.openStream(), partContentType));
+            while(contentPartIterator.hasNext()){
+                FileItemStream fis = contentPartIterator.next();
+                String contentPartUri = fis.getFieldName();
+                if(contentItem == null){
+                    contentItem = new InMemoryContentItem(id, 
+                        IOUtils.toByteArray(fis.openStream()),
+                        fis.getContentType(), metadata);
+                } else {
+                    Blob blob = new InMemoryBlob(fis.openStream(), 
fis.getContentType());
+                    UriRef contentPartId = null;
+                    if(fis.getFieldName() != null && 
!fis.getFieldName().isEmpty()){
+                        contentPartId = new UriRef(fis.getFieldName());
+                    } else {
+                        //generating a random ID might break metadata 
+                        //TODO maybe we should throw an exception instead
+                        contentPartId = new UriRef("urn:contentpart:"+ 
randomUUID());
+                    }
+                    contentItem.addPart(contentPartId, blob);
+                }
+            }
+        } else {
+            contentItem = new InMemoryContentItem(id, 
+                IOUtils.toByteArray(content.openStream()),
+                content.getContentType(), metadata);
+        }
+        return contentItem;
+    }
+    
+    /**
+     * @param httpHeaders
+     * @return if this requests parses an contentItem with metadata
+     */
+    private boolean withMetadata(MultivaluedMap<String,String> httpHeaders) {
+        boolean withMetadata = httpHeaders.containsKey("inputWithMetadata");
+        if(withMetadata){
+            String value = httpHeaders.getFirst("inputWithMetadata");
+            //null empty or "true"
+            withMetadata = value == null || value.isEmpty() || 
Boolean.parseBoolean(value);
+        }
+        return withMetadata;
+    }
+    /**
+     * Adapter from the parameter present in an {@link MessageBodyReader} to
+     * the {@link RequestContext} as used by the commons.fileupload framework
+     * @author rwesten
+     *
+     */
+    private static class MessageBodyReaderContext implements RequestContext{
+
+        private final InputStream in;
+        private final String contentType;
+        private final String charEncoding;
+
+        public MessageBodyReaderContext(InputStream in, MediaType mediaType){
+            this.in = in;
+            this.contentType = mediaType.toString();
+            String charset = mediaType.getParameters().get("charset");
+            this.charEncoding = charset == null ? "UTF-8" : charset;
+        }
+        
+        @Override
+        public String getCharacterEncoding() {
+            return charEncoding;
+        }
+
+        @Override
+        public String getContentType() {
+            return  contentType;
+        }
+
+        @Override
+        public int getContentLength() {
+            return -1;
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return in;
+        }
+        
+    }
+    
+}

Propchange: 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/reader/ContentItemReader.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ContentItemWriter.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ContentItemWriter.java?rev=1241888&view=auto
==============================================================================
--- 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ContentItemWriter.java
 (added)
+++ 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ContentItemWriter.java
 Wed Feb  8 13:09:47 2012
@@ -0,0 +1,212 @@
+package org.apache.stanbol.enhancer.jersey.writers;
+
+import static 
org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getContentParts;
+import static 
org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getMimeTypeWithParameters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.NonLiteral;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.impl.TripleImpl;
+import org.apache.clerezza.rdf.core.serializedform.Serializer;
+import org.apache.clerezza.rdf.core.serializedform.SupportedFormat;
+import org.apache.clerezza.rdf.jena.serializer.JenaSerializerProvider;
+import org.apache.clerezza.rdf.ontologies.RDF;
+import org.apache.http.entity.mime.FormBodyPart;
+import org.apache.http.entity.mime.HttpMultipart;
+import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.http.entity.mime.content.AbstractContentBody;
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.entity.mime.content.ContentDescriptor;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionMetadataHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionPlanHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryBlob;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+
+import com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap;
+
+@Provider
+public class ContentItemWriter implements MessageBodyWriter<ContentItem> {
+
+    protected ServletContext servletContext;
+    
+    protected Serializer serializer;
+
+    public ContentItemWriter(@Context ServletContext servletContext){
+        this.servletContext = servletContext;
+        if(servletContext != null){
+            serializer = ContextHelper.getServiceFromContext(Serializer.class, 
servletContext);
+        } else {
+            serializer = new Serializer();
+            serializer.bindSerializingProvider(new JenaSerializerProvider());
+        }
+    }
+    
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] 
annotations, MediaType mediaType) {
+        return MediaType.MULTIPART_FORM_DATA_TYPE.isCompatible(mediaType) &&
+                ContentItem.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public long getSize(ContentItem t,
+                        Class<?> type,
+                        Type genericType,
+                        Annotation[] annotations,
+                        MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(ContentItem ci,
+                        Class<?> type,
+                        Type genericType,
+                        Annotation[] annotations,
+                        MediaType mediaType,
+                        MultivaluedMap<String,Object> httpHeaders,
+                        OutputStream entityStream) throws IOException, 
WebApplicationException {
+
+        String boundary = "contentItem";
+        String charsetName = mediaType.getParameters().get("charset");
+        if(charsetName == null){
+            charsetName = "UTF-8";
+        }
+        Charset charset = Charset.forName(charsetName);
+        String contentType = String.format("%s/%s; charset=%s; boundary=%s",
+            
mediaType.getType(),mediaType.getSubtype(),charset.toString(),boundary);
+        HttpMultipart entity = new HttpMultipart("from-data", 
charset,boundary);
+        entity.addBodyPart(new FormBodyPart("metadata", new 
ClerezzaContentBody(
+            ci.getUri().getUnicodeString(), ci.getMetadata(),
+            //TODO: find a way to parse the intended RDF serialisation format
+            SupportedFormat.RDF_XML)));
+        
+        HttpMultipart content = new HttpMultipart("alternate", 
Charset.forName("UTF-8"),"contentParts");
+        for(Entry<UriRef,Blob> entry : 
getContentParts(ci,Blob.class).entrySet()){
+            content.addBodyPart(new 
FormBodyPart(entry.getKey().getUnicodeString(), 
+                new InputStreamBody(
+                    entry.getValue().getStream(),
+                    getMimeTypeWithParameters(entry.getValue()),
+                    null))); //no file name
+        }
+        //add all the blobs
+        entity.addBodyPart(new FormBodyPart("content",new 
MultipartContentBody(content, null)));
+        //add additional metadata stored in contentParts
+        for(Entry<UriRef,TripleCollection> entry : getContentParts(ci, 
TripleCollection.class).entrySet()){
+            entity.addBodyPart(new 
FormBodyPart(entry.getKey().getUnicodeString(), 
+                new ClerezzaContentBody(null, //no file name
+                    entry.getValue(),SupportedFormat.RDF_XML)));
+        }
+        entity.writeTo(entityStream);
+        httpHeaders.put(HttpHeaders.CONTENT_TYPE, 
+            Collections.singletonList((Object)contentType));
+    }
+
+    /**
+     * Supports sending multipart mime as {@link ContentBody}.
+     * @author Rupert Westenthaler
+     *
+     */
+    private class MultipartContentBody extends AbstractContentBody implements 
ContentBody,ContentDescriptor {
+
+        private HttpMultipart multipart;
+        private String name;
+
+        public MultipartContentBody(HttpMultipart multipart,String name){
+            super(String.format("multipart/%s; boundary=%s",
+                multipart.getSubType(), multipart.getBoundary()));
+            this.name = name;
+            this.multipart = multipart;
+        }
+        @Override
+        public String getCharset() {
+            return multipart.getCharset().toString();
+        }
+
+        @Override
+        public String getTransferEncoding() {
+            return "7bit";
+        }
+
+        @Override
+        public long getContentLength() {
+            return multipart.getTotalLength();
+        }
+
+        @Override
+        public String getFilename() {
+            return name;
+        }
+
+        @Override
+        public void writeTo(OutputStream out) throws IOException {
+            multipart.writeTo(out);
+        }
+        
+    }
+    /**
+     * Supports serialised RDF graphs as {@link ContentBody}
+     * @author Rupert Westenthaler
+     *
+     */
+    private class ClerezzaContentBody extends AbstractContentBody implements 
ContentBody,ContentDescriptor {
+
+        private TripleCollection graph;
+        private String name;
+
+        protected ClerezzaContentBody(String name, TripleCollection graph, 
String mimeType){
+            super(mimeType);
+            this.name = name;
+            this.graph = graph;
+        }
+
+        @Override
+        public String getCharset() {
+            return "UTF-8"; //clerezza uses statically UTF-8
+        }
+
+        @Override
+        public String getTransferEncoding() {
+            // TODO Javadoc says 7bit; constants is MIMETYPE define 8bit and 
binary
+            return "7bit";
+        }
+
+        @Override
+        public long getContentLength() {
+            return -1;
+        }
+
+        @Override
+        public String getFilename() {
+            return name;
+        }
+
+        @Override
+        public void writeTo(OutputStream out) throws IOException {
+            serializer.serialize(out, graph, getMediaType()+'/'+getSubType());
+        }
+        
+        
+    }
+}

Propchange: 
incubator/stanbol/trunk/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ContentItemWriter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/ContentItemReaderWriterTest.java
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/ContentItemReaderWriterTest.java?rev=1241888&view=auto
==============================================================================
--- 
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/ContentItemReaderWriterTest.java
 (added)
+++ 
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/ContentItemReaderWriterTest.java
 Wed Feb  8 13:09:47 2012
@@ -0,0 +1,164 @@
+package org.apache.stanbol.enhancer.jersey;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.NonLiteral;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.impl.SimpleMGraph;
+import org.apache.clerezza.rdf.core.impl.TripleImpl;
+import org.apache.clerezza.rdf.ontologies.RDF;
+import org.apache.commons.io.IOUtils;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
+import org.apache.stanbol.enhancer.jersey.reader.ContentItemReader;
+import org.apache.stanbol.enhancer.jersey.writers.ContentItemWriter;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionMetadataHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionPlanHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryBlob;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+import org.apache.stanbol.enhancer.servicesapi.rdf.ExecutionMetadata;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap;
+
+public class ContentItemReaderWriterTest {
+
+    private static ContentItem contentItem;
+    private static ContentItemWriter ciWriter;
+    private static ContentItemReader ciReader;
+    /**
+     * @return
+     */
+    @BeforeClass
+    public static void createTestContentItem() {
+        contentItem = new InMemoryContentItem("urn:test",
+            "<html>\n" +
+            "  <body>\n" +
+            "    This is a <b>ContentItem</b> to <i>Mime Multipart</i> 
test!\n" +
+            "  </body>\n" +
+            "</html>","text/html");
+        contentItem.addPart(new UriRef("run:text:text"), new InMemoryBlob(
+            "This is a ContentItem to Mime Multipart test!", "text/plain"));
+        contentItem.getMetadata().add(new TripleImpl(
+            new UriRef("urn:test"), RDF.type, new 
UriRef("urn:types:Document")));
+        MGraph em 
=ExecutionMetadataHelper.initExecutionMetadataContentPart(contentItem);
+        NonLiteral ep = ExecutionPlanHelper.createExecutionPlan(em, 
"testChain");
+        ExecutionPlanHelper.writeExecutionNode(em, ep, "testEngine", true, 
null);
+        ExecutionMetadataHelper.initExecutionMetadata(em, em, 
contentItem.getUri(), "testChain", false);
+        ciWriter = new ContentItemWriter(null);
+        ciReader = new ContentItemReader(null);
+    }
+    /**
+     * @param out
+     * @return
+     * @throws IOException
+     */
+    private MediaType serializeContentItem(ByteArrayOutputStream out) throws 
IOException {
+        MultivaluedMap<String,Object> headers = new 
StringKeyIgnoreCaseMultivaluedMap<Object>();
+        ciWriter.writeTo(contentItem, ContentItem.class, null, null, 
MediaType.MULTIPART_FORM_DATA_TYPE, 
+            headers , out);
+        //check the returned content type
+        String contentTypeString = 
(String)headers.getFirst(HttpHeaders.CONTENT_TYPE);
+        assertNotNull(contentTypeString);
+        MediaType contentType = MediaType.valueOf(contentTypeString);
+        return contentType;
+    }
+    
+    @Test
+    public void testWriter() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        MediaType contentType = serializeContentItem(out);
+        
assertTrue(MediaType.MULTIPART_FORM_DATA_TYPE.isCompatible(contentType));
+        assertNotNull(contentType.getParameters().get("boundary"));
+        
assertEquals(contentType.getParameters().get("boundary"),"contentItem");
+        assertNotNull(contentType.getParameters().get("charset"));
+        assertEquals(contentType.getParameters().get("charset"),"UTF-8");
+        //check the serialised multipart MIME
+        String multipartMime = new 
String(out.toByteArray(),Charset.forName(contentType.getParameters().get("charset")));
+        String[] tests = new String[]{
+            "--"+contentType.getParameters().get("boundary"),
+            "Content-Disposition: form-data; name=\"metadata\"; 
filename=\"urn:test\"",
+            "Content-Type: application/rdf+xml; charset=UTF-8",
+            "<rdf:type rdf:resource=\"urn:types:Document\"/>",
+            "--"+contentType.getParameters().get("boundary"),
+            "Content-Disposition: form-data; name=\"content\"",
+            "Content-Type: multipart/alternate; boundary=contentParts; 
charset=UTF-8",
+            "--contentParts",
+            "Content-Disposition: form-data; name=\"urn:test_main\"",
+            "Content-Type: text/html; charset=UTF-8",
+            "This is a <b>ContentItem</b> to <i>Mime Multipart</i> test!",
+            "--contentParts",
+            "Content-Disposition: form-data; name=\"run:text:text\"",
+            "Content-Type: text/plain; charset=UTF-8",
+            "This is a ContentItem to Mime Multipart test!",
+            "--contentParts--",
+            "--"+contentType.getParameters().get("boundary"),
+            "Content-Disposition: form-data; 
name=\"http://stanbol.apache.org/ontology/enhancer/executionMetadata#ChainExecution\"";,
+            "Content-Type: application/rdf+xml; charset=UTF-8",
+            "<rdf:type 
rdf:resource=\"http://stanbol.apache.org/ontology/enhancer/executionplan#ExecutionNode\"/>",
+            "--"+contentType.getParameters().get("boundary")+"--"
+        };
+        for(String test : tests){
+            int index = multipartMime.indexOf(test);
+            assertTrue(index >=0);
+            multipartMime = multipartMime.substring(index);
+        }
+    }
+
+    @Test
+    public void testReader() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        MediaType contentType = serializeContentItem(out);
+        ContentItemReader cir = new ContentItemReader(null);
+        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        ContentItem ci = cir.readFrom(ContentItem.class, null, null, 
contentType, null, in);
+        //assert ID
+        assertEquals(contentItem.getUri(), ci.getUri());
+        //assert metadata
+        MGraph copy = new SimpleMGraph();
+        copy.addAll(contentItem.getMetadata());
+        assertTrue(copy.removeAll(ci.getMetadata()));
+        assertTrue(copy.isEmpty());
+        //assert Blob
+        assertEquals(contentItem.getBlob().getMimeType(), 
ci.getBlob().getMimeType());
+        String content = IOUtils.toString(contentItem.getStream(),"UTF-8");
+        String readContent = IOUtils.toString(ci.getStream(), "UTF-8");
+        assertEquals(content, readContent);
+        Iterator<Entry<UriRef,Blob>> contentItemBlobsIt = 
ContentItemHelper.getContentParts(contentItem, 
Blob.class).entrySet().iterator();
+        Iterator<Entry<UriRef,Blob>> ciBlobsIt = 
ContentItemHelper.getContentParts(ci, Blob.class).entrySet().iterator();
+        while(contentItemBlobsIt.hasNext() && ciBlobsIt.hasNext()){
+            Entry<UriRef,Blob> contentItemBlobPart = contentItemBlobsIt.next();
+            Entry<UriRef,Blob> ciBlobPart = ciBlobsIt.next();
+            assertEquals(contentItemBlobPart.getKey(), ciBlobPart.getKey());
+            String partContentType = 
contentItemBlobPart.getValue().getMimeType();
+            String readPartContentType = ciBlobPart.getValue().getMimeType();
+            assertEquals(partContentType, readPartContentType);
+            String partContent = 
IOUtils.toString(contentItemBlobPart.getValue().getStream(),"UTF-8");
+            String readPartContent = 
IOUtils.toString(ciBlobPart.getValue().getStream(), "UTF-8");
+            assertEquals(partContent, readPartContent);
+        }
+        MGraph executionMetadata = 
contentItem.getPart(ExecutionMetadata.CHAIN_EXECUTION, MGraph.class);
+        MGraph readExecutionMetadata = 
ci.getPart(ExecutionMetadata.CHAIN_EXECUTION, MGraph.class);
+        assertNotNull(executionMetadata);
+        assertNotNull(readExecutionMetadata);
+        assertEquals(executionMetadata.size(), readExecutionMetadata.size());
+    }
+
+}

Propchange: 
incubator/stanbol/trunk/enhancer/jersey/src/test/java/org/apache/stanbol/enhancer/jersey/ContentItemReaderWriterTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
incubator/stanbol/trunk/launchers/bundlelists/stanbolcommons/src/main/bundles/list.xml
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/launchers/bundlelists/stanbolcommons/src/main/bundles/list.xml?rev=1241888&r1=1241887&r2=1241888&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/launchers/bundlelists/stanbolcommons/src/main/bundles/list.xml
 (original)
+++ 
incubator/stanbol/trunk/launchers/bundlelists/stanbolcommons/src/main/bundles/list.xml
 Wed Feb  8 13:09:47 2012
@@ -107,11 +107,6 @@
       <version>1.0</version>
     </bundle>
     <bundle>
-      <groupId>org.apache.httpcomponents</groupId>
-      <artifactId>httpcore-osgi</artifactId>
-      <version>4.0.1</version>
-    </bundle>
-    <bundle>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
       <version>1.5</version>
@@ -121,6 +116,17 @@
       <artifactId>commons-fileupload</artifactId>
       <version>1.2.2</version>
     </bundle>
+    <bundle>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore-osgi</artifactId>
+      <version>4.1.4</version>
+    </bundle>
+<!-- not a bundle (need to be embedded)
+    <bundle>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpmime</artifactId>
+      <version>4.1.2</version>
+    </bundle>  -->
     <bundle> <!-- only used by the Entityhub -->
       <groupId>joda-time</groupId>
       <artifactId>joda-time</artifactId>

Modified: incubator/stanbol/trunk/parent/pom.xml
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/parent/pom.xml?rev=1241888&r1=1241887&r2=1241888&view=diff
==============================================================================
--- incubator/stanbol/trunk/parent/pom.xml (original)
+++ incubator/stanbol/trunk/parent/pom.xml Wed Feb  8 13:09:47 2012
@@ -1004,7 +1004,16 @@
         <artifactId>commons-httpclient</artifactId>
         <version>3.1</version>
       </dependency>
-
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpcore-osgi</artifactId>
+        <version>4.1.4</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpmime</artifactId>
+        <version>4.1.2</version>
+      </dependency>
 
       <!-- Joda Time -->
       <dependency>
@@ -1091,13 +1100,6 @@
         <version>${jersey-version}</version>
       </dependency>
 
-<!--  jersey-multipart now uses a different library allowing streaming of
-      binary data (see http://mimepull.java.net/)
-     <dependency>
-          <groupId>javax.mail</groupId>
-          <artifactId>mail</artifactId>
-          <version>1.4</version>
-                </dependency>  -->
 
       <!-- JAX-RS JSR311 -->
       <!-- NOTE that JAX-RS is included and exported by jersey-core


Reply via email to