de.fuberlin.wiwiss.marbles.MarblesServlet.java Source code

Java tutorial

Introduction

Here is the source code for de.fuberlin.wiwiss.marbles.MarblesServlet.java

Source

/*
 *   Copyright (c) 2009, MediaEvent Services GmbH & Co. KG
 *   http://mediaeventservices.com
 *   
 *   This file is part of Marbles.
 *
 *   Marbles is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Marbles is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Marbles.  If not, see <http://www.gnu.org/licenses/>.
 *   
 */
package de.fuberlin.wiwiss.marbles;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.httpclient.URIException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.query.GraphQuery;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.Query;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.resultio.sparqlxml.SPARQLResultsXMLWriter;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.rdfxml.RDFXMLWriter;
import org.openrdf.sail.SailException;
import org.openrdf.sail.helpers.SailBase;
import org.openrdf.sail.memory.MemoryStore;
import org.openrdf.sail.nativerdf.NativeStore;
import org.openrdf.sail.rdbms.mysql.MySqlStore;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import de.fuberlin.wiwiss.marbles.dataproviders.DataProvider;
import de.fuberlin.wiwiss.marbles.loading.CacheController;
import de.fuberlin.wiwiss.marbles.loading.ContentTypes;
import de.fuberlin.wiwiss.marbles.loading.SemanticWebClient;
import de.fuberlin.wiwiss.marbles.loading.SpongerProvider;

import edu.mit.simile.fresnel.configuration.Configuration;
import edu.mit.simile.fresnel.configuration.NoResultsException;
import edu.mit.simile.fresnel.purpose.Purpose;
import edu.mit.simile.fresnel.results.Selection;

/**
 * Implements REST services to view, discover, load and clear URIs/URLs, as well as a SPARQL endpoint 
 * 
 * @author Christian Becker
 *
 * @web.servlet
 *   name="MarblesServlet"
 *   display-name="MarblesServlet" 
 *
 * @web.servlet-mapping
 *   url-pattern="/marbles"
 *  
 */
