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