From 6fb7ce0df033b8958f33fa919102639b7f9051d6 Mon Sep 17 00:00:00 2001 From: Joel Weiser Date: Tue, 22 Jun 2021 17:57:08 -0400 Subject: [PATCH 01/17] updates pom.xml for Sonatype --- pom.xml | 113 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/pom.xml b/pom.xml index 947c4c0..38f60cc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 org.reactome.release @@ -7,9 +9,15 @@ 1.2.2 jar - Release Common Library + ${project.groupId}:${project.artifactId} Common components used throughout the Release process. - https://reactome.org + https://github.com/reactome/release-common-lib + + + Reactome + https://reactome.org + + GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,14 +29,15 @@ Solomon Shorser solomon.shorser@oicr.on.ca - Reactome - https://reactome.org + Ontario Institute for Cancer Research + https://oicr.on.ca + Joel Weiser joel.weiser@oicr.on.ca - Reactome - https://reactome.org + Ontario Institute for Cancer Research + https://oicr.on.ca @@ -38,25 +47,32 @@ https://github.com/reactome/release-common-lib develop - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - UTF-8 - + + 3.7.2 2.1 + 4.5.13 + 2.11.0 + 1.1.4 5.1.0 1.1.0 3.6.0 + 1.2.23-SNAPSHOT + + + 1.8 + UTF-8 + + + 3.8.1 + 1.6 + 3.0.0 + 2.5.3 + 3.0.1 + 2.22.2 + 1.1.0 + 1.6.8 @@ -64,33 +80,39 @@ org.reactome.base reactome-base - 1.2.23-SNAPSHOT + ${reactome.base.version} + org.apache.logging.log4j log4j-core - 2.11.0 + ${log4j.version} + org.apache.httpcomponents httpclient - 4.5.13 + ${httpclient.version} + javax.json javax.json-api - 1.1.4 + ${javax.json.version} + org.glassfish javax.json - 1.1.4 + ${javax.json.version} + commons-net commons-net - 3.7.2 + ${commons.net.version} + org.hamcrest @@ -98,42 +120,49 @@ ${hamcrest.version} test + org.junit.jupiter junit-jupiter-api ${junit.version} test + org.junit.jupiter junit-jupiter-engine ${junit.version} test + org.junit.vintage junit-vintage-engine ${junit.version} test + org.junit.platform junit-platform-launcher ${junit.platform.version} test + org.junit.platform junit-platform-runner ${junit.platform.version} test + org.mockito mockito-inline ${mockito.version} test + org.mockito mockito-junit-jupiter @@ -162,10 +191,10 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + ${maven.compiler.version} - 1.8 - 1.8 + ${jdk.version} + ${jdk.version} @@ -173,7 +202,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.0 + ${maven.javadoc.version} attach-javadocs @@ -188,7 +217,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + ${maven.source.version} attach-sources @@ -203,7 +232,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + ${maven.gpg.version} sign-artifacts @@ -219,7 +248,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + ${nexus-staging-maven-plugin} true ossrh @@ -232,7 +261,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + ${maven.surefire.version} + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + From 74afe3de4b3816327858db40b812ef5c7528df2c Mon Sep 17 00:00:00 2001 From: Joel Weiser Date: Tue, 14 Dec 2021 14:02:26 -0500 Subject: [PATCH 02/17] removes maven tidy plugin temporarily --- pom.xml | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/pom.xml b/pom.xml index 38f60cc..d075e68 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,5 @@ - + 4.0.0 org.reactome.release @@ -264,10 +262,7 @@ ${maven.surefire.version} - + org.apache.maven.plugins maven-release-plugin @@ -280,25 +275,6 @@ v@{project.version} - - - - org.codehaus.mojo - tidy-maven-plugin - ${maven.tidy.version} - - - validate - validate - - check - - - - From 69c8e3ec92d03c6aba68fc1215d1c6b969902dcb Mon Sep 17 00:00:00 2001 From: Joel Weiser Date: Tue, 14 Dec 2021 14:04:10 -0500 Subject: [PATCH 03/17] makes updates for JavaDoc generation --- .../java/org/reactome/release/common/CustomLoggable.java | 4 ++-- .../common/dataretrieval/AuthenticatableFileRetriever.java | 4 ++-- src/main/java/org/reactome/util/general/GUnzipCallable.java | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/reactome/release/common/CustomLoggable.java b/src/main/java/org/reactome/release/common/CustomLoggable.java index 4244e29..3e0c168 100644 --- a/src/main/java/org/reactome/release/common/CustomLoggable.java +++ b/src/main/java/org/reactome/release/common/CustomLoggable.java @@ -31,7 +31,7 @@ public interface CustomLoggable * @param newAppenderName - the name of the new appender. * @param append - append, or not (truncate). * @param level - logging level. - * @return + * @return Logger with configurations from parameters set */ default Logger createLogger(String logFileName, String oldAppenderName, String newAppenderName, boolean append, Level level) { @@ -105,7 +105,7 @@ default Logger createLogger(String logFileName, String oldAppenderName, String n * @param level - logging level. * @param oldLogger - the old Logger - used to log the process of creating the new logger. * @param loggerContainerClassTypeName - name of class that invokes this method (no easy way to get that within the function via reflection, so please set this correctly). - * @return + * @return Logger with configurations from parameters set */ default Logger createLogger(String logFileName, String oldAppenderName, String newAppenderName, boolean append, Level level, Logger oldLogger, String loggerContainerClassTypeName) { diff --git a/src/main/java/org/reactome/release/common/dataretrieval/AuthenticatableFileRetriever.java b/src/main/java/org/reactome/release/common/dataretrieval/AuthenticatableFileRetriever.java index d7f1b0e..11c380b 100644 --- a/src/main/java/org/reactome/release/common/dataretrieval/AuthenticatableFileRetriever.java +++ b/src/main/java/org/reactome/release/common/dataretrieval/AuthenticatableFileRetriever.java @@ -23,7 +23,7 @@ public AuthenticatableFileRetriever() /** * Set the user name. - * @param userName + * @param userName User name to set */ public void setUserName(String userName) { @@ -32,7 +32,7 @@ public void setUserName(String userName) /** * Set the password. - * @param password + * @param password Password to set */ public void setPassword(String password) { diff --git a/src/main/java/org/reactome/util/general/GUnzipCallable.java b/src/main/java/org/reactome/util/general/GUnzipCallable.java index d977d49..e50a8c4 100644 --- a/src/main/java/org/reactome/util/general/GUnzipCallable.java +++ b/src/main/java/org/reactome/util/general/GUnzipCallable.java @@ -16,7 +16,7 @@ * It made more sense to implement Runnable, but ExecutorService.invokeAll only accepts Callables. * The return of this calling call() on this object will be true if unzipping doesn't completely fail, * but you should not rely on this. The source file does *not* get removed by this process. - *

+ * * Example usage:
 	GUnzipCallable datUnzipper1 = new GUnzipCallable(Paths.get("large_file_1.dat.gz"), Paths.get("./large_file_1.dat"));
@@ -50,7 +50,8 @@ public GUnzipCallable(Path src, Path targ)
 	
 	/**
 	 * Decompress a gzip file.
-	 * @throws IOException
+	 * @throws IOException Thrown if unable to create a file stream for the source (zipped file) or
+	 * target (unzipped file) or to read from the source or write to the target.
 	 */
 	public void decompressGzip() throws IOException
 	{

From a1754cb10b4c2c70a6f7bd502e53fe1fec68fa62 Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Tue, 14 Dec 2021 14:05:22 -0500
Subject: [PATCH 04/17] updates log4j version to 2.16.0

---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index d075e68..5b27899 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
 	org.reactome.release
 	release-common-lib
-	1.2.2
+	1.2.3
 	jar
 
 	${project.groupId}:${project.artifactId}
@@ -51,7 +51,7 @@
 		3.7.2
 		2.1
 		4.5.13
-		2.11.0
+		2.16.0
 		1.1.4
 		5.1.0
 		1.1.0

From 51437db8e78d6e294e4028b445c3096cf43cd88c Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Fri, 24 Dec 2021 03:48:30 -0500
Subject: [PATCH 05/17] updates dependencies and pom configuration

---
 pom.xml                                       | 46 +++++++-----
 .../common/dataretrieval/FileRetriever.java   | 74 +++++++------------
 .../cosmic/COSMICFileRetriever.java           | 44 +++++------
 3 files changed, 71 insertions(+), 93 deletions(-)

diff --git a/pom.xml b/pom.xml
index 5b27899..7439d0d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
 	org.reactome.release
 	release-common-lib
-	1.2.3
+	1.2.3-SNAPSHOT
 	jar
 
 	${project.groupId}:${project.artifactId}
@@ -26,9 +26,11 @@
 	
 		
 			Solomon Shorser
-			solomon.shorser@oicr.on.ca
 			Ontario Institute for Cancer Research
 			https://oicr.on.ca
+			
+				Alumni
+			
 		
 
 		
@@ -48,15 +50,15 @@
 
 	
 		
-		3.7.2
-		2.1
+		3.8.0
+		2.2
 		4.5.13
-		2.16.0
+		2.17.0
 		1.1.4
-		5.1.0
-		1.1.0
-		3.6.0
-		1.2.23-SNAPSHOT
+		5.8.2
+		1.8.2
+		3.12.4
+		1.0.0-SNAPSHOT
 
 		
 		1.8
@@ -64,10 +66,10 @@
 
 		
 		3.8.1
-		1.6
-		3.0.0
+		3.0.1
+		3.3.1
 		2.5.3
-		3.0.1
+		3.2.1
 		2.22.2
 		1.1.0
 		1.6.8
@@ -77,8 +79,8 @@
 		
 		
 			org.reactome.base
-			reactome-base
-			${reactome.base.version}
+			reactome-base-core
+			${reactome.base.core.version}
 		
 
 		
@@ -87,12 +89,6 @@
 			${log4j.version}
 		
 
-		
-			org.apache.httpcomponents
-			httpclient
-			${httpclient.version}
-		
-
 		
 			javax.json
 			javax.json-api
@@ -105,6 +101,12 @@
 			${javax.json.version}
 		
 
+		
+			commons-lang
+			commons-lang
+			2.5
+		
+
 		
 			commons-net
 			commons-net
@@ -238,6 +240,10 @@
 						
 							sign
 						
+						
+							${gpg.key}
+							${gpg.key}
+						
 					
 				
 			
diff --git a/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java b/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java
index 6572d56..f1f1669 100644
--- a/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java
+++ b/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java
@@ -1,14 +1,7 @@
 package org.reactome.release.common.dataretrieval;	
 
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.SocketException;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.io.*;
+import java.net.*;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -18,16 +11,7 @@
 
 import org.apache.commons.net.ftp.FTP;
 import org.apache.commons.net.ftp.FTPClient;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.conn.HttpHostConnectException;
-import org.apache.http.conn.UnsupportedSchemeException;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
+
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 
@@ -138,7 +122,7 @@ else if (this.uri.getScheme().equals("ftp") || this.uri.getScheme().equals("sftp
 			}
 			else
 			{
-				throw new UnsupportedSchemeException("URI "+this.uri.toString()+" uses an unsupported scheme: "+this.uri.getScheme());
+				throw new Exception("URI "+this.uri.toString()+" uses an unsupported scheme: "+this.uri.getScheme());
 			}
 		}
 		catch (URISyntaxException e)
@@ -239,49 +223,49 @@ protected void writeInputStreamToFile(InputStream inStream) throws IOException,
 			}
 		}
 	}
