#3678 Prevent duplicate @BeforeMapping and @AfterMapping calls on classes that use the Builder pattern.#3680
#3678 Prevent duplicate @BeforeMapping and @AfterMapping calls on classes that use the Builder pattern.#3680thunderhook wants to merge 4 commits intomainfrom
Conversation
…sses that use the Builder pattern.
| @@ -43,13 +48,15 @@ public void lifecycleMethodsShouldBeInvoked() { | |||
| assertThat( context.getInvokedMethods() ) | |||
| .contains( | |||
There was a problem hiding this comment.
@filiphr
At first I tried to validate the fix by using containsExactly(...). But it soon became too complex and hard to understand due to the nested nature of this test.
So I added an additional fixture test that covers this.
Do you think this should be in a separate Issue3678Mapper or is it ok to test it here?
There was a problem hiding this comment.
I removed the fixture, added the before/afterMapping only with source for the sake of completeness here.
But i provided a separate test that explicitly checks if before and after mapping methods are not called more than once.
There was a problem hiding this comment.
If we have a dedicated test do we even need changes in this particular test here?
There was a problem hiding this comment.
Not necessariliy. It was however easier to debug the internals of the method resolution due to multiple lifecycle callback methods. I will remove it after implementing the fix.
filiphr
left a comment
There was a problem hiding this comment.
I'm not sure the fix is correct.
How will this behave for something like:
@Mapper
public interface CustomMapper {
Target map(SourceA sourceA, SourceB sourceB);
@BeforeMapping
default void beforeWithA(SourceA sourceA) {
// ...
}
@BeforeMapping
default void beforeWithB(SourceB sourceB) {
// ...
}
}| import org.mapstruct.Mapper; | ||
| import org.mapstruct.ReportingPolicy; | ||
|
|
||
| @Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) |
There was a problem hiding this comment.
This is not needed for our tests as it'll anyways fails if there is a compiler warning
| @@ -43,13 +48,15 @@ public void lifecycleMethodsShouldBeInvoked() { | |||
| assertThat( context.getInvokedMethods() ) | |||
| .contains( | |||
There was a problem hiding this comment.
If we have a dedicated test do we even need changes in this particular test here?
| .containsExactly( "beforeMapping", "afterMapping" ) | ||
| .doesNotHaveDuplicates(); |
There was a problem hiding this comment.
doesNotHaveDuplicates is obsolete because we are using containsExactly.
There was a problem hiding this comment.
Yes, I just wanted to express the intent of this test more clearly. We can of course remove this.
There was a problem hiding this comment.
I think containsExactly expresses the intent clearly. It says I'm expecting exactly this and nothing more. If there are duplicates it would clearly fail
|
Looking at the issue I feel like the fix should be happening on a different level, perhaps when getting the lifecycle methods. Most likely the selection criteria needs to be changed |
Good catch. I will have a look at that again. |
| if ( !super.equals( obj ) ) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
This evaluated against Object#equals and therefore was always false. This might be a relic from legacy inheritance structures or never worked as intended.
| if ( !Objects.equals( name, other.name ) ) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Not 100% sure if the name (and already been there: declaringMapper + providingParameter) is enough to differ between multiple method references. All unit tests are green.
|
So, i tried to adapt the method retrieval process. It became confusing very quickly and hard to differ between callback lifecycle methods that belong to the builder object and the finalized object. The initial idea to remove the already invoked methods came from here: So, I had an idea how to improve this. Why not remove the methods via @filiphr WDYT? Is this good enough? Problems that arised during the attempt rewriting the selectors and criteria:
|
|
|
||
| @Mapping( target = "name", source = "sourceA.name") | ||
| @Mapping( target = "description", source = "sourceB.description") | ||
| abstract Target mapTwoSources(SourceA sourceA, SourceB sourceB); |
There was a problem hiding this comment.
Added a specific test with two sources to be sure.
|
Thanks @thunderhook. I'll need some time to think about this. I'm not too fond of the removals. I would prefer if we can select the right types when we need them. The |
Don't spend time on this yet, I think I get what you mean. I'll revisit this and hopefully come up with the solution you suggested. If there are any problems we can further discuss this here. 👍 |
|
@filiphr Short heads up: Unfortunately, I won't be able to do this any time soon. What do you think about merging the fix with this variant and creating a refactoring issue? Or is it too dangerous for you that the PR could have side effects? |
|
That's fine @thunderhook. I'll have a go at it. I'm not sure what kind of side effects this might have and I would rather spend the time on finding the right solution then rush into something |
|
@thunderhook I found a different solution for this. Have a look at #3716. I'll close this PR in favour of that one |
Fixes #3678