opencodez

Currency Conversion App in Java

Consuming a rest web service is easy and with RestTemplate Spring has made dealing with these rest services easier. In this article we will build a simple Currency Conversion App that uses the API exposed by Currency Layer

Softwares Used

For this example we will build on top of our Web Application Starter Application. The application then adds support for Rest to consume the currency conversion API. The apilayer provides very reliable and easy to integrate exchange rates api that can be used for any business of currency conversion app. The currency layer api is subscription based and they have many plans. You can choose from them as per your business and application need.  For our tutorial purpose we are using Free Plan.

We are also using caching functionality provided by Google using Guava. This is to minimize the API calls within a particular duration of time. Apart from standard Spring Boot dependencies, below are few dependencies from our pom file.

xdependencyx
	xgroupIdxorg.springframework.bootx/groupIdx
	xartifactIdxspring-boot-starter-thymeleafx/artifactIdx
x/dependencyx
xdependencyx
	xgroupIdxcom.google.guavax/groupIdx
	xartifactIdxguavax/artifactIdx
	xversionx23.5-jrex/versionx
x/dependencyx		

x!--WebJars --x

xdependencyx
	xgroupIdxorg.webjarsx/groupIdx
	xartifactIdxbootstrapx/artifactIdx
	xversionx3.3.6x/versionx
x/dependencyx
xdependencyx
	xgroupIdxorg.webjarsx/groupIdx
	xartifactIdxjqueryx/artifactIdx
	xversionx2.1.4x/versionx
x/dependencyx

x!--/WebJars --x

The currency layer api provides JSON reponse like below

{
  "terms": "https://currencylayer.com/terms",
  "privacy": "https://currencylayer.com/privacy",
    "timestamp": 1430401802,
    "source": "USD",
    "quotes": {
        "USDAED": 3.672982,
        "USDAFN": 57.8936,
        "USDALL": 126.1652,
        "USDAMD": 475.306,
        "USDANG": 1.78952,
        "USDAOA": 109.216875,
        "USDARS": 8.901966,
        "USDAUD": 1.269072,
        "USDAWG": 1.792375,
        "USDAZN": 1.04945,
        "USDBAM": 1.757305,
    [...]
    }
}

To hold this response we have to define our model class. The class will be like

@JsonIgnoreProperties(ignoreUnknown = true)
public class ConversionRates {

    private Boolean success;
    private String terms;
    private String privacy;
    private Boolean historical;
    private String date;
    private String source;
    private Long timestamp;
    
    private Error error;
    private MapxString, Stringx quotes;

    //Standard Setters and Getters 

}

@JsonIgnoreProperties(ignoreUnknown = true)
public class Error {
	
	private String code;
	private String type;
	private String info;

        //Standard Setters and Getters
}

As the api calls need access key to be sent along with request we have kept those details in our application properties.

cache.expiry = 60
api.key=ee662daeace6619e2bcf8797fe0f8460
currency.api.base.uri = http://apilayer.net/api/live

As mentioned earlier we are using simple caching provided by Google Guava. This is to minimize api calls. Once fetched exchange rates we will store them in our cache. These rates will be valid for certain duration, in our case 60 minutes. Below is our class that defines this cache.

@Component
public class RatesCache {

	@Value("${cache.expiry}")
	private int CACHE_DURATION;

	private CachexString, BigDecimalx cache;

	@PostConstruct
	public void init() {
		if (cache == null) {					
			cache = CacheBuilder.newBuilder().expireAfterWrite(CACHE_DURATION, TimeUnit.MINUTES).build();
		}
	}

	public BigDecimal getCachedRate(String code) {
		return cache.getIfPresent(code);
	}

	public void cacheRate(String code, BigDecimal rate) {
		cache.put(code, rate);
	}
}

Now we are all set and now its time to do actual API call. This is done by our business processor method as shown below

public ResponseModel getConvertedValue(Currency source, Currency target, BigDecimal amount) {

	ResponseModel rm = new ResponseModel();
	
	String rateKey = source.getCode()+target.getCode();
	
	BigDecimal cachedRate = cache.getCachedRate(rateKey);
	if(null != cachedRate) {
		rm.setConvertedValue(cachedRate.multiply(amount));
		System.out.println("Returing data for " + rateKey + " from Cache");
		return rm;
	}
	
	MultiValueMapxString, Stringx uriVariables = new LinkedMultiValueMapxString, Stringx();
	uriVariables.add("access_key", env.getProperty(Constants.API_KEY));
	uriVariables.add("currencies", target.getCode());
	uriVariables.add("source", source.getCode());
	uriVariables.add("format", "1");
	
	UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(env.getProperty(Constants.API_BASE_URL)).queryParams(uriVariables).build();

	RestTemplate restTemplate = new RestTemplate();
	ConversionRates rates = restTemplate.getForObject(uriComponents.toUri(), ConversionRates.class);
			
	if(rates.getSuccess()) {
		String cRate = rates.getQuotes().get(rateKey);
		BigDecimal bdr = new BigDecimal(cRate);
		rm.setConvertedValue(bdr.multiply(amount));
		cache.cacheRate(rateKey, bdr);
	} else {
		rm.setError(rates.getError());
		rm.setConvertedValue(BigDecimal.ZERO);
	}
	
	return rm;
}

You can see how we first checking with our cache to fetch the rates and if not available then we do actual rest call and store the rate in our cache for immediate future use.

Here is how it looks when we start our currency conversion app and access it from browserLimitation

As we are using Free Plan from Currency Layer,  this has some limitations like I can only make 1000 requests per month. The source currency switching is not allowed i.e. I can only use USD as source for conversion. If we try to do, we get error response from api. Like below 

But this limitation is  only of plan I chose. With other plans like Pro or Enterprise there are many things which you can achieve easily.

Summary

In this tutorial we have seen how easy it is to implement a currency conversion app. All thanks to Apilayer and Currencylayer. You can download the complete code from our repository. Please feel free to comment your queries or questions.

Download from Git