You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by rw...@apache.org on 2014/02/28 10:55:03 UTC

svn commit: r1572899 [2/3] - in /stanbol/trunk: ./ commons/ commons/ldpath/clerezza/ commons/ldpath/clerezza/src/main/java/org/apache/stanbol/commons/ldpath/clerezza/ commons/ldpath/clerezza/src/test/java/org/apache/stanbol/commons/ldpath/clerezza/ com...

Added: stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/EntityhubRootResource.java
URL: http://svn.apache.org/viewvc/stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/EntityhubRootResource.java?rev=1572899&view=auto
==============================================================================
--- stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/EntityhubRootResource.java (added)
+++ stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/EntityhubRootResource.java Fri Feb 28 09:55:01 2014
@@ -0,0 +1,853 @@
+/*
+ * 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.entityhub.jersey.resource;
+
+import static javax.ws.rs.HttpMethod.DELETE;
+import static javax.ws.rs.HttpMethod.GET;
+import static javax.ws.rs.HttpMethod.OPTIONS;
+import static javax.ws.rs.HttpMethod.POST;
+import static javax.ws.rs.HttpMethod.PUT;
+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_HTML_TYPE;
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+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.utils.MediaTypeUtil.getAcceptableMediaType;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.getLDPathParseExceptionMessage;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.handleLDPathRequest;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.prepareQueryLDPathProgram;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.transformQueryResults;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.marmotta.ldpath.exception.LDPathParseException;
+import org.apache.marmotta.ldpath.model.programs.Program;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
+import org.apache.stanbol.commons.namespaceprefix.NamespaceMappingUtils;
+import org.apache.stanbol.commons.namespaceprefix.NamespacePrefixService;
+import org.apache.stanbol.commons.web.viewable.Viewable;
+import org.apache.stanbol.commons.web.base.resource.BaseStanbolResource;
+import org.apache.stanbol.entityhub.core.query.QueryResultListImpl;
+import org.apache.stanbol.entityhub.jersey.utils.JerseyUtils;
+import org.apache.stanbol.entityhub.ldpath.EntityhubLDPath;
+import org.apache.stanbol.entityhub.ldpath.backend.EntityhubBackend;
+import org.apache.stanbol.entityhub.ldpath.backend.YardBackend;
+import org.apache.stanbol.entityhub.ldpath.query.LDPathSelect;
+import org.apache.stanbol.entityhub.model.clerezza.RdfValueFactory;
+import org.apache.stanbol.entityhub.servicesapi.Entityhub;
+import org.apache.stanbol.entityhub.servicesapi.EntityhubException;
+import org.apache.stanbol.entityhub.servicesapi.model.Entity;
+import org.apache.stanbol.entityhub.servicesapi.model.Representation;
+import org.apache.stanbol.entityhub.servicesapi.model.ValueFactory;
+import org.apache.stanbol.entityhub.servicesapi.model.rdf.RdfResourceEnum;
+import org.apache.stanbol.entityhub.servicesapi.query.FieldQuery;
+import org.apache.stanbol.entityhub.servicesapi.query.QueryResultList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
+@Component
+@Service(Object.class)
+@Property(name="javax.ws.rs", boolValue=true)
+@Path("/entityhub")
+public class EntityhubRootResource extends BaseStanbolResource {
+    
+    private static Logger log = LoggerFactory.getLogger(EntityhubRootResource.class);
+    /**
+     * used to extract the mediaType for the response based on the Accept
+     * header of the request.
+     */
+    private static Collection<String> ENTITY_SUPPORTED_MEDIA_TYPE_INCL_HTML;
+    static {
+        ENTITY_SUPPORTED_MEDIA_TYPE_INCL_HTML = new HashSet<String>(
+                JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES);
+        ENTITY_SUPPORTED_MEDIA_TYPE_INCL_HTML.add(TEXT_HTML);
+    }
+    /**
+     * The default search field for /find queries is the entityhub-maodel:label
+     */
+    private static final String DEFAULT_FIND_FIELD = RdfResourceEnum.label.getUri();
+    /**
+     * The default number of maximal results of searched sites.
+     */
+    private static final int DEFAULT_FIND_RESULT_LIMIT = 5;
+    
+    @Reference
+    private NamespacePrefixService nsPrefixService;
+    
+    @Reference
+    private Entityhub entityhub;
+
+    public EntityhubRootResource() {
+        super();
+    }
+    
+    @OPTIONS
+    public Response handleCorsPreflight(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers);
+        return res.build();
+    }
+    @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();
+    }
+
+    @GET
+    @Path("entity")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE, TEXT_HTML})
+    public Response getSymbol(@QueryParam("id") String symbolId, @Context HttpHeaders headers) throws WebApplicationException {
+        log.info("GET /entity Request");
+        log.info("  > id: " + symbolId);
+        log.info("  > accept: " + headers.getAcceptableMediaTypes());
+        MediaType acceptedMediaType = getAcceptableMediaType(headers, 
+            ENTITY_SUPPORTED_MEDIA_TYPE_INCL_HTML,
+            APPLICATION_JSON_TYPE);
+        if(acceptedMediaType.isCompatible(TEXT_HTML_TYPE) && symbolId == null){
+            //return HTML docu
+            ResponseBuilder rb = Response.ok(new Viewable("entity", this));
+            rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+        if (symbolId == null || symbolId.isEmpty()) {
+            // TODO: how to parse an error message
+            throw new WebApplicationException(BAD_REQUEST);
+        }
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        Entity entity;
+        try {
+            entity = entityhub.getEntity(symbolId);
+        } catch (EntityhubException e) {
+            throw new WebApplicationException(e, INTERNAL_SERVER_ERROR);
+        }
+        if (entity == null) {
+            throw new WebApplicationException(NOT_FOUND);
+        } else {
+            ResponseBuilder rb = Response.ok(entity);
+            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+    }
+        
+    @GET
+    @Path("lookup")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE, TEXT_HTML})
+    public Response lookupSymbol(@QueryParam("id") String reference,
+                                 @QueryParam("create") boolean create,
+                                 @Context HttpHeaders headers) throws WebApplicationException {
+        log.info("GET /lookup Request");
+        log.info("  > id: " + reference);
+        log.info("  > create   : " + create);
+        log.info("  > accept: " + headers.getAcceptableMediaTypes());
+        MediaType acceptedMediaType = getAcceptableMediaType(headers, 
+            ENTITY_SUPPORTED_MEDIA_TYPE_INCL_HTML,
+            APPLICATION_JSON_TYPE);
+        if(acceptedMediaType.isCompatible(TEXT_HTML_TYPE) && reference == null){
+            //return docu
+            ResponseBuilder rb = Response.ok(new Viewable("lookup", this));
+            rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        } else {
+            if (reference == null || reference.isEmpty()) {
+                // TODO: how to parse an error message
+                throw new WebApplicationException(BAD_REQUEST);
+            }
+            Entity entity;
+            try {
+                entity = entityhub.lookupLocalEntity(reference, create);
+            } catch (EntityhubException e) {
+                throw new WebApplicationException(e, INTERNAL_SERVER_ERROR);
+            }
+            if (entity == null) {
+                return Response.status(Status.NOT_FOUND).entity("No symbol found for '" + reference + "'.")
+                        .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            } else {
+                ResponseBuilder rb = Response.ok(entity);
+                rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+                //addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            }
+        }
+    }
+    @OPTIONS
+    @Path("entity/")
+    public Response handleCorsPreflightEntity(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //we need also PUT and DELETE because /entity has full CRUD
+        //enableCORS(servletContext, res, headers,GET,POST,PUT,DELETE,OPTIONS);
+        return res.build();
+    }
+
+    @POST
+    @Path("entity/")
+    @Consumes(MediaType.WILDCARD)
+    public Response createEntity(@QueryParam(value = "id") String id,
+                                 @QueryParam(value = "update") boolean allowUpdate,
+                                 Map<String,Representation> parsed,
+                                 @Context HttpHeaders headers){
+        //Set<Representation> representations = Collections.emptySet();
+        //log.info("Test: "+test);
+        log.info("Headers: "+headers.getRequestHeaders());
+        log.info("Entity: "+id);
+        log.info("Representations : "+parsed);
+        return updateOrCreateEntity(id, parsed, HttpMethod.POST, true,allowUpdate,headers);
+    }
+
+    @PUT
+    @Path("entity/")
+    @Consumes(MediaType.WILDCARD)
+    public Response updateEntity(@QueryParam(value = "id") String id, 
+                                 @QueryParam(value = "create") @DefaultValue("true") boolean allowCreate,
+                                 Map<String,Representation> parsed,
+                                 @Context HttpHeaders headers){
+        //Set<Representation> representations = Collections.emptySet();
+        //log.info("Test: "+test);
+        log.info("Headers: "+headers.getRequestHeaders());
+        log.info("Entity: "+id);
+        log.info("Representations : "+parsed);
+        return updateOrCreateEntity(id, parsed, HttpMethod.PUT, allowCreate, true, headers);
+    }
+    
+    @DELETE
+    @Path("entity")
+    public Response deleteEntity(@QueryParam(value="id") String id,
+                                 @Context HttpHeaders headers){
+        MediaType accepted = getAcceptableMediaType(headers,
+            JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES, 
+            MediaType.APPLICATION_JSON_TYPE);
+        if(id == null || id.isEmpty()){
+            return Response.status(Status.BAD_REQUEST).entity("The Request does" +
+                    "not provide the id of the Entity to delete (parameter 'id').")
+                    .header(HttpHeaders.ACCEPT, accepted).build();
+        }
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        Entity entity;
+        ResponseBuilder rb;
+        try {
+            if(id.equals("*")){
+                log.info("Deleting all Entities form the Entityhub");
+                entityhub.deleteAll();
+                rb = Response.status(Response.Status.OK);
+            } else {
+                entity = entityhub.delete(id);
+                if(entity == null){
+                    rb = Response.status(Status.NOT_FOUND).entity("An Entity with the" +
+                            "parsed id "+id+" is not managed by the Entityhub")
+                            .header(HttpHeaders.ACCEPT, accepted);
+                } else {
+                    rb =  Response.ok(entity);
+                    rb.header(HttpHeaders.CONTENT_TYPE, accepted+"; charset=utf-8")
+                        .header(HttpHeaders.ACCEPT, accepted);
+                }
+            }
+        } catch (EntityhubException e) {
+            rb = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage())
+            .header(HttpHeaders.ACCEPT, accepted);
+        }
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+    /**
+     * Implements the creation/update of Representations within the Entityhub.
+     * @param id the id of the resource to create or update. If not 
+     * <code>null</code> all parsed Representations with other
+     * ids will be ignored.
+     * @param parsed the parsed representation(s)
+     * @param method the {@link HttpMethod} used by the reuqest. Needed to create
+     * the correct response.
+     * @param create allow to create new Entities
+     * @param update allow to update existing Entities
+     * @param headers the HTTP headers of the request
+     * @return the created/updated representation as response
+     */
+    private Response updateOrCreateEntity(String id,Map<String,Representation> parsed, 
+                                          String method,
+                                          boolean create, 
+                                          boolean update,
+                                          HttpHeaders headers){
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        MediaType accepted = getAcceptableMediaType(headers,
+            JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES, 
+            MediaType.APPLICATION_JSON_TYPE);
+        if(entityhub == null){
+            return Response.status(Status.INTERNAL_SERVER_ERROR).
+                entity("The Entityhub is currently unavailable.")
+                .header(HttpHeaders.ACCEPT, accepted).build();
+        }
+        //(1) if an id is parsed we need to ignore all other representations
+        if(id != null && !"*".equals(id)){
+            Representation r = parsed.get(id);
+            if(r == null){
+                return Response.status(Status.BAD_REQUEST)
+                .entity(String.format("Parsed RDF data do not contain any "
+                    + "Information about the parsed id '%s'",id))
+                    .header(HttpHeaders.ACCEPT, accepted).build();
+            } else {
+                parsed = Collections.singletonMap(id, r);
+            }
+        }
+        //First check if all parsed Representation can be created/updated
+        if(!(create && update)){ //if both create and update are enabled skip this
+            long start = System.currentTimeMillis();
+            log.debug("   ... validate parsed Representation state (create: {}| update: {})",
+                create,update);
+            for(Entry<String,Representation> entry : parsed.entrySet()){
+                boolean exists;
+                try {
+                    exists = entityhub.isRepresentation(entry.getKey());
+                } catch (EntityhubException e) {
+                    log.error(String.format("Exception while checking the existance " +
+                        "of an Entity with id  %s in the Entityhub.",
+                        entry.getKey()),e);
+                    return Response.status(Status.INTERNAL_SERVER_ERROR)
+                    .entity(String.format("Unable to process Entity %s because of" +
+                            "an Error while checking the current version of that" +
+                            "Entity within the Entityhub (Message: %s)",
+                            entry.getKey(),e.getMessage()))
+                            .header(HttpHeaders.ACCEPT, accepted).build();
+                }
+                if((exists && !update) || (!exists && !create)){
+                    return Response.status(Status.BAD_REQUEST).entity(String.format(
+                        "Unable to %s an Entity '%s' becuase it %s and request parameter '%s' is set. " +
+                        " To allow both creating and updating of Entities you need to set "+
+                        "'%s=true' in the request",
+                        exists ? "update" : "create", entry.getKey(),
+                        exists ? "exists " : "does not exist",
+                        exists ? "update=false" : "create=false",
+                        exists ? "update" : "create"))
+                        .header(HttpHeaders.ACCEPT, accepted).build();
+                }
+            }
+            log.debug("      > checked {} entities in {}ms",
+                parsed.size(),System.currentTimeMillis()-start);
+        }
+        //store the Representations
+        //If someone parses data for more than a single Entity, but does not
+        //provide an ID for the Entity to update, this will update/create all
+        //the parsed entity. However the response can only return a single
+        //Entity!
+        //This can not be changed easily as long as there are no local URIs 
+        //for remote Entiteis as suggested by
+        // http://incubator.apache.org/stanbol/docs/trunk/entityhub/entityhubandlinkeddata.html
+        Map<String,Entity> updated = new HashMap<String,Entity>();
+        for(Representation representation : parsed.values()){
+            try {
+                Entity entity = entityhub.store(representation);
+                updated.put(entity.getId(), entity);
+            }catch (EntityhubException e) {
+                log.error(String.format("Exception while storing Entity %s" +
+                        "in the Entityhub.",representation),e);
+            }
+        }
+        //create the response for the Entity
+        // for now directly return the added entity. one could also
+        // consider returning a seeOther (303) with the get URI for the
+        // created/updated entity
+        if(updated.isEmpty()){
+            // No (valid) data parsed
+            ResponseBuilder rb = Response.status(Status.NOT_MODIFIED);
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        } else {
+            Entity entity = updated.values().iterator().next();
+            if(method.equals(HttpMethod.POST)){
+                ResponseBuilder rb = Response.created(uriInfo.getAbsolutePathBuilder()
+                    .queryParam("id", "{entityId}")
+                    .build(entity.getId()));
+                //addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            } else {
+                //return Response.noContent().build();
+                //As alternative return the modified entity
+                ResponseBuilder rb =  Response.ok(entity);
+                rb.header(HttpHeaders.CONTENT_TYPE, accepted+"; charset=utf-8");
+                //addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            }
+//            if (updated.size() == 1){
+//                return Response.status(createState? Status.CREATED:Status.OK)
+//                .entity(updated.values().iterator().next())
+//                .header(HttpHeaders.ACCEPT, accepted).build();
+//            } else { //implement serializer for list of Entitis!
+//                return Response.status(createState? Status.CREATED:Status.OK)
+//                .entity(updated.values())
+//                .header(HttpHeaders.ACCEPT, accepted).build();
+//            }
+        }
+        //return Response.seeOther(uriInfo.getAbsolutePath()).build();
+    }
+    @OPTIONS
+    @Path("/find")
+    public Response handleCorsPreflightFind(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers);
+        return res.build();
+    }
+    
+    @GET
+    @Path("/find")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE, TEXT_HTML})
+    public Response findEntityByGet(@QueryParam(value = "name") String name,
+                                    @QueryParam(value = "field") String field,
+                                    @QueryParam(value = "lang") String language,
+                                    @QueryParam(value = "limit") Integer limit,
+                                    @QueryParam(value = "offset") Integer offset,
+                                    // TODO: Jersey supports parsing multiple values in Collections.
+                                    // Use this feature here instead of using this hand crafted
+                                    // solution!
+                                    @QueryParam(value = "select") String select,
+                                    @QueryParam(value = "ldpath") String ldpath,
+                                    @Context HttpHeaders headers) {
+        return findEntity(name, field, language, limit, offset, select, ldpath, headers);
+    }
+    
+    @POST
+    @Path("/find")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE, TEXT_HTML})
+    public Response findEntity(@FormParam(value = "name") String name,
+                               @FormParam(value = "field") String parsedField,
+                               @FormParam(value = "lang") String language,
+                               @FormParam(value = "limit") Integer limit,
+                               @FormParam(value = "offset") Integer offset,
+                               // TODO: Jersey supports parsing multiple values in Collections.
+                               // Use this feature here instead of using this hand crafted
+                               // solution!
+                               @FormParam(value = "select") String select,
+                               @FormParam(value = "ldpath") String ldpath,
+                               @Context HttpHeaders headers) {
+        log.debug("/find Request");
+        final MediaType acceptedMediaType = getAcceptableMediaType(headers,
+            ENTITY_SUPPORTED_MEDIA_TYPE_INCL_HTML,
+            MediaType.APPLICATION_JSON_TYPE);
+        if(name == null || name.isEmpty()){
+            if(acceptedMediaType.isCompatible(TEXT_HTML_TYPE)){
+                //return HTML docu
+                ResponseBuilder rb = Response.ok(new Viewable("find", this));
+                rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+                //addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            } else {
+                return Response.status(Status.BAD_REQUEST)
+                    .entity("The name must not be null nor empty for find requests. Missing parameter name.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        } else {
+            final String property;
+            if (parsedField == null) {
+                property = DEFAULT_FIND_FIELD;
+            } else {
+                parsedField = parsedField.trim();
+                if (parsedField.isEmpty()) {
+                    property = DEFAULT_FIND_FIELD;
+                } else {
+                    property = nsPrefixService.getFullName(parsedField);
+                    if(property == null){
+                        String messsage = String.format("The prefix '%s' of the parsed field '%' is not "
+                            + "mapped to any namespace. Please parse the full URI instead!\n",
+                            NamespaceMappingUtils.getPrefix(parsedField),parsedField);
+                        return Response.status(Status.BAD_REQUEST)
+                                .entity(messsage)
+                                .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+                    }
+                }
+            }
+            FieldQuery query = JerseyUtils.createFieldQueryForFindRequest(name, property, language,
+                limit == null || limit < 1 ? DEFAULT_FIND_RESULT_LIMIT : limit, offset,ldpath);
+            
+            // For the Entityhub we support to select additional fields for results
+            // of find requests. For the Sites and {site} endpoint this is currently
+            // deactivated because of very bad performance with OPTIONAL graph patterns
+            // in SPARQL queries.
+            Collection<String> additionalSelectedFields = new ArrayList<String>();
+            if (select != null && !select.isEmpty()) {
+                for (String selected : select.trim().split(" ")) {
+                    if (selected != null && !selected.isEmpty()) {
+                        additionalSelectedFields.add(selected);
+                    }
+                }
+            }
+            query.addSelectedFields(additionalSelectedFields);
+            return executeQuery(query, headers, acceptedMediaType);
+        }
+    }
+
+    @OPTIONS
+    @Path("/query")
+    public Response handleCorsPreflightQuery(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers);
+        return res.build();
+    }
+    
+    @GET
+    @Path("/query")
+    public Response getQueryDocumentation(@Context HttpHeaders headers){
+        ResponseBuilder rb = Response.ok(new Viewable("query", this));
+        rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+    /**
+     * Allows to parse any kind of {@link FieldQuery} in its JSON Representation.
+     * <p>
+     * TODO: as soon as the entityhub supports multiple query types this need to be refactored. The idea is
+     * that this dynamically detects query types and than redirects them to the referenced site
+     * implementation.
+     * 
+     * @param query The field query in JSON format
+     * @param headers the header information of the request
+     * @return the results of the query
+     */
+    @POST
+    @Path("/query")
+    //@Consumes( {APPLICATION_FORM_URLENCODED + ";qs=1.0", MULTIPART_FORM_DATA + ";qs=0.9"})
+    @Consumes( {MediaType.APPLICATION_JSON})
+    public Response queryEntities(/*@FormParam("query")*/ FieldQuery query,
+                                  @Context HttpHeaders headers) {
+        final MediaType acceptedMediaType = getAcceptableMediaType(headers,
+            JerseyUtils.QUERY_RESULT_SUPPORTED_MEDIA_TYPES,
+            MediaType.APPLICATION_JSON_TYPE);
+        if(query == null && MediaType.TEXT_HTML_TYPE.isCompatible(acceptedMediaType)){
+            return getQueryDocumentation(headers);
+        }
+        return executeQuery(query, headers, acceptedMediaType);
+    }
+    
+    /**
+     * Executes the query parsed by {@link #queryEntities(String, File, HttpHeaders)}
+     * or created based {@link #findEntity(String, String, String, String, HttpHeaders)
+     * @param query The query to execute
+     * @param headers The headers used to determine the media types
+     * @return the response (results of error)
+     */
+    private Response executeQuery(FieldQuery query, HttpHeaders headers, MediaType acceptedMediaType) throws WebApplicationException {
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        if(query instanceof LDPathSelect && ((LDPathSelect)query).getLDPathSelect() != null){
+            //use the LDPath variant to process this query
+            return executeLDPathQuery(entityhub,query, ((LDPathSelect)query).getLDPathSelect(),
+                acceptedMediaType, headers);
+        } else { //use the default query execution
+            QueryResultList<Representation> result;
+            try {
+                result = entityhub.find(query);
+            } catch (EntityhubException e) {
+                String message = String.format("Exception while performing the " +
+                		"FieldQuery on the EntityHub (message: %s)", e.getMessage());
+                log.error(message, e);
+                return Response.status(Status.INTERNAL_SERVER_ERROR)
+                .entity(message)
+                .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+            ResponseBuilder rb = Response.ok(result);
+            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+    }
+    /**
+     * Execute a Query that uses LDPath to process results.
+     * @param query the query
+     * @param mediaType the mediaType for the response
+     * @param headers the http headers of the request
+     * @return the response
+     */
+    private Response executeLDPathQuery(Entityhub entityhub,FieldQuery query, String ldpathProgramString, MediaType mediaType, HttpHeaders headers) {
+        QueryResultList<Representation> result;
+        ValueFactory vf = new RdfValueFactory(new IndexedMGraph());
+        EntityhubBackend backend = new EntityhubBackend(entityhub);
+        EntityhubLDPath ldPath = new EntityhubLDPath(backend,vf);
+        //copy the selected fields, because we might need to delete some during
+        //the preparation phase
+        Set<String> selectedFields = new HashSet<String>(query.getSelectedFields());
+        //first prepare (only execute the query if the parameters are valid)
+        Program<Object> program;
+        try {
+            program = prepareQueryLDPathProgram(ldpathProgramString, selectedFields, backend, ldPath);
+        } catch (LDPathParseException e) {
+            log.warn("Unable to parse LDPath program used as select for Query:");
+            log.warn("FieldQuery: \n {}",query);
+            log.warn("LDPath: \n {}",((LDPathSelect)query).getLDPathSelect());
+            log.warn("Exception:",e);
+            return Response.status(Status.BAD_REQUEST)
+            .entity(("Unable to parse LDPath program (Messages: "+
+                    getLDPathParseExceptionMessage(e)+")!\n"))
+            .header(HttpHeaders.ACCEPT, mediaType).build();
+        } catch (IllegalStateException e) {
+            log.warn("parsed LDPath program is not compatible with parsed Query!",e);
+            return Response.status(Status.BAD_REQUEST)
+            .entity(e.getMessage())
+            .header(HttpHeaders.ACCEPT, mediaType).build();
+        }
+        //2. execute the query
+        Iterator<Representation> resultIt;
+        try { // go directly to the yard and query there for Representations
+            resultIt = entityhub.getYard().findRepresentation(query).iterator();
+        } catch (EntityhubException e) {
+            String message = String.format("Exception while performing the " +
+                "FieldQuery on the EntityHub (message: %s)", e.getMessage());
+            log.error(message, e);
+            return Response.status(Status.INTERNAL_SERVER_ERROR)
+            .entity(message)
+            .header(HttpHeaders.ACCEPT, mediaType).build();
+        }
+        //process the results
+        Collection<Representation> transformedResults = transformQueryResults(resultIt, program,
+            selectedFields, ldPath, backend, vf);
+        result = new QueryResultListImpl<Representation>(query, transformedResults, Representation.class);
+        ResponseBuilder rb = Response.ok(result);
+        rb.header(HttpHeaders.CONTENT_TYPE, mediaType+"; charset=utf-8");
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }    
+    /*--------------------------------------------------------------------------
+     * Methods for EntityMappings
+     *--------------------------------------------------------------------------
+     */
+    @OPTIONS
+    @Path("/mapping")
+    public Response handleCorsPreflightMapping(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers,GET,OPTIONS);
+        return res.build();
+    }
+    
+    @GET
+    @Path("mapping")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE,TEXT_HTML})
+    public Response getMapping(@QueryParam("id") String reference, @Context HttpHeaders headers)
+                                                                            throws WebApplicationException {
+        log.debug("get mapping for request > id : {} > accept: {}",
+            reference, headers.getAcceptableMediaTypes());
+        Set<String> supported = new HashSet<String>(JerseyUtils.REPRESENTATION_SUPPORTED_MEDIA_TYPES);
+        supported.add(TEXT_HTML);
+        MediaType acceptedMediaType = getAcceptableMediaType(
+            headers,supported, APPLICATION_JSON_TYPE);
+        
+        if (reference == null || reference.isEmpty()) {
+            //if HTML -> print the docu of the restfull service
+            if(TEXT_HTML_TYPE.isCompatible(acceptedMediaType)){
+              ResponseBuilder rb = Response.ok(new Viewable("mapping", this));
+              rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+              //addCORSOrigin(servletContext, rb, headers);
+            } else {
+                return Response.status(Status.BAD_REQUEST).entity("The mapping id (URI) is missing.\n").header(
+                    HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        }
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        Entity mapping;
+        try {
+            mapping = entityhub.getMappingById(reference);
+        } catch (EntityhubException e) {
+            log.error("error while getting the mapping for {}", reference, e);
+            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+        }
+        if (mapping == null) {
+            return Response.status(Status.NOT_FOUND).entity("No mapping found for '" + reference + "'.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+        } else {
+            ResponseBuilder rb = Response.ok(mapping);
+            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+    }
+
+    @OPTIONS
+    @Path("/mapping/entity")
+    public Response handleCorsPreflightMappingEntity(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers,GET,OPTIONS);
+        return res.build();
+    }
+
+    @GET
+    @Path("mapping/entity")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE,TEXT_HTML})
+    public Response getEntityMapping(@QueryParam("id") String entity, @Context HttpHeaders headers)
+                                                                            throws WebApplicationException {
+        log.debug("getEntityMapping() POST Request > entity: {} > accept: {}",
+            entity, headers.getAcceptableMediaTypes());
+        
+        Set<String> supported = new HashSet<String>(JerseyUtils.REPRESENTATION_SUPPORTED_MEDIA_TYPES);
+        supported.add(TEXT_HTML);
+        MediaType acceptedMediaType = getAcceptableMediaType(
+            headers,supported, APPLICATION_JSON_TYPE);
+
+        if (entity == null || entity.isEmpty()) {
+            //if HTML -> print the docu of the restfull service
+            if(TEXT_HTML_TYPE.isCompatible(acceptedMediaType)){
+                ResponseBuilder rb = Response.ok(new Viewable("mapping_entity", this));
+                rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+                //addCORSOrigin(servletContext, rb, headers);
+            } else {
+                return Response.status(Status.BAD_REQUEST).entity("No entity given. Missing parameter id.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        }
+        
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        Entity mapping;
+        try {
+            mapping = entityhub.getMappingBySource(entity);
+        } catch (EntityhubException e) {
+            throw new WebApplicationException(e, INTERNAL_SERVER_ERROR);
+        }
+        if (mapping == null) {
+            return Response.status(Status.NOT_FOUND).entity("No mapping found for entity '" + entity + "'.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+        } else {
+            ResponseBuilder rb = Response.ok(mapping);
+            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+    }
+
+    @OPTIONS
+    @Path("/mapping/symbol")
+    public Response handleCorsPreflightMappingSymbol(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers,GET,OPTIONS);
+        return res.build();
+    }
+    
+    @GET
+    @Path("mapping/symbol")
+    @Produces( {APPLICATION_JSON, RDF_XML, N3, TURTLE, X_TURTLE, RDF_JSON, N_TRIPLE,TEXT_HTML})
+    public Response getSymbolMappings(@QueryParam("id") String symbol, @Context HttpHeaders headers)
+                                                                            throws WebApplicationException {
+        log.debug("getSymbolMappings() POST Request > symbol: {} > accept: {}",
+            symbol, headers.getAcceptableMediaTypes());
+        
+        Set<String> supported = new HashSet<String>(JerseyUtils.REPRESENTATION_SUPPORTED_MEDIA_TYPES);
+        supported.add(TEXT_HTML);
+        MediaType acceptedMediaType = getAcceptableMediaType(
+            headers,supported, APPLICATION_JSON_TYPE);
+
+        if (symbol == null || symbol.isEmpty()) {
+            //if HTML -> print the docu of the restfull service
+            if(TEXT_HTML_TYPE.isCompatible(acceptedMediaType)){
+                ResponseBuilder rb = Response.ok(new Viewable("mapping_symbol", this));
+                rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+                //addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            } else {
+                return Response.status(Status.BAD_REQUEST).entity("No symbol given. Missing parameter id.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        }
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        Collection<Entity> mappings;
+        try {
+            mappings = entityhub.getMappingsByTarget(symbol);
+        } catch (EntityhubException e) {
+            throw new WebApplicationException(e, INTERNAL_SERVER_ERROR);
+        }
+        if (mappings == null || mappings.isEmpty()) {
+            return Response.status(Status.NOT_FOUND).entity("No mapping found for symbol '" + symbol + "'.\n")
+            .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+        } else {
+            // TODO: Implement Support for list of Signs, Representations and Strings
+            // For now use a pseudo QueryResultList
+            QueryResultList<Entity> mappingResultList = new QueryResultListImpl<Entity>(null,
+                    mappings, Entity.class);
+            ResponseBuilder rb = Response.ok(mappingResultList);
+            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+    }
+    /*
+     * LDPath support
+     */
+    @OPTIONS
+    @Path("/ldpath")
+    public Response handleCorsPreflightLDPath(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers,OPTIONS,GET,POST);
+        return res.build();
+    }
+    @GET
+    @Path("/ldpath")
+    public Response handleLDPathGet(
+            @QueryParam(value = "context")Set<String> contexts,
+            @QueryParam(value = "ldpath")String ldpath,
+            @Context HttpHeaders headers){
+        return handleLDPathPost(contexts, ldpath, headers);
+    }
+    @POST
+    @Path("/ldpath")
+    public Response handleLDPathPost(
+             @FormParam(value = "context")Set<String> contexts,
+             @FormParam(value = "ldpath")String ldpath,
+             @Context HttpHeaders headers){
+        //Entityhub entityhub = ContextHelper.getServiceFromContext(Entityhub.class, servletContext);
+        return handleLDPathRequest(this,new YardBackend(entityhub.getYard()), 
+            ldpath, contexts, headers);
+    }
+
+}

Added: stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/ReferencedSiteRootResource.java
URL: http://svn.apache.org/viewvc/stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/ReferencedSiteRootResource.java?rev=1572899&view=auto
==============================================================================
--- stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/ReferencedSiteRootResource.java (added)
+++ stanbol/trunk/entityhub/jersey/src/main/java/org/apache/stanbol/entityhub/jersey/resource/ReferencedSiteRootResource.java Fri Feb 28 09:55:01 2014
@@ -0,0 +1,846 @@
+/*
+ * 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.entityhub.jersey.resource;
+
+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 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.utils.MediaTypeUtil.getAcceptableMediaType;
+import static org.apache.stanbol.entityhub.jersey.utils.JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES;
+import static org.apache.stanbol.entityhub.jersey.utils.JerseyUtils.REPRESENTATION_SUPPORTED_MEDIA_TYPES;
+import static org.apache.stanbol.entityhub.jersey.utils.JerseyUtils.createFieldQueryForFindRequest;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.getLDPathParseExceptionMessage;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.handleLDPathRequest;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.prepareQueryLDPathProgram;
+import static org.apache.stanbol.entityhub.jersey.utils.LDPathHelper.transformQueryResults;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.OPTIONS;
+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.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.clerezza.rdf.core.serializedform.SupportedFormat;
+import org.apache.clerezza.rdf.ontologies.RDFS;
+import org.apache.marmotta.ldpath.exception.LDPathParseException;
+import org.apache.marmotta.ldpath.model.programs.Program;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
+import org.apache.stanbol.commons.namespaceprefix.NamespaceMappingUtils;
+import org.apache.stanbol.commons.namespaceprefix.NamespacePrefixService;
+import org.apache.stanbol.commons.web.viewable.Viewable;
+import org.apache.stanbol.commons.web.base.resource.BaseStanbolResource;
+import org.apache.stanbol.entityhub.core.query.QueryResultListImpl;
+import org.apache.stanbol.entityhub.jersey.utils.JerseyUtils;
+import org.apache.stanbol.entityhub.ldpath.EntityhubLDPath;
+import org.apache.stanbol.entityhub.ldpath.backend.SiteBackend;
+import org.apache.stanbol.entityhub.ldpath.query.LDPathSelect;
+import org.apache.stanbol.entityhub.model.clerezza.RdfRepresentation;
+import org.apache.stanbol.entityhub.model.clerezza.RdfValueFactory;
+import org.apache.stanbol.entityhub.servicesapi.defaults.NamespaceEnum;
+import org.apache.stanbol.entityhub.servicesapi.model.Entity;
+import org.apache.stanbol.entityhub.servicesapi.model.Representation;
+import org.apache.stanbol.entityhub.servicesapi.model.ValueFactory;
+import org.apache.stanbol.entityhub.servicesapi.query.FieldQuery;
+import org.apache.stanbol.entityhub.servicesapi.query.QueryResultList;
+import org.apache.stanbol.entityhub.servicesapi.site.License;
+import org.apache.stanbol.entityhub.servicesapi.site.ManagedSite;
+import org.apache.stanbol.entityhub.servicesapi.site.ManagedSiteException;
+import org.apache.stanbol.entityhub.servicesapi.site.ReferencedSiteConfiguration;
+import org.apache.stanbol.entityhub.servicesapi.site.Site;
+import org.apache.stanbol.entityhub.servicesapi.site.SiteConfiguration;
+import org.apache.stanbol.entityhub.servicesapi.site.SiteException;
+import org.apache.stanbol.entityhub.servicesapi.site.SiteManager;
+import org.apache.stanbol.entityhub.servicesapi.util.AdaptingIterator;
+import org.apache.stanbol.entityhub.web.reader.FieldQueryReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
+/**
+ * Resource to provide a REST API for the {@link SiteManager}
+ * <p/>
+ * TODO: add description
+ */
+@Component
+@Service(Object.class)
+@Property(name="javax.ws.rs", boolValue=true)
+@Path("/entityhub/site/{site}")
+public class ReferencedSiteRootResource extends BaseStanbolResource {
+    
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    
+    public static final Set<String> RDF_MEDIA_TYPES = new TreeSet<String>(Arrays.asList(SupportedFormat.N3,
+        SupportedFormat.N_TRIPLE, SupportedFormat.RDF_XML, SupportedFormat.TURTLE, SupportedFormat.X_TURTLE,
+        SupportedFormat.RDF_JSON));
+    /**
+     * The relative path used to publish the license.
+     */
+    public static final String LICENSE_PATH = "license";
+    /**
+     * The name of the resource used for Licenses of no {@link License#getUrl()} 
+     * is present
+     */
+    private static final String LICENSE_NAME = "LICENSE";
+    
+    /**
+     * The Field used for find requests if not specified TODO: This will be replaced by the EntitySearch. With
+     * this search the Site is responsible to decide what properties to use for label based searches.
+     */
+    private static final String DEFAULT_FIND_FIELD = RDFS.label.getUnicodeString();
+    
+    /**
+     * The Field used as default as selected fields for find requests TODO: Make configurable via the
+     * {@link ConfiguredSite} interface! NOTE: This feature is deactivated, because OPTIONAL selects do have
+     * very weak performance when using SPARQL endpoints
+     */
+    // private static final Collection<String> DEFAULT_FIND_SELECTED_FIELDS =
+    // Arrays.asList(RDFS.comment.getUnicodeString());
+    
+    /**
+     * The default number of maximal results.
+     */
+    private static final int DEFAULT_FIND_RESULT_LIMIT = 5;
+    
+    //private Site site;
+
+    @Reference
+    private NamespacePrefixService nsPrefixService;
+    
+    @Reference
+    private SiteManager referencedSiteManager;
+    
+    private Site getSite(String siteId) {
+        Site site = referencedSiteManager.getSite(siteId);
+        if (site == null) {
+            log.error("Site {} not found (no referenced site with that ID is present within the Entityhub",
+                siteId);
+            throw new WebApplicationException(Response.Status.NOT_FOUND);
+        }
+        if(site instanceof ManagedSite){
+            log.debug("   ... init ManagedSite");
+        }
+        return site;
+    }
+
+    
+    @GET
+    @Produces(value=MediaType.TEXT_HTML)
+    public Response getHtmlInfo(@PathParam(value = "site") String siteId, 
+            @Context HttpHeaders headers){
+        ResponseBuilder rb = Response.ok(new Viewable("index", new SiteResultData(getSite(siteId))));
+        rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+    /**
+     * Provides metadata about this referenced site as representation
+     * @param headers the request headers used to get the requested {@link MediaType}
+     * @param uriInfo used to get the URI of the current request
+     * @return the response
+     */
+    @GET
+    @Produces({APPLICATION_JSON,RDF_XML,N3,TURTLE,X_TURTLE,RDF_JSON,N_TRIPLE})
+    public Response getInfo(@PathParam(value = "site") String siteId,
+                            @Context HttpHeaders headers,
+                            @Context UriInfo uriInfo) {
+        MediaType acceptedMediaType = getAcceptableMediaType(headers, REPRESENTATION_SUPPORTED_MEDIA_TYPES,MediaType.APPLICATION_JSON_TYPE);
+        ResponseBuilder rb =  Response.ok(site2Representation(getSite(siteId), uriInfo.getAbsolutePath().toString()));
+        rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+    @GET
+    @Path(value=ReferencedSiteRootResource.LICENSE_PATH+"/{name}")
+    public Response getLicenseInfo(@PathParam(value = "site") String siteId,
+                                   @Context HttpHeaders headers,
+                                   @Context UriInfo uriInfo,
+                                   @PathParam(value = "name") String name) {
+        Site site = getSite(siteId);
+        MediaType acceptedMediaType = getAcceptableMediaType(headers, MediaType.APPLICATION_JSON_TYPE);
+        if(name == null || name.isEmpty()){
+            //return all
+        } else if(name.startsWith(LICENSE_NAME)){
+            try {
+                String numberString = name.substring(LICENSE_NAME.length());
+                if(numberString.isEmpty()){
+                    numberString = "0";
+                }
+                int count = -1; //license0 is the first one
+                if(site.getConfiguration().getLicenses() != null){
+                    for(License license : site.getConfiguration().getLicenses()){
+                        if(license.getUrl() == null){
+                            count++;
+                        }
+                        if(Integer.toString(count).equals(numberString)){
+                            ResponseBuilder rb = Response.ok(
+                                license2Representation(uriInfo.getAbsolutePath().toString(),license));
+                            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+                            //addCORSOrigin(servletContext, rb, headers);
+                            return rb.build();
+                        }
+                    }
+                }
+            }catch (NumberFormatException e) {
+                return Response.status(Status.NOT_FOUND).
+                entity("No License found.\n")
+                .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        }
+        return Response.status(Response.Status.NOT_FOUND).build();
+    }
+    @OPTIONS
+    @Path("/entity")
+    public Response handleCorsPreflightEntity(@PathParam(value = "site") String siteId,
+            @Context HttpHeaders headers){
+        Site site = getSite(siteId);
+        ResponseBuilder res = Response.ok();
+        if(site instanceof ManagedSite){
+            //enableCORS(servletContext, res, headers, OPTIONS,GET,POST,PUT,DELETE);
+        } else {
+            //enableCORS(servletContext, res, headers,OPTIONS,GET);
+        }
+        return res.build();
+    }
+    
+    /**
+     * Cool URI handler for Signs.
+     * 
+     * @param id
+     *            The id of the entity (required)
+     * @param headers
+     *            the request headers used to get the requested {@link MediaType}
+     * @return a redirection to either a browser view, the RDF meta data or the raw binary content
+     */
+    @GET
+    @Path("/entity")
+    public Response getEntityById(@PathParam(value = "site") String siteId,
+            @QueryParam(value = "id") String id, @Context HttpHeaders headers) {
+        Site site = getSite(siteId);
+        log.debug("site/{}/entity Request",site.getId());
+        log.debug("  > id       : " + id);
+        log.debug("  > accept   : " + headers.getAcceptableMediaTypes());
+        log.debug("  > mediaType: " + headers.getMediaType());
+        Collection<String> supported = new HashSet<String>(JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES);
+        supported.add(TEXT_HTML);
+        final MediaType acceptedMediaType = getAcceptableMediaType(headers,
+            supported, MediaType.APPLICATION_JSON_TYPE);
+        if (id == null || id.isEmpty()) {
+            if(MediaType.TEXT_HTML_TYPE.isCompatible(acceptedMediaType)){
+                ResponseBuilder rb = Response.ok(new Viewable("entity", new SiteResultData(site)));
+                rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+               // addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            } else {
+                return Response.status(Status.BAD_REQUEST)
+                    .entity("No or empty ID was parsed. Missing parameter id.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        }
+        log.debug("handle Request for Entity {} of Site {}", id, site.getId());
+        Entity entity;
+        try {
+            entity = site.getEntity(id);
+        } catch (SiteException e) {
+            log.error("ReferencedSiteException while accessing Site " + site.getConfiguration().getName() + 
+                " (id=" + site.getId() + ")", e);
+            throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
+        }
+        if (entity != null) {
+            ResponseBuilder rb =  Response.ok(entity);
+            rb.header(HttpHeaders.CONTENT_TYPE, acceptedMediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        } else {
+            // TODO: How to parse an ErrorMessage?
+            // create an Response with the the Error?
+            log.debug(" ... Entity {} not found on referenced site {}", 
+                id, site.getId());
+            return Response.status(Status.NOT_FOUND).
+            entity("Entity '"+id+"' not found on referenced site '"+site.getId()+"'\n")
+            .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+        }
+    }
+    @POST
+    @Path("entity/")
+    @Consumes(MediaType.WILDCARD)
+    public Response createEntity(@PathParam(value = "site") String siteId,
+                                 @QueryParam(value = "id") String id,
+                                 @QueryParam(value = "update") boolean allowUpdate,
+                                 Map<String,Representation> parsed,
+                                 @Context HttpHeaders headers){
+        //Set<Representation> representations = Collections.emptySet();
+        //log.info("Test: "+test);
+        log.info("Headers: "+headers.getRequestHeaders());
+        log.info("Entity: "+id);
+        log.info("Representations : {} parsed",parsed.size());
+        return updateOrCreateEntity(getSite(siteId), id, parsed, HttpMethod.POST, true,allowUpdate,headers);
+    }
+
+    @PUT
+    @Path("entity/")
+    @Consumes(MediaType.WILDCARD)
+    public Response updateEntity(@PathParam(value = "site") String siteId, 
+                                 @QueryParam(value = "id") String id, 
+                                 @QueryParam(value = "create") @DefaultValue("true") boolean allowCreate,
+                                 Map<String,Representation> parsed,
+                                 @Context HttpHeaders headers){
+        //Set<Representation> representations = Collections.emptySet();
+        //log.info("Test: "+test);
+        log.info("Headers: "+headers.getRequestHeaders());
+        log.info("Entity: "+id);
+        log.info("Representations : {} parsed", parsed.size());
+        return updateOrCreateEntity(getSite(siteId), id, parsed, HttpMethod.PUT, allowCreate, true, headers);
+    }
+    
+    private Response updateOrCreateEntity(Site site, String id,
+                                          Map<String,Representation> parsed,
+                                          String requestMethod,
+                                          boolean create,
+                                          boolean update,
+                                          HttpHeaders headers) {
+        long start = System.currentTimeMillis();
+        MediaType accepted = getAcceptableMediaType(headers,
+            JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES, 
+            MediaType.APPLICATION_JSON_TYPE);
+        ManagedSite managedSite;
+        if(site instanceof ManagedSite){
+            managedSite = (ManagedSite)site;
+        } else {
+            ResponseBuilder builder =  Response.status(Status.FORBIDDEN).entity(
+                String.format("The Site '%s' is not managed and does not support "
+                    +"create/update nor delete operations",site.getId()))
+            .header(HttpHeaders.ACCEPT, accepted);
+            //addCORSOrigin(servletContext, builder, headers);
+            return builder.build();
+        }
+        //(1) if an id is parsed we need to ignore all other representations
+        if(id != null && !"*".equals(id)){
+            Representation r = parsed.get(id);
+            if(r == null){
+                ResponseBuilder builder = Response.status(Status.BAD_REQUEST)
+                .entity(String.format("Parsed RDF data do not contain any "
+                    + "Information about the parsed id '%s'",id))
+                    .header(HttpHeaders.ACCEPT, accepted);
+                //addCORSOrigin(servletContext, builder, headers);
+                return builder.build();
+            } else {
+                parsed = Collections.singletonMap(id, r);
+            }
+        }
+        //First check if all parsed Representation can be created/updated
+        if(!(create && update)){ //if both create and update are enabled skip this
+            log.debug("   ... validate parsed Representation state (create: {}| update: {})",
+                create,update);
+            for(Entry<String,Representation> entry : parsed.entrySet()){
+                boolean exists;
+                try {
+                    exists = managedSite.getEntity(entry.getKey()) != null;
+                } catch (SiteException e) {
+                    log.error(String.format("Exception while checking the existance " +
+                        "of an Entity with id  %s in the Entityhub.",
+                        entry.getKey()),e);
+                    ResponseBuilder builder =  Response.status(Status.INTERNAL_SERVER_ERROR)
+                    .entity(String.format("Unable to process Entity %s because of" +
+                            "an Error while checking the current version of that" +
+                            "Entity within the Entityhub (Message: %s)",
+                            entry.getKey(),e.getMessage()))
+                            .header(HttpHeaders.ACCEPT, accepted);
+                    //addCORSOrigin(servletContext, builder, headers);
+                    return builder.build();
+                }
+                if((exists && !update) || (!exists && !create)){
+                    ResponseBuilder builder = Response.status(Status.BAD_REQUEST).entity(String.format(
+                        "Unable to %s an Entity %s becuase it %s and %s is deactivated. " +
+                        " You might want to set the '%s' parameter to TRUE in your Request",
+                        exists ? "update" : "create", entry.getKey(),
+                        exists ? "does already exists " : "does not",
+                        exists ? "updateing existing" : "creating new",
+                        exists ? "does already" : "does not exists",
+                        exists ? "update" : "create"))
+                        .header(HttpHeaders.ACCEPT, accepted);
+                    //addCORSOrigin(servletContext, builder, headers);
+                    return builder.build();
+
+                }
+            }
+        }
+        long validateCompleted = System.currentTimeMillis();
+        log.info("   ... validate request data {}ms",
+            validateCompleted-start);
+        try {
+            managedSite.store(parsed.values());
+        } catch (ManagedSiteException e) {
+            log.error(String.format("Exception while storing parsed Representations "
+                + "in the ManagedSite %s",managedSite.getId()),e);
+            ResponseBuilder builder = Response.status(Status.INTERNAL_SERVER_ERROR)
+            .entity("Unable to store parsed Entities to ManagedSite "
+                + managedSite.getId() +" because of an error (Message: "
+                + e.getMessage()+")")
+            .header(HttpHeaders.ACCEPT, accepted);
+            //addCORSOrigin(servletContext, builder, headers);
+            return builder.build();
+        }
+        ResponseBuilder builder;
+        if(create && parsed.size() == 1){
+            String createdId =  parsed.keySet().iterator().next();
+            URI created = uriInfo.getRequestUriBuilder().queryParam("id",createdId).build();
+            builder = Response.created(created);
+            builder.header(HttpHeaders.ACCEPT, accepted);
+        } else {
+            builder = Response.noContent();
+        }
+        log.info("   ... create/update {} entities in {}ms",
+            parsed.size(),System.currentTimeMillis()-validateCompleted);
+        //addCORSOrigin(servletContext, builder, headers);
+        return builder.build();
+    }
+
+    @DELETE
+    @Path("entity/")
+    public Response deleteEntity(@PathParam(value = "site") String siteId,
+                                 @QueryParam(value="id") String id,
+                                 @Context HttpHeaders headers){
+        MediaType accepted = getAcceptableMediaType(headers,
+            JerseyUtils.ENTITY_SUPPORTED_MEDIA_TYPES, 
+            MediaType.APPLICATION_JSON_TYPE);
+        ManagedSite managedSite;
+        Site site = getSite(siteId);
+        if(site instanceof ManagedSite){
+            managedSite = (ManagedSite)site;
+        } else {
+            ResponseBuilder builder =  Response.status(Status.FORBIDDEN).entity(
+                String.format("The Site '%s' is not managed and does not support "
+                    +"create/update nor delete operations",site.getId()))
+            .header(HttpHeaders.ACCEPT, accepted);
+            //addCORSOrigin(servletContext, builder, headers);
+            return builder.build();
+        }
+        if(id == null || id.isEmpty()){
+            ResponseBuilder builder =  Response.status(Status.BAD_REQUEST).entity("The Request does" +
+                    "not provide the id of the Entity to delete (parameter 'id').")
+                    .header(HttpHeaders.ACCEPT, accepted);
+            //addCORSOrigin(servletContext, builder, headers);
+            return builder.build();
+        }
+        ResponseBuilder builder;
+        try {
+            if("*".equals(id)){
+                managedSite.deleteAll();
+                builder = Response.noContent();
+            } else if(managedSite.getEntity(id) != null){
+                managedSite.delete(id);
+                builder = Response.noContent();
+            } else {
+                builder = Response.status(Status.NOT_FOUND).entity(
+                    "No Entity with the parsed Id '"+id+"' is present on the ManagedSite '"
+                    + managedSite.getId()+"'!")
+                .header(HttpHeaders.ACCEPT, accepted);
+            }
+        } catch (SiteException e) {
+            String message = "Exception while deleting '"+id+"' from ManagedSite '"
+                    + managedSite.getId()+"'!";
+            log.error(message,e);
+            builder = Response.status(Status.INTERNAL_SERVER_ERROR)
+            .entity(message + ' '+ e.getClass().getSimpleName()+": "+ e.getMessage())
+            .header(HttpHeaders.ACCEPT, accepted);
+        }
+        //addCORSOrigin(servletContext, builder, headers);
+        return builder.build();
+    }    
+    @OPTIONS
+    @Path("/find")
+    public Response handleCorsPreflightFind(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers);
+        return res.build();
+    }
+    
+    @GET
+    @Path("/find")
+    public Response findEntitybyGet(@PathParam(value = "site") String siteId,
+                                    @QueryParam(value = "name") String name,
+                                    @QueryParam(value = "field") String field,
+                                    @QueryParam(value = "lang") String language,
+                                    // @QueryParam(value="select") String select,
+                                    @QueryParam(value = "limit") @DefaultValue(value = "-1") int limit,
+                                    @QueryParam(value = "offset") @DefaultValue(value = "0") int offset,
+                                    @QueryParam(value = "ldpath") String ldpath,
+                                    @Context HttpHeaders headers) {
+        return findEntity(siteId, name, field, language, limit, offset, ldpath, headers);
+    }
+    
+    @POST
+    @Path("/find")
+    public Response findEntity(@PathParam(value = "site") String siteId,
+                               @FormParam(value = "name") String name,
+                               @FormParam(value = "field") String parsedField,
+                               @FormParam(value = "lang") String language,
+                               // @FormParam(value="select") String select,
+                               @FormParam(value = "limit") Integer limit,
+                               @FormParam(value = "offset") Integer offset,
+                               @FormParam(value = "ldpath") String ldpath,
+                               @Context HttpHeaders headers) {
+        Site site = getSite(siteId);
+        log.debug("site/{}/find Request",site.getId());
+        Collection<String> supported = new HashSet<String>(JerseyUtils.QUERY_RESULT_SUPPORTED_MEDIA_TYPES);
+        supported.add(TEXT_HTML);
+        final MediaType acceptedMediaType = getAcceptableMediaType(
+            headers, supported, MediaType.APPLICATION_JSON_TYPE);
+        if(name == null || name.isEmpty()){
+            if(MediaType.TEXT_HTML_TYPE.isCompatible(acceptedMediaType)){
+                ResponseBuilder rb = Response.ok(new Viewable("find", new SiteResultData(site)));
+                rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+                //addCORSOrigin(servletContext, rb, headers);
+                return rb.build();
+            } else {
+                return Response.status(Status.BAD_REQUEST)
+                    .entity("The name must not be null nor empty for find requests. Missing parameter name.\n")
+                    .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+            }
+        }
+        final String property;
+        if (parsedField == null) {
+            property = DEFAULT_FIND_FIELD;
+        } else {
+            parsedField = parsedField.trim();
+            if (parsedField.isEmpty()) {
+                property = DEFAULT_FIND_FIELD;
+            } else {
+                property = nsPrefixService.getFullName(parsedField);
+                if(property == null){
+                    String messsage = String.format("The prefix '%s' of the parsed field '%' is not "
+                        + "mapped to any namespace. Please parse the full URI instead!\n",
+                        NamespaceMappingUtils.getPrefix(parsedField),parsedField);
+                    return Response.status(Status.BAD_REQUEST)
+                            .entity(messsage)
+                            .header(HttpHeaders.ACCEPT, acceptedMediaType).build();
+                }
+            }
+        }
+        return executeQuery(site, createFieldQueryForFindRequest(
+                name, property, language,
+                limit == null || limit < 1 ? DEFAULT_FIND_RESULT_LIMIT : limit, 
+                offset,ldpath),
+            headers);
+    }
+    
+    @OPTIONS
+    @Path("/query")
+    public Response handleCorsPreflightQuery(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers);
+        return res.build();
+    }
+    /**
+     * Allows to parse any kind of {@link FieldQuery} in its JSON Representation. 
+     * Note that the maximum number of results (limit) and the offset of the 
+     * <p>
+     * TODO: as soon as the entityhub supports multiple query types this need to be refactored. The idea is
+     * that this dynamically detects query types and than redirects them to the referenced site
+     * implementation.
+     * @param query The field query as parsed by {@link FieldQueryReader}
+     * @param headers the header information of the request
+     * @return the results of the query
+     */
+    @POST
+    @Path("/query")
+    @Consumes( {APPLICATION_JSON})
+    public Response queryEntities(@PathParam(value = "site") String siteId,
+                                  FieldQuery query,
+                                  @Context HttpHeaders headers) {
+        return executeQuery(getSite(siteId), query,headers);
+    }
+    @GET
+    @Path("/query")
+    @Produces(TEXT_HTML)
+    public Response getQueryDocumentation(@Context HttpHeaders headers, @PathParam(value = "site") String siteId){
+        ResponseBuilder rb = Response.ok(new Viewable("query", new SiteResultData(getSite(siteId))));
+        rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML+"; charset=utf-8");
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+    
+    /**
+     * Executes the query parsed by {@link #queryEntities(String, File, HttpHeaders)} or created based
+     * {@link #findEntity(String, String, String, int, int, HttpHeaders)}
+     * 
+     * @param query
+     *            The query to execute
+     * @param headers the request headers
+     * @return the response (results of error)
+     */
+    private Response executeQuery(Site site, FieldQuery query, HttpHeaders headers) throws WebApplicationException {
+        MediaType mediaType = getAcceptableMediaType(headers, ENTITY_SUPPORTED_MEDIA_TYPES, 
+            APPLICATION_JSON_TYPE);
+        if(query instanceof LDPathSelect && ((LDPathSelect)query).getLDPathSelect() != null){
+            //use the LDPath variant to process this query
+            return executeLDPathQuery(site, query, ((LDPathSelect)query).getLDPathSelect(),
+                mediaType, headers);
+        } else { //use the default query execution
+            QueryResultList<Representation> result;
+            try {
+                result = site.find(query);
+            } catch (SiteException e) {
+                String message = String.format("Unable to Query Site '%s' (message: %s)",
+                    site.getId(),e.getMessage());
+                log.error(message, e);
+                return Response.status(Status.INTERNAL_SERVER_ERROR)
+                .entity(message)
+                .header(HttpHeaders.ACCEPT, mediaType).build();
+            }
+            ResponseBuilder rb = Response.ok(result);
+            rb.header(HttpHeaders.CONTENT_TYPE, mediaType+"; charset=utf-8");
+            //addCORSOrigin(servletContext, rb, headers);
+            return rb.build();
+        }
+    }
+
+    /**
+     * Execute a Query that uses LDPath to process results.
+     * @param query the query
+     * @param mediaType the mediaType for the response
+     * @param headers the http headers of the request
+     * @return the response
+     */
+    private Response executeLDPathQuery(Site site, FieldQuery query, String ldpathProgramString, MediaType mediaType, HttpHeaders headers) {
+        QueryResultList<Representation> result;
+        ValueFactory vf = new RdfValueFactory(new IndexedMGraph());
+        SiteBackend backend = new SiteBackend(site,vf);
+        EntityhubLDPath ldPath = new EntityhubLDPath(backend,vf);
+        //copy the selected fields, because we might need to delete some during
+        //the preparation phase
+        Set<String> selectedFields = new HashSet<String>(query.getSelectedFields());
+        //first prepare (only execute the query if the parameters are valid)
+        Program<Object> program;
+        try {
+            program = prepareQueryLDPathProgram(ldpathProgramString, selectedFields, backend, ldPath);
+        } catch (LDPathParseException e) {
+            log.warn("Unable to parse LDPath program used as select for Query:");
+            log.warn("FieldQuery: \n {}",query);
+            log.warn("LDPath: \n {}",((LDPathSelect)query).getLDPathSelect());
+            log.warn("Exception:",e);
+            return Response.status(Status.BAD_REQUEST)
+            .entity(("Unable to parse LDPath program (Messages: "+
+                    getLDPathParseExceptionMessage(e)+")!\n"))
+            .header(HttpHeaders.ACCEPT, mediaType).build();
+        } catch (IllegalStateException e) {
+            log.warn("parsed LDPath program is not compatible with parsed Query!",e);
+            return Response.status(Status.BAD_REQUEST)
+            .entity(e.getMessage())
+            .header(HttpHeaders.ACCEPT, mediaType).build();
+        }
+        //2. execute the query
+        Iterator<Representation> resultIt;
+        try { // we need to adapt from Entity to Representation
+            resultIt = new AdaptingIterator<Entity,Representation>(site.findEntities(query).iterator(),
+                    new AdaptingIterator.Adapter<Entity,Representation>() {
+                        @Override
+                        public Representation adapt(Entity value, Class<Representation> type) {
+                            return value.getRepresentation();
+                        }},Representation.class);
+        } catch (SiteException e) {
+            String message = String.format("Unable to Query Site '%s' (message: %s)",
+                site.getId(),e.getMessage());
+            log.error(message, e);
+            return Response.status(Status.INTERNAL_SERVER_ERROR)
+            .entity(message)
+            .header(HttpHeaders.ACCEPT, mediaType).build();
+        }
+        //process the results
+        Collection<Representation> transformedResults = transformQueryResults(resultIt, program,
+            selectedFields, ldPath, backend, vf);
+        result = new QueryResultListImpl<Representation>(query, transformedResults, Representation.class);
+        ResponseBuilder rb = Response.ok(result);
+        rb.header(HttpHeaders.CONTENT_TYPE, mediaType+"; charset=utf-8");
+        //addCORSOrigin(servletContext, rb, headers);
+        return rb.build();
+    }
+
+
+    /*
+     * LDPath support
+     */
+    @OPTIONS
+    @Path("/ldpath")
+    public Response handleCorsPreflightLDPath(@Context HttpHeaders headers){
+        ResponseBuilder res = Response.ok();
+        //enableCORS(servletContext, res, headers,OPTIONS,GET,POST);
+        return res.build();
+    }
+    @GET
+    @Path("/ldpath")
+    public Response handleLDPathGet(@PathParam(value = "site") String siteId,
+            @QueryParam(value = "context")Set<String> contexts,
+            @QueryParam(value = "ldpath")String ldpath,
+            @Context HttpHeaders headers){
+        return handleLDPathPost(siteId, contexts, ldpath, headers);
+    }
+    @POST
+    @Path("/ldpath")
+    public Response handleLDPathPost(
+             @PathParam(value = "site") String siteId,
+             @FormParam(value = "context")Set<String> contexts,
+             @FormParam(value = "ldpath")String ldpath,
+             @Context HttpHeaders headers){
+        Site site = getSite(siteId);
+        return handleLDPathRequest(this,new SiteBackend(site), 
+            ldpath, contexts, headers);
+    }
+    
+    /*
+     * Referenced Site Metadata
+     */
+    /**
+     * Transforms a site to a Representation that can be serialised 
+     * @param context
+     * @return
+     */
+    private Representation site2Representation(Site site, String id){
+        RdfValueFactory valueFactory = RdfValueFactory.getInstance();
+        RdfRepresentation rep = valueFactory.createRepresentation(id);
+        String namespace = NamespaceEnum.entityhub.getNamespace();
+        rep.add(namespace+"localMode", site.supportsLocalMode());
+        rep.add(namespace+"supportsSearch", site.supportsSearch());
+        SiteConfiguration config = site.getConfiguration();
+        rep.add("http://www.w3.org/2000/01/rdf-schema#label", config.getName());
+        rep.add("http://www.w3.org/1999/02/22-rdf-syntax-ns#type", valueFactory.createReference(namespace+"ReferencedSite"));
+        if(config.getDescription() != null){
+            rep.add("http://www.w3.org/2000/01/rdf-schema#description", config.getDescription());
+        }
+        if(config.getAttribution() != null){
+            rep.add("http://creativecommons.org/ns#attributionName", config.getAttribution());
+        }
+        if(config.getAttributionUrl() != null){
+            rep.add("http://creativecommons.org/ns#attributionURL", config.getAttributionUrl());
+        }
+        //add the licenses
+        if(config.getLicenses() != null){
+            int count = 0;
+            for(License license : config.getLicenses()){
+                String licenseUrl;
+                if(license.getUrl() != null){
+                    licenseUrl = license.getUrl();
+                } else {
+                    
+                    licenseUrl = id+(!id.endsWith("/")?"/":"")+
+                        LICENSE_PATH+'/'+LICENSE_NAME+(count>0?count:"");
+                    count++;
+                }
+                //if defined add the name to dc:license
+                if(license.getName() != null){
+                    rep.add("http://purl.org/dc/terms/license", licenseUrl);
+                }
+                //link to the license via cc:license
+                rep.add("http://creativecommons.org/ns#license", licenseUrl);
+            }
+        }
+        if(config.getEntityPrefixes() != null){
+            for(String prefix : config.getEntityPrefixes()){
+                rep.add(namespace+"entityPrefix", prefix);
+            }
+        } else { //all entities are allowed/processed
+            rep.add(namespace+"entityPrefix", "*");
+        }
+        if(config instanceof ReferencedSiteConfiguration){
+            ReferencedSiteConfiguration refConfig = (ReferencedSiteConfiguration)config;
+            if(refConfig.getCacheStrategy() != null){
+                rep.add(namespace+"cacheStrategy", valueFactory.createReference(namespace+"cacheStrategy-"+refConfig.getCacheStrategy().name()));
+            }
+            //add the accessUri and queryUri
+            if(refConfig.getAccessUri() != null){
+                rep.add(namespace+"accessUri", valueFactory.createReference(refConfig.getAccessUri()));
+            }
+            if(refConfig.getQueryUri() != null){
+                rep.add(namespace+"queryUri", valueFactory.createReference(refConfig.getQueryUri()));
+            }
+        }
+        return rep;
+    }
+    private Representation license2Representation(String id, License license) {
+        RdfValueFactory valueFactory = RdfValueFactory.getInstance();
+        RdfRepresentation rep = valueFactory.createRepresentation(id);
+        
+        if(license.getName() != null){
+            rep.add("http://purl.org/dc/terms/license", license.getName());
+            rep.add("http://www.w3.org/2000/01/rdf-schema#label", license.getName());
+            rep.add("http://purl.org/dc/terms/title", license.getName());
+        }
+        if(license.getText() != null){
+            rep.add("http://www.w3.org/2000/01/rdf-schema#description", license.getText());
+            
+        }
+        rep.add("http://creativecommons.org/ns#licenseUrl", 
+            license.getUrl() == null ? id:license.getUrl());
+        return rep;
+    }
+    
+    public class SiteResultData extends ResultData {
+
+        private Site site;
+
+        public SiteResultData(Site site) {
+            this.site = site;
+        }
+
+        public boolean isManagedSite() {
+            return site instanceof ManagedSite;
+        }
+
+        public Site getSite() {
+            return site;
+        }
+    }
+}