Showing posts with label taverna. Show all posts
Showing posts with label taverna. Show all posts

14 May 2009

WebServices/JAXWS for SNP, Glassfish, Taverna: my notebook

In this post I describe how to deploy a WebService in the GlassFish web server and to to use it via the Taverna workflow engine.

Server side


Classes


The JAX-WS API (the java API for Web Services) was used here. Our Web Service will be designed to
  • find the position of the SNP from his name
  • find the SNPs in a given region
First of all, a simple POJO (Plain Old Java Object) for a SNP (name, chromosome, position, ....) was created
public class SNP
implements Serializable
{
private static final long serialVersionUID = 1L;
private String name=null;
private String acn=null;
private String chromosome=null;
private String sequence=null;
private int position=-1;

(...)
//getters and setters here...
(...)

}

The interface of our web service "SnpTool" is then defined. The class is decorated with the JAX-WS annotations defining the methods of the web service and the name of the parameters:
package fr.cephb.operon.server.ws;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style=Style.DOCUMENT)
public interface SnpTool
{
@WebMethod
@WebResult(name="SnpDescriptorList")
public SNP[] getSNPByName(@WebParam(name="rsNumber")String name) throws Exception;

@WebMethod
@WebResult(name="SnpList")
public SNP[] getSNPByPosition(@WebParam(name="chromosome")String chrom,@WebParam(name="start")int start,@WebParam(name="end")int end) throws Exception;
}
The annotation @SOAPBinding(style=Style.DOCUMENT) was critical because Taverna doesn't seem to handle Style.RPC.
The service for SnpTool is then implemented. As I want to configure this WebService on deployment time (for example to specify a maximum number of SNPs to be retrieved, the default assembly, etc...), we need to get a handle to the web container: this pointer was obtained by injecting a @WebServiceContext annotation. This context is then used to retrieve the initialization parameters of the web application. Warning, this context is not initialized in the SNPToolWeb constructor.
package fr.cephb.operon.server.ws;
import java.io.File;

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.servlet.ServletContext;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;

import fr.cephb.joperon.core.bio.Assembly;

@WebService(endpointInterface="fr.cephb.operon.server.ws.SnpTool")
public class SNPToolWeb
implements SnpTool
{
@Resource
private WebServiceContext wsContext=null;

/** max number of SNP to be retrieved */
private Integer maxNumberOfSNP=null;

private Integer getMaxNumberOfSNP() throws Exception
{
if(maxNumberOfSNP!=null) return maxNumberOfSNP;
MessageContext ctxt = wsContext.getMessageContext();
ServletContext ctx = (ServletContext)ctxt.get(MessageContext.SERVLET_CONTEXT);
String s= ctx.getInitParameter("limit");
if(s!=null)
{
maxNumberOfSNP=Integer.parseInt(s);
}
return maxNumberOfSNP;
}



@Override
public SNP[] getSNPByName(String rsName) throws Exception {
//get your data here from a database, a file, etc....
//yes it returns an array because some SNP may have been merged
return snp;
}

@Override
public SNP[] getSNPByPosition(String krom, int start, int end)
throws Exception {
List<SNP> list= new ArrayList<SNP>();

//get your data here....

return list.toArray(new SNP[list.size()]);
}

}

Compile and Deploy

The developement descriptor looks like this:
<web-app>
<context-param>
<param-name>limit</param-name>
<param-value>1000000</param-value>
</context-param>
</web-app>

Ant is called to deploy this web service in Glassfish. Note that wsgen was invoked to generate the JAX-WS portable artifacts used in JAX-WS web services.
<project default="all" basedir=".">
<property environment="env"/>
<property name="home.dir" value="${env.HOME}"/>
<property name="rootdir" value="."/>
<property name="builddir" value="${rootdir}/build"/>
<property name="compiledir" value="${builddir}/compile"/>
<property file="${home.dir}/.project-properties"/>

<path id="libraries">
<pathelement path="lib1.jar"/>
<pathelement path="lib2.jar"/>
</path>

<path id="j2eelib">
<pathelement path="${appserver.dir}/lib/webservices-rt.jar"/>
<pathelement path="${appserver.dir}/lib/webservices-tools.jar"/>
</path>



<target name="demoservlet">
<mkdir dir="${compiledir}"/>
<mkdir dir="${builddir}"/>

<copy todir="${compiledir}" includeEmptyDirs="false">
<fileset dir="${rootdir}/src/java">
<filename name="**/*.java"/>
</fileset>
<fileset dir="${rootdir}/src/java">
<filename name="**/*.xml"/>
</fileset>
</copy>

