/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.server.access;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.fcrepo.common.Constants;
import org.fcrepo.server.Context;
import org.fcrepo.server.ReadOnlyContext;
import org.fcrepo.server.errors.authorization.AuthzException;
import org.fcrepo.server.errors.servletExceptionExtensions.InternalError500Exception;
import org.fcrepo.server.errors.servletExceptionExtensions.RootException;
import org.fcrepo.server.resourceIndex.ResourceIndex;
import org.fcrepo.server.security.Authorization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.trippi.RDFFormat;
import org.trippi.TripleIterator;
import org.trippi.TriplestoreReader;
import org.trippi.TriplestoreWriter;
import org.trippi.TupleIterator;
import org.trippi.server.TrippiServer;
import org.trippi.server.http.Styler;
/**
* RISearchServlet
*
* @version $Id$
*/
public class RISearchServlet
extends SpringAccessServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger =
LoggerFactory.getLogger(RISearchServlet.class);
private static final String ACTION_LABEL = "Resource Index Search";
private Authorization m_authorization;
private ResourceIndex m_writer;
private TrippiServer m_trippi;
private Styler m_styler;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
m_writer =
m_server
.getBean("org.fcrepo.server.resourceIndex.ResourceIndex", ResourceIndex.class);
m_authorization =
m_server
.getBean("org.fcrepo.server.security.Authorization", Authorization.class);
try {
String indexStylesheetPath = getPath(getIndexStylesheetLocation());
String formStylesheetPath = getPath(getFormStylesheetLocation());
String errorStylesheetPath = getPath(getErrorStylesheetLocation());
m_styler = new Styler(indexStylesheetPath,
formStylesheetPath,
errorStylesheetPath);
} catch (Exception e) {
throw new ServletException("Error loading stylesheet(s)", e);
}
}
private String getPath(String loc) {
if (loc == null) return null;
if (loc.startsWith("/")) {
String foo = getServletContext().getRealPath("/foo");
File dir = new File(foo).getParentFile().getParentFile();
File file = new File(dir, loc);
return file.toString();
} else {
return getServletContext().getRealPath(loc);
}
}
public TrippiServer getTrippiServer() throws ServletException {
if (m_trippi == null){
m_trippi = new TrippiServer(getWriter());
}
return m_trippi;
}
public TriplestoreWriter getWriter() throws ServletException {
if (m_writer == null || m_writer.getIndexLevel() == ResourceIndex.INDEX_LEVEL_OFF) {
throw new ServletException("The Resource Index Module is not "
+ "enabled.");
} else {
return m_writer;
}
}
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
try {
doGet(getTrippiServer(), request, response);
} catch (ServletException e) {
throw e;
} catch (Throwable th) {
try {
response.setContentType("text/html; charset=UTF-8");
response.setStatus(500);
StringWriter sWriter = new StringWriter();
PrintWriter out = new PrintWriter(sWriter);
out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
out.println("<error context=\"" + enc(getContext(request.getContextPath())) + "\">");
out.println("<message>" + enc(getLongestMessage(th, "Error")) + "</message>");
out.print("<detail><![CDATA[");
th.printStackTrace(out);
out.println("]]></detail>");
out.println("</error>");
out.flush();
out.close();
PrintWriter reallyOut = new PrintWriter(
new OutputStreamWriter(
response.getOutputStream(), "UTF-8"));
m_styler.sendError(sWriter.toString(), reallyOut);
reallyOut.flush();
reallyOut.close();
} catch (Exception e2) {
log("Error sending error response to browser.", e2);
throw new ServletException(th);
}
}
}
private String enc(String in) {
StringBuffer out = new StringBuffer();
for (int i = 0; i < in.length(); i++) {
char c = in.charAt(i);
if (c == '<') {
out.append("<");
} else if (c == '>') {
out.append(">");
} else if (c == '\'') {
out.append("'");
} else if (c == '"') {
out.append(""");
} else if (c == '&') {
out.append("&");
} else {
out.append(c);
}
}
return out.toString();
}
private String getLongestMessage(Throwable th, String longestSoFar) {
if (th.getMessage() != null && th.getMessage().length() > longestSoFar.length()) {
longestSoFar = th.getMessage();
}
Throwable cause = th.getCause();
if (cause == null) return longestSoFar;
return getLongestMessage(cause, longestSoFar);
}
private void doGet(TrippiServer server,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("doGet()\n" + " type: "
+ request.getParameter("type") + "\n" + " template: "
+ request.getParameter("template") + "\n" + " lang: "
+ request.getParameter("lang") + "\n" + " query: "
+ request.getParameter("query") + "\n" + " limit: "
+ request.getParameter("limit") + "\n" + " distinct: "
+ request.getParameter("distinct") + "\n" + " format: "
+ request.getParameter("format") + "\n" + " flush: "
+ request.getParameter("flush") + "\n" + " dumbTypes: "
+ request.getParameter("dumbTypes") + "\n");
}
try {
Context context =
ReadOnlyContext.getContext(Constants.HTTP_REQUEST.REST.uri,
request);
m_authorization.enforceRIFindObjects(context);
doSearch(server, request, response);
} catch (AuthzException e) {
logger.error("Authorization failed for request: "
+ request.getRequestURI() + " (actionLabel=" + ACTION_LABEL
+ ")", e);
throw RootException.getServletException(e,
request,
ACTION_LABEL,
new String[0]);
} catch (Throwable th) {
logger.error("Unexpected error servicing API-A request", th);
throw new InternalError500Exception("",
th,
request,
ACTION_LABEL,
"",
new String[0]);
}
}
private void doSearch(TrippiServer server,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (server == null) {
throw new ServletException("No such triplestore.");
}
String type = request.getParameter("type");
String template = request.getParameter("template");
String lang = request.getParameter("lang");
String query = request.getParameter("query");
String limit = request.getParameter("limit");
String distinct = request.getParameter("distinct");
String format = request.getParameter("format");
String dumbTypes = request.getParameter("dt");
String stream = request.getParameter("stream");
boolean streamImmediately = (stream != null) && (stream.toLowerCase().startsWith("t") || stream.toLowerCase().equals("on"));
String flush = request.getParameter("flush");
if (type == null && template == null && lang == null && query == null && limit == null && distinct == null && format == null) {
if (flush == null || flush.equals("")) flush = "false";
boolean doFlush = flush.toLowerCase().startsWith("t");
if (doFlush) {
TriplestoreWriter writer = server.getWriter();
if (writer != null) writer.flushBuffer();
}
response.setContentType("text/html; charset=UTF-8");
doForm(server, new PrintWriter(new OutputStreamWriter(
response.getOutputStream(), "UTF-8")),
request.getRequestURL().toString(),
request.getContextPath());
} else {
doFind(server, type, template, lang, query, limit, distinct, format, dumbTypes, streamImmediately, flush, response);
}
}
private void doForm(TrippiServer server,
PrintWriter out,
String requestURI,
String contextPath)
throws Exception {
try {
StringWriter sWriter = new StringWriter();
PrintWriter sout = new PrintWriter(sWriter);
sout.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
TriplestoreReader reader = server.getReader();
String href = enc(requestURI.replaceAll("/$", ""));
sout.println("<query-service href=\"" + href + "\" context=\"" + enc(getContext(contextPath)) + "\">");
sout.println(" <alias-map>");
Map<String, String> map = reader.getAliasMap();
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
String name = iter.next();
String uri = map.get(name);
sout.println(" <alias name=\"" + name + "\" uri=\"" + enc(uri) + "\"/>");
}
sout.println(" </alias-map>");
sout.println(" <triple-languages>");
String[] langs = reader.listTripleLanguages();
for (int i = 0; i < langs.length; i++) {
sout.println(" <language name=\"" + enc(langs[i]) + "\"/>");
}
sout.println(" </triple-languages>");
langs = reader.listTupleLanguages();
sout.println(" <tuple-languages>");
for (int i = 0; i < langs.length; i++) {
sout.println(" <language name=\"" + enc(langs[i]) + "\"/>");
}
sout.println(" </tuple-languages>");
sout.println(" <triple-output-formats>");
RDFFormat[] formats = TripleIterator.OUTPUT_FORMATS;
for (int i = 0; i < formats.length; i++) {
sout.println(" <format name=\"" + enc(formats[i].getName())
+ "\" encoding=\"" + formats[i].getEncoding()
+ "\" media-type=\"" + formats[i].getMediaType()
+ "\" extension=\"" + formats[i].getExtension() + "\"/>");
}
sout.println(" </triple-output-formats>");
sout.println(" <tuple-output-formats>");
formats = TupleIterator.OUTPUT_FORMATS;
for (int i = 0; i < formats.length; i++) {
sout.println(" <format name=\"" + enc(formats[i].getName())
+ "\" encoding=\"" + formats[i].getEncoding()
+ "\" media-type=\"" + formats[i].getMediaType()
+ "\" extension=\"" + formats[i].getExtension() + "\"/>");
}
sout.println(" </tuple-output-formats>");
sout.println("</query-service>");
sout.flush();
m_styler.sendForm(sWriter.toString(), out);
} finally {
try {
out.flush();
out.close();
} catch (Exception ex) {
log("Error closing response", ex);
}
}
}
public void doFind(TrippiServer server,
String type,
String template,
String lang,
String query,
String limit,
String distinct,
String format,
String dumbTypes,
boolean streamImmediately,
String flush,
HttpServletResponse response) throws Exception {
OutputStream out = null;
File tempFile = null;
try {
if (streamImmediately) {
String mediaType =
TrippiServer.getResponseMediaType(format,
!(type != null && type.equals("triples")),
TrippiServer.getBoolean(dumbTypes, false));
try {
response.setContentType(mediaType + "; charset=UTF-8");
out = response.getOutputStream();
server.find(type, template, lang, query, limit, distinct, format, dumbTypes, flush, out);
} catch (Exception e) {
e.printStackTrace();
throw new ServletException("Error querying", e);
}
} else {
tempFile = File.createTempFile("trippi", "result");
FileOutputStream tempOut = new FileOutputStream(tempFile);
String mediaType = server.find(type, template, lang, query, limit, distinct, format, dumbTypes, flush, tempOut);
tempOut.close();
response.setContentType(mediaType + "; charset=UTF-8");
out = response.getOutputStream();
FileInputStream results = new FileInputStream(tempFile);
sendStream(results, out);
}
} finally {
// make sure the response stream is closed and the tempfile is deld
if (out != null) try { out.close(); } catch (Exception e) { }
if (tempFile != null) tempFile.delete();
}
}
private void sendStream(InputStream in, OutputStream out) throws IOException {
try {
byte[] buf = new byte[4096];
int len;
while ( ( len = in.read( buf ) ) > 0 ) {
out.write( buf, 0, len );
}
} finally {
try {
in.close();
} catch (IOException e) {
log("Could not close result inputstream.");
}
}
}
/** Exactly the same behavior as doGet. */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
private String getIndexStylesheetLocation() {
return "ri/index.xsl";
}
private String getFormStylesheetLocation() {
return "ri/form.xsl";
}
private String getErrorStylesheetLocation() {
return "ri/error.xsl";
}
private String getContext(String origContext) {
return "ri";
}
}