From dddfad1dabe01f20cab642948ee8b65445df3bcb Mon Sep 17 00:00:00 2001
From: David Hoksza <david.hoksza@uni.lu>
Date: Wed, 22 Nov 2017 13:22:01 +0100
Subject: [PATCH] BRENDA annotator including unit tests.

---
 .../annotation/services/ModelAnnotator.java   |   8 +
 .../services/annotators/BrendaAnnotator.java  | 233 ++++++++++++++
 .../services/annotators/CazyAnnotator.java    |   2 +-
 .../applicationContext-annotation.xml         |   1 +
 .../annotators/AllAnnotatorTests.java         |   1 +
 .../annotators/BrendaAnnotatorTest.java       | 301 ++++++++++++++++++
 .../annotators/TairAnnotatorTest.java         |   4 -
 .../lcsb/mapviewer/model/map/MiriamType.java  |  27 +-
 persist/src/db/11.1.1/fix_db_20171114.sql     |   3 +
 9 files changed, 566 insertions(+), 14 deletions(-)
 create mode 100644 annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotator.java
 create mode 100644 annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotatorTest.java

diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java
index a9eb813ceb..f3b3f463ed 100644
--- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java
+++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java
@@ -16,6 +16,7 @@ import org.apache.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import lcsb.mapviewer.annotation.services.annotators.AnnotatorException;
+import lcsb.mapviewer.annotation.services.annotators.BrendaAnnotator;
 import lcsb.mapviewer.annotation.services.annotators.BiocompendiumAnnotator;
 import lcsb.mapviewer.annotation.services.annotators.CazyAnnotator;
 import lcsb.mapviewer.annotation.services.annotators.ChebiAnnotator;