<javac srcdir="${compiledir}" destdir="${compiledir}" debug="true" source="1.6" target="1.6">
<include name="**/SNPServlet.java"/>
<include name="**/SNPToolWeb.java"/>
<classpath>
<path refid="libraries"/>
<pathelement location="${appserver.dir}/lib/j2ee.jar"/>
</classpath>
</javac>


<echo message="Running wsgen"/>
<exec executable="${appserver.dir}/bin/wsgen">
<arg value="-cp"/> <arg value="${appserver.dir}/lib/j2ee.jar:$lib1.jar:$lib2.jar:${compiledir}"/>
<arg value="-verbose"/>
<arg value="-s"/> <arg value="${compiledir}"/>
<arg value="-d"/> <arg value="${compiledir}"/>
<arg value="-wsdl"/>
<arg value="-keep"/>
<arg value="fr.cephb.operon.server.ws.SNPToolWeb"/>
</exec>


<delete includeEmptyDirs="true">
<fileset dir="${compiledir}" includes="**/*.java"/>
</delete>

<war destfile="${builddir}/snp2rdf.war" webxml="${compiledir}/WEB-INF/web.xml">
<lib file="lib1.jar"/>
<classes dir="${compiledir}"/>
</war>

<delete dir="${compiledir}"/>

<exec executable="asadmin" failonerror="true">
<arg value="deploy"/>
<arg line="--user username"/>
<arg line="--passwordfile ${passwordfile}"/>
<arg value="--host"/>
<arg value="localhost"/>
<arg value="--port"/>
<arg value="${domain.admin.port}"/>
<arg value="--echo=true"/>
</>
<arg value="--libraries"/><arg value="lib1.jar"/>
<arg value="${builddir}/snp2rdf.war"/>
</exec>
</target>
</project>

The WSDL

After this WebService was compiled and deployed, we can see its WSDL at http://www.example.org:8080/snp2rdf/SNPToolWebService?wsdl:
<definitions targetNamespace="http://ws.server.operon.cephb.fr/" name="SNPToolWebService">
<types>
<xsd:schema>
<xsd:import namespace="http://ws.server.operon.cephb.fr/" schemaLocation="http://www.example.org:8080/snp2rdf/SNPToolWebService?xsd=1"/>
</xsd:schema>
</types>
<message name="getSNPByName">
<part name="parameters" element="tns:getSNPByName"/>
</message>
<message name="getSNPByNameResponse">
<part name="parameters" element="tns:getSNPByNameResponse"/>
</message>
<message name="Exception">
<part name="fault" element="tns:Exception"/>
</message>
<message name="getAssemblyName">
<part name="parameters" element="tns:getAssemblyName"/>
</message>
<message name="getAssemblyNameResponse">
<part name="parameters" element="tns:getAssemblyNameResponse"/>
</message>
<message name="getSNPByPosition">
<part name="parameters" element="tns:getSNPByPosition"/>
</message>
<message name="getSNPByPositionResponse">
<part name="parameters" element="tns:getSNPByPositionResponse"/>
</message>
<portType name="SnpTool">
<operation name="getSNPByName">
<input message="tns:getSNPByName"/>
<output message="tns:getSNPByNameResponse"/>
<fault message="tns:Exception" name="Exception"/>
</operation>
<operation name="getAssemblyName">
<input message="tns:getAssemblyName"/>
<output message="tns:getAssemblyNameResponse"/>
<fault message="tns:Exception" name="Exception"/>
</operation>
<operation name="getSNPByPosition">
<input message="tns:getSNPByPosition"/>
<output message="tns:getSNPByPositionResponse"/>
<fault message="tns:Exception" name="Exception"/>
</operation>
</portType>
<binding name="SNPToolWebPortBinding" type="tns:SnpTool">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="getSNPByName">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</fault>
</operation>
<operation name="getAssemblyName">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</fault>
</operation>
<operation name="getSNPByPosition">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</fault>
</operation>
</binding>
<service name="SNPToolWebService">
<port name="SNPToolWebPort" binding="tns:SNPToolWebPortBinding">
<soap:address location="http://www.example.org:8080/snp2rdf/SNPToolWebService"/>
</port>
</service>
</definitions>


Client side


Creating a Client with wsimport


I've previously described how to use wsimport to generate the classes using a WebService in a previous post (see "The EBI/IntAct Web-Service API, my notebook")

Using the WS with Taverna



