diff --git a/rtd_phosphonetx/.classpath b/rtd_phosphonetx/.classpath
index efc56346735878751c8c2a0b1a7f889b73e3c206..06912ae07f796a366c8742cbc0fb3ce79a44b8eb 100644
--- a/rtd_phosphonetx/.classpath
+++ b/rtd_phosphonetx/.classpath
@@ -26,5 +26,9 @@
 	<classpathentry kind="lib" path="/libraries/spring/spring.jar" sourcepath="/libraries/spring/src.jar"/>
 	<classpathentry kind="lib" path="/libraries/spring/webmvc/spring-webmvc.jar" sourcepath="/libraries/spring/webmvc/src.jar"/>
 	<classpathentry kind="lib" path="/libraries/hibernate-search/jms.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/hamcrest/hamcrest-core.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/hamcrest/hamcrest-library.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/objenesis/objenesis-1.0.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/jmock.jar"/>
 	<classpathentry kind="output" path="targets/classes"/>
 </classpath>
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/Dict.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/Dict.java
index 219b0bf17c0282aebd41baee70c200d3062b63cf..436cb61e780910740d5a8a120abc6a11fd47e069 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/Dict.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/Dict.java
@@ -24,8 +24,8 @@ package ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.applicatio
 class Dict
 {
     public static final String QUERY_MENU_TITLE = "query_menu_title";
-    public static final String QUERY_ALL_PROTEINS_BY_EXPERIMENT = "query_all_proteins_by_experiment";
     public static final String SELECTED_EXPERIMENT_LABEL = "selected_experiment_label";
     public static final String PROTEIN_DESCRIPTION = "protein_description";
+    public static final String FALSE_DISCOVERY_RATE = "false_discovery_rate";
 
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowerToolBar.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowerToolBar.java
index cb73747241eb4f7dd6074c7cafa5a5a554f3f331..b39eb3b8f235516f87c6b40cbc61fa8e762881ea 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowerToolBar.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowerToolBar.java
@@ -16,10 +16,6 @@
 
 package ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.application;
 
-import com.extjs.gxt.ui.client.Events;
-import com.extjs.gxt.ui.client.event.BaseEvent;
-import com.extjs.gxt.ui.client.event.Listener;
-import com.extjs.gxt.ui.client.widget.form.TextField;
 import com.extjs.gxt.ui.client.widget.toolbar.AdapterToolItem;
 import com.extjs.gxt.ui.client.widget.toolbar.LabelToolItem;
 import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
@@ -27,7 +23,9 @@ import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.ExperimentChooserField;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.IChosenEntityListener;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.ExperimentChooserField.ExperimentChooserFieldAdaptor;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.IPhosphoNetXClientServiceAsync;
 
@@ -45,20 +43,19 @@ class ProteinByExperimentBrowerToolBar extends ToolBar
                 + GenericConstants.LABEL_SEPARATOR));
         final ExperimentChooserFieldAdaptor chooser =
                 ExperimentChooserField.create("", false, null, viewContext.getCommonViewContext());
-        TextField<String> textField = chooser.getTextField();
-        textField.setReadOnly(true);
-        textField.addListener(Events.Valid, new Listener<BaseEvent>()
+        ExperimentChooserField chooserField = chooser.getChooserField();
+        chooserField.setReadOnly(true);
+        chooserField.addChosenEntityListener(new IChosenEntityListener<Experiment>()
             {
-                public void handleEvent(BaseEvent be)
+                public void entityChosen(Experiment entity)
                 {
-                    ExperimentIdentifier identifier = chooser.getValue();
-                    if (identifier != null)
+                    if (entity != null)
                     {
-                        browserGrid.update(identifier);
+                        browserGrid.update(ExperimentIdentifier.createIdentifier(entity));
                     }
                 }
             });
-        add(new AdapterToolItem(textField));
+        add(new AdapterToolItem(chooserField));
         add(new AdapterToolItem(chooser.getChooseButton()));
     }
 
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
index c1ae86334df70afd9ceaf707a1d4217e7d725b26..77f2e9b2b42c5ea26585b260f4bbe2cc0d7e782f 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
@@ -66,16 +66,17 @@ class ProteinByExperimentBrowserGrid extends AbstractSimpleBrowserGrid<ProteinIn
     
     private ProteinByExperimentBrowserGrid(IViewContext<IPhosphoNetXClientServiceAsync> viewContext)
     {
-        super(viewContext.getCommonViewContext(), BROWSER_ID, GRID_ID);
+        super(viewContext.getCommonViewContext(), BROWSER_ID, GRID_ID, false);
         specificViewContext = viewContext;
         setDisplayTypeIDGenerator(PhosphoNetXDisplayTypeIDGenerator.PROTEIN_BY_EXPERIMENT_BROWSER_GRID);
     }
     
     void update(ExperimentIdentifier identifier)
     {
+        System.out.println("ProteinByExperimentBrowserGrid.update() "+identifier);
         criteria = new ListProteinByExperimentCriteria();
         criteria.setExperimentID(identifier.getTechID());
-        refresh();
+        refresh();           
     }
 
     @Override
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinColDefKind.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinColDefKind.java
index 2feab25bf751537470fa52d1e23661fbbec7f7b9..bdc81acb123e96873552e4a7950b845d3586a1ba 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinColDefKind.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinColDefKind.java
@@ -16,6 +16,8 @@
 
 package ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.application;
 
