//The MIT License // // Copyright (c) 2004 Mindswap Research Group, University of Maryland, College Park // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. package org.mindswap.swoop.annotea; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.xerces.impl.dv.util.Base64; import org.mindswap.swoop.SwoopModel; import org.mindswap.swoop.change.ChangeLog; import org.mindswap.swoop.change.OntologyChangeRenderer; import org.mindswap.swoop.utils.owlapi.CorrectedRDFRenderer; import org.semanticweb.owl.impl.model.OWLConnectionImpl; import org.semanticweb.owl.io.Parser; import org.semanticweb.owl.io.Renderer; import org.semanticweb.owl.io.owl_rdf.OWLRDFErrorHandler; import org.semanticweb.owl.io.owl_rdf.OWLRDFParser; import org.semanticweb.owl.model.OWLClass; import org.semanticweb.owl.model.OWLDataProperty; import org.semanticweb.owl.model.OWLException; import org.semanticweb.owl.model.OWLIndividual; import org.semanticweb.owl.model.OWLObjectProperty; import org.semanticweb.owl.model.OWLOntology; import org.xml.sax.SAXException; /** * @author ronwalf * * This is the main interface to an Annotea server. */ public class AnnoteaClient { protected Parser parser; protected URL serverURL; protected String username; protected String password; protected SwoopModel swoopModel; public AnnoteaClient(URL serverURL, SwoopModel swoopModel) { init(serverURL, null, null, swoopModel); } public AnnoteaClient(URL serverURL, String username, String password, SwoopModel swoopModel) { init(serverURL, username, password, swoopModel); } protected void init(URL serverURL, String username, String password, SwoopModel swoopModel) { this.serverURL = serverURL; this.username = username; this.password = password; this.swoopModel = swoopModel; } public void delete(URL location) throws AnnoteaException { try { HttpURLConnection connection = (HttpURLConnection) location.openConnection(); connection.setRequestMethod("DELETE"); connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new AnnoteaException("Could not delete annotation. Response was: " +connection.getResponseCode() + " " + connection.getResponseMessage()); } } catch (Exception e) { throw new AnnoteaException(e); } } protected OWLOntology findXOnt(URI about, String variableName) throws AnnoteaException{ try { String query = "?"+variableName+"=" + URLEncoder.encode(about.toString(), "US-ASCII"); URL queryURL = new URL(serverURL.toString() + query); HttpURLConnection connection = (HttpURLConnection) queryURL .openConnection(); connection.addRequestProperty("Accept", "application/rdf+xml"); connection.connect(); Reader reader = new BufferedReader(new InputStreamReader(connection .getInputStream())); OWLRDFParser parser = new OWLRDFParser(); parser.setOWLRDFErrorHandler(new OWLRDFErrorHandler() { public void owlFullConstruct(int code, String message) throws SAXException { } public void error(String message) throws SAXException { throw new SAXException(message.toString()); } public void warning(String message) throws SAXException { //System.out.println("RDFParser: " + message.toString()); } public void owlFullConstruct(int code, String message, Object obj) throws SAXException { // TODO Auto-generated method stub } }); parser.setConnection(new OWLConnectionImpl()); OWLOntology ontology = parser.parseOntology(reader, new URI( queryURL.toString())); return ontology; } catch (Exception e) { e.printStackTrace(); throw new AnnoteaException(e); } } /** * Parses an OWL Individual to get a Description object * @param annotOnt - OWL ontology that contains individual * @param ind - individual that wraps description * @return */ public Description parseIndividual(OWLOntology annotOnt, OWLIndividual ind) { Description desc = new Description(); try { // get URI of individual and set it to desc - location if (ind.getURI()!=null) { desc.setLocation(ind.getURI().toURL()); } // get annotation type of individual Set types = ind.getTypes(annotOnt); if (types.size()>1) { // has a subclass of "Annotation" type as well (eg. Comment,Explanation..) // hence put type = subclass instead of "Annotation" Iterator iter = types.iterator(); while (iter.hasNext()) { OWLClass type = (OWLClass) iter.next(); if (!type.getURI().equals(((OWLClass) Annotea.INSTANCE.annoteaMap.get("Annotation")).getURI())) desc.setAnnotationType(type); } } else desc.setAnnotationType((OWLClass) types.iterator().next()); // get property values Map dataValues = ind.getDataPropertyValues(annotOnt); if(dataValues.size() > 0) { for (Iterator it = dataValues.keySet().iterator(); it.hasNext();) { OWLDataProperty prop = (OWLDataProperty) it.next(); Set vals = (Set) dataValues.get(prop); // check for author property value if (prop.getURI().equals(((OWLDataProperty) Annotea.INSTANCE.annoteaMap.get("author")).getURI())) { String author = vals.iterator().next().toString(); author = author.substring(0, author.indexOf("^")); desc.setAuthor(author); } // check for created property value else if (prop.getURI().equals(((OWLDataProperty) Annotea.INSTANCE.annoteaMap.get("created")).getURI())) { String created = vals.iterator().next().toString(); created = created.substring(0, created.indexOf("^")); desc.setCreated(created); } // check for entity definition string value else if (prop.getURI().equals(((OWLDataProperty) Annotea.INSTANCE.annoteaMap.get("entityDefinition")).getURI())) { String defn = vals.iterator().next().toString(); defn = defn.substring(0, defn.indexOf("^")); defn = this.recoverHTML(defn); desc.setAnnotatedEntityDefinition(defn); } // *** BACKWARD COMPATIBILITY: check for Body in node itself // This was a problem in the earlier Annotations posted // that http:Body was part of the Annotation Instance itself // hence accounting for it here to recover old annotations else if (prop.getURI().equals(((OWLDataProperty) Annotea.INSTANCE.annoteaMap.get("Body")).getURI())) { // get httpBody location String body = vals.iterator().next().toString(); body = body.substring(body.indexOf("<html>")+6, body.indexOf("</html>")); desc.setBody(body); } // check for hasChangeSet value - Java serialized else if (prop.getURI().equals(((OWLDataProperty) Annotea.INSTANCE.annoteaMap.get("hasChangeSetJAVA")).getURI())) { if (SwoopModel.changeSharingMethod==SwoopModel.JAVA_SER) { // get Ontology Change Set as a Java Serialized Object // get value of user-defined dataproperty 'hasChangeSet' String changeStr = vals.iterator().next().toString(); changeStr = changeStr.substring(0, changeStr.indexOf("^")); // decode it from its base64 representation byte[] changeSetbytes = Base64.decode(changeStr); // convert bytes into java object ByteArrayInputStream bs = new ByteArrayInputStream(changeSetbytes); ObjectInputStream in = new ObjectInputStream(bs); List ontologyChangeSet = (ArrayList) in.readObject(); desc.setOntologyChangeSet(ontologyChangeSet); } } // check for hasChangeSet value - RDF/XML serialized else if (prop.getURI().equals(((OWLDataProperty) Annotea.INSTANCE.annoteaMap.get("hasChangeSetRDF")).getURI())) { if (SwoopModel.changeSharingMethod==SwoopModel.RDFXML_SER) { // PARSE SERIALIZED RDF/XML OF CHANGES INTO OWL-API CHANGE OBJECTS String changeStr = vals.iterator().next().toString(); changeStr = changeStr.substring(0, changeStr.indexOf("^")); changeStr = this.recoverHTML(changeStr); File tmpFile = new File("temp"); FileWriter writer = new FileWriter(tmpFile); writer.write(changeStr); writer.close(); // reopen temp file String filePath = tmpFile.toURI().toString(); OWLOntology changeSetOnt = this.loadChangeSetOntology(filePath); OntologyChangeRenderer changeRenderer = new OntologyChangeRenderer(new ChangeLog(null, swoopModel)); try { desc.setOntologyChangeSet(changeRenderer.deserializeOntologyChanges(changeSetOnt)); } catch (Exception e) { e.printStackTrace(); } } } } } // get object property values on annotation instance Map objectValues = ind.getObjectPropertyValues(annotOnt); if (objectValues.size()>0) { for (Iterator it = objectValues.keySet().iterator(); it.hasNext();) { OWLObjectProperty prop = (OWLObjectProperty) it.next(); Set vals = (Set) objectValues.get(prop); // get values of "annotates" if (prop.getURI().equals(((OWLObjectProperty) Annotea.INSTANCE.annoteaMap.get("annotates")).getURI())) { // one is actual OWLNamedObject and second is its context (ontology) URI[] annotates = new URI[vals.size()]; int ctr = 0; for (Iterator valIter = vals.iterator(); valIter.hasNext(); ) { OWLIndividual indVal = (OWLIndividual) valIter.next(); URI indURI = indVal.getURI(); annotates[ctr++] = indURI; } //***! rearrange so that actual OWLNamedObject is first and ontology is second // now done in AnnoteaRenderer! desc.setAnnotates(annotates); } // get value of body - anonymous individual // For BACKWARD COMPATIBILITY: added desc.getBody()==null else if (desc.getBody()==null && prop.getURI().equals(((OWLObjectProperty) Annotea.INSTANCE.annoteaMap.get("body")).getURI())) { OWLIndividual bodyVal = (OWLIndividual) vals.iterator().next(); if (bodyVal.getURI()!=null) { URL bodyURL = new URL(bodyVal.getURI().toString()); BufferedReader in = new BufferedReader(new InputStreamReader(bodyURL.openStream())); String bodyStr = ""; String inputLine; while ((inputLine = in.readLine()) != null) { bodyStr += inputLine; } desc.setBody(bodyStr); } } } } } catch (Exception e) { System.out.println("Error reading annotation ("+e.getMessage()+")"); e.printStackTrace(); } return desc; } public OWLOntology loadChangeSetOntology(String url) throws Exception { OWLRDFParser parser = new OWLRDFParser(); parser.setOWLRDFErrorHandler(new OWLRDFErrorHandler() { public void owlFullConstruct(int code, String message) throws SAXException { } public void error(String message) throws SAXException { throw new SAXException(message.toString()); } public void warning(String message) throws SAXException { //System.out.println("RDFParser: " + message.toString()); } public void owlFullConstruct(int code, String message, Object obj) throws SAXException { } }); // load and parse ChangeSet Ontology from local "temp" URI location = new URI(url); parser.setConnection(new OWLConnectionImpl()); return (parser.parseOntology(location)); } /** * Returns a set of description objects defined as instances in the * ontology returned by findAnnotationsOnt(URI about) * @param about * @return * @throws AnnoteaException */ public Set findAnnotations(URI about) throws AnnoteaException { OWLOntology annotOnt = findAnnotationsOnt(about); Set descriptionSet = new HashSet(); try { Iterator iter = annotOnt.getIndividuals().iterator(); while (iter.hasNext()) { OWLIndividual descInd = (OWLIndividual) iter.next(); if (descInd.getTypes(annotOnt).size()>0) descriptionSet.add(parseIndividual(annotOnt, descInd)); } } catch (OWLException e) { e.printStackTrace(); } return descriptionSet; } public OWLOntology findAnnotationsOnt(URI about) throws AnnoteaException { return findXOnt(about, "w3c_annotates"); } public Description findReplies(URI root) throws AnnoteaException{ return null; } public OWLOntology findRepliesOnt(URI root) throws AnnoteaException { return findXOnt(root, "w3c_reply_tree"); } public Description get(URL location) { return null; } public OWLOntology getOnt(URL location) throws AnnoteaException { try { URI location_uri = new URI(location.toString()); Parser parser = new OWLRDFParser(); OWLOntology ontology = parser.parseOntology(location_uri); return ontology; } catch (Exception e) { throw new AnnoteaException(e); } } public URL post(Description description) throws AnnoteaException { OWLOntology ontology = description.buildOntology(); return (post(ontology)); } /** * Post an ontology to the annotea server by rendering it first * in RDF/XML using CorrectedRDFRenderer * @param ontology * @return * @throws AnnoteaException */ public URL post(OWLOntology ontology ) throws AnnoteaException { if (ontology == null) { throw new NullPointerException(); } try { HttpURLConnection connection = (HttpURLConnection) serverURL.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/rdf+xml"); connection.setDoOutput(true); Writer writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), Charset.forName("UTF-8"))); Renderer renderer = new CorrectedRDFRenderer(); renderer.renderOntology(ontology, writer); writer.close(); //connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_CREATED) { throw new AnnoteaException("Ontology not created! Reason: " +connection.getResponseCode() + " "+connection.getResponseMessage()); } return new URL(connection.getHeaderField("Location")); } catch (Exception e) { e.printStackTrace(); throw new AnnoteaException(e); } } public void put(URL location, Description description) throws AnnoteaException { put(location, description.buildOntology()); } public void put(URL location, OWLOntology ontology) throws AnnoteaException { try { HttpURLConnection connection = (HttpURLConnection) location.openConnection(); connection.setRequestMethod("PUT"); connection.setRequestProperty("Content-Type", "application/rdf+xml"); connection.setDoOutput(true); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), Charset.forName("UTF-8"))); Renderer renderer = new CorrectedRDFRenderer(); renderer.renderOntology(ontology, writer); connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new AnnoteaException("Ontology not created! Reason: " +connection.getResponseCode() + " "+connection.getResponseMessage()); } } catch (Exception e) { throw new AnnoteaException(e); } } protected String recoverHTML(String html) { html = html.replaceAll(">", ">"); html = html.replaceAll("<", "<"); html = html.replaceAll("&", "&"); return html; } }