Skip to content

Commit 9c3818a

Browse files
authored
Merge pull request #61 from reallyinsane/osgi
Removed static references to SPI instances. @atsticks We can see @reallyinsane now in https://jcp.org/en/participation/members/H so I guess we're good to merge this now.
2 parents a27ead1 + 51d9e40 commit 9c3818a

File tree

9 files changed

+554
-122
lines changed

9 files changed

+554
-122
lines changed

pom.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<artifactId>money-api</artifactId>
1616
<packaging>bundle</packaging>
1717

18-
<version>1.0.1</version>
18+
<version>1.0.2</version>
1919
<name>JSR 354 (Money and Currency API)</name>
2020
<url>http://java.net/projects/javamoney</url>
2121
<inceptionYear>2012</inceptionYear>
@@ -34,6 +34,7 @@
3434
<basedir>.</basedir>
3535
<!-- Dependency versions -->
3636
<testng.version>6.8.5</testng.version>
37+
<mockito.version>1.10.19</mockito.version>
3738
</properties>
3839

3940
<organization>
@@ -399,6 +400,12 @@
399400
<version>${testng.version}</version>
400401
<scope>test</scope>
401402
</dependency>
403+
<dependency>
404+
<groupId>org.mockito</groupId>
405+
<artifactId>mockito-all</artifactId>
406+
<version>${mockito.version}</version>
407+
<scope>test</scope>
408+
</dependency>
402409
</dependencies>
403410
</dependencyManagement>
404411

@@ -408,6 +415,11 @@
408415
<artifactId>testng</artifactId>
409416
<scope>test</scope>
410417
</dependency>
418+
<dependency>
419+
<groupId>org.mockito</groupId>
420+
<artifactId>mockito-all</artifactId>
421+
<scope>test</scope>
422+
</dependency>
411423
</dependencies>
412424

413425
<build>

src/main/java/javax/money/Monetary.java

Lines changed: 63 additions & 95 deletions
Large diffs are not rendered by default.