+import com.google.gwt.i18n.client.NumberFormat;
+
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.AbstractColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.ProteinInfo;
@@ -25,7 +27,7 @@ import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.Protein
  */
 public enum ProteinColDefKind implements IColumnDefinitionKind<ProteinInfo>
 {
-    DESCRIPTION(new AbstractColumnDefinitionKind<ProteinInfo>(Dict.PROTEIN_DESCRIPTION, false)
+    DESCRIPTION(new AbstractColumnDefinitionKind<ProteinInfo>(Dict.PROTEIN_DESCRIPTION)
         {
             @Override
             public String tryGetValue(ProteinInfo entity)
@@ -33,8 +35,22 @@ public enum ProteinColDefKind implements IColumnDefinitionKind<ProteinInfo>
                 return entity.getDescription();
             }
         }), 
-    ;
+    FALSE_DISCOVERY_RATE(new AbstractColumnDefinitionKind<ProteinInfo>(Dict.FALSE_DISCOVERY_RATE)
+        {
+            @Override
+            public String tryGetValue(ProteinInfo entity)
+            {
+                return render(entity.getFalseDiscoveryRate());
+            }
+        }), ;
 
+    private static final NumberFormat DECIMAL_FORMAT = NumberFormat.getDecimalFormat();
+    
+    protected static String render(double value)
+    {
+        return DECIMAL_FORMAT.format(value);
+    }
+    
     private final AbstractColumnDefinitionKind<ProteinInfo> columnDefinitionKind;
 
     private ProteinColDefKind(AbstractColumnDefinitionKind<ProteinInfo> columnDefinitionKind)
@@ -51,4 +67,5 @@ public enum ProteinColDefKind implements IColumnDefinitionKind<ProteinInfo>
     {
         return columnDefinitionKind;
     }
+    
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/QueryMenu.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/QueryMenu.java
index b97cf019459624d0619e32a222fcfe12d62bd524..9c456fb22cbf2b54bf4148e6c14625ebfdb37fdb 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/QueryMenu.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/QueryMenu.java
@@ -42,7 +42,16 @@ public class QueryMenu extends TopMenuItem
     
     private static enum ActionMenuKind implements IActionMenuItem
     {
-        ALL_PROTEINS_OF_AN_EXPERIMENT;
+        ALL_PROTEINS_OF_AN_EXPERIMENT()
+        {
+            @Override
+            IDisposableComponent createComponent(
+                    IViewContext<IPhosphoNetXClientServiceAsync> viewContext)
+            {
+                return ProteinByExperimentBrowserGrid.create(viewContext);
+            }
+        }
+        ;
 
         public String getMenuId()
         {
@@ -51,36 +60,33 @@ public class QueryMenu extends TopMenuItem
 
         public String getMenuText(IMessageProvider messageProvider)
         {
-            return messageProvider.getMessage(this.name());
-        }
-    }
-    
-    private static final class TabItemFactory implements ITabItemFactory
-    {
-        protected final IViewContext<IPhosphoNetXClientServiceAsync> viewContext;
-        private final String id;
-        private final String tabLabelKey;
-        private final IDisposableComponent disposableComponent;
-
-        TabItemFactory(IViewContext<IPhosphoNetXClientServiceAsync> viewContext,
-                String tabLabelKey, IDisposableComponent disposableComponent)
-        {
-            this.viewContext = viewContext;
-            this.tabLabelKey = tabLabelKey;
-            this.disposableComponent = disposableComponent;
-            this.id = ID + tabLabelKey;
+            return messageProvider.getMessage(this.name() + "_menu_item");
         }
-
-        public String getId()
+        
+        String getTabLabelKey()
         {
-            return id;
+            return this.name() + "_tab_label";
         }
         
-        public ITabItem create()
+        ActionMenu createActionMenu(final IViewContext<IPhosphoNetXClientServiceAsync> viewContext)
         {
-            String menuItemText = viewContext.getMessage(tabLabelKey);
-            return DefaultTabItem.create(menuItemText, disposableComponent, viewContext);
+            return new ActionMenu(this, viewContext, new ITabItemFactory()
+                {
+                    public String getId()
+                    {
+                        return ID + getTabLabelKey();
+                    }
+
+                    public ITabItem create()
+                    {
+                        String menuItemText = viewContext.getMessage(getTabLabelKey());
+                        return DefaultTabItem.create(menuItemText, createComponent(viewContext),
+                                viewContext);
+                    }
+                });
         }
+        
+        abstract IDisposableComponent createComponent(IViewContext<IPhosphoNetXClientServiceAsync> viewContext);
     }
     
     public QueryMenu(IViewContext<IPhosphoNetXClientServiceAsync> viewContext)
@@ -89,14 +95,10 @@ public class QueryMenu extends TopMenuItem
         setIconStyle(TopMenu.ICON_STYLE);
         
         Menu menu = new Menu();
-        IDisposableComponent disposableComponent =
-                ProteinByExperimentBrowserGrid.create(viewContext);
-        TabItemFactory factory =
-                new TabItemFactory(viewContext, Dict.QUERY_ALL_PROTEINS_BY_EXPERIMENT,
-                        disposableComponent);
-        ActionMenu actionMenu =
-                new ActionMenu(ActionMenuKind.ALL_PROTEINS_OF_AN_EXPERIMENT, viewContext, factory);
-        menu.add(actionMenu);
+        for (ActionMenuKind actionMenuKind : ActionMenuKind.values())
+        {
+            menu.add(actionMenuKind.createActionMenu(viewContext));
+        }
         setMenu(menu);
     }
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/dto/ProteinInfo.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/dto/ProteinInfo.java
index 909f359260892a853e0f3de2994e77df9bf4c6d0..a73faf2f2ef1558693d508f0ca699c0215d0161f 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/dto/ProteinInfo.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/dto/ProteinInfo.java
@@ -27,27 +27,41 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
  */
 public class ProteinInfo implements IsSerializable
 {
-    private TechId annotationID;
+    private TechId id;
+    
     private String description;
+    
+    private double falseDiscoveryRate;
 
-    public final String getDescription()
+    public final TechId getId()
     {
-        return description;
+        return id;
     }
 
-    public final void setDescription(String description)
+    public final void setId(TechId id)
     {
-        this.description = description;
+        this.id = id;
     }
 
-    public final TechId getAnnotationID()
+    public final double getFalseDiscoveryRate()
     {
-        return annotationID;
+        return falseDiscoveryRate;
     }
 
-    public final void setAnnotationID(TechId annotationID)
+    public final void setFalseDiscoveryRate(double falseDiscoveryRate)
     {
-        this.annotationID = annotationID;
+        this.falseDiscoveryRate = falseDiscoveryRate;
     }
 
+    public final String getDescription()
+    {
+        return description;
+    }
+
+    public final void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/public/phosphonetx-dictionary.js b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/public/phosphonetx-dictionary.js
index a216d3e77b1c781db91486a7b50cabd39a121d3c..c44e230eec8852d8b61a6c5c50a51b637217bc47 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/public/phosphonetx-dictionary.js
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/public/phosphonetx-dictionary.js
@@ -1,10 +1,13 @@
 // PhosphoNetX dictionary
 var phosphonetx = {
   query_menu_title: "Queries",
-  ALL_PROTEINS_OF_AN_EXPERIMENT: "All Proteins of an Experiment",
-  query_all_proteins_by_experiment: "Proteins of an Experiment",
+  ALL_PROTEINS_OF_AN_EXPERIMENT_menu_item: "All Proteins of an Experiment",
+  ALL_PROTEINS_OF_AN_EXPERIMENT_tab_label: "Proteins of an Experiment",
   selected_experiment_label: "Experiment",
+  
+  // Protein grid
   protein_description: "Protein",
+  false_discovery_rate: "FDR",
   
   // LAST LINE: KEEP IT AT THE END
   lastline: "" // we need a line without a comma
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/ListProteinOriginalDataProvider.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/ListProteinOriginalDataProvider.java
index 31fb6cc1dd37ef1a7397bf0e0e652eb7e11f1e39..c07c72409a7cf4296c2b456fb7bff7a8ddb19574 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/ListProteinOriginalDataProvider.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/ListProteinOriginalDataProvider.java
@@ -24,7 +24,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IOriginalDat
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.ProteinInfo;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.IPhosphoNetXServer;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProteinReference;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
 
 /**
  * 
@@ -46,13 +46,14 @@ class ListProteinOriginalDataProvider implements IOriginalDataProvider<ProteinIn
     
     public List<ProteinInfo> getOriginalData() throws UserFailureException
     {
-        List<ProteinReference> references = server.listProteinReferencesByExperiment(sessionToken, experimentID);
-        List<ProteinInfo> infos = new ArrayList<ProteinInfo>(references.size());
-        for (ProteinReference proteinReference : references)
+        List<IdentifiedProtein> proteins = server.listProteinsByExperiment(sessionToken, experimentID);
+        List<ProteinInfo> infos = new ArrayList<ProteinInfo>(proteins.size());
+        for (IdentifiedProtein protein : proteins)
         {
             ProteinInfo proteinInfo = new ProteinInfo();
-            proteinInfo.setAnnotationID(new TechId(proteinReference.getAnnotationID()));
-            proteinInfo.setDescription(proteinReference.getDescription());
+            proteinInfo.setId(new TechId(protein.getId()));
+            proteinInfo.setDescription(protein.getDescription());
+            proteinInfo.setFalseDiscoveryRate(protein.getFalseDiscoveryRate());
             infos.add(proteinInfo);
         }
         return infos;
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServer.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServer.java
index df08222c02f2a2a90379a60cfdf288b16714dd63..943c6d170b0fdb5b5a18d5238d4484e93aed37a5 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServer.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServer.java
@@ -16,13 +16,10 @@
 
 package ch.systemsx.cisd.openbis.plugin.phosphonetx.server;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import javax.annotation.Resource;
 
-import net.lemnik.eodsql.DataSet;
-
 import org.springframework.stereotype.Component;
 
 import ch.rinn.restrictions.Private;
@@ -35,10 +32,12 @@ import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlug
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.IBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.IIdentifiedProteinTable;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IPhosphoNetXDAOFactory;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.IPhosphoNetXServer;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.ResourceNames;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProteinReference;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
 
 /**
  * @author Franz-Josef Elmer
@@ -49,6 +48,9 @@ public class PhosphoNetXServer extends AbstractServer<IPhosphoNetXServer> implem
 {
     @Resource(name = ResourceNames.PHOSPHONETX_DAO_FACTORY)
     private IPhosphoNetXDAOFactory specificDAOFactory;
+    
+    @Resource(name = ResourceNames.PHOSPHONETX_BO_FACTORY)
+    private IBusinessObjectFactory specificBOFactory;
 
     public PhosphoNetXServer()
     {
@@ -58,11 +60,13 @@ public class PhosphoNetXServer extends AbstractServer<IPhosphoNetXServer> implem
     @Private
     PhosphoNetXServer(ISessionManager<Session> sessionManager, IDAOFactory daoFactory,
             IPhosphoNetXDAOFactory specificDAOFactory,
+            IBusinessObjectFactory specificBOFactory,
             ISampleTypeSlaveServerPlugin sampleTypeSlaveServerPlugin,
             IDataSetTypeSlaveServerPlugin dataSetTypeSlaveServerPlugin)
     {
         super(sessionManager, daoFactory, sampleTypeSlaveServerPlugin, dataSetTypeSlaveServerPlugin);
         this.specificDAOFactory = specificDAOFactory;
+        this.specificBOFactory = specificBOFactory;
     }
 
     @Override
@@ -76,16 +80,14 @@ public class PhosphoNetXServer extends AbstractServer<IPhosphoNetXServer> implem
         return new PhosphoNetXServerLogger(getSessionManager(), invocationSuccessful, elapsedTime);
     }
 
-    public List<ProteinReference> listProteinReferencesByExperiment(String sessionToken,
+    public List<IdentifiedProtein> listProteinsByExperiment(String sessionToken,
             TechId experimentId) throws UserFailureException
     {
+        final Session session = getSessionManager().getSession(sessionToken);
+        IIdentifiedProteinTable proteinTable = specificBOFactory.createProteinTable(session);
         ExperimentPE experiment = getDAOFactory().getExperimentDAO().getByTechId(experimentId);
-        String permId = experiment.getPermId();
-        DataSet<ProteinReference> resultSet =
-                specificDAOFactory.getProteinQueryDAO().listProteinsByExperiment(permId);
-        ArrayList<ProteinReference> refrences = new ArrayList<ProteinReference>();
-        refrences.addAll(resultSet);
-        return refrences;
+        proteinTable.load(experiment.getPermId());
+        return proteinTable.getIdentifiedProteins();
     }
 
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServerLogger.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServerLogger.java
index 5baa17ea4144394e25362768381b894580ce6a09..d399812e41c74a048ac969461fbf35efdbb050e8 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServerLogger.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/PhosphoNetXServerLogger.java
@@ -28,7 +28,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SampleGenerationDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.IPhosphoNetXServer;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProteinReference;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
 
 /**
  * @author Franz-Josef Elmer
@@ -62,10 +62,10 @@ public class PhosphoNetXServerLogger extends AbstractServerLogger implements IPh
                 newSample.getSampleType(), newSample.getIdentifier(), attachments.size());
     }
 
-    public List<ProteinReference> listProteinReferencesByExperiment(String sessionToken,
+    public List<IdentifiedProtein> listProteinsByExperiment(String sessionToken,
             TechId experimentId) throws UserFailureException
     {
-        logAccess(sessionToken, "list_protein_references_by_experiment", "ID(%s)", experimentId);
+        logAccess(sessionToken, "list_proteins_by_experiment", "ID(%s)", experimentId);
         return null;
     }
 
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractBusinessObject.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractBusinessObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..2410c8bca14c33a72bcb8abbcb925e14657c7426
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractBusinessObject.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IPhosphoNetXDAOFactory;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+abstract class AbstractBusinessObject
+{
+    private final IDAOFactory daoFactory;
+    private final IPhosphoNetXDAOFactory specificDAOFactory;
+    private final Session session;
+
+    AbstractBusinessObject(IDAOFactory daoFactory, IPhosphoNetXDAOFactory specificDAOFactory, Session session)
+    {
+        this.daoFactory = daoFactory;
+        this.specificDAOFactory = specificDAOFactory;
+        this.session = session;
+    }
+    
+    protected final PersonPE getRegistrator()
+    {
+        PersonPE registrator = session.tryGetPerson();
+        assert registrator != null : "Session with unknown person: " + session;
+        return registrator;
+    }
+
+    protected final IDAOFactory getDaoFactory()
+    {
+        return daoFactory;
+    }
+
+    protected final IPhosphoNetXDAOFactory getSpecificDAOFactory()
+    {
+        return specificDAOFactory;
+    }
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..9caaee3c2d94732bc158d4b2382dde0bce56a270
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IPhosphoNetXDAOFactory;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class BusinessObjectFactory implements IBusinessObjectFactory
+{
+    private final IDAOFactory daoFactory;
+    private final IPhosphoNetXDAOFactory specificDAOFactory;
+
+    public BusinessObjectFactory(IDAOFactory daoFactory, IPhosphoNetXDAOFactory specificDAOFactory)
+    {
+        this.daoFactory = daoFactory;
+        this.specificDAOFactory = specificDAOFactory;
+    }
+
+    public IIdentifiedProteinTable createProteinTable(Session session)
+    {
+        return new IdentifiedProteinTable(daoFactory, specificDAOFactory, session);
+    }
+
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ErrorModel.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ErrorModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba47c2d503bd60fe45300680245e92bf310ea82b
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ErrorModel.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.lemnik.eodsql.DataSet;
+
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IPhosphoNetXDAOFactory;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IProteinQueryDAO;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProbabilityFDRMapping;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+class ErrorModel
+{
+    private static final class ProbabilityToFDRCalculator
+    {
+        private static final class MappingEntry implements Comparable<MappingEntry>
+        {
+            private final double probability;
+            private final double fdr;
+
+            MappingEntry(double probability, double fdr)
+            {
+                this.probability = probability;
+                this.fdr = fdr;
+            }
+
+            public int compareTo(MappingEntry that)
+            {
+                return probability < that.probability ? -1 : (probability > that.probability ? 1 : 0);
+            }
+
+            @Override
+            public String toString()
+            {
+                return probability + " = " + fdr;
+            }
+        }
+        
+        private final List<MappingEntry> mappingEntries = new ArrayList<MappingEntry>();
+
+        void add(double probability, double falseDiscoveryRate)
+        {
+            mappingEntries.add(new MappingEntry(probability, falseDiscoveryRate));
+        }
+
+        void init()
+        {
+            Collections.sort(mappingEntries);
+        }
+        
+        double calculateFDR(double probability)
+        {
+            int index = Collections.binarySearch(mappingEntries, new MappingEntry(probability, 0));
+            if (index >= 0)
+            {
+                return mappingEntries.get(index).fdr;
+            }
+            // calculate by linear interpolation
+            int index1 = -index - 1;
+            int index0 = index1 - 1;
+            assert index0 >= 0;
+            MappingEntry m0 = mappingEntries.get(index0);
+            MappingEntry m1 = mappingEntries.get(index1);
+            double scale = (m1.fdr - m0.fdr) / (m1.probability - m0.probability);
+            return m0.fdr + scale * (probability - m0.probability);
+        }
+    }
+    
+    private final Map<Long, ProbabilityToFDRCalculator> calculators =
+            new HashMap<Long, ProbabilityToFDRCalculator>();
+
+    private final IPhosphoNetXDAOFactory specificDAOFactory;
+
+    ErrorModel(IPhosphoNetXDAOFactory specificDAOFactory)
+    {
+        this.specificDAOFactory = specificDAOFactory;
+    }
+
+    void setFalseDiscoveryRateFor(IdentifiedProtein protein)
+    {
+        ProbabilityToFDRCalculator calculator = getCalculator(protein.getDataSetID());
+        protein.setFalseDiscoveryRate(calculator.calculateFDR(protein.getProbability()));
+    }
+
+    private ProbabilityToFDRCalculator getCalculator(long dataSetID)
+    {
+        ProbabilityToFDRCalculator calculator = calculators.get(dataSetID);
+        if (calculator == null)
+        {
+            calculator = new ProbabilityToFDRCalculator();
+            IProteinQueryDAO dao = specificDAOFactory.getProteinQueryDAO();
+            DataSet<ProbabilityFDRMapping> mmappings = dao.getProbabilityFDRMapping(dataSetID);
+            for (ProbabilityFDRMapping probabilityFDRMapping : mmappings)
+            {
+                double probability = probabilityFDRMapping.getProbability();
+                double falseDiscoveryRate = probabilityFDRMapping.getFalseDiscoveryRate();
+                calculator.add(probability, falseDiscoveryRate);
+            }
+            calculator.init();
+            calculators.put(dataSetID, calculator);
+        }
+        return calculator;
+    }
+
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..5555bf0c91f4a3e5a338b4a998ff72e066c2c52d
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IBusinessObjectFactory
+{
+    public IIdentifiedProteinTable createProteinTable(Session session);
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IIdentifiedProteinTable.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IIdentifiedProteinTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9f48e107962c2cc40fe8b74869d5b7848653f50
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IIdentifiedProteinTable.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import java.util.List;
+
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IIdentifiedProteinTable
+{
+    public void load(String experimentPermID);
+    
+    public List<IdentifiedProtein> getIdentifiedProteins();
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IdentifiedProteinTable.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IdentifiedProteinTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..1221ddd1789d9661619bf0cb0651601928520437
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IdentifiedProteinTable.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.lemnik.eodsql.DataSet;
+
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IPhosphoNetXDAOFactory;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+class IdentifiedProteinTable extends AbstractBusinessObject implements IIdentifiedProteinTable
+{
+    private List<IdentifiedProtein> proteins;
+
+    public IdentifiedProteinTable(IDAOFactory daoFactory,
+            IPhosphoNetXDAOFactory specificDAOFactory, Session session)
+    {
+        super(daoFactory, specificDAOFactory, session);
+    }
+
+    public List<IdentifiedProtein> getIdentifiedProteins()
+    {
+        if (proteins == null)
+        {
+            throw new IllegalStateException("No proteins loaded.");
+        }
+        return proteins;
+    }
+
+    public void load(String experimentPermID)
+    {
+        proteins = new ArrayList<IdentifiedProtein>();
+        DataSet<IdentifiedProtein> resultSet =
+            getSpecificDAOFactory().getProteinQueryDAO().listProteinsByExperiment(experimentPermID);
+        ErrorModel errorModel = new ErrorModel(getSpecificDAOFactory());
+        for (IdentifiedProtein protein : resultSet)
+        {
+            errorModel.setFalseDiscoveryRateFor(protein);
+            proteins.add(protein);
+        }
+    }
+
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/dataaccess/IProteinQueryDAO.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/dataaccess/IProteinQueryDAO.java
index 5407d2a0cae965e7bb8885c1f4e7098468d1c48a..b9ffe904a1638513270348cd8b672005ab9a07da 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/dataaccess/IProteinQueryDAO.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/dataaccess/IProteinQueryDAO.java
@@ -20,7 +20,8 @@ import net.lemnik.eodsql.BaseQuery;
 import net.lemnik.eodsql.DataSet;
 import net.lemnik.eodsql.Select;
 
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProteinReference;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProbabilityFDRMapping;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
 
 /**
  * 
@@ -29,9 +30,12 @@ import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProteinReference;
  */
 public interface IProteinQueryDAO extends BaseQuery
 {
-    @Select("select a.id, a.protein_description "
-            + "from annotations as a left join proteins as p on a.prot_id = p.id "
+    @Select("select * from probability_fdr_mappings where dase_id = ?{1}")
+    public DataSet<ProbabilityFDRMapping> getProbabilityFDRMapping(long dataSetID);
+    
+    @Select("select ip.id as id, d.id as data_set_id, p.id as protein_id, p.probability, ip.description "
+            + "from identified_proteins as ip left join proteins as p on ip.prot_id = p.id "
             + "left join data_sets as d on p.dase_id = d.id "
             + "left join experiments as e on d.expe_id = e.id where e.perm_id = ?{1}")
-    public DataSet<ProteinReference> listProteinsByExperiment(String experimentPermID);
+    public DataSet<IdentifiedProtein> listProteinsByExperiment(String experimentPermID);
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/IPhosphoNetXServer.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/IPhosphoNetXServer.java
index acca2830dd8d829825deb09a87397df394c882e9..b271d168e3f25d8bb155103a9019d9a6459ca3db 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/IPhosphoNetXServer.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/IPhosphoNetXServer.java
@@ -27,7 +27,7 @@ import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RoleSet;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.AbstractTechIdPredicate.ExperimentTechIdPredicate;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProteinReference;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
 
 /**
  * 
@@ -38,7 +38,7 @@ public interface IPhosphoNetXServer extends IServer
 {
     @Transactional
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ProteinReference> listProteinReferencesByExperiment(String sessionToken,
+    public List<IdentifiedProtein> listProteinsByExperiment(String sessionToken,
             @AuthorizationGuard(guardClass = ExperimentTechIdPredicate.class)
             TechId experimentId) throws UserFailureException;
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/ResourceNames.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/ResourceNames.java
index e12af898d9c5ec7c2585122ba8ca5e8ec6360a3a..a691b156c81abb4cf0f752648b82ed4727146977 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/ResourceNames.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/ResourceNames.java
@@ -32,5 +32,7 @@ public class ResourceNames
     public final static String PHOSPHONETX_PLUGIN_SERVER = "phosphonetx-plugin-server";
     
     public final static String PHOSPHONETX_DAO_FACTORY = "phosphonetx-dao-factory";
+    
+    public final static String PHOSPHONETX_BO_FACTORY = "phosphonetx-bo-factory";
 
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/IdentifiedProtein.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/IdentifiedProtein.java
new file mode 100644
index 0000000000000000000000000000000000000000..96536a6de418ffa869e39a7d7cccb20f7b16bff9
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/IdentifiedProtein.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto;
+
+import net.lemnik.eodsql.ResultColumn;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class IdentifiedProtein
+{
+    @ResultColumn("id")
+    private long id;
+    
+    @ResultColumn("data_set_id")
+    private long dataSetID;
+    
+    @ResultColumn("protein_id")
+    private long proteinID;
+    
+    @ResultColumn("description")
+    private String description;
+    
+    @ResultColumn("probability")
+    private double probability;
+    
+    private double falseDiscoveryRate;
+
+    public final long getId()
+    {
+        return id;
+    }
+
+    public final void setId(long id)
+    {
+        this.id = id;
+    }
+
+    public final String getDescription()
+    {
+        return description;
+    }
+
+    public final void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    public final long getDataSetID()
+    {
+        return dataSetID;
+    }
+
+    public final void setDataSetID(long dataSetID)
+    {
+        this.dataSetID = dataSetID;
+    }
+
+    public final long getProteinID()
+    {
+        return proteinID;
+    }
+
+    public final void setProteinID(long proteinID)
+    {
+        this.proteinID = proteinID;
+    }
+
+    public final double getProbability()
+    {
+        return probability;
+    }
+
+    public final void setProbability(double probability)
+    {
+        this.probability = probability;
+    }
+
+    public final double getFalseDiscoveryRate()
+    {
+        return falseDiscoveryRate;
+    }
+
+    public final void setFalseDiscoveryRate(double falseDiscoveryRate)
+    {
+        this.falseDiscoveryRate = falseDiscoveryRate;
+    }
+
+
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/ProteinReference.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/ProbabilityFDRMapping.java
similarity index 59%
rename from rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/ProteinReference.java
rename to rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/ProbabilityFDRMapping.java
index 2024769e817fcf0ec062308cd225dd70fd2ea185..8739170f579b9d3b09a8f4dbb33da8da569ce928 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/ProteinReference.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/ProbabilityFDRMapping.java
@@ -23,33 +23,31 @@ import net.lemnik.eodsql.ResultColumn;
  *
  * @author Franz-Josef Elmer
  */
-public class ProteinReference
+public class ProbabilityFDRMapping
 {
-    @ResultColumn("id")
-    private long annotationID;
+    @ResultColumn("probability")
+    private double probability;
     
-    @ResultColumn("protein_description")
-    private String description;
+    @ResultColumn("false_discovery_rate")
+    private double falseDiscoveryRate;
 
-    public final String getDescription()
+    public final double getProbability()
     {
-        return description;
+        return probability;
     }
 
-    public final void setDescription(String description)
+    public final void setProbability(double probability)
     {
-        this.description = description;
+        this.probability = probability;
     }
 
-    public final long getAnnotationID()
+    public final double getFalseDiscoveryRate()
     {
-        return annotationID;
+        return falseDiscoveryRate;
     }
 
-    public final void setAnnotationID(long annotationID)
+    public final void setFalseDiscoveryRate(double falseDiscoveryRate)
     {
-        this.annotationID = annotationID;
+        this.falseDiscoveryRate = falseDiscoveryRate;
     }
-
-
 }
diff --git a/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml b/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml
index c68cbeee9d45bd8739f1ea846a5f62ff6a0493e1..3af8c91f3616e68c737e3f4f7dab30daa5513c3e 100644
--- a/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml
+++ b/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml
@@ -33,4 +33,10 @@
           class="ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.db.PhosphoNetXDAOFactory">
         <constructor-arg ref="phosphonetx-db-configuration-context"/>
     </bean>
+    
+    <bean id="phosphonetx-bo-factory"
+          class="ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.BusinessObjectFactory">
+        <constructor-arg ref="dao-factory"/>
+        <constructor-arg ref="phosphonetx-dao-factory"/>
+    </bean>      
 </beans>
\ No newline at end of file
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/MockDataSet.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/MockDataSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..3612944c3a88718c2fa78d573bc56b7d69ad4daf
--- /dev/null
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/MockDataSet.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server;
+
+import java.util.ArrayList;
+
+import net.lemnik.eodsql.DataSet;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class MockDataSet<T> extends ArrayList<T> implements DataSet<T>
+{
+    private static final long serialVersionUID = 1L;
+
+    public void close()
+    {
+    }
+
+    public void disconnect()
+    {
+    }
+
+    public boolean isConnected()
+    {
+        return false;
+    }
+
+}
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ErrorModelTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ErrorModelTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3aace8dbac0cb58a66364b8fc1677e99442d7ffb
--- /dev/null
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ErrorModelTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.MockDataSet;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IPhosphoNetXDAOFactory;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.dataaccess.IProteinQueryDAO;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.IdentifiedProtein;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.ProbabilityFDRMapping;
+
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class ErrorModelTest extends AssertJUnit
+{
+    private static final double TOL = 1e-6;
+    private static final long DATA_SET1_ID = 42;
+    private static final long DATA_SET2_ID = 43;
+    
+    private Mockery context;
+    private IPhosphoNetXDAOFactory specificDAOFactory;
+    private IProteinQueryDAO proteinQueryDAO;
+    private ErrorModel errorModel;
+    
+    @BeforeMethod
+    public void beforeMethod()
+    {
+        context = new Mockery();
+        specificDAOFactory = context.mock(IPhosphoNetXDAOFactory.class);
+        proteinQueryDAO = context.mock(IProteinQueryDAO.class);
+        errorModel = new ErrorModel(specificDAOFactory);
+        context.checking(new Expectations()
+            {
+                {
+                    allowing(specificDAOFactory).getProteinQueryDAO();
+                    will(returnValue(proteinQueryDAO));
+                    
+                    atMost(1).of(proteinQueryDAO).getProbabilityFDRMapping(DATA_SET1_ID);
+                    MockDataSet<ProbabilityFDRMapping> dataSet1 = new MockDataSet<ProbabilityFDRMapping>();
+                    createEntry(dataSet1, 0.4, 0.9);
+                    createEntry(dataSet1, 0.5, 0.95);
+                    will(returnValue(dataSet1));
+                    
+                    atMost(1).of(proteinQueryDAO).getProbabilityFDRMapping(DATA_SET2_ID);
+                    MockDataSet<ProbabilityFDRMapping> dataSet2 = new MockDataSet<ProbabilityFDRMapping>();
+                    createEntry(dataSet2, 1, 1);
+                    createEntry(dataSet2, 0.4, 0.9);
+                    createEntry(dataSet2, 0.1, 0.7);
+                    createEntry(dataSet2, 0.0, 0.6);
+                    will(returnValue(dataSet2));
+                }
+            });
+    }
+    
+    @AfterMethod
+    public void afterMethod()
+    {
+        // To following line of code should also be called at the end of each test method.
+        // Otherwise one do not known which test failed.
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testExact()
+    {
+        IdentifiedProtein protein = create(DATA_SET1_ID, 0.4);
+        errorModel.setFalseDiscoveryRateFor(protein);
+        
+        assertEquals(0.9, protein.getFalseDiscoveryRate(), TOL);
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testInterpolation()
+    {
+        IdentifiedProtein protein = create(DATA_SET1_ID, 0.42);
+        errorModel.setFalseDiscoveryRateFor(protein);
+        
+        assertEquals(0.91, protein.getFalseDiscoveryRate(), TOL);
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testCachedMapping()
+    {
+        IdentifiedProtein protein = create(DATA_SET1_ID, 0.42);
+        errorModel.setFalseDiscoveryRateFor(protein);
+        
+        assertEquals(0.91, protein.getFalseDiscoveryRate(), TOL);
+        
+        protein = create(DATA_SET2_ID, 0.25);
+        errorModel.setFalseDiscoveryRateFor(protein);
+        
+        assertEquals(0.8, protein.getFalseDiscoveryRate(), TOL);
+        
+        protein = create(DATA_SET1_ID, 0.5);
+        errorModel.setFalseDiscoveryRateFor(protein);
+        
+        assertEquals(0.95, protein.getFalseDiscoveryRate(), TOL);
+        
+        context.assertIsSatisfied();
+    }
+    
+    private IdentifiedProtein create(long dataSetID, double probability)
+    {
+        IdentifiedProtein protein = new IdentifiedProtein();
+        protein.setDataSetID(dataSetID);
+        protein.setProbability(probability);
+        return protein;
+    }
+    
+    private void createEntry(MockDataSet<ProbabilityFDRMapping> dataSet, double propability,
+            double falseDiscoveryRate)
+    {
+        ProbabilityFDRMapping mapping = new ProbabilityFDRMapping();
+        mapping.setProbability(propability);
+        mapping.setFalseDiscoveryRate(falseDiscoveryRate);
+        dataSet.add(mapping);
+    }
+}