/**
* ARPServlet - Servlet for the ARP RDF parser
*
* Copyright © World Wide Web Consortium, (Massachusetts Institute of
* Technology, Institut National de Recherche en Informatique et en
* Automatique, Keio University).
*
* All Rights Reserved.
*
* Please see the full Copyright clause at
* <http://www.w3.org/Consortium/Legal/copyright-software.html>
*
* This servlet is a wrapper for the ARP RDF parser. The servlet
* expects the following variables through the POST method:
*
* RDF - the RDF in an XML syntax
*
* SAVE_DOT_FILE - if "on", the DOT file is saved and a link to the
* file is provided to the user
*
* SAVE_RDF - if "on", the RDF will be copied to a file.
*
* EMBEDDED_RDF - if "on", then the RDF is not enclosed in <RDF>...</RDF>
* tags.
*
* URI - the URI to parse [instead of the RDF]; may not be specified
*
* ORIENTATION - the graph's orientation (left to right or top to
* bottom
*
* FONT_SIZE - the font size to use [10, 12, 14, 16 and 20 are supported]
*
* GRAPH_FORMAT - the graph's output format. Supported values are:
*
* GIF_EMBED - embed the graph as a GIF [the default]
* GIF_LINK - don't embed the GIF but create a link for it
* SVG_LINK - create the graph in SVG format and create a link to the file
* PNG_EMBED - create the graph in PNG format and embed the graph in the
* document that is returned
* PNG_LINK - create the graph in PNG format and create a link to the file
* PS_LINK - create a PostScript image of the file and a link to the file
* HP_PCL_LINK - create a HPGL/2 - PCL (Laserwriter) image of the file
* and a link to the file
* HP_GL_LINK - create a HPGL - PCL (pen plotter) image of the file and
* a link to the file
* NO_GRAPH - do not generate a graph
*
* NTRIPLES if "on" the tabular output will be in the NTriples format;
* otherwise a table of Subject, Predicate, Objects will be generated
*
* @author Art Barstow <barstow@w3.org>
*
* The graphics package is AT&T's GraphVis tool.
*/
package org.w3c.rdf.examples;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.StringTokenizer;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;
import org.xml.sax.InputSource;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ErrorHandler;
import org.xml.sax.helpers.*;
import org.apache.regexp.RE;
import com.hp.hpl.jena.rdf.arp.*;
public class ARPServlet extends HttpServlet
{
final static public String REVISION = "$Id: ARPServlet.java,v 1.11 2001/09/11 13:12:44 barstow Exp $";
// The email address for bug reports
private static final String MAIL_TO = "www-rdf-validator@w3.org";
// Names of the POST parameters (described above) and their
// defaults
private static final String TEXT = "RDF";
private static final String SAVE_DOT_FILE = "SAVE_DOT_FILE";
private static final String SAVE_RDF = "SAVE_RDF";
private static final String EMBEDDED_RDF = "EMBEDDED_RDF";
private static final String URI = "URI";
private static final String NTRIPLES = "NTRIPLES";
private static final String NODE_COLOR = "NODE_COLOR";
private static final String DEFAULT_NODE_COLOR = "black";
private static final String NODE_TEXT_COLOR = "NODE_TEXT_COLOR";
private static final String DEFAULT_NODE_TEXT_COLOR = "black";
private static final String EDGE_COLOR = "EDGE_COLOR";
private static final String DEFAULT_EDGE_COLOR = "black";
private static final String EDGE_TEXT_COLOR = "EDGE_TEXT_COLOR";
private static final String DEFAULT_EDGE_TEXT_COLOR = "black";
private static final String ORIENTATION = "ORIENTATION";
private static final String DEFAULT_ORIENTATION = "TB"; // Top to Bottom
private static final String FONT_SIZE = "FONT_SIZE";
private static final String DEFAULT_FONT_SIZE = "10";
private static final String FORMAT = "FORMAT";
private static final String FORMAT_GIF_EMBED = "GIF_EMBED";
private static final String FORMAT_GIF_LINK = "GIF_LINK";
private static final String FORMAT_SVG_LINK = "SVG_LINK";
private static final String FORMAT_PNG_EMBED = "PNG_EMBED";
private static final String FORMAT_PNG_LINK = "PNG_LINK";
private static final String FORMAT_PS_LINK = "PS_LINK";
private static final String FORMAT_HP_PCL_LINK = "HP_PCL_LINK";
private static final String FORMAT_HP_GL_LINK = "HP_GL_LINK";
private static final String FORMAT_NO_GRAPH = "NO_GRAPH";
// Fonts are not currently configurable
private static final String DEFAULT_FONT = "arial";
// Names of the servlet's parameters - for Jigsaw web server
private static final String SERVLET_TMP_DIR = "SERVLET_TMP_DIR";
private static final String GRAPH_VIZ_ROOT = "GRAPH_VIZ_ROOT";
private static final String GRAPH_VIZ_PATH = "GRAPH_VIZ_PATH";
private static final String GRAPH_VIZ_LIB_DIR = "GRAPH_VIZ_LIB_DIR";
private static final String GRAPH_VIZ_FONT_DIR = "GRAPH_VIZ_FONT_DIR";
// Variables for the servlet's parameters
private static String m_ServletTmpDir = null;
private static String m_GraphVizPath = null;
private static String m_GraphVizFontDir = null;
private static String m_GraphVizLibDir = null;
// Names of environment variable need by GraphVis
private static String DOTFONTPATH = "DOTFONTPATH";
private static String LD_LIBRARY_PATH = "LD_LIBRARY_PATH";
// Names used for temporary files
private static final String TMP_FILE_PREFIX = "servlet_";
private static final String SUFFIX_TMP_DIR = ".tmp";
private static final String SUFFIX_DOT = ".dot";
private static final String SUFFIX_RDF = ".rdf";
// Names used for file suffixes and for GraphViz's command line
// option
private static final String NAME_GIF = "gif";
private static final String NAME_HPGL = "hpgl";
private static final String NAME_PCL = "pcl";
private static final String NAME_PNG = "png";
private static final String NAME_PS = "ps";
private static final String NAME_SVG = "svg";
// Default GraphViz parameter names and their default values
// Servlet name
private static final String SERVLET_NAME = "ARPServlet";
// Name for the DOT file title
private static final String DOT_TITLE = "dotfile";
// The string to use for a namespace name when no
// namespace is available - e.g. for the RDF that is
// directly entered into the input form.
private static final String DEFAULT_NAMESPACE = "genid:";
/*
* Create a File object in the m_ServletTmpDir directory
*
*@param directory the file's directory
*@param prefix the file's prefix name (not its directory)
*@param suffix the file's suffix or extension name
*@return a File object if a temporary file is created; null otherwise
*/
private File createTempFile (String directory, String prefix, String suffix)
{
File f;
try {
File d = new File(directory);
f = File.createTempFile(prefix, suffix, d);
} catch (Exception e) {
return null;
}
return f;
}
/*
* Given a URI string, open it, read its contents into a String
* and return the String
*
*@param uri the URI to open
*@return the content at the URI or null if any error occurs
*/
private String getByteStream (String uri)
{
try {
URL url = new URL(uri);
InputStream is = url.openStream();
String s = new String("");
int c;
int numRead = 0;
while ((c = is.read()) != -1) {
s += (char)c;
if (numRead == 15) {
// A server could return content but not the RDF/XML that
// we need. Check the beginning of s and if it looks like
// a generic HTML message, return an error.
if (s.startsWith("<!DOCTYPE"))
return null;
}
numRead++;
}
if (s.equals(""))
// Nothing was returned
return null;
return s;
} catch (Exception e) {
return null;
}
}
/*
* Copy the given string of RDF to a file in the given directory.
* This is only done if the servlet is explictly asked to save
* the RDF to a file.
*
*@param dir the file's directory
*@param rdf the string of RDF
*/
private void copyRDFStringToFile(String tmpDir, String rdf)
{
try {
// Generate a unique file name
File tmpFile = createTempFile(tmpDir, TMP_FILE_PREFIX, SUFFIX_RDF);
if (tmpFile == null) {
// Not really a critical error, just return
return;
}
// Create a PrintWriter for the GraphViz consumer
FileWriter fw = new FileWriter(tmpFile);
PrintWriter pw = new PrintWriter(fw);
pw.println(rdf);
pw.close();
} catch (Exception e) {
System.err.println(SERVLET_NAME + ": error occured trying to save RDF to file '" + tmpDir + TMP_FILE_PREFIX + SUFFIX_RDF + "'.");
return;
}
}
/*
* Given the graph's format option, return either the corresponding
* command line option for that option or the file name suffix for
* the graph option. For example GIF files have ".gif" for its
* suffix and GraphViz uses "-Tgif" for the command line.
*
* NOTE: default is GIF.
*
*@param graphFormat the graph's output format
*@param suffix. If true, the name returned is for the graph's
* file name suffix; otherwise, the name returned is for the
* graph's command line option.
*@return the suffix to use for the graph's output file
*/
private String getFormatName(String graphFormat, boolean suffix) {
String name = (suffix) ? "." : "-T";
if (graphFormat.equals(FORMAT_PNG_EMBED)) return name + NAME_PNG;
if (graphFormat.equals(FORMAT_PNG_LINK)) return name + NAME_PNG;
if (graphFormat.equals(FORMAT_SVG_LINK)) return name + NAME_SVG;
if (graphFormat.equals(FORMAT_PS_LINK)) return name + NAME_PS;
if (graphFormat.equals(FORMAT_HP_GL_LINK)) return name + NAME_HPGL;
if (graphFormat.equals(FORMAT_HP_PCL_LINK)) return name + NAME_PCL;
return name + NAME_GIF;
}
/*
* Invokes the GraphVis program to create a graph image from the
* the given DOT data file
*
*@param dotFileName the name of the DOT data file
*@param outputFileName the name of the output data file
*@return true if success; false if any failure occurs
*/
private boolean generateGraphFile(String dotFileName,
String outputFileName, String graphFormat)
{
String environment[] = {DOTFONTPATH + "=" + m_GraphVizFontDir,
LD_LIBRARY_PATH + "=" + m_GraphVizLibDir};
String formatOption = getFormatName(graphFormat, false);
String cmdArray[] = {m_GraphVizPath, formatOption, "-o", outputFileName, dotFileName};
Runtime rt = Runtime.getRuntime();
try {
Process p = rt.exec(cmdArray, environment);
p.waitFor();
} catch (Exception e) {
System.err.println("Error: generating OutputFile.");
return false;
}
return true;
}
/*
* Returns a parameter from a request or the parameter's default
* value.
*
*@param req a Servlet request
*@return if the request contains the specfied parameter its value
* in the request is returned; otherwise its default value is
* returned
*/
private String getParameter(HttpServletRequest req, String param,
String defString)
{
String s = req.getParameter(param);
return (s == null) ? defString : s;
}
/*
* If the request contains any graph-related parameters, pass them
* to the graph consumer for handling
*
*@param req the response
*@param consumer the GraphViz consumer
*/
private void processGraphParameters (HttpServletRequest req, PrintWriter pw)
{
// Print the graph header
pw.println("digraph " + DOT_TITLE + "{ " );
// Look for colors
String nodeColor = getParameter(req, NODE_COLOR,
DEFAULT_NODE_COLOR);
String nodeTextColor = getParameter(req, NODE_TEXT_COLOR,
DEFAULT_NODE_TEXT_COLOR);
String edgeColor = getParameter(req, EDGE_COLOR,
DEFAULT_EDGE_COLOR);
String edgeTextColor = getParameter(req, EDGE_TEXT_COLOR,
DEFAULT_EDGE_TEXT_COLOR);
String fontSize = getParameter(req, FONT_SIZE,
DEFAULT_FONT_SIZE);
// Orientation must be either
String orientation = req.getParameter (ORIENTATION);
if (orientation.equals("LR"))
orientation = "LR";
else
orientation = DEFAULT_ORIENTATION;
// Add an attribute for all of the graph's nodes
pw.println("node [fontname=" + DEFAULT_FONT +
",fontsize=" + fontSize +
",color=" + nodeColor +
",fontcolor=" + nodeTextColor +
",shape=box];");
// Add an attribute for all of the graph's edges
pw.println("edge [fontname=" + DEFAULT_FONT +
",fontsize=" + fontSize +
",color=" + edgeColor +
",fontcolor=" + edgeTextColor + "];");
// Add an attribute for the orientation
pw.println("rankdir=" + orientation + ";");
}
private static class SaxErrorHandler implements org.xml.sax.ErrorHandler
{
ServletOutputStream out;
boolean silent = false;
String fatalErrors = "";
String errors = "";
String warnings = "";
/*
* Constructuor for a SaxErrorHandler
*
*@param out the servlet's output stream
*@param silent if false, output is suprressed
*/
public SaxErrorHandler(ServletOutputStream out, boolean silent)
{
this.out = out;
this.silent = silent;
}
/*
* Create a formatted string from the exception's message
*
*@param e the SAX Parse Exception
*@return a formatted string
*/
private static String format(org.xml.sax.SAXParseException e)
{
String msg = e.getMessage();
if (msg == null)
msg = e.toString();
return msg + "[Line = " + e.getLineNumber() + ", Column = " + e.getColumnNumber() + "]";
}
/*
* Handle a parse error
*
*@param e the SAX Parse Exception
*/
public void error(org.xml.sax.SAXParseException e)
throws org.xml.sax.SAXException
{
if (this.silent) return;
this.errors += "Error: " + format(e) + "<br />";
}
/*
* Handle a fatal parse error
*
*@param e the SAX Parse Exception
*/
public void fatalError(org.xml.sax.SAXParseException e)
throws org.xml.sax.SAXException
{
if (this.silent) return;
this.fatalErrors += "FatalError: " + format(e) + "<br />";
}
/*
* Handle a parse warning
*
*@param e the SAX Parse Exception
*/
public void warning(org.xml.sax.SAXParseException e)
throws org.xml.sax.SAXException
{
if (this.silent) return;
this.warnings += "Warning: " + format(e) + "<br />";
}
/*
* Return the error messages
*
*@return the error messages or an empty string if there are
* no messages
*/
public String getErrors()
{
return this.errors;
}
/*
* Return the fatal error messages
*
*@return the fatal error messages or an empty string if there are
* no messages
*/
public String getFatalErrors()
{
return this.fatalErrors;
}
/*
* Return the warning messages
*
*@return the warning messages or an empty string if there are
* no messages
*/
public String getWarnings()
{
return this.warnings;
}
}
/*
* Generate a graph of the RDF data model
*
*@param out the servlet's output stream
*@param pw the graph file's PrintWriter
*@param dotFile the File handle for the graph file
*@param rdf the RDF text
*@param req a Servlet request
*@param graphFormat the graph's format
*@param saveRDF the RDF can be cached [saved to the file system]
*@param saveDOTFile the DOT file should be cached
*/
private void generateGraph(ServletOutputStream out, PrintWriter pw,
File dotFile, String rdf, HttpServletRequest req, String graphFormat,
boolean saveRDF, boolean saveDOTFile)
{
try {
out.println("<hr title=\"visualisation\">");
out.println("<h3>Graph of the data model</h3>");
// The temporary directory
String tmpDir = m_ServletTmpDir;
// Add the graph footer
pw.println( " }");
// Close the DOT input file so the GraphViz can
// open and read it
pw.close();
// Generate a unique file name for the output file
// that will be created
String suffix = getFormatName(graphFormat, true);
File outputFile = createTempFile(tmpDir, TMP_FILE_PREFIX, suffix);
if (outputFile == null) {
out.println("Failed to create a temporary file for the graph. A graph cannot be generated.");
dotFile.delete();
return;
}
// Pass the DOT data file to the GraphViz dot program
// so it can create a graph image of the data model
String dotFileName = dotFile.getAbsolutePath();
String outputFileName = outputFile.getAbsolutePath();
if (!generateGraphFile(dotFileName, outputFileName, graphFormat)) {
out.println("An attempt to create a graph failed.");
dotFile.delete();
outputFile.delete();
return;
}
// Handle the DOT file
if (saveDOTFile) {
// Make the DOT file link'able if so requested
String dotPath = SERVLET_NAME + SUFFIX_TMP_DIR +
File.separator + dotFile.getName();
out.println("<a href=\"" + dotPath + "\">Download the DOT file.</a><br /><br />");
}
else {
// Delete it ...
dotFile.delete();
}
// NOTE: Cannot delete the output file here because its
// pathname is returned to the client
String imagePath = SERVLET_NAME + SUFFIX_TMP_DIR + File.separator +
outputFile.getName();
// Handle the embedded image formats first
if (graphFormat.equals(FORMAT_GIF_EMBED) ||
graphFormat.equals(FORMAT_PNG_EMBED)) {
if (outputFile.length() > 0)
out.println("<img src=\"" + imagePath + "\"/>");
else
out.println("The graph image file is empty.");
} else {
if (outputFile.length() > 0)
out.println("<a href=\"" + imagePath + "\">Get/view the graph's image file (" + suffix + ").</a><br /><br />");
else
out.println("The graph image file is empty.");
}
// One last thing to do before exiting - copy the RDF to a file
if (saveRDF)
copyRDFStringToFile(tmpDir, rdf);
} catch (Exception e) {
System.err.println("Exception generating graph: " + e.getMessage());
}
}
/*
* Search the given string for substring "key"
* and if it is found, replace it with string "replacement"
*
*@param input the input string
*@param key the string to search for
*@param replacement the string to replace all occurences of "key"
*@return if no substitutions are done, input is returned; otherwise
* a new string is returned.
*/
public static String replaceString(String input, String key,
String replacement)
{
try {
RE re = new RE(key);
return re.subst(input, replacement);
} catch (Exception e) {
return input;
}
}
/*
* Print the document's header info
*
*@param out the servlet's output stream
*/
private void printDocumentHeader (ServletOutputStream out)
{
try {
out.println( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" +
"<html><head>" +
"<title>RDF Validator</title>" +
"<link href='http://www.w3.org/StyleSheets/base.css' rel='stylesheet' type='text/css'/>" +
"<style type='text/css'>" +
" TD {" +
" background:#EEEEEE;" +
" font-family:'courier new',courier,serif;" +
" }" +
"</style>" +
"</head>" +
"<body>");
} catch (Exception e) {
System.err.println("Exception (printDocumentHeader): " + e.getMessage());
}
}
/*
* Print the rdf listing
*
*@param out the servlet's output stream
*@param rdf the RDF code
*/
private void printListing (ServletOutputStream out, String rdf,
boolean needCR)
{
try {
out.println("<hr title=\"original source\">" +
"<h3>The original RDF/XML document</h3>" +
"<pre>");
String s = replaceString(rdf, "<", "<");
// Now output the RDF one line at a time with line numbers
int lineNum = 1;
int nl = 0;
String terminator = needCR?"\n":"";
do {
String tok;
nl = s.indexOf('\n');
if ( nl == -1 ) {
tok = s;
} else {
tok = s.substring(0,nl);
s = s.substring(nl+1);
}
out.print("<a name=\"" + lineNum + "\">" + lineNum +
"</a>: " + tok + terminator);
lineNum++;
} while ( nl != -1 );
out.println("</pre>");
} catch (Exception e) {
System.err.println("Exception (printListing): " + e.getMessage());
}
}
/*
* Print the header for the triple listing
*
*@param out the servlet's output stream
*/
private void printTripleTableHeader (ServletOutputStream out, boolean nTriples)
{
try {
if (nTriples) {
out.println("<h3>Triples of the Data Model in " +
"<a href=\"http://www.w3.org/2001/sw/RDFCore/ntriples/\">" +
"N-Triples</a> Format (Sub, Pred, Obj)</h3>" +
"<pre>");
} else {
out.println("<hr title=\"triples\">");
out.println("<h3>Triples of the Data Model</h3>");
out.println("<table border><tr>" +
"<td><b>Number</b></td>" +
"<td><b>Subject</b></td>" +
"<td><b>Predicate</b></td>" +
"<td><b>Object</b></td>" +
"</tr>");
}
} catch (Exception e) {
System.err.println("Exception (printTripleTableHeader): " + e.getMessage());
}
}
/*
* Print the footer info for the triple listing
*
*@param out the servlet's output stream
*/
private void printTripleTableFooter (ServletOutputStream out,
boolean nTriples)
{
try {
if (nTriples)
out.println("</pre>");
else
out.println("</table>");
} catch (Exception e) {
System.err.println("Exception (printTripleTableFooter): " + e.getMessage());
}
}
/*
* Print the document's footer info
*
*@param out the servlet's output stream
*@param rdf the RDF code
*/
private void printDocumentFooter (ServletOutputStream out, String rdf)
{
try {
String s;
s = "<hr title=\"Problem reporting\">" +
"<h3>Feedback</h3>" +
"<p>If you suspect the parser is in error, please enter an explanation below and then press the <b>Submit problem report</b> button, to mail the report (and listing) to <i>" + MAIL_TO + "</i></p>" +
"<form enctype='text/plain' method='post' action='mailto:" + MAIL_TO + "'>" +
"<textarea cols='60' rows='4' name='report'></textarea>";
out.println(s);
out.println("<input type='hidden' name='RDF' value=\"<?xml version="1.0">");
// The listing is being passed as a parameter so the '<'
// and '"' characters must be replaced with < and ",
// respectively
if (rdf != null) {
String s1;
s1 = replaceString(rdf, "<", "<");
s1 = replaceString(s1, ">", ">");
s1 = replaceString(s1, "\"", """);
out.println(s1);
}
out.println("\"\\>");
out.println("<input type='submit' value='Submit problem report'\\>" +
"</form></body></html>");
} catch (Exception e) {
System.err.println("Exception (printDocumentFooter): " + e.getMessage());
}
}
/*
* Servlet's get info method
*/
public String getServletInfo () {
return "Servlet wrapper for the ARP RDF parser. This is revision " + REVISION;
}
/*
* Servlet's init method
*
*@param config the servlet's configuration object
*@throws ServletException
*/
public void init(ServletConfig config) throws ServletException
{
super.init (config);
// Cache the parameters
m_ServletTmpDir = config.getInitParameter(SERVLET_TMP_DIR);
// All of the Graph Viz paths extend from GRAPH_VIZ_ROOT
String GraphVizRoot = config.getInitParameter(GRAPH_VIZ_ROOT);
m_GraphVizPath = GraphVizRoot + "/" + config.getInitParameter(GRAPH_VIZ_PATH);
m_GraphVizFontDir = GraphVizRoot + "/" + config.getInitParameter(GRAPH_VIZ_FONT_DIR);
m_GraphVizLibDir = GraphVizRoot + "/" + config.getInitParameter(GRAPH_VIZ_LIB_DIR);
if (m_ServletTmpDir == null || GraphVizRoot == null) {
System.err.println (
"<html>" +
"<h1>Servlet Initialization Error</h1>" +
"<h2>One or more of the following parameters has not been initialized: " +
SERVLET_TMP_DIR + "," + GRAPH_VIZ_ROOT + "," +
GRAPH_VIZ_FONT_DIR + "," + GRAPH_VIZ_LIB_DIR + "," +
GRAPH_VIZ_PATH + "." +
"</h2>" +
"</html>");
}
}
/*
* Servlet's destroy info method
*/
public void destroy () {
super.destroy ();
}
/*
* Servlet's doGet info method - NOT supported
*
*@param req the request
*@param res the response
*@throws ServletException, IOException
*/
public void doGet (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String sRDF = req.getParameter(TEXT);
String sURI = req.getParameter(URI);
if (sURI == null && sRDF == null) {
ServletOutputStream out = res.getOutputStream ();
out.println("<h1>Data Error</h1>" +
"Must specify the RDF (RDF is a string) or the URI parameter." +
"</h1>");
return;
}
process(req, res,
(sRDF != null) ? java.net.URLDecoder.decode(sRDF) : null,
(sURI != null) ? java.net.URLDecoder.decode(sURI) : null);
}
/*
* Servlet's doPost method
*
*@param req the request
*@param res the response
*@throws ServletException, IOException
*/
public void doPost (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String sRDF = req.getParameter (TEXT);
String sURI = req.getParameter (URI);
if (sURI == null && sRDF == null) {
ServletOutputStream out = res.getOutputStream ();
out.println("<h1>Neither the RDF or an URI was specified" +
"and one of them must be specified.</h1>");
return;
}
process(req,res,sRDF, sURI);
}
/*
* Output a Resource in NTriples syntax
*
*@param out the servlet's output stream
*@param r the Resource to output
*/
static private void printResource(ServletOutputStream out, AResource r)
{
try {
if (r.isAnonymous() )
out.print("_:j" + r.getAnonymousID() + " ");
else
out.print("<" + r.getURI() + "> ");
} catch (IOException ioe) {
System.err.println("IOException: printing resource.");
}
}
/*
* Output a Literal in NTriples syntax
*
*@param out the servlet's output stream
*@param l the Literal to output
*/
static private void printNTripleLiteral(ServletOutputStream out, ALiteral l)
{
try {
out.print("\"");
char ar[] = l.toString().toCharArray();
for (int i=0;i<ar.length;i++) {
switch (ar[i]) {
case '\\':
out.print("\\\\");
break;
case '"':
out.print("\\\"");
break;
case '\n':
out.print("\\n");
break;
case '\r':
out.print("\\r");
break;
case '\t':
out.print("\\t");
break;
default:
if ( ar[i] >= 32 && ar[i] <= 255 )
out.print(ar[i]);
}
}
out.print("\" ");
} catch (IOException ioe) {
System.err.println("IOException: printing literal.");
}
}
/*
* Control point for outputing an triple in NTriple syntax
*
*@param out the servlet's output stream
*@param subj the subject
*@param pred the predicate
*@param objRes the object as a Resource (may be null)
*@param objLit the object as a Literal (may be null)
*/
static private void printNTriple(ServletOutputStream out, AResource subj,
AResource pred, AResource objRes, ALiteral objLit)
{
try {
printResource(out, subj);
printResource(out, pred);
if (objRes != null)
printResource(out, objRes);
else
printNTripleLiteral(out, objLit);
out.println(".");
} catch (IOException ioe) {
System.err.println("IOException: printing NTriple.");
}
}
/*
* Create a HTML anchor from the URI or anonNode of the
* given Resource
*
*@param s the string
*@return the string as an HTML anchor
*/
static private String addAnchor(AResource r)
{
if (r.isAnonymous())
return DEFAULT_NAMESPACE + r.getAnonymousID();
else
return "<a href='" + r.getURI() + "'>" + r.getURI() + "</a>";
}
/*
* Output a triple as a row in HTML
*
*@param out the servlet's output stream
*@param subj the subject
*@param pred the predicate
*@param objRes the object as a Resource (may be null)
*@param objLit the object as a Literal (may be null)
*@param num the statement number
*/
static private void printTableRow(ServletOutputStream out, AResource subj,
AResource pred, AResource objRes, ALiteral objLit, int num)
{
try {
out.println("<tr><td>" + num + "</td>");
out.println("<td>" + addAnchor(subj) + "</td>");
out.println("<td>" + addAnchor(pred) + "</td>");
if (objRes != null)
out.println("<td>" + addAnchor(objRes) + "</td>");
else {
out.println("<td>");
String s1 = objLit.toString().trim();
s1 = replaceString(s1, "<", "<");
s1 = replaceString(s1, ">", ">");
out.println(s1);
out.println("</td>");
}
out.println("</tr>");
} catch (IOException ioe) {
System.err.println("IOException: printing TableRow.");
}
}
private static class SH implements StatementHandler
{
ServletOutputStream out;
boolean isNTriples;
int numStatements = 0;
PrintWriter pw;
/*
* Constructuor for the StatementHandler. The primary
* responsiblitly is to cache init variables
*
*@param out the servlet's output stream
*@param isNTriples if true, output using the NTriples
*@param pw the Dot file's PrintWriter
* syntax; otherwise use HTML syntax
*/
public SH(ServletOutputStream out, boolean isNTriples, PrintWriter pw)
{
this.out = out;
this.isNTriples = isNTriples;
this.pw = pw;
}
/*
* Generic handler for a Resource/Resource/Resource triple (S/P/O).
* Dispatches to the methods that do the real work.
*
*@param subj the subject
*@param pred the predicate
*@param obj the object (as a Resource)
*/
public void statement(AResource subj, AResource pred, AResource obj)
{
statementResource(subj, pred, obj);
statementDotResource(subj, pred, obj);
}
/*
* Generic handler for a Resource/Resource/Resource triple (S/P/O).
* Dispatches to the methods that do the real work.
*
*@param subj the subject
*@param pred the predicate
*@param obj the object (as a Literal)
*/
public void statement(AResource subj, AResource pred, ALiteral lit)
{
statementLiteral(subj, pred, lit);
statementDotLiteral(subj, pred, lit);
}
/*
* Handler for a Resource/Resource/Resource triple (S/P/O)
* Outputs the given triple using NTriples or HTML syntax.
*
*@param subj the subject
*@param pred the predicate
*@param obj the object (as a Resource)
*/
public void statementResource(AResource subj, AResource pred, AResource obj)
{
numStatements++;
if (this.isNTriples)
printNTriple(out, subj, pred, obj, null);
else
printTableRow(out, subj, pred, obj, null, this.numStatements);
}
/*
* Handler for a Resource/Resource/Literal triple (S/P/O)
* Outputs the given triple using NTriples or HTML syntax.
*
*@param subj the subject
*@param pred the predicate
*@param obj the object (as a Literal)
*/
public void statementLiteral(AResource subj, AResource pred, ALiteral lit)
{
numStatements++;
if (this.isNTriples)
printNTriple(out, subj, pred, null, lit);
else
printTableRow(out, subj, pred, null, lit, this.numStatements);
}
/*
* Handler for a Resource/Resource/Resource triple (S/P/O).
* Outputs the given triple using Dot syntax.
*
*@param subj the subject
*@param pred the predicate
*@param obj the object (as a Resource)
*/
public void statementDotResource(AResource subj, AResource pred, AResource obj)
{
if (this.pw == null) return;
// Make an ellipse around the Subject
if (subj.isAnonymous()) {
this.pw.println("\"" + DEFAULT_NAMESPACE + subj.getAnonymousID() +
"\" [shape=ellipse];");
this.pw.print("\"" + DEFAULT_NAMESPACE + subj.getAnonymousID());
} else {
this.pw.println("\"" + subj.getURI() +
"\" [shape=ellipse];");
this.pw.print("\"" + subj.getURI());
}
this.pw.print("\" -> ");
if (obj.isAnonymous()) {
this.pw.print("\"" + DEFAULT_NAMESPACE + obj.getAnonymousID());
} else {
this.pw.print("\"" + obj.getURI());
}
this.pw.print("\" [label=\"");
if (pred.isAnonymous()) {
this.pw.print(DEFAULT_NAMESPACE + pred.getAnonymousID());
} else {
this.pw.print(pred.getURI());
}
this.pw.println("\"];");
// Make an ellipse around the Object
if (obj.isAnonymous()) {
this.pw.println("\"" + DEFAULT_NAMESPACE + obj.getAnonymousID() +
"\" [shape=ellipse];");
} else {
this.pw.println("\"" + obj.getURI() +
"\" [shape=ellipse];");
}
}
/*
* Handler for a Resource/Resource/Literal triple (S/P/O).
* Outputs the given triple using Dot syntax.
*
*@param subj the subject
*@param pred the predicate
*@param obj the object (as a Literal)
*/
public void statementDotLiteral(AResource subj, AResource pred, ALiteral lit)
{
if (this.pw == null) return;
if (subj.isAnonymous()) {
this.pw.println("\"" + DEFAULT_NAMESPACE + subj.getAnonymousID() +
"\" [shape=ellipse];");
this.pw.print("\"" + DEFAULT_NAMESPACE + subj.getAnonymousID());
} else {
this.pw.println("\"" + subj.getURI() +
"\" [shape=ellipse];");
this.pw.print("\"" + subj.getURI());
}
/*
if (subj.isAnonymous())
this.pw.print("\"" + DEFAULT_NAMESPACE + subj.getAnonymousID());
else
this.pw.print("\"" + subj.getURI());
*/
/*
* Before outputing the object (Literal) do the following:
*
* o Remove leading whitespace
* o GraphViz/DOT cannot handle embedded line terminators characters
* so they must be replaced with spaces
* o Limit the number of chars to make the graph legible
* o Embedd the literal in quotes
* o Escape double quotes
*/
String s1 = new String(lit.toString());
s1 = s1.replace('\n', ' ');
s1 = s1.replace('\f', ' ');
s1 = s1.replace('\r', ' ');
if (s1.indexOf('"') != -1)
s1 = replaceString(s1, "\"", "\\\"");
// Anything beyond 80 chars makes the graph too large
String tmpObject;
if (s1.length() >= 80)
tmpObject = s1.substring(0, 80) + " ...";
else
tmpObject = s1.substring(0, s1.length());
tmpObject = "\\\"" + tmpObject + "\\\"";
this.pw.print("\" -> \"" + tmpObject);
this.pw.print("\" [label=\"");
if (pred.isAnonymous())
this.pw.print(DEFAULT_NAMESPACE + pred.getAnonymousID());
else
this.pw.print(pred.getURI());
this.pw.println("\" ];");
}
}
private void printErrorMessages(ServletOutputStream out,
SaxErrorHandler eh)
{
try {
String s;
s = eh.getFatalErrors();
if (s != null && s.length() >= 1)
out.println("<h2>Fatal Error Messages</h2>" + s);
s = eh.getErrors();
if (s != null && s.length() >= 1)
out.println("<h2>Error Messages</h2>" + s);
s = eh.getWarnings();
if (s != null && s.length() >= 1)
out.println("<h2>Warning Messages</h2>" + s);
} catch (Exception e) {
System.err.println(SERVLET_NAME + ": Error printing error messages.");
}
}
/*
* Initialize the graph output file. If an error occurs, this
* function will print an error message.
*
*@param out the servlet's output stream
*@req the servlet request object
*@return the File object for the graph file; null if an error occurs
*/
private File initGraphFile(ServletOutputStream out,
HttpServletRequest req)
{
try {
// Stop if any of the parameters are missing
if (m_ServletTmpDir == null ||
m_GraphVizPath == null ||
m_GraphVizFontDir == null ||
m_GraphVizLibDir == null)
{
// Put the paths in a comment in the returned content
out.println("<!-- SERVLET_TMP_DIR = " + m_ServletTmpDir);
out.println("GRAPH_VIZ_PATH = " + m_GraphVizPath);
out.println("GRAPH_LIB_DIR = " + m_GraphVizLibDir);
out.println("GRAPH_FONT_DIR = " + m_GraphVizFontDir + " -->");
out.println("Servlet initialization failed. A graph cannot be generated.");
return null;
}
} catch (Exception e) {
System.err.println("Unable to create a temporary graph file. A graph cannot be generated.");
return null;
}
File dotFile = null;
try {
// Must generate a unique file name that the DOT handler will use
dotFile = createTempFile(m_ServletTmpDir, TMP_FILE_PREFIX, SUFFIX_DOT);
if (dotFile == null) {
out.println("<h1>Failed to create a temporary graph file. A graph cannot be generated.</h1>");
return null;
}
} catch (IOException ioe) {
try {
out.println("<h1>Unable to create a temporary graph file. A graph cannot be generated.</h1>");
} catch (Exception e2) {
System.err.println("Unable to create a temporary graph file. A graph cannot be generated.");
}
}
return dotFile;
}
/*
* Check if the given URI is supported or not
*
*@param out the servlet's output stream
*@param uri the URI to check
*@return true if the URI is supported; false otherwise
*/
private boolean isURISupported(ServletOutputStream out, String uri)
{
try {
if (uri.length() >= 4 && uri.substring(0,4).equalsIgnoreCase("file")) {
out.println("<h1>file URI Schemes are NOT Supported</h1>");
out.println("URIs from the 'file' URI scheme are not supported by this servlet.");
return false;
}
} catch (Exception e) {
System.err.println("Exception in isURISupported.");
return false;
}
return true;
}
/*
* Handle the servlets doGet or doPut request
*
*@param req the servlet's request
*@param res the servlet's response
*@throws SevletException, IOException
*/
private void process(HttpServletRequest req, HttpServletResponse res,
String sRDF, String sURI) throws ServletException, IOException
{
ServletOutputStream out = res.getOutputStream ();
res.setContentType ("text/html");
String sSaveRDF = req.getParameter (SAVE_RDF);
String sSaveDOTFile = req.getParameter (SAVE_DOT_FILE);
String sFormat = req.getParameter (FORMAT);
String sNTriples = req.getParameter (NTRIPLES);
String sEmbedded = req.getParameter (EMBEDDED_RDF);
boolean nTriples = (sNTriples != null) ? true : false;
// ARP parser has embedded = true by default so if user
// wants embedding, must set it to false
boolean embedded = (sEmbedded != null) ? false : true;
String sError = null;
InputStream in = null;
String xmlBase = DEFAULT_NAMESPACE;
printDocumentHeader (out);
if (sURI != null && sURI.length() >= 1) {
// First check for unsupported URIs
if (!isURISupported(out, sURI)) {
printDocumentFooter(out, null);
return;
}
xmlBase = sURI;
try {
File ff = new File(sURI);
in = new FileInputStream(ff);
} catch (Exception ignore) {
try {
URL url;
url=new URL(sURI);
in = url.openStream();
sRDF = getByteStream(sURI);
if (sRDF == null)
sError = "error";
} catch (Exception e) {
sError = "error";
}
}
}
if (sError != null) {
out.println("<h1>RDF Load Error</h1>");
out.println("An attempt to load the RDF from URI '" + sURI + "' failed. (The URI may not exist or the server is down.)");
printDocumentFooter(out, null);
return;
}
PrintWriter pw = null; // The writer for the graph file
File dotFile = null; // The graph file
if (sFormat != null && !sFormat.equals(FORMAT_NO_GRAPH)) {
dotFile = initGraphFile(out, req);
if (dotFile == null)
// Assume error has been reported
return;
// Create a PrintWriter for the DOT handler
FileWriter fw = new FileWriter(dotFile);
if (fw != null)
pw = new PrintWriter(fw);
if (pw != null)
// Add the graph header
processGraphParameters (req, pw);
}
// Create the StatementHandler - it will handle triples for
// the table/ntriples and the graph file
SH sh = new SH(out, nTriples, pw);
// Create the ErrorHandler
SaxErrorHandler errorHandler = new SaxErrorHandler(out, false);
// Create and initialize the parser
ARP parser = new com.hp.hpl.jena.rdf.arp.ARP();
parser.setErrorHandler(errorHandler);
parser.setStatementHandler(sh);
parser.setEmbedding(embedded);
printListing (out, sRDF, sURI != null && sURI.length() >= 1);
printTripleTableHeader (out, nTriples);
try {
if (sURI != null && sURI.length() >= 1) {
parser.load(in, xmlBase);
} else {
StringReader sr = new StringReader (sRDF);
parser.load(sr, xmlBase);
}
} catch (IOException ioe) {
sError = "IOError parsing: " + sURI + ": " + ioe.toString();
} catch (Exception ex) {
sError = "Exception parsing: " + sURI + ": " + ex.toString();
}
printTripleTableFooter(out, nTriples);
printErrorMessages(out, errorHandler);
if (sError != null) {
out.println ("<h1>Parser Loading Error</h1>");
out.println (sError);
printDocumentFooter(out, null);
return;
}
res.flushBuffer();
if (sFormat != null && !sFormat.equals(FORMAT_NO_GRAPH)) {
generateGraph(out, pw, dotFile, sRDF, req, sFormat,
(sSaveRDF != null) ? true : false,
(sSaveDOTFile != null && sSaveDOTFile.equals ("on") ? true : false));
}
if (sURI != null && sURI.length() >= 1)
printDocumentFooter(out, null);
else
printDocumentFooter(out, sRDF);
}
}
Webmaster