@@ -69,6 +70,12 @@ public class ModelAnnotator {
 	 * Default class logger.
 	 */
 	private static Logger					 logger						= Logger.getLogger(ModelAnnotator.class);
+	
+	/**
+	 * BRENDA annotator.
+	 */
+	@Autowired
+	private BrendaAnnotator 			brendaAnnotator;
 
 	/**
 	 * Service accessing <a href= "http://biocompendium.embl.de/" >internal
@@ -159,6 +166,7 @@ public class ModelAnnotator {
 		availableAnnotators = new ArrayList<>();
 		defaultAnnotators = new ArrayList<>();
 
+		addAnnotator(brendaAnnotator);
 		addAnnotator(biocompendiumAnnotator);
 		addAnnotator(cazyAnnotator);
 		addAnnotator(chebiBackend);
diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotator.java
new file mode 100644
index 0000000000..cbd483007a
--- /dev/null
+++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotator.java
@@ -0,0 +1,233 @@
+package lcsb.mapviewer.annotation.services.annotators;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import lcsb.mapviewer.annotation.cache.GeneralCacheInterface;
+import lcsb.mapviewer.annotation.cache.SourceNotAvailable;
+import lcsb.mapviewer.annotation.cache.WebPageDownloader;
+import lcsb.mapviewer.annotation.services.ExternalServiceStatus;
+import lcsb.mapviewer.annotation.services.ExternalServiceStatusType;
+import lcsb.mapviewer.annotation.services.IExternalService;
+import lcsb.mapviewer.annotation.services.WrongResponseCodeIOException;
+import lcsb.mapviewer.common.exception.InvalidArgumentException;
+import lcsb.mapviewer.model.map.BioEntity;
+import lcsb.mapviewer.model.map.MiriamData;
+import lcsb.mapviewer.model.map.MiriamType;
+import lcsb.mapviewer.model.map.species.Gene;
+import lcsb.mapviewer.model.map.species.Protein;
+import lcsb.mapviewer.model.map.species.Rna;
+
+/**
+ * This is a class that implements a backend to Brenda enzyme database.
+ * 
+ * @author David Hoksza
+ * 
+ */
+public class BrendaAnnotator extends ElementAnnotator implements IExternalService {
+
+	/**
+	 * Default class logger.
+	 */
+	private static Logger	logger					= Logger.getLogger(BrendaAnnotator.class);
+	
+	/**
+	 * Service used for annotation of entities using {@link MiriamType#TAIR_LOCUS
+	 * TAIR}.
+	 */
+	@Autowired
+	private TairAnnotator		tairAnnotator;	
+
+	/**
+	 * Pattern used for finding UniProt symbol from TAIR info page .
+	 */
+	private Pattern				uniprotECMatcher	= Pattern.compile("EC=((\\d+\\.-\\.-\\.-)|(\\d+\\.\\d+\\.-\\.-)|(\\d+\\.\\d+\\.\\d+\\.-)|(\\d+\\.\\d+\\.\\d+\\.\\d+))");
+
+	/**
+	 * Default constructor.
+	 */
+	public BrendaAnnotator() {
+		super(BrendaAnnotator.class, new Class[] { Protein.class, Gene.class, Rna.class }, false);
+	}
+
+	@Override
+	public ExternalServiceStatus getServiceStatus() {
+		ExternalServiceStatus status = new ExternalServiceStatus(getCommonName(), getUrl());
+
+		GeneralCacheInterface cacheCopy = getCache();
+		this.setCache(null);
+
+		try {
+			Collection<MiriamData> mds = uniprotToBrenda(new MiriamData(MiriamType.UNIPROT, "P12345"));
+
+			status.setStatus(ExternalServiceStatusType.OK);
+			List<String> ecs = new ArrayList<>();
+			if (mds != null) {
+				for (MiriamData md: mds) {
+					ecs.add(md.getResource());
+				}
+			}
+			if (mds == null || mds.size() != 2 || ecs.indexOf("2.6.1.1") < 0 || ecs.indexOf("2.6.1.7") < 0) {
+				status.setStatus(ExternalServiceStatusType.CHANGED);
+			}
+		} catch (Exception e) {
+			logger.error(status.getName() + " is down", e);
+			status.setStatus(ExternalServiceStatusType.DOWN);
+		}
+		this.setCache(cacheCopy);
+		return status;
+	}
+
+	@Override
+	public void annotateElement(BioEntity object) throws AnnotatorException {
+		if (isAnnotatable(object)) {
+			
+			MiriamData mdTair = null;			
+			for (MiriamData md : object.getMiriamData()) {
+				if (md.getDataType().equals(MiriamType.BRENDA)) {
+					return;
+				}
+				else if (md.getDataType().equals(MiriamType.TAIR_LOCUS)) {
+					mdTair = md;
+				}				
+			}			
+			if (mdTair != null) {
+				tairAnnotator.annotateElement(object);
+			}
+			
+			List<MiriamData> mdUniprots = new ArrayList<MiriamData>();
+			for (MiriamData md : object.getMiriamData()) {
+				if (md.getDataType().equals(MiriamType.UNIPROT)) {
+					mdUniprots.add(md);
+				}			
+			}
+			
+			List<String> ecIds = new ArrayList<String>();
+			for (MiriamData mdUniprot: mdUniprots) {
+				Collection<MiriamData> mdBrendas = uniprotToBrenda(mdUniprot);
+				if (mdBrendas != null) {
+					for (MiriamData mdBrenda: mdBrendas) {
+						if (ecIds.indexOf(mdBrenda.getResource()) == -1) {
+							ecIds.add(mdBrenda.getResource());
+							object.addMiriamData(mdBrenda);						
+						}					
+					}					
+				}								
+			}
+		}		
+	}
+
+	/**
+	 * Returns URL to UniProt restfull API about UniProt entry.
+	 * 
+	 * @param uniprotId
+	 *          UniProt identifier
+	 * @return URL to UniProt restfull API about UniProt entry
+	 */
+	private String getUniprotUrl(String uniprotId) {
+		return "http://www.uniprot.org/uniprot/" + uniprotId + ".txt";
+	}
+
+	/**
+	 * Parse UniProt webpage to find information about
+	 * {@link MiriamType#BRENDA}, i.e. EC, and returns them.
+	 * 
+	 * @param pageContent
+	 *          UniProt info page
+	 * @return BRENDA family identifier, i.e. EC, found on the page
+	 */
+	private Collection<MiriamData> parseUniprot(String pageContent) {
+		Collection<MiriamData> result = new HashSet<MiriamData>();
+		Matcher m = uniprotECMatcher.matcher(pageContent);
+		while (m.find()) {
+			result.add(new MiriamData(MiriamType.BRENDA, m.group(1)));
+		}
+		return result;
+	}
+
+	@Override
+	public Object refreshCacheQuery(Object query) throws SourceNotAvailable {
+		String name;
+		String result = null;
+		if (query instanceof String) {
+			name = (String) query;
+			if (name.startsWith("http")) {
+				try {
+					result = getWebPageContent(name);
+				} catch (IOException e) {
+					throw new SourceNotAvailable(e);
+				}
+			} else {
+				throw new InvalidArgumentException("Don't know what to do with query: " + query);
+			}
+		} else {
+			throw new InvalidArgumentException("Don't know what to do with class: " + query.getClass());
+		}
+		return result;
+	}
+
+	/**
+	 * Transform UniProt identifier to CAZy identifier.
+	 * 
+	 * @param UniProt
+	 *          {@link MiriamData} with UniProt identifier
+	 * @return Collection of {@link MiriamData} with BRENDA identifier
+	 * @throws AnnotatorException
+	 *           thrown when there is a problem with accessing external database
+	 */
+	public Collection<MiriamData> uniprotToBrenda(MiriamData uniprot) throws AnnotatorException {
+		if (uniprot == null) {
+			return null;
+		}
+
+		if (!MiriamType.UNIPROT.equals(uniprot.getDataType())) {
+			throw new InvalidArgumentException(MiriamType.UNIPROT + " expected.");
+		}
+
+		String accessUrl = getUniprotUrl(uniprot.getResource());
+		try {
+			String pageContent = getWebPageContent(accessUrl);
+			Collection<MiriamData> collection = parseUniprot(pageContent);
+			if (collection.size() > 0) {
+				return collection;
+			} else {
+				logger.warn("Cannot find EC data for UniProt id: " + uniprot.getResource());
+				return null;
+			}
+		} catch (WrongResponseCodeIOException exception) {
+			logger.warn("Wrong response code when retrieving EC data for UniProt id: " + uniprot.getResource());
+			return null;
+		} catch (IOException exception) {
+			throw new AnnotatorException(exception);
+		}
+	}
+
+	@Override
+	public String getCommonName() {
+		return MiriamType.BRENDA.getCommonName();
+	}
+
+	@Override
+	public String getUrl() {
+		return MiriamType.BRENDA.getDbHomepage();
+	}
+
+	@Override
+	protected WebPageDownloader getWebPageDownloader() {
+		return super.getWebPageDownloader();
+	}
+
+	@Override
+	protected void setWebPageDownloader(WebPageDownloader webPageDownloader) {
+		super.setWebPageDownloader(webPageDownloader);
+	}
+
+}
diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CazyAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CazyAnnotator.java
index 6fb6ef830a..a0fa95d515 100644
--- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CazyAnnotator.java
+++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CazyAnnotator.java
@@ -59,7 +59,7 @@ public class CazyAnnotator extends ElementAnnotator implements IExternalService
 	}
 
 	@Override
