/** * SiRPACServlet - Simple RDF Parser & Compiler Servlet wrapper * * 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 * * * This servlet is a wrapper for the SiRPAC RDF parser. The servlet * expects the following variables through the POST method: * * RDF - the RDF in an XML syntax * * BAGS - if "on", each Description should have its own Bag; * the default is not to do this. * * STREAM - if "on", the stream mode is turned off so that aboutEach * and aboutEachPrefix are supported. It is off by default. * * 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. * * 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 * * 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.helpers.*; import org.w3c.rdf.model.*; import org.w3c.rdf.syntax.*; import org.w3c.rdf.syntax.RDFConsumer; import org.w3c.rdf.util.xml.DumpConsumer; import org.w3c.rdf.util.xml.ErrorStore; import org.w3c.rdf.util.xml.GenericParser; import org.w3c.rdf.implementation.model.StatementImpl; import org.w3c.rdf.implementation.model.NodeFactoryImpl; import org.w3c.rdf.implementation.syntax.sirpac.*; public class SiRPACServlet extends HttpServlet { final static public String REVISION = "$Id: SiRPACServlet.java,v 1.22 2001/06/08 19:32:47 barstow Exp $"; // The email address for bug reports private static final String MAIL_TO = "barstow@w3.org"; // Names of the POST parameters (described above) and their // defaults private static final String TEXT = "RDF"; private static final String BAG = "BAGS"; private static final String STREAM_MODE = "STREAM"; private static final String SAVE_DOT_FILE = "SAVE_DOT_FILE"; private static final String SAVE_RDF = "SAVE_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 SIRPAC_TMP_DIR = "SIRPAC_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_SiRPACTmpDir = 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 = "sirpac_"; 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 = "SiRPACServlet"; // 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 = "online:"; /* * Create a File object in the m_SiRPACTmpDir 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; } /* * Copy the given string of RDF to a file in the given directory * *@param dir the file's directory *@param rdf the string of RDF *@return void */ 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) { // Just return - not critical 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 generateOutputFile(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) { 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, GraphVizDumpConsumer consumer) { // Look for colors String s; 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 consumer.addGraphAttribute("node [fontname=" + DEFAULT_FONT + ",fontsize=" + fontSize + ",color=" + nodeColor + ",fontcolor=" + nodeTextColor + "]"); // Add an attribute for all of the graph's edges consumer.addGraphAttribute("edge [fontname=" + DEFAULT_FONT + ",fontsize=" + fontSize + ",color=" + edgeColor + ",fontcolor=" + edgeTextColor + "]"); // Add an attribute for the orientation consumer.addGraphAttribute("rankdir=" + orientation + ";"); } /* * Generate a graph of the RDF data model * *@param out the servlet's output stream *@param rdf the RDF text *@param req a Servlet request *@param source the name of input source *@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 *@param createBags the parser should create a bag for each Description *@param streamMode the parser's stream mode setting */ private void generateGraph (ServletOutputStream out, String rdf, HttpServletRequest req, String source, String graphFormat, boolean saveRDF, boolean saveDOTFile, boolean createBags, boolean streamMode) { try { out.println("
"); out.println("

Graph of the data model

"); // Stop if any of the parameters are missing if (m_SiRPACTmpDir == null || m_GraphVizPath == null || m_GraphVizFontDir == null || m_GraphVizLibDir == null) { // Put the paths in a comment in the returned content out.println(""); out.println("Servlet initialization failed. A graph cannot be generated."); return; } // The temporary directory String tmpDir = m_SiRPACTmpDir; // Must generate a unique file name that the DOT consumer // will use File dotFile = createTempFile(tmpDir, TMP_FILE_PREFIX, SUFFIX_DOT); if (dotFile == null) { out.println("Failed to create a temporary DOT file. A graph cannot be generated."); return; } // Create a PrintWriter for the GraphViz consumer FileWriter fw = new FileWriter(dotFile); PrintWriter pw = new PrintWriter(fw); // Run the parser using the DOT consumer to capture // the output in a file StringReader sr = new StringReader (rdf); InputSource is = new InputSource (sr); GraphVizDumpConsumer consumer = new GraphVizDumpConsumer(pw); // Process any graph-related parameters in the request processGraphParameters(req, consumer); // Reinitialize the parser's genid counter so the genids // of the triple will match the genids of the graph SiRPAC sirpac = new SiRPAC(); ErrorStore errorHandler = new ErrorStore(); sirpac.setErrorHandler(errorHandler); sirpac.createBags (createBags); sirpac.setStreamMode (streamMode); sirpac.setSource(source); try { sirpac.parse(is, consumer); } catch (Exception e) { out.println("An attempt to generate the graph data failed (" + e.getMessage() + ")."); pw.close(); dotFile.delete(); return; } // Must close the DOT input file so the GraphViz can // open and read it pw.close(); // Must 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 (!generateOutputFile(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("Get the DOT file.

"); } 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(""); else out.println("The graph image file is empty."); } else { if (outputFile.length() > 0) out.println("Get/view the graph's image file (" + suffix + ").

"); 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: " + 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) { StringBuffer sb = new StringBuffer(""); StringTokenizer st = new StringTokenizer(input, key); // Must handle the case where the input string begins with the key if (input.startsWith(key)) sb = sb.append(replacement); while (st.hasMoreTokens()) { sb = sb.append(st.nextToken()); if (st.hasMoreTokens()) sb = sb.append(replacement); } if (sb.length() >= 1) return sb.toString(); else return input; } /* * Print the document's header info * *@param out the servlet's output stream */ private void printDocumentHeader (ServletOutputStream out) { try { out.println(""); out.println(""); out.println("RDF creation"); out.println(""); out.println(""); out.println(""); } catch (Exception e) { System.err.println("Exception: " + 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("
"); out.println("

The original RDF/XML document

"); out.println("
");

            String s = replaceString(rdf, "<", "<");
            StringTokenizer st = new StringTokenizer(s, "\n");

            // Now output the RDF one line at a time with line numbers
            int lineNum = 1;
            while (st.hasMoreTokens()) {
                if (needCR) 
                    out.print ("" + lineNum +
                               ": " + st.nextToken() + "\n");
                else
                    out.print ("" + lineNum +
                               ": " + st.nextToken());
                lineNum++;
            }

            out.println("
"); } catch (Exception e) { System.err.println("Exception: " + 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("

Triples of the Data Model in N-Triples Format (Sub, Pred, Obj)

"); out.println("
");
            } else {
                out.println("
"); out.println("

Triples of the Data Model

"); out.println("" + "" + "" + "" + "" + ""); } } catch (Exception e) { System.err.println("Exception: " + e.getMessage()); } } /* * Print the footer info for the triple listing * *@param out the servlet's output stream */ private void printTripleTableFooter (ServletOutputStream out, SiRPACServletDumpConsumer consumer, boolean nTriples) { try { if (nTriples) out.println(""); else out.println("
NumberSubjectPredicateObject
"); if (consumer != null) out.println("

The number of triples = " + consumer.getNumStatements() + "

"); } catch (Exception e) { System.err.println("Exception: " + 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 { out.println("
"); out.println("

Feedback

"); out.println("

If you suspect that SiRPAC produced an error, please enter an explanation below and then press the Submit problem report button, to mail the report (and listing) to " + MAIL_TO + "

"); out.println("
"); out.println(""); out.println("

"); // The listing is being passed as a parameter so the '<' // and '"' characters must be replaced with < and ", // respectively if (rdf != null) { String s1 = replaceString(rdf, "<", "<"); String s2 = replaceString(s1, "\"", """); out.println(s2 + "\">"); } out.println(""); out.println("

"); out.println("
"); out.println(""); out.println(""); } catch (Exception e) { System.err.println("Exception: " + e.getMessage()); } } /* * 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("" + "

Servlet Initialization Error

" + "

One or more of the following parameters has not been initialized: " + SIRPAC_TMP_DIR + "," + GRAPH_VIZ_ROOT + "," + GRAPH_VIZ_FONT_DIR + "," + GRAPH_VIZ_LIB_DIR + "," + GRAPH_VIZ_PATH + "." + "

" + ""); } } /* * 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 */ public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ServletOutputStream out = res.getOutputStream (); res.setContentType ("text/html"); out.println ("

GET is NOT supported!

\n\n

Please send RDF through POST!

\n"); } /* * Servlet's doPost method * *@param req the request *@param res the response */ public void doPost (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ServletOutputStream out = res.getOutputStream (); String sRDF = req.getParameter (TEXT); String sBags = req.getParameter (BAG); String sStreamMode = req.getParameter (STREAM_MODE); String sSaveRDF = req.getParameter (SAVE_RDF); String sSaveDOTFile = req.getParameter (SAVE_DOT_FILE); String sURI = req.getParameter (URI); String sFormat = req.getParameter (FORMAT); String sNTriples = req.getParameter (NTRIPLES); SiRPAC sirpac = new SiRPAC(); ErrorStore errorHandler = new ErrorStore(); InputSource is = null; boolean error = false; String sError = null; boolean nTriples = false; SiRPACServletDumpConsumer consumer = new SiRPACServletDumpConsumer(); if (sNTriples != null && sNTriples.equals("on")) { nTriples = true; consumer.setNTriplesOutput(true); } // Initialize the parser sirpac.setErrorHandler(errorHandler); // If a URI was sent in the request, it takes precedence if (sURI != null && sURI.length() >= 1) { try { is = GenericParser.getInputSource(sURI); sRDF = getByteStream(sURI); if (sRDF == null) sError = "An attempt to load the RDF from URI '" + sURI + "' failed. (The file may not exist or the server is down.)"; sirpac.setSource(sURI); } catch (MalformedURLException e) { sError = "An attempt to load URI '" + sURI + "' produced a MalforedURL Exception.\n[" + e.getMessage() + "]"; } catch (IOException e) { sError = "An attempt to load URI '" + sURI + "' produced an IO Exception.\n[" + e.getMessage() + "]"; } } else { StringReader sr = new StringReader (sRDF); is = new InputSource (sr); sirpac.setSource(DEFAULT_NAMESPACE); } if (sError != null) { printDocumentHeader (out); out.println ("

" + sError + "

\n"); printDocumentFooter(out, null); return; } printDocumentHeader (out); printListing (out, sRDF, sURI != null && sURI.length() >= 1); printTripleTableHeader (out, nTriples); try { // Override the default triple output handler consumer.setOutputStream(out); // Create a bag per Description element? [off by default] if (sBags != null && sBags.equals ("on")) sirpac.createBags (true); // Set parser's streaming mode [on by default] if (sStreamMode != null && sStreamMode.equals ("on")) sirpac.setStreamMode (false); sirpac.parse(is, consumer); printTripleTableFooter(out, consumer, nTriples); if (sFormat != null && !sFormat.equals(FORMAT_NO_GRAPH)) { generateGraph(out, sRDF, req, sirpac.source(), sFormat, (sSaveRDF != null) ? true : false, (sSaveDOTFile != null && sSaveDOTFile.equals ("on") ? true : false), (sBags != null && sBags.equals ("on") ? true : false), (sStreamMode != null && sStreamMode.equals ("on") ? false : true)); } } catch (SAXException e) { error = true; } catch (Exception e) { error = true; e.printStackTrace (); } res.setContentType ("text/html"); if (error) { printTripleTableFooter(out, null, nTriples); out.println ("

Errors during parsing

\n"); out.println ("
\n");

            // Make the line number a link to the listing
            out.println ("Fatal error: " + errorHandler.getErrorMessage());
            out.println ("   (Line number = " + "" + 
                         errorHandler.getLineNumber() + "" +
                         ", Column number = " + 
                         errorHandler.getColumnNumber() + ")");

	    out.println ("
\n\n"); } printDocumentFooter(out, sRDF); } }