/*
* 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.ontologymanager.web.resources;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.CONFLICT;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE;
//import static org.apache.stanbol.commons.web.base.CorsHelper.addCORSOrigin;
import static org.apache.stanbol.commons.web.base.format.KRFormat.FUNCTIONAL_OWL;
import static org.apache.stanbol.commons.web.base.format.KRFormat.FUNCTIONAL_OWL_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.MANCHESTER_OWL;
import static org.apache.stanbol.commons.web.base.format.KRFormat.MANCHESTER_OWL_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.N3;
import static org.apache.stanbol.commons.web.base.format.KRFormat.N3_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.N_TRIPLE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.N_TRIPLE_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.OWL_XML;
import static org.apache.stanbol.commons.web.base.format.KRFormat.OWL_XML_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.RDF_JSON;
import static org.apache.stanbol.commons.web.base.format.KRFormat.RDF_JSON_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.RDF_XML;
import static org.apache.stanbol.commons.web.base.format.KRFormat.RDF_XML_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.TURTLE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.TURTLE_TYPE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.X_TURTLE;
import static org.apache.stanbol.commons.web.base.format.KRFormat.X_TURTLE_TYPE;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
//import com.sun.jersey.api.view.ImplicitProduces;
//import com.sun.jersey.multipart.BodyPart;
//import com.sun.jersey.multipart.FormDataBodyPart;
//import com.sun.jersey.multipart.FormDataMultiPart;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.apache.clerezza.commons.rdf.Graph;
import org.apache.clerezza.commons.rdf.ImmutableGraph;
import org.apache.clerezza.commons.rdf.Triple;
import org.apache.clerezza.commons.rdf.impl.utils.TripleImpl;
import org.apache.clerezza.commons.rdf.impl.utils.simple.SimpleGraph;
import org.apache.clerezza.jaxrs.utils.form.MultiPartBody;
import org.apache.clerezza.rdf.core.serializedform.Parser;
import org.apache.clerezza.rdf.core.serializedform.UnsupportedFormatException;
import org.apache.clerezza.rdf.ontologies.OWL;
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.owl.util.OWL2Constants;
import org.apache.stanbol.commons.owl.util.OWLUtils;
import org.apache.stanbol.commons.owl.util.URIUtils;
import org.apache.stanbol.commons.web.viewable.Viewable;
//import org.apache.stanbol.commons.web.base.ContextHelper;
import org.apache.stanbol.ontologymanager.multiplexer.clerezza.collector.GraphMultiplexer;
import org.apache.stanbol.ontologymanager.registry.api.RegistryContentException;
import org.apache.stanbol.ontologymanager.registry.api.RegistryManager;
import org.apache.stanbol.ontologymanager.registry.api.model.Library;
import org.apache.stanbol.ontologymanager.servicesapi.collector.OntologyCollector;
import org.apache.stanbol.ontologymanager.servicesapi.io.OntologyInputSource;
import org.apache.stanbol.ontologymanager.servicesapi.io.Origin;
import org.apache.stanbol.ontologymanager.servicesapi.ontology.Multiplexer;
import org.apache.stanbol.ontologymanager.servicesapi.ontology.OntologyHandleException;
import org.apache.stanbol.ontologymanager.servicesapi.ontology.OntologyLoadingException;
import org.apache.stanbol.ontologymanager.servicesapi.ontology.OntologyProvider;
import org.apache.stanbol.ontologymanager.servicesapi.ontology.OrphanOntologyKeyException;
import org.apache.stanbol.ontologymanager.servicesapi.scope.OntologySpace;
import org.apache.stanbol.ontologymanager.servicesapi.scope.Scope;
import org.apache.stanbol.ontologymanager.servicesapi.scope.ScopeManager;
import org.apache.stanbol.ontologymanager.servicesapi.session.Session;
import org.apache.stanbol.ontologymanager.servicesapi.session.SessionManager;
import org.apache.stanbol.ontologymanager.servicesapi.util.OntologyUtils;
import org.apache.stanbol.ontologymanager.sources.owlapi.OntologyContentInputSource;
import org.semanticweb.owlapi.model.AddImport;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLImportsDeclaration;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyID;
import org.semanticweb.owlapi.model.RemoveImport;
import org.semanticweb.owlapi.model.SetOntologyID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides the basic HTTP methods for storing and managing ontologies regardless of them belonging to a
* specific network, scope or session.
*
* @author anuzzolese, alexdma
*
*/
@Component
@Service(Object.class)
@Property(name = "javax.ws.rs", boolValue = true)
@Path("/ontonet")
// @ImplicitProduces(MediaType.TEXT_HTML + ";qs=2")
public class RootResource extends AbstractOntologyAccessResource {
protected Multiplexer descriptor;
protected Multiplexer getDescriptor() {
if (descriptor == null) {
if (ontologyProvider == null) throw new IllegalStateException(
"Tried to obtain a multiplexer before an ontology provider was ready. This shouldn't happen.");
descriptor = new GraphMultiplexer(ontologyProvider.getMetaGraph(Graph.class));
}
return descriptor;
}
public class OntologyStats extends ResultData {
private Set<OntologyCollector> handles;
private Set<OWLOntologyID> identifiers;
public Map<OWLOntologyID,OntologyProvider.Status> dependencies;
private OWLOntology o;
private UriInfo uriInfo;
private OWLOntologyID submitted;
public OntologyStats(UriInfo uriInfo,
OWLOntologyID key,
OWLOntology o,
Set<OWLOntologyID> identifiers,
Map<OWLOntologyID,OntologyProvider.Status> dependencies,
Set<OntologyCollector> handles) {
// this.servletContext = context;
this.uriInfo = uriInfo;
this.dependencies = dependencies;
this.submitted = key;
this.o = o;
this.identifiers = identifiers;
this.handles = handles;
}
public Set<String> getAliases() {
Set<String> aliases = new HashSet<String>();
for (OWLOntologyID alias : identifiers)
// if (!o.getOntologyID().equals(alias))
aliases.add(OntologyUtils.encode(alias));
return Collections.unmodifiableSet(aliases);
}
public Map<String,String> getDependencies() {
Map<String,String> deps = new HashMap<String,String>();
for (OWLOntologyID dep : dependencies.keySet()) {
deps.put(OntologyUtils.encode(dep), dependencies.get(dep).toString());
}
return Collections.unmodifiableMap(deps);
}
public OWLOntologyID getRepresentedOntologyKey() {
return submitted;
}
public Set<String> getScopeHandles() {
Set<String> handles = new HashSet<String>();
for (OntologyCollector handle : this.handles)
if (handle instanceof OntologySpace) handles.add(handle.getID());
return handles;
}
public Set<String> getSessionHandles() {
Set<String> handles = new HashSet<String>();
for (OntologyCollector handle : this.handles)
if (handle instanceof Session) handles.add(handle.getID());
return handles;
}
public int getTotalAxioms() {
return o.getAxiomCount();
}
public String stringForm(OWLOntologyID ontologyID) {
return OntologyUtils.encode(ontologyID);
}
}
private Logger log = LoggerFactory.getLogger(getClass());
@Reference
protected ScopeManager onManager;
/*
* Placeholder for the OntologyProvider to be fetched from the servlet context.
*/
@Reference
protected OntologyProvider<?> ontologyProvider;
/*
* Placeholder for the OntologyProvider to be fetched from the servlet context.
*/
@Reference
protected RegistryManager registryManager;
@Reference
protected SessionManager sessionManager;
public RootResource() {
super();
}
/*
* TODO before implementing removal, we need OWL dependency checks. Also, this is quite a strong method
* and would be best implemented with RESTful authentication.
*/
// @DELETE
public Response clear(@Context HttpHeaders headers) {
ResponseBuilder rb = Response.ok();
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
@PUT
@Path("/{ontologyId:.+}")
public Response createOntologyEntry(@PathParam("ontologyId") String ontologyId,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
OWLOntologyID key = OntologyUtils.decode(ontologyId);
ResponseBuilder rb;
if (ontologyProvider.listAllRegisteredEntries().contains(key)) {
rb = Response.status(CONFLICT);
} else {
ontologyProvider.createBlankOntologyEntry(key);
rb = Response.created(uriInfo.getRequestUri());
}
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
@DELETE
@Path("/{ontologyId:.+}")
public Response deleteOntology(@PathParam("ontologyId") String ontologyId, @Context HttpHeaders headers) {
OWLOntologyID key = OntologyUtils.decode(ontologyId);
ResponseBuilder rb;
try {
if (!ontologyProvider.hasOntology(key)) {
rb = Response.status(NOT_FOUND);
} else {
try {
// TODO check aliases!
ontologyProvider.removeOntology(key);
rb = Response.ok();
} catch (OntologyHandleException e) {
rb = Response.status(CONFLICT);
}
}
} catch (OrphanOntologyKeyException e) {
log.warn("Orphan ontology key {}. No associated graph found in store.", e.getOntologyKey());
rb = Response.status(NOT_FOUND);
}
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
public Set<String> getAliases(final OWLOntologyID ontologyId) {
// TODO use rdfViewable instead of Viewable to make separation of
// presentation and application logic cleaner
return AccessController.doPrivileged(new PrivilegedAction<Set<String>>() {
@Override
public Set<String> run() {
Set<String> aliases = new HashSet<String>();
for (OWLOntologyID alias : ontologyProvider.listAliases(ontologyId)) {
aliases.add(OntologyUtils.encode(alias));
}
return aliases;
}
});
}
private Graph getGraph(String ontologyId, boolean merged, URI requestUri) {
long before = System.currentTimeMillis();
OWLOntologyID key = OntologyUtils.decode(ontologyId);
log.debug("Will try to retrieve ontology {} from provider.", key);
/*
* Export directly to Graph since the OWLOntologyWriter uses (de-)serializing converters for the
* other formats.
*
* Use oTemp for the "real" graph and o for the graph that will be exported. This is due to the fact
* that in o we want to change import statements, but we do not want these changes to be stored
* permanently.
*/
Graph o = null, oTemp = null;
try {
oTemp = ontologyProvider.getStoredOntology(key, Graph.class, merged);
} catch (Exception ex) {
log.warn("Retrieval of ontology with ID " + key + " failed.", ex);
}
if (oTemp == null) {
log.debug("Ontology {} missing from provider. Trying libraries...", key);
// TODO remove once registry supports OWLOntologyID as public key.
IRI iri = URIUtils.sanitize(IRI.create(ontologyId));
// See if we can touch a library. TODO: replace with event model on the ontology provider.
int minSize = -1;
IRI smallest = null;
for (Library lib : registryManager.getLibraries(iri)) {
int size = lib.getChildren().length;
if (minSize < 1 || size < minSize) {
smallest = lib.getIRI();
minSize = size;
}
}
if (smallest != null) {
log.debug("Selected library for ontology {} is {} .", iri, smallest);
try {
oTemp = registryManager.getLibrary(smallest).getOntology(iri, Graph.class);
} catch (RegistryContentException e) {
log.warn("The content of library " + smallest + " could not be accessed.", e);
}
}
}
// This is needed because we need to change import statements. No need to use a more efficient but
// resource-intensive IndexedGraph, since both o and oTemp will be GC'ed after serialization.
if (oTemp != null) {
o = new SimpleGraph(oTemp);
}
if (o == null) {
log.debug("Ontology {} not found in any ontology provider or library.", ontologyId);
return null;
}
log.debug("Retrieved ontology {} .", ontologyId);
// Rewrite imports
String uri = uriInfo.getRequestUri().toString();
URI base = URI.create(uri.substring(0, uri.lastIndexOf(ontologyId) - 1));
// Rewrite import statements
/*
* TODO manage import rewrites better once the container ID is fully configurable (i.e. instead of
* going upOne() add "session" or "ontology" if needed).
*/
Iterator<Triple> imports = o.filter(null, OWL.imports, null);
Set<Triple> oldImports = new HashSet<Triple>();
while (imports.hasNext()) {
oldImports.add(imports.next());
}
for (Triple t : oldImports) {
// construct new statement
String s = ((org.apache.clerezza.commons.rdf.IRI) t.getObject()).getUnicodeString();
if (s.contains("::")) {
s = s.substring(s.indexOf("::") + 2, s.length());
}
org.apache.clerezza.commons.rdf.IRI target = new org.apache.clerezza.commons.rdf.IRI(base + "/" + s);
o.add(new TripleImpl(t.getSubject(), OWL.imports, target));
// remove old statement
o.remove(t);
}
// Versioning.
OWLOntologyID id = OWLUtils.extractOntologyID(o);
if (id != null && !id.isAnonymous() && id.getVersionIRI() == null) {
org.apache.clerezza.commons.rdf.IRI viri = new org.apache.clerezza.commons.rdf.IRI(requestUri.toString());
log.debug("Setting version IRI for export : {}", viri);
o.add(new TripleImpl(new org.apache.clerezza.commons.rdf.IRI(id.getOntologyIRI().toString()), new org.apache.clerezza.commons.rdf.IRI(
OWL2Constants.OWL_VERSION_IRI), viri));
}
log.debug("Exported as Clerezza ImmutableGraph in {} ms. Handing over to writer.", System.currentTimeMillis()
- before);
return o;
}
public Set<String> getHandles(final OWLOntologyID ontologyId) {
// TODO use rdfViewable instead of Vieable to make separation of
// presentation and application logic cleaner
return AccessController.doPrivileged(new PrivilegedAction<Set<String>>() {
@Override
public Set<String> run() {
Set<String> handles = new HashSet<String>();
if (onManager != null) {
for (Scope scope : onManager.getRegisteredScopes()) {
if (scope.getCoreSpace().hasOntology(ontologyId)
|| scope.getCustomSpace().hasOntology(ontologyId)) {
handles.add(scope.getID());
}
}
}
if (sessionManager != null) {
for (String sesId : sessionManager.getRegisteredSessionIDs()) {
if (sessionManager.getSession(sesId).hasOntology(ontologyId)) {
handles.add(sesId);
}
}
}
return handles;
}
});
}
@GET
@Produces(TEXT_HTML)
public Response getHtmlInfo(@Context HttpHeaders headers) {
ResponseBuilder rb = Response.ok(new Viewable("index", this));
rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML + "; charset=utf-8");
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
public Response getMetadata(@PathParam("ontologyId") String ontologyId,
@Context UriInfo uriInfo,
@Context HttpHeaders headers) {
ResponseBuilder rb;
org.apache.clerezza.commons.rdf.IRI me = new org.apache.clerezza.commons.rdf.IRI(getPublicBaseUri() + "ontonet/" + ontologyId);
Graph mImmutableGraph = new SimpleGraph();
for (String alias : getAliases(OntologyUtils.decode(ontologyId))) {
mImmutableGraph.add(new TripleImpl(new org.apache.clerezza.commons.rdf.IRI(getPublicBaseUri() + "ontonet/" + alias), OWL.sameAs, me));
}
rb = Response.ok(mImmutableGraph);
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
@GET
@Produces({RDF_XML, TURTLE, X_TURTLE, APPLICATION_JSON, RDF_JSON})
public Response getMetaGraph(@Context HttpHeaders headers) {
ResponseBuilder rb = Response.ok(ontologyProvider.getMetaGraph(ImmutableGraph.class));
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
public SortedSet<OWLOntologyID> getOntologies() {
// As this method is invoked from the template it would be too late
// to handle AccessControlExceptionS
// TODO use rdfViewable instead of Vieable to make separation of
// presentation and application logic cleaner
return AccessController.doPrivileged(new PrivilegedAction<SortedSet<OWLOntologyID>>() {
@Override
public SortedSet<OWLOntologyID> run() {
// No orphans included.
SortedSet<OWLOntologyID> filtered = new TreeSet<OWLOntologyID>();
Set<OWLOntologyID> orphans = ontologyProvider.listOrphans();
for (OWLOntologyID id : ontologyProvider.getPublicKeys()) {
if (id != null && !orphans.contains(id)) {
filtered.add(id);
}
}
return filtered;
}
});
}
public Set<OWLOntologyID> getOrphans() {
// As this method is invoked from the template it would be too late
// to handle AccessControlExceptionS
// TODO use rdfViewable instead of Vieable to make separation of
// presentation and application logic cleaner
return AccessController.doPrivileged(new PrivilegedAction<Set<OWLOntologyID>>() {
@Override
public Set<OWLOntologyID> run() {
return ontologyProvider.listOrphans();
}
});
}
private OWLOntology getOWLOntology(String ontologyId, boolean merge, URI requestUri) {
long before = System.currentTimeMillis();
IRI iri = URIUtils.sanitize(IRI.create(ontologyId));
log.debug("Will try to retrieve ontology {} from provider.", iri);
// TODO be selective: if the ontology is small enough, use OWLOntology otherwise export to ImmutableGraph.
OWLOntology o = null;
try {
// XXX Guarantee that there MUST always be an entry for any decoded ontology ID submitted.
OWLOntologyID id = OntologyUtils.decode(ontologyId);
o = ontologyProvider.getStoredOntology(id, OWLOntology.class, merge);
} catch (Exception ex) {
log.warn("Retrieval of ontology with ID " + iri + " failed.", ex);
}
if (o == null) {
log.debug("Ontology {} missing from provider. Trying libraries...", iri);
// See if we can touch a library. TODO: replace with event model on the ontology provider.
int minSize = -1;
IRI smallest = null;
for (Library lib : registryManager.getLibraries(iri)) {
int size = lib.getChildren().length;
if (minSize < 1 || size < minSize) {
smallest = lib.getIRI();
minSize = size;
}
}
if (smallest != null) {
log.debug("Selected library for ontology {} is {} .", iri, smallest);
try {
o = registryManager.getLibrary(smallest).getOntology(iri, OWLOntology.class);
} catch (RegistryContentException e) {
log.warn("The content of library " + smallest + " could not be accessed.", e);
}
}
}
if (o == null) {
log.debug("Ontology {} not found in any ontology provider or library.", iri);
return null;
}
log.debug("Retrieved ontology {} .", iri);
// Rewrite import statements - no ontology collector to do it for us here.
URI base = URI.create(getPublicBaseUri() + "ontonet/");
List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
OWLDataFactory df = o.getOWLOntologyManager().getOWLDataFactory();
// TODO manage import rewrites better once the container ID is fully configurable.
for (OWLImportsDeclaration oldImp : o.getImportsDeclarations()) {
changes.add(new RemoveImport(o, oldImp));
String s = oldImp.getIRI().toString();
if (s.contains("::")) {
s = s.substring(s.indexOf("::") + 2, s.length());
}
IRI target = IRI.create(base + s);
changes.add(new AddImport(o, df.getOWLImportsDeclaration(target)));
}
// Versioning.
OWLOntologyID id = o.getOntologyID();
if (!id.isAnonymous() && id.getVersionIRI() == null) {
IRI viri = IRI.create(requestUri);
log.debug("Setting version IRI for export : {}", viri);
changes.add(new SetOntologyID(o, new OWLOntologyID(id.getOntologyIRI(), viri)));
}
o.getOWLOntologyManager().applyChanges(changes);
log.debug("Exported as Clerezza ImmutableGraph in {} ms. Handing over to writer.", System.currentTimeMillis()
- before);
return o;
}
public int getSize(final OWLOntologyID ontologyId) {
// TODO use rdfViewable instead of Viewable to make separation of
// presentation and application logic cleaner
return AccessController.doPrivileged(new PrivilegedAction<Integer>() {
@Override
public Integer run() {
return getDescriptor().getSize(ontologyId);
}
});
}
@GET
@Path("/{ontologyId:.+}")
@Produces(value = {APPLICATION_JSON, N3, N_TRIPLE, RDF_JSON})
public Response getStandaloneGraph(@PathParam("ontologyId") String ontologyId,
@DefaultValue("false") @QueryParam("meta") boolean meta,
@DefaultValue("false") @QueryParam("merge") boolean merged,
@Context UriInfo uriInfo,
@Context HttpHeaders headers) {
if (meta) {
return getMetadata(ontologyId, uriInfo, headers);
}
ResponseBuilder rb;
if (ontologyId == null || ontologyId.isEmpty()) {
rb = Response.status(BAD_REQUEST);
}
OWLOntologyID key = OntologyUtils.decode(ontologyId);
if (ontologyProvider.listOrphans().contains(key)) {
rb = Response.status(NO_CONTENT);
} else {
Graph o = getGraph(ontologyId, merged, uriInfo.getRequestUri());
rb = o == null ? Response.status(NOT_FOUND) : Response.ok(o);
}
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
/**
* Gets the ontology with the given identifier in its version managed by the session.
*
* @param sessionId
* the session identifier.
* @param ontologyId
* the ontology identifier.
* @param uriInfo
* @param headers
* @return the requested managed ontology, or {@link Status#NOT_FOUND} if either the sessionn does not
* exist, or the if the ontology either does not exist or is not managed.
*/
@GET
@Path("/{ontologyId:.+}")
@Produces(value = {RDF_XML, TURTLE, X_TURTLE, MANCHESTER_OWL, FUNCTIONAL_OWL, OWL_XML, TEXT_PLAIN})
public Response getStandaloneOntology(@PathParam("ontologyId") String ontologyId,
@DefaultValue("false") @QueryParam("merge") boolean merged,
@Context UriInfo uriInfo,
@Context HttpHeaders headers) {
ResponseBuilder rb;
if (ontologyId == null || ontologyId.isEmpty()) {
rb = Response.status(BAD_REQUEST);
}
OWLOntologyID key = OntologyUtils.decode(ontologyId);
if (ontologyProvider.listOrphans().contains(key)) {
rb = Response.status(NO_CONTENT);
} else {
OWLOntology o = getOWLOntology(ontologyId, merged, uriInfo.getRequestUri());
rb = o == null ? Response.status(NOT_FOUND) : Response.ok(o);
}
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
@POST
@Path("/{ontologyId:.+}")
@Consumes({MULTIPART_FORM_DATA})
@Produces({TEXT_HTML, TEXT_PLAIN, RDF_XML, TURTLE, X_TURTLE, N3})
public Response loadOntologyContent(@PathParam("ontologyId") String ontologyId,
MultiPartBody data,
@Context HttpHeaders headers) {
ResponseBuilder rb = performLoadOntology(data, headers,
Origin.create(OntologyUtils.decode(ontologyId)));
// rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML + "; charset=utf-8");
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
protected ResponseBuilder performLoadOntology(MultiPartBody data, HttpHeaders headers, Origin<?>... keys) {
log.debug(" post(MultiPartBody data)");
ResponseBuilder rb = null;
IRI location = null;
byte[] file = null; // If found, it takes precedence over location.
String format = null;
List<OWLOntologyID> aliases = new ArrayList<OWLOntologyID>();
if (data.getFormFileParameterValues("file").length > 0) {
file = data.getFormFileParameterValues("file")[0].getContent();
}
// else {
if (data.getTextParameterValues("format").length > 0) {
String value = data.getTextParameterValues("format")[0];
if (!value.equals("auto")) {
format = value;
}
}
if (data.getTextParameterValues("url").length > 0) {
String value = data.getTextParameterValues("url")[0];
try {
URI.create(value); // To throw 400 if malformed.
location = IRI.create(value);
} catch (Exception ex) {
log.error("Malformed IRI for " + value, ex);
throw new WebApplicationException(ex, BAD_REQUEST);
}
}
if (data.getTextParameterValues("alias").length > 0) {
for (String value : data.getTextParameterValues("alias")) {
if (!"null".equals(value)) {
try {
aliases.add(OntologyUtils.decode(value));
} catch (Exception ex) {
log.error("Malformed public key for " + value, ex);
throw new WebApplicationException(ex, BAD_REQUEST);
}
}
}
}
log.debug("Parameters:");
log.debug("file: {}", file != null && file.length > 0 ? "NOT-NULL" : "null");
log.trace("file data: {}", file);
log.debug("url: {}", location);
log.debug("format: {}", format);
log.debug("alias: {}", aliases);
// Then add the file
OWLOntologyID key = null;
if (file != null && file.length > 0) {
/*
* Because the ontology provider's load method could fail after only one attempt without resetting
* the stream, we might have to do that ourselves.
*/
List<String> formats;
if (format != null && !format.trim().isEmpty()) {
formats = Collections.singletonList(format);
} else // The RESTful API has its own list of preferred formats
{
formats = Arrays.asList(RDF_XML, TURTLE, X_TURTLE, N3, N_TRIPLE, OWL_XML, FUNCTIONAL_OWL,
MANCHESTER_OWL, RDF_JSON);
}
log.debug("Will try {} supported formats", formats.size());
int unsupported = 0, failed = 0;
Iterator<String> itf = formats.iterator();
if (!itf.hasNext()) {
throw new OntologyLoadingException("No suitable format found or defined.");
}
do {
String f = itf.next();
try {
// Re-instantiate the stream on every attempt
InputStream content = new BufferedInputStream(new ByteArrayInputStream(file));
// ClerezzaOWLUtils.guessOntologyID(new FileInputStream(file), Parser.getInstance(), f);
OWLOntologyID guessed = OWLUtils.guessOntologyID(content, Parser.getInstance(), f);
if (guessed != null && !guessed.isAnonymous() && ontologyProvider.hasOntology(guessed)) {
rb = Response.status(Status.CONFLICT);
this.submitted = guessed;
if (headers.getAcceptableMediaTypes().contains(MediaType.TEXT_HTML_TYPE)) {
rb.entity(new Viewable("/imports/409.ftl", this));
rb.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML + "; charset=utf-8");
}
break;
} else {
content = new BufferedInputStream(new ByteArrayInputStream(file));
key = ontologyProvider.loadInStore(content, f, true, keys);
}
} catch (UnsupportedFormatException e) {
log.warn(
"POST method failed for media type {}. This should not happen (should fail earlier)",
headers.getMediaType());
// rb = Response.status(UNSUPPORTED_MEDIA_TYPE);
unsupported++;
} catch (IOException e) {
log.debug(">>> FAILURE format {} (I/O error)", f);
failed++;
}catch(ConcurrentModificationException e){
log.error("Exception logged", e);
failed++;
} catch (Exception e) { // SAXParseException and others
log.debug(">>> FAILURE format {} (parse error)", f);
log.debug("Caught exception {} : {}", e.getClass(), e.getLocalizedMessage());
log.trace("Exception logged", e);
failed++;
}
} while ((key == null/* || key.isAnonymous() */) && itf.hasNext());
if ((key == null || key.isAnonymous()) && rb == null) {
if (failed > 0) {
throw new WebApplicationException(BAD_REQUEST);
} else if (unsupported > 0) {
throw new WebApplicationException(UNSUPPORTED_MEDIA_TYPE);
}
}
} else if (location != null) {
try { // Here we try every format supported by the Java API
key = ontologyProvider.loadInStore(location, null, true, keys);
} catch (Exception e) {
log.error("Failed to load ontology from " + location, e);
Throwable cause = e.getCause();
String html = "<h1>400 Bad Request</h1>" + "<p>Failed to load ontology from <a href=\""
+ location + "\" target=\"_blank\">" + location + "</a></p>";
if (cause != null) html += "<p>logged cause was: "
+ cause.getLocalizedMessage().replace("<", "<")
.replace(">", ">") + "</p>";
return Response.status(BAD_REQUEST).type(TEXT_HTML).entity(html);
}
} else if (!aliases.isEmpty()) // No content but there are aliases.
{
for (Origin<?> origin : keys) {
if (origin.getReference() instanceof OWLOntologyID) {
OWLOntologyID primary = ((OWLOntologyID) origin.getReference());
if (ontologyProvider.getStatus(primary) != org.apache.stanbol.ontologymanager.servicesapi.ontology.OntologyProvider.Status.NO_MATCH) {
for (OWLOntologyID alias : aliases) {
try {
if (ontologyProvider.addAlias(primary, alias) && key == null) {
key = alias;
}
} catch (IllegalArgumentException ex) {
log.warn("Cannot add alias");
log.warn(" ... ontology key: {}", primary);
log.warn(" ... alias: {}", alias);
log.warn(" ... reason: ", ex);
continue;
}
}
}
}
}
} else {
log.error("Bad request");
log.error(" file is: {}", file);
throw new WebApplicationException(BAD_REQUEST);
}
if (key != null && !key.isAnonymous()) {
String uri = OntologyUtils.encode(key);
if (uri != null && !uri.isEmpty()) {
rb = Response.ok();
if (headers.getAcceptableMediaTypes().contains(MediaType.TEXT_HTML_TYPE)) {
rb.entity(new Viewable("index", this));
rb.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML + "; charset=utf-8");
}
} else {
rb = Response.ok();
}
} else if (rb == null) {
rb = Response.status(Status.INTERNAL_SERVER_ERROR);
}
return rb;
}
/**
* Helper method to make sure a ResponseBuilder is created on every conditions, so that it is then
* possible to enable CORS on it afterwards.
*
* @param ontologyId
* @return
*/
protected ResponseBuilder performShowOntology(String ontologyId) {
if (ontologyId == null || ontologyId.isEmpty()) {
return Response.status(BAD_REQUEST);
}
OWLOntologyID key = OntologyUtils.decode(ontologyId);
if (ontologyProvider.listOrphans().contains(key)) {
return Response.status(NO_CONTENT);
}
OWLOntology o = getOWLOntology(ontologyId, false, uriInfo.getRequestUri());
if (o == null) {
return Response.status(NOT_FOUND);
}
// Assemble dependency list
Map<OWLOntologyID,OntologyProvider.Status> deps = new HashMap<OWLOntologyID,OntologyProvider.Status>();
for (OWLOntologyID dep : getDescriptor().getDependencies(key)) {
deps.put(dep, ontologyProvider.getStatus(dep));
}
Set<OntologyCollector> handles = new HashSet<OntologyCollector>();
if (onManager != null) {
for (Scope scope : onManager.getRegisteredScopes()) {
if (scope.getCoreSpace().hasOntology(key)) {
handles.add(scope.getCoreSpace());
}
if (scope.getCustomSpace().hasOntology(key)) {
handles.add(scope.getCustomSpace());
}
}
}
if (sessionManager != null) {
for (String sesId : sessionManager.getRegisteredSessionIDs()) {
if (sessionManager.getSession(sesId).hasOntology(key)) {
handles.add(sessionManager.getSession(sesId));
}
}
}
return Response.ok(new Viewable("ontology", new OntologyStats(uriInfo, key, o, ontologyProvider
.listAliases(key), deps, handles)));
}
@POST
@Consumes({MULTIPART_FORM_DATA})
@Produces({TEXT_HTML, TEXT_PLAIN, RDF_XML, TURTLE, X_TURTLE, N3})
public Response postOntology(MultiPartBody data, @Context HttpHeaders headers) {
ResponseBuilder rb = performLoadOntology(data, headers);
// rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML + "; charset=utf-8");
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
@GET
@Path("/{ontologyId:.+}")
@Produces(TEXT_HTML)
public Response showOntology(@PathParam("ontologyId") String ontologyId,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResponseBuilder rb = performShowOntology(ontologyId);
rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML + "; charset=utf-8");
// addCORSOrigin(servletContext, rb, headers);
return rb.build();
}
/**
* POSTs an ontology content as application/x-www-form-urlencoded
*
* @param content
* @param headers
* @return
*/
@POST
@Consumes(value = {RDF_XML, TURTLE, X_TURTLE, N3, N_TRIPLE, OWL_XML, FUNCTIONAL_OWL, MANCHESTER_OWL,
RDF_JSON})
public Response storeOntology(InputStream content, @Context HttpHeaders headers) {
long before = System.currentTimeMillis();
ResponseBuilder rb;
MediaType mt = headers.getMediaType();
if (RDF_XML_TYPE.equals(mt) || TURTLE_TYPE.equals(mt) || X_TURTLE_TYPE.equals(mt)
|| N3_TYPE.equals(mt) || N_TRIPLE_TYPE.equals(mt) || RDF_JSON_TYPE.equals(mt)) {
OWLOntologyID key = null;
try {
key = ontologyProvider.loadInStore(content, headers.getMediaType().toString(), true);
rb = Response.ok();
} catch (UnsupportedFormatException e) {
log.warn(
"POST method failed for media type {}. This should not happen (should fail earlier)",
headers.getMediaType());
rb = Response.status(UNSUPPORTED_MEDIA_TYPE);
} catch (IOException e) {
throw new WebApplicationException(e, BAD_REQUEST);
}
// An exception should have been thrown earlier, but just in case.
if (key == null || key.isAnonymous()) {
rb = Response.status(Status.INTERNAL_SERVER_ERROR);
}
} else if (OWL_XML_TYPE.equals(mt) || FUNCTIONAL_OWL_TYPE.equals(mt)
|| MANCHESTER_OWL_TYPE.equals(mt)) {
try {
OntologyInputSource<OWLOntology> src = new OntologyContentInputSource(content);
ontologyProvider.loadInStore(src.getRootOntology(), true);
rb = Response.ok();
} catch (OWLOntologyCreationException e) {
throw new WebApplicationException(e, INTERNAL_SERVER_ERROR);
}
} else {
rb = Response.status(UNSUPPORTED_MEDIA_TYPE);
}
// addCORSOrigin(servletContext, rb, headers);
Response r = rb.build();
log.debug("POST request for ontology addition completed in {} ms with status {}.",
(System.currentTimeMillis() - before), r.getStatus());
return r;
}
}