From 2ae0c20bfbc6e3671cf4e818b4d85211b3c9b412 Mon Sep 17 00:00:00 2001 From: sernamar Date: Fri, 6 Sep 2024 23:00:59 +0200 Subject: [PATCH 1/6] Add CoinbaseRateProvider implementation to address issue #3 --- .../provider/CoinbaseRateProvider.java | 97 +++++++++++++++++++ .../provider/CoinbaseRateProviderTest.java | 38 ++++++++ 2 files changed, 135 insertions(+) create mode 100644 crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java create mode 100644 crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java diff --git a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java new file mode 100644 index 0000000..6264daf --- /dev/null +++ b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java @@ -0,0 +1,97 @@ +package org.javamoney.shelter.bitcoin.provider; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.javamoney.moneta.convert.ExchangeRateBuilder; +import org.javamoney.moneta.spi.AbstractRateProvider; +import org.javamoney.moneta.spi.DefaultNumberValue; + +import javax.money.convert.*; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +public class CoinbaseRateProvider extends AbstractRateProvider { + private static final RateType RATE_TYPE = RateType.DEFERRED; + private static final ProviderContext CONTEXT = ProviderContextBuilder.of("CoinbaseRateProvider", RATE_TYPE) + .set("providerDescription", "Coinbase - Bitcoin exchange rate provider") + .build(); + private static final String DEFAULT_BASE_CURRENCY = "BTC"; + + private final List supportedCurrencies = new ArrayList<>(); + private final Map rates = new ConcurrentHashMap<>(); + + private final Logger log = Logger.getLogger(getClass().getName()); + + public CoinbaseRateProvider() { + super(CONTEXT); + loadSupportedCurrencies(); + } + + @Override + public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { + var baseCurrency = conversionQuery.getBaseCurrency(); + var termCurrency = conversionQuery.getCurrency(); + var conversionContext = ConversionContext.of(getContext().getProviderName(), RATE_TYPE); + + if (!DEFAULT_BASE_CURRENCY.equals(baseCurrency.getCurrencyCode())) { + throw new CurrencyConversionException(baseCurrency, termCurrency, conversionContext, "Base currency not supported: " + baseCurrency); + } + + if (!supportedCurrencies.contains(termCurrency.getCurrencyCode())) { + throw new CurrencyConversionException(baseCurrency, termCurrency, conversionContext, "Term currency not supported: " + termCurrency); + } + + loadRates(); + + var rate = rates.get(termCurrency.getCurrencyCode()); + if (rate == null) { + throw new CurrencyConversionException(baseCurrency, termCurrency, conversionContext, "Rate not available for currency: " + termCurrency); + } + return new ExchangeRateBuilder(conversionContext) + .setBase(baseCurrency) + .setTerm(termCurrency) + .setFactor(DefaultNumberValue.of(rate)) + .build(); + } + + private void loadSupportedCurrencies() { + try { + var httpClient = HttpClient.newHttpClient(); + var url = "https://api.coinbase.com/v2/currencies"; + var request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + var mapper = new ObjectMapper(); + var jsonNode = mapper.readTree(response.body()); + var dataNode = jsonNode.get("data"); + dataNode.forEach(node -> supportedCurrencies.add(node.get("id").asText())); + } catch (IOException | InterruptedException e) { + log.severe("Failed to load supported currencies from Coinbase API: " + e.getMessage()); + } + } + + private void loadRates() { + try { + var httpClient = HttpClient.newHttpClient(); + var url = "https://api.coinbase.com/v2/exchange-rates?currency=" + DEFAULT_BASE_CURRENCY; + var request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + var mapper = new ObjectMapper(); + var jsonNode = mapper.readTree(response.body()); + var ratesNode = jsonNode.get("data").get("rates"); + ratesNode.fields().forEachRemaining(entry -> rates.put(entry.getKey(), entry.getValue().asDouble())); + } catch (IOException | InterruptedException e) { + log.severe("Failed to load exchange rates from Coinbase API: " + e.getMessage()); + } + } +} diff --git a/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java b/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java new file mode 100644 index 0000000..1de7d23 --- /dev/null +++ b/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java @@ -0,0 +1,38 @@ +package org.javamoney.shelter.bitcoin.provider; + +import org.javamoney.moneta.CurrencyUnitBuilder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.money.UnknownCurrencyException; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; + +public class CoinbaseRateProviderTest { + private static CoinbaseRateProvider coinbaseRateProvider; + + @BeforeClass + public static void setUpBeforeClass() { + coinbaseRateProvider = new CoinbaseRateProvider(); + CurrencyUnitBuilder.of("BTC", "BitcoinProvider") + .setDefaultFractionDigits(8) + .build(true); + } + + @AfterClass + public static void tearDownAfterClass() { + coinbaseRateProvider = null; + } + + @Test + public void testGetExchangeRate() { + assertNotNull(coinbaseRateProvider.getExchangeRate("BTC", "USD")); + } + + @Test + public void testGetExchangeRateWithInvalidCurrency() { + assertThrows(UnknownCurrencyException.class, () -> coinbaseRateProvider.getExchangeRate("BTC", "INVALID")); + } +} \ No newline at end of file From c9ffad6c9fada4d777fa3fe5f9c46c46bab717b4 Mon Sep 17 00:00:00 2001 From: sernamar Date: Sat, 7 Sep 2024 05:46:41 +0200 Subject: [PATCH 2/6] Add tests for not supported base or term currency --- .../bitcoin/provider/CoinbaseRateProviderTest.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java b/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java index 1de7d23..2a1d0c4 100644 --- a/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java +++ b/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import javax.money.UnknownCurrencyException; +import javax.money.convert.CurrencyConversionException; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; @@ -16,7 +17,7 @@ public class CoinbaseRateProviderTest { @BeforeClass public static void setUpBeforeClass() { coinbaseRateProvider = new CoinbaseRateProvider(); - CurrencyUnitBuilder.of("BTC", "BitcoinProvider") + CurrencyUnitBuilder.of("BTC", "CoinbaseRateProvider") .setDefaultFractionDigits(8) .build(true); } @@ -32,7 +33,12 @@ public void testGetExchangeRate() { } @Test - public void testGetExchangeRateWithInvalidCurrency() { - assertThrows(UnknownCurrencyException.class, () -> coinbaseRateProvider.getExchangeRate("BTC", "INVALID")); + public void testGetExchangeRateWithNotSupportedBaseCurrency() { + assertThrows(CurrencyConversionException.class, () -> coinbaseRateProvider.getExchangeRate("USD", "BTC")); + } + + @Test + public void testGetExchangeRateWithNotSupportedTermCurrency() { + assertThrows(CurrencyConversionException.class, () -> coinbaseRateProvider.getExchangeRate("BTC", "ZWL")); } } \ No newline at end of file From faf8f2903ff35cc698d7473a65a3661260ba708f Mon Sep 17 00:00:00 2001 From: sernamar Date: Sat, 7 Sep 2024 05:48:48 +0200 Subject: [PATCH 3/6] Replace `var` with explicit types for Java 8 compatibility --- .../provider/CoinbaseRateProvider.java | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java index 6264daf..3850d64 100644 --- a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java +++ b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java @@ -1,10 +1,12 @@ package org.javamoney.shelter.bitcoin.provider; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.javamoney.moneta.convert.ExchangeRateBuilder; import org.javamoney.moneta.spi.AbstractRateProvider; import org.javamoney.moneta.spi.DefaultNumberValue; +import javax.money.CurrencyUnit; import javax.money.convert.*; import java.io.IOException; import java.net.URI; @@ -12,6 +14,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; +import java.util.Currency; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -36,9 +39,9 @@ public CoinbaseRateProvider() { @Override public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { - var baseCurrency = conversionQuery.getBaseCurrency(); - var termCurrency = conversionQuery.getCurrency(); - var conversionContext = ConversionContext.of(getContext().getProviderName(), RATE_TYPE); + CurrencyUnit baseCurrency = conversionQuery.getBaseCurrency(); + CurrencyUnit termCurrency = conversionQuery.getCurrency(); + ConversionContext conversionContext = ConversionContext.of(getContext().getProviderName(), RATE_TYPE); if (!DEFAULT_BASE_CURRENCY.equals(baseCurrency.getCurrencyCode())) { throw new CurrencyConversionException(baseCurrency, termCurrency, conversionContext, "Base currency not supported: " + baseCurrency); @@ -50,7 +53,7 @@ public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { loadRates(); - var rate = rates.get(termCurrency.getCurrencyCode()); + Number rate = rates.get(termCurrency.getCurrencyCode()); if (rate == null) { throw new CurrencyConversionException(baseCurrency, termCurrency, conversionContext, "Rate not available for currency: " + termCurrency); } @@ -63,15 +66,15 @@ public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { private void loadSupportedCurrencies() { try { - var httpClient = HttpClient.newHttpClient(); - var url = "https://api.coinbase.com/v2/currencies"; - var request = HttpRequest.newBuilder() + HttpClient httpClient = HttpClient.newHttpClient(); + String url = "https://api.coinbase.com/v2/currencies"; + HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .build(); - var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - var mapper = new ObjectMapper(); - var jsonNode = mapper.readTree(response.body()); - var dataNode = jsonNode.get("data"); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(response.body()); + JsonNode dataNode = jsonNode.get("data"); dataNode.forEach(node -> supportedCurrencies.add(node.get("id").asText())); } catch (IOException | InterruptedException e) { log.severe("Failed to load supported currencies from Coinbase API: " + e.getMessage()); @@ -80,15 +83,15 @@ private void loadSupportedCurrencies() { private void loadRates() { try { - var httpClient = HttpClient.newHttpClient(); - var url = "https://api.coinbase.com/v2/exchange-rates?currency=" + DEFAULT_BASE_CURRENCY; - var request = HttpRequest.newBuilder() + HttpClient httpClient = HttpClient.newHttpClient(); + String url = "https://api.coinbase.com/v2/exchange-rates?currency=" + DEFAULT_BASE_CURRENCY; + HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .build(); - var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - var mapper = new ObjectMapper(); - var jsonNode = mapper.readTree(response.body()); - var ratesNode = jsonNode.get("data").get("rates"); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(response.body()); + JsonNode ratesNode = jsonNode.get("data").get("rates"); ratesNode.fields().forEachRemaining(entry -> rates.put(entry.getKey(), entry.getValue().asDouble())); } catch (IOException | InterruptedException e) { log.severe("Failed to load exchange rates from Coinbase API: " + e.getMessage()); From b6c0a5330d6d2296f5beadaba77785b93e91d367 Mon Sep 17 00:00:00 2001 From: sernamar Date: Mon, 9 Sep 2024 05:09:53 +0200 Subject: [PATCH 4/6] Throw a `MonetaryException` if the HTTP request to Coinbase API fails --- .../shelter/bitcoin/provider/CoinbaseRateProvider.java | 9 +++------ .../bitcoin/provider/CoinbaseRateProviderTest.java | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java index 3850d64..4e8b8c8 100644 --- a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java +++ b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java @@ -7,6 +7,7 @@ import org.javamoney.moneta.spi.DefaultNumberValue; import javax.money.CurrencyUnit; +import javax.money.MonetaryException; import javax.money.convert.*; import java.io.IOException; import java.net.URI; @@ -14,11 +15,9 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; -import java.util.Currency; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; public class CoinbaseRateProvider extends AbstractRateProvider { private static final RateType RATE_TYPE = RateType.DEFERRED; @@ -30,8 +29,6 @@ public class CoinbaseRateProvider extends AbstractRateProvider { private final List supportedCurrencies = new ArrayList<>(); private final Map rates = new ConcurrentHashMap<>(); - private final Logger log = Logger.getLogger(getClass().getName()); - public CoinbaseRateProvider() { super(CONTEXT); loadSupportedCurrencies(); @@ -77,7 +74,7 @@ private void loadSupportedCurrencies() { JsonNode dataNode = jsonNode.get("data"); dataNode.forEach(node -> supportedCurrencies.add(node.get("id").asText())); } catch (IOException | InterruptedException e) { - log.severe("Failed to load supported currencies from Coinbase API: " + e.getMessage()); + throw new MonetaryException("Failed to load supported currencies from Coinbase API", e); } } @@ -94,7 +91,7 @@ private void loadRates() { JsonNode ratesNode = jsonNode.get("data").get("rates"); ratesNode.fields().forEachRemaining(entry -> rates.put(entry.getKey(), entry.getValue().asDouble())); } catch (IOException | InterruptedException e) { - log.severe("Failed to load exchange rates from Coinbase API: " + e.getMessage()); + throw new MonetaryException("Failed to load exchange rates from Coinbase API", e); } } } diff --git a/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java b/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java index 2a1d0c4..fb22ac6 100644 --- a/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java +++ b/crypto-currency/bitcoin/src/test/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProviderTest.java @@ -5,7 +5,6 @@ import org.junit.BeforeClass; import org.junit.Test; -import javax.money.UnknownCurrencyException; import javax.money.convert.CurrencyConversionException; import static org.junit.Assert.assertNotNull; From 394cb844e3c1d88f7832b26f579ec21c6efa9920 Mon Sep 17 00:00:00 2001 From: sernamar Date: Mon, 9 Sep 2024 05:42:33 +0200 Subject: [PATCH 5/6] Add try-with-resources construct to ensure proper closing of `HttpClient` after usage --- .../shelter/bitcoin/provider/CoinbaseRateProvider.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java index 4e8b8c8..82b5762 100644 --- a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java +++ b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java @@ -62,8 +62,7 @@ public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { } private void loadSupportedCurrencies() { - try { - HttpClient httpClient = HttpClient.newHttpClient(); + try (HttpClient httpClient = HttpClient.newHttpClient()){ String url = "https://api.coinbase.com/v2/currencies"; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) @@ -79,8 +78,7 @@ private void loadSupportedCurrencies() { } private void loadRates() { - try { - HttpClient httpClient = HttpClient.newHttpClient(); + try (HttpClient httpClient = HttpClient.newHttpClient()){ String url = "https://api.coinbase.com/v2/exchange-rates?currency=" + DEFAULT_BASE_CURRENCY; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) From a8ec1b6a01c099491a62f66c88c9384af5c92803 Mon Sep 17 00:00:00 2001 From: sernamar Date: Mon, 9 Sep 2024 05:46:08 +0200 Subject: [PATCH 6/6] Switch to Apache HttpClient for JDK 8 compatibility --- crypto-currency/bitcoin/pom.xml | 5 ++ .../provider/CoinbaseRateProvider.java | 46 +++++++++---------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/crypto-currency/bitcoin/pom.xml b/crypto-currency/bitcoin/pom.xml index 7f36c48..c8c716d 100644 --- a/crypto-currency/bitcoin/pom.xml +++ b/crypto-currency/bitcoin/pom.xml @@ -166,6 +166,11 @@ pom compile + + org.apache.httpcomponents + httpclient + 4.5.14 + diff --git a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java index 82b5762..68e4aa7 100644 --- a/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java +++ b/crypto-currency/bitcoin/src/main/java/org/javamoney/shelter/bitcoin/provider/CoinbaseRateProvider.java @@ -2,6 +2,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +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.javamoney.moneta.convert.ExchangeRateBuilder; import org.javamoney.moneta.spi.AbstractRateProvider; import org.javamoney.moneta.spi.DefaultNumberValue; @@ -10,10 +14,6 @@ import javax.money.MonetaryException; import javax.money.convert.*; import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -62,33 +62,31 @@ public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { } private void loadSupportedCurrencies() { - try (HttpClient httpClient = HttpClient.newHttpClient()){ + try (CloseableHttpClient httpClient = HttpClients.createDefault()){ String url = "https://api.coinbase.com/v2/currencies"; - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(url)) - .build(); - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - ObjectMapper mapper = new ObjectMapper(); - JsonNode jsonNode = mapper.readTree(response.body()); - JsonNode dataNode = jsonNode.get("data"); - dataNode.forEach(node -> supportedCurrencies.add(node.get("id").asText())); - } catch (IOException | InterruptedException e) { + HttpGet request = new HttpGet(url); + try (CloseableHttpResponse response = httpClient.execute(request)){ + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); + JsonNode dataNode = jsonNode.get("data"); + dataNode.forEach(node -> supportedCurrencies.add(node.get("id").asText())); + } + } catch (IOException e) { throw new MonetaryException("Failed to load supported currencies from Coinbase API", e); } } private void loadRates() { - try (HttpClient httpClient = HttpClient.newHttpClient()){ + try (CloseableHttpClient httpClient = HttpClients.createDefault()){ String url = "https://api.coinbase.com/v2/exchange-rates?currency=" + DEFAULT_BASE_CURRENCY; - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(url)) - .build(); - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - ObjectMapper mapper = new ObjectMapper(); - JsonNode jsonNode = mapper.readTree(response.body()); - JsonNode ratesNode = jsonNode.get("data").get("rates"); - ratesNode.fields().forEachRemaining(entry -> rates.put(entry.getKey(), entry.getValue().asDouble())); - } catch (IOException | InterruptedException e) { + HttpGet request = new HttpGet(url); + try (CloseableHttpResponse response = httpClient.execute(request)){ + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); + JsonNode ratesNode = jsonNode.get("data").get("rates"); + ratesNode.fields().forEachRemaining(entry -> rates.put(entry.getKey(), entry.getValue().asDouble())); + } + } catch (IOException e) { throw new MonetaryException("Failed to load exchange rates from Coinbase API", e); } }