com.coinblesk.server.service.ForexService.java Source code

Java tutorial

Introduction

Here is the source code for com.coinblesk.server.service.ForexService.java

Source

/*
 * Copyright 2016 The Coinblesk team and the CSG Group at University of Zurich
 *
 * 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.
 */
package com.coinblesk.server.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.Gson;

/**
 *
 * Service that provides bitcoin and forex exchange rates. This class also
 * caches exchange rates.
 *
 * @author rvoellmy
 * @author Thomas Bocek
 *
 */
@Service
final public class ForexService {

    // 18min
    public final static int CACHING_TIME_RATE_MILLIS = 18 * 60 * 1000;
    // 2 days
    public final static int CACHING_TIME_SYMBOL_MILLIS = 2 * 24 * 60 * 60 * 1000;

    private final static String PLACEHOLDER = "{{PLACEHOLDER}}";
    private final static String YAHOO_API = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20("
            + PLACEHOLDER + ")&format=json&env=store://datatables.org/alltableswithkeys";

    private final Cache<String, BigDecimal> exchangeRatesCache = CacheBuilder.newBuilder()
            .expireAfterWrite(CACHING_TIME_RATE_MILLIS, TimeUnit.MILLISECONDS).build();

    private final Cache<String, Boolean> exchangeRatesSymbolCache = CacheBuilder.newBuilder()
            .expireAfterWrite(CACHING_TIME_SYMBOL_MILLIS, TimeUnit.MILLISECONDS).build();

    /**
     * Returns the exchange rate of 1 USD against the specified currency (by
     * default CHF)
     *
     * @return the forex exchange rate
     * @throws Exception
     */
    public BigDecimal getExchangeRate(final String symbolFrom, final String symbolTo) throws Exception {
        final String pair = symbolFrom + symbolTo;
        return getExchangeRates(pair).get(pair);
    }

    public Map<String, BigDecimal> getExchangeRates(final String... pairs) throws Exception {
        // mark as used
        Map<String, BigDecimal> exchangeRates = new HashMap<>(pairs.length);
        // this empty most of the times:
        List<String> unknowRates = new ArrayList<>(1);
        for (String pair : pairs) {
            exchangeRatesSymbolCache.put(pair, Boolean.TRUE);
            BigDecimal exchangeRate = exchangeRatesCache.getIfPresent(pair);

            if (exchangeRate == null) {
                unknowRates.add(pair);
            } else {
                exchangeRates.put(pair, exchangeRate);
            }
        }

        if (!unknowRates.isEmpty()) {
            String values = String.join("\",\"", unknowRates);
            String rates = '"' + values + '"';
            final String url = YAHOO_API.replace(PLACEHOLDER, rates);
            final StringBuffer response = ServiceUtils.doHttpRequest(url);
            // gets actual exchange rate out of Json Object and saves it to last.
            final Gson gson = new Gson();
            if (unknowRates.size() > 1) {
                final RootMulti root = gson.fromJson(response.toString(), RootMulti.class);
                for (RootMulti.Query.Results.Rate rate : root.query.results.rate) {
                    BigDecimal exchangeRate = new BigDecimal(rate.Rate);
                    exchangeRatesCache.put(rate.id, exchangeRate);
                    exchangeRates.put(rate.id, exchangeRate);
                }
            } else {
                final RootSingle root = gson.fromJson(response.toString(), RootSingle.class);
                BigDecimal exchangeRate = new BigDecimal(root.query.results.rate.Rate);
                exchangeRatesCache.put(root.query.results.rate.id, exchangeRate);
                exchangeRates.put(root.query.results.rate.id, exchangeRate);
            }
        }

        return exchangeRates;
    }

    /*-
     * minimized JSON representation. Query result looks like:
     *   {
     *      "query":{
     *        "count":1,
     *         "created":"2015-08-13T10:10:05Z",
     *         "lang":"en-US",
     *         "results":{
     *            "rate":{
     *               "id":"CHFUSD",
     *               "Name":"CHF/USD",
     *               "Rate":"1.0222",
     *               "Date":"8/13/2015",
     *               "Time":"11:10am",
     *               "Ask":"1.0224",
     *               "Bid":"1.0221"
     *            }
     *         }
     *      }
     *   }
     *   </code>
     */
    private static class RootSingle {
        private Query query;

        private static class Query {
            private Results results;

            private static class Results {
                private Rate rate;

                private static class Rate {
                    private String Rate;
                    private String id;
                }
            }
        }
    }

    private static class RootMulti {
        private Query query;

        private static class Query {
            private Results results;

            private static class Results {
                private List<Rate> rate = new ArrayList<>();

                private static class Rate {
                    private String Rate;
                    private String id;
                }
            }
        }
    }

    final static public class ForexTask {

        @Autowired
        private ForexService service;

        // call every 6 minutes
        @Scheduled(fixedRate = ForexService.CACHING_TIME_RATE_MILLIS / 3)
        public void doTask() throws Exception {
            Set<String> set = service.exchangeRatesSymbolCache.asMap().keySet();
            service.getExchangeRates(set.toArray(new String[set.size()]));
        }
    }
}