eu.semlibproject.annotationserver.restapis.APIHelper.java Source code

Java tutorial

Introduction

Here is the source code for eu.semlibproject.annotationserver.restapis.APIHelper.java

Source

/*
 * Copyright (c) 2013 Net7 SRL, <http://www.netseven.it/>
 * 
 * This file is part of Pundit: Annonation Server.
 * 
 * Pundit: Annonation Server is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Pundit: Annonation Server is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Pundit: Annonation Server.  If not, see <http ://www.gnu.org/licenses/>.
 *
 * See LICENSE.TXT or visit <http://thepund.it> for the full text of the license.
 */

package eu.semlibproject.annotationserver.restapis;

import eu.semlibproject.annotationserver.MediaType;
import eu.semlibproject.annotationserver.SemlibConstants;
import eu.semlibproject.annotationserver.SesameRDFJSONConverter;
import eu.semlibproject.annotationserver.hibernate.Userdata;
import eu.semlibproject.annotationserver.managers.ConfigManager;
import eu.semlibproject.annotationserver.managers.HibernateManager;
import eu.semlibproject.annotationserver.managers.RepositoryManager;
import eu.semlibproject.annotationserver.managers.TokenManager;
import eu.semlibproject.annotationserver.managers.UtilsManager;
import eu.semlibproject.annotationserver.models.Annotation;
import eu.semlibproject.annotationserver.models.Notebook;
import eu.semlibproject.annotationserver.models.User;
import eu.semlibproject.annotationserver.repository.RepositoryException;
import eu.semlibproject.annotationserver.security.PermissionsManager;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

/**
 * Base class for all class implementing restful API.
 * It contains contains shared and helper methods for restful API.
 * 
 * @author Michele Nucci
 */
public class APIHelper {

    // For user token
    protected String userToken = null;

    private Logger logger = Logger.getLogger(APIHelper.class.getName());

    public APIHelper(@Context HttpServletRequest req, @Context ServletContext servletContext) {
        ConfigManager configManager = ConfigManager.getInstance();

        if (servletContext != null) {
            configManager.setMainServletContext(servletContext);
        }

        configManager.setServletContextPath(req.getContextPath());

        String token = (String) req.getAttribute(SemlibConstants.LOGGED_USER_ATTR);
        if (StringUtils.isNotBlank(token)) {
            this.userToken = token;
        }
    }

    public User getCurrentLoggedUser() {
        if (StringUtils.isNotBlank(userToken)) {
            return TokenManager.getInstance().getUserFromToken(userToken);
        }

        return null;
    }

    public User getCurrentLoggedUserOrAnonUser() {
        User currentLoggedUser = this.getCurrentLoggedUser();
        if (currentLoggedUser == null || StringUtils.isBlank(currentLoggedUser.getUserID())
                || !currentLoggedUser.isAuthenticated()) {
            return User.createAnonymousUser();
        }

        return currentLoggedUser;
    }

    // ==== Notebooks API Helpers ================    

    /**
     * Check if a specified Notebook exists and if it is valid.
     * 
     * @param notebookID                the Notebook ID to check
     * @param checkForPublicReadability if <code>true</code> check if the specified Notebook exists
     *                                  and if it is public readable.
     * 
     * @return                          <ul>
     *                                      <li>Status.OK:  if the specified Notebook exists and if it is valid. If the
     *                                          param <code>checkForPublicReadability</code> is <code>true</code> 
     *                                          the specified Notebooks is also public readable</li>
     *                                      <li>Status.NOT_FOUND: if the specified Notebook does not exists</li>
     *                                      <li>Status.FORBIDDEN: if the specified Notebook exists but it is not public readable</li>
     *                                      <li>Status.BAD_REQUEST: if the specified Notebook is not valid</li>
     *                                      <li>Status.INTERNAL_SERVER_ERROR: in case of general internal error</li>
     *                                  </ul>
     */
    public Response.Status checkNotebookID(String notebookID, boolean checkForPublicReadability) {

        int hashNumOfChars = UtilsManager.getInstance().getCurrentHASHLenght();
        if (hashNumOfChars == -1) {
            return Response.Status.INTERNAL_SERVER_ERROR;
        }

        if (StringUtils.isBlank(notebookID) || notebookID.length() < hashNumOfChars) {
            return Response.Status.BAD_REQUEST;
        } else {

            try {
                if (checkForPublicReadability) {
                    // Check if the specified notebook exists and it is public readable
                    return RepositoryManager.getInstance().getCurrentDataRepository()
                            .notebookExistsAndIsPublic(notebookID);
                } else {
                    // Check if the specified notebook exists        
                    return (!RepositoryManager.getInstance().getCurrentDataRepository().notebookExists(notebookID))
                            ? Status.NOT_FOUND
                            : Status.OK;
                }
            } catch (RepositoryException ex) {
                logger.log(Level.SEVERE, null, ex);
                return Response.Status.INTERNAL_SERVER_ERROR;
            }
        }
    }

    /**
     * Check if a specified Notebook is public or private and return a JSON response
     * 
     * @param callback          the JSONP callback or null
     * @param notebookID        the Notebook ID
     * @param accepts           the accepted data forma
     * @param checkForPrivate   If <code>true</code> check if the specified Notebook is private;
     *                          if <code>false</code> check if the specified Notebook is public
     * 
     * @return              This API return a response containing JSON data with the following format:<br/>
     *                      <code>{ "NotebookPublic"|"NotebookPrivate": "0|1" }</code><br/><br/>
     */
    public Response notebookPublicOrPrivate(String callback, String notebookID, String accepts,
            boolean checkForPrivate) {
        Status notebookIDStatus = this.checkNotebookID(notebookID, false);
        if (notebookIDStatus != Status.OK) {
            return Response.status(notebookIDStatus).build();
        }

        try {
            boolean notebookPublic = RepositoryManager.getInstance().getCurrentDataRepository()
                    .isNotebookPublic(notebookID);

            JSONObject jsonData = new JSONObject();
            if (checkForPrivate) {
                jsonData.put(SemlibConstants.JSON_NOTEBOOK_PRIVATE, ((notebookPublic) ? "0" : "1"));
            } else {
                jsonData.put(SemlibConstants.JSON_NOTEBOOK_PUBLIC, ((notebookPublic) ? "1" : "0"));
            }

            String fResponse = jsonData.toString(2);
            String faccepts = MediaType.APPLICATION_JSON;

            return this.createFinalResponseForNotebooksAPI(Response.Status.OK, callback, faccepts, fResponse, null);

        } catch (Exception ex) {
            logger.log(Level.SEVERE, null, ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Get all metadata about a specified Notebook. 
     * Core implementation. This API do not check user rights.     
     * 
     * @param notebookID    the Notebook ID
     * @param callback      the JSONP callback function or null
     * @param accepts       the accepted format (application/rdf+xml or application/json)
     * @return              HTTP Response Status Code:
     *                      <ul>
     *                          <li>"200 OK" and the RDF graph as payload response</li>
     *                          <li>"204 No Content" if there are no triples in the specified notebook</li>
     *                          <li>"400 Bad Request" if the notebook id is not specified or it is incorrect</li>
     *                          <li>"404 Not Found" if the specified notebook does not exists</li>
     *                          <li>"500 Internal Server Error" in case of error</li>
     *                      </ul>
     */
    public Response notebookMetadata(String notebookID, String callback, String accepts) {
        // This is necessary to handle the GET request from normal browser
        UtilsManager utilsManager = UtilsManager.getInstance();
        String cAccepts = utilsManager.getCorrectAcceptValue(callback, accepts);
        String triplesFormat = utilsManager.getCorrectTripleFormat(callback, accepts, cAccepts);

        try {
            String triples = RepositoryManager.getInstance().getCurrentRDFRepository()
                    .getNotebookMetadata(notebookID, triplesFormat);
            if (triples == null || triples.length() == 0) {
                return Response.noContent().build();
            } else {
                return createFinalResponseForNotebooksAPI(Response.Status.OK, callback, cAccepts, triples, null);
            }
        } catch (RepositoryException ex) {
            logger.log(Level.SEVERE, null, ex);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Get all content contained in a specified Notebook
     * 
     * @param notebookId        the Notebook ID
     * @param limit             max number of annotation to retrieve (-1 for no limits)
     * @param offset            offset for annotations retrieval (-1 for no offset)
     * @param orderingMode      ordering mode (1 = desc)
     * @return 
     */
    public Response dumpOfNotebook(String notebookId, String callback, String accepts, String limit, String offset,
            String orderingMode) {
        try {
            String notebookMetadata = RepositoryManager.getInstance().getCurrentRDFRepository()
                    .getNotebookMetadata(notebookId, MediaType.APPLICATION_JSON);
            if (notebookMetadata == null) {
                return Response.status(Status.BAD_REQUEST).build();
            } else if (notebookMetadata.length() == 0) {
                return Response.status(Status.NO_CONTENT).build();
            }

            JSONObject finalJSONData = new JSONObject();
            finalJSONData.put(SemlibConstants.JSON_METADATA, notebookMetadata);

            JSONArray annotationsData = new JSONArray();

            int qLimit = UtilsManager.getInstance().parseLimitOrOffset(limit);
            int qOffset = UtilsManager.getInstance().parseLimitOrOffset(offset);

            boolean desc = false;
            if (orderingMode != null && orderingMode.equals("1")) {
                desc = true;
            }

            List<String> annotationIDs = RepositoryManager.getInstance().getCurrentRDFRepository()
                    .getAnnotationsIDsInNotebook(notebookId, qLimit, qOffset, desc);
            if (annotationIDs.size() > 0) {
                for (String cID : annotationIDs) {
                    String data = this.allAnnotationData(cID);
                    if (StringUtils.isNotBlank(data)) {
                        annotationsData.put(data);
                    }
                }
            }

            finalJSONData.put(SemlibConstants.JSON_ANNOTATIONS, annotationsData);

            // This is necessary to handle the GET request from normal browser
            String cAccepts = UtilsManager.getInstance().getCorrectAcceptValue(callback, accepts);
            return this.createFinalResponseForAnnotationsAPI(callback, cAccepts, finalJSONData.toString(2));

        } catch (JSONException ex) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        } catch (RepositoryException ex) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Get a list of Notebooks basing on NotebookAPI.NotebookListType
     * 
     * @param listType  the Notebooks list type
     * @param callback  the JSONP callback function
     * 
     * @return          a list of Notebook IDs
     */
    public Response getNotebooksList(NotebooksAPI.NotebookListType listType, String callback) {

        User currentLoggedUser = getCurrentLoggedUserOrAnonUser();

        try {
            List<String> notebookIDsList = null;
            if (listType == NotebooksAPI.NotebookListType.ACTIVE_NOTEBOOKS) {
                notebookIDsList = RepositoryManager.getInstance().getCurrentDataRepository()
                        .getActiveNotebooksForCurrentLoggedUser(currentLoggedUser.getUserID());
            } else if (listType == NotebooksAPI.NotebookListType.OWNED_NOTEBOOKS) {
                notebookIDsList = RepositoryManager.getInstance().getCurrentDataRepository()
                        .getNotebooksOwnedByUser(currentLoggedUser.getUserID());
            } else if (listType == NotebooksAPI.NotebookListType.PUBLIC_NOTEBOOKS) {
                notebookIDsList = RepositoryManager.getInstance().getCurrentDataRepository()
                        .getAllPublicNotebooks();
            } else {
                return Response.status(Status.BAD_REQUEST).build();
            }

            if (notebookIDsList == null || notebookIDsList.isEmpty()) {
                return Response.status(Status.NO_CONTENT).build();
            }

            JSONObject jsonData = new JSONObject();
            JSONArray notebookList = new JSONArray(notebookIDsList);

            try {
                jsonData.put(SemlibConstants.JSON_NOTEBOOK_IDS, notebookList);

                String fResponse = jsonData.toString(2);
                String faccepts = MediaType.APPLICATION_JSON;

                return createFinalResponseForNotebooksAPI(Response.Status.OK, callback, faccepts, fResponse, null);

            } catch (JSONException ex) {
                logger.log(Level.SEVERE, null, ex);
                return Response.status(Status.INTERNAL_SERVER_ERROR).build();
            }

        } catch (RepositoryException re) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Get the list of all annotations contained within a Notebook with related metadata. 
     * Core implementation. This API do not check user rights. 
     * 
     * @param notebookID    the Notebook ID
     * @param callback      the JSONP callback function or null     
     * @param accepts       the accepted format (application/rdf+xml or application/json)
     * @param limit         max number of annotation to retrieve (-1 for no limits)
     * @param offset        offset for annotations retrieval (-1 for no offset)
     * @param orderby       property for order by
     * @param orderingMode  (optional) 1/0 (desc/asc) for orderby
     * @return              The list of all annotations contained within a Notebook and related metadata as response payload.
     *                      The returned data format could be: application/rdf+xml or application/json (see the accepts parameter)<br/><br/>
     *                      HTTP Response Status Code:
     *                      <ul>
     *                          <li>"200 OK" in case of succe</li>
     *                          <li>"204 No Content" if there are no triples in the specified notebook</li>
     *                          <li>"400 Bad Request" if the notebook id is not specified or it is incorrect</li>
     *                          <li>"403 Forbidden" if the current logged User has not the correct right to access to the specified Notebook</li>
     *                          <li>"404 Not Found" if the specified notebook does not exists</li>
     *                          <li>"500 Internal Server Error" in case of error</li>
     *                      </ul>
     */
    public Response annotationsListAndMetadata(String notebookID, String callback, String accepts, String orderby,
            String orderingMode, String limit, String offset) {
        int qLimit = UtilsManager.getInstance().parseLimitOrOffset(limit);
        int qOffset = UtilsManager.getInstance().parseLimitOrOffset(offset);
        boolean desc = false;

        if (orderingMode != null && orderingMode.equals("1")) {
            desc = true;
        }

        // This is necessary to handle the GET request from normal browser
        UtilsManager utilsManager = UtilsManager.getInstance();
        String cAccepts = utilsManager.getCorrectAcceptValue(callback, accepts);
        String triplesFormat = utilsManager.getCorrectTripleFormat(callback, accepts, cAccepts);

        Notebook tempNotebook = Notebook.getEmptyNotebookObject();
        tempNotebook.setID(notebookID);

        try {
            String triples = RepositoryManager.getInstance().getCurrentRDFRepository()
                    .getNotebookAnnotationListAndMetadata(tempNotebook, qLimit, qOffset, orderby, desc,
                            triplesFormat);
            if (triples == null || triples.length() == 0) {
                return Response.status(Status.NO_CONTENT).build();
            } else {
                return createFinalResponseForNotebooksAPI(Response.Status.OK, callback, cAccepts, triples, null);
            }
        } catch (RepositoryException ex) {
            logger.log(Level.SEVERE, null, ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Get the RDF graph composed by all the triples of the annotations in the specified notebook. 
     * Core implementation. This API do not check user rights.
     * 
     * @param callback      the JSONP callback function or null
     * @param notebookID    the notebook ID
     * @param accepts       the accepted format (application/rdf+xml or application/json)
     * @return              HTTP Response Status Code:
     *                      <ul>
     *                          <li>"200 OK" and the RDF graph as payload response</li>
     *                          <li>"204 No Content" if there are no triples in the specified notebook</li>
     *                          <li>"400 Bad Request" if the notebook id is not specified or it is incorrect</li>
     *                          <li>"404 Not Found" if the specified notebook does not exists</li>
     *                          <li>"500 Internal Server Error" in case of error</li>
     *                      </ul>
     */
    public Response annotationsTriples(String notebookID, String callback, String accepts) {
        // This is necessary to handle the GET request from normal browser
        UtilsManager utilsManager = UtilsManager.getInstance();
        String cAccepts = utilsManager.getCorrectAcceptValue(callback, accepts);
        String triplesFormat = utilsManager.getCorrectTripleFormat(callback, accepts, cAccepts);

        String triples = RepositoryManager.getInstance().getCurrentRDFRepository()
                .getAllTriplesAnnotations(notebookID, triplesFormat);
        if (StringUtils.isBlank(triples)) {
            return Response.noContent().build();
        } else {
            return createFinalResponseForNotebooksAPI(Response.Status.OK, callback, cAccepts, triples, null);
        }
    }

    /**
     * Create the final response for the client basing on callback and contentType.
     * 
     * @param responseStatus        the response status for the final response
     * @param callback              the JSONP callback function
     * @param contentType           the final Content-Type for this response
     * @param data                  the response data
     * @param createdURI            the URI for the Created response (could be null for other type of response)
     * 
     * @return                      the final Response for the current request
     */
    public Response createFinalResponseForNotebooksAPI(Response.Status responseStatus, String callback,
            String contentType, String data, String createdURI) {

        String fContentType = contentType;
        String fResponse = data;

        if (StringUtils.isNotBlank(callback)) {

            // We have a JSONP request
            fContentType = MediaType.APPLICATION_JAVASCRIPT;
            fResponse = UtilsManager.getInstance().wrapJSONPResponse(fResponse, callback);

        } else if (contentType != null && contentType.contains(MediaType.TEXT_HTML)) {

            // We have a request from a normal browser
            // In this case is correct becouse we have JSON that will be shown as plain text
            fContentType = MediaType.TEXT_PLAIN;

        }

        if (responseStatus == Response.Status.CREATED && (createdURI != null && createdURI.length() > 0)) {
            return Response.created(URI.create(createdURI))
                    .header(SemlibConstants.HTTP_HEADER_CONTENT_TYPE, fContentType).entity(fResponse).build();
        } else if (fContentType != null) {
            return Response.ok(fResponse).header(SemlibConstants.HTTP_HEADER_CONTENT_TYPE, fContentType).build();
        } else {
            return Response.ok(fResponse).build();
        }

    }

    /**
     * Create a new Annotation
     * 
     * @param notebookID            a Notebook ID
     * @param annotationData        the annotation data
     * @param contentType           the content type of the annotation data
     * @param annotationContext     additional annotation contexts
     * 
     * @return                  The ID of the created annotation in JSON format as response payload:<br/>
     *                          <code>{ AnnotationID : { ANNOTATION-ID } }</code><br/>
     *                          HTTP Header:<br/> 
     *                          Location: URI-of-the-new-annotation<br/><br/>
     *                          HTTP Response Status Code:
     *                          <ul>
     *                              <li>"201 Created" on success</li>
     *                              <li>"400 Bad Request" if the request is not correct</li>
     *                              <li>"403 Forbidden" if the current logged User has not the correct rights to access to the specified Notebooks</li>
     *                              <li>"500 Internal Server Error" in case of internal error</li>
     *                          </ul>            
     */
    protected Response createNewAnnotation(String notebookID, String annotationData, String contentType,
            String annotationContext) {

        User currentLoggedUser = this.getCurrentLoggedUserOrAnonUser();

        Status notebookIDStatus = this.checkNotebookID(notebookID, false);
        if (notebookIDStatus != Status.OK) {
            return Response.status(notebookIDStatus).build();
        }

        try {
            PermissionsManager permManager = eu.semlibproject.annotationserver.managers.SecurityManager
                    .getInstance().getPermissionManager();

            boolean canPostNewAnnotation = permManager.canWriteNotebook(currentLoggedUser.getUserID(), notebookID);
            if (!canPostNewAnnotation) {
                return Response.status(Status.FORBIDDEN).build();
            }
        } catch (eu.semlibproject.annotationserver.security.SecurityException se) {
            logger.log(Level.SEVERE, null, se);
            return Response.status(se.getStatusCode()).build();
        }

        // ..everything should be ok so, create the new Annotation
        Annotation newAnnotation = null;
        try {
            newAnnotation = Annotation.createNewAnnotation(currentLoggedUser, annotationData, contentType,
                    notebookID, annotationContext);
        } catch (JSONException ex) {
            logger.log(Level.SEVERE, null, ex);
            return Response.status(Status.BAD_REQUEST).build();
        } catch (UnsupportedEncodingException ex1) {
            logger.log(Level.SEVERE, null, ex1);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        } catch (NoSuchAlgorithmException ex2) {
            logger.log(Level.SEVERE, null, ex2);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        Status response = RepositoryManager.getInstance().writeAnnotation(currentLoggedUser, newAnnotation);
        if (response == Status.BAD_REQUEST) {
            return Response.status(response).build();
        } else if (response != Status.OK) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        // Create JSON response
        JSONObject jsonResponse = new JSONObject();
        try {

            jsonResponse.put(SemlibConstants.JSON_ANNOTATION_ID, newAnnotation.getID());

            String fResponse = jsonResponse.toString();
            String fContentType = MediaType.APPLICATION_JSON;

            return this.createFinalResponseForNotebooksAPI(Response.Status.CREATED, null, fContentType, fResponse,
                    newAnnotation.getURI());

        } catch (JSONException ex) {
            logger.log(Level.SEVERE, ex.getMessage().toString(), ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Prepare a SPARQL query to be executed on the internal SPARQL end-point
     * 
     * @param query             the SPARQL query
     * @param annotationsIDs    the list of all annotations ID in a Notebooks
     * @return                  the prepared query
     */
    public String prepareQueryForNotebooksSPARQLEndPoint(String query, List<String> annotationsIDs) {

        String froms = "";
        String fromNameds = "";

        for (String annID : annotationsIDs) {
            String annotationGraph = Annotation.getGraphURIFromID(annID);
            String itemGraph = Annotation.getItemsGraphURIFormID(annID);
            froms += "FROM <" + annotationGraph + "> FROM <" + itemGraph + "> ";
            fromNameds += "FROM NAMED <" + annotationGraph + "> FROM NAMED <" + itemGraph + "> ";
        }

        String finalFroms = " " + froms + fromNameds;

        // Remove any existing FROM and FROM NAMED from the original query
        int startIndex = -1;
        Pattern regExPattern = Pattern.compile("(FROM <.+>\\s*|FROM NAMED <.+>\\s*)+", Pattern.CASE_INSENSITIVE);
        Matcher matcher = regExPattern.matcher(query);

        if (matcher.find()) {
            startIndex = matcher.start();
            String cleanQuery = matcher.replaceAll("");

            StringBuilder finalQuery = new StringBuilder(cleanQuery);
            finalQuery.insert(startIndex, finalFroms);

            return finalQuery.toString();
        } else {
            int indexOfWhere = query.toLowerCase().indexOf("where");
            StringBuilder strBuilder = new StringBuilder(query);
            strBuilder.insert(indexOfWhere, finalFroms);
            return strBuilder.toString();
        }
    }

    /**
     * Prepare the path for the URL to query the internal SPARQL endpoint
     * 
     * @param query             the query to process
     * @param annotationIDs     a list of Annotation's IDs
     * @param httpRequest       the original HTTP request
     * @return                  the prepared URL query path
     */
    public String prepareParametersForSPARQLEndPointQuery(String query, List<String> annotationIDs,
            HttpServletRequest httpRequest) {
        String finalQuery = this.prepareQueryForNotebooksSPARQLEndPoint(query, annotationIDs);
        String finalParameters = "query=" + finalQuery;
        Enumeration<String> parametersName = httpRequest.getParameterNames();
        while (parametersName.hasMoreElements()) {
            String parameter = parametersName.nextElement();
            if (parameter.equalsIgnoreCase("query")) {
                continue;
            }

            String parameterValue = httpRequest.getParameter(parameter);
            finalParameters += "&" + parameter + "=" + parameterValue;
        }

        return finalParameters;
    }

    /**
     * Execute a given SPARQL query
     * 
     * @param requestParameters all Requeste parameters
     * @param accept            the type of the accepted payload. 
     *                          For this, see the supported payload by the current RDF Storage
     * @return                  a Response with the result of the SPARQL query
     */
    public Response executeQueryOnSPARQLEndPoint(String query, String requestParameters, String accept) {

        ConfigManager configManager = ConfigManager.getInstance();
        String sparqlEndPointURL = RepositoryManager.getInstance().getCurrentRDFRepository().getSPARQLEndPointUrl();

        try {
            // This is necessary to obtain a correct URL escaping! =====
            // For more info, see the Java documentation...
            URL tempUrl = new URL(sparqlEndPointURL);
            java.net.URI uri = new java.net.URI(tempUrl.getProtocol(), tempUrl.getHost() + ":" + tempUrl.getPort(),
                    tempUrl.getPath(), requestParameters, null);
            String queryURL = uri.toString();
            // =========================================================

            GetMethod getRequest = new GetMethod(queryURL);
            if (StringUtils.isNotBlank(accept)) {
                if (accept.contains(MediaType.APPLICATION_RDFJSON)) {
                    Pattern startPattern = Pattern.compile("^\\s*CONSTRUCT.", Pattern.CASE_INSENSITIVE);
                    Matcher nMatcher = startPattern.matcher(query);
                    if (!nMatcher.find()) {
                        // in this case is a select query...JSON/RDF, at the moment, is not supported for tuples
                        return Response.status(Status.BAD_REQUEST).build();
                    }

                    getRequest.addRequestHeader(SemlibConstants.HTTP_HEADER_ACCEPT, MediaType.APPLICATION_RDFXML);
                } else {
                    getRequest.addRequestHeader(SemlibConstants.HTTP_HEADER_ACCEPT, accept);
                }
            }

            if (configManager.useAuthenticationForRepository()) {
                String username = configManager.getUsername();
                String password = configManager.getPassword();
                String encodedData = UtilsManager.getInstance().base64Encode(username + ":" + password);
                getRequest.addRequestHeader("Authorization", "Basic " + encodedData);
            }

            HttpClient httpClient = new HttpClient();

            try {
                int statusCode = httpClient.executeMethod(getRequest);
                if (statusCode != HttpStatus.SC_OK) {
                    // Get the returned payload                    
                    String payloadContent = getRequest.getResponseBodyAsString();

                    ResponseBuilder responseBuilder = Response.status(statusCode);

                    if (StringUtils.isNotBlank(payloadContent)) {
                        responseBuilder.entity(payloadContent);

                        Header CTheader = getRequest.getResponseHeader(SemlibConstants.HTTP_HEADER_CONTENT_TYPE);
                        if (CTheader != null) {
                            String contentTypeValue = CTheader.getValue();
                            if (StringUtils.isNotBlank(contentTypeValue)) {
                                responseBuilder.header(SemlibConstants.HTTP_HEADER_CONTENT_TYPE, contentTypeValue);
                            }
                        }
                    }

                    return responseBuilder.build();
                } else {
                    // Get the returned payload                    
                    String payloadContent = getRequest.getResponseBodyAsString();

                    // Get the Content-Type
                    String contentTypeValue = null;
                    String contentDispositionValue = null;

                    ResponseBuilder responseBuilder = Response.status(statusCode);

                    Header CTheader = getRequest.getResponseHeader(SemlibConstants.HTTP_HEADER_CONTENT_TYPE);
                    Header CDheader = getRequest.getResponseHeader(SemlibConstants.HTTP_HEADER_CONTENT_DISPOSITION);

                    if (CTheader != null) {
                        if (accept.contains(MediaType.APPLICATION_RDFJSON)) {
                            payloadContent = SesameRDFJSONConverter.getInstance().RDFToJson(payloadContent);
                            responseBuilder.header(SemlibConstants.HTTP_HEADER_CONTENT_TYPE,
                                    MediaType.APPLICATION_RDFJSON);
                        } else {
                            contentTypeValue = CTheader.getValue();
                            responseBuilder.header(SemlibConstants.HTTP_HEADER_CONTENT_TYPE, contentTypeValue);
                        }
                    }

                    if (CDheader != null) {
                        contentDispositionValue = CDheader.getValue();
                        responseBuilder.header(SemlibConstants.HTTP_HEADER_CONTENT_DISPOSITION,
                                contentDispositionValue);
                    }

                    return responseBuilder.entity(payloadContent).build();
                }
            } catch (Exception ex) {
                Logger.getLogger(APIHelper.class.getName()).log(Level.SEVERE, null, ex);
                return Response.status(Status.INTERNAL_SERVER_ERROR).build();
            } finally {
                getRequest.releaseConnection();
            }

        } catch (URISyntaxException ex) {
            Logger.getLogger(APIHelper.class.getName()).log(Level.SEVERE, null, ex);
            return Response.status(Status.BAD_REQUEST).build();
        } catch (MalformedURLException ex) {
            Logger.getLogger(APIHelper.class.getName()).log(Level.SEVERE, null, ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    // ==== Annotations API Helpers ================

    /**
     * Get specific data about a specific annotation
     * 
     * @param annotationID          an Annotation ID
     * @param annotationDataType    an AnnotationDataType (@see Annotation.AnnotationDataType)
     * @param dataFormat            the format of the output data           
     * @return                      a String containing specific data about a specified annotation
     * 
     * @throws RepositoryException 
     */
    public String annotationData(String annotationID, Annotation.AnnotationDataType annotationDataType,
            String dataFormat) throws RepositoryException {
        String data = null;
        if (annotationDataType == Annotation.AnnotationDataType.ANNOTATION_METADATA) {
            return RepositoryManager.getInstance().getCurrentRDFRepository().getAnnotationMetadata(annotationID,
                    dataFormat);
        } else if (annotationDataType == Annotation.AnnotationDataType.ANNOTATION_GRAPH) {
            return RepositoryManager.getInstance().getCurrentRDFRepository().getAnnotationTriples(annotationID,
                    dataFormat);
        } else if (annotationDataType == Annotation.AnnotationDataType.ANNOTATION_ITEM) {
            return RepositoryManager.getInstance().getCurrentRDFRepository().getAllAnnotationItems(annotationID,
                    dataFormat);
        }

        return data;
    }

    /**
     * Get all data metadata, graph and items (if they exist) about a specified annotation.
     * The output data format is:<br/>
     * <pre>
     * {
     *      "metadata": {
     *          // JSON/RDF
     *          ... ... ...
     *      },
     *      "graph": {
     *          // JSON/RDF
     *          ... ... ...
     *      },
     *      "itesm": {
     *          // JSON/RDF
     *          ... ... ...
     *      }
     * }
     * </pre>
     * 
     * 
     * @param annotationID      an Annotation ID
     * @return                  all data about a specified annotation
     * 
     * @throws JSONException 
     */
    public String allAnnotationData(String annotationID) throws JSONException {

        String annotationMetadata = null;
        String annotationGraph = null;
        String annotationItems = null;

        try {
            annotationMetadata = this.annotationData(annotationID,
                    Annotation.AnnotationDataType.ANNOTATION_METADATA, MediaType.APPLICATION_JSON);
        } catch (RepositoryException re) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, null, re);
        }

        try {
            annotationGraph = this.annotationData(annotationID, Annotation.AnnotationDataType.ANNOTATION_GRAPH,
                    MediaType.APPLICATION_JSON);
        } catch (RepositoryException re) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, null, re);
        }

        try {
            annotationItems = this.annotationData(annotationID, Annotation.AnnotationDataType.ANNOTATION_ITEM,
                    MediaType.APPLICATION_JSON);
        } catch (RepositoryException re) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, null, re);
        }

        if (StringUtils.isBlank(annotationMetadata) && StringUtils.isBlank(annotationGraph)
                && StringUtils.isBlank(annotationItems)) {
            return null;
        } else {
            JSONObject jsonData = new JSONObject();

            if (StringUtils.isNotBlank(annotationMetadata)) {
                JSONObject jsonMetadata = new JSONObject(annotationMetadata);
                jsonData.put(SemlibConstants.JSON_METADATA, jsonMetadata);
            }

            if (StringUtils.isNotBlank(annotationGraph)) {
                JSONObject jsonGraph = new JSONObject(annotationGraph);
                jsonData.put(SemlibConstants.JSON_GRAPH, jsonGraph);
            }

            if (StringUtils.isNotBlank(annotationItems)) {
                JSONObject jsonItems = new JSONObject(annotationItems);
                jsonData.put(SemlibConstants.JSON_ITEMS, jsonItems);
            }

            return jsonData.toString();
        }
    }

    /**
     * Get all metadata associated to a specified annotation.
     * Core implementation. This API do not check user rights.
     * 
     * @param annotationID  the ID of the Annotation
     * @param callback      the JSONP callback function
     * @param accept        the accepted format
     * 
     * @return all metadata associated to a a specified annotation The returned format 
     *         must be specified using the HTTP header "Accept:".<br/><br/>
     *         HTTP Response Status Code:
     *         <ul>
     *          <li>200 "OK": and the annotation metadata in payload (using the specified format)</li>
     *          <li>400 "Bad Request": if the specified annotation ID is not valid</li>
     *          <li>404 "Not Found": if the speficied annotation ID does not exists</li>
     *          <li>500 "Internal Server Error": in case of error</li>
     *         </ul>
     */
    public Response annotationMetadata(String annotationID, String callback, String accept) {
        // This is necessary to handle the GET request from normal browser
        String cAccepts = UtilsManager.getInstance().getCorrectAcceptValue(callback, accept);

        String annotationMetadata = null;
        try {

            String triplesFormat = cAccepts;
            if (cAccepts.equalsIgnoreCase(MediaType.APPLICATION_JAVASCRIPT)) {
                triplesFormat = MediaType.APPLICATION_JSON;
            }

            annotationMetadata = this.annotationData(annotationID,
                    Annotation.AnnotationDataType.ANNOTATION_METADATA, triplesFormat);

        } catch (Exception ex) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, ex.getMessage().toString(), ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        return createFinalResponseForAnnotationsAPI(callback, cAccepts, annotationMetadata);
    }

    /**
     * Get all the triples associated to a specific annotation.
     * Core implementation. This API do not check user rights.
     * 
     * @param annotationID  the ID of the Annotation
     * @param callback      the JSONP callback function
     * @param accept        the accepted format
     * 
     * @return all triples associated to a specified annotation. The returned format 
     *         must be specified using the HTTP header "Accept:".<br/><br/>
     *         HTTP Response Status Code:
     *         <ul>
     *          <li>200 "OK": and the annotation metadata in payload (using the specified format)</li>
     *          <li>400 "Bad Request": if the specified annotation ID is not valid</li>
     *          <li>404 "Not Found": if the speficied annotation ID does not exists</li>
     *          <li>500 "Internal Server Error": in case of error</li>
     *         </ul>
     */
    public Response annotationContent(String annotationID, String callback, String accept) {
        // This is necessary to handle the GET request from normal browser
        String cAccepts = UtilsManager.getInstance().getCorrectAcceptValue(callback, accept);

        String annotationData = null;
        try {
            String triplesFormat = cAccepts;
            if (cAccepts.equalsIgnoreCase(MediaType.APPLICATION_JAVASCRIPT)) {
                triplesFormat = MediaType.APPLICATION_JSON;
            }

            annotationData = this.annotationData(annotationID, Annotation.AnnotationDataType.ANNOTATION_GRAPH,
                    triplesFormat);

        } catch (Exception ex) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, ex.getMessage().toString(), ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        return createFinalResponseForAnnotationsAPI(callback, cAccepts, annotationData);
    }

    /**
     * Return all Items associated to a given annotation
     * Core implementation. This API do not check user rights.
     * 
     * @param annotationID  a valid annotation ID
     * @param callback      callback function for JSONP 
     * @param accept        the accepted format (n3, application/rdf+xml or application/json)
     * 
     * @return              HTTP Response Status Code:
     *                      <ul>
     *                          <li>"200 OK" annotation metadata</li>
     *                          <li>"204 No Content" if there are no annotation metadata for the specified parameters</li>
     *                          <li>"400 Bad Request" if the specified parameters or the request are incorrect</li>
     *                          <li>"500 Internal Server Error" in case of error</li>
     *                      </ul>
     */
    public Response annotationItems(String annotationID, String callback, String accept) {
        // This is necessary to handle the GET request from normal browser
        String cAccepts = UtilsManager.getInstance().getCorrectAcceptValue(callback, accept);

        String annotationItems = null;
        try {

            String triplesFormat = cAccepts;
            if (cAccepts.equalsIgnoreCase(MediaType.APPLICATION_JAVASCRIPT)) {
                triplesFormat = MediaType.APPLICATION_JSON;
            }

            annotationItems = this.annotationData(annotationID, Annotation.AnnotationDataType.ANNOTATION_ITEM,
                    triplesFormat);

        } catch (Exception ex) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, ex.getMessage().toString(), ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        return createFinalResponseForAnnotationsAPI(callback, cAccepts, annotationItems);
    }

    /**
     * Search for all annotation metadata according to specific parameters in all public Notebooks
     * Core implementation. This API do not check user rights.
     * 
     * @param notebookIDsList   a list of Notebooks ID
     * @param callback          callback function for JSONP
     * @param queryParams       parameters (JSON format), encoded
     * @param cAccept           the accepted format (n3, application/rdf+xml or application/json)
     * @param qLimit            max number of annotation to retrieve
     * @param qOffset           offset for annotations retrieval
     * @param orderby           property for order by
     * @param desc              <code>true</code> descending order
     * 
     * @return              HTTP Response Status Code:
     *                      <ul>
     *                          <li>"200 OK" annotation metadata</li>
     *                          <li>"204 No Content" if there are no annotation metadata for the specified parameters</li>
     *                          <li>"400 Bad Request" if the specified parameters or the request are incorrect</li>     
     *                          <li>"500 Internal Server Error" in case of error</li>
     *                      </ul>
     */
    public Response searchAnnotationMetadata(List<String> notebookIDsList, String queryParams, String callback,
            String cAccepts, int qLimit, int qOffset, String orderby, boolean desc) {
        String annotationMetadata = null;
        try {

            String triplesFormat = cAccepts;
            if (cAccepts.equalsIgnoreCase(MediaType.APPLICATION_JAVASCRIPT)) {
                triplesFormat = MediaType.APPLICATION_JSON;
            }

            annotationMetadata = RepositoryManager.getInstance().getCurrentRDFRepository()
                    .searchMetadataWithParameters(queryParams, qLimit, qOffset, orderby, desc, triplesFormat,
                            notebookIDsList);

        } catch (Exception ex) {
            Logger.getLogger(AnnotationsAPI.class.getName()).log(Level.SEVERE, ex.getMessage().toString(), ex);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        return createFinalResponseForAnnotationsAPI(callback, cAccepts, annotationMetadata);
    }

    /**
     * Get generic data from the RDB given a key
     * 
     * @param key   the key
     * @return  a Response contains the favorites items for the current logged user<br/><br/>
     *          HTTP Response Status Code:
     *          <ul>
     *              <li>"200 Ok" in case of success</li>
     *              <li>"203 Forbidden" if this API is called whit no logged user</li>
     *              <li>"204 No Content" if there are no favorites items for the current logged user</li>
     *              <li>"500 Internal Server Error" for internal errors</li>
     *          </ul>
     */
    public Response getGenericDataByKey(String key) {
        User currentLoggedUser = this.getCurrentLoggedUser();
        if (currentLoggedUser == null || StringUtils.isBlank(currentLoggedUser.getUserID())) {
            return Response.status(Status.FORBIDDEN).build();
        }

        if (StringUtils.isBlank(key)) {
            return Response.status(Status.BAD_REQUEST).build();
        }

        String userID = currentLoggedUser.getUserID();

        try {
            Session hSession = HibernateManager.getSessionFactory().getCurrentSession();
            hSession.beginTransaction();
            Query query = hSession
                    .createQuery("from Userdata as cdata where cdata.userid = :uid and cdata.datakey = :datakey");
            query.setParameter("uid", userID);
            query.setParameter("datakey", key);
            List<Userdata> userdata = query.list();
            hSession.getTransaction().commit();

            if (userdata != null && userdata.size() > 0) {
                Userdata cUserData = userdata.get(0);
                String data = cUserData.getData();
                if (StringUtils.isNotBlank(data)) {
                    return Response.status(Status.OK)
                            .header(SemlibConstants.HTTP_HEADER_CONTENT_TYPE, MediaType.TEXT_PLAIN_UTF8)
                            .entity(data).build();
                } else {
                    return Response.status(Status.NO_CONTENT).build();
                }
            } else {
                return Response.status(Status.NO_CONTENT).build();
            }

        } catch (HibernateException he) {
            Logger.getLogger(ServicesAPI.class.getName()).log(Level.SEVERE, null, he);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Store generic data into the RDB (key => value)
     * 
     * @param key   the key
     * @param data  the data to store
     * @return      HTTP Response Status Code:<br/>
     *              <ul>
     *                  <li>"200 Ok" in case of success</li>
     *                  <li>"203 Forbidden" if this API is called whit no logged user</li>
     *                  <li>"400 Bad Request" if the data or the request is not valid</li>
     *                  <li>"500 Internal Server Error" for internal errors</li>
     *              </ul>
     */
    public Response storeGenericDataByKey(String key, String data) {
        User currentLoggedUser = this.getCurrentLoggedUser();
        if (currentLoggedUser == null || StringUtils.isBlank(currentLoggedUser.getUserID())) {
            return Response.status(Status.FORBIDDEN).build();
        }

        if (StringUtils.isBlank(data) || StringUtils.isBlank(key)) {
            return Response.status(Status.BAD_REQUEST).build();
        }

        String userID = currentLoggedUser.getUserID();

        try {
            Session hSession = HibernateManager.getSessionFactory().getCurrentSession();
            hSession.beginTransaction();

            Query query = hSession
                    .createQuery("update Userdata set data = :data where userid = :uid and datakey = :datakey");
            query.setParameter("data", data);
            query.setParameter("uid", userID);
            query.setParameter("datakey", key);
            int updResults = query.executeUpdate();
            hSession.getTransaction().commit();

            if (updResults <= 0) {
                Userdata userdata = new Userdata();
                userdata.setUserid(userID);
                userdata.setData(data);
                userdata.setDatakey(key);

                Session h2Session = HibernateManager.getSessionFactory().getCurrentSession();

                h2Session.beginTransaction();
                h2Session.save(userdata);
                h2Session.getTransaction().commit();

                int recordId = userdata.getId();
                if (recordId <= 0) {
                    return Response.status(Status.INTERNAL_SERVER_ERROR).build();
                }
            }

            return Response.status(Status.OK).build();

        } catch (HibernateException he) {
            Logger.getLogger(ServicesAPI.class.getName()).log(Level.SEVERE, null, he);
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Check if a specified Annotation ID exists and if it is valid
     * 
     * @param annotationID                  the Annotation ID to check
     * @param checkIfIsInAPublicNotebook    if <code>true</code> check if the specified Annotation is also in a public Notebook
     * 
     * @return              <ul>
     *                          <li>Status.OK: if the specified Annotation exists and if it is valid</li>
     *                          <li>Status.NOT_FOUND: if the specified Annotation does not exists</li>
     *                          <li>Status.FORBIDDEN: if the specified Annotation is not in a public Notebook</li>
     *                          <li>Status.BAD_REQUEST: if the specified Annotation is not valid</li>
     *                          <li>Status.INTERNAL_SERVER_ERROR: in case of general internal error</li>
     *                      </ul>
     */
    public Status checkAnnotationID(String annotationkID, boolean checkIfIsInAPublicNotebook) {
        int hashNumOfChars = UtilsManager.getInstance().getCurrentHASHLenght();
        if (hashNumOfChars == -1) {
            return Status.INTERNAL_SERVER_ERROR;
        }

        if (annotationkID == null || annotationkID.length() < hashNumOfChars) {
            return Status.BAD_REQUEST;
        } else {

            try {
                if (checkIfIsInAPublicNotebook) {
                    return RepositoryManager.getInstance().getCurrentDataRepository()
                            .annotationExistsAndIsInPublicNotebooks(annotationkID);
                } else {
                    return (!RepositoryManager.getInstance().getCurrentDataRepository()
                            .annotationExists(annotationkID)) ? Status.NOT_FOUND : Status.OK;
                }
            } catch (RepositoryException ex) {
                logger.log(Level.SEVERE, null, ex);
                return Status.INTERNAL_SERVER_ERROR;
            }
        }
    }

    /**
     * Create the final response for the current request
     * 
     * @param callback          the callback function to use (jsonp). <code>null</code> for no JSONP response
     * @param contentType       the response Content-Type
     * @param annotationData    the annotation data
     * 
     * @return                  the Response with the annotation data
     */
    public Response createFinalResponseForAnnotationsAPI(String callback, String contentType,
            String annotationData) {

        if (annotationData == null) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        } else {
            if (StringUtils.isBlank(annotationData)) {
                return Response.status(Status.NO_CONTENT).build();
            } else if (annotationData.length() > 0) {
                if (StringUtils.isNotBlank(callback)) {
                    String fResponse = UtilsManager.getInstance().wrapJSONPResponse(annotationData, callback);
                    return Response.ok(fResponse, contentType).build();
                } else {
                    return Response.ok(annotationData, contentType).build();
                }
            } else {
                return Response.status(Status.BAD_REQUEST).build();
            }
        }

    }

}