-
 	
-	protected void doHttpDownload(Path path) throws HttpHostConnectException, IOException, Exception
+	protected void doHttpDownload(Path path) throws Exception, IOException
 	{
-		this.doHttpDownload(path, HttpClientContext.create());
-	}
-	
-	protected void doHttpDownload(Path path, HttpClientContext context) throws Exception, HttpHostConnectException, IOException
-	{
-		HttpGet get = new HttpGet(this.uri);
+
+		HttpURLConnection urlConnection = (HttpURLConnection) this.uri.toURL().openConnection();
 		//Need to multiply by 1000 because timeouts are in milliseconds.
-		RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
-											.setConnectTimeout(1000 * (int)this.timeout.getSeconds())
-											.setSocketTimeout(1000 * (int)this.timeout.getSeconds())
-											.setConnectionRequestTimeout(1000 * (int)this.timeout.getSeconds()).build();
+		int delayInMilliseconds = 1000 * (int)this.timeout.getSeconds();
+		urlConnection.setConnectTimeout(delayInMilliseconds);
+		urlConnection.setReadTimeout(delayInMilliseconds);
+
+		//HttpGet get = new HttpGet(this.uri);
+		//Need to multiply by 1000 because timeouts are in milliseconds.
+		//RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
+		//									.setConnectTimeout(1000 * (int)this.timeout.getSeconds())
+		//									.setSocketTimeout(1000 * (int)this.timeout.getSeconds())
+		//									.setConnectionRequestTimeout(1000 * (int)this.timeout.getSeconds()).build();
 		
-		get.setConfig(config);
+		//get.setConfig(config);
 		
 		int retries = this.numRetries;
 		boolean done = retries + 1 <= 0;
 		while(!done)
 		{
-			try( CloseableHttpClient client = HttpClients.createDefault();
-				CloseableHttpResponse response = client.execute(get, context);
-				OutputStream outputFile = new FileOutputStream(path.toFile()))
+			try
 			{
-				int statusCode = response.getStatusLine().getStatusCode();
+				int statusCode = urlConnection.getResponseCode();
 				// If status code was not 200, we should print something so that the users know that an unexpected response was received.
-				if (statusCode != HttpStatus.SC_OK)
+				if (statusCode != HttpURLConnection.HTTP_OK)
 				{
 					if (String.valueOf(statusCode).startsWith("4") || String.valueOf(statusCode).startsWith("5"))
 					{
-						logger.error("Response code was 4xx/5xx: {}, Status line is: {}", statusCode, response.getStatusLine());
+						logger.error("Response code was 4xx/5xx: {}, Status line is: {}", statusCode, urlConnection.getResponseMessage());
 					}
 					else
 					{
-						logger.warn("Response was not \"200\". It was: {}", response.getStatusLine());
+						logger.warn("Response was not \"200\". It was: {}", urlConnection.getResponseMessage());
 					}
 				}
-				response.getEntity().writeTo(outputFile);
+
+				Files.copy(urlConnection.getInputStream(), path);
 				done = true;
 			}
-			catch (ConnectTimeoutException e)
+			catch (SocketTimeoutException e)
 			{
 				// we will only be retrying the connection timeouts, defined as the time required to establish a connection.
 				// we will not handle socket timeouts (inactivity that occurs after the connection has been established).
@@ -295,12 +279,6 @@ protected void doHttpDownload(Path path, HttpClientContext context) throws Excep
 					throw new Exception("Connection timed out. Number of retries ("+this.numRetries+") exceeded. No further attempts will be made.", e);
 				}
 			}
-			catch (HttpHostConnectException e)
-			{
-				logger.error("Could not connect to host {} !",get.getURI().getHost());
-				e.printStackTrace();
-				throw e;
-			}
 			catch (IOException e) {
 				logger.error("Exception caught: {}",e.getMessage());
 				throw e;
diff --git a/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java b/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
index d5327b4..dab8e1b 100644
--- a/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
+++ b/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
@@ -5,21 +5,13 @@
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.net.*;
 import java.util.Base64;
 
 import javax.json.Json;
 import javax.json.JsonObject;
 import javax.json.JsonReader;
 
-import org.apache.http.HttpStatus;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
 
 /**
  * Get COSMIC data file.
@@ -45,8 +37,7 @@ public COSMICFileRetriever()
 		super();
 	}
 	
-	private boolean retrieveAndSetCOSMICDownloadURL() throws UnsupportedEncodingException
-	{
+	private boolean retrieveAndSetCOSMICDownloadURL() throws IOException {
 		boolean gotDownloadURLOK = false;
 		// The NEW process to download from COSMIC requires a few steps.
 		// 1) Generate a base64-encoded string of the username and password.
@@ -55,26 +46,29 @@ private boolean retrieveAndSetCOSMICDownloadURL() throws UnsupportedEncodingExce
 		
 		// Encoded string.
 		String encodedUsernamePassword = Base64.getEncoder().encodeToString((this.userName + ":" + this.password).getBytes("UTF-8"));
-		
-		HttpGet get = new HttpGet(this.uri);
+
+		HttpURLConnection urlConnection = (HttpURLConnection) this.uri.toURL().openConnection();
 		//Need to multiply by 1000 because timeouts are in milliseconds.
 		int delayInMilliseconds = 1000 * (int)this.timeout.getSeconds();
-		RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
-											.setConnectTimeout(delayInMilliseconds)
-											.setSocketTimeout(delayInMilliseconds)
-											.setConnectionRequestTimeout(delayInMilliseconds).build();
-		get.setConfig(config);
-		get.setHeader("Authorization", "Basic "+encodedUsernamePassword);
+		urlConnection.setConnectTimeout(delayInMilliseconds);
+		urlConnection.setReadTimeout(delayInMilliseconds);
+		urlConnection.setRequestProperty("Authorization", "Basic "+encodedUsernamePassword);
+		//HttpGet get = new HttpGet(this.uri);
+
+//		RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
+//											.setConnectTimeout(delayInMilliseconds)
+//											.setSocketTimeout(delayInMilliseconds)
+//											.setConnectionRequestTimeout(delayInMilliseconds).build();
+//		get.setConfig(config);
+//		get.setHeader("Authorization", "Basic "+encodedUsernamePassword);
 		String downloadURL = null;
-		try( CloseableHttpClient client = HttpClients.createDefault();
-				CloseableHttpResponse response = client.execute(get) )
-		{
-				int statusCode = response.getStatusLine().getStatusCode();
-				String responseString = EntityUtils.toString(response.getEntity());
+		try {
+				int statusCode = urlConnection.getResponseCode();
+				String responseString = urlConnection.getResponseMessage();
 				// If status code was not 200, we should print something so that the users know that an unexpected response was received.
 				switch (statusCode)
 				{
-					case HttpStatus.SC_OK:
+					case HttpURLConnection.HTTP_OK:
 					// Now we need to turn parse the JSON in responseString and extract the URL to download from.
 					JsonReader reader = Json.createReader(new StringReader(responseString));
 					JsonObject responseObject = reader.readObject();

From 1895a0bac2f13dbcaaeea4f9903b7bd924a7b7be Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Fri, 24 Dec 2021 04:27:21 -0500
Subject: [PATCH 06/17] updates EnsemblServiceResponseProcessor to use
 HttpURLConnection

---
 .../EnsemblServiceResponseProcessor.java      | 161 ++++++++----------
 1 file changed, 74 insertions(+), 87 deletions(-)

diff --git a/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java b/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java
index fab4ebd..fd8756e 100644
--- a/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java
+++ b/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java
@@ -1,19 +1,18 @@
 package org.reactome.util.ensembl;
 
+import java.io.BufferedReader;
 import java.io.IOException;
-import java.net.URI;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URLConnection;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang.StringUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.ParseException;
-import org.apache.http.util.EntityUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -35,7 +34,7 @@ public final class EnsemblServiceResponseProcessor
 
 	/**
 	 * Constructs a new EnsemblServiceResponseProcessor object with the specified logger
-	 * to record information about HttpResponse objects from the EnsEMBL service
+	 * to record information about responses from the EnsEMBL service
 	 * @param logger Logger object to record information about processed responses
 	 */
 	public EnsemblServiceResponseProcessor(Logger logger)
@@ -50,7 +49,7 @@ public EnsemblServiceResponseProcessor(Logger logger)
 
 	/**
 	 * Constructs a new EnsemblServiceResponseProcessor object with a default logger
-	 * to record information about HttpResponse objects from the EnsEMBL service
+	 * to record information about responses from the EnsEMBL service
 	 */
 	public EnsemblServiceResponseProcessor()
 	{
@@ -60,30 +59,15 @@ public EnsemblServiceResponseProcessor()
 	/**
 	 * Processes the HttpResponse from the queried EnsEMBL service, logs relevant information for the end-user, and
 	 * returns the important information of the response as an EnsemblServiceResult object
-	 * @param response HttpResponse from the EnsEMBL service
-	 * @param originalURI The URI of the EnsEMBL service queried (e.g rest.ensembl.org or rest.ensemblgenomes.org)
+	 * @param urlConnection URLConnection from the EnsEMBL service
 	 * @return EnsemblServiceResult object containing the relevant information from the response
-	 * @deprecated use {@link #processResponse(HttpResponse) instead}
+	 * @throws IOException
 	 */
-	@Deprecated
-	public EnsemblServiceResult processResponse(HttpResponse response, URI originalURI)
-	{
-		return processResponse(response);
-	}
-
-	/**
-	 * Processes the HttpResponse from the queried EnsEMBL service, logs relevant information for the end-user, and
-	 * returns the important information of the response as an EnsemblServiceResult object
-	 * @param response HttpResponse from the EnsEMBL service
-	 * @return EnsemblServiceResult object containing the relevant information from the response
-	 */
-	public EnsemblServiceResult processResponse(HttpResponse response)
-	{
-		EnsemblServiceResult result = response.containsHeader("Retry-After") ?
-			processResponseWithRetryAfter(response) :
-			processResponseWhenNotOverQueryQuota(response);
-
-		processXRateLimitRemaining(response);
+	public EnsemblServiceResult processResponse(HttpURLConnection urlConnection) throws IOException {
+		EnsemblServiceResult result = urlConnection.getHeaderFields().get("Retry-After") != null ?
+			processResponseWithRetryAfter(urlConnection) :
+			processResponseWhenNotOverQueryQuota(urlConnection);
+		processXRateLimitRemaining(urlConnection);
 
 		return result;
 	}
@@ -136,7 +120,7 @@ private void initializeTimeoutRetriesRemaining()
 	}
 
 	/**
-	 * Process the query's response object when the response contains the header "Retry-After" which is most likely
+	 * Process the query's URLConnection object when the response contains the header "Retry-After" which is most likely
 	 * to happen if too many requests are sent in a fixed time, using up our quota with the service resulting in a
 	 * need to wait before more requests can be sent.
 	 *
@@ -145,20 +129,18 @@ private void initializeTimeoutRetriesRemaining()
 	 * before retrying.
 	 *
 	 * The response's status message, reason phrase, and headers will also be logged for debugging.
-	 * @param response Response object to process
+	 * @param urlConnection urlConnection URLConnection from the EnsEMBL service
 	 * @return EnsemblServiceResult object with the response status, isOkToRetry, and wait time to retry set
 	 */
-	EnsemblServiceResult processResponseWithRetryAfter(HttpResponse response)
-	{
-		logger.debug("Response message: {} ; Reason code: {}; Headers: {}",
-			response.getStatusLine().toString(),
-			response.getStatusLine().getReasonPhrase(),
-			getHeaders(response)
+	EnsemblServiceResult processResponseWithRetryAfter(HttpURLConnection urlConnection) throws IOException {
+		logger.debug("Response message: {} ; Headers: {}",
+			urlConnection.getResponseMessage(),
+			getHeaders(urlConnection)
 		);
 
 		EnsemblServiceResult result = this.new EnsemblServiceResult();
-		result.setStatus(response.getStatusLine().getStatusCode());
-		result.setWaitTime(processWaitTime(response));
+		result.setStatus(urlConnection.getResponseCode());
+		result.setWaitTime(processWaitTime(urlConnection));
 		result.setOkToRetry(timesWaitedThresholdNotMet());
 
 		if (result.isOkToRetry())
@@ -170,21 +152,21 @@ EnsemblServiceResult processResponseWithRetryAfter(HttpResponse response)
 	}
 
 	/**
-	 * Processes the query's response object when the response does not contain the header "Retry-After".  This method
-	 * will attempt to retrieve the content, and return it as the result value in an EnsemblServiceResult object.
+	 * Processes the query's URLConnection object when the response does not contain the header "Retry-After".  This
+	 * method will attempt to retrieve the content, and return it as the result value in an EnsemblServiceResult
+	 * object.
 	 *
 	 * Certain HTTP response error codes (e.g. 400, 404, 500, 504) will cause the error to be logged and a
 	 * EnsemblServiceResult object without content set in the result value to be returned.
-	 * @param response Response object to process
+	 * @param urlConnection HttpURLConnection from the EnsEMBL service
 	 * @return EnsemblServiceResult object with the content set as its result value if the content was obtained
 	 */
-	EnsemblServiceResult processResponseWhenNotOverQueryQuota(HttpResponse response)
-	{
+	EnsemblServiceResult processResponseWhenNotOverQueryQuota(HttpURLConnection urlConnection) throws IOException {
 		EnsemblServiceResult result = this.new EnsemblServiceResult();
-		result.setStatus(response.getStatusLine().getStatusCode());
-		switch (response.getStatusLine().getStatusCode())
+		result.setStatus(urlConnection.getResponseCode());
+		switch (urlConnection.getResponseCode())
 		{
-			case HttpStatus.SC_GATEWAY_TIMEOUT:
+			case HttpURLConnection.HTTP_GATEWAY_TIMEOUT:
 				logger.error("Request timed out! {} retries remaining", timeoutRetriesRemaining);
 
 				timeoutRetriesRemaining--;
@@ -198,28 +180,27 @@ EnsemblServiceResult processResponseWhenNotOverQueryQuota(HttpResponse response)
 					initializeTimeoutRetriesRemaining();
 				}
 				break;
-			case HttpStatus.SC_OK:
-				result.setResult(parseContent(response));
+			case HttpURLConnection.HTTP_OK:
+				result.setResult(parseContent(urlConnection));
 				break;
-			case HttpStatus.SC_NOT_FOUND:
+			case HttpURLConnection.HTTP_NOT_FOUND:
 				logger.error("Response code 404 ('Not found') received: {}",
-					response.getStatusLine().getReasonPhrase()
+					urlConnection.getResponseMessage()
 				);
 				// If we got 404, don't retry.
 				break;
-			case HttpStatus.SC_INTERNAL_SERVER_ERROR:
-				logger.error("Error 500 detected! Message: {}",response.getStatusLine().getReasonPhrase());
+			case HttpURLConnection.HTTP_INTERNAL_ERROR:
+				logger.error("Error 500 detected! Message: {}", urlConnection.getResponseMessage());
 				// If we get 500 error then we should just get  out of here. Maybe throw an exception?
 				break;
-			case HttpStatus.SC_BAD_REQUEST:
-				logger.trace("Response code was 400 ('Bad request'). Message from server: {}", parseContent(response));
+			case HttpURLConnection.HTTP_BAD_REQUEST:
+				logger.trace("Response code was 400 ('Bad request'). Message from server: {}", parseContent(urlConnection));
 				break;
 			default:
 				// Log any other kind of response.
-				result.setResult(parseContent(response));
-				logger.info("Unexpected response {} with message: {}",
-					response.getStatusLine().getStatusCode(),
-					response.getStatusLine().getReasonPhrase()
+				result.setResult(parseContent(urlConnection));
+				logger.info("Unexpected response: {}",
+					urlConnection.getResponseMessage()
 				);
 				break;
 		}
@@ -232,17 +213,16 @@ EnsemblServiceResult processResponseWhenNotOverQueryQuota(HttpResponse response)
 	 * "X-RateLimit-Remaining".
 	 *
 	 * The number of requests remaining will be logged for debugging if it is a multiple of 1000.  If no
-	 * "X-RateLimit-Remaining" header is found in the response object, its absence will be logged along with
+	 * "X-RateLimit-Remaining" header is found in the URLConnection, its absence will be logged along with
 	 * the HTTP response code received, the response headers received, and the last known number of requests
 	 * remaining.
-	 * @param response Response object from which to obtain the number of requests remaining
+	 * @param urlConnection URLConnection from the EnsEMBL service
 	 */
-	void processXRateLimitRemaining(HttpResponse response)
-	{
-		if (response.containsHeader("X-RateLimit-Remaining"))
+	void processXRateLimitRemaining(HttpURLConnection urlConnection) throws IOException {
+		if (urlConnection.getHeaderFields().get("X-RateLimit-Remaining") != null)
 		{
 			EnsemblServiceResponseProcessor.numRequestsRemaining.set(
-				parseIntegerHeaderValue(response, "X-RateLimit-Remaining")
+				parseIntegerHeaderValue(urlConnection, "X-RateLimit-Remaining")
 			);
 			if (EnsemblServiceResponseProcessor.numRequestsRemaining.get() % 1000 == 0)
 			{
@@ -256,8 +236,8 @@ void processXRateLimitRemaining(HttpResponse response)
 					"Headers returned are: {} " + System.lineSeparator() +
 					"Last known value for remaining was {}",
 
-				response.getStatusLine().toString(),
-				getHeaders(response),
+				urlConnection.getResponseMessage(),
+				getHeaders(urlConnection),
 				EnsemblServiceResponseProcessor.numRequestsRemaining
 			);
 		}
@@ -267,13 +247,13 @@ void processXRateLimitRemaining(HttpResponse response)
 	 * Returns the duration to wait before retrying the query.  The wait time is determined from the "Retry-After"
 	 * header in the query response and is multiplied by the number of times the query has been requested to wait.
 	 * This is to give the increasing server buffer time on each failure requesting the query waits.
-	 * @param response Response object from which to obtain the base wait time
+	 * @param urlConnection URLConnection from the EnsEMBL service
 	 * @return Wait time as Duration object ("Retry-After" value multiplied by the number of times the query has
 	 * requested to wait).
 	 */
-	private Duration processWaitTime(HttpResponse response)
+	private Duration processWaitTime(HttpURLConnection urlConnection)
 	{
-		Duration waitTime = Duration.ofSeconds(parseIntegerHeaderValue(response,"Retry-After"));
+		Duration waitTime = Duration.ofSeconds(parseIntegerHeaderValue(urlConnection,"Retry-After"));
 
 		logger.warn("The server told us to wait, so we will wait for {} * {} before trying again.",
 			waitTime, getWaitMultiplier()
@@ -307,19 +287,21 @@ private boolean timesWaitedThresholdNotMet()
 	}
 
 	/**
-	 * Parses and returns the content from the response object passed.  Content is parsed as UTF-8.  A stacktrace is
-	 * printed to STDERR and an empty String returned if an exception occurs during parsing of the response content.
-	 * @param response Response object to query for content
-	 * @return Content as String from the response object passed.  An empty String if an exception occurs during
+	 * Parses and returns the content from the URLConnection object passed.  Content is parsed as UTF-8.  A stacktrace
+	 * is printed to STDERR and an empty String returned if an exception occurs during parsing of the response content.
+	 * @param urlConnection URLConnection from the EnsEMBL service
+	 * @return Content as String from the URLConnection object passed.  An empty String if an exception occurs during
 	 * parsing of the content.
 	 */
-	private String parseContent(HttpResponse response)
+	private String parseContent(HttpURLConnection urlConnection)
 	{
 		try
 		{
-			return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+			return new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8))
+				.lines()
+				.collect(Collectors.joining(System.lineSeparator()));
 		}
-		catch (ParseException | IOException e)
+		catch (IOException e)
 		{
 			e.printStackTrace();
 			return "";
@@ -328,23 +310,28 @@ private String parseContent(HttpResponse response)
 
 	/**
 	 * Returns the value of a header, which is an integer, as an integer.
-	 * @param response Response object to query for headers
+	 * @param urlConnection URLConnection from the EnsEMBL service
 	 * @param header Specific header for which to get the value (which should be an integer)
-	 * @return Integer value of the header passed for the response object passed
+	 * @return Integer value of the header passed for the URLConnection object passed
 	 */
-	private static int parseIntegerHeaderValue(HttpResponse response, String header) throws NumberFormatException
+	private static int parseIntegerHeaderValue(URLConnection urlConnection, String header) throws NumberFormatException
 	{
-		return Integer.parseInt(response.getHeaders(header)[0].getValue());
+		return Integer.parseInt(urlConnection.getHeaderFields().get(header).get(0));
 	}
 
 	/**
-	 * Returns the headers of the response object as a list of Strings
-	 * @param response Response object from which to get the headers
-	 * @return List of Strings representing the headers from the passed response object
+	 * Returns the headers of the URLConnection object as a list of Strings
+	 * @param urlConnection URLConnection from the EnsEMBL service
+	 * @return List of Strings representing the headers from the passed URLConnection object
 	 */
-	private List getHeaders(HttpResponse response)
+	private List getHeaders(URLConnection urlConnection)
 	{
-		return Arrays.stream(response.getAllHeaders()).map(Object::toString).collect(Collectors.toList());
+		List headerNamesAndValues = new ArrayList<>();
+		for (String headerName : urlConnection.getHeaderFields().keySet()) {
+			String headerValues = urlConnection.getHeaderFields().get(headerName).stream().collect(Collectors.joining(", "));
+			headerNamesAndValues.add(headerName + ": " + headerValues);
+		}
+		return headerNamesAndValues;
 	}
 
 	/**

From debe3cb6522b816bf954ebd0564aa8c62a05f55c Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Fri, 24 Dec 2021 04:28:02 -0500
Subject: [PATCH 07/17] updates version for public API changes to
 2.0.0-SNAPSHOT

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 7439d0d..0406706 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
 	org.reactome.release
 	release-common-lib
-	1.2.3-SNAPSHOT
+	2.0.0-SNAPSHOT
 	jar
 
 	${project.groupId}:${project.artifactId}

From 80b4afb675a6cf7609ec569faac5705a15af9374 Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Sat, 25 Dec 2021 23:34:30 -0500
Subject: [PATCH 08/17] updates file retrieval tests to use URLConnection and
 makes small fixes to main code

---
 .../common/dataretrieval/FileRetriever.java   |  44 ++--
 .../cosmic/COSMICFileRetriever.java           |  46 ++---
 .../dataretrieval/COSMICFileRetrieverIT.java  |   4 +-
 .../dataretrieval/TestFileRetrieval.java      | 190 +++++++-----------
 .../EnsemblServiceResponseProcessorTest.java  | 116 +++++------
 5 files changed, 178 insertions(+), 222 deletions(-)

diff --git a/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java b/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java
index f1f1669..6a763ad 100644
--- a/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java
+++ b/src/main/java/org/reactome/release/common/dataretrieval/FileRetriever.java
@@ -5,6 +5,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.time.Duration;
 import java.time.Instant;
@@ -128,12 +129,12 @@ else if (this.uri.getScheme().equals("ftp") || this.uri.getScheme().equals("sftp
 		catch (URISyntaxException e)
 		{
 			logger.error("Error creating download destination: " + this.destination, e);
-			e.printStackTrace();
+			throw e;
 		}
 		catch (IOException e)
 		{
 			logger.error("Unable to create parent directory of download destination: " + path.toString(), e);
-			e.printStackTrace();
+			throw e;
 		}
 		catch (Exception e)
 		{
@@ -144,12 +145,12 @@ else if (this.uri.getScheme().equals("ftp") || this.uri.getScheme().equals("sftp
 
 	}
 
-	protected void doFtpDownload() throws SocketException, IOException, FileNotFoundException, Exception
+	protected void doFtpDownload() throws Exception
 	{
 		doFtpDownload(null, null);
 	}
 	
-	protected void doFtpDownload(String user, String password) throws SocketException, IOException, FileNotFoundException, Exception
+	protected void doFtpDownload(String user, String password) throws Exception
 	{
 		// user is "anonymous" if provided username is null/empty
 		if (user == null || user.trim().equals(""))
@@ -223,31 +224,23 @@ protected void writeInputStreamToFile(InputStream inStream) throws IOException,
 			}
 		}
 	}
-	
+
 	protected void doHttpDownload(Path path) throws Exception, IOException
 	{
-
-		HttpURLConnection urlConnection = (HttpURLConnection) this.uri.toURL().openConnection();
-		//Need to multiply by 1000 because timeouts are in milliseconds.
-		int delayInMilliseconds = 1000 * (int)this.timeout.getSeconds();
-		urlConnection.setConnectTimeout(delayInMilliseconds);
-		urlConnection.setReadTimeout(delayInMilliseconds);
-
-		//HttpGet get = new HttpGet(this.uri);
-		//Need to multiply by 1000 because timeouts are in milliseconds.
-		//RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
-		//									.setConnectTimeout(1000 * (int)this.timeout.getSeconds())
-		//									.setSocketTimeout(1000 * (int)this.timeout.getSeconds())
-		//									.setConnectionRequestTimeout(1000 * (int)this.timeout.getSeconds()).build();
-		
-		//get.setConfig(config);
-		
 		int retries = this.numRetries;
 		boolean done = retries + 1 <= 0;
 		while(!done)
 		{
 			try
 			{
+				HttpURLConnection urlConnection = getHttpURLConnection();
+
+				//Need to multiply by 1000 because timeouts are in milliseconds.
+				int delayInMilliseconds = 1000 * (int)this.timeout.getSeconds();
+				urlConnection.setConnectTimeout(delayInMilliseconds);
+
+				urlConnection.setReadTimeout(delayInMilliseconds);
+
 				int statusCode = urlConnection.getResponseCode();
 				// If status code was not 200, we should print something so that the users know that an unexpected response was received.
 				if (statusCode != HttpURLConnection.HTTP_OK)
@@ -262,7 +255,7 @@ protected void doHttpDownload(Path path) throws Exception, IOException
 					}
 				}
 
-				Files.copy(urlConnection.getInputStream(), path);
+				Files.copy(urlConnection.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
 				done = true;
 			}
 			catch (SocketTimeoutException e)
@@ -286,6 +279,10 @@ protected void doHttpDownload(Path path) throws Exception, IOException
 		}
 	}
 
+	protected HttpURLConnection getHttpURLConnection() throws IOException {
+		return (HttpURLConnection) this.uri.toURL().openConnection();
+	}
+
 	public Duration getMaxAge()
 	{
 		return this.maxAge;
@@ -347,5 +344,4 @@ public int getNumRetries()
 		return this.numRetries;
 	}
 	
-}
-
+}
\ No newline at end of file
diff --git a/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java b/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
index dab8e1b..6019dc8 100644
--- a/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
+++ b/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
@@ -2,11 +2,10 @@
 
 import org.reactome.release.common.dataretrieval.AuthenticatableFileRetriever;
 
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
 import java.net.*;
 import java.util.Base64;
+import java.util.stream.Collectors;
 
 import javax.json.Json;
 import javax.json.JsonObject;
@@ -53,37 +52,30 @@ private boolean retrieveAndSetCOSMICDownloadURL() throws IOException {
 		urlConnection.setConnectTimeout(delayInMilliseconds);
 		urlConnection.setReadTimeout(delayInMilliseconds);
 		urlConnection.setRequestProperty("Authorization", "Basic "+encodedUsernamePassword);
-		//HttpGet get = new HttpGet(this.uri);
 
-//		RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
-//											.setConnectTimeout(delayInMilliseconds)
-//											.setSocketTimeout(delayInMilliseconds)
-//											.setConnectionRequestTimeout(delayInMilliseconds).build();
-//		get.setConfig(config);
-//		get.setHeader("Authorization", "Basic "+encodedUsernamePassword);
 		String downloadURL = null;
 		try {
 				int statusCode = urlConnection.getResponseCode();
-				String responseString = urlConnection.getResponseMessage();
+				String content = getContent(urlConnection);
 				// If status code was not 200, we should print something so that the users know that an unexpected response was received.
 				switch (statusCode)
 				{
 					case HttpURLConnection.HTTP_OK:
-					// Now we need to turn parse the JSON in responseString and extract the URL to download from.
-					JsonReader reader = Json.createReader(new StringReader(responseString));
-					JsonObject responseObject = reader.readObject();
-					downloadURL = responseObject.get("url").toString().replaceAll("\"", "");
-					// Update this object's downloadURL to be the one that came back from the request
-					this.setDataURL(new URI(downloadURL));
-					logger.info("COSMIC download URL has been set.");
-					gotDownloadURLOK = true;
-					// Call downloadData of FileRetriever to perform a "normal" download, now that the special URL has been set.
-					break;
+						// Now we need to turn parse the JSON in responseString and extract the URL to download from.
+						JsonReader reader = Json.createReader(new StringReader(content));
+						JsonObject responseObject = reader.readObject();
+						downloadURL = responseObject.get("url").toString().replaceAll("\"", "");
+						// Update this object's downloadURL to be the one that came back from the request
+						this.setDataURL(new URI(downloadURL));
+						logger.info("COSMIC download URL has been set.");
+						gotDownloadURLOK = true;
+						// Call downloadData of FileRetriever to perform a "normal" download, now that the special URL has been set.
+						break;
 					
 					default:
-					logger.error("Non-200 status code: {} Response String is: {}", statusCode, responseString);
-					gotDownloadURLOK = false;
-					break;
+						logger.error("Non-200 response: {}", urlConnection.getResponseMessage());
+						gotDownloadURLOK = false;
+						break;
 				}
 		}
 		catch (IOException e)
@@ -112,5 +104,9 @@ protected void downloadData() throws UnsupportedEncodingException, Exception
 			logger.warn("The COSMIC Download URL was not updated successfully, so file download was not attempted.");
 		}
 	}
-	
+
+	private String getContent(HttpURLConnection urlConnection) throws IOException {
+		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
+		return bufferedReader.lines().collect(Collectors.joining(System.lineSeparator()));
+	}
 }
diff --git a/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java b/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java
index 9e603da..f550553 100644
--- a/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java
+++ b/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java
@@ -17,8 +17,6 @@
 
 public class COSMICFileRetrieverIT
 {
-
-	
 	@Test
 	public void testGetCOSMICData() throws URISyntaxException, FileNotFoundException, IOException
 	{
@@ -48,4 +46,4 @@ public void testGetCOSMICData() throws URISyntaxException, FileNotFoundException
 			fail();
 		}
 	}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/reactome/release/common/dataretrieval/TestFileRetrieval.java b/src/test/java/org/reactome/release/common/dataretrieval/TestFileRetrieval.java
index 7cb621e..e630b8c 100644
--- a/src/test/java/org/reactome/release/common/dataretrieval/TestFileRetrieval.java
+++ b/src/test/java/org/reactome/release/common/dataretrieval/TestFileRetrieval.java
@@ -5,12 +5,12 @@
 
 import static org.junit.Assert.assertTrue;
 
-import static org.mockito.ArgumentMatchers.any;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyString;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.InputStream;
+import java.net.SocketTimeoutException;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Paths;
@@ -19,16 +19,7 @@
 import java.util.Random;
 
 import org.apache.commons.net.ftp.FTPClient;
-import org.apache.http.HttpEntity;
-import org.apache.http.StatusLine;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.protocol.HttpContext;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -50,18 +41,7 @@ public class TestFileRetrieval {
 	
 	@Mock
 	URI mockUri;
-	
-	@Mock
-	CloseableHttpClient mockClient;
-	
-	@Mock
-	CloseableHttpResponse mockResponse;
 
-	@Mock
-	StatusLine mockStatusLine;
-	
-	HttpEntity entity = new ByteArrayEntity(MESSAGE_CONTENT.getBytes());
-	
 	@Before
 	public void setup()
 	{
@@ -69,48 +49,38 @@ public void setup()
 	}
 	
 	/**
-	 * Test method for {@link org.reactome.addlinks.dataretrieval.FileRetriever#fetchData()}.
+	 * Test method for {@link org.reactome.release.common.dataretrieval.FileRetriever#fetchData()}.
 	 * @throws Exception 
 	 */
 	@Test
 	public void testFetchData() throws Exception
 	{
-		try(MockedStatic mockedStatic = Mockito.mockStatic(HttpClients.class);)
-		{
-			DataRetriever retriever = new FileRetriever();
-			//retrieve google - it should be pretty easy.
-			URI uri = new URI("http://www.google.com");
-			retriever.setDataURL(uri);
-			
-			Mockito.when(mockResponse.getEntity()).thenReturn(entity);
-			Mockito.when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
-			Mockito.when(mockClient.execute(any(HttpUriRequest.class))).thenReturn(mockResponse);
-			Mockito.when(mockClient.execute(any(HttpUriRequest.class), (HttpContext) any(HttpContext.class))).thenReturn(mockResponse);
-			
-			Mockito.when(HttpClients.createDefault()).thenReturn(mockClient);
-			
-			String dest = "/tmp/testFetchData_"+ String.valueOf((new Random()).nextInt(Integer.MAX_VALUE));
-			retriever.setFetchDestination(dest);
-			Duration age = Duration.of(5, ChronoUnit.SECONDS);
-			retriever.setMaxAge(age);
-			
-			retriever.fetchData();
-			assertTrue(Files.exists(Paths.get(dest)));
-			
-			//Sleep for 6 seconds, and then re-download because the file is stale (MAX AGE was 5 seconds).
-			Thread.sleep(Duration.of(6, ChronoUnit.SECONDS).toMillis());
-			retriever.fetchData();
-			assertTrue(Files.exists(Paths.get(dest)));
-			//now set a longer maxAge.
-			age = Duration.of(100, ChronoUnit.SECONDS);
-			retriever.setMaxAge(age);
-			// this time, the file will not be stale (because maxAge is larger) so nothing will be downloaded.
-			retriever.fetchData();
-			//check that the file exists.
-			assertTrue(Files.exists(Paths.get(dest)));
-		}
+		DataRetriever retriever = new FileRetriever();
+		//retrieve google - it should be pretty easy.
+		URI uri = new URI("https://www.google.com");
+		retriever.setDataURL(uri);
+
+		String dest = "/tmp/testFetchData_"+ String.valueOf((new Random()).nextInt(Integer.MAX_VALUE));
+		retriever.setFetchDestination(dest);
+		Duration age = Duration.of(5, ChronoUnit.SECONDS);
+		retriever.setMaxAge(age);
+
+		retriever.fetchData();
+		assertTrue(Files.exists(Paths.get(dest)));
+
+		//Sleep for 6 seconds, and then re-download because the file is stale (MAX AGE was 5 seconds).
+		Thread.sleep(Duration.of(6, ChronoUnit.SECONDS).toMillis());
+		retriever.fetchData();
+		assertTrue(Files.exists(Paths.get(dest)));
+		//now set a longer maxAge.
+		age = Duration.of(100, ChronoUnit.SECONDS);
+		retriever.setMaxAge(age);
+		// this time, the file will not be stale (because maxAge is larger) so nothing will be downloaded.
+		retriever.fetchData();
+		//check that the file exists.
+		assertTrue(Files.exists(Paths.get(dest)));
 	}
-	
+
 	/**
 	 * Test retrieving FTP data.
 	 * @throws Exception
@@ -153,7 +123,7 @@ public void prepare(FTPClient localMockFTPClient, Context context) throws Throwa
 		}
 	}
 	
-	/**
+/**
 	 * Test FTP Error handling.
 	 * @throws Exception
 	 */
@@ -202,75 +172,67 @@ public void prepare(FTPClient localMockFTPClient, Context context) throws Throwa
 	
 	/**
 	 * Test HTTP Error handling
-	 * @throws ClientProtocolException
-	 * @throws IOException
 	 * @throws Exception
 	 */
 	@Test
-	public void testHttpErr() throws ClientProtocolException, IOException, Exception
+	public void testHttpErr() throws Exception
 	{
-		try(MockedStatic mockedStatic = Mockito.mockStatic(HttpClients.class);)
+		FileRetriever retriever = Mockito.spy(FileRetriever.class);
+		//retrieve google - it should be pretty easy.
+		retriever.setDataURL(new URI("https://www.google.com"));
+		String dest = "/tmp/testFetchData_"+ String.valueOf((new Random()).nextInt(Integer.MAX_VALUE));
+		retriever.setFetchDestination(dest);
+		retriever.setMaxAge(Duration.of(1,ChronoUnit.SECONDS));
+		retriever.setNumRetries(0);
+		retriever.setTimeout(Duration.of(1, ChronoUnit.SECONDS));
+
+		try
 		{
-			Mockito.when(mockClient.execute(any(HttpUriRequest.class), any(HttpContext.class))).thenThrow(new ClientProtocolException("MOCK Generic Error"));
-			
-			Mockito.when(HttpClients.createDefault()).thenReturn(mockClient);
-			
-			DataRetriever retriever = new FileRetriever();
-			//retrieve google - it should be pretty easy.
-			URI uri = new URI("http://www.google.com");
-			retriever.setDataURL(uri);
-			String dest = "/tmp/testFetchData_"+ String.valueOf((new Random()).nextInt(Integer.MAX_VALUE));
-			retriever.setFetchDestination(dest);
-			Duration age = Duration.of(1,ChronoUnit.SECONDS);
-			retriever.setMaxAge(age);
-			((FileRetriever)retriever).setNumRetries(0);
-			((FileRetriever)retriever).setTimeout(Duration.of(1, ChronoUnit.SECONDS));
-			try
-			{
-				retriever.fetchData();
-			}
-			catch (Exception e)
-			{
-				e.printStackTrace();
-				assertTrue(e.getMessage().contains("MOCK Generic Error"));
-			}
+			Mockito.doThrow(new SocketTimeoutException("Dummy text"))
+				.when(retriever).getHttpURLConnection();
+			retriever.fetchData();
+
+			// Test fails here since an fetch data should throw an exception going to the catch block
+			// (that is, fetching data on previous line should throw an exception due to the mock)
+			fail();
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+			assertTrue(e.getMessage().contains("No further attempts will be made"));
 		}
 	}
 	
 	/**
 	 * Test HTTP Retry.
-	 * @throws ClientProtocolException
-	 * @throws IOException
 	 * @throws Exception
 	 */
 	@Test
-	public void testHttpRetry() throws ClientProtocolException, IOException, Exception
+	public void testHttpRetry() throws Exception
 	{
-		try(MockedStatic mockedStatic = Mockito.mockStatic(HttpClients.class);)
+		FileRetriever retriever = Mockito.spy(FileRetriever.class);
+		//retrieve google - it should be pretty easy.
+		retriever.setDataURL(new URI("https://www.google.com"));
+		String dest = "/tmp/testFetchData_"+ String.valueOf((new Random()).nextInt(Integer.MAX_VALUE));
+		retriever.setFetchDestination(dest);
+		retriever.setMaxAge(Duration.of(1,ChronoUnit.SECONDS));
+		retriever.setNumRetries(1);
+		retriever.setTimeout(Duration.of(1, ChronoUnit.SECONDS));
+
+		try
 		{
-			Mockito.when(mockClient.execute(any(HttpUriRequest.class), any(HttpContext.class))).thenThrow(new ConnectTimeoutException("MOCK Timeout Error"));
-			
-			Mockito.when(HttpClients.createDefault()).thenReturn(mockClient);
-			
-			DataRetriever retriever = new FileRetriever();
-			//retrieve google - it should be pretty easy.
-			URI uri = new URI("http://www.google.com");
-			retriever.setDataURL(uri);
-			String dest = "/tmp/testFetchData_"+ String.valueOf((new Random()).nextInt(Integer.MAX_VALUE));
-			retriever.setFetchDestination(dest);
-			Duration age = Duration.of(1,ChronoUnit.SECONDS);
-			retriever.setMaxAge(age);
-			((FileRetriever)retriever).setNumRetries(1);
-			((FileRetriever)retriever).setTimeout(Duration.of(1, ChronoUnit.SECONDS));
-			try
-			{
-				retriever.fetchData();
-			}
-			catch (Exception e)
-			{
-				e.printStackTrace();
-				assertTrue(e.getMessage().contains("Connection timed out. Number of retries (1) exceeded. No further attempts will be made."));
-			}
+			Mockito.doThrow(new SocketTimeoutException("Dummy text"))
+				.when(retriever).getHttpURLConnection();
+			retriever.fetchData();
+
+			// Test fails here since an fetch data should throw an exception going to the catch block
+			// (that is, fetching data on previous line should throw an exception due to the mock)
+			fail();
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+			assertTrue(e.getMessage().contains("Connection timed out. Number of retries (1) exceeded. No further attempts will be made."));
 		}
 	}
 }
\ No newline at end of file
diff --git a/src/test/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessorTest.java b/src/test/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessorTest.java
index 2d0b4f6..138ecde 100644
--- a/src/test/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessorTest.java
+++ b/src/test/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessorTest.java
@@ -6,16 +6,19 @@
 import static org.reactome.util.ensembl.EnsemblServiceResponseProcessor.MAX_TIMES_TO_WAIT;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.net.HttpURLConnection;
 import java.time.Duration;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.commons.lang.StringUtils;
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.entity.BasicHttpEntity;
-import org.apache.http.message.BasicHeader;
 import org.apache.logging.log4j.Logger;
+import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -35,9 +38,8 @@ public class EnsemblServiceResponseProcessorTest {
 	private final String DUMMY_RESPONSE_CONTENT = "Dummy Content";
 
 	@Mock
-	private HttpResponse response;
-	@Mock
-	private StatusLine statusLine;
+	private HttpURLConnection urlConnection;
+
 	@Mock
 	private Logger logger;
 
@@ -54,10 +56,10 @@ public void createEnsemblServiceResponseProcessor() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterSingleResponseWithRetryAfter() {
+	public void correctEnsemblServiceResultAfterSingleResponseWithRetryAfter() throws IOException {
 		mockResponseWithRetryHeader(TOO_MANY_REQUESTS_STATUS_CODE, TOO_MANY_REQUESTS_REASON_PHRASE);
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWithRetryAfter(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWithRetryAfter(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(TOO_MANY_REQUESTS_STATUS_CODE)));
 		assertThat("isOkayToRetry is false", result.isOkToRetry(), is(true));
@@ -67,18 +69,18 @@ public void correctEnsemblServiceResultAfterSingleResponseWithRetryAfter() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterMaximumNumberOfResponsesWithRetryAfter() {
+	public void correctEnsemblServiceResultAfterMaximumNumberOfResponsesWithRetryAfter() throws IOException {
 		mockResponseWithRetryHeader(TOO_MANY_REQUESTS_STATUS_CODE, TOO_MANY_REQUESTS_REASON_PHRASE);
 
 		// First call the method up to the maximum number of times
 		for (int i = 1; i < MAX_TIMES_TO_WAIT; i++) {
-			ensemblServiceResponseProcessor.processResponseWithRetryAfter(response);
+			ensemblServiceResponseProcessor.processResponseWithRetryAfter(urlConnection);
 		}
 
 		final int expectedMultiplierAfterMaximumNumberOfResponsesWithRetry = MAX_TIMES_TO_WAIT;
 		final int expectedWaitTime = RETRY_AFTER_VALUE * expectedMultiplierAfterMaximumNumberOfResponsesWithRetry;
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWithRetryAfter(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWithRetryAfter(urlConnection);
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(TOO_MANY_REQUESTS_STATUS_CODE)));
 		assertThat("isOkayToRetry is true", result.isOkToRetry(), is(false));
 		assertThat("Wait time is unexpected", result.getWaitTime(),
@@ -86,14 +88,14 @@ public void correctEnsemblServiceResultAfterMaximumNumberOfResponsesWithRetryAft
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterOkayResponse() {
-		final int okayStatusCode = HttpStatus.SC_OK;
+	public void correctEnsemblServiceResultAfterOkayResponse() throws IOException {
+		final int okayStatusCode = HttpURLConnection.HTTP_OK;
 		final String okayReasonPhrase = "OK";
 
 		mockResponse(okayStatusCode, okayReasonPhrase);
 		mockResponseEntityWithContent();
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(okayStatusCode)));
 		assertThat("Result content is unexpected", result.getResult(), is(equalTo(DUMMY_RESPONSE_CONTENT)));
@@ -102,13 +104,13 @@ public void correctEnsemblServiceResultAfterOkayResponse() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterGatewayTimeoutResponse() {
-		final int gatewayTimeoutStatusCode = HttpStatus.SC_GATEWAY_TIMEOUT;
+	public void correctEnsemblServiceResultAfterGatewayTimeoutResponse() throws IOException {
+		final int gatewayTimeoutStatusCode = HttpURLConnection.HTTP_GATEWAY_TIMEOUT;
 		final String gatewayTimeoutReasonPhrase = "Gateway Time-out";
 
 		mockResponse(gatewayTimeoutStatusCode, gatewayTimeoutReasonPhrase);
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(gatewayTimeoutStatusCode)));
 		assertThat("Non-empty result", result.getResult(), is(equalTo(StringUtils.EMPTY)));
@@ -117,8 +119,8 @@ public void correctEnsemblServiceResultAfterGatewayTimeoutResponse() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterGatewayTimeoutResponseAndMaximumTimeoutRetriesAttempted() {
-		final int gatewayTimeoutStatusCode = HttpStatus.SC_GATEWAY_TIMEOUT;
+	public void correctEnsemblServiceResultAfterGatewayTimeoutResponseAndMaximumTimeoutRetriesAttempted() throws IOException {
+		final int gatewayTimeoutStatusCode = HttpURLConnection.HTTP_GATEWAY_TIMEOUT;
 		final String gatewayTimeoutReasonPhrase = "Gateway Time-out";
 		final int allowedTimeoutRetries = ensemblServiceResponseProcessor.getTimeoutRetriesRemaining();
 
@@ -126,10 +128,10 @@ public void correctEnsemblServiceResultAfterGatewayTimeoutResponseAndMaximumTime
 
 		// Use all allowed timeout retries
 		for (int i = 1; i < allowedTimeoutRetries; i++) {
-			ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+			ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 		}
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(gatewayTimeoutStatusCode)));
 		assertThat("Non-empty result", result.getResult(), is(equalTo(StringUtils.EMPTY)));
 		assertThat("isOkayToRetry is true", result.isOkToRetry(), is(equalTo(false)));
@@ -140,13 +142,13 @@ public void correctEnsemblServiceResultAfterGatewayTimeoutResponseAndMaximumTime
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterNotFoundResponse() {
-		final int notFoundStatusCode = HttpStatus.SC_NOT_FOUND;
+	public void correctEnsemblServiceResultAfterNotFoundResponse() throws IOException {
+		final int notFoundStatusCode = HttpURLConnection.HTTP_NOT_FOUND;
 		final String notFoundReasonPhrase = "Not Found";
 
 		mockResponse(notFoundStatusCode, notFoundReasonPhrase);
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(notFoundStatusCode)));
 		assertThat("Non-empty result", result.getResult(), is(equalTo(StringUtils.EMPTY)));
@@ -155,13 +157,13 @@ public void correctEnsemblServiceResultAfterNotFoundResponse() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterInternalServerErrorResponse() {
-		final int internalServerErrorStatusCode = HttpStatus.SC_INTERNAL_SERVER_ERROR;
+	public void correctEnsemblServiceResultAfterInternalServerErrorResponse() throws IOException {
+		final int internalServerErrorStatusCode = HttpURLConnection.HTTP_INTERNAL_ERROR;
 		final String internalServerErrorReasonPhrase = "Internal Server Error";
 
 		mockResponse(internalServerErrorStatusCode, internalServerErrorReasonPhrase);
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(internalServerErrorStatusCode)));
 		assertThat("Non-empty result", result.getResult(), is(equalTo(StringUtils.EMPTY)));
@@ -170,14 +172,14 @@ public void correctEnsemblServiceResultAfterInternalServerErrorResponse() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterBadRequestResponse() {
-		final int badRequestStatusCode = HttpStatus.SC_BAD_REQUEST;
+	public void correctEnsemblServiceResultAfterBadRequestResponse() throws IOException {
+		final int badRequestStatusCode = HttpURLConnection.HTTP_BAD_REQUEST;
 		final String badRequestReasonPhrase = "Bad Request";
 
 		mockResponse(badRequestStatusCode, badRequestReasonPhrase);
 		mockResponseEntityWithContent();
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(badRequestStatusCode)));
 		assertThat("Non-empty result", result.getResult(), is(equalTo(StringUtils.EMPTY)));
@@ -186,14 +188,14 @@ public void correctEnsemblServiceResultAfterBadRequestResponse() {
 	}
 
 	@Test
-	public void correctEnsemblServiceResultAfterUnexpectedResponse() {
-		final int movedPermanentlyStatusCode = HttpStatus.SC_MOVED_PERMANENTLY;
+	public void correctEnsemblServiceResultAfterUnexpectedResponse() throws IOException {
+		final int movedPermanentlyStatusCode = HttpURLConnection.HTTP_MOVED_PERM;
 		final String movedPermanentlyReasonPhrase = "Moved Permanently";
 
 		mockResponse(movedPermanentlyStatusCode, movedPermanentlyReasonPhrase);
 		mockResponseEntityWithContent();
 
-		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(response);
+		EnsemblServiceResult result = ensemblServiceResponseProcessor.processResponseWhenNotOverQueryQuota(urlConnection);
 
 		assertThat("Incorrect status code", result.getStatus(), is(equalTo(movedPermanentlyStatusCode)));
 		assertThat("Result content is unexpected", result.getResult(), is(equalTo(DUMMY_RESPONSE_CONTENT)));
@@ -202,54 +204,56 @@ public void correctEnsemblServiceResultAfterUnexpectedResponse() {
 	}
 
 	@Test
-	public void correctNumberOfRequestsRemainingSetAfterResponseWithXRateLimit() {
-		final int okayStatusCode = HttpStatus.SC_OK;
+	public void correctNumberOfRequestsRemainingSetAfterResponseWithXRateLimit() throws IOException {
+		final int okayStatusCode = HttpURLConnection.HTTP_OK;
 		final String okayReasonPhrase = "OK";
 
 		mockResponse(okayStatusCode, okayReasonPhrase);
 		mockXRateLimitHeader();
 
-		ensemblServiceResponseProcessor.processXRateLimitRemaining(response);
+		ensemblServiceResponseProcessor.processXRateLimitRemaining(urlConnection);
 
 		assertThat(
 			EnsemblServiceResponseProcessor.getNumRequestsRemaining(), is(equalTo(X_RATE_LIMIT_REMAINING_VALUE))
 		);
 	}
 
-	private void mockResponseWithRetryHeader(int statusCode, String reasonPhrase) {
+	private void mockResponseWithRetryHeader(int statusCode, String reasonPhrase) throws IOException {
 		mockResponse(statusCode, reasonPhrase);
 		mockRetryHeader();
 	}
 
-	private void mockResponse(int statusCode, String reasonPhrase) {
-		Mockito.when(response.getStatusLine()).thenReturn(statusLine);
-		Mockito.when(statusLine.getStatusCode()).thenReturn(statusCode);
-		Mockito.when(statusLine.getReasonPhrase()).thenReturn(reasonPhrase);
+	private void mockResponse(int statusCode, String reasonPhrase) throws IOException {
+		Mockito.when(urlConnection.getResponseMessage()).thenReturn("HTTP/1.0 " + statusCode + " " + reasonPhrase);
+		Mockito.when(urlConnection.getResponseCode()).thenReturn(statusCode);
 	}
 
 	private void mockRetryHeader() {
 		final String retryAfterHeaderName = "Retry-After";
 		final String retryAfterHeaderValue = Integer.toString(RETRY_AFTER_VALUE);
-		final Header[] headers = new Header[] {new BasicHeader(retryAfterHeaderName, retryAfterHeaderValue)};
-		Mockito.when(response.getAllHeaders()).thenReturn(headers);
-		Mockito.when(response.getHeaders(retryAfterHeaderName)).thenReturn(headers);
+		Map> headers = new HashMap<>();
+		headers.put(retryAfterHeaderName, Collections.singletonList(retryAfterHeaderValue));
+
+//		final Header[] headers = new Header[] {new BasicHeader(retryAfterHeaderName, retryAfterHeaderValue)};
+		Mockito.when(urlConnection.getHeaderFields()).thenReturn(headers);
 	}
 
 	private void mockXRateLimitHeader() {
 		final String xRateLimitHeaderName = "X-RateLimit-Remaining";
 		final String xRateLimitHeaderValue = Integer.toString(X_RATE_LIMIT_REMAINING_VALUE);
-		final Header[] headers = new Header[] {new BasicHeader(xRateLimitHeaderName, xRateLimitHeaderValue)};
-		Mockito.when(response.containsHeader(xRateLimitHeaderName)).thenReturn(true);
-		Mockito.when(response.getAllHeaders()).thenReturn(headers);
-		Mockito.when(response.getHeaders(xRateLimitHeaderName)).thenReturn(headers);
+		//final Header[] headers = new Header[] {new BasicHeader(xRateLimitHeaderName, xRateLimitHeaderValue)};
+
+		Map> headers = new HashMap<>();
+		headers.put(xRateLimitHeaderName, Collections.singletonList(xRateLimitHeaderValue));
+
+		Mockito.when(urlConnection.getHeaderFields()).thenReturn(headers);
 	}
 
-	private void mockResponseEntityWithContent() {
-		final BasicHttpEntity entity = new BasicHttpEntity();
+	private void mockResponseEntityWithContent() throws IOException {
 		final InputStream content = new ByteArrayInputStream(DUMMY_RESPONSE_CONTENT.getBytes());
 
-		entity.setContent(content);
-		entity.setContentLength(DUMMY_RESPONSE_CONTENT.getBytes().length);
-		Mockito.when(response.getEntity()).thenReturn(entity);
+		Mockito.when(urlConnection.getInputStream()).thenReturn(content);
+		Mockito.when(urlConnection.getContent()).thenReturn(content);
+		Mockito.when(urlConnection.getContentLength()).thenReturn(DUMMY_RESPONSE_CONTENT.getBytes().length);
 	}
 }
\ No newline at end of file

From dc4ea4bda3af95cbaa2180f5dc7ea0a93188ee75 Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Thu, 30 Dec 2021 02:32:10 -0500
Subject: [PATCH 09/17] adds plugin to execute integration tests

---
 pom.xml | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/pom.xml b/pom.xml
index 0406706..02452d4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -268,6 +268,27 @@
 				${maven.surefire.version}
 			
 
+			
+			
+				org.apache.maven.plugins
+				maven-failsafe-plugin
+				2.15
+				
+					
+						integration-test
+						
+							integration-test
+						
+					
+					
+						verify
+						
+							verify
+						
+					
+				
+			
+
 			
 			
 				org.apache.maven.plugins

From 77199f208d5eb4df00526682700554852d716651 Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Mon, 1 Apr 2024 14:50:10 -0400
Subject: [PATCH 10/17] adds methods to get curator, release, and general db
 adaptors

---
 .../org/reactome/util/general/DBUtils.java    | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 src/main/java/org/reactome/util/general/DBUtils.java

diff --git a/src/main/java/org/reactome/util/general/DBUtils.java b/src/main/java/org/reactome/util/general/DBUtils.java
new file mode 100644
index 0000000..9ccf814
--- /dev/null
+++ b/src/main/java/org/reactome/util/general/DBUtils.java
@@ -0,0 +1,31 @@
+package org.reactome.util.general;
+
+import org.gk.persistence.MySQLAdaptor;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * @author Joel Weiser (joel.weiser@oicr.on.ca)
+ *         Created 4/1/2024
+ */
+public class DBUtils {
+
+    public static MySQLAdaptor getCuratorDbAdaptor(Properties configProperties) throws SQLException {
+        return getDbAdaptor("curator.database", configProperties);
+    }
+
+    public static MySQLAdaptor getReleaseDbAdaptor(Properties configProperties) throws SQLException {
+        return getDbAdaptor("release.database", configProperties);
+    }
+
+    public static MySQLAdaptor getDbAdaptor(String prefix, Properties configProperties) throws SQLException {
+        return new MySQLAdaptor(
+            configProperties.getProperty(prefix + ".host", "localhost"),
+            configProperties.getProperty(prefix + ".name"),
+            configProperties.getProperty(prefix + ".user", "root"),
+            configProperties.getProperty(prefix + ".password", "root"),
+            Integer.parseInt(configProperties.getProperty(prefix + ".port", "3306"))
+        );
+    }
+}

From 280b44cef99c4d636775df3c006edafa2a62755d Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Mon, 1 Apr 2024 14:51:19 -0400
Subject: [PATCH 11/17] refactors getMySQLAdaptorFromProperties to use DBUtils

---
 .../org/reactome/release/common/ReleaseStep.java  | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/reactome/release/common/ReleaseStep.java b/src/main/java/org/reactome/release/common/ReleaseStep.java
index 61bb223..ecf883f 100644
--- a/src/main/java/org/reactome/release/common/ReleaseStep.java
+++ b/src/main/java/org/reactome/release/common/ReleaseStep.java
@@ -6,6 +6,7 @@
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.gk.persistence.MySQLAdaptor;
+import org.reactome.util.general.DBUtils;
 
 /**
  * This class provides the framework for a release step in Reactome's data-release-pipeline
@@ -24,6 +25,8 @@ public abstract class ReleaseStep
 	 */
 	protected boolean testMode;
 
+
+
 	/**
 	 * Parses a Properties object to obtain database connection information and return a database adaptor as a
 	 * MySQLAdaptor object.
@@ -35,16 +38,8 @@ public abstract class ReleaseStep
 	 * @return Database adaptor as a MySQLAdaptor object
 	 * @throws SQLException Thrown if unable to create a MySQLAdaptor object to return
 	 */
-	protected static MySQLAdaptor getMySQLAdaptorFromProperties(Properties props) throws SQLException
-	{
-
-		String dbHost = props.getProperty("db.host", "localhost");
-		String dbUser = props.getProperty("db.user");
-		String dbPassword = props.getProperty("db.password");
-		String dbName = props.getProperty("db.name");
-		int dbPort = Integer.parseInt(props.getProperty("db.port", "3306"));
-
-		return new MySQLAdaptor(dbHost, dbName, dbUser, dbPassword, dbPort);
+	protected static MySQLAdaptor getMySQLAdaptorFromProperties(Properties props) throws SQLException {
+		return DBUtils.getDbAdaptor("db", props);
 	}
 
 	/**

From e18c61c11578978f4595eb624ab67d9e4a4faefd Mon Sep 17 00:00:00 2001
From: jweiser 
Date: Thu, 27 Feb 2025 23:33:31 -0500
Subject: [PATCH 12/17] replaces javax.json with json-simple

---
 pom.xml                                          | 14 ++++----------
 .../cosmic/COSMICFileRetriever.java              | 16 +++++++++-------
 2 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/pom.xml b/pom.xml
index 02452d4..137df5a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,21 +90,15 @@
 		
 
 		
-			javax.json
-			javax.json-api
-			${javax.json.version}
-		
-
-		
-			org.glassfish
-			javax.json
-			${javax.json.version}
+			com.googlecode.json-simple
+			json-simple
+			${json.simple.version}
 		
 
 		
 			commons-lang
 			commons-lang
-			2.5
+			${commons.lang.version}
 		
 
 		
diff --git a/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java b/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
index 6019dc8..9c30753 100644
--- a/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
+++ b/src/main/java/org/reactome/release/common/dataretrieval/cosmic/COSMICFileRetriever.java
@@ -1,5 +1,8 @@
 package org.reactome.release.common.dataretrieval.cosmic;
 
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
 import org.reactome.release.common.dataretrieval.AuthenticatableFileRetriever;
 
 import java.io.*;
@@ -7,11 +10,6 @@
 import java.util.Base64;
 import java.util.stream.Collectors;
 
-import javax.json.Json;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-
-
 /**
  * Get COSMIC data file.
  * 
@@ -62,8 +60,9 @@ private boolean retrieveAndSetCOSMICDownloadURL() throws IOException {
 				{
 					case HttpURLConnection.HTTP_OK:
 						// Now we need to turn parse the JSON in responseString and extract the URL to download from.
-						JsonReader reader = Json.createReader(new StringReader(content));
-						JsonObject responseObject = reader.readObject();
+
+						JSONParser jsonParser = new JSONParser();
+						JSONObject responseObject = (JSONObject) jsonParser.parse(content);
 						downloadURL = responseObject.get("url").toString().replaceAll("\"", "");
 						// Update this object's downloadURL to be the one that came back from the request
 						this.setDataURL(new URI(downloadURL));
@@ -87,6 +86,9 @@ private boolean retrieveAndSetCOSMICDownloadURL() throws IOException {
 		{
 			logger.error("The URL from COSMIC might be malformed. URL is: \"{}\", Error message is: {}", downloadURL, e.getMessage() );
 			e.printStackTrace();
+		} catch (ParseException e) {
+			logger.error("Error parsing JSON content.  URL is: {}, Error message is {}", downloadURL, e.getMessage());
+			e.printStackTrace();
 		}
 		return gotDownloadURLOK;
 	}

From 5c8953363ec5e03587b7f0455534a9554cc281e8 Mon Sep 17 00:00:00 2001
From: jweiser 
Date: Thu, 27 Feb 2025 23:35:10 -0500
Subject: [PATCH 13/17] adds message to throws IOException in JavaDoc comment

---
 .../reactome/util/ensembl/EnsemblServiceResponseProcessor.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java b/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java
index fd8756e..260bd3f 100644
--- a/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java
+++ b/src/main/java/org/reactome/util/ensembl/EnsemblServiceResponseProcessor.java
@@ -61,7 +61,7 @@ public EnsemblServiceResponseProcessor()
 	 * returns the important information of the response as an EnsemblServiceResult object
 	 * @param urlConnection URLConnection from the EnsEMBL service
 	 * @return EnsemblServiceResult object containing the relevant information from the response
-	 * @throws IOException
+	 * @throws IOException Thrown if unable to get response code or message from urlConnection
 	 */
 	public EnsemblServiceResult processResponse(HttpURLConnection urlConnection) throws IOException {
 		EnsemblServiceResult result = urlConnection.getHeaderFields().get("Retry-After") != null ?

From 14609d6f4bf31b8377637830e0b3c1d8d5e3f020 Mon Sep 17 00:00:00 2001
From: jweiser 
Date: Thu, 27 Feb 2025 23:35:46 -0500
Subject: [PATCH 14/17] formats comment for line length

---
 .../java/org/reactome/util/general/MandatoryProperties.java    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/reactome/util/general/MandatoryProperties.java b/src/main/java/org/reactome/util/general/MandatoryProperties.java
index db340f5..3175d6b 100644
--- a/src/main/java/org/reactome/util/general/MandatoryProperties.java
+++ b/src/main/java/org/reactome/util/general/MandatoryProperties.java
@@ -3,7 +3,8 @@
 import java.util.Properties;
 
 /**
- * A specialised implementation of Properties that will throw an exception if a requested property key is missing or has no value. 
+ * A specialised implementation of Properties that will throw an exception if a requested property key is missing or
+ * has no value.
  * @author sshorser
  *
  */

From 255832b02ec1239c03019bf5b850659475f6b21a Mon Sep 17 00:00:00 2001
From: jweiser 
Date: Thu, 27 Feb 2025 23:36:11 -0500
Subject: [PATCH 15/17] updates COSMIC version in test to v99

---
 .../release/common/dataretrieval/COSMICFileRetrieverIT.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java b/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java
index f550553..b084d14 100644
--- a/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java
+++ b/src/test/java/org/reactome/release/common/dataretrieval/COSMICFileRetrieverIT.java
@@ -29,7 +29,7 @@ public void testGetCOSMICData() throws URISyntaxException, FileNotFoundException
 		retriever.setPassword(props.getProperty("password"));
 		
 		// classification.csv is a pretty small file, ~2MB I think, so a good choice for testing downloads.
-		retriever.setDataURL(new URI("https://cancer.sanger.ac.uk/cosmic/file_download/GRCh38/cosmic/v92/classification.csv"));
+		retriever.setDataURL(new URI("https://cancer.sanger.ac.uk/cosmic/file_download/GRCh38/cosmic/v99/classification.csv"));
 		retriever.setMaxAge(Duration.ofSeconds(10));
 		retriever.setTimeout(Duration.ofSeconds(100));
 		retriever.setFetchDestination("/tmp/cosmic_classification.csv");

From 029d743ec95c3f277763bfb5b7ed28328239d1e5 Mon Sep 17 00:00:00 2001
From: jweiser 
Date: Thu, 27 Feb 2025 23:36:50 -0500
Subject: [PATCH 16/17] updates pom.xml for Sonatype

---
 pom.xml | 76 ++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 45 insertions(+), 31 deletions(-)

diff --git a/pom.xml b/pom.xml
index 137df5a..274b668 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,25 +1,23 @@
 
-
+
 	4.0.0
 
 	org.reactome.release
 	release-common-lib
-	2.0.0-SNAPSHOT
+	2.0.0
 	jar
 
 	${project.groupId}:${project.artifactId}
-	Common components used throughout the Release process.
+	Common components used throughout the Release process
 	https://github.com/reactome/release-common-lib
-
 	
 		Reactome
 		https://reactome.org
 	
-
 	
 		
-			GNU AFFERO GENERAL PUBLIC LICENSE
-			https://www.gnu.org/licenses/agpl-3.0.txt
+			The Apache Software License, Version 2.0
+			https://www.apache.org/licenses/LICENSE-2.0.txt
 		
 	
 
@@ -47,31 +45,43 @@
 		https://github.com/reactome/release-common-lib
 		develop
 	
+	
+	
+		
+			ossrh
+			https://oss.sonatype.org/service/local/staging/deploy/maven2
+		
+		
+			ossrh
+			https://oss.sonatype.org/content/repositories/snapshots
+		
+	
 
 	
 		
+		2.5
 		3.8.0
 		2.2
 		4.5.13
 		2.17.0
-		1.1.4
+		1.1.1
 		5.8.2
 		1.8.2
 		3.12.4
-		1.0.0-SNAPSHOT
+		1.0.1
 
 		
-		1.8
+		11
 		UTF-8
 
 		
 		3.8.1
 		3.0.1
-		3.3.1
+		3.11.2
 		2.5.3
 		3.2.1
 		2.22.2
-		1.1.0
+		1.3.0
 		1.6.8
 	
 
@@ -129,13 +139,6 @@
 			test
 		
 
-		
-			org.junit.vintage
-			junit-vintage-engine
-			${junit.version}
-			test
-		
-
 		
 			org.junit.platform
 			junit-platform-launcher
@@ -205,6 +208,14 @@
 						
 					
 				
+				
+					11
+					-Xdoclint:none
+
+					
+						https://docs.oracle.com/en/java/javase/11/docs/api/
+					
+				
 			
 
 			
@@ -296,18 +307,21 @@
 					v@{project.version}
 				
 			
+
+			
+				org.codehaus.mojo
+				tidy-maven-plugin
+				${maven.tidy.version}
+				
+					
+						validate
+						validate
+						
+							check
+						
+					
+				
+			
 		
 	
-
-	
-	
-		
-			ossrh
-			https://oss.sonatype.org/service/local/staging/deploy/maven2
-		
-		
-			ossrh
-			https://oss.sonatype.org/content/repositories/snapshots
-		
-	
 

From cc717b9f568b5de2cd9a63166d7cff45cf3b9a37 Mon Sep 17 00:00:00 2001
From: Joel Weiser 
Date: Fri, 28 Feb 2025 17:34:23 -0500
Subject: [PATCH 17/17] Create LICENSE

---
 LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)
 create mode 100644 LICENSE

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.