File:  [Public] / java / classes / org / w3c / rdf / examples / SiRPACServlet.java
Revision 1.10: download - view: text, annotated - select for diffs
Tue Oct 17 20:34:46 2000 UTC (23 years, 8 months ago) by barstow
Branches: MAIN
CVS tags: HEAD
barstow: Added support for the POST to include a parameter named
  "STREAM".  If this parameter is set to "off", then the SiRPAC
  parser will be invoked with streaming turned off.  The stream
  mode is on by default but must be turned off to process the
  aboutEach[Prefix] attributes.

/**
 * 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
 * <http://www.w3.org/Consortium/Legal/copyright-software.html>
 *
 * This servlet is a wrapper for the SiRPAC RDF parser.  The servlet 
 * expects the following variables through the POST method:
 *
 * 1. "RDF" - the RDF/XML document 
 * 2. "BAGS" - if "on", each Description should have its own Bag;
 *   the default is not to do this.
 * 3. "STREAM" if "off", the stream mode is turned off so that aboutEach
 *   and aboutEachPrefix are supported.
 *
 * @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.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.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.10 2000/10/17 20:34:46 barstow Exp $";

    // The email address for bug reports
    private static final String MAIL_TO = "barstow@w3.org";

    // Names of the POST parameters
    //   The XML'ized RDF
    private static final String POST_TEXT            = "RDF";
    //   Flag for turning on treating each Description as a bag
    private static final String POST_BAG             = "BAGS";
    //   Flag for turning off SiRPAC's stream parsing mode
    private static final String POST_STREAM_MODE     = "STREAM";
 
    // Names of the servlet's parameters
    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 TMP_DIR_SUFFIX  = ".tmp";
    private static final String DOT_SUFFIX      = ".dot";
    private static final String GIF_SUFFIX      = ".gif";

    // Default GraphViz parameter names and their default values
    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";

    // Fonts are not currently configurable
    private static final String DEFAULT_FONT = "arial";

    // Servlet name
    private static final String SERVLET_NAME = "SiRPACServlet";

    // The parser
    private SiRPAC	  m_sirpac = null;
    // The error handler
    private ErrorStore    m_errorHandler;

    /*
     * 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;
    }

    /*
     * Invokes the GraphVis program to create a GIF image from the
     * the given DOT data file
     *
     *@param dotFileName the name of the DOT data file
     *@param gifFileName the name of the GIF data file 
     *@return true if success; false if any failure occurs
     */
    private boolean generateGifFile(String dotFileName, String gifFileName) {
        String environment[] = {DOTFONTPATH     + "=" + m_GraphVizFontDir,
                                LD_LIBRARY_PATH + "=" + m_GraphVizLibDir};

        String cmdArray[] = {m_GraphVizPath, "-Tgif", "-o", gifFileName, 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("Left to Right"))
            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
     */
    private void generateGraph (ServletOutputStream out, String rdf, HttpServletRequest req) {
        try {
            out.println("<hr title=\"visualisation\">");
            out.println("<h3>Graph of the data model</h3>");

            // 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("<!-- SIRPAC TMP = " + m_SiRPACTmpDir);
                out.println("GRAPH VIZ  = " + m_GraphVizPath);
                out.println("GRAPH LIB  = " + m_GraphVizLibDir);
                out.println("GRAPH FON  = " + m_GraphVizFontDir + " -->");

                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, DOT_SUFFIX);
            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);

	    try {
                m_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 GIF file
            // that will be created
            File gifFile = createTempFile(tmpDir, TMP_FILE_PREFIX, GIF_SUFFIX);
            if (gifFile == null) {
                out.println("Failed to create a temporary GIF file. A graph cannot be generated.");
                dotFile.delete();
                return;
            }

            // Pass the DOT data file to the GraphViz dot program
            // so it can create a GIF image of the data model
            String dotFileName = dotFile.getAbsolutePath();
            String gifFileName = gifFile.getAbsolutePath();

            if (!generateGifFile(dotFileName, gifFileName)) {
                out.println("An attempt to create a graph failed.");
                dotFile.delete();
                gifFile.delete();
                return;
            }

            // Cleanup
            dotFile.delete();

            // NOTE: Cannot delete the GIF file here because its
            // pathname is returned to the client
            String imagePath = SERVLET_NAME +
                               TMP_DIR_SUFFIX + File.separator + 
                               gifFile.getName();

            if (gifFile.length() > 0)
                out.println("<img src=\"" + imagePath + "\"/>");
            else
                out.println("The graph image file is empty.");

        } 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.
     */
    private 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("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"");
            out.println("      \"http://www.w3.org/TR/REC-html40/loose.dtd\">");
            out.println("<HTML><HEAD>");
            out.println("<TITLE>RDF creation</TITLE>");
            out.println("<LINK HREF=\"rdf.css\" REL=\"stylesheet\">");
            out.println("</HEAD>");
            out.println("<BODY>");

        } 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) {
        try {
            out.println("<hr title=\"original source\">");
            out.println("<h3>The original RDF/XML document</h3>");
            out.println("<pre>");

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

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

            out.println("</pre>");
        } 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 printTripleHeader (ServletOutputStream out) {
        try {
            out.println("<hr title=\"triples\">");
            out.println("<h3>Triples of the data model</h3>");
            
            // The output for each triple will be pre-formatted
            out.println("<pre>");
        } 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 printTripleFooter (ServletOutputStream out) {
        try {
            out.println("</pre>");
        } 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("<hr title=\"Problem reporting\">");
            out.println("<h3>Feedback</h3>");
            out.println("<p>If you suspect that SiRPAC produced an 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>");
            out.println("<form enctype=\"text/plain\" method=\"post\" action=\"mailto:" + MAIL_TO + "\">");
            out.println("<textarea cols=\"60\" rows=\"4\" name=\"report\"></textarea>");
            out.println("<p><input type=\"hidden\" name=\"RDF\" value=\"&lt;?xml version=&quot;1.0&quot;?>");

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

            out.println("<input type=\"submit\" value=\"Submit problem report\">");
            out.println("</form>");
            out.println("<hr><a href=\"/description\">Back</a> to main page.");
            out.println("</BODY>");
            out.println("</HTML>");

        } catch (Exception e) {
            System.err.println("Exception: " + e.getMessage());
        }

    }

    /*
     * Servlet's get info method
     */
    public String getServletInfo () {
	return "Servlet Wrapper for SiRPAC. This is revision " + REVISION;
    }

    /*
     * Servlet's init method
     *
     *@param config the servlet's configuration object
     */
    public void init(ServletConfig config) throws ServletException {
	super.init (config);

	m_sirpac = new SiRPAC();
        m_errorHandler = new ErrorStore();
        m_sirpac.setErrorHandler(m_errorHandler);

        // Cache the parameters
        m_SiRPACTmpDir = config.getInitParameter(SIRPAC_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);
    }

    /*
     * 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 ("<h1>GET is NOT supported!</h1>\n\n<p>Please send RDF through POST!</p>\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 (POST_TEXT);
	String		             sBags = req.getParameter (POST_BAG);
	String		             sStreamMode = req.getParameter (POST_STREAM_MODE);
	StringReader	             sr = new StringReader (sRDF);
	InputSource	             is = new InputSource (sr);
        boolean                      error = false;
        SiRPACServletDumpConsumer    consumer = new SiRPACServletDumpConsumer();

        // Re-initialize the parser
	m_sirpac = new SiRPAC();
        m_errorHandler = new ErrorStore();
        m_sirpac.setErrorHandler(m_errorHandler);

        printDocumentHeader (out);
        printListing (out, sRDF);
        printTripleHeader (out);

	try {
            // Override the default triple output handler
            consumer.setOutputStream(out);

            // Toggle Bag handling - always false unless explicitly
            // included in the request
            m_sirpac.createBags (false);
	    if (sBags != null && sBags.equals ("on"))
		m_sirpac.createBags (true);

            // Set parser's streaming mode
	    if (sStreamMode != null && sBags.equals ("off"))
		m_sirpac.setStreamMode (false);

            m_sirpac.parse(is, consumer);

            generateGraph(out, sRDF, req);

	} catch (SAXException e) {
            error = true;
	} catch (Exception e) {
            error = true;
	    e.printStackTrace ();
	}

        printTripleFooter(out);

	res.setContentType ("text/html");

	if (error) {
	    out.println ("<h1>Errors during parsing</h1>\n");
            out.println ("<pre>\n");

            // Make the line number a link to the listing
            out.println ("Fatal error: " + m_errorHandler.getErrorMessage());
            out.println ("   (Line number = " + "<a href=\"#" + 
                         m_errorHandler.getLineNumber() + "\">" + 
                         m_errorHandler.getLineNumber() + "</a>" +
                         ", Column number = " + 
                         m_errorHandler.getColumnNumber() + ")");

	    out.println ("</pre>\n\n");
	}

        printDocumentFooter(out, sRDF);
    }
}

Webmaster