Skip to content

Conversation

@sri-adarsh-kumar
Copy link
Contributor

@sri-adarsh-kumar sri-adarsh-kumar commented Oct 18, 2024

Moves jackson-datatype-money module from zalando

Context

Related to #5 and zalando/jackson-datatype-money#224

From the above conversations, there is a consensus to move the zalando/jackson-datatype-money library as a sub-module of this repository.

In order to achieve this, I have moved the files almost 1:1 from the source repository.

Only major changes were related to tests. The source repo had Junit Jupiter tests with @ParameterizedTest. The destination library already had other tests using junitParams.Parameters, so this was adopted.

Review Suggestions

Please focus on

  • How to use correct License
  • How to attribute credits correctly
  • Package naming conventions

@@ -0,0 +1,13 @@
//TODO how is this generated
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how this file is generated.
Is there some documentation for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be running

../mvnw moditect:generate-module-info

from new modules project dir. Let me try this on PR.

This provides the base, but must be modified; looking at other modules's module-info.java for inspiration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmh. Alas, that command throws NPE.

on-behalf-of: Zalando OSS Community
Co-authored-by: Willi Schönborn <[email protected]>
Co-authored-by: Philipp Hirch <[email protected]>
Co-authored-by: Jörn Horstmann <[email protected]>
Co-authored-by: Lauri at Zalando <[email protected]>
Co-authored-by: msparer <[email protected]>
Co-authored-by: Alexander Yastrebov <[email protected]>
Co-authored-by: Alexey Venderov <[email protected]>
Co-authored-by: Alexander Yastrebov <[email protected]>
Co-authored-by: Arnaud BOIVIN <[email protected]>
Co-authored-by: Bartosz Ocytko <[email protected]>
Co-authored-by: Carlos Freund <[email protected]>
Co-authored-by: Dario Seidl <[email protected]>
Co-authored-by: Georgios Andrianakis <[email protected]>
Co-authored-by: Lauri at Zalando <[email protected]>
Co-authored-by: Martin Prebio <[email protected]>
Co-authored-by: Sean Sullivan <[email protected]>
Co-authored-by: Touko Vainio-Kaila <[email protected]>
Co-authored-by: lukasniemeier-zalando <[email protected]>
@sri-adarsh-kumar sri-adarsh-kumar marked this pull request as ready for review October 22, 2024 17:00
@sri-adarsh-kumar
Copy link
Contributor Author

@cowtowncoder Please review when you have time.


<dependency>
<groupId>org.javamoney.moneta</groupId>
<artifactId>moneta-core</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test-only dependency? Or does module require specific Money API implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module supports Money API implementations like moneta's FastMoney, Money and RoundedMoney.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right: I was wondering if inclusion of specific implementation was problematic wrt using something else -- but I guess one would just use Maven dependency exclude, or some other mechanism.

Or put another way: I can see such dependency necessary for testing but wasn't sure it was really needed as regular ("compile") dependency. If you think it is needed that's fine: just double-checking.

Copy link

@KeepItSimpleStupid KeepItSimpleStupid Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi !
First, thanks for this jackson-datatype-money module to the Zalando team, we're using it for a long time and are really pleased that it's about to be more integrated to the Jackson ecosystem :)

Specifically on this thread :
I would say that if the module is about to be named javax-money, maybe it's worth reconsidering the support of the deserialization of org.javamoney.moneta.FastMoney, org.javamoney.moneta.Money, org.javamoney.moneta.RoundedMoney, 3 concrete classes belonging to Moneta, the only implementation of javax-money spec and support only the deserialization of javax.money.MonetaryAmount ?
Except for tests, the usage of the package org.javamoney is only located in MoneyModule.java so it seems quite straightforward to remove the support and get rid of moneta-core as a compile time dependency.
For us who use only javax.money.MonetaryAmount in our code, it would ease the dependency management since we won't have to fear to break this module if we update Moneta.
Maybe that would mean an additional module javax-money-moneta if deserializing those 3 classes is still needed by some ? This module could depend and reuse some code from the javax-money module though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KeepItSimpleStupid Sounds like a good idea to me; modules should focus on doing one concrete thing well and just that (as general guideline).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there was an CCLA, yes, but I haven't gotten one from Zalando. And based on discussions it sounded like there was hesitation to go with CLA. Either one is fine with me, but I do want this first inclusion to be covered by one or the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this comment, I think we will make a CCLA following the merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bocytko Is my assumption correct?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cowtowncoder there is no hesitation on the CLA. Based on prior comments I thought that there is no dependency between the PR merge and CLA. All that matters is that abaf36d and 487cbf9 covering the OSS contribution do not require the CLA. Fine for the follow-on commits which accumulated in this PR :)

