package uk.ac.ebi.ep.enzymeservices.reactome;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.concurrent.Callable;
import org.apache.commons.lang.NotImplementedException;
import org.apache.log4j.Logger;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import uk.ac.ebi.biobabel.util.xml.XPathSAXHandler;
import uk.ac.ebi.ep.data.enzyme.model.EnzymeReaction;
import uk.ac.ebi.ep.data.enzyme.model.Pathway;
import uk.ac.ebi.rhea.domain.Database;
/**
* Class to retrieve reactions and pathways from Reactome REST web services.
* @author rafa
*/
public class ReactomeWsCallable implements Callable<Pathway> {
private static final Logger LOGGER =
Logger.getLogger(ReactomeWsCallable.class);
// public static final String WS_BASE_URL =
// "http://reactomews.oicr.on.ca:8080/ReactomeRESTfulAPI/RESTfulWS/queryById/";
public static final String WS_BASE_URL =
"http://reactomews.oicr.on.ca:8080/ReactomeRESTfulAPI/RESTfulWS/queryById/DatabaseObject/";
// Hrefs within descriptions have to be made absolute:
private static final String AHREF_PATTERN = "<a href='/";
private static final String AHREF_REPLACEMENT =
"<a href='http://www.reactome.org/";
// XPaths for the SAX parser:
private static final String DISPLAYNAME = "//{reactomeClass}/displayName";
private static final String FIGURE_DISPLAYNAME =
"//{reactomeClass}/figure/displayName";
private static final String SUMMATION_DBID =
"//{reactomeClass}/summation/dbId";
private static final String SUMMATION_TEXT = "//summation/text";
public enum ReactomeClass {
Reaction, Pathway, Summation
}
private ReactomeConfig config;
private String pathwayId;
/**
* Constructs a new callable.
* @param config
* @param pathwayId a Reactome stable pathway ID (may be <code>null</code>,
* if this object is not to be used as Callable).
*/
public ReactomeWsCallable(ReactomeConfig config, String pathwayId) {
this.config = config;
this.pathwayId = pathwayId;
}
public Pathway call() throws Exception {
return getPathway(pathwayId);
}
/**
* Gets a reaction (complete with description and xref to Reactome).
* @param reactionId a Reactome reaction stable ID.
* @return an EnzymeReaction object with name, URL and description (if
* available).
* @throws ReactomeConnectionException in case of problem getting the XML
* from Reactome.
* @throws ReactomeFetchDataException in case of problem parsing the
* XML from Reactome.
*/
public EnzymeReaction getReaction(String reactionId)
throws ReactomeConnectionException, ReactomeFetchDataException {
EnzymeReaction reaction = new EnzymeReaction();
reaction.setId(reactionId);
reaction.setUrl(Database.REACTOME.getEntryUrl(reactionId));
StringBuilder sb = new StringBuilder(config.getWsBaseUrl())
.append(ReactomeClass.Reaction.name()).append('/')
.append(reactionId);
InputStream is = null;
final String nameXpath = DISPLAYNAME.replace(
"{reactomeClass}", ReactomeClass.Reaction.name().toLowerCase());
final String summIdXpath = SUMMATION_DBID.replace(
"{reactomeClass}", ReactomeClass.Reaction.name().toLowerCase());
try {
XMLReader xr = XMLReaderFactory.createXMLReader();
XPathSAXHandler handler =
new XPathSAXHandler(nameXpath, summIdXpath);
xr.setContentHandler(handler);
URL url = new URL(sb.toString());
URLConnection urlCon = config.getUseProxy()
? url.openConnection()
: url.openConnection(Proxy.NO_PROXY);
urlCon.setRequestProperty("Accept", "application/xml");
is = urlCon.getInputStream();
InputSource inputSource = new InputSource(is);
xr.parse(inputSource);
if (handler.getResults().get(nameXpath) != null){
reaction.setName(handler.getResults()
.get(nameXpath).iterator().next());
}
if (handler.getResults().get(summIdXpath) != null){
final String summId =
handler.getResults().get(summIdXpath).iterator().next();
reaction.setDescription(getDescription(summId));
}
} catch (MalformedURLException e) {
throw new ReactomeConnectionException(sb.toString(), e);
} catch (IOException e) {
throw new ReactomeConnectionException(sb.toString(), e);
} catch (SAXException e) {
throw new ReactomeFetchDataException(e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOGGER.error(e);
}
}
}
return reaction;
}
/**
* Retrieves one pathway from the Reactome web services.
* @param pathwayId a <b>stable</b> Reactome ID <b>without version</b>
* (for example, "REACT_21342", not "REACT_21342.1").
* @return a Pathway with name, description, URL and image (if available).
* @throws ReactomeConnectionException in case of problem getting the XML
* from Reactome.
* @throws ReactomeFetchDataException in case of problem parsing the
* XML from Reactome.
*/
public Pathway getPathway(String pathwayId)
throws ReactomeConnectionException, ReactomeFetchDataException{
Pathway pathway = new Pathway();
pathway.setId(pathwayId);
pathway.setUrl(Database.REACTOME.getEntryUrl(pathwayId));
String sb = config.getWsBaseUrl() + ReactomeClass.Pathway.name() + '/' + pathwayId;
InputStream is = null;
final String nameXpath = DISPLAYNAME.replace(
"{reactomeClass}", ReactomeClass.Pathway.name().toLowerCase());
final String summIdXpath = SUMMATION_DBID.replace(
"{reactomeClass}", ReactomeClass.Pathway.name().toLowerCase());
final String figureXpath = FIGURE_DISPLAYNAME.replace(
"{reactomeClass}", ReactomeClass.Pathway.name().toLowerCase());
try {
XMLReader xr = XMLReaderFactory.createXMLReader();
XPathSAXHandler handler =
new XPathSAXHandler(nameXpath, summIdXpath, figureXpath);
xr.setContentHandler(handler);
URL url = new URL(sb);
URLConnection urlCon = config.getUseProxy()
? url.openConnection()
: url.openConnection(Proxy.NO_PROXY);
urlCon.setRequestProperty("Accept", "application/xml");
is = urlCon.getInputStream();
InputSource inputSource = new InputSource(is);
xr.parse(inputSource);
if (handler.getResults().get(nameXpath) != null){
pathway.setName(handler.getResults()
.get(nameXpath).iterator().next());
}
if (handler.getResults().get(summIdXpath) != null){
final String summId =
handler.getResults().get(summIdXpath).iterator().next();
pathway.setDescription(getDescription(summId));
}
if (handler.getResults().get(figureXpath) != null){
pathway.setImage(handler.getResults()
.get(figureXpath).iterator().next());
}
} catch (MalformedURLException e) {
throw new ReactomeConnectionException(sb, e);
} catch (IOException e) {
throw new ReactomeConnectionException(sb, e);
} catch (SAXException e) {
throw new ReactomeFetchDataException(e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOGGER.error(e);
}
}
}
return pathway;
}
/**
* Gets pathways related to a reaction.
* @param reactionId a Reactome reaction stable ID
* @return a collection of pathways in which the reaction can be involved,
* or <code>null</code> if none found.
*/
public Collection<Pathway> getPathways(String reactionId) {
throw new NotImplementedException();
}
/**
* Retrieves a description of a reaction or pathway.
* @param summaryId A Reactome summary ID.
* @return a description.
* @throws ReactomeConnectionException in case of problem getting the XML
* from Reactome.
* @throws ReactomeFetchDataException in case of problem parsing the
* XML from Reactome.
*/
protected String getDescription(String summaryId)
throws ReactomeConnectionException, ReactomeFetchDataException{
String desc = null;
StringBuilder sb = new StringBuilder(config.getWsBaseUrl())
.append(ReactomeClass.Summation.name()).append('/')
.append(summaryId);
InputStream is = null;
try {
URL url = new URL(sb.toString());
URLConnection urlCon = config.getUseProxy()?
url.openConnection():
url.openConnection(Proxy.NO_PROXY);
urlCon.setRequestProperty("Accept", "application/xml");
is = urlCon.getInputStream();
InputSource inputSource = new InputSource(is);
XMLReader xr = XMLReaderFactory.createXMLReader();
XPathSAXHandler handler = new XPathSAXHandler(SUMMATION_TEXT);
xr.setContentHandler(handler);
xr.parse(inputSource);
Collection<String> summations =
handler.getResults().get(SUMMATION_TEXT);
if (summations != null){
desc = summations.iterator().next()
.replaceAll(AHREF_PATTERN, AHREF_REPLACEMENT);
}
} catch (MalformedURLException e) {
throw new ReactomeConnectionException(sb.toString(), e);
} catch (IOException e) {
throw new ReactomeConnectionException(sb.toString(), e);
} catch (SAXException e) {
throw new ReactomeFetchDataException(e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOGGER.error(e);
}
}
}
return desc;
}
/**
* Retrieves the description for a Reactome object (reaction or pathway).
* @param reactomeId the Reactome <b>stable</b> ID
* @return a description for the object.
* @throws ReactomeServiceException if there is any problem with the web
* service.
*/
public String getDescription(ReactomeClass reactomeClass, String reactomeId)
throws ReactomeServiceException {
String desc = null;
switch (reactomeClass) {
case Reaction:
desc = getReaction(reactomeId).getDescription();
break;
case Pathway:
desc = getPathway(reactomeId).getDescription();
break;
default:
throw new IllegalArgumentException(
"Descriptions only for reactions and pathways");
}
return desc;
}
}