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.internal; 017 018import javax.money.MonetaryAmount; 019import javax.money.MonetaryAmountFactory; 020import javax.money.MonetaryException; 021import javax.money.spi.Bootstrap; 022import javax.money.spi.MonetaryAmountFactoryProviderSpi; 023import javax.money.spi.MonetaryAmountsSingletonSpi; 024 025import java.util.Map; 026import java.util.Objects; 027import java.util.Optional; 028import java.util.Set; 029import java.util.concurrent.ConcurrentHashMap; 030 031/** 032 * Default implementation ot {@link javax.money.spi.MonetaryAmountsSingletonSpi} loading the SPIs on startup 033 * initially once, using the 034 * JSR's {@link javax.money.spi.Bootstrap} mechanism. 035 */ 036public class DefaultMonetaryAmountsSingletonSpi implements MonetaryAmountsSingletonSpi { 037 038 private final Map<Class<? extends MonetaryAmount>, MonetaryAmountFactoryProviderSpi<?>> factories = 039 new ConcurrentHashMap<>(); 040 041 private Class<? extends MonetaryAmount> configuredDefaultAmountType = loadDefaultAmountType(); 042 043 public DefaultMonetaryAmountsSingletonSpi() { 044 for (MonetaryAmountFactoryProviderSpi<?> f : Bootstrap.getServices(MonetaryAmountFactoryProviderSpi.class)) { 045 factories.putIfAbsent(f.getAmountType(), f); 046 } 047 } 048 049 /** 050 * Tries to load the default {@link MonetaryAmount} class from 051 * {@code javamoney.properties} with contents as follows:<br/> 052 * <code> 053 * javax.money.defaults.amount.class=my.fully.qualified.ClassName 054 * </code> 055 * 056 * @return the loaded default class, or {@code null} 057 */ 058 // type check should be safe, exception will be logged if not. 059 private Class<? extends MonetaryAmount> loadDefaultAmountType() { 060 return null; 061 } 062 063 064 // save cast, since members are managed by this instance 065 @SuppressWarnings("unchecked") 066 @Override 067 public <T extends MonetaryAmount> MonetaryAmountFactory<T> getAmountFactory(Class<T> amountType) { 068 MonetaryAmountFactoryProviderSpi<T> f = MonetaryAmountFactoryProviderSpi.class.cast(factories.get(amountType)); 069 if (Objects.nonNull(f)) { 070 return f.createMonetaryAmountFactory(); 071 } 072 throw new MonetaryException("No matching MonetaryAmountFactory found, type=" + amountType.getName()); 073 } 074 075 @Override 076 public Set<Class<? extends MonetaryAmount>> getAmountTypes() { 077 return factories.keySet(); 078 } 079 080 /* 081 * (non-Javadoc) 082 * 083 * @see javax.money.spi.MonetaryAmountsSpi#getDefaultAmountType() 084 */ 085 @Override 086 public Class<? extends MonetaryAmount> getDefaultAmountType() { 087 if (Objects.isNull(configuredDefaultAmountType)) { 088 for (MonetaryAmountFactoryProviderSpi<?> f : Bootstrap.getServices(MonetaryAmountFactoryProviderSpi.class)) { 089 if (f.getQueryInclusionPolicy() == MonetaryAmountFactoryProviderSpi.QueryInclusionPolicy.ALWAYS) { 090 configuredDefaultAmountType = f.getAmountType(); 091 break; 092 } 093 } 094 } 095 return Optional.ofNullable(configuredDefaultAmountType) 096 .orElseThrow(() -> new MonetaryException("No MonetaryAmountFactoryProviderSpi registered.")); 097 } 098 099}