public class MarblesServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    static final long serialVersionUID = 1L;

    /* Parameters read from web.xml */

    /**
     * Path to data directory with subdirectories for fresnel configuration ("fresnel"),
     * built-in ontologies ("ontologies") and XSLT transformations ("xsl"), absolute or relative to the Marbles app directory
     */
    private String dataRoot;

    /**
     * Full HTTP path to assets in the "web" directory
     */
    private URL assetsURL = null;

    /**
     * HTTP URL of this service
     */
    private URL serviceURL = null;

    /**
     * The directory that contains the fresnel configuration
     */
    private final String fresnelDirectory = "fresnel";

    /**
      * The directory that contains ontologies to load
      */
    private final String ontologiesDirectory = "ontologies";

    /**
     * The directory that contains the XSLT stylesheet
     */
    private final String xslDirectory = "xsl";

    /**
     * The filename of the XSLT stylesheet to use
     */
    private final String xslTransformation = "to-xhtml.xsl";

    private Logger logger;

    private CacheController cacheController;
    private SemanticWebClient semwebClient;

    SailRepository confRepository = null, ontoRepository = null, dataRepository = null, metaDataRepository = null;
    SameAsInferencer sameAsInferencer = null;
    ValueFactory valueFactory = null;

    /**
     * Converts a relative path to an absolute, if necessary
     * @param   directory
     * @returns   absolutized path, or null if invalid
     */
    private String getAbsolutePath(String directory, ServletContext context) {
        if (directory != null && !new File(directory).isDirectory()
                && new File(context.getRealPath(directory)).isDirectory())
            return context.getRealPath(directory);
        else
            return directory;
    }

    /**
     * Reads servlet configuration, initializes Sesame repositories and loads ontologies
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        ServletContext context = config.getServletContext();

        dataRoot = getAbsolutePath(config.getInitParameter("dataRoot"), context);

        if (!(dataRoot != null && new File(dataRoot).isDirectory()))
            throw new ServletException("Invalid dataRoot " + (dataRoot == null ? "(null)" : dataRoot));

        try {
            if (config.getInitParameter("assetsURL") != null)
                assetsURL = new URL(config.getInitParameter("assetsURL"));
        } catch (Exception e) {
            throw new ServletException("Invalid assetsURL " + config.getInitParameter("assetsURL"));
        }

        try {
            if (config.getInitParameter("serviceURL") != null)
                serviceURL = new URL(config.getInitParameter("serviceURL"));
        } catch (Exception e) {
            throw new ServletException("Invalid serviceURL " + config.getInitParameter("serviceURL"));
        }

        org.apache.log4j.BasicConfigurator.configure();

        boolean enableDebugging = (config.getInitParameter("enableDebugging") != null
                && config.getInitParameter("enableDebugging").equalsIgnoreCase("true"));

        Logger.getRootLogger().setLevel(enableDebugging ? Level.DEBUG : Level.WARN);

        /* Set up HTTP Client logging */
        // Commented out at this should be configured on the server level      
        //      System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Log4JLogger");
        //      System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
        //      System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire.header", "debug");
        //      System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");      

        /* Set up Sesame MySQL RDBMS */
        try {
            confRepository = new SailRepository(new MemoryStore());
            confRepository.initialize();

            ontoRepository = new SailRepository(
                    new NativeStore(new File(getAbsolutePath(config.getInitParameter("ontologyStore"), context))));
            ontoRepository.initialize();

            SailBase baseStore = null;

            if (config.getInitParameter("mysqlDb") != null && config.getInitParameter("mysqlServer") != null
                    && config.getInitParameter("mysqlUser") != null
                    && config.getInitParameter("mysqlPass") != null) {
                MySqlStore myStore = new MySqlStore(config.getInitParameter("mysqlDb"));
                myStore.setServerName(config.getInitParameter("mysqlServer"));
                myStore.setUser(config.getInitParameter("mysqlUser"));
                myStore.setPassword(config.getInitParameter("mysqlPass"));
                myStore.setMaxNumberOfTripleTables(16);
                //myStore.setIndexed(true);
                baseStore = myStore;
            } else {
                baseStore = new NativeStore(
                        new File(getAbsolutePath(config.getInitParameter("cacheStore"), context)));
            }

            /* SameAsInferencer requires an InferencerConnection, which is provided by the native store */
            sameAsInferencer = new SameAsInferencer(baseStore);
            sameAsInferencer.setAutoInference(false);

            dataRepository = new SailRepository(sameAsInferencer);
            dataRepository.initialize();

            metaDataRepository = new SailRepository(
                    new NativeStore(new File(getAbsolutePath(config.getInitParameter("metadataStore"), context))));
            metaDataRepository.initialize();

            RepositoryConnection ontoConn = ontoRepository.getConnection();
            ValueFactory ontoValueFactory = ontoRepository.getValueFactory();

            /* Load ontologies */
            File ontoDir = new File(dataRoot + "/" + ontologiesDirectory);
            for (File f : ontoDir.listFiles(new RDFFilenameFilter())) {
                URI ontoResource = ontoValueFactory.createURI("file://" + f.getName());

                /* Only load new ontologies */
                if (!ontoConn.hasStatement(null, null, null, false, ontoResource)) {
                    try {
                        loadTriples(ontoRepository, f, null /* parameters */, ontoResource);
                    } catch (Exception e) {
                        System.err.println("Error loading " + f.getName());
                        e.printStackTrace();
                    }
                }
            }
            ontoConn.close();

            valueFactory = dataRepository.getValueFactory();

            /* Set up external data providers */
            ArrayList<DataProvider> dataProviders = new ArrayList<DataProvider>();
            if (config.getInitParameter("dataProviders") != null) {
                String[] providers = config.getInitParameter("dataProviders").split(",");
                for (String dsName : providers) {
                    try {
                        DataProvider d = (DataProvider) (this.getClass().getClassLoader().loadClass(dsName))
                                .newInstance();
                        dataProviders.add(d);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }

            /* Set up Virtuoso Sponger */
            SpongerProvider spongerProvider = null;
            if (config.getInitParameter("spongerServiceURL") != null) {
                spongerProvider = new SpongerProvider(config.getInitParameter("spongerServiceURL"));
            }

            cacheController = new CacheController(dataRepository, metaDataRepository);
            semwebClient = new SemanticWebClient(cacheController, spongerProvider, dataProviders);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Shuts down repositories
     */
    @Override
    public void destroy() {
        try {
            if (dataRepository != null)
                dataRepository.shutDown();

            if (metaDataRepository != null)
                metaDataRepository.shutDown();

            if (ontoRepository != null)
                ontoRepository.shutDown();

            if (confRepository != null)
                confRepository.shutDown();
        } catch (RepositoryException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        super.destroy();
    }

    /**
     * Loads fresnel configuration, substituting the "##lang##"
     * parameter for a specified language
     * 
     * @param language   Language tag to use, e.g. "en" or "de"
     * @throws Exception
     */
    private void loadFresnelConfig(String language) throws Exception {
        RepositoryConnection configConn = confRepository.getConnection();
        configConn.remove((Resource) null, null, null);
        configConn.commit();
        configConn.close();

        HashMap<String, String> parameters = new HashMap<String, String>();
        parameters.put("lang", language);

        File fresnelDir = new File(dataRoot + "/" + fresnelDirectory);
        for (File f : fresnelDir.listFiles(new RDFFilenameFilter())) {
            loadTriples(confRepository, f, parameters);
        }
    }

    /**
     * Handles HTTP GET and POST requests
     *  
     * @param request
     * @param response
     * @throws IOException
     */
    protected void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");

        /* Try to prevent spidering */
        response.setHeader("X-Robots-Tag", "nofollow");
        OutputStream outputStream = response.getOutputStream();

        /* Call REST services */
        if (request.getParameter("do") != null) {
            if (request.getParameter("do").equals("clear") && request.getParameter("url") != null) {
                clearData(request.getParameter("url"), outputStream);
            } else if (request.getParameter("do").equals("load") && request.getParameter("url") != null) {
                loadData(request.getParameter("url"), outputStream);
            } else if (request.getParameter("do").equals("discover") && request.getParameter("uri") != null) {
                discoverResource(request.getParameter("uri"), outputStream);
            }
        } else if (request.getParameter("query") != null) {
            sparqlQuery(request, response,
                    outputStream); /* recognize SPARQL queries using the <code>query</code> parameter */
        } else
            fresnelView(request, response); /* default to view generation */
    }

    /**
     * Removes an URL from the cache (<code>clear</code>) 
     * @param url   The URL to remove
     * @param outputStream   Output stream to communicate the result to
     */
    private void clearData(String url, OutputStream outputStream) {
        cacheController.removeData(url);
        PrintWriter writer = new PrintWriter(outputStream);
        writer.print("ok");
        writer.close();
    }

    /**
     * (Re-)loads an URL into the cache (<code>load</code>) 
     * @param url   The URL to load
     * @param outputStream   Output stream to communicate the result to
     */
    private void loadData(String url, OutputStream outputStream) {
        try {
            semwebClient.loadURL(new org.apache.commons.httpclient.URI(url, true), true /* wait */);
        } catch (URIException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }

        PrintWriter writer = new PrintWriter(outputStream);
        writer.print("ok");
        writer.close();
    }

    /**
     * Tries to dereference the URI and queries data providers for data;
     * then follows known predicates (<code>discover</code>)
     *  
     * @param uri   The resource of interest 
     * @param outputStream   Output stream to communicate the result to
     */
    public void discoverResource(String uri, OutputStream outputStream) {
        Resource mainResource = valueFactory.createURI(uri);
        List<org.apache.commons.httpclient.URI> retrievedURLs = semwebClient.discoverResource(mainResource,
                false /* don't wait */);
        try {
            sameAsInferencer.addInferredForResource(mainResource);
        } catch (SailException e) {
            e.printStackTrace();
        } catch (RepositoryException e) {
            e.printStackTrace();
        }

        /* List retrieved URLs for debugging purposes */
        PrintWriter writer = new PrintWriter(outputStream);
        if (retrievedURLs != null) {
            for (org.apache.commons.httpclient.URI url : retrievedURLs) {
                writer.println(url.toString() + "<br/>");
            }
        }
        writer.close();
    }

    /**
     * Handles a SPARQL query. 
     * The endpoint supports the <code>SELECT</code>, <code>CONSTRUCT</code> and
     * <code>DESCRIBE</code> query forms; triple serialization is limited to the <code>RDF/XML<code> format.
     * The indication of graphs using the <code>defaultgraphuri</code> and <code>namedgraphuri</code>
     * elements is not supported; however graphs may be indicated in the query text using
     * <code>FROM</code> and <code>FROM NAMED</code> keywords
     * 
     * @param request
     * @param response
     * @param outputStream
     */
    private void sparqlQuery(HttpServletRequest request, HttpServletResponse response, OutputStream outputStream) {
        RepositoryConnection conn = null;
        String query = request.getParameter("query");

        try {
            conn = dataRepository.getConnection();
            Query q = conn.prepareQuery(QueryLanguage.SPARQL, query);

            if (q instanceof TupleQuery) {
                /* <code>SELECT</code> form */
                response.setContentType("application/sparql-results+xml");
                SPARQLResultsXMLWriter sparqlWriter = new SPARQLResultsXMLWriter(outputStream);
                ((TupleQuery) q).evaluate(sparqlWriter);
            } else if (q instanceof GraphQuery) {
                /* <code>CONSTRUCT</code> and <code>DESCRIBE</code> forms */
                response.setContentType("application/rdf+xml");
                RDFXMLWriterUnique rdfXmlWriter = new RDFXMLWriterUnique(outputStream);
                ((GraphQuery) q).evaluate(rdfXmlWriter);
            }
        } catch (MalformedQueryException e) {
            /* Report errors using HTTP 400 Bad Request and provide error details */
            //response.setStatus(400);
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.write("Unable to parse query: " + e.getMessage());
            printWriter.close();
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null)
                try {
                    conn.close();
                } catch (RepositoryException e) {
                    e.printStackTrace();
                }
        }
    }

    /**
     * Discovers an URI and renders a given view for it (<code>view</code>).
     *
     * @param request
     * @param response
     * @throws IOException
     */
    public void fresnelView(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8"));
        String errorString = null;
        Selection selected = null;
        List<org.apache.commons.httpclient.URI> retrievedURLs = null;
        Resource focalResource = null;
        Configuration conf = null;
        String redirectLocation = null;

        try {
            /* Reload the Fresnel configuration using the provided language */
            String langPref = (request.getParameter("lang") != null ? request.getParameter("lang") : "en");
            loadFresnelConfig(langPref);

            conf = new Configuration(confRepository, ontoRepository);

            /* Create the focal resource */
            if (request.getParameter("uri") != null) {
                String uriString = request.getParameter("uri");
                if (uriString.startsWith("_:")) /* blank node */
                    focalResource = valueFactory.createBNode(uriString.substring(2));
                else
                    focalResource = valueFactory.createURI(uriString);

                /* Collect data about the focal resource */
                if (request.getParameter("skipload") == null) {
                    retrievedURLs = semwebClient.discoverResource(focalResource, true /* wait */);
                } /* skip */

                /* Initiate manual owl:sameAs inference */
                if (request.getParameter("skipInference") == null) {
                    sameAsInferencer.addInferredForResource(focalResource);
                }

                if (conf.hasWarnings())
                    writer.println(conf.getWarningsString());

                Purpose purpose = null;

                /* Look up the requested lens purpose */
                if (request.getParameter("purpose") != null
                        && (!request.getParameter("purpose").equals("defaultPurpose")))
                    purpose = new Purpose(new URIImpl(Constants.nsFresnelExt + request.getParameter("purpose")));
                else
                    purpose = new Purpose(new URIImpl(
                            "http://www.w3.org/2004/09/fresnel#defaultLens")); /* this must be provided, or a random lens is chosen */

                try {
                    /* Perform Fresnel selection using the requested display purpose and language */
                    selected = conf.select(dataRepository, focalResource, purpose, langPref);

                    /* Perform Fresnel formatting */
                    selected = conf.format(dataRepository, selected);
                } catch (NoResultsException e) {

                    /* 
                     * If no results are found, redirect the user to the resource
                     * if it is not an RDF document.
                     * This code is not reached when there already is some data about the resource.
                     */
                    RepositoryConnection metaDataConn = null;
                    try {
                        metaDataConn = metaDataRepository.getConnection();

                        /* Manual support for one level of redirects */
                        String resourceRedirect = cacheController.getCachedHeaderDataValue(metaDataConn,
                                focalResource, "location");
                        Resource resourceURI = (resourceRedirect == null ? focalResource
                                : new URIImpl(resourceRedirect));

                        /* Get target content type */
                        String contentType = cacheController.getCachedHeaderDataValue(metaDataConn, resourceURI,
                                "content-type");
                        if (contentType != null && !ContentTypes.isRDF(contentType)) {
                            redirectLocation = focalResource.toString();
                        }
                    } catch (RepositoryException re) {
                        re.printStackTrace();
                    } catch (IllegalArgumentException ie) {
                        ie.printStackTrace();
                    } finally {
                        try {
                            if (metaDataConn != null)
                                metaDataConn.close();
                        } catch (RepositoryException re) {
                            re.printStackTrace();
                        }
                    }
                }
            } /* uri != null */
        } catch (Exception e) {
            e.printStackTrace();
            errorString = e.getMessage();
        }

        /* Output */
        try {
            /* Handle redirection to non-RDF data */
            if (redirectLocation != null) {
                response.setHeader("Location", redirectLocation);
                response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
            } else { /* Perform XSL output */

                /*
                 * When there are no results, we still need a selection object in 
                 * order to render the fresnel tree
                 */
                if (selected == null)
                    selected = new Selection(conf);

                Document fresnelTree = selected.render();
                addSources(fresnelTree, retrievedURLs);

                /* Prepare XSLT */
                StreamSource styleSource = new StreamSource(
                        new File(dataRoot + "/" + xslDirectory + "/" + xslTransformation));
                net.sf.saxon.TransformerFactoryImpl tf = new net.sf.saxon.TransformerFactoryImpl();
                Transformer styleTransformer = tf.newTransformer(styleSource);

                /* Debug output */
                if (request.getParameter("debug") != null) {
                    /* debug output: fresnel tree */
                    StringWriter stringWriter = new StringWriter();
                    Transformer treeTransformer = tf.newTransformer();
                    treeTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
                    treeTransformer.transform(new DOMSource(fresnelTree), new StreamResult(stringWriter));
                    styleTransformer.setParameter("fresnelTree", stringWriter.getBuffer().toString());
                }

                /* Apply parameters */
                styleTransformer.setParameter("assetsURL", assetsURL != null ? assetsURL.toString() : "");
                styleTransformer.setParameter("serviceURL", serviceURL != null ? serviceURL.toString() : "");
                styleTransformer.setParameter("errorString", errorString);
                styleTransformer.setParameter("mainResource",
                        focalResource != null ? focalResource.toString() : "");
                if (request.getParameter("purpose") != null)
                    styleTransformer.setParameter("purpose", request.getParameter("purpose"));
                else
                    styleTransformer.setParameter("purpose", "defaultPurpose");

                if (request.getParameter("mobile") != null)
                    styleTransformer.setParameter("isMobile", request.getParameter("mobile"));

                HashMap<String, String[]> newParameters = new HashMap<String, String[]>(request.getParameterMap());

                if (!newParameters.containsKey("lang"))
                    newParameters.put("lang", new String[] { "en" });

                for (Object key : new String[] { "purpose", "uri" }) {
                    newParameters.remove(key);
                }

                String parameterString = "";

                /* Serialize parameters for use in HTML links and forms */
                for (Object key : newParameters.keySet()) {
                    parameterString += (parameterString.equals("") ? "" : "&") + key + "="
                            + ((String[]) newParameters.get(key))[0];
                }

                styleTransformer.setParameter("sessionParams", parameterString);

                /* Perform rendering */
                StreamResult res = new StreamResult(writer);
                styleTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
                styleTransformer.transform(new DOMSource(fresnelTree), res);
            }
        } catch (Exception e) {
            e.printStackTrace();
            writer.print(e.getMessage());
        } finally {
        }
    }

    /* (non-Java-doc)
     * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        handleRequest(request, response);
    }

    /* (non-Java-doc)
     * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        handleRequest(request, response);
    }

    /**
     * Reads data from a URL in a given syntax into a Sesame repository;
     * supports {@link ParamReader}
     * 
     * @param store Add RDF to this <code>LocalRepository</code>
     * @param url The <code>String</code> location of the data
     * @param syntax The <code>String</code> syntax of the data
     * @throws Exception For any problems encountered during read of data from the URL
     */
    public static void loadTriples(Repository store, File file, HashMap<String, String> parameters,
            Resource... context) throws Exception {
        RDFFormat format = null;
        if (file.getName().endsWith(".nt"))
            format = RDFFormat.NTRIPLES;
        else if (file.getName().endsWith(".rdf"))
            format = RDFFormat.RDFXML;
        else if (file.getName().endsWith(".n3"))
            format = RDFFormat.N3;
        else if (file.getName().endsWith(".ttl"))
            format = RDFFormat.TURTLE;
        else
            throw new ServletException("No RDF format known for " + file.getName());

        String baseURI = "file://" + file.getAbsoluteFile();
        RepositoryConnection conn = store.getConnection();

        BufferedReader fileReader = new BufferedReader(new FileReader(file));
        if (parameters != null) {
            ParamReader paramReader = new ParamReader(parameters, fileReader);
            conn.add(paramReader, baseURI, format, context);
        } else
            conn.add(fileReader, baseURI, format, context);

        conn.commit();
        conn.close();
    }

    /**
     * Filename filter that accepts only file extensions commonly used for RDF data, namely
     * <code>.nt</code>, <code>.rdf</code>, <code>.ttl</code>, <code>.n3</code>
     */
    private class RDFFilenameFilter implements FilenameFilter {
        public boolean accept(File dir, String name) {
            return (!name.startsWith(".")) && (name.endsWith(".nt") || name.endsWith(".rdf")
                    || name.endsWith(".ttl") || name.endsWith(".n3"));
        }
    }

    /**
     * Enhances source data with consistently colored icons;
     * adds detailed source list to Fresnel output
     * 
     * @param doc   The Fresnel tree 
     */
    private void addSources(Document doc, List<org.apache.commons.httpclient.URI> retrievedURLs) {
        int colorIndex = 0;
        HashMap<String, Source> sources = new HashMap<String, Source>();

        NodeList nodeList = doc.getElementsByTagName("source");
        int numNodes = nodeList.getLength();

        for (int i = 0; i < numNodes; i++) {
            Node node = nodeList.item(i);
            String uri = node.getFirstChild().getFirstChild().getNodeValue();

            Source source;

            /* Get source, create it if necessary */
            if (null == (source = sources.get(uri))) {
                source = new Source(uri);
                colorIndex = source.determineIcon(colorIndex);
                sources.put(uri, source);
            }

            /* Enhance source reference with icon */
            Element sourceIcon = doc.createElementNS(Constants.nsFresnelView, "sourceIcon");
            sourceIcon.appendChild(doc.createTextNode(source.getIcon()));
            node.appendChild(sourceIcon);
        }

        /* Supplement source list with retrieved URLs */
        if (retrievedURLs != null)
            for (org.apache.commons.httpclient.URI uri : retrievedURLs) {
                Source source;
                if (null == (source = sources.get(uri.toString()))) {
                    source = new Source(uri.toString());
                    colorIndex = source.determineIcon(colorIndex);
                    sources.put(uri.toString(), source);
                }
            }

        /* Provide list of sources */
        RepositoryConnection metaDataConn = null;
        try {
            metaDataConn = metaDataRepository.getConnection();
            Element sourcesElement = doc.createElementNS(Constants.nsFresnelView, "sources");
            for (String uri : sources.keySet()) {
                Source source = sources.get(uri);
                sourcesElement.appendChild(source.toElement(doc, cacheController, metaDataConn));
            }

            Node results = doc.getFirstChild();
            results.appendChild(sourcesElement);

        } catch (RepositoryException e) {
            e.printStackTrace();
        } finally {
            try {
                if (metaDataConn != null)
                    metaDataConn.close();
            } catch (RepositoryException e) {
                e.printStackTrace();
            }
        }
    }
}