Time-wise, if bound to a printout+signature, I can get the CCLA signed and sent to you early next week.

Copy link
Member

@cowtowncoder cowtowncoder Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I may have been unclear here. I think every PR should be done under a CLA or CCLA -- but for the initial one I'd only need one that covers the submitter (and not everyone who collaborated on source repository).
And then from thereon the usual (C)CLA coverage for further PRs (that is, if there are new contributors not yet covered).

Timing is fine with me -- I will make sure this gets merge before 2.19.0 gets released.
I will be taking one week off in a week so there's no super hurry.

I hope this helps!

import static org.apiguardian.api.API.Status.MAINTAINED;

@API(status = MAINTAINED)
public final class CurrencyUnitSerializer extends StdSerializer<CurrencyUnit> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly should use StdScalarSerializer.

}

@Override
public Object deserializeWithType(final JsonParser parser, final DeserializationContext context,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If extending StdDeserializer (or, StdScaralDeserializer), wouldn't need to implement this method, I think.

private void checkPresent(final JsonParser parser, @Nullable final Object value, final String name)
throws JsonParseException {
if (value == null) {
throw new JsonParseException(parser, format("Missing property: '%s'", name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sub-optimal exception as it does not indicate value type that has the issue.
Should usually be passed DeserializationContext and call one of methods it provides for throwing more specific (semantic) exceptions.

public void serializeWithType(final MonetaryAmount value, final JsonGenerator generator,
final SerializerProvider provider, final TypeSerializer serializer) throws IOException {

// effectively assuming no type information at all
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... which is basically wrong :-(

(meaning, won't work with @JsonTypeInfo)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review comment.
Do you see this as a blocker to merge this PR?

As this is not strictly related to moving the module from Zalando repo, I would prefer to treat this as a future improvement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think it is blocker: good point on "let's move it first, then improve".

@cowtowncoder
Copy link
Member

@sri-adarsh-kumar Ok, so technically I think this looks ok (although I have suggestions and concerns wrt code actually).
But one question I have is whether we need someone else's approval for contribution -- or are you the author? (I understand you work for Zalando from profile?).

I think we need one or more CLAs (from https://github.com/FasterXML/jackson/ either individual or Corporate CLA):

  1. From you (wrt directly adding files here), regardless of authorship of code
  2. Additional CLA(s) from whoever can contribute code itself (if not you).

It might be simplest to get CCLA from Zalando, although that is not a requirement if authors can give individual CLAs.

Changed License reference (WIP)
Removed cross-module dependency to jsonSchema
Testing specific Modules instead of findAndRegisterModules
Assert subtypes of JSONProcessingException
CurrencyUnitDeserializer extends StdScalarDeserializer
CurrencyUnitSerializer extends StdScalarSerializer
MonetaryAmountDeserializer throws semantic exceptions using DeserializationContext
MoneyModule version uses PackageVersion


@API(status = STABLE)
public final class MoneyModule extends Module {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we actually rename this as JavaxMoneyModule?

... also, as a side-note, is there need for "Jakarta Money module"? (wrt javax licensing resulting in "forking" of javax APIs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me check on this.

@cowtowncoder
Copy link
Member

@sri-adarsh-kumar Thank you for updates, good job so far!

The other thing then has to do with CLAs, permissions -- see my earlier note.
If we can get that through, I should be able to merge this pr!
(getting it merged into master for 3.0 will have its challenges but one thing at a time :) )

Copy link

@KeepItSimpleStupid KeepItSimpleStupid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments

Comment on lines 62 to 65
//Scan all registered MonetaryAmount types and add a default deserializer for them
for (Class c : Monetary.getAmountTypes()) {
deserializers.addDeserializer(c, new MonetaryAmountDeserializer<>((amount, currency) -> Monetary.getAmountFactory(c).setNumber(amount).setCurrency(currency).create(), names));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the intent here, but this for-loop completely by-passes the amountFactory field, making the module inconsistent because it doesn't let the user customize the way that these amountTypes would be deserialized.
=> I suggest that you remove this for-loop and let this JavaxMoneyModule focus on deserializing to MonetaryAmount fields only. Deserializing to fields of type org.javamoney.moneta.* (and customizing it) will be handled solely by the MonetaMoneyModule (as already implemented)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the code as requested.

public final class MonetaMoneyModule extends Module {

private final JavaxMoneyModule baseModule;
private final MonetaryAmountFactory<? extends MonetaryAmount> amountFactory;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this amountFactory field is never read so it can be removed ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the above requested change, this will be needed to support the appropriate amount factory for MonetaryAmount.

This will be Money by default, or FastMoney or others if configured as such.

Comment on lines 76 to 77
//Use provided amountFactory to deserialize a MonetaryAmount
deserializers.addDeserializer(MonetaryAmount.class, new MonetaryAmountDeserializer<>(amountFactory, names));
Copy link

@KeepItSimpleStupid KeepItSimpleStupid Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the modifications :)

If the default constructor becomes

public MonetaMoneyModule() {
        this(new JavaxMoneyModule().withMonetaryAmountFactory(Money::of), FieldNames.defaults(),
                Money::of, FastMoney::of, Money::of, RoundedMoney::of);
    }

and the method withMonetaryAmountFactory becomes

    private <T extends MonetaryAmount> MonetaMoneyModule withMonetaryAmountFactory(final MonetaryAmountFactory<T> amountFactory) {
        return new MonetaMoneyModule(baseModule.withMonetaryAmountFactory(amountFactory), names, amountFactory,
                fastMoneyFactory, moneyFactory, roundedMoneyFactory);
    }

I think you can get rid of both :

  • this extra deserializer for MonetaryAmount and rely only on the one defined in the baseModule
  • the amountFactory field of this MonetaMoneyModule class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. Changed now.

I had previously assumed that the deserializing logic in MonetaModule will overwrite the Base Module
But this appears not to be the case.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks !
You can get rid of the extra dezerializer for CurrencyUnit as well ;)
And I think it will all look good to me after that !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh, right again. Done.

@cowtowncoder cowtowncoder added cla-received Marker to denote that required CLA received and removed cla-needed CLA needed from submitter labels Mar 17, 2025
@cowtowncoder
Copy link
Member

CCLA received, hope to merge this RSN.

@@ -0,0 +1,198 @@
# Jackson Datatype Money
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be "Jackson Datatype Moneta"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I have fixed it.

@cowtowncoder
Copy link
Member

Hmmh. Ok, I did approve: but realized I do think module names should be tweak slightly.
Here's what I plan to change before merge:

  1. Main javax.money module: change artifactId to jackson-datatype-javax-money (since we have jackson-datatype-joda-money) -- not just jackson-datatype-money
  2. Moneta module: leave artifactId as jackson-datatype-moneta

@cowtowncoder
Copy link
Member

@sri-adarsh-kumar Ok things look good with one exception: trying to do #57 for Jackson 3 (branch 3.x) and basically, as far as I know & with all the info I have, it looks like it may not be possible to support JPMS (module-info.java) with Lombok, as things are currently.

I was wondering if there is any chance to de-Lombokify javax-money and moneta modules?
It looks like number of classes that actually use it is quite small, and I think it is possible to use Lombok to just generate sources, check in?

Alternatively if someone did figure out a way to fix Maven build to work, that'd be great outcome too -- but based on googling, chatgpt'ing, I am bit pessimistic about that being doable.

@sri-adarsh-kumar
Copy link
Contributor Author

@cowtowncoder I see from #59 and #60 you have done this already.

Pls let me know if there are any open tasks for me.

@cowtowncoder
Copy link
Member

@sri-adarsh-kumar Yes, sorry, forgot to update here.

I think we are good -- thank you for the follow up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-received Marker to denote that required CLA received

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants