blob: 7e1a197838c26752a5b3e35bb95a48a46335684d [file] [log] [blame]
/*
* 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.commons.web.sparql.resource;
import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
import java.util.*;
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.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import org.apache.clerezza.commons.rdf.Graph;
import org.apache.clerezza.commons.rdf.IRI;
import org.apache.clerezza.rdf.core.access.TcManager;
import org.apache.clerezza.rdf.core.sparql.ParseException;
import org.apache.felix.scr.annotations.Activate;
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;
import org.apache.stanbol.commons.web.viewable.Viewable;
import org.apache.stanbol.commons.web.base.resource.BaseStanbolResource;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
/**
* This is the SPARQL endpoint which is used throughout the Stanbol. It uses {@link BundleContext} to retrive
* {@link Graph} s registered to OSGi environment. To be able to execute SPARQL queries on triple
* collections, they should be registered to the OSGi environment with the following parameters:
*
* <p>
* <ul>
* <li>graph.uri <b>(required)</b> : The URI of the graph. This is the same as used with the TcManager</li>
* <li>service.ranking: If this parameter is not specified, "0" will be used as default value.</li>
* <li>graph.name: The name of the graph. Human readable name intended to be used in the UI</li>
* <li>graph.description: human readable description providing additional information about the RDF graph</li>
* </ul>
* </p>
*
* <p>
* If a uri is not specified, the graph having highest service.ranking value will be chosen.
* </p>
*
*/
@Component
@Service(Object.class)
@Property(name = "javax.ws.rs", boolValue = true)
@Path("/sparql")
public class SparqlEndpointResource extends BaseStanbolResource {
@Reference
protected TcManager tcManager;
private static final String GRAPH_URI = "graph.uri";
private BundleContext bundleContext;
@Activate
protected void activate(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
//TODO re-enable
/*@OPTIONS
public Response handleCorsPreflight(@Context HttpHeaders headers) {
ResponseBuilder res = Response.ok();
enableCORS(servletContext, res, headers);
return res.build();
}*/
/**
* HTTP GET service to execute SPARQL queries on {@link Graph}s registered to OSGi environment.
* If a <code>null</code>, it is assumed that the request is coming from the HTML interface of SPARQL
* endpoint. Otherwise the query is executed on the triple collection specified by <code>graphUri</code>.
* But, if no graph uri is passed, then the triple collection having highest service.ranking value is
* chosen.
*
* Type of the result is determined according to type of the query such that if the specified query is
* either a <b>describe query</b> or <b>construct query</b>, results are returned in
* <b>application/rdf+xml</b> format, otherwise in <b>pplication/sparql-results+xml</b> format.
*
* @param graphUri
* the URI of the graph on which the SPARQL query will be executed.
* @param sparqlQuery
* SPARQL query to be executed
* @return
*/
@GET
@Consumes(APPLICATION_FORM_URLENCODED)
@Produces({TEXT_HTML + ";qs=2", "application/sparql-results+xml", "application/rdf+xml"})
public Response sparql(@QueryParam(value = "graphuri") String graphUri,
@QueryParam(value = "query") String sparqlQuery,
@Context HttpHeaders headers) throws InvalidSyntaxException {
if (sparqlQuery == null) {
populateGraphList(getServices(null));
return Response.ok(new Viewable("index", this), TEXT_HTML).build();
}
String mediaType = "application/sparql-results+xml";
Graph tripleCollection = getGraph(graphUri);
ResponseBuilder rb;
if (tripleCollection != null) {
Object result;
try {
result = tcManager.executeSparqlQuery(sparqlQuery, tripleCollection);
if (result instanceof Graph) {
mediaType = "application/rdf+xml";
}
rb = Response.ok(result, mediaType);
} catch (ParseException e) {
rb = Response.status(Status.BAD_REQUEST).entity(e.getMessage());
}
} else {
rb = Response.status(Status.NOT_FOUND).entity(
String.format("There is no registered graph with given uri: %s", graphUri));
}
//addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
/**
* HTTP GET service to execute SPARQL queries on {@link Graph}s registered to OSGi environment.
* For details, see {@link #sparql(String, String, HttpHeaders)}
*/
@POST
@Consumes(APPLICATION_FORM_URLENCODED)
@Produces({"application/sparql-results+xml", "application/rdf+xml"})
public Response postSparql(@FormParam("graphuri") String graphUri,
@FormParam("query") String sparqlQuery,
@Context HttpHeaders headers) throws InvalidSyntaxException {
return sparql(graphUri, sparqlQuery, headers);
}
private Graph getGraph(String graphUri) throws InvalidSyntaxException {
Map<ServiceReference,Graph> services = getServices(graphUri);
if (services != null && services.size() > 0) {
return services.get(services.keySet().iterator().next());
}
return null;
}
private void populateGraphList(Map<ServiceReference,Graph> services) {
if (services != null) {
for (ServiceReference service : services.keySet()) {
Object graphUri = service.getProperty(GRAPH_URI);
if (service.getProperty(GRAPH_URI) instanceof IRI) {
graphUri = ((IRI) graphUri).getUnicodeString();
}
Object graphName = service.getProperty("graph.name");
Object graphDescription = service.getProperty("graph.description");
if (graphUri instanceof String && graphName instanceof String
&& graphDescription instanceof String) {
tripleCollections.add(new GraphInfo((String) graphUri, (String) graphName,
(String) graphDescription));
}
}
}
}
private Map<ServiceReference,Graph> getServices(String graphUri) throws InvalidSyntaxException {
Map<ServiceReference,Graph> registeredGraphs = new LinkedHashMap<ServiceReference,Graph>();
ServiceReference[] refs = bundleContext.getServiceReferences(Graph.class.getName(),
getFilter(graphUri));
if (refs != null) {
if (refs.length > 1) {
Arrays.sort(refs);
}
for (ServiceReference ref : refs) {
registeredGraphs.put(ref, (Graph) bundleContext.getService(ref));
}
}
return registeredGraphs;
}
private String getFilter(String graphUri) {
String constraint = "(%s=%s)";
StringBuilder filterString;
if (graphUri != null) {
filterString = new StringBuilder("(&");
filterString.append(String.format(constraint, GRAPH_URI, graphUri));
} else {
filterString = new StringBuilder();
}
filterString
.append(String.format(constraint, Constants.OBJECTCLASS, Graph.class.getName()));
if (graphUri != null) {
filterString.append(')');
}
return filterString.toString();
}
/*
* HTML View
*/
private List<GraphInfo> tripleCollections = new ArrayList<SparqlEndpointResource.GraphInfo>();
public List<GraphInfo> getGraphList() {
return this.tripleCollections;
}
public class GraphInfo {
private String graphUri;
private String graphName;
private String graphDescription;
public GraphInfo(String graphUri, String graphName, String graphDescription) {
this.graphUri = graphUri;
this.graphName = graphName != null ? graphName : "";
this.graphDescription = graphDescription != null ? graphDescription : "";
}
public String getGraphUri() {
return graphUri;
}
public String getGraphName() {
return graphName;
}
public String getGraphDescription() {
return graphDescription;
}
}
}