You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by og...@apache.org on 2011/03/28 20:41:25 UTC

svn commit: r1086333 - in /incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java: ./ org/ org/apache/ org/apache/stanbol/ org/apache/stanbol/enhancer/ org/apache/stanbol/enhancer/jersey/ org/apache/stanbol/enhancer/jersey/c...

Author: ogrisel
Date: Mon Mar 28 18:41:24 2011
New Revision: 1086333

URL: http://svn.apache.org/viewvc?rev=1086333&view=rev
Log:
STANBOL-120: started to refactor enhancer/jersey to use the new WebFragment incremental contribution system (step 3)

Added:
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/CachingDereferencerEngine.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/EntityCacheProvider.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/fragment/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/fragment/EnhancerWebFragment.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/ContentItemResource.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/EnginesRootResource.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/SparqlQueryResource.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/StoreRootResource.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/GraphWriter.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/JsonLdSerializerProvider.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetToXml.java
    incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetWriter.java

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/CachingDereferencerEngine.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/CachingDereferencerEngine.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/CachingDereferencerEngine.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/CachingDereferencerEngine.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,227 @@
+package org.apache.stanbol.enhancer.jersey.cache;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.clerezza.rdf.core.Graph;
+import org.apache.clerezza.rdf.core.MGraph;
+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.NoSuchEntityException;
+import org.apache.clerezza.rdf.core.access.WeightedTcProvider;
+import org.apache.clerezza.rdf.core.impl.SimpleMGraph;
+import org.apache.clerezza.rdf.core.serializedform.Parser;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementJobManager;
+import org.apache.stanbol.enhancer.servicesapi.ServiceProperties;
+import org.apache.stanbol.enhancer.servicesapi.Store;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_XML;
+
+/**
+ * Simple engine that does not enhance content items but fetches resources
+ * metadata from remote sites to cache them locally for the sole purpose of
+ * displaying up to date data in the user interface.
+ * <p>
+ * This engine might be replaced by a proper dereferencer engine in a future
+ * version of enhancer.
+ *
+ * @author Olivier Grisel
+ */
+@Component(immediate = true, metatype = true)
+@Service
+public class CachingDereferencerEngine implements EnhancementEngine,
+        ServiceProperties, EntityCacheProvider {
+
+    public static final String ENTITY_CACHE_GRAPH_NAME = "enhancerEntityCache";
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * The default value for the Execution of this Engine. Currently set to
+     * {@link EnhancementJobManager#DEFAULT_ORDER}
+     */
+    public static final Integer defaultOrder = ORDERING_POST_PROCESSING;
+
+    @Reference
+    protected Parser parser;
+
+    @Reference
+    protected Store store;
+
+    @Reference
+    protected WeightedTcProvider tcProvider;
+
+    protected ThreadPoolExecutor executor;
+
+    protected BlockingQueue<TripleCollection> serializationQueue;
+
+    protected Thread serializer;
+
+    protected boolean serializerActive = false;
+
+    protected LinkedBlockingQueue<Runnable> fetchTaskQueue;
+
+    protected void activate(ComponentContext ce) throws IOException {
+        fetchTaskQueue = new LinkedBlockingQueue<Runnable>();
+        executor = new ThreadPoolExecutor(4, 10, 5, TimeUnit.MINUTES,
+                fetchTaskQueue);
+        serializationQueue = new LinkedBlockingQueue<TripleCollection>();
+        final MGraph entityCache = getEntityCache();
+        serializerActive = true;
+        serializer = new Thread() {
+            @Override
+            public void run() {
+                while (serializerActive) {
+                    try {
+                        entityCache.addAll(serializationQueue.take());
+                    } catch (InterruptedException e) {
+                        log.error(e.getMessage(), e);
+                    }
+                }
+            }
+        };
+        serializer.start();
+    }
+
+    protected void deactivate(ComponentContext ce) throws IOException {
+        executor.shutdownNow();
+        executor = null;
+        fetchTaskQueue = null;
+        // stop the serialization queue by sending it a last job
+        serializerActive = false;
+        serializationQueue.add(new SimpleMGraph());
+    }
+
+    @Override
+    public void computeEnhancements(ContentItem ci) throws EngineException {
+        MGraph metadata = ci.getMetadata();
+        Set<UriRef> references = new HashSet<UriRef>();
+        Iterator<Triple> entities = metadata.filter(null,
+                Properties.ENHANCER_ENTITY_REFERENCE, null);
+        while (entities.hasNext()) {
+            references.add((UriRef) entities.next().getObject());
+        }
+
+        final MGraph entityCache = getEntityCache();
+        for (final UriRef reference : references) {
+            if (entityCache.filter(reference, null, null).hasNext()) {
+                // already in cache
+                continue;
+            }
+            if (fetchTaskQueue.contains(reference)) {
+                // optim: do not try to submit twice the same job
+                continue;
+            }
+
+            // asynchronously dereference (fire and forget)
+            executor.execute(new Runnable() {
+                @Override
+                public boolean equals(Object other) {
+                    // overridden to implement queue membership to avoid
+                    // duplicate submissions for dereferencing of the same URI
+                    if (other instanceof UriRef) {
+                        return reference.equals(other);
+                    }
+                    return this == other;
+                }
+
+                @Override
+                public void run() {
+                    try {
+                        serializationQueue.add(dereference(reference));
+                    } catch (IOException e) {
+                        log.warn("unable to dereference " + reference + " : "
+                                + e.getMessage());
+                        log.debug(e.getMessage(), e);
+                    }
+                }
+            });
+        }
+    }
+
+    public Graph dereferenceHTTP(UriRef reference) throws IOException {
+        final URL url = new URL(reference.getUnicodeString());
+        final URLConnection con = url.openConnection();
+        con.addRequestProperty("Accept", RDF_XML);
+        return parser.parse(con.getInputStream(), RDF_XML);
+    }
+
+    public Graph dereferenceSPARQL(String endpointURL, UriRef reference)
+            throws IOException {
+
+        StringBuilder query = new StringBuilder();
+        query.append("CONSTRUCT { ");
+        query.append(reference);
+        query.append(" ?p ?o } WHERE { ");
+        query.append(reference);
+        query.append(" ?p ?o }");
+
+        String format = RDF_XML;
+        final URI uri = UriBuilder.fromUri(endpointURL).queryParam("query",
+                "{query}").queryParam("format", "{format}").build(
+                query.toString(), format);
+        final URLConnection con = uri.toURL().openConnection();
+        con.addRequestProperty("Accept", format);
+        return parser.parse(con.getInputStream(), format);
+    }
+
+    public Graph dereference(UriRef reference) throws IOException {
+        log.debug("dereferencing: " + reference);
+        // TODO: make the switch between SPARQL and HTTP configurable
+        if (reference.getUnicodeString().startsWith(
+                "http://dbpedia.org/resource/")) {
+            // special handling of dbpedia references using SPARQL since the
+            // basic HTTP dereference run the risk of a truncated output
+            return dereferenceSPARQL("http://dbpedia.org/sparql", reference);
+        } else {
+            return dereferenceHTTP(reference);
+        }
+    }
+
+    @Override
+    public int canEnhance(ContentItem ci) throws EngineException {
+        return ENHANCE_SYNCHRONOUS;
+    }
+
+    @Override
+    public Map<String, Object> getServiceProperties() {
+        return Collections.unmodifiableMap(Collections.singletonMap(
+                ENHANCEMENT_ENGINE_ORDERING,
+                (Object) defaultOrder));
+    }
+
+    public MGraph getEntityCache() {
+        final UriRef graphUri = new UriRef(ENTITY_CACHE_GRAPH_NAME);
+        try {
+            return tcProvider.getMGraph(graphUri);
+        } catch (NoSuchEntityException e) {
+            return tcProvider.createMGraph(graphUri);
+        }
+    }
+
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/EntityCacheProvider.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/EntityCacheProvider.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/EntityCacheProvider.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/cache/EntityCacheProvider.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,9 @@
+package org.apache.stanbol.enhancer.jersey.cache;
+
+import org.apache.clerezza.rdf.core.MGraph;
+
+public interface EntityCacheProvider {
+
+    MGraph getEntityCache();
+
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/fragment/EnhancerWebFragment.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/fragment/EnhancerWebFragment.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/fragment/EnhancerWebFragment.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/fragment/EnhancerWebFragment.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,61 @@
+package org.apache.stanbol.enhancer.jersey.fragment;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.commons.web.WebFragment;
+import org.apache.stanbol.enhancer.jersey.resource.EnginesRootResource;
+import org.apache.stanbol.enhancer.jersey.resource.SparqlQueryResource;
+import org.apache.stanbol.enhancer.jersey.resource.StoreRootResource;
+
+import freemarker.cache.ClassTemplateLoader;
+import freemarker.cache.TemplateLoader;
+
+/**
+ * Statically define the list of available resources and providers to be contributed to the the Stanbol JAX-RS
+ * Endpoint.
+ */
+@Component(immediate = true, metatype = true)
+@Service
+public class EnhancerWebFragment implements WebFragment {
+
+    private static final String NAME = "enhancer";
+
+    private static final String STATIC_RESOURCE_PATH = "org/apache/stanbol/enhancer/jersey/static";
+
+    private static final String TEMPLATE_PATH = "org/apache/stanbol/enhancer/jersey/templates";
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Set<Class<?>> getJaxrsResourceClasses() {
+        Set<Class<?>> classes = new HashSet<Class<?>>();
+        // resources
+        classes.add(EnginesRootResource.class);
+        classes.add(StoreRootResource.class);
+        classes.add(SparqlQueryResource.class);
+        return classes;
+    }
+
+    @Override
+    public Set<Object> getJaxrsResourceSingletons() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public String getStaticResourceClassPath() {
+        return STATIC_RESOURCE_PATH;
+    }
+
+    @Override
+    public TemplateLoader getTemplateLoader() {
+        return new ClassTemplateLoader(getClass(), TEMPLATE_PATH);
+    }
+
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/ContentItemResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/ContentItemResource.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/ContentItemResource.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/ContentItemResource.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,477 @@
+package org.apache.stanbol.enhancer.jersey.resource;
+
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+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.Properties.GEO_LAT;
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.GEO_LONG;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.clerezza.rdf.core.Graph;
+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.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.TypedLiteral;
+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.core.sparql.QueryParser;
+import org.apache.clerezza.rdf.core.sparql.ResultSet;
+import org.apache.clerezza.rdf.core.sparql.SolutionMapping;
+import org.apache.clerezza.rdf.core.sparql.query.SelectQuery;
+import org.apache.clerezza.rdf.utils.GraphNode;
+import org.apache.commons.io.IOUtils;
+import org.apache.stanbol.commons.web.resource.NavigationMixin;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jersey.api.view.ImplicitProduces;
+
+@ImplicitProduces(TEXT_HTML + ";qs=2")
+public class ContentItemResource extends NavigationMixin {
+
+    @SuppressWarnings("unused")
+    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 Map<UriRef, String> DEFAULT_THUMBNAILS = new HashMap<UriRef, String>();
+    static {
+        DEFAULT_THUMBNAILS.put(DBPEDIA_PERSON, "/static/images/user_48.png");
+        DEFAULT_THUMBNAILS.put(DBPEDIA_ORGANISATION, "/static/images/organization_48.png");
+        DEFAULT_THUMBNAILS.put(DBPEDIA_PLACE, "/static/images/compass_48.png");
+    }
+
+    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;
+
+    protected final TripleCollection remoteEntityCache;
+
+    protected Collection<EntityExtractionSummary> people;
+
+    protected Collection<EntityExtractionSummary> organizations;
+
+    protected Collection<EntityExtractionSummary> places;
+
+    public ContentItemResource(String localId, ContentItem ci,
+            TripleCollection remoteEntityCache, UriInfo uriInfo,
+            TcManager tcManager, Serializer serializer) throws IOException {
+        this.contentItem = ci;
+        this.localId = localId;
+        this.uriInfo = uriInfo;
+        this.tcManager = tcManager;
+        this.serializer = serializer;
+        this.remoteEntityCache = remoteEntityCache;
+
+        if (localId != null) {
+            URI rawURI = UriBuilder.fromPath("/store/raw/" + localId).build();
+            if (ci.getMimeType().equals("text/plain")) {
+                this.textContent = IOUtils.toString(ci.getStream(), "UTF-8");
+            } else if (ci.getMimeType().startsWith("image/")) {
+                this.imageSrc = rawURI;
+            }
+            this.downloadHref = rawURI;
+            this.metadataHref = UriBuilder.fromPath(
+                    "/store/metadata/" + localId).build();
+        }
+    }
+
+    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;
+    }
+
+    public Collection<EntityExtractionSummary> getPersonOccurrences()
+            throws ParseException {
+        if (people == null) {
+            people = getOccurrences(DBPEDIA_PERSON);
+        }
+        return people;
+    }
+
+    public Collection<EntityExtractionSummary> getOrganizationOccurrences()
+            throws ParseException {
+        if (organizations == null) {
+            organizations = getOccurrences(DBPEDIA_ORGANISATION);
+        }
+        return organizations;
+    }
+
+    public Collection<EntityExtractionSummary> getPlaceOccurrences()
+            throws ParseException {
+        if (places == null) {
+            places = getOccurrences(DBPEDIA_PLACE);
+        }
+        return places;
+    }
+
+    public Collection<EntityExtractionSummary> getOccurrences(UriRef type)
+            throws ParseException {
+        MGraph graph = contentItem.getMetadata();
+        String q = "PREFIX enhancer: <http://fise.iks-project.eu/ontology/> "
+                + "PREFIX dc:   <http://purl.org/dc/terms/> "
+                + "SELECT ?textAnnotation ?text ?entity ?entity_label ?confidence WHERE { "
+                + "  ?textAnnotation a enhancer:TextAnnotation ."
+                + "  ?textAnnotation dc:type %s ."
+                + "  ?textAnnotation enhancer:selected-text ?text ."
+                + " OPTIONAL {"
+                + "   ?entityAnnotation dc:relation ?textAnnotation ."
+                + "   ?entityAnnotation a enhancer:EntityAnnotation . "
+                + "   ?entityAnnotation enhancer:entity-reference ?entity ."
+                + "   ?entityAnnotation enhancer:entity-label ?entity_label ."
+                + "   ?entityAnnotation enhancer:confidence ?confidence . }"
+                + "} ORDER BY ?text ";
+        q = String.format(q, type);
+
+        SelectQuery query = (SelectQuery) QueryParser.getInstance().parse(q);
+        ResultSet result = tcManager.executeSparqlQuery(query, graph);
+        Map<String, EntityExtractionSummary> occurrenceMap = new TreeMap<String, EntityExtractionSummary>();
+        LiteralFactory lf = LiteralFactory.getInstance();
+        while (result.hasNext()) {
+            SolutionMapping mapping = result.next();
+
+            UriRef textAnnotationUri = (UriRef) mapping.get("textAnnotation");
+            if (graph.filter(textAnnotationUri, Properties.DC_RELATION, null).hasNext()) {
+                // this is not the most specific occurrence of this name: skip
+                continue;
+            }
+            // TODO: collect the selected text and contexts of subsumed
+            // annotations
+
+            TypedLiteral textLiteral = (TypedLiteral) mapping.get("text");
+            String text = lf.createObject(String.class, textLiteral);
+
+            EntityExtractionSummary entity = occurrenceMap.get(text);
+            if (entity == null) {
+                entity = new EntityExtractionSummary(text, type);
+                occurrenceMap.put(text, entity);
+            }
+            UriRef entityUri = (UriRef) mapping.get("entity");
+            if (entityUri != null) {
+                String label = ((Literal) mapping.get("entity_label")).getLexicalForm();
+                Double confidence = lf.createObject(Double.class,
+                        (TypedLiteral) mapping.get("confidence"));
+                Graph properties = new GraphNode(entityUri, remoteEntityCache).getNodeContext();
+                entity.addSuggestion(entityUri, label, confidence, properties);
+            }
+        }
+        return occurrenceMap.values();
+    }
+
+    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 EntityExtractionSummary(String name, UriRef type) {
+            this.name = name;
+            this.type = type;
+            mentions.add(name);
+        }
+
+        public void addSuggestion(UriRef uri, String label, Double confidence,
+                TripleCollection properties) {
+            EntitySuggestion suggestion = new EntitySuggestion(uri, type,
+                    label, confidence, properties);
+            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 DEFAULT_THUMBNAILS.get(type);
+            }
+            return suggestions.get(0).getThumbnailSrc();
+        }
+
+        public String getMissingThumbnailSrc() {
+            return DEFAULT_THUMBNAILS.get(type);
+        }
+
+        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;
+
+        public EntitySuggestion(UriRef uri, UriRef type, String label,
+                Double confidence, TripleCollection entityProperties) {
+            this.uri = uri;
+            this.label = label;
+            this.type = type;
+            this.confidence = confidence;
+            this.entityProperties = entityProperties;
+        }
+
+        @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> abstracts = entityProperties.filter(uri,
+                    THUMBNAIL, null);
+            while (abstracts.hasNext()) {
+                Resource object = abstracts.next().getObject();
+                if (object instanceof UriRef) {
+                    return ((UriRef) object).getUnicodeString();
+                }
+            }
+            return DEFAULT_THUMBNAILS.get(type);
+        }
+
+        public String getMissingThumbnailSrc() {
+            return DEFAULT_THUMBNAILS.get(type);
+        }
+
+        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 (abstract_.getLanguage().equals(new Language("en"))) {
+                        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 SimpleMGraph();
+        if (remoteEntityCache != null) {
+            LiteralFactory lf = LiteralFactory.getInstance();
+            for (EntityExtractionSummary p : getPlaceOccurrences()) {
+                EntitySuggestion bestGuess = p.getBestGuess();
+                if (bestGuess == null) {
+                    continue;
+                }
+                UriRef uri = new UriRef(bestGuess.getUri());
+                Iterator<Triple> latitudes = remoteEntityCache.filter(uri,
+                        GEO_LAT, null);
+                if (latitudes.hasNext()) {
+                    g.add(latitudes.next());
+                }
+                Iterator<Triple> longitutes = remoteEntityCache.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);
+        return out.toString("utf-8");
+    }
+
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/EnginesRootResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/EnginesRootResource.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/EnginesRootResource.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/EnginesRootResource.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,213 @@
+package org.apache.stanbol.enhancer.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_JSON_TYPE;
+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.RDF_JSON;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_XML;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.access.TcManager;
+import org.apache.clerezza.rdf.core.serializedform.Serializer;
+import org.apache.stanbol.commons.web.resource.NavigationMixin;
+import org.apache.stanbol.enhancer.jersey.cache.EntityCacheProvider;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementJobManager;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+import org.codehaus.jettison.json.JSONArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jersey.api.view.ImplicitProduces;
+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("/engines")
+@ImplicitProduces(TEXT_HTML + ";qs=2")
+public class EnginesRootResource extends NavigationMixin {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    protected EnhancementJobManager jobManager;
+
+    protected TcManager tcManager;
+
+    protected Serializer serializer;
+
+    protected TripleCollection entityCache;
+
+    // bind the job manager by looking it up from the servlet request context
+    public EnginesRootResource(@Context ServletContext context) {
+        jobManager = (EnhancementJobManager) context.getAttribute(EnhancementJobManager.class.getName());
+        tcManager = (TcManager) context.getAttribute(TcManager.class.getName());
+        serializer = (Serializer) context.getAttribute(Serializer.class.getName());
+        EntityCacheProvider entityCacheProvider = (EntityCacheProvider) context.getAttribute(EntityCacheProvider.class.getName());
+        if (entityCacheProvider != null) {
+            entityCache = entityCacheProvider.getEntityCache();
+        }
+    }
+
+    public List<EnhancementEngine> getActiveEngines() {
+        if (jobManager != null) {
+            return jobManager.getActiveEngines();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    private List<EnhancementEngine> getEngines() {
+        if (jobManager != null) {
+            return jobManager.getActiveEngines();
+        }
+        return new ArrayList<EnhancementEngine>();
+    }
+    
+    @GET
+    @Produces(APPLICATION_JSON)
+    public JSONArray getEnginesAsJsonArray() {
+        JSONArray uriArray = new JSONArray();
+        for (EnhancementEngine engine : getEngines()) {
+            UriBuilder ub = uriInfo.getAbsolutePathBuilder();
+            URI userUri = ub.path(makeEngineId(engine)).build();
+            uriArray.put(userUri.toASCIIString());
+        }
+        return uriArray;
+    }
+
+    @GET
+    @Produces(TEXT_PLAIN)
+    public String getEnginesAsString() {
+        final StringBuilder sb = new StringBuilder();
+        for (EnhancementEngine engine : getEngines()) {
+            sb.append(engine.getClass().getName());
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+    public static String makeEngineId(EnhancementEngine engine) {
+        // TODO: add a property on engines to provided custom local ids and make
+        // this static method a method of the interface EnhancementEngine
+        String engineClassName = engine.getClass().getSimpleName();
+        String suffixToRemove = "EnhancementEngine";
+        if (engineClassName.endsWith(suffixToRemove)) {
+            engineClassName = engineClassName.substring(0,
+                    engineClassName.length() - suffixToRemove.length());
+        }
+        return engineClassName.toLowerCase();
+    }
+
+    /**
+     * Form-based OpenCalais-compatible interface
+     *
+     * TODO: should we parse the OpenCalais paramsXML and find the closest 
+     * Stanbol Enhancer semantics too?
+     *
+     * Note: the format parameter is not part of the official API
+     *
+     * @throws EngineException if the content is somehow corrupted
+     * @throws IOException
+     */
+    @POST
+    @Consumes(APPLICATION_FORM_URLENCODED)
+    public Response enhanceFromForm(@FormParam("content") String content,
+            @FormParam("format") String format,
+            @FormParam("ajax") boolean buildAjaxview,
+            @Context HttpHeaders headers) throws EngineException, IOException {
+        log.info("enhance from From: "+content);        ContentItem ci = new InMemoryContentItem(content.getBytes("UTF-8"),
+                TEXT_PLAIN);
+        return enhanceAndBuildResponse(format, headers, ci, buildAjaxview);
+    }
+
+    /**
+     * Media-Type based handling of the raw POST data.
+     *
+     * @param data binary payload to analyze
+     * @param uri optional URI for the content items (to be used as an
+     *            identifier in the enhancement graph)
+     * @throws EngineException if the content is somehow corrupted
+     * @throws IOException
+     */
+    @POST
+    @Consumes(WILDCARD)
+    public Response enhanceFromData(byte[] data,
+            @QueryParam(value = "uri") String uri, @Context HttpHeaders headers)
+            throws EngineException, 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 basted on the content
+            uri = null;
+        }
+        ContentItem ci = new InMemoryContentItem(uri, data, format);
+        return enhanceAndBuildResponse(null, headers, ci, false);
+    }
+
+    protected Response enhanceAndBuildResponse(String format,
+            HttpHeaders headers, ContentItem ci, boolean buildAjaxview)
+            throws EngineException, IOException {
+        if (jobManager != null) {
+            jobManager.enhanceContent(ci);
+        }
+        MGraph graph = ci.getMetadata();
+
+        if (buildAjaxview) {
+            ContentItemResource contentItemResource = new ContentItemResource(
+                    null, ci, entityCache, uriInfo, tcManager, serializer);
+            contentItemResource.setRdfSerializationFormat(format);
+            Viewable ajaxView = new Viewable("/ajax/contentitem",
+                    contentItemResource);
+            return Response.ok(ajaxView).type(TEXT_HTML).build();
+        }
+        if (format != null) {
+            // force mimetype from form params
+            return Response.ok(graph, format).build();
+        }
+        if (headers.getAcceptableMediaTypes().contains(
+                APPLICATION_JSON_TYPE)) {
+            // force RDF JSON media type (TODO: move this logic
+            return Response.ok(graph, RDF_JSON).build();
+        } else if (headers.getAcceptableMediaTypes().isEmpty()) {
+            // use RDF/XML as default format to keep compat with OpenCalais
+            // clients
+            return Response.ok(graph, RDF_XML).build();
+        }
+
+        // traditional response lookup
+        return Response.ok(graph).build();
+    }
+
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/SparqlQueryResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/SparqlQueryResource.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/SparqlQueryResource.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/SparqlQueryResource.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,89 @@
+package org.apache.stanbol.enhancer.jersey.resource;
+
+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.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import org.apache.clerezza.rdf.core.access.TcManager;
+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.stanbol.commons.web.resource.NavigationMixin;
+import org.apache.stanbol.enhancer.servicesapi.Store;
+import org.apache.stanbol.enhancer.servicesapi.SparqlQueryEngine.SparqlQueryEngineException;
+
+import com.sun.jersey.api.view.Viewable;
+
+
+import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+
+/**
+ * Implementation of a SPARQL endpoint as defined by the W3C:
+ *
+ * http://www.w3.org/TR/rdf-sparql-protocol/
+ *
+ * (not 100% compliant yet, please report bugs/missing features in the issue
+ * tracker).
+ *
+ * If the "query" parameter is not present, then fallback to display and HTML
+ * view with an ajax-ified form to test the SPARQL endpoint from the browser.
+ */
+@Path("/sparql")
+public class SparqlQueryResource extends NavigationMixin {
+
+    protected Store store;
+
+    protected TcManager tcManager;
+
+    public SparqlQueryResource(@Context ServletContext servletContext) {
+        tcManager = (TcManager) servletContext.getAttribute(TcManager.class.getName());
+        store = (Store) servletContext.getAttribute(Store.class.getName());
+    }
+
+    @GET
+    @Consumes(APPLICATION_FORM_URLENCODED)
+    @Produces( { TEXT_HTML + ";qs=2",
+            "application/sparql-results+xml", "application/rdf+xml",
+            APPLICATION_XML })
+    public Object sparql(@QueryParam(value = "query") String sparqlQuery,
+            @Deprecated @QueryParam(value = "q") String q)
+            throws SparqlQueryEngineException, ParseException {
+        if (q != null) {
+            // compat with old REST API that was not respecting the SPARQL RDF
+            // protocol
+            sparqlQuery = q;
+        }
+        if (sparqlQuery == null) {
+            return Response.ok(new Viewable("index", this), TEXT_HTML).build();
+        }
+        Query query = QueryParser.getInstance().parse(sparqlQuery);
+        String mediaType = "application/sparql-results+xml";
+        if (query instanceof DescribeQuery || query instanceof ConstructQuery) {
+            mediaType = "application/rdf+xml";
+        }
+        Object result = tcManager.executeSparqlQuery(query,
+                store.getEnhancementGraph());
+        return Response.ok(result, mediaType).build();
+    }
+
+    @POST
+    @Consumes(APPLICATION_FORM_URLENCODED)
+    @Produces( { "application/sparql-results+xml", "application/rdf+xml",
+            APPLICATION_XML })
+    public Object postSparql(@FormParam("query") String sparqlQuery)
+            throws SparqlQueryEngineException, ParseException {
+        return sparql(sparqlQuery, null);
+    }
+
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/StoreRootResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/StoreRootResource.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/StoreRootResource.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/resource/StoreRootResource.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,412 @@
+package org.apache.stanbol.enhancer.jersey.resource;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
+import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+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 java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+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.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.clerezza.rdf.core.MGraph;
+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.serializedform.Serializer;
+import org.apache.clerezza.rdf.core.sparql.ParseException;
+import org.apache.clerezza.rdf.core.sparql.QueryParser;
+import org.apache.clerezza.rdf.core.sparql.ResultSet;
+import org.apache.clerezza.rdf.core.sparql.SolutionMapping;
+import org.apache.clerezza.rdf.core.sparql.query.SelectQuery;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.stanbol.commons.web.resource.NavigationMixin;
+import org.apache.stanbol.enhancer.jersey.cache.EntityCacheProvider;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementJobManager;
+import org.apache.stanbol.enhancer.servicesapi.Store;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jersey.api.view.Viewable;
+import com.sun.jersey.core.header.FormDataContentDisposition;
+
+/**
+ * Resource to provide a CRU[D] REST API for content items and there related
+ * enhancements.
+ * <p>
+ * Creation is achieved using either POST requests on the root of the store or
+ * as PUT requests on the expected content item URI.
+ * <p>
+ * Retrieval is achieved using simple GET requests on the content item or
+ * enhancement public URIs.
+ * <p>
+ * Update is achieved by issue a PUT request on an existing content item public
+ * URI.
+ * <p>
+ * The Delete operation is not implemented yet.
+ */
+@Path("/store")
+public class StoreRootResource extends NavigationMixin {
+
+    public static final Set<String> RDF_MEDIA_TYPES = new TreeSet<String>(
+            Arrays.asList(N3, N_TRIPLE, RDF_XML, TURTLE, X_TURTLE, RDF_JSON));
+
+    private static final Logger log = LoggerFactory.getLogger(StoreRootResource.class);
+
+    protected TcManager tcManager;
+
+    protected Store store;
+
+    protected EnhancementJobManager jobManager;
+
+    protected Serializer serializer;
+
+    protected UriInfo uriInfo;
+
+    protected int offset = 0;
+
+    protected int pageSize = 5;
+
+    protected List<RecentlyEnhanced> recentlyEnhanced;
+
+    protected TripleCollection entityCache;
+
+    public static class RecentlyEnhanced {
+
+        public final String localId;
+
+        public final String uri;
+
+        public final String mimetype;
+
+        public final long enhancements;
+
+        public RecentlyEnhanced(String uri, String mimetype, long enhancements) {
+            this.localId = uri.substring(uri.lastIndexOf("/") + 1);
+            this.uri = uri;
+            this.mimetype = mimetype;
+            this.enhancements = enhancements;
+        }
+
+        public String getLocalId() {
+            return localId;
+        }
+
+        public String getUri() {
+            return uri;
+        }
+
+        public String getMimetype() {
+            return mimetype;
+        }
+
+        public long getEnhancements() {
+            return enhancements;
+        }
+
+    }
+
+    public StoreRootResource(@Context ServletContext context,
+            @Context UriInfo uriInfo, @QueryParam(value = "offset") int offset,
+            @QueryParam(value = "pageSize") @DefaultValue("5") int pageSize)
+            throws ParseException {
+        tcManager = (TcManager) context.getAttribute(TcManager.class.getName());
+        store = (Store) context.getAttribute(Store.class.getName());
+        jobManager = (EnhancementJobManager) context.getAttribute(EnhancementJobManager.class.getName());
+        serializer = (Serializer) context.getAttribute(Serializer.class.getName());
+        EntityCacheProvider entityCacheProvider = (EntityCacheProvider) context.getAttribute(EntityCacheProvider.class.getName());
+        if (entityCacheProvider != null) {
+            entityCache = entityCacheProvider.getEntityCache();
+        }
+
+        if (store == null || tcManager == null) {
+            log.error("Missing either store={} or tcManager={}", store,
+                    tcManager);
+            throw new WebApplicationException(404);
+        }
+        this.uriInfo = uriInfo;
+        this.offset = offset;
+        this.pageSize = pageSize;
+
+        // compute the latest: todo move some of this as OSGi parameters
+        // somewhere
+        String q = "PREFIX enhancer: <http://fise.iks-project.eu/ontology/> "
+                + "PREFIX dc:   <http://purl.org/dc/terms/> "
+                + "SELECT DISTINCT ?content WHERE { "
+                + "  ?enhancement enhancer:extracted-from ?content ."
+                + "  ?enhancement dc:created ?extraction_time . } "
+                + "ORDER BY DESC(?extraction_time) LIMIT %d OFFSET %d";
+        q = String.format(q, pageSize, offset);
+
+        SelectQuery query = (SelectQuery) QueryParser.getInstance().parse(q);
+        ResultSet result = tcManager.executeSparqlQuery(query,
+                store.getEnhancementGraph());
+
+        recentlyEnhanced = new ArrayList<RecentlyEnhanced>();
+        while (result.hasNext()) {
+            SolutionMapping mapping = result.next();
+            UriRef content = (UriRef) mapping.get("content");
+            ContentItem ci = store.get(content.getUnicodeString());
+            String mimetype = null;
+            long enhancements = 0;
+            if (ci != null) {
+                mimetype = ci.getMimeType();
+                Iterator<Triple> it = ci.getMetadata().filter(null,
+                        Properties.ENHANCER_EXTRACTED_FROM, content);
+                while (it.hasNext()) {
+                    it.next();
+                    enhancements++;
+                }
+                recentlyEnhanced.add(new RecentlyEnhanced(
+                        content.getUnicodeString(), mimetype, enhancements));
+            }
+        }
+    }
+
+    public List<RecentlyEnhanced> getRecentlyEnhancedItems()
+            throws ParseException {
+        return recentlyEnhanced;
+    }
+
+    public URI getMoreRecentItemsUri() {
+        if (offset >= pageSize) {
+            return uriInfo.getBaseUriBuilder().path(getClass()).queryParam(
+                    "offset", offset - pageSize).build();
+        } else {
+            return null;
+        }
+    }
+
+    public URI getOlderItemsUri() {
+        if (recentlyEnhanced.size() < pageSize) {
+            return null;
+        } else {
+            return uriInfo.getBaseUriBuilder().path(getClass()).queryParam(
+                    "offset", offset + pageSize).build();
+        }
+    }
+
+    public UriRef makeContentItemUri(byte[] data) {
+        // TODO: factorize this logic out in a dedicated OSGi service
+        return ContentItemHelper.makeDefaultUri(uriInfo.getBaseUri()
+                + "store/content/", data);
+    }
+
+    public UriRef makeContentItemUri(String localId) {
+        // TODO: factorize this logic out in a dedicated OSGi service
+        return new UriRef(uriInfo.getBaseUri() + "store/content/" + localId);
+    }
+
+    @GET
+    @Produces(TEXT_HTML + ";qs=2")
+    public Viewable getView() {
+        return new Viewable("index", this);
+    }
+
+    /**
+     * Cool URI handler for the uploaded resource.
+     *
+     * @param localId the local id of the resource in the Stanbol Enhancer store
+     * @param headers
+     * @return a redirection to either a browser view, the RDF metadata or the
+     *         raw binary content
+     */
+    @GET
+    @Path("/content/{localId}")
+    public Response getContent(@PathParam(value = "localId") String localId,
+            @Context HttpHeaders headers) {
+
+        UriRef uri = makeContentItemUri(localId);
+        ContentItem ci = store.get(uri.getUnicodeString());
+        if (ci == null) {
+            throw new WebApplicationException(404);
+        }
+
+        // handle smart redirection to browser view
+        for (MediaType mt : headers.getAcceptableMediaTypes()) {
+            if (mt.toString().startsWith(TEXT_HTML)) {
+                return Response.temporaryRedirect(
+                        UriBuilder.fromPath("/store/page/" + localId).build()).build();
+            }
+        }
+
+        // handle smart redirection to RDF metadata view
+        for (MediaType mt : headers.getAcceptableMediaTypes()) {
+            if (RDF_MEDIA_TYPES.contains(mt.toString())) {
+                return Response.temporaryRedirect(
+                        UriBuilder.fromPath("/store/metadata/" + localId).build()).build();
+            }
+        }
+        return Response.temporaryRedirect(
+                UriBuilder.fromPath("/store/raw/" + localId).build()).build();
+    }
+
+    @GET
+    @Path("/raw/{localId}")
+    public Response getRawContent(@PathParam(value = "localId") String localId)
+            throws IOException {
+        UriRef uri = makeContentItemUri(localId);
+        ContentItem ci = store.get(uri.getUnicodeString());
+        if (ci == null) {
+            throw new WebApplicationException(404);
+        }
+        return Response.ok(ci.getStream(), ci.getMimeType()).build();
+    }
+
+    @Path("/page/{localId}")
+    @Produces(TEXT_HTML)
+    public ContentItemResource getContentItemView(
+            @PathParam(value = "localId") String localId) throws IOException {
+        UriRef uri = makeContentItemUri(localId);
+        ContentItem ci = store.get(uri.getUnicodeString());
+        if (ci == null) {
+            throw new WebApplicationException(404);
+        }
+        return new ContentItemResource(localId, ci, entityCache, uriInfo,
+                tcManager, serializer);
+    }
+
+    @GET
+    @Path("/metadata/{localId}")
+    public MGraph getContentItemMetaData(
+            @PathParam(value = "localId") String localId) {
+        UriRef uri = makeContentItemUri(localId);
+        // TODO: rewrite me to perform a CONSTRUCT query on the TcManager
+        // instead
+        ContentItem ci = store.get(uri.getUnicodeString());
+        if (ci == null) {
+            throw new WebApplicationException(404);
+        }
+        return ci.getMetadata();
+    }
+
+    @POST
+    @Consumes(WILDCARD + ";qs=0.5")
+    public Response createContentItem(byte[] data, @Context HttpHeaders headers)
+            throws URISyntaxException, EngineException {
+        String uri = makeContentItemUri(data).getUnicodeString();
+        return createEnhanceAndRedirect(data, headers.getMediaType(), uri);
+    }
+
+    @POST
+    @Consumes( { APPLICATION_FORM_URLENCODED + ";qs=1.0",
+            MULTIPART_FORM_DATA + ";qs=0.9" })
+    public Response createContentItemFromForm(
+            @FormParam("content") String content, @FormParam("url") String url,
+            @FormParam("file") File file,
+            @FormParam("file") FormDataContentDisposition disposition,
+            @Context HttpHeaders headers) throws URISyntaxException,
+            EngineException, MalformedURLException, IOException {
+        byte[] data = null; // TODO: rewrite me in a streamable way to avoid
+        // buffering all the content in memory
+        MediaType mt = null;
+        if (content != null && !content.trim().isEmpty()) {
+            data = content.getBytes();
+            mt = TEXT_PLAIN_TYPE;
+        } else if (url != null && !url.trim().isEmpty()) {
+            try {
+                URLConnection uc = (new URL(url)).openConnection();
+                data = IOUtils.toByteArray(uc.getInputStream());
+                mt = MediaType.valueOf(uc.getContentType());
+            } catch (IOException e) {
+                // TODO: collect remote resource error message for user friendly
+                // feedback
+            }
+        } else if (file != null) {
+            data = FileUtils.readFileToByteArray(file);
+            String lowerFilename = disposition.getFileName().toLowerCase();
+            // TODO: use a mimetype sniffer lib instead
+            if (lowerFilename.matches(".*\\.jpe?g")) {
+                mt = MediaType.valueOf("image/jpeg");
+            } else {
+                mt = APPLICATION_OCTET_STREAM_TYPE;
+            }
+        }
+        if (data != null && mt != null) {
+            String uri = makeContentItemUri(data).getUnicodeString();
+            return createEnhanceAndRedirect(data, mt, uri, true);
+        } else {
+            // TODO: add user-friendly feedback on empty requests from a form
+            return Response.seeOther(new URI("/store")).build();
+        }
+    }
+
+    @PUT
+    @Path("/content/{localId}")
+    @Consumes(WILDCARD)
+    public Response createContentItemWithId(
+            @PathParam(value = "localId") String localId, byte[] data,
+            @Context HttpHeaders headers) throws URISyntaxException,
+            EngineException {
+        String uri = makeContentItemUri(localId).getUnicodeString();
+        return createEnhanceAndRedirect(data, headers.getMediaType(), uri);
+    }
+
+    protected Response createEnhanceAndRedirect(byte[] data,
+            MediaType mediaType, String uri) throws EngineException,
+            URISyntaxException {
+        return createEnhanceAndRedirect(data, mediaType, uri, false);
+    }
+
+    protected Response createEnhanceAndRedirect(byte[] data,
+            MediaType mediaType, String uri, boolean useExplicitRedirect)
+            throws EngineException, URISyntaxException {
+        ContentItem ci = store.create(uri, data, mediaType.toString());
+        jobManager.enhanceContent(ci);
+        store.put(ci);
+        if (useExplicitRedirect) {
+            // use an redirect to point browsers to newly created content
+            return Response.seeOther(new URI(uri)).build();
+        } else {
+            // use the correct way of notifying the RESTful client that the
+            // resource has been successfully created
+            return Response.created(new URI(uri)).build();
+        }
+    }
+
+    // TODO: implement GET handler for dereferencable Enhancement URIs using
+    // SPARQL DESCRIBE query
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/GraphWriter.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/GraphWriter.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/GraphWriter.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/GraphWriter.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,60 @@
+package org.apache.stanbol.enhancer.jersey.writers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+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.TripleCollection;
+import org.apache.clerezza.rdf.core.serializedform.Serializer;
+import org.apache.clerezza.rdf.core.serializedform.SupportedFormat;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.*;
+
+
+@Provider
+@Produces( { TEXT_PLAIN, N3, N_TRIPLE, RDF_XML, TURTLE, X_TURTLE,
+        RDF_JSON, APPLICATION_JSON })
+public class GraphWriter implements MessageBodyWriter<TripleCollection> {
+
+    @Context
+    protected ServletContext servletContext;
+
+    protected Serializer getSerializer() {
+        return (Serializer) servletContext.getAttribute(Serializer.class.getName());
+    }
+
+    public boolean isWriteable(Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return TripleCollection.class.isAssignableFrom(type);
+    }
+
+    public long getSize(TripleCollection t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return -1;
+    }
+
+    public void writeTo(TripleCollection t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream) throws IOException,
+            WebApplicationException {
+
+        if (mediaType == null || mediaType.isWildcardType() || TEXT_PLAIN.equals(mediaType.toString())) {
+           getSerializer().serialize(entityStream, t, APPLICATION_JSON);
+        } else {
+            getSerializer().serialize(entityStream, t, mediaType.toString());
+        }
+    }
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/JsonLdSerializerProvider.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/JsonLdSerializerProvider.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/JsonLdSerializerProvider.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/JsonLdSerializerProvider.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,156 @@
+package org.apache.stanbol.enhancer.jersey.writers;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.clerezza.rdf.core.BNode;
+import org.apache.clerezza.rdf.core.NonLiteral;
+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.serializedform.SerializingProvider;
+import org.apache.clerezza.rdf.core.serializedform.SupportedFormat;
+import org.apache.stanbol.jsonld.JsonLd;
+import org.apache.stanbol.jsonld.JsonLdResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+
+/**
+ * Implements a <a href="http://json-ld.org/">JSON-LD</a> serialization of a Clerezza
+ * {@link TripleCollection}.<br>
+ * <br>
+ * Note: This implementation is based on <a href="http://json-ld.org/spec/">JSON-LD
+ * specification</a> draft from 25 October 2010.
+ *
+ * @author Fabian Christ
+ *
+ * @scr.component immediate="true"
+ * @scr.service
+ *                 interface="org.apache.clerezza.rdf.core.serializedform.SerializingProvider"
+ */
+@SupportedFormat(JsonLdSerializerProvider.SUPPORTED_FORMAT)
+public class JsonLdSerializerProvider implements SerializingProvider {
+
+    public static final String SUPPORTED_FORMAT = APPLICATION_JSON;
+
+    private static final String RDF_NS_TYPE="http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
+
+    private static final Logger logger = LoggerFactory.getLogger(JsonLdSerializerProvider.class);
+
+    // Map from Namespace -> to Prefix
+    private Map<String, String> namespacePrefixMap = new HashMap<String, String>();
+
+    private int indentation = 2;
+
+    @Override
+    public void serialize(OutputStream serializedGraph, TripleCollection tc, String formatIdentifier) {
+        if (!formatIdentifier.equals(SUPPORTED_FORMAT)) {
+            logger.info("serialize() the format '" + formatIdentifier + "' is not supported by this implementation");
+            return;
+        }
+
+        JsonLd jsonLd = new JsonLd();
+        jsonLd.setNamespacePrefixMap(this.namespacePrefixMap);
+
+        Map<NonLiteral, String> subjects = createSubjectsMap(tc);
+        for (NonLiteral subject : subjects.keySet()) {
+            JsonLdResource resource = new JsonLdResource();
+            resource.setSubject(subject.toString());
+
+            Iterator<Triple> triplesFromSubject = tc.filter(subject, null, null);
+            while (triplesFromSubject.hasNext()) {
+                Triple currentTriple = triplesFromSubject.next();
+                if (currentTriple.getPredicate().getUnicodeString().equals(RDF_NS_TYPE)) {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("serialize() adding rdf:type: \"a\":" + currentTriple.getObject());
+                    }
+                    resource.addType(currentTriple.getObject().toString());
+                } else {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("serializer() adding predicate " + currentTriple.getPredicate().toString() + " with object " + currentTriple.getObject().toString());
+                    }
+
+                    String property = currentTriple.getPredicate().getUnicodeString();
+                    String value = currentTriple.getObject().toString();
+                    resource.putProperty(property, value);
+                }
+            }
+
+            jsonLd.put(resource.getSubject(), resource);
+        }
+
+        try {
+            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(serializedGraph));
+            writer.write(jsonLd.toString(this.indentation));
+            writer.flush();
+        } catch (IOException ioe) {
+            logger.error(ioe.getMessage());
+            throw new RuntimeException(ioe.getMessage());
+        }
+    }
+
+    private Map<NonLiteral, String> createSubjectsMap(TripleCollection tc) {
+        Map<NonLiteral, String> subjects = new HashMap<NonLiteral, String>();
+        int bNodeCounter = 0;
+        for (Triple triple : tc) {
+            NonLiteral subject = triple.getSubject();
+            if (!subjects.containsKey(subject)) {
+                if (subject instanceof UriRef) {
+                    subjects.put(subject, subject.toString());
+                } else if (subject instanceof BNode) {
+                    bNodeCounter++;
+                    subjects.put(subject, "_:bnode" + bNodeCounter);
+                }
+            }
+        }
+        return subjects;
+    }
+
+    /**
+     * Get the known namespace to prefix mapping.
+     *
+     * @return A {@link Map} from namespace String to prefix String.
+     */
+    public Map<String, String> getNamespacePrefixMap() {
+        return namespacePrefixMap;
+    }
+
+    /**
+     * Sets the known namespaces for the serializer.
+     *
+     * @param knownNamespaces A {@link Map} from namespace String to prefix String.
+     */
+    public void setNamespacePrefixMap(Map<String, String> knownNamespaces) {
+        this.namespacePrefixMap = knownNamespaces;
+    }
+
+    /**
+     * Returns the current number of space characters which are used
+     * to indent the serialized output.
+     *
+     * @return Number of space characters used for indentation.
+     */
+    public int getIndentation() {
+        return indentation;
+    }
+
+    /**
+     * Sets the number of characters used per indentation level for the serialized output.<br />
+     * Set this value to zero (0) if you don't want indentation. Default value is 2.
+     *
+     * @param indentation Number of space characters used for indentation.
+     */
+    public void setIndentation(int indentation) {
+        this.indentation = indentation;
+    }
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetToXml.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetToXml.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetToXml.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetToXml.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,97 @@
+package org.apache.stanbol.enhancer.jersey.writers;
+
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.clerezza.rdf.core.BNode;
+import org.apache.clerezza.rdf.core.Language;
+import org.apache.clerezza.rdf.core.PlainLiteral;
+import org.apache.clerezza.rdf.core.Resource;
+import org.apache.clerezza.rdf.core.TypedLiteral;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.sparql.ResultSet;
+import org.apache.clerezza.rdf.core.sparql.SolutionMapping;
+import org.apache.clerezza.rdf.core.sparql.query.Variable;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+class ResultSetToXml {
+
+    private final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+    Document toDocument(ResultSet rs) throws ParserConfigurationException {
+        final Document doc = dbf.newDocumentBuilder().newDocument();
+
+        // root element
+        Element root = doc.createElement("sparql");
+        root.setAttribute("xmlns", "http://www.w3.org/2005/sparql-results#");
+        doc.appendChild(root);
+        Element head = doc.createElement("head");
+        root.appendChild(head);
+
+        // result set
+        Element results = doc.createElement("results");
+        SolutionMapping solutionMapping = null;
+        while (rs.hasNext()) {
+            solutionMapping = rs.next();
+            createResultElement(solutionMapping, results, doc);
+        }
+        createVariable(solutionMapping, head, doc);
+        root.appendChild(results);
+
+        return doc;
+    }
+
+    private void createResultElement(SolutionMapping solutionMap, Element results, Document doc) {
+        Set<Variable> keys = solutionMap.keySet();
+        Element result = doc.createElement("result");
+        results.appendChild(result);
+        for (Variable key : keys) {
+            Element bindingElement = doc.createElement("binding");
+            bindingElement.setAttribute("name", key.getName());
+            bindingElement.appendChild(createValueElement(
+                    (Resource) solutionMap.get(key), doc));
+            result.appendChild(bindingElement);
+        }
+    }
+
+    private void createVariable(SolutionMapping solutionMap, Element head, Document doc) {
+        if(solutionMap != null) {
+            Set<Variable> keys = solutionMap.keySet();
+            for (Variable key : keys) {
+                Element varElement = doc.createElement("variable");
+                varElement.setAttribute("name", key.getName());
+                head.appendChild(varElement);
+            }
+        }
+    }
+
+    private Element createValueElement(Resource resource, Document doc) {
+        Element value;
+        if (resource instanceof UriRef) {
+            value = doc.createElement("uri");
+            value.appendChild(doc.createTextNode(((UriRef) resource)
+                    .getUnicodeString()));
+        } else if (resource instanceof TypedLiteral) {
+            value = doc.createElement("literal");
+            value.appendChild(doc.createTextNode(((TypedLiteral) resource)
+                    .getLexicalForm()));
+            value.setAttribute("datatype", (((TypedLiteral) resource)
+                    .getDataType().getUnicodeString()));
+        } else if (resource instanceof PlainLiteral) {
+            value = doc.createElement("literal");
+            value.appendChild(doc.createTextNode(((PlainLiteral) resource)
+                    .getLexicalForm()));
+            Language lang = ((PlainLiteral) resource).getLanguage();
+            if (lang != null) {
+                value.setAttribute("xml:lang", (lang.toString()));
+            }
+        } else {
+            value = doc.createElement("bnode");
+            value.appendChild(doc.createTextNode(((BNode) resource).toString()));
+        }
+        return value;
+    }
+}

Added: incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetWriter.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetWriter.java?rev=1086333&view=auto
==============================================================================
--- incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetWriter.java (added)
+++ incubator/stanbol/branches/http-endpoint-refactoring/enhancer/jersey/src/main/java/org/apache/stanbol/enhancer/jersey/writers/ResultSetWriter.java Mon Mar 28 18:41:24 2011
@@ -0,0 +1,70 @@
+package org.apache.stanbol.enhancer.jersey.writers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+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 javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.clerezza.rdf.core.sparql.ResultSet;
+import org.w3c.dom.Document;
+
+/**
+ * MessageBodyWriter for a ResultSet.
+ * Clerezza does provide such a writer, but it seems to require
+ * quite a lot of Clerezza dependencies that we don't really need.
+ */
+@Provider
+@Produces({"application/sparql-results+xml", "application/xml", "text/xml"})
+public class ResultSetWriter implements MessageBodyWriter<ResultSet> {
+
+    public static final String ENCODING = "UTF-8";
+
+    @Override
+    public long getSize(ResultSet resultSet, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return ResultSet.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public void writeTo(ResultSet resultSet, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+            throws IOException, WebApplicationException {
+
+        try {
+            Document doc = new ResultSetToXml().toDocument(resultSet);
+            DOMSource domSource = new DOMSource(doc);
+            StreamResult streamResult = new StreamResult(entityStream);
+            TransformerFactory tf = TransformerFactory.newInstance();
+            Transformer serializer = tf.newTransformer();
+            serializer.setOutputProperty(OutputKeys.ENCODING,ENCODING);
+            serializer.setOutputProperty(OutputKeys.INDENT,"yes");
+            serializer.transform(domSource, streamResult);
+        } catch(TransformerException te) {
+            throw new IOException("TransformerException in writeTo()", te);
+        } catch(ParserConfigurationException pce) {
+            throw new IOException("Exception in ResultSetToXml.toDocument()", pce);
+        }
+
+        entityStream.flush();
+    }
+}