diff --git a/java-spi/exchange-rate-api/pom.xml b/java-spi/exchange-rate-api/pom.xml new file mode 100644 index 0000000000..27651533a9 --- /dev/null +++ b/java-spi/exchange-rate-api/pom.xml @@ -0,0 +1,14 @@ + + 4.0.0 + + exchange-rate-api + jar + + + com.baeldung + java-spi + 1.0.0-SNAPSHOT + + + diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/ExchangeRate.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/ExchangeRate.java new file mode 100644 index 0000000000..afc7ef92ce --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/ExchangeRate.java @@ -0,0 +1,42 @@ +package com.baeldung.rate; + +import com.baeldung.rate.exception.ProviderNotFoundException; +import com.baeldung.rate.spi.ExchangeRateProvider; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; + +public final class ExchangeRate { + + private static final String DEFAULT_PROVIDER = "com.baeldung.rate.spi.YahooFinanceExchangeRateProvider"; + + //All providers + public static List providers() { + List services = new ArrayList<>(); + ServiceLoader loader = ServiceLoader.load(ExchangeRateProvider.class); + loader.forEach(exchangeRateProvider -> { + services.add(exchangeRateProvider); + }); + return services; + } + + //Default provider + public static ExchangeRateProvider provider() { + return provider(DEFAULT_PROVIDER); + } + + //provider by name + public static ExchangeRateProvider provider(String providerName) { + ServiceLoader loader = ServiceLoader.load(ExchangeRateProvider.class); + Iterator it = loader.iterator(); + while (it.hasNext()) { + ExchangeRateProvider provider = it.next(); + if (providerName.equals(provider.getClass().getName())) { + return provider; + } + } + throw new ProviderNotFoundException("Exchange Rate provider " + providerName + " not found"); + } +} diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/api/Quote.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/api/Quote.java new file mode 100644 index 0000000000..577af3b618 --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/api/Quote.java @@ -0,0 +1,44 @@ +package com.baeldung.rate.api; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public class Quote { + private String currency; + private BigDecimal ask; + private BigDecimal bid; + private LocalDate date; + //... + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public BigDecimal getAsk() { + return ask; + } + + public void setAsk(BigDecimal ask) { + this.ask = ask; + } + + public BigDecimal getBid() { + return bid; + } + + public void setBid(BigDecimal bid) { + this.bid = bid; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } +} \ No newline at end of file diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/api/QuoteManager.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/api/QuoteManager.java new file mode 100644 index 0000000000..16416eaf65 --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/api/QuoteManager.java @@ -0,0 +1,8 @@ +package com.baeldung.rate.api; + +import java.time.LocalDate; +import java.util.List; + +public interface QuoteManager { + List getQuotes(String baseCurrency, LocalDate date); +} diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/exception/ProviderNotFoundException.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/exception/ProviderNotFoundException.java new file mode 100644 index 0000000000..3a2d92c4fd --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/exception/ProviderNotFoundException.java @@ -0,0 +1,13 @@ +package com.baeldung.rate.exception; + +public class ProviderNotFoundException extends RuntimeException { + + public ProviderNotFoundException() { + super(); + } + + public ProviderNotFoundException(String message) { + super(message); + } + +} diff --git a/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/spi/ExchangeRateProvider.java b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/spi/ExchangeRateProvider.java new file mode 100644 index 0000000000..c3157b2b03 --- /dev/null +++ b/java-spi/exchange-rate-api/src/main/java/com/baeldung/rate/spi/ExchangeRateProvider.java @@ -0,0 +1,7 @@ +package com.baeldung.rate.spi; + +import com.baeldung.rate.api.QuoteManager; + +public interface ExchangeRateProvider { + QuoteManager create(); +} \ No newline at end of file diff --git a/java-spi/exchange-rate-app/pom.xml b/java-spi/exchange-rate-app/pom.xml new file mode 100644 index 0000000000..7e64cf7438 --- /dev/null +++ b/java-spi/exchange-rate-app/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + exchange-rate-app + jar + + + com.baeldung + java-spi + 1.0.0-SNAPSHOT + + + + + com.baeldung + exchange-rate-api + 1.0.0-SNAPSHOT + + + com.baeldung + exchange-rate-impl + 1.0.0-SNAPSHOT + + + + diff --git a/java-spi/exchange-rate-app/src/main/java/com.baeldung.rate.app/MainApp.java b/java-spi/exchange-rate-app/src/main/java/com.baeldung.rate.app/MainApp.java new file mode 100644 index 0000000000..fd43ed3a85 --- /dev/null +++ b/java-spi/exchange-rate-app/src/main/java/com.baeldung.rate.app/MainApp.java @@ -0,0 +1,21 @@ +package com.baeldung.rate.app; + +import com.baeldung.rate.ExchangeRate; +import com.baeldung.rate.api.Quote; + +import java.time.LocalDate; +import java.util.List; + +public class MainApp { + public static void main(String... args) { + ExchangeRate.providers().forEach(provider -> { + System.out.println("Retreiving USD quotes from provider :" + provider); + List quotes = provider.create().getQuotes("USD", LocalDate.now()); + System.out.println(String.format("%14s%12s|%12s", "","Ask", "Bid")); + System.out.println("----------------------------------------"); + quotes.forEach(quote -> { + System.out.println("USD --> " + quote.getCurrency() + " : " + String.format("%12f|%12f", quote.getAsk(), quote.getBid())); + }); + }); + } +} diff --git a/java-spi/exchange-rate-impl/pom.xml b/java-spi/exchange-rate-impl/pom.xml new file mode 100644 index 0000000000..ec22791351 --- /dev/null +++ b/java-spi/exchange-rate-impl/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + exchange-rate-impl + jar + + + com.baeldung + java-spi + 1.0.0-SNAPSHOT + + + + + com.baeldung + exchange-rate-api + 1.0.0-SNAPSHOT + + + com.squareup.okhttp3 + okhttp + 3.10.0 + + + javax.json.bind + javax.json.bind-api + 1.0 + + + org.eclipse + yasson + 1.0.1 + + + org.glassfish + javax.json + 1.1.2 + + + + diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponse.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponse.java new file mode 100644 index 0000000000..9ba4fb26b0 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponse.java @@ -0,0 +1,26 @@ +package com.baeldung.rate.impl; + +import com.baeldung.rate.api.Quote; + +import java.util.List; + +public class QuoteResponse { + private List result; + private String error; + + public List getResult() { + return result; + } + + public void setResult(List result) { + this.result = result; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponseWrapper.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponseWrapper.java new file mode 100644 index 0000000000..6d7be086f0 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/QuoteResponseWrapper.java @@ -0,0 +1,13 @@ +package com.baeldung.rate.impl; + +public class QuoteResponseWrapper { + private QuoteResponse quoteResponse; + + public QuoteResponse getQuoteResponse() { + return quoteResponse; + } + + public void setQuoteResponse(QuoteResponse quoteResponse) { + this.quoteResponse = quoteResponse; + } +} diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooFinanceExchangeRateProvider.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooFinanceExchangeRateProvider.java new file mode 100644 index 0000000000..9a069cfde4 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooFinanceExchangeRateProvider.java @@ -0,0 +1,13 @@ +package com.baeldung.rate.impl; + +import com.baeldung.rate.api.QuoteManager; +import com.baeldung.rate.spi.ExchangeRateProvider; + +public class YahooFinanceExchangeRateProvider implements ExchangeRateProvider { + + @Override + public QuoteManager create() { + return new YahooQuoteManagerImpl(); + } + +} \ No newline at end of file diff --git a/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooQuoteManagerImpl.java b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooQuoteManagerImpl.java new file mode 100644 index 0000000000..8cc68259be --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/java/com/baeldung/rate/impl/YahooQuoteManagerImpl.java @@ -0,0 +1,65 @@ +package com.baeldung.rate.impl; + +import com.baeldung.rate.api.Quote; +import com.baeldung.rate.api.QuoteManager; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import javax.json.bind.JsonbBuilder; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.LocalDate; +import java.util.Currency; +import java.util.List; + +public class YahooQuoteManagerImpl implements QuoteManager { + + static final String URL_PROVIDER = "https://query1.finance.yahoo.com/v7/finance/quote"; + OkHttpClient client = new OkHttpClient(); + + @Override + public List getQuotes(String baseCurrency, LocalDate date) { + + StringBuilder sb = new StringBuilder(); + Currency.getAvailableCurrencies().forEach(currency -> { + if (!currency.equals(currency.getCurrencyCode())) { + sb.append(baseCurrency).append(currency.getCurrencyCode()).append("=X").append(","); + } + }); + + String value = ""; + try { + value = URLEncoder.encode(sb.toString().substring(0, sb.toString().length() - 1), "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + String queryString = String.format("%s=%s", "symbols", value); + String response = doGetRequest(queryString); + System.out.println(response); + return map(response); + } + + private List map(String response) { + QuoteResponseWrapper qrw = JsonbBuilder.create().fromJson(response, QuoteResponseWrapper.class); + return qrw.getQuoteResponse().getResult(); + } + + String doGetRequest(String queryString) { + String fullUrl = URL_PROVIDER + "?" + queryString; + + System.out.println(fullUrl); + Request request = new Request.Builder() + .url(fullUrl) + .build(); + Response response = null; + try { + response = client.newCall(request).execute(); + return response.body().string(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/java-spi/exchange-rate-impl/src/main/resources/META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider b/java-spi/exchange-rate-impl/src/main/resources/META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider new file mode 100644 index 0000000000..67ba8a8227 --- /dev/null +++ b/java-spi/exchange-rate-impl/src/main/resources/META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider @@ -0,0 +1 @@ +com.baeldung.rate.impl.YahooFinanceExchangeRateProvider \ No newline at end of file diff --git a/java-spi/pom.xml b/java-spi/pom.xml new file mode 100644 index 0000000000..8eaa184d8d --- /dev/null +++ b/java-spi/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + java-spi + pom + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + exchange-rate-api + exchange-rate-impl + exchange-rate-app + + + diff --git a/pom.xml b/pom.xml index 3092c0fa40..0c72a82d2f 100644 --- a/pom.xml +++ b/pom.xml @@ -278,6 +278,7 @@ lucene vraptor persistence-modules/java-cockroachdb + java-spi