# HG changeset patch # User dwinter # Date 1321973277 -3600 # Node ID 77530be3c747a79d90bd4b4e736e6d19eef84d0f intial diff -r 000000000000 -r 77530be3c747 .classpath --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.classpath Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 000000000000 -r 77530be3c747 .project --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.project Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,39 @@ + + + AnnotationManager + + + NamedIdentityManager + protege + protege-core + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.jdt.core.javanature + org.eclipse.wst.jsdt.core.jsNature + + diff -r 000000000000 -r 77530be3c747 .settings/.jsdtscope --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/.jsdtscope Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff -r 000000000000 -r 77530be3c747 .settings/org.eclipse.jdt.core.prefs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.jdt.core.prefs Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,8 @@ +#Fri Oct 28 19:18:03 CEST 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff -r 000000000000 -r 77530be3c747 .settings/org.eclipse.wst.common.component --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.wst.common.component Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,39 @@ + + + + + + + uses + + + uses + + + uses + + + uses + + + uses + + + uses + + + uses + + + uses + + + uses + + + uses + + + + + diff -r 000000000000 -r 77530be3c747 .settings/org.eclipse.wst.common.project.facet.core.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -r 000000000000 -r 77530be3c747 .settings/org.eclipse.wst.jsdt.ui.superType.container --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.wst.jsdt.ui.superType.container Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff -r 000000000000 -r 77530be3c747 .settings/org.eclipse.wst.jsdt.ui.superType.name --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.wst.jsdt.ui.superType.name Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,1 @@ +Window \ No newline at end of file diff -r 000000000000 -r 77530be3c747 WebContent/META-INF/MANIFEST.MF --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebContent/META-INF/MANIFEST.MF Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff -r 000000000000 -r 77530be3c747 WebContent/WEB-INF/web.xml.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebContent/WEB-INF/web.xml.template Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,36 @@ + + +RESTfulJavaWebServices-Restlet + + +org.restlet.application +de.mpiwg.itgroup.annotationManager.restlet.RestServer + + + + +de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUser +USERNAME + + +de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUserPassword +PASSWORD + + + +RestletServlet + +org.restlet.ext.servlet.ServerServlet + + + + + +RestletServlet +/annotator/* + + \ No newline at end of file diff -r 000000000000 -r 77530be3c747 WebContent/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebContent/index.html Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,12 @@ + + +
+
User:
+
Password:
+
Pointer (what to be annotated):
+
URL (of the annotation,optional ):
+
Text:
+
+
+ + \ No newline at end of file diff -r 000000000000 -r 77530be3c747 docs/howToTest.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/howToTest.txt Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,17 @@ +Test POST: + +curl -i -X POST -d "username=james&xpointer=xx" http://127.0.01:8280/AnnotationManager/annotations + +curl -i -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"username":"testname","xpointer":"xx","text":"mytext"}' http://127.0.0.1:8280/AnnotationManager/annotator/annotations + +curl -i -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"username":"testname","xpointer":"xx","text":"mytext"}' http://euler.mpiwg-berlin.mpg.de:58080/AnnotationManager/annotator/annotations + + +curl -i -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d ' {"id": "39fc339cf058bd22176771b3e3187329","created": "2011-05-24T18:52:08.036814","uri": "http://some.uri","text": "A note I wrote", "quote": "The lady said this.","ranges": [{"start": "/p[69]/font/font", "end": "/p[70]/font/font", "startOffset": "0", "endOffset": "120"}], "annotator_schema_version": "v1.0"}' http://127.0.0.1:8280/AnnotationManager/annotator/annotations + + +Test: get: + +curl -i -H "Accept: application/json" -X GET http://127.0.0.1:8280/AnnotationManager/annotator/annotations?uri=http://www.mpiwg-berlin.mpg.de + +curl -i -H "Accept: application/json" -X GET http://127.0.0.1:8280/AnnotationManager/annotator/annotations?uri=http://some.uri diff -r 000000000000 -r 77530be3c747 docs/jaas_config_example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/jaas_config_example Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,9 @@ +File Edit Options Buffers Tools Help +BasicJaasAuthenticationApplication { + com.sun.security.auth.module.LdapLoginModule REQUIRED + userProvider="ldap://mpiwgldap.mpiwg-berlin.mpg.de/ou=people,dc=mpiwg-berlin,dc=mpg,dc=de" + userFilter="(&(uid={USERNAME})(objectClass=inetOrgPerson))" + debug=true + useSSL=false +; +}; \ No newline at end of file diff -r 000000000000 -r 77530be3c747 docs/testBook.jpage --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/testBook.jpage Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,1 @@ +Syntax error on token "",0,1))/range-to(end-point(string-range("", delete this token Pattern rg = Pattern.compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)/range-to\\(end-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)\\)"); Matcher m = rg.matcher("http://some.uri#xpointer(start-point(string-range(\"/p[69]/font/font\",0,1))/range-to(end-point(string-range(\"/p[70]/font/font\",120,1))))"); m.find(); System.out.println(m.group(4)); (No explicit return value) \ No newline at end of file diff -r 000000000000 -r 77530be3c747 libs/log4j-1.2.15.jar Binary file libs/log4j-1.2.15.jar has changed diff -r 000000000000 -r 77530be3c747 libs/org.json.jar Binary file libs/org.json.jar has changed diff -r 000000000000 -r 77530be3c747 libs/org.restlet.ext.jaas.jar Binary file libs/org.restlet.ext.jaas.jar has changed diff -r 000000000000 -r 77530be3c747 libs/org.restlet.ext.json.jar Binary file libs/org.restlet.ext.json.jar has changed diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/Constants/NS.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/Constants/NS.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,9 @@ +package de.mpiwg.itgroup.annotationManager.Constants; + +public class NS { + public static String ANNOTATION_TYPE="http://www.w3.org/2000/10/annotationType#"; + public static String ANNOTATION_NS="http://www.w3.org/2000/10/annotation-ns#"; + public static String RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + public static String MPIWG_annot="http://ontologies.mpiwg-berlin.mpg.de/annotations/"; + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/Errors/TripleStoreSearchError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/Errors/TripleStoreSearchError.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,10 @@ +package de.mpiwg.itgroup.annotationManager.Errors; + +public class TripleStoreSearchError extends Error { + + /** + * + */ + private static final long serialVersionUID = -2082690329117989805L; + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/Errors/TripleStoreStoreError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/Errors/TripleStoreStoreError.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,5 @@ +package de.mpiwg.itgroup.annotationManager.Errors; + +public class TripleStoreStoreError extends Error { + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/Errors/XPointerError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/Errors/XPointerError.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,10 @@ +package de.mpiwg.itgroup.annotationManager.Errors; + +public class XPointerError extends Error { + + /** + * + */ + private static final long serialVersionUID = 6385261924906311174L; + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/RDFHandling/Convert.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/RDFHandling/Convert.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,239 @@ +package de.mpiwg.itgroup.annotationManager.RDFHandling; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.apache.log4j.Logger; +import org.openrdf.repository.RepositoryException; +import org.restlet.Context; +import org.restlet.engine.component.ChildContext; + +import de.mpiwg.itgroup.annotationManager.Constants.NS; +import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreStoreError; +import de.mpiwg.itgroup.annotationManager.Errors.XPointerError; +import de.mpiwg.itgroup.nimanager.exceptions.TripleStoreHandlerException; +import de.mpiwg.itgroup.nimanager.owl.TripleStoreHandler; +import de.mpiwg.itgroup.nimanager.owl.TripleStoreHandler.Quadruple; +import de.mpiwg.itgroup.nimanager.owl.TripleStoreHandler.LiteralQuadruple; + +/** + * @author dwinter + * + * Klasse zum Konvertieren von Annotationen zum mpiwg RDF-Format: + * http://ontologies.mpiwg-berlin.mpg.de/annotations/ + */ + +public class Convert { + private String ctx; + private Logger logger = Logger.getRootLogger(); + private String urlBase="http://ontologies.mpiwg-berlin.mpg.de/annotations/"; //TODO should go into config + public Convert(String c){ + ctx=c; + } + + /** + * Fasst alle Parameter zusammen, die eine Annotation bilden + * @author dwinter + * + */ + static public class Annotation { + public String xpointer=null; //if queried xpointer should contain the first xpointer in the xpointers list, if there is more than one. + public String creator=null; + public String time=null; + public String text=null; + public String type=null; + public String url; + public List xpointers=null; // list of xpointers on the page url, can be empty or null if there is only one. + + /** + * @param xpointer Beschreibt die Annotation + * @param creator Username des Creators + * @param time Erstellungszeit, wenn null wird das aktuelle Datum verwenden beim Konvertieren + * @param text der Annotation + * @param type Annotationstype (Entsprechend den in http://www.w3.org/2000/10/annotationType# definierten.) + * @param url Url einer Annotation + */ + public Annotation(String xpointer, String creator, String time, String text, String type, String url){ + this.xpointer=xpointer; + this.creator=creator; + this.time=time; + this.text=text; + this.type=type; + this.url=url; + } + + /** + * @param xpointer Beschreibt die Annotation + * @param creator Username des Creators + * @param time Erstellungszeit, wenn null wird das aktuelle Datum verwenden beim Konvertieren + * @param text der Annotation + * @param type Annotationstype (Entsprechend den in http://www.w3.org/2000/10/annotationType# definierten.) + */ + public Annotation(String xpointer, String creator, String time, String annot, String type){ + this.xpointer=xpointer; + this.creator=creator; + this.time=time; + this.text=annot; + this.type=type; + this.url=null; + } + + + } + private String annotUrl=null; + + /** + * + * @param xpointer Beschreibt die Annotation + * @param creator Username des Creators + * @param time Erstellungszeit, wenn null wird das aktuelle Datum verwenden beim Konvertieren + * @param text der Annotation + * @param type Annotationstype (Entsprechend den in http://www.w3.org/2000/10/annotationType# definierten.) + * @return + */ + + private List annot2quadruple(String xpointer, String creator, String time, String annot, String type){ + return annot2quadruple(new Annotation(xpointer, creator, time, annot, type)); + } + + + /** + * @param annot + * @return + */ + public List annot2quadruple(Annotation annot){ + List retQuad = new ArrayList(); + + String annotation = createRessourceURL("annot:"); + + if (annot.time==null){ + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'"); + annot.time=format.format(Calendar.getInstance().getTime()); + + } + + //TODO: check type + retQuad.add(new Quadruple(annotation, NS.RDF+"type", NS.ANNOTATION_TYPE+annot.type, ctx)); + + //add author + retQuad.add(new LiteralQuadruple(annotation, NS.ANNOTATION_NS+"author", annot.creator, ctx)); + + // creation time + retQuad.add(new LiteralQuadruple(annotation, NS.ANNOTATION_NS+"created", annot.time, ctx)); + + String[] xpointerSplitted = annot.xpointer.split("#"); + + if (xpointerSplitted.length>2){ + annotUrl=null; + throw new XPointerError(); + } + + // now add the xpointers + retQuad.add(new Quadruple(annotation, NS.MPIWG_annot+"docuviewerText", xpointerSplitted[0], ctx)); + retQuad.add(new Quadruple(annotation, NS.MPIWG_annot+"textSelection", annot.xpointer, ctx)); + + String annotationtext =createRessourceURL("annotText:"); + + retQuad.add(new Quadruple(annotation, NS.ANNOTATION_NS+"body", annotationtext, ctx)); + + retQuad.add(new Quadruple(annotationtext, NS.RDF+"type", NS.MPIWG_annot+"StandardTextNote", ctx)); + + retQuad.add(new LiteralQuadruple(annotationtext, NS.MPIWG_annot+"containsText", annot.text, ctx)); + + for (Quadruple ret:retQuad){ + logger.debug(ret.toString()); + } + + annotUrl=annotation; + return retQuad; + } + + /** + * Erzeuge eine urn aus der aktullen Zeit in millis + * @return + */ + private String createRessourceURL(String prefix) { + + Calendar cal = Calendar.getInstance(); + + long time = cal.getTimeInMillis(); + + return String.format("%s%s%s", urlBase,prefix,time); + + + + } + + /** + * gibt nach die nach aufruf eines Converters erzeuge aktuelle url der annotation zurueck + * @return + */ + public String getAnnotUrl() { + + return annotUrl; + } + + /** + * @param args + */ + public static void main(String[] args) { + Convert myConvert = new Convert("http://annotations.rdf"); + List rets = myConvert.annot2quadruple("http://mpdl-dev.mpiwg-berlin.mpg.de/ECHOdocuViewfullTest?url=/mpiwg/online/permanent/library/163127KK&viewMode=text&pn=7#xpointer(string-range(id("s1"), "", 66, 12))", "mbuchman", null, "myannot", "Example"); + for (Quadruple ret:rets){ + System.out.println(ret.toString()); + } + + } + + + public String storeAnnotation(Convert.Annotation annot) throws TripleStoreStoreError { + + //Convert convert = new Convert(""); + + + if ((annot.type==null) || annot.type.equals("")){ + annot.type="Comment"; + } + + List annots = new ArrayList(); + if (annot.text!=null && !annot.text.equals("")) + annots=annot2quadruple(annot); + if (annot.url!=null && !annot.url.equals("")) + annots.addAll(rel2quadruple(annot)); + + + + try { + ChildContext context = (ChildContext)Context.getCurrent(); + String user = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUser"); + String pw = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUserPassword"); + + TripleStoreHandler th = new TripleStoreHandler("jdbc:virtuoso://virtuoso.mpiwg-berlin.mpg.de:1111", user, pw); + + th.write(annots); + } catch (RepositoryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new TripleStoreStoreError(); + } catch (TripleStoreHandlerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new TripleStoreStoreError(); + } + + + + return getAnnotUrl(); +} + + + private List rel2quadruple(Annotation annot) { + // TODO Auto-generated method stub + return new ArrayList(); + } + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/RDFHandling/RDFSearcher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/RDFHandling/RDFSearcher.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,104 @@ +package de.mpiwg.itgroup.annotationManager.RDFHandling; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.tiles.context.ListAttribute; +import org.openrdf.query.BindingSet; +import org.openrdf.query.TupleQueryResult; +import org.restlet.Context; +import org.restlet.engine.component.ChildContext; + +import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreSearchError; +import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert.Annotation; +import de.mpiwg.itgroup.nimanager.exceptions.TripleStoreHandlerException; +import de.mpiwg.itgroup.nimanager.owl.TripleStoreHandler; + +public class RDFSearcher { + + private String urlBase="http://ontologies.mpiwg-berlin.mpg.de/annotations/"; //TODO should go into config + + private TripleStoreHandler th; + + private String context; + + private Logger logger= Logger.getRootLogger(); + + public RDFSearcher(String context) { + this.context=context; + } + + + + public List search(String uri, String user, String limit, + String offset) throws TripleStoreHandlerException, TripleStoreSearchError { + + List retAnnots = new ArrayList(); + ChildContext context = (ChildContext)Context.getCurrent(); + String tripleStoreUser = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUser"); + String tripleStorePW = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUserPassword"); + + th = new TripleStoreHandler("jdbc:virtuoso://virtuoso.mpiwg-berlin.mpg.de:1111", tripleStoreUser, tripleStorePW);//TODO should go into config + String queryString=""; + + String whereString = "?s ?p ."; + //whereString +="?s ?link."; + + if(uri!=null && !uri.equals("")){ + whereString +=String.format("?s <%s>.",uri);} + else { + whereString +=String.format("?s ?uri."); + } + whereString +=String.format("?s ?xpointer."); + whereString +=String.format("?s ?annotText."); + + if(user!=null && !user.equals("")){ + whereString +=String.format("?s \"%s\".",user); + } else { + whereString +=String.format("?s ?author."); + } + + whereString +=String.format("?s ?created."); + + whereString +=String.format("?annotText ?text."); + + queryString=String.format("select distinct * where {%s}",whereString); + + + logger.debug("RDFSearcher:"+queryString); + + try { + TupleQueryResult results = th.querySPARQL(queryString); + + while (results.hasNext()) { + BindingSet result = results.next(); + String annotUri; + if(uri!=null && !uri.equals("")){ + annotUri=uri; + } else { + annotUri = result.getBinding("uri").getValue().stringValue(); + } + + String annotUser; + if(user!=null && !user.equals("")){ + annotUser=user; + } else { + annotUser = result.getBinding("author").getValue().stringValue(); + } + + + Annotation annot = new Annotation(result.getBinding("xpointer").getValue().stringValue(), + annotUser, result.getBinding("created").getValue().stringValue(), + result.getBinding("text").getValue().stringValue(), null, + annotUri); + retAnnots.add(annot); + } + } catch (Exception e) { + throw new TripleStoreSearchError(); + } + // TODO Auto-generated method stub + return retAnnots; + } + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/restlet/AddAndSearchAnnotations.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AddAndSearchAnnotations.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,482 @@ +//TODO: handle XML-Post des Annoteaprotocolls http://www.w3.org/2001/Annotea/User/Protocol.html + +package de.mpiwg.itgroup.annotationManager.restlet; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.restlet.data.ClientInfo; +import org.restlet.data.Form; +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.json.JsonRepresentation; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.resource.Get; +import org.restlet.resource.Options; +import org.restlet.resource.Post; +import org.restlet.resource.ServerResource; +import org.restlet.security.User; + +import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreSearchError; +import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreStoreError; +import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert; +import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert.Annotation; +import de.mpiwg.itgroup.annotationManager.RDFHandling.RDFSearcher; +import de.mpiwg.itgroup.nimanager.exceptions.TripleStoreHandlerException; + +public class AddAndSearchAnnotations extends ServerResource { + + private Logger logger = Logger.getRootLogger(); + + /** + * + * json hash: username: name des users xpointer: xpointer auf den Ausschnitt + * (incl. der URL des Dokumentes) text: text der annotation annoturl: url + * auf eine Annotation falls extern + * + * @return + */ + + + @Options + public void doOptions(Representation entity) { + Form responseHeaders = (Form) getResponse().getAttributes().get( + "org.restlet.http.headers"); + if (responseHeaders == null) { + responseHeaders = new Form(); + getResponse().getAttributes().put("org.restlet.http.headers", + responseHeaders); + } + responseHeaders.add("Access-Control-Allow-Origin", "*"); + responseHeaders.add("Access-Control-Allow-Methods", "POST,OPTIONS,GET"); + responseHeaders.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Annotator-Account-Id, X-Annotator-User-Id, X-Annotator-Auth-Token-Valid-Until, X-Annotator-Auth-Token"); + responseHeaders.add("Access-Control-Allow-Credentials", "false"); + responseHeaders.add("Access-Control-Max-Age", "60"); + } + + @Get("json") + public Representation doGetHTML(Representation entity){ + + doOptions(entity); + Form form = getRequest().getResourceRef().getQueryAsForm(); + String uri = form.getFirstValue("uri"); + String user = form.getFirstValue("user"); + + String limit=form.getFirstValue("limit"); + String offset=form.getFirstValue("offset"); + + +// + RDFSearcher searcher = new RDFSearcher("file:///annotations"); //TODO should ge into config file + + JSONArray ja; + try { + + List annots=searcher.search(uri,user,limit,offset); + + ja = new JSONArray(); + for (Convert.Annotation annot:annots){ + JSONObject jo = new JSONObject(); + jo.put("text", annot.text); + jo.put("uri",annot.url); + + JSONObject userObject= new JSONObject(); + userObject.put("id",annot.creator); + + RestServer restServer = (RestServer) getApplication(); + String userName=restServer.getUserNameFromLdap(annot.creator); + userObject.put("name",userName); + + jo.put("user",userObject); + + List xpointer = new ArrayList(); + + if (annot.xpointers==null || annot.xpointers.size()==0) + xpointer.add(annot.xpointer); + else { + for(String xpointerString:annot.xpointers){ + xpointer.add(xpointerString); + } + } + jo.put("ranges", transformToRanges(xpointer)); + ja.put(jo); + } + } catch (TripleStoreHandlerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL,"TripleStoreHandler Error"); + return null; + } catch (TripleStoreSearchError e) { + // TODO Auto-generated catch block + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL,"TripleStoreSearch Error"); + return null; + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL,"JSon Error"); + return null; + } + + JSONObject retObject = new JSONObject(); + try { + retObject.put("rows",ja); + retObject.put("total",ja.length()); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL,"JSon Error"); + return null; + } + + logger.debug("sending:"); + logger.debug(retObject); + return new JsonRepresentation(retObject); + } + + private JSONArray transformToRanges(List xpointers) { + + JSONArray ja = new JSONArray(); + + Pattern rg = Pattern.compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)/range-to\\(end-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)\\)"); + Pattern rg1 = Pattern.compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)"); + + + + try { + for(String xpointer:xpointers){ + String decoded =URLDecoder.decode(xpointer,"utf-8"); + Matcher m=rg.matcher(decoded); + m.find(); + //if (m.matches()){ + { + JSONObject jo = new JSONObject(); + jo.put("start", m.group(1)); + jo.put("startOffset", m.group(2)); + jo.put("end", m.group(3)); + jo.put("endOffset", m.group(4)); + ja.put(jo); + //} + } + m=rg1.matcher(xpointer); + if (m.matches()){ + JSONObject jo = new JSONObject(); + jo.put("start", m.group(1)); + jo.put("startOffset", m.group(2)); + + ja.put(jo); + } + + + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + return ja; + + + + + + } + + @Post("json") + public Representation doPostJson(Representation entity) { + + String retVal = doPost(entity); + JSONObject jo; + try { + jo = new JSONObject("{\"annotUrl\":\"" + retVal + "\"}"); + } catch (JSONException e) { + setStatus(Status.SERVER_ERROR_INTERNAL); + return null; + } + JsonRepresentation retRep = new JsonRepresentation(jo); + return retRep; + } + + @Post("html") + public Representation doPostHtml(Representation entity) { + String retVal = doPost(entity); + if (retVal == null) { + return null; + } + String text = String.format( + "%s", retVal + .replace(">", "").replace("<", ""), + retVal.replace(">", ">").replace("<", "<")); + Representation retRep = new StringRepresentation(text, + MediaType.TEXT_HTML); + return retRep; + } + + public String doPost(Representation entity) { + Convert.Annotation annot; + // versuche basic authentifizierung und hole den Benutzer von dort. + + // User authUser;= handleBasicAuthentification(entity); + + if (entity.getMediaType().equals(MediaType.APPLICATION_JSON)) { + + JsonRepresentation jrep; + try { + jrep = new JsonRepresentation(entity); + } catch (IOException e1) { + setStatus(Status.SERVER_ERROR_INTERNAL); + return null; + } + + // try { + // logger.debug(jrep.getText()); + // } catch (IOException e1) { + // // TODO Auto-generated catch block + // e1.printStackTrace(); + // } + // + + try { + JSONObject jo = jrep.getJsonObject(); + String mode = jo.getString("mode"); // hole modus + if (mode==null || mode.equals("")) + mode="annotea"; // default mode (annotea) TODO make this configurable + + if (mode.equals("annotator") ) { // annotator format + annot = handleAnnotatorSchema(jo, entity); + logger.debug("storing annotator object"); + logger.debug(jo); + } else if (mode.equals("annotea")){ + annot = handleAnnotea(jo, entity); + } else { + setStatus(Status.CLIENT_ERROR_BAD_REQUEST,"mode "+mode+"not supported!"); + return null; + } + + } catch (JSONException e) { + setStatus(Status.CLIENT_ERROR_BAD_REQUEST); + return null; + } + + } else if (entity.getMediaType().equals(MediaType.APPLICATION_WWW_FORM)) { + annot = handleForm(entity); + + } else { + setStatus(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); + + return null; + } + + if (annot==null){ + return null; + } + if (annot.xpointer == null || annot.creator == null) { + setStatus(Status.CLIENT_ERROR_BAD_REQUEST); + + return null; + } + + + + try { + return new Convert("file:///annotations").storeAnnotation(annot); + } catch (TripleStoreStoreError e) { + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStore Error"); + return null; + } + } + + protected Convert.Annotation handleForm(Representation entity) { + Convert.Annotation annot; + Form form = new Form(entity); + String username = form.getValues("username"); + String password = form.getValues("password"); + String xpointer = form.getValues("xpointer"); + String text = form.getValues("text"); + String url = form.getValues("url"); + String type = form.getValues("type"); + RestServer restServer = (RestServer) getApplication(); + + // falls user and password nicht null sind: + User userFromForm = null; + if (username != null && password != null) { + if (restServer.authenticate(username, password, getRequest())) { + userFromForm = new User(username); + } + } + User authUser = null; + + if (userFromForm == null) { + authUser = handleBasicAuthentification(entity); + } + + // weder BasicAuth noch FormAuth + if (authUser == null && userFromForm == null) { + setStatus(Status.CLIENT_ERROR_FORBIDDEN); + return null; + } + + if (userFromForm != null) { + username = userFromForm.getIdentifier(); + } else { + username = authUser.getIdentifier(); + } + + annot = new Convert.Annotation(xpointer, username, null, text, + type, url); + return annot; + } + + @Post + public Representation doPostHtml2(Representation entity) { + return doPostHtml(entity); + } + + private User handleBasicAuthentification(Representation entity) { + RestServer restServer = (RestServer) getApplication(); + if (!restServer.authenticate(getRequest(), getResponse())) { + // Not authenticated + return null; + } + + ClientInfo ci = getRequest().getClientInfo(); + logger.debug(ci); + return getRequest().getClientInfo().getUser(); + + } + + /** + * using a minimal annotation format based on the annotea specification + * + * @param jo + * must contain xpointer, text,url,type and can contain a + * username, if not the username form the authentification will + * be used. + * @param authUser + * user object + * @return + * @throws JSONException + */ + public Annotation handleAnnotea(JSONObject jo, Representation entity) + throws JSONException { + + User authUser = handleBasicAuthentification(entity); + String username = jo.getString("username"); // not required, if no + // username given authuser + // will be used. + String xpointer = jo.getString("xpointer"); + String text = null; + if (jo.has("text")) + text = jo.getString("text"); + + String url = null; + if (jo.has("url")) + url = jo.getString("url"); + + String type = null; + if (jo.has("type")) + type = jo.getString("type"); + + if (username == null) + username = authUser.getIdentifier(); + + return new Convert.Annotation(xpointer, username, null, text, type, url); + } + + /** + * uses the specification from the annotator project. + * + * @see{https://github.com/okfn/annotator/wiki/Annotation-format The user + * object must + * contain an + * id and + * password or + * basic + * authentification + * is used. + * + * @param jo + * @param authUser + * @return + * @throws JSONException + */ + public Convert.Annotation handleAnnotatorSchema(JSONObject jo, + Representation entity) throws JSONException { + Convert.Annotation annot; + String url = jo.getString("uri"); + String text = jo.getString("text"); + + String username = null; + if (jo.has("user")) { // not required, if no username given authuser + // will be used otherwise username and password + // has to be submitted + JSONObject user = jo.getJSONObject("user"); + if (user.has("id")) { + username = user.getString("id"); + if(!user.has("password")){ + User authUser = handleBasicAuthentification(entity); + if (authUser==null){ + setStatus(Status.CLIENT_ERROR_FORBIDDEN); + return null; + } + username = authUser.getIdentifier(); + } else { + String password = user.getString("password"); + if (!((RestServer) getApplication()).authenticate(username, + password, getRequest())) { + setStatus(Status.CLIENT_ERROR_FORBIDDEN); + return null; + } + } + } + + } else { + User authUser = handleBasicAuthentification(entity); + if (authUser == null) { + setStatus(Status.CLIENT_ERROR_FORBIDDEN); + return null; + } + username = authUser.getIdentifier(); + } + + String xpointer; + if (jo.has("ranges")) { + JSONObject ranges = jo.getJSONArray("ranges").getJSONObject(0); + String start = ranges.getString("start"); + String end = ranges.getString("end"); + String startOffset = ranges.getString("startOffset"); + String endOffset = ranges.getString("endOffset"); + + try { + xpointer = url+"#"+ + URLEncoder.encode(String.format( + "xpointer(start-point(string-range(\"%s\",%s,1))/range-to(end-point(string-range(\"%s\",%s,1))))", + start, startOffset, end, endOffset),"utf-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL); + return null; + } + } else { + xpointer = url; + } + return new Convert.Annotation(xpointer, username, null, text, null); + } + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/restlet/Dummy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/Dummy.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,45 @@ +package de.mpiwg.itgroup.annotationManager.restlet; + +import java.io.FileWriter; +import java.io.IOException; + +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.resource.Get; +import org.restlet.resource.Post; +import org.restlet.resource.ServerResource; + +public class Dummy extends ServerResource { + + @Post + public Representation doPost(Representation entity){ + + try { + FileWriter fh = new FileWriter("/tmp/input", true); + fh.write(entity.toString()); + fh.write(entity.getText()); + fh.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return new StringRepresentation("thanks"); + } + + + @Get + public Representation doGet(Representation entity){ + + try { + FileWriter fh = new FileWriter("/tmp/input", true); + fh.write(entity.toString()); + fh.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return new StringRepresentation("thanks"); + } +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/restlet/MyCallBackHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/MyCallBackHandler.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,48 @@ +package de.mpiwg.itgroup.annotationManager.restlet; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.ConfirmationCallback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +public class MyCallBackHandler implements CallbackHandler { + + + private String username; + private String password; + + public MyCallBackHandler(String username, String password) { + this.username=username; + this.password=password; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof TextOutputCallback) { + + // do nothing + } else if (callbacks[i] instanceof NameCallback) { + + NameCallback nc = (NameCallback)callbacks[i]; + nc.setName(username); + + } else if (callbacks[i] instanceof PasswordCallback) { + + PasswordCallback nc = (PasswordCallback)callbacks[i]; + nc.setPassword(password.toCharArray()); + } else { + throw new UnsupportedCallbackException + (callbacks[i], "Unrecognized Callback"); + } + } + + } + +} diff -r 000000000000 -r 77530be3c747 src/de/mpiwg/itgroup/annotationManager/restlet/RestServer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/RestServer.java Tue Nov 22 15:47:57 2011 +0100 @@ -0,0 +1,222 @@ +package de.mpiwg.itgroup.annotationManager.restlet; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Hashtable; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.restlet.Application; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; + +import org.restlet.data.ChallengeScheme; +import org.restlet.data.ClientInfo; +import org.restlet.ext.jaas.JaasVerifier; +import org.restlet.routing.Router; +import org.restlet.routing.Template; +import org.restlet.routing.TemplateRoute; +import org.restlet.security.ChallengeAuthenticator; +import org.restlet.security.MapVerifier; +import org.restlet.security.User; +import org.restlet.security.Verifier; + +import com.sun.org.apache.xalan.internal.xsltc.runtime.Attributes; +import com.sun.security.auth.login.ConfigFile; + + + + +public class RestServer extends Application { + + + private ChallengeAuthenticator authenticator; + private CallbackHandler callbackHandler; + + /** Erzeuge einen Authenticator + * @return + */ + private ChallengeAuthenticator createAuthenticator() { + Context context = getContext(); + boolean optional = true; + ChallengeScheme challengeScheme = ChallengeScheme.HTTP_BASIC; + String realm = "Annotation Service"; + + // MapVerifier isn't very secure; see docs for alternatives + //MapVerifier verifier = new MapVerifier(); + //verifier.getLocalSecrets().put("user", "password".toCharArray()); + + JaasVerifier verifier = new JaasVerifier("BasicJaasAuthenticationApplication"); + + + Configuration jaasConfig; + jaasConfig = createConfiguration(); + + + verifier.setConfiguration(jaasConfig); + verifier.setUserPrincipalClassName("com.sun.security.auth.UserPrincipal"); + + ChallengeAuthenticator auth = new ChallengeAuthenticator(context, optional, challengeScheme, realm, verifier) { + @Override + protected boolean authenticate(Request request, Response response) { + if (request.getChallengeResponse() == null) { + return false; + } else { + return super.authenticate(request, response); + } + } + }; + + return auth; + } + + protected Configuration createConfiguration() { + Configuration jaasConfig; + URI confUri; + try { + confUri = new URI("file:///etc/jaasAuth.conf"); //TODO shoould be configurable + } catch (URISyntaxException e) { + e.printStackTrace(); + confUri = null; + } + + jaasConfig= new ConfigFile(confUri); + return jaasConfig; + } + + public RestServer(Context parentContext){ + super(parentContext); + + Logger rl = Logger.getRootLogger(); + BasicConfigurator.configure(); + rl.setLevel(Level.DEBUG); + + + } + + public synchronized Restlet createInboundRoot(){ + this.authenticator = createAuthenticator(); + + + Router router = new Router(getContext()); + + router.attach("/annotations",AddAndSearchAnnotations.class); + router.attach("/search",AddAndSearchAnnotations.class); // annotator api askes for different uris for search and adding + router.attach("/dummy",Dummy.class); + + authenticator.setNext(router); + return authenticator; + + + + } + + public boolean authenticate(Request request, Response response) { + if (!request.getClientInfo().isAuthenticated()) { + authenticator.challenge(response, false); + return false; + } + + if(request.getClientInfo().getUser()==null) //FIXME sometimes ist authenticated true, but no user + { + authenticator.challenge(response, false); + return false; + } + return true; + } + + public boolean authenticate(String username, String password,Request request) { + LoginContext lc; + + try { + Configuration conf = createConfiguration(); + + lc = new LoginContext("BasicJaasAuthenticationApplication", null, new MyCallBackHandler(username,password),conf); + lc.login(); + } catch (LoginException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + + Subject subject = lc.getSubject(); + ClientInfo clientInfo = new ClientInfo(); + User user = new User(username); + clientInfo.setAuthenticated(true); + clientInfo.setUser(user); + + request.setClientInfo(clientInfo); + return true; + } + + public String getUserNameFromLdap(String creator) { + String retString=creator; // falls nichts gefunden wird einfach den creator zurueckgeben + Hashtable env = new Hashtable(); + String sp = "com.sun.jndi.ldap.LdapCtxFactory"; + env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, sp); + + String ldapUrl = "ldap://mpiwgldap.mpiwg-berlin.mpg.de/dc=mpiwg-berlin,dc=mpg,dc=de";//TODO should go into config file + env.put(javax.naming.Context.PROVIDER_URL, ldapUrl); + + DirContext dctx; + try { + dctx = new InitialDirContext(env); + } catch (NamingException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + return retString; + } + + String base = "ou=People"; + + SearchControls sc = new SearchControls(); + String[] attributeFilter = { "cn", "mail" }; + sc.setReturningAttributes(attributeFilter); + sc.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String filter = "(uid="+creator+")"; + + try { + NamingEnumeration results = dctx.search(base, filter, sc); + while (results.hasMore()) { + SearchResult sr = (SearchResult) results.next(); + javax.naming.directory.Attributes attrs = sr.getAttributes(); + + Attribute attr = attrs.get("cn"); + retString=(String) attr.get(); + } + } catch (NamingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + dctx.close(); + } catch (NamingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return retString; + } + +}