src/main/java/javax/money/convert/MonetaryConversions.java

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ public final class MonetaryConversions{
5656
* The SPI currently active, use {@link ServiceLoader} to register an
5757
* alternate implementation.
5858
*/
59-
private static final MonetaryConversionsSingletonSpi MONETARY_CONVERSION_SPI = Optional.of(
60-
Bootstrap.getService(MonetaryConversionsSingletonSpi.class)).get();
59+
private static final MonetaryConversionsSingletonSpi getMonetaryConversionsSpi() {
60+
return Optional.of(Bootstrap.getService(MonetaryConversionsSingletonSpi.class)).get();
61+
}
6162

6263
/**
6364
* Private singleton constructor.
@@ -78,11 +79,11 @@ public static CurrencyConversion getConversion(CurrencyUnit termCurrency, String
7879
Objects.requireNonNull(providers);
7980
Objects.requireNonNull(termCurrency);
8081
if(providers.length == 0){
81-
return MONETARY_CONVERSION_SPI.getConversion(
82+
return getMonetaryConversionsSpi().getConversion(
8283
ConversionQueryBuilder.of().setTermCurrency(termCurrency).setProviderNames(getDefaultConversionProviderChain())
8384
.build());
8485
}
85-
return MONETARY_CONVERSION_SPI.getConversion(
86+
return getMonetaryConversionsSpi().getConversion(
8687
ConversionQueryBuilder.of().setTermCurrency(termCurrency).setProviderNames(providers).build());
8788
}
8889

@@ -110,7 +111,7 @@ public static CurrencyConversion getConversion(String termCurrencyCode, String..
110111
* @throws IllegalArgumentException if the query defines {@link ExchangeRateProvider}s that are not available.
111112
*/
112113
public static CurrencyConversion getConversion(ConversionQuery conversionQuery){
113-
return Optional.ofNullable(MONETARY_CONVERSION_SPI)
114+
return Optional.ofNullable(getMonetaryConversionsSpi())
114115
.orElseThrow(() -> new MonetaryException("No MonetaryConversionsSingletonSpi " +
115116
"loaded, " +
116117
"query functionality is not " +
@@ -124,7 +125,7 @@ public static CurrencyConversion getConversion(ConversionQuery conversionQuery){
124125
* @return true, if a conversion is accessible from {@link #getConversion(ConversionQuery)}.
125126
*/
126127
public static boolean isConversionAvailable(ConversionQuery conversionQuery){
127-
return Optional.ofNullable(MONETARY_CONVERSION_SPI)
128+
return Optional.ofNullable(getMonetaryConversionsSpi())
128129
.orElseThrow(() -> new MonetaryException("No MonetaryConversionsSingletonSpi " +
129130
"loaded, " +
130131
"query functionality is not " +
@@ -140,7 +141,7 @@ public static boolean isConversionAvailable(ConversionQuery conversionQuery){
140141
* @return true, if a conversion is accessible from {@link #getConversion(String, String...)}.
141142
*/
142143
public static boolean isConversionAvailable(String currencyCode, String... providers){
143-
return Optional.ofNullable(MONETARY_CONVERSION_SPI)
144+
return Optional.ofNullable(getMonetaryConversionsSpi())
144145
.orElseThrow(() -> new MonetaryException("No MonetaryConversionsSingletonSpi " +
145146
"loaded, " +
146147
"query functionality is not " +
@@ -156,7 +157,7 @@ public static boolean isConversionAvailable(String currencyCode, String... provi
156157
* @return true, if a conversion is accessible from {@link #getConversion(String, String...)}.
157158
*/
158159
public static boolean isConversionAvailable(CurrencyUnit termCurrency, String... providers){
159-
return Optional.ofNullable(MONETARY_CONVERSION_SPI)
160+
return Optional.ofNullable(getMonetaryConversionsSpi())
160161
.orElseThrow(() -> new MonetaryException("No MonetaryConversionsSingletonSpi " +
161162
"loaded, " +
162163
"query functionality is not " +
@@ -175,10 +176,10 @@ public static boolean isConversionAvailable(CurrencyUnit termCurrency, String...
175176
public static ExchangeRateProvider getExchangeRateProvider(String... providers){
176177
if(providers.length == 0){
177178
List<String> defaultProviderChain = getDefaultConversionProviderChain();
178-
return MONETARY_CONVERSION_SPI.getExchangeRateProvider(ConversionQueryBuilder.of().setProviderNames(
179+
return getMonetaryConversionsSpi().getExchangeRateProvider(ConversionQueryBuilder.of().setProviderNames(
179180
defaultProviderChain.toArray(new String[defaultProviderChain.size()])).build());
180181
}
181-
ExchangeRateProvider provider = MONETARY_CONVERSION_SPI
182+
ExchangeRateProvider provider = getMonetaryConversionsSpi()
182183
.getExchangeRateProvider(ConversionQueryBuilder.of().setProviderNames(providers).build());
183184
return Optional.ofNullable(provider)
184185
.orElseThrow(() -> new MonetaryException("No such rate provider: " + Arrays.toString(providers)));
@@ -218,7 +219,7 @@ public static ExchangeRateProvider getExchangeRateProvider(
218219
* @throws IllegalArgumentException if no such {@link ExchangeRateProvider} is available.
219220
*/
220221
public static ExchangeRateProvider getExchangeRateProvider(ConversionQuery conversionQuery){
221-
return Optional.ofNullable(MONETARY_CONVERSION_SPI).orElseThrow(() -> new MonetaryException(
222+
return Optional.ofNullable(getMonetaryConversionsSpi()).orElseThrow(() -> new MonetaryException(
222223
"No MonetaryConversionsSingletonSpi loaded, query functionality is not available."))
223224
.getExchangeRateProvider(conversionQuery);
224225
}
@@ -230,7 +231,7 @@ public static ExchangeRateProvider getExchangeRateProvider(ConversionQuery conve
230231
* @return true, if a rate provider is accessible from {@link #getExchangeRateProvider(ConversionQuery)}}.
231232
*/
232233
public static boolean isExchangeRateProviderAvailable(ConversionQuery conversionQuery){
233-
return Optional.ofNullable(MONETARY_CONVERSION_SPI)
234+
return Optional.ofNullable(getMonetaryConversionsSpi())
234235
.orElseThrow(() -> new MonetaryException("No MonetaryConversionsSingletonSpi " +
235236
"loaded, " +
236237
"query functionality is not " +
@@ -247,14 +248,14 @@ public static boolean isExchangeRateProviderAvailable(ConversionQuery conversion
247248
* @return all supported provider ids, never {@code null}.
248249
*/
249250
public static Collection<String> getConversionProviderNames(){
250-
Collection<String> providers = Optional.ofNullable(MONETARY_CONVERSION_SPI).orElseThrow(
251+
Collection<String> providers = Optional.ofNullable(getMonetaryConversionsSpi()).orElseThrow(
251252
() -> new MonetaryException(
252253
"No MonetaryConversionsSingletonSpi loaded, query functionality is not available."))
253254
.getProviderNames();
254255
if(Objects.isNull(providers)){
255256
Logger.getLogger(MonetaryConversions.class.getName()).warning(
256257
"No supported rate/conversion providers returned by SPI: " +
257-
MONETARY_CONVERSION_SPI.getClass().getName());
258+
getMonetaryConversionsSpi().getClass().getName());
258259
return Collections.emptySet();
259260
}
260261
return providers;
@@ -266,12 +267,12 @@ public static Collection<String> getConversionProviderNames(){
266267
* @return the default provider, never {@code null}.
267268
*/
268269
public static List<String> getDefaultConversionProviderChain(){
269-
List<String> defaultChain = Optional.ofNullable(MONETARY_CONVERSION_SPI).orElseThrow(
270+
List<String> defaultChain = Optional.ofNullable(getMonetaryConversionsSpi()).orElseThrow(
270271
() -> new MonetaryException(
271272
"No MonetaryConversionsSingletonSpi loaded, query functionality is not available."))
272273
.getDefaultProviderChain();
273274
Objects.requireNonNull(defaultChain, "No default provider chain provided by SPI: " +
274-
MONETARY_CONVERSION_SPI.getClass().getName());
275+
getMonetaryConversionsSpi().getClass().getName());
275276
return defaultChain;
276277
}
277278

src/main/java/javax/money/format/MonetaryFormats.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
*/
2828
public final class MonetaryFormats {
2929

30-
private static final MonetaryFormatsSingletonSpi MONETARY_FORMATS_SINGLETON_SPI = loadMonetaryFormatsSingletonSpi();
30+
private static final MonetaryFormatsSingletonSpi getMonetaryFormatsSpi() {
31+
return loadMonetaryFormatsSingletonSpi();
32+
}
3133

3234
/**
3335
* Private singleton constructor.
@@ -61,7 +63,7 @@ private static MonetaryFormatsSingletonSpi loadMonetaryFormatsSingletonSpi() {
6163
* @return true, if a corresponding {@link MonetaryAmountFormat} is accessible.
6264
*/
6365
public static boolean isAvailable(Locale locale, String... providers) {
64-
return Optional.ofNullable(MONETARY_FORMATS_SINGLETON_SPI).orElseThrow(() -> new MonetaryException(
66+
return Optional.ofNullable(getMonetaryFormatsSpi()).orElseThrow(() -> new MonetaryException(
6567
"No MonetaryFormatsSingletonSpi " + "loaded, query functionality is not available."))
6668
.isAvailable(locale, providers);
6769
}
@@ -89,7 +91,7 @@ public static MonetaryAmountFormat getAmountFormat(Locale locale, String... prov
8991
* @return true, if a corresponding {@link MonetaryAmountFormat} is accessible.
9092
*/
9193
public static boolean isAvailable(AmountFormatQuery formatQuery) {
92-
return Optional.ofNullable(MONETARY_FORMATS_SINGLETON_SPI).orElseThrow(() -> new MonetaryException(
94+
return Optional.ofNullable(getMonetaryFormatsSpi()).orElseThrow(() -> new MonetaryException(
9395
"No MonetaryFormatsSingletonSpi " + "loaded, query functionality is not available."))
9496
.isAvailable(formatQuery);
9597
}
@@ -105,7 +107,7 @@ public static boolean isAvailable(AmountFormatQuery formatQuery) {
105107
* corresponding {@link MonetaryAmountFormat} instance.
106108
*/
107109
public static MonetaryAmountFormat getAmountFormat(AmountFormatQuery formatQuery) {
108-
return Optional.ofNullable(MONETARY_FORMATS_SINGLETON_SPI).orElseThrow(() -> new MonetaryException(
110+
return Optional.ofNullable(getMonetaryFormatsSpi()).orElseThrow(() -> new MonetaryException(
109111
"No MonetaryFormatsSingletonSpi " + "loaded, query functionality is not available."))
110112
.getAmountFormat(formatQuery);
111113
}
@@ -121,7 +123,7 @@ public static MonetaryAmountFormat getAmountFormat(AmountFormatQuery formatQuery
121123
* corresponding {@link MonetaryAmountFormat} instance.
122124
*/
123125
public static Collection<MonetaryAmountFormat> getAmountFormats(AmountFormatQuery formatQuery) {
124-
return Optional.ofNullable(MONETARY_FORMATS_SINGLETON_SPI).orElseThrow(() -> new MonetaryException(
126+
return Optional.ofNullable(getMonetaryFormatsSpi()).orElseThrow(() -> new MonetaryException(
125127
"No MonetaryFormatsSingletonSpi " + "loaded, query functionality is not available."))
126128
.getAmountFormats(formatQuery);
127129
}
@@ -148,7 +150,7 @@ public static MonetaryAmountFormat getAmountFormat(String formatName, String...
148150
* @return all available locales, never {@code null}.
149151
*/
150152
public static Set<Locale> getAvailableLocales(String... providers) {
151-
return MONETARY_FORMATS_SINGLETON_SPI.getAvailableLocales(providers);
153+
return getMonetaryFormatsSpi().getAvailableLocales(providers);
152154
}
153155

154156
/**
@@ -157,14 +159,14 @@ public static Set<Locale> getAvailableLocales(String... providers) {
157159
* @return the provider names, never null.
158160
*/
159161
public static Collection<String> getFormatProviderNames() {
160-
Collection<String> providers = Optional.ofNullable(MONETARY_FORMATS_SINGLETON_SPI).orElseThrow(
162+
Collection<String> providers = Optional.ofNullable(getMonetaryFormatsSpi()).orElseThrow(
161163
() -> new MonetaryException(
162164
"No MonetaryFormatsSingletonSpi loaded, query functionality is not available."))
163165
.getProviderNames();
164166
if (Objects.isNull(providers)) {
165167
Logger.getLogger(MonetaryFormats.class.getName()).warning(
166168
"No supported rate/conversion providers returned by SPI: " +
167-
MONETARY_FORMATS_SINGLETON_SPI.getClass().getName());
169+
getMonetaryFormatsSpi().getClass().getName());
168170
return Collections.emptySet();
169171
}
170172
return providers;
@@ -176,7 +178,7 @@ public static Collection<String> getFormatProviderNames() {
176178
* @return the default provider chain, never null.
177179
*/
178180
public static List<String> getDefaultFormatProviderChain() {
179-
return Optional.ofNullable(MONETARY_FORMATS_SINGLETON_SPI).orElseThrow(() -> new MonetaryException(
181+
return Optional.ofNullable(getMonetaryFormatsSpi()).orElseThrow(() -> new MonetaryException(
180182
"No MonetaryFormatsSingletonSpi " + "loaded, query functionality is not available."))
181183
.getDefaultProviderChain();
182184
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU
3+
* ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT. PLEASE READ THE TERMS AND CONDITIONS OF THIS
4+
* AGREEMENT CAREFULLY. BY DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF
5+
* THE AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE" BUTTON AT THE
6+
* BOTTOM OF THIS PAGE. Specification: JSR-354 Money and Currency API ("Specification") Copyright
7+
* (c) 2012-2013, Credit Suisse All rights reserved.
8+
*/
9+
package javax.money;
10+
11+
import java.util.ArrayList;
12+
import java.util.HashMap;
13+
import java.util.List;
14+
import java.util.Map;
15+
16+
import javax.money.spi.Bootstrap;
17+
import javax.money.spi.CurrencyProviderSpi;
18+
import javax.money.spi.ServiceProvider;
19+
20+
import org.mockito.Mockito;
21+
import org.testng.annotations.AfterMethod;
22+
import org.testng.annotations.BeforeMethod;
23+
24+
/**
25+
* Abstract test supporting to switch SPI implementations by using a ServiceProvider providing manually registered SPIs only.
26+
* @author Matthias Hanisch
27+
*/
28+
public abstract class AbstractDynamicServiceProviderTest {
29+
30+
private ServiceProvider originalServiceProvider;
31+
private TestServiceProvider testServiceProvider;
32+
33+
/**
34+
* Initialized Bootstrap so that default ServiceProvider is enabled. Then stores the default ServiceProvider in
35+
* {@link #originalServiceProvider}. Initializes Bootstrap with default ServiceProvider again so that it is
36+
* usable in the Test. Also initialized {@link #testServiceProvider}.
37+
*/
38+
@BeforeMethod
39+
public void prepare() {
40+
Bootstrap.getService(CurrencyProviderSpi.class);
41+
ServiceProvider empty = Mockito.mock(ServiceProvider.class);
42+
originalServiceProvider= Bootstrap.init(empty);
43+
Bootstrap.init(originalServiceProvider);
44+
testServiceProvider = new TestServiceProvider();
45+
}
46+
47+
/**
48+
* Restores the default ServiceProvider.
49+
*/
50+
@AfterMethod
51+
public void restore() {
52+
testServiceProvider.clearServices();
53+
Bootstrap.init(originalServiceProvider);
54+
}
55+
56+
protected final void initTestServiceProvider() {
57+
Bootstrap.init(testServiceProvider);
58+
}
59+
60+
protected final void initOriginalServiceProvider() {
61+
Bootstrap.init(originalServiceProvider);
62+
}
63+
64+
/**
65+
* Registers a SPI service so that it is accessible via {@link Bootstrap#getService(Class)} when using {@link #testServiceProvider}.
66+
* @param serviceType The SPI type.
67+
* @param service The SPI instance.
68+
*/
69+
protected final <T> void registerService(Class<T> serviceType, T service) {
70+
testServiceProvider.registerService(serviceType, service);
71+
}
72+
73+
class TestServiceProvider implements ServiceProvider {
74+
75+
private Map<Class<?>, List<?>> services = new HashMap<>();
76+
77+
@Override
78+
public int getPriority() {
79+
return 0;
80+
}
81+
82+
@Override
83+
public <T> List<T> getServices(Class<T> serviceType) {
84+
return (List<T>) services.get(serviceType);
85+
}
86+
87+
@Override
88+
public <T> T getService(Class<T> serviceType) {
89+
List<T> servicesOfType = getServices(serviceType);
90+
if(servicesOfType==null||servicesOfType.isEmpty()) {
91+
return null;
92+
} else {
93+
return servicesOfType.get(0);
94+
}
95+
}
96+
97+
public <T> void registerService(Class<T> serviceType, T service) {
98+
List<T> servicesOfType = (List<T>) services.get(serviceType);
99+
if(servicesOfType==null) {
100+
servicesOfType = new ArrayList<>();
101+
services.put(serviceType, servicesOfType);
102+
}
103+
servicesOfType.add(service);
104+
}
105+
106+
public void clearServices() {
107+
services.clear();
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)