Skip to content

Huge overhead in creating MonetaryAmount on the fly. #436

@garretwilson

Description

@garretwilson

Maybe I'm not understanding this, so please correct me if I'm mistaken, but there seems to be a huge overhead in creating monetary amounts in the real world.

Let's say a client is processing connections to a financial server in several threads. Each thread wants to simply create a MonetaryAmount instance using the default implementation. Naturally that would be Monetary.getDefaultAmountFactory(), but we don't know what the amount factory will be until runtime (which is why we're not hard-coding the implementation to use any particular amount factory). But there's a problem: javax.money.MonetaryAmountFactory specifically says it is not guaranteed to be thread-safe:

Implementation specification
Instances of this interface are not required to be thread-safe!

Since we are plugging in a serializer/deserializer to our serialization library (e.g. Jackson), we have no idea if these will be called from a single thread or not, so we are forced to call Monetary.getDefaultAmountFactory() each time, are we not?

Unfortunately javax.money.Monetary.getDefaultAmountFactory() has a huge overhead—it actually invokes all the Java SPI lookup infrastructure each time!!

    public static MonetaryAmountFactory<?> getDefaultAmountFactory() {
        return Optional.ofNullable(monetaryAmountsSingletonSpi())
                .orElseThrow(() -> new MonetaryException("No MonetaryAmountsSingletonSpi loaded."))
                .getDefaultAmountFactory();
    }

    private static MonetaryAmountsSingletonSpi monetaryAmountsSingletonSpi() {
        try {
            return Bootstrap.getService(MonetaryAmountsSingletonSpi.class);
        } catch (Exception e) {
            Logger.getLogger(Monetary.class.getName())
                    .log(Level.SEVERE, "Failed to load MonetaryAmountsSingletonSpi.", e);
            return null;
        }
    }

So for each one of those thousands or millions of monetary amounts, the application is supposed to invoke the Java SPI infrastructure to look up the default amount MonetaryAmountFactory? I haven't ran any tests, but just on the face of it that sounds absurd. I need to keep the default MonetaryAmountFactory around somewhere, but as mentioned above, MonetaryAmountFactory isn't guaranteed to be thread safe.

Am I expected to resort to ThreadLocal or something similar in my deserializer so I can keep a separate amount factory around per thread?

Note that the JavaxMoneyModule for Jackson (which I believe came from Zalando) completely ignores the admonition about thread safety, and keeps a MonetaryAmountFactory around to be used by all deserialization, ignoring threads!

    private <T extends MonetaryAmount> JavaxMoneyModule(final AmountWriter<?> writer, final FieldNames names, final MonetaryAmountFormatFactory formatFactory, final MonetaryAmountFactory<T> amountFactory) {

        this.writer = writer;
        this.names = names;
        this.formatFactory = formatFactory;
        this.amountFactory = amountFactory;
    }

…

        //Use provided amountFactory to deserialize a MonetaryAmount
        deserializers.addDeserializer(MonetaryAmount.class, new MonetaryAmountDeserializer<>(amountFactory, names));

It would thus seem the Zalander/Jackson Java money module is blatantly ignoring the thread-safety of the MonetaryAmountFactory (probably because, like me, the alternative seems unfathomable). But thread-safety isn't just something we can ignore.

How was this envisioned to be used in the real world?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions