/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rdf.providing;
import com.hp.hpl.jena.rdf.model.Model;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.handle.HandleManager;
import org.dspace.rdf.RDFUtil;
import org.dspace.utils.DSpace;
/**
*
* @author Pascal-Nicolas Becker (dspace -at- pascal -hyphen- becker -dot- de)
*/
public class DataProviderServlet extends HttpServlet {
protected static final String DEFAULT_LANG = "TURTLE";
private static final Logger log = Logger.getLogger(DataProviderServlet.class);
/**
* Processes requests for both HTTP
* <code>GET</code> and
* <code>POST</code> methods.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// set all incoming encoding to UTF-8
request.setCharacterEncoding("UTF-8");
// we expect either a path containing only the language information
// or a path in the form /handle/<prefix>/<suffix>[/language].
String lang = this.detectLanguage(request);
String cType = this.detectContentType(request, lang);
String pathInfo = request.getPathInfo();
log.debug("lang = " + lang + ", cType = " + cType + " and pathInfo: " + pathInfo);
if (StringUtils.isEmpty(pathInfo) || StringUtils.countMatches(pathInfo, "/") < 2)
{
String dspaceURI =
(new DSpace()).getConfigurationService().getProperty("dspace.url");
this.serveNamedGraph(dspaceURI, lang, cType, response);
return;
}
// remove trailing slash of the path info and split it.
String[] path = request.getPathInfo().substring(1).split("/");
// if we have 2 slashes or less, we sent repository information (see above)
assert path.length >= 2;
String handle = path[0] + "/" + path[1];
log.debug("Handle: " + handle + ".");
// As we offer a public sparql endpoint, all information that we stored
// in the triplestore is public. It is important to check whether a
// DSpaceObject is readable for a anonym user before storing it in the
// triplestore. It is important to remove DSpaceObjects from the
// triplestore, that gets revoked or become restricted. As this is done
// by RDFizer and RDFUtil we do not have to take care for permissions here!
Context context = null;
DSpaceObject dso = null;
try
{
context = new Context(Context.READ_ONLY);
dso = HandleManager.resolveToObject(context, handle);
}
catch (SQLException ex)
{
log.error("SQLException: " + ex.getMessage(), ex);
context.abort();
// probably a problem with the db connection => send Service Unavailable
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return;
}
catch (IllegalStateException ex)
{
log.error("Cannot resolve handle " + handle
+ ". IllegalStateException:" + ex.getMessage(), ex);
context.abort();
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if (dso == null)
{
log.info("Cannot resolve handle '" + handle + "' to dso. => 404");
context.abort();
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
String identifier = null;
try
{
identifier = RDFUtil.generateIdentifier(context, dso);
}
catch (SQLException ex)
{
log.error("SQLException: " + ex.getMessage(), ex);
context.abort();
// probably a problem with the db connection => send Service Unavailable
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return;
}
if (identifier == null)
{
// cannot generate identifier for dso?!
log.error("Cannot generate identifier for " + dso.getTypeText()
+ " " + dso.getID() + "!");
context.abort();
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
log.debug("Loading and sending named graph " + identifier + ".");
context.abort();
this.serveNamedGraph(identifier, lang, cType, response);
}
protected void serveNamedGraph(String uri, String lang, String contentType,
HttpServletResponse response)
throws ServletException, IOException
{
Model result = null;
result = RDFUtil.loadModel(uri);
if (result == null || result.isEmpty())
{
response.sendError(HttpServletResponse.SC_NOT_FOUND);
if (result != null) result.close();
log.info("Sent 404 Not Found, as the loaded model was null or "
+ "empty (URI: " + uri + ").");
return;
}
response.setContentType(contentType);
PrintWriter out = response.getWriter();
log.debug("Set content-type to " + contentType + ".");
try {
result.write(out, lang);
}
finally
{
result.close();
out.close();
}
}
protected String detectContentType(HttpServletRequest request, String lang)
{
// It is usefull to be able to overwrite the content type, to see the
// request result directly in the browser. If a parameter "text" is part
// of the request, we send the result with the content type "text/plain".
if (request.getParameter("text") != null) return "text/plain;charset=UTF-8";
if (lang.equalsIgnoreCase("TURTLE")) return "text/turtle;charset=UTF-8";
if (lang.equalsIgnoreCase("n3")) return "text/n3;charset=UTF-8";
if (lang.equalsIgnoreCase("RDF/XML")) return "application/rdf+xml;charset=UTF-8";
if (lang.equalsIgnoreCase("N-TRIPLE")) return "application/n-triples;charset=UTF-8";
throw new IllegalStateException("Cannot set content type for unknown language.");
}
protected String detectLanguage(HttpServletRequest request)
{
String pathInfo = request.getPathInfo();
if (StringUtils.isEmpty(pathInfo)) return DEFAULT_LANG;
String[] path = request.getPathInfo().split("/");
String lang = path[(path.length - 1)];
if (StringUtils.endsWithIgnoreCase(lang, "ttl")) return "TURTLE";
if (StringUtils.equalsIgnoreCase(lang, "n3")) return "N3";
if (StringUtils.equalsIgnoreCase(lang, "rdf")
|| StringUtils.equalsIgnoreCase(lang, "xml"))
{
return "RDF/XML";
}
if (StringUtils.endsWithIgnoreCase(lang, "nt")) return "N-TRIPLE";
return DEFAULT_LANG;
}
/**
* Handles the HTTP
* <code>GET</code> method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP
* <code>POST</code> method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
public String getServletInfo() {
return "Serves repository content as rdf serialization (RDF/XML, Turtle, N-Triples and N3).";
}
}