-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace field injection with setter injection in 3.x
#2769
Comments
3.x
Removing field injection in Basically this feature request consists in replacing code like: public static class Builder implements Supplier<ConsoleAppender> {
@PluginBuilderAttribute
private boolean follow;
public Builder setFollow(boolean follow) {
this.follow = follow;
return this;
}
} with public static class Builder implements Supplier<ConsoleAppender> {
private boolean follow;
public Builder setFollow(@PluginAttribute boolean follow) {
this.follow = follow;
return this;
}
} so I would leave it as "good first issue". I believe that the process can be done automatically by writing an OpenRewrite rule. |
Question: Can we implement this change in the following manner?
That is, effectively support legacy and stop with field injection in Log4j 3 and onwards. |
Hi @vy and @ppkarwasz, If this issue has not been picked up yet, can I contribute to this, do I have to be assigned to this issue or I can just start working on it? |
Thank you for your help. I have assigned you to the issue. |
Hi @ppkarwasz , re: to modify the annotation processor in 2.x to fail if a plugin builder attribute does not have a public setter (or at least a wither). I was considering the changes needed here, and have already started working on some, I did have a question though, when it comes to identifying if a Field has a public setter, we cannot look at the implementation of the class' methods (not sure if I could find any other way of finding setters looking at the reflection API), and hence to check if a field has a public setter I have the following proposal:
For point 3 and 4, if someone is not using the naming standard for setters, then there is really a problem there overall the logic looks something of this sorts:
This would be sort of our best effort to check if a field has a public setter. |
Hi @jaykataria1111,
The usage of the Reflection API suggests to me that you want to do this check at runtime, right? I would rather do this check in our Note: If you never wrote an annotation processor, its API has a steep learning curve, but the feature only requires a small part of this API:
|
To test the annotation processor, we have some examples in |
This is true, I have never worked on an annotation processor, good learning opportunity! Thanks for listing out the changes that I need this provides a good starting point appreciate it a lot :) You are awesome! :) 💯 |
So my original idea was something of this sorts, This would be during the build of the field, which would be runtime In Plugin Builder.java
Implementing a constraint Validator, and changing the interface.
I do see this has a lots of flaws though already for that purpose, and its too drastic Doing it at compile time makes more sense. Thanks a lot for giving me a direction on this. |
Yes, it does. Validators should be used for something that the user can fix. This can not be fixed by the user, only by the developer. |
Hi @ppkarwasz Made some good progress but needed your input on something: I am not sure if we should add type checking here:
I added this code:
But the issue that I face is with such kind of setters, we are throwing an error in such cases due to type checking :
As you can see the setAge method has a different parameter type than the fields type. if we do have a hard check on the naming convention "setFieldName" (This becomes more restrictive, do not recommend, I have seen people get really creative for no reason) that has a number of problems as well. I do believe we should do some sort of checks, just checking for Hence I have come up with the following plan:
after that I believe we can give up, because then people are doing something really creative with their code. Hence building on the above statement, I have the following logic, which seems to work :) :
If this approach to identify setters looks good to you I can add tests and open a PR :) lmk what are your thoughts |
Checking the type of the setter parameter is not necessary. All the configuration attributes in Log4j are provided as Instead of checking the type of the parameter, it would be more useful to check the return type of the setter: the return types of our setters are not always equal to the type of the builder (e.g. they can be type variables for abstract builders), but the return type should be assignable to the builder type. I believe that |
Note: I noticed that some of our builders have complex conventions for the name of the setter: the setter for an |
… plugin builder attribute does not have a public setter.
Hi @ppkarwasz
+1. I removed the type check and kept the naming check and fix the compilation issues for the purpose. I had to change the name of some fields due to this. It should be a backwards compatible change.
I did incorporate that in code: Some of the methods had a void return type I fixed that for example changed SocketPerformancePreferences
The issue is that, the check-api-comp build task fails for the purpose when I do the code change, should we ignore it? All of my changes so far are backwards compatible, but the tool thinks that these are "MAJOR" changes. I have created a dummy PR in my own repo: Code Changes to show you the code changes and how they should be backward compatible, please let me know if that works, also I am new to github, so learning its quirks, My company uses its own system, so workflows code reviewing are a bit different. So please bear with me if it is not a fancy PR and just my repo, as long as you can see the commit and the diff that is all that matters to me :)
😃 , yeah it is interesting to see, this sort of usage of setters, I must say I wasn't acquainted it with it, which just gives me more breadth of knowledge. |
Great job! 💯
Unfortunately, changing the name of In PR #3174 I fixed the setter problems I found.
Nice catch! Unfortunately changing any public signature is technically a breaking change. Of course the ultimate classification is up to the developers. We have two choices here:
I am quite strict on binary compatibility, even if it concerns code that probably nobody uses. @vy, @JWT007, what do you think? Is fixing those broken setters in |
Hi @ppkarwasz hmmm I am not sure - what is the real impact? How many really create the SocketPerformancePreferences programmatically? :/ I think if someone is creating a SocketPerformancePreferences programmatically they are doing it like this:
In this case changing the return type would not really be a breaking-change because no one is currently expecting a return type. The only difference would be that one could chain the commands after the change:
Worse would be going from a return type to void. :) |
Hi @JWT007,
IIRC, you are using the plugin builders quite a lot. Do you plan to also support
The change will not break source compatibility, but binary compatibility will be broken and the code needs to be recompiled. |
@ppkarwasz in my special case we won't support the SocketAppender at all so this wouldn't affect me at all :) Hmm ok binary compatibility sure - but only for those programmatically configuring - I assume they would need to recompile when upgrading the library version anyways. |
you could just deprecate the old signatures and add new ones for "with***"? |
I am sorry, got confused. Do we want to deprecate the setters all together?
This option was also mentioned. Just was thinking of the direction. re, had a little dumb question:
I see but wouldn't the expectation be to not recompile? Is there a possibility of this being used by other folks? If that is the case then, this makes more sense:
@ppkarwasz @JWT007 That is the action I am going to take. Also just fyi thanks a lot @ppkarwasz , I am getting to learn a lot! Did not know about Semantic Versioning and how to properly deprecate this is awesome 🎉 |
I thought rather to keep the methods as they are and add logic to // This setter has the wrong return type
@SuppressWarnings("log4j.return.type")
public void setBandwidth(final int bandwidth) {
this.bandwidth = bandwidth;
}
@SuppressWarnings("log4j.return.type")
public void setConnectionTime(final int connectionTime) {
this.connectionTime = connectionTime;
}
@SuppressWarnings("log4j.return.type")
public void setLatency(final int latency) {
this.latency = latency;
} This way:
|
@ppkarwasz , that makes more sense let me do that! :) |
… plugin builder attribute does not have a public setter.
Hi @ppkarwasz, I have updated the code, it is late here in my time zone, so have not written the unit tests yet, but just leaving the code here, so that you can peruse through it once, to see what I have done and if it looks good, will add the unit tests and raise the PR by tomorrow soon 😺 |
… plugin builder attribute does not have a public setter.
… plugin builder attribute does not have a public setter.
… plugin builder attribute does not have a public setter.
… plugin builder attribute does not have a public setter.
… plugin builder attribute does not have a public setter.
Hi @ppkarwasz here is a pull request, I have built on top of your change because without your change I will get compilation issues: #3195 |
Hi @ppkarwasz, had a general question about how Pull Requests and Code reviews work in this repo, do I have to reach out to folks to drive it or, someone volunteering will just pickup the review on their own time. Also for 3.x I will start looking at the OpenRewrite rule. |
We don't have a lot of active maintainers (more maintainers are always welcome, see becoming a committer on the ASF website), but we'll look at your PR as soon as we have time. |
Hi @ppkarwasz , it would be awesome I can contribute more, let me see the steps to become a contributor, this is awesome thanks a lot 🙂 |
Hi @ppkarwasz, I am writing an OpenRewrite recipe for the general setters, going to take a dependency for OpenRewrite for the purpose, wanted to share as an FYI. hope taking dependencies is fine. |
That is great! Note that the general problem of moving an annotation from a field to a getter or setter is not Log4j-specific: most dependency injection frameworks (JAXB, JPA, JSR 330, Spring) allow users to annotate fields, getters or setters. @timtebeek, do you have any recommendations on how to rewrite: public static class Builder implements Supplier<ConsoleAppender> {
@PluginBuilderAttribute
private boolean follow;
public Builder setFollow(boolean follow) {
this.follow = follow;
return this;
}
} to public static class Builder implements Supplier<ConsoleAppender> {
private boolean follow;
public Builder setFollow(@PluginAttribute boolean follow) {
this.follow = follow;
return this;
}
} |
@ppkarwasz , that would be awesome if there is a pre-written recipe that we could use! 🎉 Thanks for sharing this @ppkarwasz , it was pretty cool to learn about a openRewrite and what a better way to get up to speed with an actual application! Getting to learn a lot!
Yeah, so run the rule locally, and just not add dependency file changes in the commit? |
I would say that implementing such a change should consist of several steps:
Such a procedure would help reviewing the PR. A reviewer can just pull your first commit and verify that the second commit contains only changes generated by OpenRewrite. Changes generated by OpenRewrite are massive and we need to show the world that PRs are reviewed with due diligence. |
@timtebeek, regarding this:
I tried to lookup if there are any prewritten recipes for the purpose which would help us achieve the above case ^^ and that does not seem to be the case, will we have to write our own recipe? Would that be the way to go? |
Hi @ppkarwasz, trying to understand this statement better:
If we go with a custom recipe, i.e. a custom rule that we make, then I need to add that rule in this repo: https://github.com/apache/logging-log4j-tools ? |
Yes I believe we need to write a new OpenRewrite recipe in Java.
It is probably better to write a more generic rule that, e.g. moves annotation |
Thanks @ppkarwasz , awesome, let me write the rule and contribute it to OpenRewrite project! Would be fun! |
… plugin builder attribute does not have a public setter.
… plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
…rning if a plugin builder attribute does not have a public setter.
As issue #2766 shows, there are still some Log4j plugin builders that don't have setters for all their configuration attributes.
Since field injection becomes more problematic in newer Java version, I believe we should add the missing setters and make sure all new attributes come with a public setter.
Therefore I propose:
2.x
to fail if a plugin builder attribute does not have a public setter (or at least a wither).2.x
.Regarding
3.x
I would prefer to remove field injection completely fromlog4j-plugins
and use builders and setter injection everywhere. IMHO our DI subsystem does not need to have all the features of a fully-fledged DI.Obviously we might still need field injection if we want to support all the existing
2.x
plugins, but I would still love a 200 KiB dependency injection system.The text was updated successfully, but these errors were encountered: