File:  [Public] / java / classes / org / w3c / rdf / examples / SiRPACServlet.java
Revision 1.15: download - view: text, annotated - select for diffs
Wed Nov 1 17:18:58 2000 UTC (23 years, 7 months ago) by barstow
Branches: MAIN
CVS tags: HEAD
barstow: added a call to the parser's setGenindNumber(int) method so
  that the genids in the triple output will be the same as the genids
  in the graph.

/**
 * 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 "on", the stream mode is turned off so that aboutEach
 *   and aboutEachPrefix are supported.
 * 4. "SAVE_RDF" if "on", the RDF will be copied to a file.
 *
 * @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.15 2000/11/01 17:18:58 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";
    //   Flag to indicate that the RDF should be copied to a file
    private static final String POST_SAVE_RDF        = "SAVE_RDF";
 
    // 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";
    private static final String RDF_SUFFIX      = ".rdf";

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


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

    /*
     * 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, boolean saveRDF) {
        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);

            // Reinitialize the parser's genid counter so the genids
            // of the triple will match the genids of the graph
            m_sirpac.setGenidNumber(0);

	    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.");

            // 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("<!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, SiRPACServletDumpConsumer consumer) {
        try {
            out.println("</pre>");
            if (consumer != null)
                out.println("<p>The number of triples = " + consumer.getNumStatements() + "</p>");
        } 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/>");
            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);
	String		             sSaveRDF = req.getParameter (POST_SAVE_RDF);
	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 && sStreamMode.equals ("on"))
		m_sirpac.setStreamMode (false);

            m_sirpac.parse(is, consumer);

            printTripleFooter(out, consumer);

            generateGraph(out, sRDF, req, (sSaveRDF != null) ? true : false);

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


	res.setContentType ("text/html");

	if (error) {
            printTripleFooter(out, null);
	    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