I'm using the Java money (JSR354) reference implementation found here: http://javamoney.github.io/ri.html
However, I'm using it in an OSGI environment. This is giving me the following exception:
[qtp305372452-33] ERROR org.javamoney.moneta.spi.MonetaryConfig - Error loading javamoney.properties, ignoring bundleresource://7.fwk302155142:3/javamoney.properties
java.lang.IllegalStateException: AmbiguousConfiguration detected for 'load.ECBHistoricRateProvider.resource'.
at org.javamoney.moneta.spi.MonetaryConfig.updateConfig(MonetaryConfig.java:90)
at org.javamoney.moneta.spi.MonetaryConfig.<init>(MonetaryConfig.java:53)
at org.javamoney.moneta.spi.MonetaryConfig.<clinit>(MonetaryConfig.java:39)
at org.javamoney.moneta.DefaultMonetaryContextFactory.getContext(DefaultMonetaryContextFactory.java:38)
at org.javamoney.moneta.Money.<clinit>(Money.java:79)
at org.javamoney.moneta.internal.MoneyAmountBuilder.create(MoneyAmountBuilder.java:42)
at org.javamoney.moneta.internal.MoneyAmountBuilder.create(MoneyAmountBuilder.java:33)
at org.javamoney.moneta.spi.AbstractAmountBuilder.create(AbstractAmountBuilder.java:61)
at com.eijsink.ef.module.pricing.Price.createAmount(Price.java:214)
at com.eijsink.ef.module.pricing.Price.<init>(Price.java:73)
at com.eijsink.ef.module.pricing.view.PriceField.storeValue(PriceField.java:210)
at com.eijsink.ef.module.pricing.view.PriceField.lambda$6(PriceField.java:169)
at com.eijsink.ef.module.pricing.view.PriceField$$Lambda$163/23040973.valueChange(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:508)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:198)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:161)
at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:977)
at com.vaadin.ui.AbstractField.fireValueChange(AbstractField.java:1137)
at com.vaadin.ui.AbstractField.setValue(AbstractField.java:548)
at com.vaadin.ui.AbstractSelect.setValue(AbstractSelect.java:709)
at com.vaadin.ui.ComboBox.changeVariables(ComboBox.java:674)
at com.vaadin.server.communication.ServerRpcHandler.changeVariables(ServerRpcHandler.java:486)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:305)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:184)
at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:92)
at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41)
at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1408)
at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:350)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.equinox.http.servlet.internal.ServletRegistration.service(ServletRegistration.java:61)
at org.eclipse.equinox.http.servlet.internal.ProxyServlet.processAlias(ProxyServlet.java:128)
at org.eclipse.equinox.http.servlet.internal.ProxyServlet.service(ProxyServlet.java:76)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.equinox.http.jetty.internal.HttpServerManager$InternalHttpServiceServlet.service(HttpServerManager.java:386)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:501)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:229)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:533)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1088)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:428)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:982)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1043)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:667)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:744)
The exception is not thrown when using the Java money classes, but instead it is only logged. Everything works, but this exception pops up all the time in my logs, which is very annoying for me and my colleagues.
I've traced the problem down to this class in the library:
package org.javamoney.moneta.spi;
//imports
public final class MonetaryConfig {
private static final Logger LOG = Logger
.getLogger(MonetaryConfig.class.getName());
private static final MonetaryConfig INSTANCE = new MonetaryConfig();
private Map<String, String> config = new HashMap<>();
private Map<String, Integer> priorities = new HashMap<>();
private MonetaryConfig() {
try {
Enumeration<URL> urls = getClass().getClassLoader().getResources(
"javamoney.properties");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
try {
Properties props = new Properties();
props.load(url.openStream());
updateConfig(props);
} catch (Exception e) {
LOG.log(Level.SEVERE,
"Error loading javamoney.properties, ignoring "
+ url, e);
}
}
} catch (IOException e) {
LOG.log(Level.SEVERE, "Error loading javamoney.properties.", e);
}
}
private void updateConfig(Properties props) {
for (Map.Entry<Object, Object> en : props.entrySet()) {
String key = en.getKey().toString();
String value = en.getValue().toString();
int prio = 0;
if (key.startsWith("{")) {
int index = key.indexOf('}');
if (index > 0) {
String prioString = key.substring(1, index);
try {
prio = Integer.parseInt(prioString);
key = key.substring(index + 1);
} catch (NumberFormatException e) {
LOG.warning("Invalid config key in javamoney.properties: " + key);
}
}
}
Integer existingPrio = priorities.get(key);
if (Objects.isNull(existingPrio)) {
priorities.put(key, prio);
config.put(key, value);
} else if (existingPrio < prio) {
priorities.put(key, prio);
config.put(key, value);
} else if (existingPrio == prio) {
throw new IllegalStateException(
"AmbiguousConfiguration detected for '" + key + "'.");
}
// else ignore entry with lower prio!
}
}
public static Map<String, String> getConfig() {
return Collections.unmodifiableMap(INSTANCE.config);
}
}
The updateConfig() method throws the exception. The MonetaryConfig constructor logs it. The problem occurs because the line
Enumeration<URL> urls = getClass().getClassLoader().getResources(
"javamoney.properties");
returns 2 urls for the same file, namely: "bundleresource://7.fwk302155142/javamoney.properties" and "bundleresource://7.fwk302155142:3/javamoney.properties".
I checked the .JAR file, there is only 1 properties file and it is definitely being read twice.
The real source of the problem is the way the resource is loaded. In OSGI, this is not the way to load resources (You should get the resource from your bundle instead). However, I do not know how to modify the way this library loads the resource (apart from modifying and recompiling the sources, which I don't want to do).
Is there any OSGI technique I can use when loading this library to make this work properly?
EDIT: My Manifest bundle classpath
Bundle-ClassPath: jars/moneta-1.0-RC3.jar,
jars/money-api-1.0-RC3.jar,
jars/javax.annotation-api-1.2.jar
From the URLs:
bundleresource://7.fwk302155142/javamoney.properties
bundleresource://7.fwk302155142:3/javamoney.properties
It looks like the resource is in there twice due to the Bundle-Classpath
. The :3
should refer an index into the Bundle-Classpath
. Make sure that no entry in the Bundle-Classpath
contains the properties file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With