Skip to content

[master] Json refactoring (step 1/2 - isolating Jackson from our code)#1145

Merged
mathieucarbou merged 1 commit intoTerracotta-OSS:masterfrom
mathieucarbou:json
Apr 25, 2023
Merged

[master] Json refactoring (step 1/2 - isolating Jackson from our code)#1145
mathieucarbou merged 1 commit intoTerracotta-OSS:masterfrom
mathieucarbou:json

Conversation

@mathieucarbou
Copy link
Copy Markdown
Member

@mathieucarbou mathieucarbou commented Nov 22, 2022

This refactoring is a first step required to switch to Gson, but also anyway a good refactoring to have in order to isolate Jackson from our code and reduce the direct dependencies we have on it.

  • Introduced Terracotta Json API (currently implemented by Jackson)
  • Isolate Jackson classes from Terracotta code:
    • no direct imports anymore
    • The use of Jackson specific mixins and such are isolated in a *.json package
  • Refactored Sanskrit to get rid of its coupling with Jackson classes
  • Added a Workaround in NomadChange json serialisation due to a bug in how Jackson is handling polymorphism serialisation

@mathieucarbou mathieucarbou self-assigned this Nov 22, 2022
@mathieucarbou mathieucarbou changed the base branch from master to release/5.9 November 22, 2022 19:46
@mathieucarbou mathieucarbou marked this pull request as ready for review December 15, 2022 09:45
@mathieucarbou mathieucarbou marked this pull request as draft January 26, 2023 15:32
@mathieucarbou mathieucarbou changed the base branch from release/5.9 to master March 27, 2023 15:01
@mathieucarbou mathieucarbou changed the title [draft] Json refactoring [master] Json refactoring Mar 27, 2023
@mathieucarbou mathieucarbou marked this pull request as ready for review March 28, 2023 11:16
@mathieucarbou mathieucarbou changed the title [master] Json refactoring [master] Json refactoring (step 1 - isolating Jackson from our code) Mar 28, 2023
@mathieucarbou mathieucarbou changed the title [master] Json refactoring (step 1 - isolating Jackson from our code) [master] Json refactoring (step 1/2 - isolating Jackson from our code) Mar 28, 2023
@mathieucarbou
Copy link
Copy Markdown
Member Author

(rebased)

@mobasherul
Copy link
Copy Markdown
Contributor

This refactoring is a first step required to switch to Gson, but also anyway a good refactoring to have in order to isolate Jackson from our code and reduce the direct dependencies we have on it.

  • Introduced Terracotta Json API (currently implemented by Jackson)

  • Isolate Jackson classes from Terracotta code:

    • no direct imports anymore
    • The use of Jackson specific mixins and such are isolated in a *.json package
  • Refactored Sanskrit to get rid of its coupling with Jackson classes

  • Added a Workaround in NomadChange json serialisation due to a bug in how Jackson is handling polymorphism serialisation

@mathieucarbou When moving to some other json library then mixin present in *.json package in all the affected modules will still need to be changed right ?

@mathieucarbou
Copy link
Copy Markdown
Member Author

mathieucarbou commented Mar 29, 2023

This refactoring is a first step required to switch to Gson, but also anyway a good refactoring to have in order to isolate Jackson from our code and reduce the direct dependencies we have on it.

  • Introduced Terracotta Json API (currently implemented by Jackson)

  • Isolate Jackson classes from Terracotta code:

    • no direct imports anymore
    • The use of Jackson specific mixins and such are isolated in a *.json package
  • Refactored Sanskrit to get rid of its coupling with Jackson classes

  • Added a Workaround in NomadChange json serialisation due to a bug in how Jackson is handling polymorphism serialisation

@mathieucarbou When moving to some other json library then mixin present in *.json package in all the affected modules will still need to be changed right ?

Yes! This PR is just isolating all the TC code from Jackson. Currently in platform all the Jackson imports are everywhere. TC code is tightly coupled with Jackson.

This PR (1/2) is a first step: it isolates jackson imports in:

  • the JsonFactory impl
  • in a Json.Module impl for each module requiring some custom mappings.

Then, when converting to Gson, only rewriting those impl will be required because all the TC code now depends on the new TC Json API I introduced here.

Next PR (2/2) will switch the Json Factory impl and these mpdules to Gson.

public String toString() {
return "ObjectMapperFactory{" +
"pretty=" + pretty +
", modules=" + modules +
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Forgot eol.

}
for (Json.Module module : modules) {
if (module instanceof JacksonModule) {
((JacksonModule) module).configure(mapper);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

style: can you move this in the for-loop above.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

no... modules need to be registered before triggering the configuration because there are some dependencies to resolve ;-) I will add a comment.

}

@Override
public void removeKey(String key) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this would be better named as clearValue.

Copy link
Copy Markdown
Member Author

@mathieucarbou mathieucarbou Mar 29, 2023

Choose a reason for hiding this comment

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

Ahah! That's the catch ;-) It is really a remove. The main object is SanskritObjectImpl. This map is just a visitor collecting the different sanskrit operations. And the removal operation, so that is gets serialized correctly in json, needs to have a representation in the map so that it can be serialized. In sanskrit, a removal is mapped to a key associated to null.

this si completely different with the sanskritobjectimpl which holds the object graph and is mutated by sanskrit operations.

setLong(key, (Long) value);
} else if (value instanceof SanskritObject) {
setObject(key, (SanskritObject) value);
} else if (value == null || value instanceof Number) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cannot this block just be removed and handled by the final one.

private final T body;

@JsonCreator
public DiagnosticResponseMixin(@JsonProperty("body") T body,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is it OK to have an empty body? That is, should it be required?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, totally. Jackson mixing are just a way to apply Jackson annotations to model classes without touching them.
Model classes will remain as-is. But All this Jackson modules containing mixing and serializers will be converted to Gson mappers at the end.

Here, what is important are the annotations, that are applied to the real object thanks to the mixin declaration above.

}

@Override
public <T> T map(SanskritObject src, Class<T> dest, String version) throws SanskritException {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused argument version.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No it is used ;-) In the real impl in the persistence module.

.replaceAll("\"buildId\":\"[^\"]*\"", "\"buildId\":\"Build ID\"")
.replaceAll("\"version\":\"[^\"]*\"", "\"version\":\"<version>\"")
.replaceAll("\"clientId\":\"[0-9]+@[^:]*:([^:]*):[^\"]*\"", "\"clientId\":\"[email protected]:$1:<uuid>\"")
.replaceAll("\"clientId\":\"[0-9]+@[^:]*:([^:]*):[^\"]*\"", "\"clientId\": \"[email protected]:$1:<uuid>\"")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't see any other spaces-after-semi colons in here. Was this purposeful?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

weird! I don't know how it happened...

try {
return mapper.readTree(new File(AbstractTest.class.getResource("/" + file).toURI()));
} catch (Exception e) {
return mapper.parse(new File(AbstractTest.class.getResource("/" + file).toURI()));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I guess I wonder why not use the parse(URL) version?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just replaced the method calls ;-) not did a full replace ;-)

}

@Override
public <T> T map(SanskritObject src, Class<T> dest, String version) throws SanskritException {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused argument version.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

no, it is really used ;-)

/**
* @author Mathieu Carbou
*/
public class TestModule extends SimpleModule implements Json.Module {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is it possible to consolidate the various TestModules and TestData?

Copy link
Copy Markdown
Member Author

@mathieucarbou mathieucarbou Mar 29, 2023

Choose a reason for hiding this comment

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

No: TestData are the pojo classes used for testing.

TestModule are the mixin annotations telling the json framework how to serialise and deserialise the objects... So first one is completely json unrelated (normal java classes) and the json module is currently implemented with jackson, but soon wil be implemented with gson.

The TestData classes won't be touched for the Jackson => Gson switch

(there is also no common place for these testing classes)

@mathieucarbou mathieucarbou requested a review from GaryWKeim March 29, 2023 19:43
@mathieucarbou
Copy link
Copy Markdown
Member Author

(rebased)

* Isolate Jackson classes from Terracotta code
* Introduced Terracotta Json API (currently implemented by Jackson)
* Refactored Sanskrit to get rid of its coupling with Jackson classes
@mathieucarbou mathieucarbou merged commit 6656468 into Terracotta-OSS:master Apr 25, 2023
@mathieucarbou mathieucarbou deleted the json branch April 25, 2023 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants