view src/main/java/de/mpiwg/itgroup/annotationManager/restlet/SearchAnnotations.java @ 26:235b91ba8dff

on the way to new annotations...
author casties
date Thu, 26 Apr 2012 11:44:57 +0200
parents a3e324009990
children e5f5848892a2
line wrap: on
line source

//TODO: handle XML-Post des Annoteaprotocolls http://www.w3.org/2001/Annotea/User/Protocol.html

package de.mpiwg.itgroup.annotationManager.restlet;

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.Context;
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.ServerResource;
import org.restlet.security.User;

import de.mpiwg.itgroup.annotationManager.Constants.NS;
import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreSearchError;
import de.mpiwg.itgroup.annotationManager.RDFHandling.Annotation;
import de.mpiwg.itgroup.annotationManager.RDFHandling.RDFSearcher;
import de.mpiwg.itgroup.annotationManager.drupal.AnnotationHandler;
import de.mpiwg.itgroup.annotationManager.drupal.UnknowUserException;
import de.mpiwg.itgroup.triplestoremanager.exceptions.TripleStoreHandlerException;


public class SearchAnnotations extends ServerResource {

	private Logger logger = Logger.getRootLogger();



	@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);
		}
		Form requestHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers");
		String origin = requestHeaders.getFirstValue("Origin", true);
		if (origin == null) {
            responseHeaders.add("Access-Control-Allow-Origin", "*");
		} else {
            responseHeaders.add("Access-Control-Allow-Origin", origin);
		}
		responseHeaders.add("Access-Control-Allow-Methods", "OPTIONS,GET");
		String allowHeaders = requestHeaders.getFirstValue("Access-Control-Request-Headers", true);
		if (allowHeaders != null) {
		    responseHeaders.add("Access-Control-Allow-Headers", allowHeaders);
		}
	    //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", "true");
		responseHeaders.add("Access-Control-Max-Age", "60");
	}
	
	@Get("html")
	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");

		try {
			if (uri!=null){
			uri = URLDecoder.decode(uri, "utf-8");
			}
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
			setStatus(Status.CLIENT_ERROR_NOT_ACCEPTABLE);
			return null;
		}
		
		RDFSearcher searcher = new RDFSearcher("file:///annotations"); //TODO should ge into config file

		String retString="<html><body><table>";
		String lineFormat="<tr><td><a href=\"%s\">%s</a></td>" +
				"<td><a href=\"%s\">%s</a></td><td>%s</td><td>%s</td><td><a href=\"%s\">%s</a></td><td><a href=\"%s\">%s</a></td></div>";
		try {
			
			List<Annotation> annots=searcher.searchByUriUser(uri,user,limit,offset);

			for (Annotation annot:annots){
				
				
				RestServer restServer = (RestServer) getApplication();
				String userName=restServer.getUserNameFromLdap(annot.creator);
				List<String> xpointer = new ArrayList<String>();

				if (annot.xpointers==null || annot.xpointers.size()==0)
					retString+=String.format(lineFormat, userName,userName,annot.url,annot.url,annot.time,annot.text,annot.xpointer,annot.xpointer,annot.getAnnotationUri(),annot.getAnnotationUri());
				else {
					for(String xpointerString:annot.xpointers){
						retString+=String.format(lineFormat, userName,userName,annot.url,annot.url,annot.time,annot.text,xpointerString,xpointerString,annot.getAnnotationUri(),annot.getAnnotationUri());
					}
				}
			
			}
		} 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;
		} 

		retString+="</table></body></html>";
		
		logger.debug("sending:");
		logger.debug(retString);
		return new StringRepresentation(retString,MediaType.TEXT_HTML);
	}

	/**
	 * Erzeugt aus einer Annotation, das f�r den Annotator notwendige JSON-Format
	 * @param annot
	 * @return
	 */
	public JSONObject annot2AnnotatorJSON(Annotation annot){
		JSONObject jo = new JSONObject();
		try {
			jo.put("text", annot.text);
			jo.put("uri",annot.url);

			JSONObject userObject= new JSONObject();
			userObject.put("id",annot.creator);
			
			RestServer restServer = (RestServer) getApplication();
			
			String userID= annot.creator;
			if (userID.startsWith(NS.MPIWG_PERSONS_URL)){
				userID=userID.replace(NS.MPIWG_PERSONS_URL, ""); //entferne NAMESPACE
			}
			String userName=restServer.getUserNameFromLdap(userID);
			userObject.put("name",userName);
			
			jo.put("user",userObject);

			List<String> xpointer = new ArrayList<String>();

			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));
			jo.put("id", annot.getAnnotationUri());
			return jo;
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	@Get("json")
	public Representation doGetJSON(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<Annotation> annots=searcher.searchByUriUser(uri,user,limit,offset);

			ja = new JSONArray();
			for (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 userID= annot.creator;
//				if (userID.startsWith(NS.MPIWG_PERSONS)){
//					userID=userID.replace(NS.MPIWG_PERSONS, ""); //entferne NAMESPACE
//				}
//				String userName=restServer.getUserNameFromLdap(userID);
//				userObject.put("name",userName);
//				
//				jo.put("user",userObject);
//
//				List<String> xpointer = new ArrayList<String>();
//
//				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));
				JSONObject jo = annot2AnnotatorJSON(annot);
				if (jo!=null){
					ja.put(annot2AnnotatorJSON(annot));
				} else {
					setStatus(Status.SERVER_ERROR_INTERNAL,"JSon Error");
					return null;
				}
			}
		} 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;
		} 

		JSONObject result = new JSONObject();
		try {
			result.put("rows",ja);
			result.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(result);
		return new JsonRepresentation(result);
	}

	private JSONArray transformToRanges(List<String> 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);
				
				if (m.find()){
				{
					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.find()){
					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;
	}


	
	/**
	 * 
	 * @param entity should contain a form with the parameters "username", "password", "xpointer","text","uri","type"
	 * 
	 * username,password is optional, if not given BasicAuthentification is used.
	 * 
	 * 
	 * 
	 * If username given as a URI, the username will be transformed to an URI, username will be added to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
	 * 
	 * @return
	 */
	protected Annotation handleForm(Representation entity) {
		Annotation annot;
		Form form = new Form(entity);
		String username = form.getValues("username");
		String mode = form.getValues("mode");
		String password = form.getValues("password");
		String xpointer = form.getValues("xpointer");
		String text = form.getValues("text");
		String title = form.getValues("title");
		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();
		}

		//username should be a URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
		String usernameOrig=username;
		if (!username.startsWith("http"))
			username=NS.MPIWG_PERSONS_URL+username;
		
		if (mode.equals("complexAnnotation")){// Annotation mit text in externer ressource
			
			Context context = getContext();
			String drupalPath = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.drupalServer");
			
			
			AnnotationHandler ah = new AnnotationHandler(drupalPath);
			JSONObject newAnnot;
			try {
				newAnnot = ah.createAnnotation(title, text, usernameOrig, password);
			} catch (UnknowUserException e1) {
				setStatus(Status.CLIENT_ERROR_FORBIDDEN);
				e1.printStackTrace();
				return null;
			}
			try {
				annot= new Annotation(xpointer, username, null, text, type, newAnnot.getString("node_uri"));
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				setStatus(Status.SERVER_ERROR_INTERNAL);
				return null;
			}
		} else
			annot = new Annotation(xpointer, username, null, text,
				type, url);
		return annot;
	}

	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
	 * The username will be transformed to an URI if not given already as URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
	
	 * @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();

		//username should be a URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
		if (!username.startsWith("http"))
			username=NS.MPIWG_PERSONS_URL+username;
		
		return new 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.
	 * The username will be transformed to an URI if not given already as URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
	 * @param jo
	 * @param authUser
	 * @return
	 * @throws JSONException
	 */
	public Annotation handleAnnotatorSchema(JSONObject jo,
			Representation entity) throws JSONException {
		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;
		}
		
		//username should be a URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
		if (!username.startsWith("http"))
			username=NS.MPIWG_PERSONS_URL+username;
		
		return new Annotation(xpointer, username, null, text, null);
	}

}