001/** 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package org.javamoney.moneta.spi; 017 018import javax.money.convert.ConversionQuery; 019import javax.money.convert.CurrencyConversionException; 020import javax.money.convert.ExchangeRate; 021import javax.money.convert.ExchangeRateProvider; 022import javax.money.convert.ProviderContext; 023import javax.money.convert.ProviderContextBuilder; 024import javax.money.convert.RateType; 025import java.util.ArrayList; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Objects; 029import java.util.Optional; 030import java.util.Set; 031import java.util.logging.Level; 032import java.util.logging.Logger; 033 034/** 035 * This class implements a {@link ExchangeRateProvider} that delegates calls to 036 * a collection of child {@link ExchangeRateProvider} instance. 037 * 038 * @author Anatole Tresch 039 */ 040public class CompoundRateProvider extends AbstractRateProvider { 041 /** 042 * Key used to store a list of child {@link javax.money.convert.ProviderContext} instances of the providers 043 * contained within this instance. 044 * @deprecated Will be private in next major release. 045 */ 046 @Deprecated 047 public static final String CHILD_PROVIDER_CONTEXTS_KEY = "childProviderContexts"; 048 /** 049 * The {@link ExchangeRateProvider} instances. 050 */ 051 private final List<ExchangeRateProvider> providers = new ArrayList<>(); 052 053 /** 054 * Constructor. 055 * 056 * @param providers The collection of child {@link ExchangeRateProvider} 057 * instances this class delegates calls to. 058 */ 059 public CompoundRateProvider(Iterable<ExchangeRateProvider> providers) { 060 super(createContext(providers)); 061 for (ExchangeRateProvider exchangeRateProvider : providers) { 062 addProvider(exchangeRateProvider); 063 } 064 } 065 066 private static ProviderContext createContext(Iterable<ExchangeRateProvider> providers) { 067 Set<RateType> rateTypeSet = new HashSet<>(); 068 StringBuilder providerName = new StringBuilder("Compound: "); 069 List<ProviderContext> childContextList = new ArrayList<>(); 070 for (ExchangeRateProvider exchangeRateProvider : providers) { 071 childContextList.add(exchangeRateProvider.getContext()); 072 providerName.append(exchangeRateProvider.getContext().getProviderName()); 073 providerName.append(',' ); 074 rateTypeSet.addAll(exchangeRateProvider.getContext().getRateTypes()); 075 } 076 providerName.setLength(providerName.length() - 1); 077 078 ProviderContextBuilder builder = ProviderContextBuilder.of(providerName.toString(), rateTypeSet); 079 builder.set(CHILD_PROVIDER_CONTEXTS_KEY, childContextList); 080 return builder.build(); 081 } 082 083 /** 084 * Add an additional {@link ExchangeRateProvider} to the instance's delegate 085 * list. 086 * 087 * @param prov The {@link ExchangeRateProvider} to be added, not {@code null} 088 * . 089 * @throws java.lang.NullPointerException if the provider is null. 090 * . 091 */ 092 private void addProvider(ExchangeRateProvider prov) { 093 providers.add(Optional.ofNullable(prov) 094 .orElseThrow(() -> new NullPointerException("ConversionProvider required."))); 095 } 096 097 /* 098 * (non-Javadoc) 099 * 100 * @see 101 * javax.money.convert.ExchangeRateProvider#getExchangeRate(javax.money. 102 * CurrencyUnit, javax.money.CurrencyUnit, 103 * javax.money.convert.ConversionContext) 104 */ 105 @Override 106 public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { 107 for (ExchangeRateProvider prov : this.providers) { 108 try { 109 if (prov.isAvailable(conversionQuery)) { 110 ExchangeRate rate = prov.getExchangeRate(conversionQuery); 111 if (Objects.nonNull(rate)) { 112 return rate; 113 } 114 } 115 } catch (Exception e) { 116 Logger.getLogger(getClass().getName()).log(Level.WARNING, 117 "Rate Provider did not return data though at check before data was flagged as available," + 118 " provider=" + prov.getContext().getProviderName() + ", query=" + conversionQuery); 119 } 120 } 121 throw new CurrencyConversionException(conversionQuery.getBaseCurrency(), conversionQuery.getCurrency(), null, 122 "All delegate prov iders failed to deliver rate, providers=" + this.providers + 123 ", query=" + conversionQuery); 124 } 125 126 127}