I've used this WSDL to run my first Taverna workflow: the input is the SNP "rs25". The Web Services invoked finds its position on the human genome and find its neighbours at 100bp. The XML result is then saved to a local file.
  • The green nodes are the WebServices.
  • The blue nodes are the constants (e.g. "rs25")
  • The orange node is a simple Java BeanShell script extending the position of the SNP:
    left=Math.max(0,Integer.parseInt(position)-Integer.parseInt(extend));
    right=Integer.parseInt(position)+Integer.parseInt(extend);
  • the purple nodes are the XML scavengers (a mysterious thing used to convert a structure to/from a XML file) and the processors (e.g. write to a file)

We this workflow was invoked it saved the following file:
<ns2:getSNPByPositionResponse>
<SnpList>
<acn>rs12699208</acn>
<chromosome>Chr7</chromosome>
<name>rs12699208</name>
<position>11549694</position>
<sequence>(...)TATAGCTTCAACATATATGAAAAAAATGTCCACTGA[R]TAGTTCCTGGTGGAGAACTCTCCCATCTCTTTTG</sequence>
</SnpList>
<SnpList>
<acn>rs27</acn>
<chromosome>Chr7</chromosome>
<name>rs27</name>
<position>11549750</position>
<sequence>(..)CCCCCATTTGAGATCCTTCTTCATCTCACCTG[S]TACCTCTCAATCCCGGTGAACCAAAAGAGATGGG(...)</sequence>
</SnpList>

(...)

<SnpList>
<acn>rs7458209</acn>
<chromosome>Chr7</chromosome>
<name>rs7458209</name>
<position>11551560</position>
<sequence>(...)GAAAACTTTAGGAAGCAAACAT[Y]GTTTTATTAAGAAAACAGGTTAAGCAAGATGGCTGACAGGAAGAGCTTCTCC(...)</sequence>
</SnpList>
</ns2:getSNPByPositionResponse>

This workflow was then uploaded and shared on
. Please note that this web service is under developpement and might be replaced and/or switched off soon.

That's it !
Pierre

22 December 2008

Knime.org: creating a new Source Node reading Fasta sequences.

This blog is about how I wrote a java plugin for the workflow engine KNIME (http://www.knime.org). This plugin reads a FASTA file containing one or more sequence and transforms it into a table containing to columns: one for the name of the sequence and the other for the sequence itself.

In the last weeks , I've been looking for a workflow engine that could be easily handled by the members of my lab. I tested three tools:

  • Taverna (now in version 2) is mainly devoted to the web services. I've been trying to learn how to use Taverna a few times but I still don't find it user-friendly, not intuitive
  • http://kepler-project.org/: 141Mo ! Ouch !
  • http://www.knime.org: until the last version , the development version crashed at startup on my computer, but now everything works fine


KNime - The Konstanz Information Miner is a modular environment -, which enables easy visual assembly and interactive execution of a data pipeline. Knime is built over eclipse and each node in the workflow is developed as a plugin. As far as I understand Knime, it only handles tabular data and that's why I wrote this new plugin converting a set of fasta files. The Knime SDK comes with a dialog wizard creating a the java stubs required to create a new KNime node. Here are a few files:


XXXNodeModel
implements the logic of the node. The most important method
BufferedDataTable[] execute(finalBufferedDataTable[] inData,
final ExecutionContext exec) throws Exception
takes as input one or more table, transforms it and returns an array of one or more table

XXXNodeDialog
A Swing-based dialog used to select the option of a node. Here, I've created a dialog selecting the fasta file

XXXNodeView
Visualizes the result of the node. Here, I didn't wrote a view but one could imagine a graphical interface drawing the GC%, etc...

XXXNodeFactory
A class generating the model, the dialog the views, ...

XXXNodePlugin
Describes the plugin. It is only used by eclipse



The sources I wrote are available here: warning, the sources are a draft and I'm still learning the Knime API, I guess there must have cleaver/smarter/safer way to write this stuff
  • FastaIterator.java: iterator over a fasta file
  • FastaTable.java: the tabular representation of the sequences. It contains two columns Name and Sequence
  • ReadFastaNodeDialog.java: the dialog selecting the fasta file
  • ReadFastaNodeFactory.java: the node factory
  • ReadFastaNodeModel.java: implements the logic of the node. Creates and returns the FastaTable
  • /ReadFastaNodePlugin: used by eclipse


Here is a screenshot: my Node reads a fasta file and transforms it into a two-columns table 'grep' all the sequences containing the word ONCOGENE, sort the sequences and output the result in a table (smaller window at the bottom).







That's it for tonight.

Pierre