You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by fl...@apache.org on 2012/06/19 00:51:28 UTC

svn commit: r1351524 [4/9] - in /incubator/stanbol/branches/cameltrial/flow: jersey/src/main/java/org/apache/stanbol/flow/ jersey/src/main/java/org/apache/stanbol/flow/jersey/ jersey/src/main/java/org/apache/stanbol/flow/jersey/fragment/ jersey/src/mai...

Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/ContentItemResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/ContentItemResource.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/ContentItemResource.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/ContentItemResource.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,825 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.stanbol.flow.jersey.resource;
+
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+import static org.apache.stanbol.commons.web.base.CorsHelper.addCORSOrigin;
+import static org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper.getReference;
+import static org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper.getReferences;
+import static org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper.getString;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.OntologicalClasses.DBPEDIA_ORGANISATION;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.OntologicalClasses.DBPEDIA_PERSON;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.OntologicalClasses.DBPEDIA_PLACE;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.OntologicalClasses.SKOS_CONCEPT;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.DC_RELATION;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.DC_TYPE;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.ENHANCER_CONFIDENCE;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.ENHANCER_ENTITY_LABEL;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.ENHANCER_ENTITY_REFERENCE;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.GEO_LAT;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.GEO_LONG;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.TechnicalClasses.ENHANCER_ENTITYANNOTATION;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.TechnicalClasses.ENHANCER_TEXTANNOTATION;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.clerezza.rdf.core.Language;
+import org.apache.clerezza.rdf.core.Literal;
+import org.apache.clerezza.rdf.core.LiteralFactory;
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.NonLiteral;
+import org.apache.clerezza.rdf.core.PlainLiteral;
+import org.apache.clerezza.rdf.core.Resource;
+import org.apache.clerezza.rdf.core.Triple;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.access.TcManager;
+import org.apache.clerezza.rdf.core.impl.SimpleMGraph;
+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.core.sparql.ParseException;
+import org.apache.clerezza.rdf.ontologies.RDF;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
+import org.apache.stanbol.commons.web.base.resource.BaseStanbolResource;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.NoSuchPartException;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionMetadataHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionPlanHelper;
+import org.apache.stanbol.enhancer.servicesapi.rdf.ExecutionMetadata;
+import org.apache.stanbol.enhancer.servicesapi.rdf.ExecutionPlan;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jersey.api.view.Viewable;
+
+public class ContentItemResource extends BaseStanbolResource {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    // TODO make this configurable trough a property
+    public static final UriRef SUMMARY = new UriRef("http://www.w3.org/2000/01/rdf-schema#comment");
+
+    // TODO make this configurable trough a property
+    public static final UriRef THUMBNAIL = new UriRef("http://dbpedia.org/ontology/thumbnail");
+    public static final UriRef DEPICTION = new UriRef("http://xmlns.com/foaf/0.1/depiction");
+
+    public final Map<UriRef,String> defaultThumbnails = new HashMap<UriRef,String>();
+
+    protected ContentItem contentItem;
+
+    protected String localId;
+
+    protected String textContent;
+
+    protected URI imageSrc;
+
+    protected URI downloadHref;
+
+    protected URI metadataHref;
+
+    protected final TcManager tcManager;
+
+    protected final Serializer serializer;
+
+    protected String serializationFormat = SupportedFormat.RDF_XML;
+
+
+    /**
+     * Map holding the extraction mapped by {@link Properties#DC_TYPE} and the
+     * {@link Properties#ENHANCER_SELECTED_TEXT}.
+     * This map is initialised by {@link #initOccurrences()}.
+     */
+    protected Map<UriRef,Map<String,EntityExtractionSummary>> extractionsByTypeMap = 
+        new HashMap<UriRef,Map<String,EntityExtractionSummary>>();
+
+    private MGraph executionMetadata;
+
+    private ChainExecution chainExecution;
+
+    private ArrayList<org.apache.stanbol.flow.jersey.resource.ContentItemResource.Execution> engineExecutions;
+    
+    public ContentItemResource(String localId,
+                               ContentItem ci,
+                               UriInfo uriInfo,
+                               String storePath,
+                               TcManager tcManager,
+                               Serializer serializer,
+                               ServletContext servletContext) throws IOException {
+        this.contentItem = ci;
+        this.localId = localId;
+        this.uriInfo = uriInfo;
+        this.tcManager = tcManager;
+        this.serializer = serializer;
+        this.servletContext = servletContext;
+
+        if (localId != null) {
+            URI rawURI = uriInfo.getBaseUriBuilder().path(storePath).path("raw").path(localId).build();
+            Entry<UriRef,Blob> plainTextContentPart = ContentItemHelper.getBlob(contentItem, Collections.singleton("text/plain"));
+            if (plainTextContentPart != null) {
+                this.textContent = ContentItemHelper.getText(plainTextContentPart.getValue());
+            } 
+            if (ci.getMimeType().startsWith("image/")) {
+                this.imageSrc = rawURI;
+            }
+            this.downloadHref = rawURI;
+            this.metadataHref = uriInfo.getBaseUriBuilder().path(storePath).path("metadata").path(localId).build();
+        }
+        defaultThumbnails.put(DBPEDIA_PERSON, getStaticRootUrl() + "/home/images/user_48.png");
+        defaultThumbnails.put(DBPEDIA_ORGANISATION, getStaticRootUrl() + "/home/images/organization_48.png");
+        defaultThumbnails.put(DBPEDIA_PLACE, getStaticRootUrl() + "/home/images/compass_48.png");
+        defaultThumbnails.put(SKOS_CONCEPT, getStaticRootUrl() + "/home/images/black_gear_48.png");
+        defaultThumbnails.put(null, getStaticRootUrl() + "/home/images/unknown_48.png");
+        long start = System.currentTimeMillis();
+        initOccurrences();
+        //init ExecutionMetadata
+        try {
+            executionMetadata = ci.getPart(ExecutionMetadata.CHAIN_EXECUTION, MGraph.class);
+        } catch(NoSuchPartException e){
+            executionMetadata = null;
+        }
+        if(executionMetadata != null){
+            NonLiteral ce = ExecutionMetadataHelper.getChainExecution(executionMetadata, ci.getUri());
+            if(ce != null){
+                chainExecution = new ChainExecution(executionMetadata, ce);
+                engineExecutions = new ArrayList<Execution>();
+                for(NonLiteral ex : ExecutionMetadataHelper.getExecutions(executionMetadata, ce)){
+                    engineExecutions.add(new Execution(chainExecution,executionMetadata, ex));
+                }
+                Collections.sort(engineExecutions);
+            } else {
+                chainExecution = null;
+                engineExecutions = null;
+            }
+        }
+        log.info(" ... {}ms fro parsing Enhancement Reuslts",System.currentTimeMillis()-start);
+    }
+
+    public String getRdfMetadata(String mediatype) throws UnsupportedEncodingException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        serializer.serialize(out, contentItem.getMetadata(), mediatype);
+        return out.toString("utf-8");
+    }
+
+    public String getRdfMetadata() throws UnsupportedEncodingException {
+        return getRdfMetadata(serializationFormat);
+    }
+
+    public ContentItem getContentItem() {
+        return contentItem;
+    }
+
+    public String getLocalId() {
+        return localId;
+    }
+
+    public String getTextContent() {
+        return textContent;
+    }
+
+    public URI getImageSrc() {
+        return imageSrc;
+    }
+
+    public URI getDownloadHref() {
+        return downloadHref;
+    }
+
+    public URI getMetadataHref() {
+        return metadataHref;
+    }
+    /**
+     * Checks if there are Occurrences
+     */
+    public boolean hasOccurrences(){
+        for(Map<String,EntityExtractionSummary> occ : extractionsByTypeMap.values()){
+            if(!occ.isEmpty()){
+                return true;
+            }
+        }
+        return false;
+    }
+    /**
+     * Used to print occurrences with other types than the natively supported
+     */
+    public Collection<UriRef> getOtherOccurrencyTypes(){
+        Set<UriRef>  types = new HashSet<UriRef>(extractionsByTypeMap.keySet());
+        types.remove(DBPEDIA_PERSON);
+        types.remove(DBPEDIA_ORGANISATION);
+        types.remove(DBPEDIA_PLACE);
+        types.remove(SKOS_CONCEPT);
+        types.remove(null); //other
+        return types;
+    }
+    public String extractLabel(UriRef uri){
+        String fullUri = uri.getUnicodeString();
+        int index = Math.max(fullUri.lastIndexOf('#'),fullUri.lastIndexOf('/'));
+        index = Math.max(index, fullUri.lastIndexOf(':'));
+        //do not convert if the parsed uri does not contain a local name
+        if(index > 0 && index+1 < fullUri.length()){
+            return StringUtils.capitalize(fullUri.substring(index+1).replaceAll("[\\-_]", " "));
+        } else {
+            return uri.getUnicodeString();
+        }
+    }
+    public Collection<EntityExtractionSummary> getOccurrences(UriRef type){
+        Map<String,EntityExtractionSummary> typeMap = extractionsByTypeMap.get(type);
+        Collection<EntityExtractionSummary> typeOccurrences;
+        if(typeMap != null){
+            typeOccurrences = typeMap.values();
+        } else {
+            typeOccurrences  = Collections.emptyList();
+        }
+        return typeOccurrences;
+    }
+    
+    public Collection<EntityExtractionSummary> getPersonOccurrences() throws ParseException {
+        return getOccurrences(DBPEDIA_PERSON);
+    }
+    public Collection<EntityExtractionSummary> getOtherOccurrences() throws ParseException {
+        return getOccurrences(null);
+    }
+
+    public Collection<EntityExtractionSummary> getOrganizationOccurrences() throws ParseException {
+        return getOccurrences(DBPEDIA_ORGANISATION);
+    }
+
+    public Collection<EntityExtractionSummary> getPlaceOccurrences() throws ParseException {
+        return getOccurrences(DBPEDIA_PLACE);
+    }
+    public Collection<EntityExtractionSummary> getConceptOccurrences() throws ParseException {
+        return getOccurrences(SKOS_CONCEPT);
+    }
+    enum EAProps {
+        label,
+        entity,
+        confidence
+    }
+
+    private void initOccurrences() {
+        MGraph graph = contentItem.getMetadata();
+        LiteralFactory lf = LiteralFactory.getInstance();
+        Map<UriRef,Collection<NonLiteral>> suggestionMap = new HashMap<UriRef,Collection<NonLiteral>>();
+        // 1) get Entity Annotations
+        Map<NonLiteral,Map<EAProps,Object>> entitySuggestionMap = new HashMap<NonLiteral,Map<EAProps,Object>>();
+        Iterator<Triple> entityAnnotations = graph.filter(null, RDF.type, ENHANCER_ENTITYANNOTATION);
+        while(entityAnnotations.hasNext()){
+            NonLiteral entityAnnotation = entityAnnotations.next().getSubject();
+            //to avoid multiple lookups (e.g. if one entityAnnotation links to+
+            //several TextAnnotations) we cache the data in an intermediate Map
+            Map<EAProps,Object> eaData = new EnumMap<EAProps,Object>(EAProps.class);
+            eaData.put(EAProps.entity, getReference(graph, entityAnnotation, ENHANCER_ENTITY_REFERENCE));
+            eaData.put(EAProps.label, getString(graph, entityAnnotation, ENHANCER_ENTITY_LABEL));
+            eaData.put(EAProps.confidence, EnhancementEngineHelper.get(
+                graph, entityAnnotation, ENHANCER_CONFIDENCE, Double.class, lf));
+            entitySuggestionMap.put(entityAnnotation, eaData);
+            Iterator<UriRef> textAnnotations = getReferences(graph, entityAnnotation, DC_RELATION);
+            while(textAnnotations.hasNext()){
+                UriRef textAnnotation = textAnnotations.next();
+                Collection<NonLiteral> suggestions = suggestionMap.get(textAnnotation);
+                if(suggestions == null){
+                    suggestions = new ArrayList<NonLiteral>();
+                    suggestionMap.put(textAnnotation, suggestions);
+                }
+                suggestions.add(entityAnnotation);
+            }
+        }
+        // 2) get the TextAnnotations
+        Iterator<Triple> textAnnotations = graph.filter(null, RDF.type, ENHANCER_TEXTANNOTATION);
+        while(textAnnotations.hasNext()){
+            NonLiteral textAnnotation = textAnnotations.next().getSubject();
+            if (graph.filter(textAnnotation, DC_RELATION, null).hasNext()) {
+                // this is not the most specific occurrence of this name: skip
+                continue;
+            }
+            String text = getString(graph, textAnnotation, Properties.ENHANCER_SELECTED_TEXT);
+            if(text == null){
+                //ignore text annotations without text
+                continue;
+            }
+            Iterator<UriRef> types = getReferences(graph, textAnnotation, DC_TYPE);
+            if(!types.hasNext()){ //create an iterator over null in case no types are present
+                types = Collections.singleton((UriRef)null).iterator();
+            }
+            while(types.hasNext()){
+                UriRef type = types.next();
+                Map<String,EntityExtractionSummary> occurrenceMap = extractionsByTypeMap.get(type);
+                if(occurrenceMap == null){
+                    occurrenceMap = new TreeMap<String,EntityExtractionSummary>(String.CASE_INSENSITIVE_ORDER);
+                    extractionsByTypeMap.put(type, occurrenceMap);
+                }
+                EntityExtractionSummary entity = occurrenceMap.get(text);
+                if(entity == null){
+                    entity = new EntityExtractionSummary(text, type, defaultThumbnails);
+                    occurrenceMap.put(text, entity);
+                }
+                Collection<NonLiteral> suggestions = suggestionMap.get(textAnnotation);
+                if(suggestions != null){
+                    for(NonLiteral entityAnnotation : suggestions){
+                        Map<EAProps,Object> eaData = entitySuggestionMap.get(entityAnnotation);
+                        entity.addSuggestion(
+                            (UriRef)eaData.get(EAProps.entity),
+                            (String)eaData.get(EAProps.label), 
+                            (Double)eaData.get(EAProps.confidence), 
+                            graph);
+                    }
+                }
+            }
+        }
+    }
+
+    public ChainExecution getChainExecution(){
+        return chainExecution;
+    }
+    
+    public Collection<Execution> getEngineExecutions(){
+        return engineExecutions;
+    }
+    
+    
+    public static class EntityExtractionSummary implements Comparable<EntityExtractionSummary> {
+
+        protected final String name;
+
+        protected final UriRef type;
+
+        protected List<EntitySuggestion> suggestions = new ArrayList<EntitySuggestion>();
+
+        protected List<String> mentions = new ArrayList<String>();
+
+        public final Map<UriRef,String> defaultThumbnails;
+
+        public EntityExtractionSummary(String name, UriRef type, Map<UriRef,String> defaultThumbnails) {
+            this.name = name;
+            this.type = type;
+            mentions.add(name);
+            this.defaultThumbnails = defaultThumbnails;
+        }
+
+        public void addSuggestion(UriRef uri, String label, Double confidence, TripleCollection properties) {
+            EntitySuggestion suggestion = new EntitySuggestion(uri, type, label, confidence, properties,
+                    defaultThumbnails);
+            if (!suggestions.contains(suggestion)) {
+                suggestions.add(suggestion);
+                Collections.sort(suggestions);
+            }
+        }
+
+        public String getName() {
+            EntitySuggestion bestGuess = getBestGuess();
+            if (bestGuess != null) {
+                return bestGuess.getLabel();
+            }
+            return name;
+        }
+
+        public String getUri() {
+            EntitySuggestion bestGuess = getBestGuess();
+            if (bestGuess != null) {
+                return bestGuess.getUri();
+            }
+            return null;
+        }
+
+        public String getSummary() {
+            if (suggestions.isEmpty()) {
+                return "";
+            }
+            return suggestions.get(0).getSummary();
+        }
+
+        public String getThumbnailSrc() {
+            if (suggestions.isEmpty()) {
+                return getMissingThumbnailSrc();
+            }
+            return suggestions.get(0).getThumbnailSrc();
+        }
+
+        public String getMissingThumbnailSrc() {
+            String source = defaultThumbnails.get(type);
+            if(source == null){
+                source = defaultThumbnails.get(null);//default
+            }
+            return source;
+        }
+
+        public EntitySuggestion getBestGuess() {
+            if (suggestions.isEmpty()) {
+                return null;
+            }
+            return suggestions.get(0);
+        }
+
+        public List<EntitySuggestion> getSuggestions() {
+            return suggestions;
+        }
+
+        public List<String> getMentions() {
+            return mentions;
+        }
+
+        @Override
+        public int compareTo(EntityExtractionSummary o) {
+            return getName().compareTo(o.getName());
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            EntityExtractionSummary that = (EntityExtractionSummary) o;
+
+            return !(name != null ? !name.equals(that.name) : that.name != null);
+        }
+
+        @Override
+        public int hashCode() {
+            return name != null ? name.hashCode() : 0;
+        }
+    }
+
+    public static class EntitySuggestion implements Comparable<EntitySuggestion> {
+
+        protected final UriRef uri;
+
+        protected final UriRef type;
+
+        protected final String label;
+
+        protected final Double confidence;
+
+        protected TripleCollection entityProperties;
+
+        protected final Map<UriRef,String> defaultThumbnails;
+
+        public EntitySuggestion(UriRef uri,
+                                UriRef type,
+                                String label,
+                                Double confidence,
+                                TripleCollection entityProperties,
+                                Map<UriRef,String> defaultThumbnails) {
+            this.uri = uri;
+            this.label = label;
+            this.type = type;
+            this.confidence = confidence != null ? confidence : 0.0;
+            this.entityProperties = entityProperties;
+            this.defaultThumbnails = defaultThumbnails;
+        }
+
+        @Override
+        public int compareTo(EntitySuggestion o) {
+            // order suggestions by decreasing confidence
+            return -confidence.compareTo(o.confidence);
+        }
+
+        public String getUri() {
+            return uri.getUnicodeString();
+        }
+
+        public Double getConfidence() {
+            return confidence;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+
+        public String getThumbnailSrc() {
+            Iterator<Triple> thumbnails = entityProperties.filter(uri, THUMBNAIL, null);
+            while (thumbnails.hasNext()) {
+                Resource object = thumbnails.next().getObject();
+                if (object instanceof UriRef) {
+                    return ((UriRef) object).getUnicodeString();
+                }
+            }
+            //if no dbpedia ontology thumbnail was found. try the same with foaf:depiction
+            thumbnails = entityProperties.filter(uri, DEPICTION, null);
+            while (thumbnails.hasNext()) {
+                Resource object = thumbnails.next().getObject();
+                if (object instanceof UriRef) {
+                    return ((UriRef) object).getUnicodeString();
+                }
+            }
+            return getMissingThumbnailSrc();
+        }
+
+        public String getMissingThumbnailSrc() {
+            String source = defaultThumbnails.get(type);
+            if(source == null){
+                source = defaultThumbnails.get(null);
+            }
+            return source;
+        }
+
+        public String getSummary() {
+            Iterator<Triple> abstracts = entityProperties.filter(uri, SUMMARY, null);
+            while (abstracts.hasNext()) {
+                Resource object = abstracts.next().getObject();
+                if (object instanceof PlainLiteral) {
+                    PlainLiteral abstract_ = (PlainLiteral) object;
+                    if (new Language("en").equals(abstract_.getLanguage())) {
+                        return abstract_.getLexicalForm();
+                    }
+                }
+            }
+            return "";
+        }
+
+        // consider entities with same URI as equal even if we have alternate
+        // label values
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((uri == null) ? 0 : uri.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (getClass() != obj.getClass()) return false;
+            EntitySuggestion other = (EntitySuggestion) obj;
+            if (uri == null) {
+                if (other.uri != null) return false;
+            } else if (!uri.equals(other.uri)) return false;
+            return true;
+        }
+
+    }
+
+    public void setRdfSerializationFormat(String format) {
+        serializationFormat = format;
+    }
+
+    /**
+     * @return an RDF/JSON descriptions of places for the word map widget
+     */
+    public String getPlacesAsJSON() throws ParseException, UnsupportedEncodingException {
+        MGraph g = new IndexedMGraph();
+        LiteralFactory lf = LiteralFactory.getInstance();
+        MGraph metadata = contentItem.getMetadata();
+        for (EntityExtractionSummary p : getPlaceOccurrences()) {
+            EntitySuggestion bestGuess = p.getBestGuess();
+            if (bestGuess == null) {
+                continue;
+            }
+            UriRef uri = new UriRef(bestGuess.getUri());
+            Iterator<Triple> latitudes = metadata.filter(uri, GEO_LAT, null);
+            if (latitudes.hasNext()) {
+                g.add(latitudes.next());
+            }
+            Iterator<Triple> longitutes = metadata.filter(uri, GEO_LONG, null);
+            if (longitutes.hasNext()) {
+                g.add(longitutes.next());
+                g.add(new TripleImpl(uri, Properties.RDFS_LABEL, lf.createTypedLiteral(bestGuess.getLabel())));
+            }
+        }
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        serializer.serialize(out, g, SupportedFormat.RDF_JSON);
+        
+        String rdfString = out.toString("utf-8");
+        return rdfString;
+    }
+
+    @GET
+    @Produces(TEXT_HTML)
+    public Response get(@Context HttpHeaders headers) {
+        ResponseBuilder rb = Response.ok(new Viewable("index", this));
+        rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+        addCORSOrigin(servletContext,rb, headers);
+        return rb.build();
+    }
+    
+    public class ExecutionNode {
+        
+        private final NonLiteral node;
+        private final TripleCollection ep;
+        private final boolean optional;
+        private final String engineName;
+        
+        public ExecutionNode(TripleCollection executionPlan, NonLiteral node) {
+            this.node = node;
+            this.ep = executionPlan;
+            this.optional = ExecutionPlanHelper.isOptional(ep, node);
+            this.engineName = ExecutionPlanHelper.getEngine(ep, node);
+        }
+        
+        public boolean isOptional() {
+            return optional;
+        }
+        public String getEngineName() {
+            return engineName;
+        }
+        
+        @Override
+        public int hashCode() {
+            return node.hashCode();
+        }
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof ExecutionNode && ((ExecutionNode)o).node.equals(node);
+        }
+    }
+    public class Execution implements Comparable<Execution>{
+        
+        protected DateFormat format = new SimpleDateFormat("HH-mm-ss.SSS");
+        protected final NonLiteral node;
+        private final ExecutionNode executionNode;
+        private final UriRef status;
+        protected final TripleCollection graph;
+        private final Date started;
+        private final Date completed;
+        private final Long duration;
+        private ChainExecution chain;
+        public Execution(ChainExecution parent, TripleCollection graph, NonLiteral node) {
+            this.chain = parent;
+            this.graph = graph;
+            this.node = node;
+            NonLiteral executionNode = ExecutionMetadataHelper.getExecutionNode(graph, node);
+            if(executionNode != null){
+                this.executionNode = new ExecutionNode(graph, executionNode);
+            } else {
+                this.executionNode = null;
+            }
+            this.status = getReference(graph, node, ExecutionMetadata.STATUS);
+            this.started = ExecutionMetadataHelper.getStarted(graph, node);
+            this.completed = ExecutionMetadataHelper.getCompleted(graph, node);
+            if(started != null && completed != null){
+                this.duration = completed.getTime() - started.getTime();
+            } else {
+                this.duration = null;
+            }
+        }
+
+        /**
+         * @return the executionNode
+         */
+        public ExecutionNode getExecutionNode() {
+            return executionNode;
+        }
+        public String getStatusText(){
+            if(ExecutionMetadata.STATUS_COMPLETED.equals(status)){
+                return "completed";
+            } else if(ExecutionMetadata.STATUS_FAILED.equals(status)){
+                return "failed";
+            } else if(ExecutionMetadata.STATUS_IN_PROGRESS.equals(status)){
+                return "in-progress";
+            } else if(ExecutionMetadata.STATUS_SCHEDULED.equals(status)){
+                return "scheduled";
+            } else if(ExecutionMetadata.STATUS_SKIPPED.equals(status)){
+                return "skipped";
+            } else {
+                return "unknown";
+            }
+        }
+        public Date getStart(){
+            return started;
+        }
+        public Date getCompleted(){
+            return completed;
+        }
+        public boolean isFailed(){
+            return ExecutionMetadata.STATUS_FAILED.equals(status);
+        }
+        public boolean isCompleted(){
+            return ExecutionMetadata.STATUS_COMPLETED.equals(status);
+        }
+        public String getOffsetText(){
+            if(chain == null || chain.getStart() == null || started == null){
+                return null;
+            } else {
+                return String.format("%6dms",started.getTime() - chain.getStart().getTime());
+            }
+        }
+        public String getDurationText(){
+            if(duration == null){
+                return "[duration not available]";
+            } else if(duration < 1025){
+                return duration+"ms";
+            } else {
+                return String.format("%.2fsec",(duration.floatValue()/1000));
+            }
+        }
+        public String getStartTime(){
+            if(started != null){
+                return format.format(started);
+            } else {
+                return "unknown";
+            }
+        }
+        public String getCompletionTime(){
+            if(completed != null){
+                return format.format(completed);
+            } else {
+                return "unknown";
+            }
+        }
+        @Override
+        public int hashCode() {
+            return node.hashCode();
+        }
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof ExecutionNode && ((ExecutionNode)o).node.equals(node);
+        }
+        @Override
+        public int compareTo(Execution e2) {
+            if(started != null && e2.started != null){
+                int result = started.compareTo(e2.started);
+                if(result == 0){
+                    if(completed != null && e2.completed != null){
+                        result = started.compareTo(e2.completed);
+                        if(result == 0){
+                            return node.toString().compareTo(e2.toString());
+                        } else {
+                            return result;
+                        }
+                    } else if (completed == null && e2.completed == null){
+                        return node.toString().compareTo(e2.toString());
+                    } else {
+                        return completed == null ? -1 : 1;
+                    }
+                } else {
+                    return result;
+                }
+            } else if (started == null && e2.started == null){
+                return node.toString().compareTo(e2.toString());
+            } else {
+                return started == null ? -1 : 1;
+            }
+        }
+    }
+    public class ChainExecution extends Execution {
+        
+        private final String chainName;
+        
+        public ChainExecution(TripleCollection graph, NonLiteral node) {
+            super(null,graph,node);
+            NonLiteral ep = ExecutionMetadataHelper.getExecutionPlanNode(graph, node);
+            if(ep != null){
+                chainName = EnhancementEngineHelper.getString(graph, ep, ExecutionPlan.CHAIN);
+            } else {
+                chainName = null;
+            }
+        }
+        
+        public String getChainName(){
+            return chainName;
+        }
+    }
+    
+}

Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/FlowRootResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/FlowRootResource.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/FlowRootResource.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/FlowRootResource.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.stanbol.flow.jersey.resource;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+import static javax.ws.rs.core.MediaType.WILDCARD;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.N3;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.N_TRIPLE;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_JSON;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_XML;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.TURTLE;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.X_TURTLE;
+import static org.apache.stanbol.commons.web.base.CorsHelper.addCORSOrigin;
+import static org.apache.stanbol.flow.jersey.utils.EnhancerUtils.addActiveChains;
+import static org.apache.stanbol.flow.jersey.utils.EnhancerUtils.addActiveEngines;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import org.apache.clerezza.rdf.core.Graph;
+import org.apache.clerezza.rdf.core.MGraph;
+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.core.sparql.ParseException;
+import org.apache.clerezza.rdf.core.sparql.QueryParser;
+import org.apache.clerezza.rdf.core.sparql.query.ConstructQuery;
+import org.apache.clerezza.rdf.core.sparql.query.DescribeQuery;
+import org.apache.clerezza.rdf.core.sparql.query.Query;
+import org.apache.clerezza.rdf.ontologies.RDF;
+import org.apache.stanbol.commons.web.base.utils.MediaTypeUtil;
+import org.apache.stanbol.enhancer.servicesapi.Chain;
+import org.apache.stanbol.enhancer.servicesapi.ChainException;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementException;
+import org.apache.stanbol.enhancer.servicesapi.SparqlQueryEngine.SparqlQueryEngineException;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Enhancer;
+
+import com.sun.jersey.api.view.Viewable;
+
+
+/**
+ * RESTful interface to browse the list of available engines and allow to call them in a stateless,
+ * synchronous way.
+ * <p>
+ * If you need the content of the extractions to be stored on the server, use the StoreRootResource API
+ * instead.
+ */
+@Path("/flow")
+public final class FlowRootResource extends AbstractEnhancerUiResource {
+
+    public FlowRootResource(@Context ServletContext context){
+        super(null,context);
+    }
+    
+    @GET
+    @Produces(value={APPLICATION_JSON,N3,N_TRIPLE,RDF_JSON,RDF_XML,TURTLE,X_TURTLE})
+    public Response getEngines(@Context HttpHeaders headers){
+        MGraph graph = getEnhancerConfigGraph();
+        ResponseBuilder res = Response.ok(graph);
+        addCORSOrigin(servletContext,res, headers);
+        return res.build();
+    }
+
+    /**
+     * Creates the RDF graph for the current Stanbol Enhancer configuration
+     * @return the graph with the configuration
+     */
+    private MGraph getEnhancerConfigGraph() {
+        String rootUrl = uriInfo.getBaseUriBuilder().path(getRootUrl()).build().toString();
+        UriRef enhancerResource = new UriRef(rootUrl+"enhancer");
+        MGraph graph = new SimpleMGraph();
+        graph.add(new TripleImpl(enhancerResource, RDF.type, Enhancer.ENHANCER));
+        addActiveEngines(engineManager, graph, rootUrl);
+        addActiveChains(chainManager, graph, rootUrl);
+        return graph;
+    }
+    
+    @POST
+    @Path("{flowGraph}")
+    @Consumes(WILDCARD)
+    public Response enhanceFromData(byte[] data,
+                                    @QueryParam(value = "uri") String uri,
+                                    @Context HttpHeaders headers,
+                                    @PathParam("flowGraph") final String flowGraph) throws EnhancementException, IOException {
+        String format = TEXT_PLAIN;
+        if (headers.getMediaType() != null) {
+            format = headers.getMediaType().toString();
+        }
+        if (uri != null && uri.isEmpty()) {
+            // let the store build an internal URI based on the content
+            uri = null;
+        }
+        ContentItem ci = new InMemoryContentItem(uri, data, format);
+        if (jobManager != null) {
+        	/**
+        	 * TODO : That's actually an hack for use the jobmanager.enhanceContent() interface.
+        	 * That's not really a chain that we want to pass but an flowgraph...
+        	 * May we have to differ from the jobmanager impl... but not too much could be cool...
+        	 * At the end the flow graph can be able to call engines://, chain:// and store://  
+        	 */
+        	Chain ch = new Chain() {
+				
+				@Override
+				public String getName() {
+					return flowGraph;
+				}
+				
+				@Override
+				public Graph getExecutionPlan() throws ChainException {
+					// TODO Auto-generated method stub
+					return null;
+				}
+				
+				@Override
+				public Set<String> getEngines() throws ChainException {
+					// TODO Auto-generated method stub
+					return null;
+				}
+			};
+            //jobManager.enhanceContent(ci, getChain());
+			jobManager.enhanceContent(ci, ch);
+        }
+        ResponseBuilder rb = Response.ok(ci);
+        MediaType mediaType = MediaTypeUtil.getAcceptableMediaType(headers, null);
+        if (mediaType != null) {
+            rb.header(HttpHeaders.CONTENT_TYPE, mediaType);
+        }
+        addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+    
+    /*
+    TODO :
+    A method that return the list of endpoints of a context from this method camelContext.getEndpoints()
+    optionaly manage differents context and allow definition of differents context. 
+    }*/
+
+}

Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancementPropertiesHelper.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancementPropertiesHelper.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancementPropertiesHelper.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancementPropertiesHelper.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,212 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.stanbol.flow.jersey.utils;
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.rdf.ExecutionMetadata;
+import org.apache.stanbol.enhancer.servicesapi.rdf.ExecutionPlan;
+
+/**
+ * Defines Constants and utilities for using EnhancementProperties
+ */
+public final class EnhancementPropertiesHelper {
+
+    private EnhancementPropertiesHelper(){/* no instances allowed*/}
+    /**
+     * URI used to register an {@link ContentItem#getPart(int, Class) contentPart}
+     * of the type {@link Map Map&lt;String,Objext&gt;} that contains properties
+     * for the enhancement process. <p>
+     * TODO: This might move to servicesapi and also accessible to enhancement
+     * engines
+     */
+    public static final UriRef ENHANCEMENT_PROPERTIES_URI = new UriRef(
+            "urn:apache.org:stanbol.web:enhancement.properties");
+    /**
+     * Boolean switch parsed as {@link QueryParam} tha allows to deactivate the
+     * inclusion of the {@link ContentItem#getMetadata()} in the Response
+     */
+    public static final String OMIT_METADATA = "stanbol.enhancer.web.omitMetadata";
+    /**
+     * {@link Set Set&lt;String&gt;} containing all the URIs of the
+     * {@link ContentItem#getPart(UriRef, Class) ContentParts} representing 
+     * RDF data (compatible to Clerezza {@link TripleCollection}). If the 
+     * returned set contains '*' than all such content parts need to be returned.<p>
+     * NOTE: This can also be used to include the EnhancementProperties
+     * as "applciation/json" in the Response by adding this
+     * {@link EnhancementPropertiesHelper#ENHANCEMENT_PROPERTIES_URI uri}.
+     */
+    public static final String OUTPUT_CONTENT_PART = "stanbol.enhancer.web.outputContentPart";
+    /**
+     * Allows to omit all parsed content parts regardless of the {@link #OUTPUT_CONTENT_PART}
+     * configuration
+     */
+    public static final String OMIT_PARSED_CONTENT = "stanbol.enhancer.web.omitParsed";
+    /**
+     * {@link Collection Collection&lt;String&gt;} containing mime types. This
+     * allows to specify what versions of the parsed content to be included in
+     * the response. e.g. ["text/*","application/pdf"] would include all text
+     * formats and PDF.
+     */
+    public static final String OUTPUT_CONTENT = "stanbol.enhancer.web.outputContent";
+    /**
+     * This allows to copy the {@link ExecutionMetadata} and {@link ExecutionPlan}
+     * data stored in a {@link ContentItem#getPart(UriRef, Class) contentPart} with
+     * the URI {@link ExecutionMetadata#CHAIN_EXECUTION} over to the
+     * {@link ContentItem#getMetadata() metadata} of the content item.<p>
+     * This feature is intended to allow users to retrieve such meta information
+     * without the need to use parse Multipart MIME responses.
+     */
+    public static final String INCLUDE_EXECUTION_METADATA = "stanbol.enhancer.web.executionmetadata";
+    /**
+     * The used format to encode RDF graphs for "multipart/*" responses. This
+     * needs to be parsed separately, because the Accept header needs to be
+     * set to "multipart/from-data" in such cases
+     */
+    public static final String RDF_FORMAT = "stanbol.enhancer.web.rdfFormat";
+    /**
+     * {@link Set Set&lt;String&gt;} containing all the {@link UriRef}s of 
+     * {@link ContentItem#getPart(int, Class) ContentItem.getPart}(uri,{@link Blob})
+     * that where parsed with the request.
+     */
+    public static final String PARSED_CONTENT_URIS = "stanbol.enhancer.web.parsedContentURIs";
+    
+    /**
+     * Getter for the EnhancementProperties for an {@link ContentItem}. If they
+     * do not already exist they are created and added to the ContentItem as
+     * contentPart with the URI {@link #ENHANCEMENT_PROPERTIES_URI}
+     * @param ci the contentItem MUST NOT be NULL
+     * @return the enhancement properties
+     * @throws IllegalArgumentException if <code>null</code> is parsed as {@link ContentItem}.
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String,Object> getEnhancementProperties(ContentItem ci){
+        if(ci == null){
+            throw new IllegalArgumentException("The parsed ContentItem MUST NOT be NULL!");
+        }
+        Map<String,Object> enhancementProperties;
+        try {
+            enhancementProperties = ci.getPart(ENHANCEMENT_PROPERTIES_URI, Map.class);
+        } catch (RuntimeException e) {
+           enhancementProperties = new HashMap<String,Object>();
+           ci.addPart(ENHANCEMENT_PROPERTIES_URI, enhancementProperties);
+        }
+        return enhancementProperties;
+    }
+    
+    
+    private static Object get(Map<String,Object> enhancementProperties,String key){
+        return enhancementProperties == null ? null : enhancementProperties.get(key);
+    }
+    /**
+     * Getter for the value of the parsed type for a given key.
+     * @param enhancementProperties the enhancement properties
+     * @param key the key
+     * @param type the type MUST NOT be <code>null</code>
+     * @return the values
+     * @throws ClassCastException if the value is not compatible to the
+     * parsed type
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T get(Map<String,Object> enhancementProperties,String key,Class<T> type){
+        if(type == null){
+            throw new IllegalArgumentException("The parsed type MUST NOT be NULL!");
+        }
+        Object value = get(enhancementProperties, key);
+        if(value == null){
+            return null;
+        } else if(type.isAssignableFrom(value.getClass())){
+            return (T)value;
+        } else {
+            throw new ClassCastException("EnhancementProperties value for key '"
+                    + key +"' is not of the expected type "+type+" but was"
+                    + value.getClass());
+        }
+    }
+    /**
+     * Getter for the boolean state based on the value of the parsed key.
+     * If the value is not of type {@link Boolean} the 
+     * {@link Boolean#parseBoolean(String)} is used on the {@link Object#toString()}
+     * method of the value.
+     * @param enhancementProperties the enhancementProperties
+     * @param key the key
+     * @return the state
+     */
+    public static boolean getState(Map<String,Object> enhancementProperties,String key){
+        Object state = get(enhancementProperties, key);
+        return state == null ? false : 
+            state instanceof Boolean ? ((Boolean)state).booleanValue() : 
+                Boolean.parseBoolean(state.toString());
+    }
+    public static boolean isOmitParsedContent(Map<String,Object> enhancementProperties){
+        return getState(enhancementProperties, OMIT_PARSED_CONTENT);
+    }
+    public static boolean isIncludeExecutionMetadata(Map<String,Object> enhancementProperties){
+        return getState(enhancementProperties, INCLUDE_EXECUTION_METADATA);
+    }
+    public static boolean isOmitMetadata(Map<String,Object> enhancementProperties){
+        return getState(enhancementProperties, OMIT_METADATA);
+    }
+    /**
+     * 
+     * @param enhancementProperties
+     * @return
+     * @throws ClassCastException if the value is not an Set
+     */
+    @SuppressWarnings("unchecked")
+    public static Collection<String> getParsedContentURIs(Map<String,Object> enhancementProperties){
+        return (Collection<String>)get(enhancementProperties, PARSED_CONTENT_URIS, Collection.class);
+    }
+    /**
+     * 
+     * @param enhancementProperties
+     * @return
+     * @throws ClassCastException if the value is not an {@link Set}
+     */
+    @SuppressWarnings("unchecked")
+    public static Collection<String> getOutputContentParts(Map<String,Object> enhancementProperties){
+        return (Collection<String>)get(enhancementProperties, OUTPUT_CONTENT_PART, Collection.class);
+    }
+    /**
+     * 
+     * @param enhancementProperties
+     * @return
+     * @throws ClassCastException if the value is not an {@link Collections}
+     */
+    @SuppressWarnings("unchecked")
+    public static Collection<String> getOutputContent(Map<String,Object> enhancementProperties){
+        return (Collection<String>)get(enhancementProperties, OUTPUT_CONTENT, Collection.class);
+    }
+    /**
+     * 
+     * @param enhancementProperties
+     * @return
+     * @throws ClassCastException if the value is not an {@link Collections}
+     */
+    public static String getRdfFormat(Map<String,Object> enhancementProperties){
+        return (String) get(enhancementProperties,RDF_FORMAT,String.class);
+    }
+}
\ No newline at end of file

Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancerUtils.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancerUtils.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancerUtils.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/utils/EnhancerUtils.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.stanbol.flow.jersey.utils;
+
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Enhancer.ENHANCEMENT_ENGINE;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.impl.PlainLiteralImpl;
+import org.apache.clerezza.rdf.core.impl.TripleImpl;
+import org.apache.clerezza.rdf.ontologies.RDF;
+import org.apache.clerezza.rdf.ontologies.RDFS;
+import org.apache.stanbol.commons.web.base.resource.BaseStanbolResource;
+import org.apache.stanbol.enhancer.servicesapi.Chain;
+import org.apache.stanbol.enhancer.servicesapi.ChainManager;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngineManager;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Enhancer;
+import org.osgi.framework.ServiceReference;
+
+public final class EnhancerUtils {
+
+    private EnhancerUtils(){};
+    
+    
+    /**
+     * Uses the parsed {@link EnhancementEngineManager} to build a Map
+     * representing the current snapshot of the active enhancement engines.
+     * 
+     * @param engineManager The engine manager used to build the snapshot
+     * @return the map with the names as key and an Entry with the {@link ServiceReference}
+     * and the {@link EnhancementEngine} instance as value.
+     */
+    public static Map<String, Entry<ServiceReference,EnhancementEngine>> buildEnginesMap(EnhancementEngineManager engineManager) {
+        Map<String, Entry<ServiceReference,EnhancementEngine>> engines = new HashMap<String,Map.Entry<ServiceReference,EnhancementEngine>>();
+        for(String engineName : engineManager.getActiveEngineNames()){
+            ServiceReference engineRef = engineManager.getReference(engineName);
+            if(engineRef != null){
+                EnhancementEngine engine = engineManager.getEngine(engineRef);
+                if(engine != null){
+                    Map<ServiceReference,EnhancementEngine> m = Collections.singletonMap(engineRef, engine);
+                    engines.put(engineName, m.entrySet().iterator().next());
+                }
+            }
+        }
+        return engines;
+    }
+    /**
+     * Uses the parsed {@link ChainManager} to build a Map
+     * representing the current snapshot of the active enhancement chains.
+     * 
+     * @param chainManager The chain manager used to build the snapshot
+     * @return the map with the names as key and an Entry with the {@link ServiceReference}
+     * and the {@link Chain} instance as value.
+     */
+    public static Map<String,Map.Entry<ServiceReference,Chain>> buildChainsMap(ChainManager chainManager) {
+        Map<String,Map.Entry<ServiceReference,Chain>> chains = new HashMap<String,Map.Entry<ServiceReference,Chain>>();
+        for(String chainName : chainManager.getActiveChainNames()){
+            ServiceReference chainRef = chainManager.getReference(chainName);
+            if(chainRef != null){
+                Chain chain = chainManager.getChain(chainRef);
+                if(chain != null){
+                    Map<ServiceReference,Chain> m = Collections.singletonMap(chainRef, chain);
+                    chains.put(chainName, m.entrySet().iterator().next());
+                }
+            }
+        }
+        return chains;
+    }
+    /**
+     * Create the RDF data for the currently active EnhancementEngines.<p>
+     * Note the the parsed rootUrl MUST already consider offsets configured
+     * for the Stanbol RESTful service. When called from within a
+     * {@link BaseStanbolResource} the following code segment should be used:<p>
+     * <code><pre>
+     *     String rootUrl = uriInfo.getBaseUriBuilder().path(getRootUrl()).build().toString();
+     * </pre></code>
+     * @param engineManager the enhancement engine manager
+     * @param graph the RDF graph to add the triples
+     * @param rootUrl the root URL used by the current request
+     */
+    public static void addActiveEngines(EnhancementEngineManager engineManager,MGraph graph, String rootUrl) {
+        addActiveEngines(buildEnginesMap(engineManager).values(), graph, rootUrl);
+    }
+    /**
+     * Create the RDF data for the currently active EnhancementEngines.<p>
+     * Note the the parsed rootUrl MUST already consider offsets configured
+     * for the Stanbol RESTful service. When called from within a
+     * {@link BaseStanbolResource} the following code segment should be used:<p>
+     * <code><pre>
+     *     String rootUrl = uriInfo.getBaseUriBuilder().path(getRootUrl()).build().toString();
+     * </pre></code>
+     * @param activeEngines the active enhancement engines as {@link Entry entries}.
+     * @param graph the RDF graph to add the triples
+     * @param rootUrl the root URL used by the current request
+     * @see EnhancerUtils#buildEnginesMap(EnhancementEngineManager)
+     */
+    public static void addActiveEngines(Iterable<Entry<ServiceReference,EnhancementEngine>> activeEngines,MGraph graph, String rootUrl) {
+        UriRef enhancerResource = new UriRef(rootUrl+"enhancer");
+        graph.add(new TripleImpl(enhancerResource, RDF.type, Enhancer.ENHANCER));
+        for(Entry<ServiceReference,EnhancementEngine> entry : activeEngines){
+            UriRef engineResource = new UriRef(rootUrl+"enhancer/engine/"+entry.getValue().getName());
+            graph.add(new TripleImpl(enhancerResource, Enhancer.HAS_ENGINE, engineResource));
+            graph.add(new TripleImpl(engineResource, RDF.type, ENHANCEMENT_ENGINE));
+            graph.add(new TripleImpl(engineResource, RDFS.label, new PlainLiteralImpl(entry.getValue().getName())));
+        }
+    }
+    
+    /**
+     * Create the RDF data for the currently active Enhancement {@link Chain}s.<p>
+     * Note the the parsed rootUrl MUST already consider offsets configured
+     * for the Stanbol RESTful service. When called from within a
+     * {@link BaseStanbolResource} the following code segment should be used:<p>
+     * <code><pre>
+     *     String rootUrl = uriInfo.getBaseUriBuilder().path(getRootUrl()).build().toString();
+     * </pre></code>
+     * @param chainManager the enhancement chain manager.
+     * @param graph the RDF graph to add the triples
+     * @param rootUrl the root URL used by the current request
+     */
+    public static void addActiveChains(ChainManager chainManager, MGraph graph, String rootUrl) {
+        addActiveChains(buildChainsMap(chainManager).values(), chainManager.getDefault(), graph, rootUrl);
+    }
+    /**
+     * Create the RDF data for the currently active Enhancement {@link Chain}s.<p>
+     * Note the the parsed rootUrl MUST already consider offsets configured
+     * for the Stanbol RESTful service. When called from within a
+     * {@link BaseStanbolResource} the following code segment should be used:<p>
+     * <code><pre>
+     *     String rootUrl = uriInfo.getBaseUriBuilder().path(getRootUrl()).build().toString();
+     * </pre></code>
+     * @param activeChains the active enhancement chains as {@link Entry entries}.
+     * @param defaultChain the default chain
+     * @param graph the RDF graph to add the triples
+     * @param rootUrl the root URL used by the current request
+     */
+    public static void addActiveChains(Iterable<Entry<ServiceReference,Chain>> activeChains, Chain defaultChain, MGraph graph, String rootUrl) {
+        UriRef enhancer = new UriRef(rootUrl+"enhancer");
+        graph.add(new TripleImpl(enhancer, RDF.type, Enhancer.ENHANCER));
+        for(Entry<ServiceReference,Chain> entry : activeChains){
+            UriRef chainResource = new UriRef(rootUrl+"enhancer/chain/"+entry.getValue().getName());
+            graph.add(new TripleImpl(enhancer, Enhancer.HAS_CHAIN, chainResource));
+            if(entry.getValue().equals(defaultChain)){
+                graph.add(new TripleImpl(enhancer, Enhancer.HAS_DEFAULT_CHAIN, chainResource));
+            }
+            graph.add(new TripleImpl(chainResource, RDF.type, Enhancer.ENHANCEMENT_CHAIN));
+            graph.add(new TripleImpl(chainResource, RDFS.label, new PlainLiteralImpl(entry.getValue().getName())));
+        }
+    }
+}

Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,545 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.stanbol.flow.jersey.writers;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
+import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA_TYPE;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static javax.ws.rs.core.MediaType.WILDCARD_TYPE;
+import static org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getBlob;
+import static org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getContentParts;
+import static org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getMimeTypeWithParameters;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.ENHANCEMENT_PROPERTIES_URI;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getEnhancementProperties;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getOutputContent;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getOutputContentParts;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getParsedContentURIs;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.isOmitMetadata;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.isOmitParsedContent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+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.commons.io.IOUtils;
+import org.apache.http.entity.mime.FormBodyPart;
+import org.apache.http.entity.mime.HttpMultipart;
+import org.apache.http.entity.mime.MIME;
+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.StringBody;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.commons.web.base.writers.JsonLdSerializerProvider;
+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.flow.jersey.utils.EnhancementPropertiesHelper;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+@Provider
+public class ContentItemWriter implements MessageBodyWriter<ContentItem> {
+
+    /**
+     * The "multipart/*" wilrcard
+     */
+    private static final MediaType MULTIPART = MediaType.valueOf(MULTIPART_FORM_DATA_TYPE.getType()+"/*");
+    private static final String CONTENT_ITEM_BOUNDARY = "contentItem";
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+    private static final MediaType DEFAULT_RDF_FORMAT = new MediaType(
+        APPLICATION_JSON_TYPE.getType(),
+        APPLICATION_JSON_TYPE.getSubtype(),
+        Collections.singletonMap("charset", UTF8.toString()));
+    
+    private Serializer __serializer;
+    
+    private ServletContext context;
+
+    public ContentItemWriter(@Context ServletContext context){
+        this.context = context;
+    }
+    /**
+     * Lazzy initialisation for the {@link Serializer}
+     * @return the {@link Serializer}
+     */
+    protected final Serializer getSerializer(){
+        /*
+         * Needed because Jersey tries to create an instance
+         * during initialisation. At that time the {@link BundleContext} required
+         * by {@link ContextHelper#getServiceFromContext(Class, ServletContext)}
+         * is not yet present resulting in an Exception.
+         */
+        if(__serializer == null){
+            if(context != null){
+                __serializer = ContextHelper.getServiceFromContext(Serializer.class, context);
+            } else {
+                __serializer = new Serializer();
+                __serializer.bindSerializingProvider(new JenaSerializerProvider());
+                __serializer.bindSerializingProvider(new JsonLdSerializerProvider());
+            }
+        }
+        return __serializer;
+    }
+    
+    @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 {
+
+        //(0) handle default dataType
+        Map<String,Object> properties = getEnhancementProperties(ci);
+        boolean omitMetadata = isOmitMetadata(properties);
+        if(!MULTIPART.isCompatible(mediaType)){ //two possible cases
+            if(!omitMetadata){ //  (1) just return the RDF data
+                //(1.a) Backward support for default dataType if no Accept header is set
+                if (mediaType.isWildcardType() || 
+                        TEXT_PLAIN_TYPE.isCompatible(mediaType) || 
+                        APPLICATION_OCTET_STREAM_TYPE.isCompatible(mediaType)) {
+                    mediaType = new MediaType(APPLICATION_JSON_TYPE.getType(), 
+                        APPLICATION_JSON_TYPE.getSubtype(),
+                        //Clerezza serialisers are hard coded to use UTF-8
+                        Collections.singletonMap("charset", UTF8.toString()));
+                    httpHeaders.putSingle("Content-Type", mediaType.toString());
+                }
+                getSerializer().serialize(entityStream, ci.getMetadata(), mediaType.toString());
+            } else { //  (2) return a single content part
+                Entry<UriRef,Blob> contentPart = getBlob(ci, Collections.singleton(mediaType.toString()));
+                if(contentPart == null){ //no alternate content with the requeste media type
+                    throw new WebApplicationException(
+                        Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE)
+                        .entity("The requested enhancement chain has not created an " +
+                        		"version of the parsed content in the reuqest media " +
+                        		"type "+mediaType.toString())
+                        .build());
+                } else { //found -> stream the content to the client
+                    //NOTE: This assumes that the presence of a charset
+                    //      implies reading/writing character streams
+                    String requestedCharset = mediaType.getParameters().get("charset");
+                    String blobCharset = contentPart.getValue().getParameter().get("charset");
+                    Charset readerCharset = blobCharset == null ? UTF8 : Charset.forName(blobCharset);
+                    Charset writerCharset = requestedCharset == null ? null : Charset.forName(requestedCharset);
+                    if(writerCharset != null && !writerCharset.equals(readerCharset)){
+                        //we need to transcode
+                        Reader reader = new InputStreamReader(
+                            contentPart.getValue().getStream(),readerCharset);
+                        Writer writer = new OutputStreamWriter(entityStream, writerCharset);
+                        IOUtils.copy(reader, writer);
+                        IOUtils.closeQuietly(reader);
+                    } else { //no transcoding
+                        if(requestedCharset == null && blobCharset != null){
+                            httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE, 
+                                mediaType.toString()+"; charset="+blobCharset);
+                        }
+                        InputStream in  = contentPart.getValue().getStream();
+                        IOUtils.copy(in, entityStream);
+                        IOUtils.closeQuietly(in);
+                    }
+                }
+            }
+        } else { // multipart mime requested!
+            final String charsetName = mediaType.getParameters().get("charset");
+            final Charset charset = charsetName != null ? Charset.forName(charsetName) : UTF8;
+            MediaType rdfFormat;
+            String rdfFormatString = EnhancementPropertiesHelper.getRdfFormat(properties);
+            if(rdfFormatString == null || rdfFormatString.isEmpty()){
+                rdfFormat = DEFAULT_RDF_FORMAT;
+            } else {
+                try {
+                    rdfFormat = MediaType.valueOf(rdfFormatString);
+                    if(rdfFormat.getParameters().get("charset") == null){
+                        //use the charset of the default RDF format
+                        rdfFormat = new MediaType(
+                            rdfFormat.getType(), rdfFormat.getSubtype(), 
+                            DEFAULT_RDF_FORMAT.getParameters());
+                    }
+                } catch (IllegalArgumentException e) {
+                    throw new WebApplicationException(e, 
+                        Response.status(Response.Status.BAD_REQUEST)
+                        .entity(String.format("The specified RDF format '%s' (used "
+                            + " to serialize all RDF parts of multipart MIME responses)"
+                            + " is not a well formated MIME type",rdfFormatString))
+                        .build());
+                }
+            }
+            //(1) setting the correct header
+            String contentType = String.format("%s/%s; charset=%s; boundary=%s",
+                mediaType.getType(),mediaType.getSubtype(),charset.toString(),CONTENT_ITEM_BOUNDARY);
+            httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE,contentType);
+            HttpMultipart entity = new HttpMultipart("from-data", charset ,CONTENT_ITEM_BOUNDARY);
+            //(2) serialising the metadata
+            if(!isOmitMetadata(properties)){
+                entity.addBodyPart(new FormBodyPart("metadata", new ClerezzaContentBody(
+                    ci.getUri().getUnicodeString(), ci.getMetadata(),
+                    rdfFormat)));
+            }
+            //(3) serialising the Content (Bloby)
+            //(3.a) Filter based on parameter
+            List<Entry<UriRef,Blob>> includedBlobs = filterBlobs(ci, properties);
+            //(3.b) Serialise the filtered
+            if(!includedBlobs.isEmpty()) {
+                HttpMultipart content = new HttpMultipart("alternate", UTF8 ,"contentParts");
+                for(Entry<UriRef,Blob> entry : includedBlobs){
+                    content.addBodyPart(new FormBodyPart(entry.getKey().getUnicodeString(), 
+                        new BlobContentBody(entry.getValue()))); //no file name
+                }
+                //add all the blobs
+                entity.addBodyPart(new FormBodyPart("content",new MultipartContentBody(content, null)));
+            } //else no content to include
+            Set<String> includeContentParts = getIncludedContentPartURIs(properties);
+            if(includeContentParts != null){
+                //(4) serialise EnhancementProperties
+                if(includeContentParts.isEmpty() || includeContentParts.contains(
+                    ENHANCEMENT_PROPERTIES_URI.getUnicodeString())) {
+                    JSONObject object;
+                    try {
+                        object = toJson(properties);
+                    } catch (JSONException e) {
+                        throw new WebApplicationException(e,
+                            Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+                            .entity("Unable to convert EnhancementProperties to " +
+                            		"JSON (values : "+properties+")!").build());
+                    }
+                    entity.addBodyPart(new FormBodyPart(
+                        ENHANCEMENT_PROPERTIES_URI.getUnicodeString(), 
+                        new StringBody(object.toString(),MediaType.APPLICATION_JSON,UTF8)));
+                }
+                //(5) additional RDF metadata stored in contentParts
+                for(Entry<UriRef,TripleCollection> entry : getContentParts(ci, TripleCollection.class).entrySet()){
+                    if(includeContentParts.isEmpty() || includeContentParts.contains(
+                        entry.getKey())){
+                        entity.addBodyPart(new FormBodyPart(entry.getKey().getUnicodeString(), 
+                            new ClerezzaContentBody(null, //no file name
+                                entry.getValue(),rdfFormat)));
+                    } // else ignore this content part
+                }
+            }
+            entity.writeTo(entityStream);
+        }   
+            
+    }
+    /**
+     * @param properties
+     * @return
+     */
+    private JSONObject toJson(Map<?,?> map) throws JSONException {
+        JSONObject object = new JSONObject();
+        for(Entry<?,?> entry : map.entrySet()){
+            Object value = getValue(entry.getValue());
+            object.put(entry.getKey().toString(),value);
+        }
+        return object;
+    }
+    /**
+     * @param entry
+     * @return
+     * @throws JSONException
+     */
+    private Object getValue(Object javaValue) throws JSONException {
+        Object value;
+        if(javaValue instanceof Collection<?>){
+            value = new JSONArray();
+            for(Object o : (Collection<?>)javaValue){
+                ((JSONArray)value).put(getValue(o));
+            }
+        } else if(javaValue instanceof Map<?,?>){
+            value = toJson((Map<?,?>)javaValue);
+        } else {
+            value = javaValue;
+        }
+        return value;
+    }
+    /**
+     * @param properties
+     * @return
+     */
+    private Set<String> getIncludedContentPartURIs(Map<String,Object> properties) {
+        Collection<String> ocp = getOutputContentParts(properties);
+        if(ocp == null || ocp.isEmpty()){
+            return null;
+        }
+        Set<String> includeContentParts = new HashSet<String>(ocp);
+        if(includeContentParts != null){
+            if(includeContentParts.isEmpty()){ //empty == none
+                includeContentParts = null;
+            } else if (includeContentParts.contains("*")){ // * == all -> empty list
+                includeContentParts = Collections.emptySet();
+            }
+        }
+        return includeContentParts;
+    }
+    /**
+     * @param ci
+     * @param properties
+     * @return
+     */
+    private List<Entry<UriRef,Blob>> filterBlobs(ContentItem ci, Map<String,Object> properties) {
+        final List<Entry<UriRef,Blob>> includedContentPartList;
+        Set<MediaType> includeMediaTypes = getIncludedMediaTypes(properties);
+        if(includeMediaTypes == null){
+            includedContentPartList = Collections.emptyList();
+        } else {
+            includedContentPartList = new ArrayList<Map.Entry<UriRef,Blob>>();
+            Set<String> ignoreContentPartUris = getIgnoredContentURIs(properties);
+            nextContentPartEntry: 
+            for(Entry<UriRef,Blob> entry : getContentParts(ci,Blob.class).entrySet()){
+                if(!ignoreContentPartUris.contains(entry.getKey().getUnicodeString())){
+                    Blob blob = entry.getValue();
+                    MediaType blobMediaType = MediaType.valueOf(blob.getMimeType());
+                    for(MediaType included : includeMediaTypes) {
+                        if(blobMediaType.isCompatible(included)){
+                            includedContentPartList.add(entry);
+                            continue nextContentPartEntry;
+                        }
+                    }
+                } //else ignore this Blob
+            }
+        }
+        return includedContentPartList;
+    }
+    /**
+     * @param properties
+     * @return
+     */
+    private Set<String> getIgnoredContentURIs(Map<String,Object> properties) {
+        Set<String> ignoreContentPartUris = isOmitParsedContent(properties) ?
+                new HashSet<String>(getParsedContentURIs(properties)) : null;
+        if(ignoreContentPartUris == null){
+            ignoreContentPartUris = Collections.emptySet();
+        }
+        return ignoreContentPartUris;
+    }
+    /**
+     * @param properties
+     * @return
+     */
+    private Set<MediaType> getIncludedMediaTypes(Map<String,Object> properties) throws WebApplicationException {
+        Collection<String> includeMediaTypeStrings = getOutputContent(properties);
+        if(includeMediaTypeStrings == null){
+            return null;
+        }
+        Set<MediaType> includeMediaTypes = new HashSet<MediaType>(includeMediaTypeStrings.size());
+        for(String includeString : includeMediaTypeStrings){
+            if(includeString != null){
+                includeString.trim();
+                if(!includeString.isEmpty()){
+                    if("*".equals(includeString)){ //also support '*' for '*/*'
+                        includeMediaTypes.add(WILDCARD_TYPE);
+                    } else {
+                        try {
+                            includeMediaTypes.add(MediaType.valueOf(includeString));
+                        } catch (IllegalArgumentException e){
+                            throw new WebApplicationException(e, 
+                                Response.status(Response.Status.BAD_REQUEST)
+                                .entity("The parsed outputContent parameter "
+                                    + includeMediaTypeStrings +" contain an "
+                                    + "illegal formated MediaType!")
+                                .build());
+                        }
+                    }
+                }
+            }
+        }
+        if(includeMediaTypes.contains(WILDCARD_TYPE)){
+            includeMediaTypes = Collections.singleton(WILDCARD_TYPE);
+        }
+        return includeMediaTypes;
+    }
+
+    /**
+     * 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 MIME.ENC_8BIT;
+        }
+
+        @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 charset;
+        private String name;
+
+        protected ClerezzaContentBody(String name, TripleCollection graph, MediaType mimeType){
+            super(mimeType.getType()+'/'+mimeType.getSubtype());
+            charset = mimeType.getParameters().get("charset");
+            if(charset == null || charset.isEmpty()){
+                charset = UTF8.toString();
+            }
+            this.name = name;
+            this.graph = graph;
+        }
+
+        @Override
+        public String getCharset() {
+            return charset;
+        }
+
+        @Override
+        public String getTransferEncoding() {
+            return MIME.ENC_8BIT;
+        }
+
+        @Override
+        public long getContentLength() {
+            return -1;
+        }
+
+        @Override
+        public String getFilename() {
+            return name;
+        }
+
+        @Override
+        public void writeTo(OutputStream out) throws IOException {
+            getSerializer().serialize(out, graph, getMediaType()+'/'+getSubType());
+        }
+    }
+    private class BlobContentBody extends AbstractContentBody {
+        
+        private Blob blob;
+
+        public BlobContentBody(Blob blob) {
+            super(blob.getMimeType());
+            this.blob = blob;
+        }
+
+        @Override
+        public String getFilename() {
+            return null;
+        }
+
+        @Override
+        public void writeTo(OutputStream out) throws IOException {
+            InputStream in = blob.getStream();
+            IOUtils.copy(in, out);
+            IOUtils.closeQuietly(in);
+        }
+
+        @Override
+        public String getCharset() {
+            return blob.getParameter().get("charset");
+        }
+
+        @Override
+        public String getTransferEncoding() {
+            return blob.getParameter().get("charset") == null ?
+                    MIME.ENC_BINARY : MIME.ENC_8BIT;
+        }
+
+        @Override
+        public long getContentLength() {
+            return -1;
+        }
+        
+    }
+    
+}