
java.fedora.server.access.TrippiServlet Maven / Gradle / Ivy
Show all versions of fcrepo-client Show documentation
/*
* -----------------------------------------------------------------------------
*
* License and Copyright: The contents of this file are subject to the
* Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of
* the License at
* http://www.fedora-commons.org/licenses.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The entire file consists of original code.
* Copyright © 2008 Fedora Commons, Inc.
*
Copyright © 2002-2007 The Rector and Visitors of the University of
* Virginia and Cornell University
* All rights reserved.
*
* -----------------------------------------------------------------------------
*/
/**
* NOTE: RISearchServlet currently points to this rather
* than the TrippiServlet in Trippi, because in
* this maintenance branch, we don't want to depend
* on an unreleased version of Trippi.
*/
package fedora.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.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.trippi.RDFFormat;
import org.trippi.TripleIterator;
import org.trippi.TriplestoreConnector;
import org.trippi.TriplestoreReader;
import org.trippi.TriplestoreWriter;
import org.trippi.TrippiException;
import org.trippi.TupleIterator;
import org.trippi.config.TrippiConfig;
import org.trippi.config.TrippiProfile;
import org.trippi.server.TrippiServer;
import org.trippi.server.http.Styler;
/**
* A Java servlet that exposes TrippiServer
(s) via HTTP.
*
* The required servlet initialization parameter, configFile specifies
* the full path to the trippi.config file. When responding to a call at the
* base URL of this servlet, all profiles in the configFile will be listed.
* They can be accessed separately at baseURL/profileId.
*
* The optional parameter, profileId, indicates a particular triplestore
* to expose. If specified, this changes the behavior of this servlet
* so that only one triplestore is exposed. It is accessed directly at
* the baseURL of this servlet.
*
* @author [email protected]
*/
public class TrippiServlet
extends HttpServlet {
//////////////////////////////////////////////////////////////////////////
// Used in single-server mode.
//////////////////////////////////////////////////////////////////////////
/**
* The connector to expose.
*/
private TriplestoreConnector m_connector;
/**
* The server instance.
*/
private TrippiServer m_server;
//////////////////////////////////////////////////////////////////////////
// Used in multi-server mode.
//////////////////////////////////////////////////////////////////////////
/**
* Map of TriplestoreConnector
s
* keyed by TrippiProfile
s.
*/
private Map m_connectors;
/**
* Map of TrippiServer
s keyed by profile id.
*/
private Map m_servers;
/**
* Stylesheet transformer for index, form, and error pages.
*/
private Styler m_styler;
//////////////////////////////////////////////////////////////////////////
// Initialization methods
//////////////////////////////////////////////////////////////////////////
public TriplestoreReader getReader() throws ServletException {
return null;
}
public TriplestoreWriter getWriter() throws ServletException {
return null;
}
/**
* Get the single connector that this servlet is configured to expose,
* pre-configured with aliases.
*
* Otherwise, return null.
*/
public TriplestoreConnector getConnector() throws ServletException {
String profileId = getInitParameter("profileId");
if (profileId == null) return null;
String configFile = getInitParameter("configFile");
if (configFile != null && !configFile.equals("")) {
try {
TrippiConfig config = new TrippiConfig(new File(configFile));
TrippiProfile profile = (TrippiProfile) config.getProfiles().get(profileId);
if (profile != null) {
TriplestoreConnector conn = profile.getConnector();
conn.getReader().setAliasMap(config.getAliasMap());
return conn;
} else {
throw new TrippiException("No such profile: " + profileId);
}
} catch (Exception e) {
throw new ServletException("Error initializing in single-server mode.", e);
}
} else {
throw new ServletException("configFile initialization parameter missing.");
}
}
/**
* Get all connectors that this servlet is configured to expose, pre-configured
* with aliases.
*
* This will be a Map, with TrippiProfile
objects as keys.
*/
private Map getConnectors() throws ServletException {
String configFile = getInitParameter("configFile");
if (configFile != null && !configFile.equals("")) {
Map connectors = new HashMap();
try {
TrippiConfig config = new TrippiConfig(new File(configFile));
Map profiles = config.getProfiles();
Iterator iter = profiles.keySet().iterator();
while (iter.hasNext()) {
TrippiProfile profile = (TrippiProfile) profiles.get(iter.next());
TriplestoreConnector conn = profile.getConnector();
conn.getReader().setAliasMap(config.getAliasMap());
connectors.put(profile, conn);
}
return connectors;
} catch (Exception e) {
// clean up any connectors that have been opened
Iterator iter = connectors.keySet().iterator();
while (iter.hasNext()) {
TrippiProfile profile = (TrippiProfile) iter.next();
TriplestoreConnector conn = (TriplestoreConnector) connectors.get(profile);
try {
conn.close();
} catch (TrippiException e2) {
log("Error closing connector", e2);
}
}
throw new ServletException("Error initializing in multi-server mode.", e);
}
} else {
throw new ServletException("configFile initialization parameter missing.");
}
}
/**
* Override this method to return false if the connector(s) should not
* be closed when the servlet is stopped.
*/
public boolean closeOnDestroy() { return true; }
// implementations can return something different... this will affect the
// value of the root "context" element on the error, index, and form pages.
// this value comes in handy for stylesheets that need to make references
// to other things.
public String getContext(String origContext) {
return origContext;
}
// implementations can override to return anything.
// these should be /web/paths/like/this.xsl
public String getErrorStylesheetLocation() {
return getInitParameter("errorStylesheetLocation");
}
public String getIndexStylesheetLocation() {
return getInitParameter("indexStylesheetLocation");
}
public String getFormStylesheetLocation() {
return getInitParameter("formStylesheetLocation");
}
////////////////////////////////////////////////////////////////////////
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);
}
}
/**
* Initialize the servlet.
*/
public final void init() throws ServletException {
// first load the styles
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);
}
// then init whichever kind of accessor we're going to use
TriplestoreWriter writer = getWriter();
if (writer != null) {
m_server = new TrippiServer(writer);
return;
}
TriplestoreReader reader = getReader();
if (reader != null) {
m_server = new TrippiServer(reader);
return;
}
m_connector = getConnector();
if (m_connector != null) {
m_server = new TrippiServer(m_connector);
} else {
m_connectors = getConnectors();
// build the id-to-server map
m_servers = new HashMap();
Iterator iter = m_connectors.keySet().iterator();
while (iter.hasNext()) {
TrippiProfile profile = (TrippiProfile) iter.next();
TriplestoreConnector conn = (TriplestoreConnector) m_connectors.get(profile);
m_servers.put(profile.getId(), new TrippiServer(conn));
}
}
}
/**
* Dispatch the request to the appropriate server.
*
* If no server is specified in the request URI, and the servlet is running
* in multi-server mode, show an index of servers.
*/
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
try {
// first decide which server to use for the response
String profileId = request.getPathInfo();
if (profileId != null) profileId = profileId.replaceAll("/", "");
if (profileId == null || profileId.equals("")) {
if (m_server != null) {
doGet(m_server, request, response);
} else {
response.setContentType("text/html; charset=UTF-8");
doIndex(
new PrintWriter(
new OutputStreamWriter(response.getOutputStream(),
"UTF-8")),
request.getRequestURL().toString(),
request.getContextPath());
}
} else {
if (m_server != null) {
throw new ServletException("Not in multi-server mode.");
} else {
doGet((TrippiServer) m_servers.get(profileId), 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("");
out.println("");
out.println("" + enc(getLongestMessage(th, "Error")) + " ");
out.print(" ");
out.println(" ");
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);
}
public void doGet(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 = m_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);
}
}
public void doIndex(PrintWriter out, String requestURI, String contextPath)
throws Exception {
try {
StringWriter sWriter = new StringWriter();
PrintWriter sout = new PrintWriter(sWriter);
sout.println("");
String href = enc(requestURI.replaceAll("/$", ""));
sout.println("");
Iterator iter = m_connectors.keySet().iterator();
while (iter.hasNext()) {
TrippiProfile profile = (TrippiProfile) iter.next();
sout.println(" ");
Map config = profile.getConfiguration();
Iterator names = config.keySet().iterator();
while (names.hasNext()) {
String name = (String) names.next();
String value = (String) config.get(name);
sout.println(" ");
}
sout.println(" ");
}
sout.println(" ");
sout.flush();
m_styler.sendIndex(sWriter.toString(), out);
} finally {
try {
out.flush();
out.close();
} catch (Exception ex) {
log("Error closing response", ex);
}
}
}
public void doForm(TrippiServer server,
PrintWriter out,
String requestURI,
String contextPath)
throws Exception {
try {
StringWriter sWriter = new StringWriter();
PrintWriter sout = new PrintWriter(sWriter);
sout.println("");
TriplestoreReader reader = server.getReader();
String href = enc(requestURI.replaceAll("/$", ""));
sout.println("");
sout.println(" ");
Map map = reader.getAliasMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
String name = (String) iter.next();
String uri = (String) map.get(name);
sout.println(" ");
}
sout.println(" ");
sout.println(" ");
String[] langs = reader.listTripleLanguages();
for (int i = 0; i < langs.length; i++) {
sout.println(" ");
}
sout.println(" ");
langs = reader.listTupleLanguages();
sout.println(" ");
for (int i = 0; i < langs.length; i++) {
sout.println(" ");
}
sout.println(" ");
sout.println(" ");
RDFFormat[] formats = TripleIterator.OUTPUT_FORMATS;
for (int i = 0; i < formats.length; i++) {
sout.println(" ");
}
sout.println(" ");
sout.println(" ");
formats = TupleIterator.OUTPUT_FORMATS;
for (int i = 0; i < formats.length; i++) {
sout.println(" ");
}
sout.println(" ");
sout.println(" ");
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.");
}
}
}
/**
* Close the connector instance when cleaning up if closeOnDestroy().
*/
public void destroy() {
if (closeOnDestroy()) {
if (m_connector != null) {
// single-server mode
try {
m_connector.close();
} catch (Exception e) {
log("Error closing connector", e);
}
} else if (m_connectors != null) {
// multi-server mode
Iterator iter = m_connectors.values().iterator();
while (iter.hasNext()) {
TriplestoreConnector conn = (TriplestoreConnector) iter.next();
try {
conn.close();
} catch (Exception e) {
log("Error closing connector", e);
}
}
}
}
}
/** Exactly the same behavior as doGet. */
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}