-	public ExternalServiceStatus getServiceStatus() { //TODO
+	public ExternalServiceStatus getServiceStatus() { 
 		ExternalServiceStatus status = new ExternalServiceStatus(getCommonName(), getUrl());
 
 		GeneralCacheInterface cacheCopy = getCache();
diff --git a/annotation/src/main/resources/applicationContext-annotation.xml b/annotation/src/main/resources/applicationContext-annotation.xml
index 4e0956e5d1..ee83e7e987 100644
--- a/annotation/src/main/resources/applicationContext-annotation.xml
+++ b/annotation/src/main/resources/applicationContext-annotation.xml
@@ -13,6 +13,7 @@
 	<!-- Annotation services -->
 	
 	<!-- Annotators -->
+	<bean id="BrendaAnnotator" class="lcsb.mapviewer.annotation.services.annotators.BrendaAnnotator"/>
 	<bean id="BiocompendiumAnnotator" class="lcsb.mapviewer.annotation.services.annotators.BiocompendiumAnnotator"/>
 	<bean id="CazyAnnotator" class="lcsb.mapviewer.annotation.services.annotators.CazyAnnotator"/>
 	<bean id="ChebiAnnotator" class="lcsb.mapviewer.annotation.services.annotators.ChebiAnnotator"/>
diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/AllAnnotatorTests.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/AllAnnotatorTests.java
index 1f994c0d3c..8325d7f57c 100644
--- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/AllAnnotatorTests.java
+++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/AllAnnotatorTests.java
@@ -6,6 +6,7 @@ import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
 @SuiteClasses({ AnnotatorExceptionTest.class, //
+		BrendaAnnotatorTest.class, //
 		BiocompendiumAnnotatorTest.class, //
 		CazyAnnotatorTest.class, //
 		ChebiAnnotatorTest.class, //
diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotatorTest.java
new file mode 100644
index 0000000000..87711ddccc
--- /dev/null
+++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/BrendaAnnotatorTest.java
@@ -0,0 +1,301 @@
+package lcsb.mapviewer.annotation.services.annotators;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.io.IOException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import lcsb.mapviewer.annotation.AnnotationTestFunctions;
+import lcsb.mapviewer.annotation.cache.WebPageDownloader;
+import lcsb.mapviewer.annotation.services.ExternalServiceStatusType;
+import lcsb.mapviewer.common.exception.InvalidArgumentException;
+import lcsb.mapviewer.model.map.MiriamData;
+import lcsb.mapviewer.model.map.MiriamType;
+import lcsb.mapviewer.model.map.species.GenericProtein;
+import lcsb.mapviewer.model.map.species.Species;
+
+public class BrendaAnnotatorTest extends AnnotationTestFunctions {
+	
+	@Autowired
+	BrendaAnnotator brendaAnnotator;
+
+	@Before
+	public void setUp() throws Exception {
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+	
+	@Test
+	public void testUniprotToCazy() throws Exception {
+		try {
+			Collection<MiriamData> mds = brendaAnnotator.uniprotToBrenda(new MiriamData(MiriamType.UNIPROT, "P12345"));
+			assertEquals(mds.size(), 2);			
+			MiriamData md1 = new MiriamData(MiriamType.BRENDA, "2.6.1.1");
+			MiriamData md2 = new MiriamData(MiriamType.BRENDA, "2.6.1.7");
+			for (MiriamData md: mds) {
+				assertTrue(md.compareTo(md1) == 0 || md.compareTo(md2) == 0);
+			}
+			
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testAnnotateFromUniprot() throws Exception {
+		try {
+
+			Species protein = new GenericProtein("id");
+			protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "P12345"));
+			
+			brendaAnnotator.annotateElement(protein);
+
+			int cntMds = 0;
+
+			for (MiriamData md : protein.getMiriamData()) {
+				if (md.getDataType().equals(MiriamType.BRENDA)) {
+					cntMds++;
+					assertTrue("Invalid BRENDA annotation extracted from BRENDA annotator", 
+							md.getResource().equals("2.6.1.1") || md.getResource().equals("2.6.1.7") );
+				}
+			}
+			
+			assertTrue("Incorrect BRENDA annotations extracted from BRENDA annotator", cntMds == 2);
+			
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+	
+	@Test
+	public void testAnnotateFromTair() throws Exception {
+		try {
+
+			Species protein = new GenericProtein("id");
+			protein.setName("bla");
+			protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "AT5G48930"));
+			
+			brendaAnnotator.annotateElement(protein);
+
+			MiriamData mdBrenda = null;
+
+			for (MiriamData md : protein.getMiriamData()) {
+				if (md.getDataType().equals(MiriamType.BRENDA)) {
+					mdBrenda = md; //there should be only one EC number for that TAIR<->UNIPROT record
+				}
+			}
+			
+			assertTrue("No BRENDA annotation extracted from BRENDA annotator", mdBrenda != null);
+			assertTrue("Invalid BRENDA annotation extracted from BRENDA annotator based on TAIR", mdBrenda.getResource().equals("2.3.1.133") );
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}		
+	}
+	
+	@Test
+	public void testAnnotateMultipleUniprots() throws Exception {
+		try {
+
+			Species protein = new GenericProtein("id");
+			protein.setName("bla");
+			protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "Q9SG95"));
+			protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "Q12540"));
+			
+			brendaAnnotator.annotateElement(protein);
+
+			assertTrue("Wrong number of BRENDA identifiers extracted from BRENDA annotator", protein.getMiriamData().size() == 4 );
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}		
+	}
+	
+	@Test
+	public void testAnnotateMultipleUniprotsWithIdenticalEC() throws Exception {
+		try {
+
+			Species protein = new GenericProtein("id");
+			protein.setName("bla");
+			protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "Q9SG95"));
+			protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "Q8L5J1"));
+			
+			brendaAnnotator.annotateElement(protein);
+
+			assertTrue("Wrong number of BRENDA identifiers extracted from BRENDA annotator", protein.getMiriamData().size() == 3 );
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}		
+	}
+	
+	@Test
+	public void testAnnotateInvalidEmpty() throws Exception {
+		try {
+			Species protein = new GenericProtein("id");
+			protein.setName("bla");
+			brendaAnnotator.annotateElement(protein);
+
+			assertEquals(0, protein.getMiriamData().size());
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+	
+	@Test
+	public void testAnnotateInvalidUniprot() throws Exception {
+		try {
+			Species protein = new GenericProtein("id");
+			protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "bla"));
+			brendaAnnotator.annotateElement(protein);
+
+			assertEquals(1, protein.getMiriamData().size());
+
+			assertEquals(1, getWarnings().size());
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+	
+	@Test
+	public void testAnnotateInvalidTair() throws Exception {
+		try {
+			Species protein = new GenericProtein("id");
+			protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "bla"));
+			brendaAnnotator.annotateElement(protein);
+
+			assertEquals(1, protein.getMiriamData().size());
+
+			assertEquals(1, getWarnings().size());
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testInvalidUniprotToCazyNull() throws Exception {
+		try {
+			assertNull(brendaAnnotator.uniprotToBrenda(null));
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testInvalidUniprotToCazyWrongMd() throws Exception {
+		try {
+			brendaAnnotator.uniprotToBrenda(new MiriamData(MiriamType.WIKIPEDIA, "bla"));
+			fail("Exception expected");
+		} catch (InvalidArgumentException e) {
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testRefreshInvalidCacheQuery() throws Exception {
+		try {
+			brendaAnnotator.refreshCacheQuery("invalid_query");
+			fail("Exception expected");
+		} catch (InvalidArgumentException e) {
+			assertTrue(e.getMessage().contains("Don't know what to do"));
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testRefreshInvalidCacheQuery2() throws Exception {
+		try {
+			brendaAnnotator.refreshCacheQuery(new Object());
+			fail("Exception expected");
+		} catch (InvalidArgumentException e) {
+			assertTrue(e.getMessage().contains("Don't know what to do"));
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testRefreshCacheQuery() throws Exception {
+		try {
+			Object res = brendaAnnotator.refreshCacheQuery("http://google.cz/");
+			assertNotNull(res);
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testStatus() throws Exception {
+		try {
+			assertEquals(ExternalServiceStatusType.OK, brendaAnnotator.getServiceStatus().getStatus());
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testSimulateDownStatus() throws Exception {
+		WebPageDownloader downloader = brendaAnnotator.getWebPageDownloader();
+		try {
+			WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
+			when(mockDownloader.getFromNetwork(anyString(), anyString(), anyString())).thenThrow(new IOException());
+			brendaAnnotator.setWebPageDownloader(mockDownloader);
+			assertEquals(ExternalServiceStatusType.DOWN, brendaAnnotator.getServiceStatus().getStatus());
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		} finally {
+			brendaAnnotator.setWebPageDownloader(downloader);
+		}
+	}
+
+	@Test
+	public void testSimulateChangedStatus() throws Exception {
+		WebPageDownloader downloader = brendaAnnotator.getWebPageDownloader();
+		try {
+			WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
+			when(mockDownloader.getFromNetwork(anyString(), anyString(), anyString())).thenReturn("GN   Name=ACSS2; Synonyms=ACAS2;");
+			brendaAnnotator.setWebPageDownloader(mockDownloader);
+			assertEquals(ExternalServiceStatusType.CHANGED, brendaAnnotator.getServiceStatus().getStatus());
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		} finally {
+			brendaAnnotator.setWebPageDownloader(downloader);
+		}
+	}
+
+}
diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java
index b2df54d225..10d1bb10e7 100644
--- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java
+++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java
@@ -10,17 +10,13 @@ import static org.mockito.Mockito.when;
 
 import java.io.IOException;
 
-import org.apache.http.client.HttpResponseException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.springframework.beans.factory.annotation.Autowired;
 
-import javassist.NotFoundException;
 import lcsb.mapviewer.annotation.AnnotationTestFunctions;
-import lcsb.mapviewer.annotation.cache.GeneralCacheInterface;
-import lcsb.mapviewer.annotation.cache.GeneralCacheWithExclusion;
 import lcsb.mapviewer.annotation.cache.WebPageDownloader;
 import lcsb.mapviewer.annotation.services.ExternalServiceStatusType;
 import lcsb.mapviewer.common.exception.InvalidArgumentException;
diff --git a/model/src/main/java/lcsb/mapviewer/model/map/MiriamType.java b/model/src/main/java/lcsb/mapviewer/model/map/MiriamType.java
index 1644e64526..831ca861b8 100644
--- a/model/src/main/java/lcsb/mapviewer/model/map/MiriamType.java
+++ b/model/src/main/java/lcsb/mapviewer/model/map/MiriamType.java
@@ -22,15 +22,24 @@ import lcsb.mapviewer.model.map.species.Rna;
  */
 @SuppressWarnings("unchecked")
 public enum MiriamType {
-  /**
-   * Chemical Abstracts Service database: http://commonchemistry.org.
-   */
-  CAS("Chemical Abstracts Service", //
-      "http://commonchemistry.org", //
-      new String[] { "urn:miriam:cas" }, //
-      new Class<?>[] {}, "MIR:00000237"), //
-      
-  /**
+	/**
+	 * Brenda enzyme database: 	http://www.brenda-enzymes.org.
+	 */
+	BRENDA("BRENDA", //
+			"http://www.brenda-enzymes.org", //
+			new String[] { "urn:miriam:brenda" }, //
+			new Class<?>[] {}, "MIR:00100101"), //
+	
+	/**
+	 * Chemical Abstracts Service database: http://commonchemistry.org.
+	 */
+	CAS("Chemical Abstracts Service", //
+			"http://commonchemistry.org", //
+			new String[] { "urn:miriam:cas" }, //
+			new Class<?>[] {}, "MIR:00000237"), //
+	
+	/**
+
 	 * The Carbohydrate-Active Enzyme (CAZy) database: http://www.cazy.org/.
 	 */
 	CAZY("CAZy", //
diff --git a/persist/src/db/11.1.1/fix_db_20171114.sql b/persist/src/db/11.1.1/fix_db_20171114.sql
index 8dd8092a16..771e3f42a9 100644
--- a/persist/src/db/11.1.1/fix_db_20171114.sql
+++ b/persist/src/db/11.1.1/fix_db_20171114.sql
@@ -3,3 +3,6 @@ INSERT INTO cache_type(validity, classname) VALUES (365, 'lcsb.mapviewer.annotat
 
 DELETE FROM cache_type WHERE classname = 'lcsb.mapviewer.annotation.services.annotators.CazyAnnotator';
 INSERT INTO cache_type(validity, classname) VALUES (365, 'lcsb.mapviewer.annotation.services.annotators.CazyAnnotator');
+
+DELETE FROM cache_type WHERE classname = 'lcsb.mapviewer.annotation.services.annotators.BrendaAnnotator';
+INSERT INTO cache_type(validity, classname) VALUES (365, 'lcsb.mapviewer.annotation.services.annotators.BrendaAnnotator');
-- 
GitLab