Skip to content
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

Nested object is not initialized if no matching property is defined with constructor binding #18917

Closed
snicoll opened this issue Nov 7, 2019 · 13 comments
Assignees
Labels
status: noteworthy A noteworthy issue to call out in the release notes type: bug A general bug
Milestone

Comments

@snicoll
Copy link
Member

snicoll commented Nov 7, 2019

Consider the following example:

@ConfigurationProperties("acme")
@ConstructorBinding
public class AcmeProperties {

	private final Security security;

	public AcmeProperties(Security security) {
		this.security = security;
	}

	public Security getSecurity() {
		return this.security;
	}

	public static class Security {

		private final String username;

		private final String password;

		public Security(@DefaultValue("user") String username, String password) {
			this.username = username;
			this.password = password;
		}

		public String getUsername() {
			return this.username;
		}

		public String getPassword() {
			return this.password;
		}
	}
}

If this object is bound and no acme.security.* property is available in the environment, null is provided to the top-level constructor. If a matching property is found, the Security type is initialized and provided to the constructor.

This is consistent with what we do with JavaBean binding. The main difference here is that the object may have default values assigned to the constructor (here a default user). One solution would be to provide an instance no matter what. The alternative is for the user to check if the constructor provided a null instance and then create a default Security.

There are pros and cons for each:

  • If we create a default instance no matter what, we no longer have the signal that the user didn't provide any key for that nested Security object
  • If we don't care about that, we have to check for null and then create an instance with a copy paste of the @DefaultValue we already provided.

A middle-ground would be to make this configurable, perhaps using @Nullable as we do for actuator endpoints.

@snicoll snicoll added for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels Nov 7, 2019
@snicoll
Copy link
Member Author

snicoll commented Nov 7, 2019

@Nullable won't work as that would flip the responsibility. As for always creating the object, it is going to break use cases where the inner class is flagged with @Valid. Users are relying on the fact the instance is set to trigger validation conditionally.

@philwebb philwebb added type: bug A general bug and removed for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels Nov 8, 2019
@philwebb philwebb added this to the 2.2.x milestone Nov 8, 2019
@OLPMO
Copy link
Contributor

OLPMO commented Nov 10, 2019

Is it a good idea to solve this problem by throwing an exception? @snicoll

@snicoll
Copy link
Member Author

snicoll commented Nov 10, 2019

We've decided to always create the object no matter what to promote the fact browsing through the hierarchy is guaranteed.

@OLPMO
Copy link
Contributor

OLPMO commented Nov 11, 2019

If you do so,how would you solve the problem that lose the signal that the user didn't provide any key for that nested Security object.

@mbhave mbhave self-assigned this Nov 14, 2019
@mbhave mbhave added the for: team-attention An issue we'd like other members of the team to review label Nov 14, 2019
@philwebb philwebb added status: on-hold We can't start working on this issue yet and removed for: team-attention An issue we'd like other members of the team to review labels Nov 15, 2019
@snicoll
Copy link
Member Author

snicoll commented Jan 20, 2020

We should probably extend the scope of this issue to other use cases that are similar, i.e:

  • Collection-based types is null rather than empty
  • Optional is null, rather than Optional#empty

Given the team decision, it looks like we should do this as well for consistency.

@philwebb I can see this issue is on hold and I don't remember why that is.

@philwebb
Copy link
Member

🤷‍♀ I can't remember either

@snicoll snicoll removed the status: on-hold We can't start working on this issue yet label Jan 22, 2020
@zeratul021
Copy link

zeratul021 commented Feb 27, 2020

Hello team,

I've just found this problem in my project. I see on-hold tag removed, does this mean this bug will we fixed in next release?

Thanks!

@snicoll
Copy link
Member Author

snicoll commented Feb 27, 2020

does this mean this bug will we fixed in next release?

That's not what the label means. On hold means we can't make progress at the moment. We don't quite remember why we added it so now it's back to the "issues to fix in 2.2.x" bucket. There is no guarantee that it will be fixed for the next release though.

@zeratul021
Copy link

@snicoll thanks for letting me know. Would you be interested in co-op, like a PR? I'd just need a few pointers, where to start looking.

@mbhave
Copy link
Contributor

mbhave commented Feb 28, 2020

@zeratul021 Thanks for the offer but I think it's better if we handle this one. I'd started looking into but we ran into a few issues which is why the issue was put on hold. I'll pick this up again when time permits.

@zeratul021
Copy link

@mbhave any update on this?

@mbhave mbhave added the for: team-attention An issue we'd like other members of the team to review label Apr 10, 2020
@mbhave mbhave closed this as completed in af6d538 Apr 20, 2020
@mbhave mbhave removed the for: team-attention An issue we'd like other members of the team to review label Apr 20, 2020
@mbhave mbhave modified the milestones: 2.2.x, 2.2.7 Apr 20, 2020
@snicoll
Copy link
Member Author

snicoll commented Apr 21, 2020

There is a @DefaultValue copy in the annotation processor tests that hasn't been reflected. Given that the value is optional now, I suspect something should be done there too. At the very least we should have a test to validate the scenario.

@snicoll snicoll reopened this Apr 21, 2020
@snicoll
Copy link
Member Author

snicoll commented Apr 21, 2020

Turns out everything was ok. The only downside with that approach is that you can write something like this now:

@ConfigurationProperties("test")
public class EmptyDefaultValueProperties {

	@ConstructorBinding
	public EmptyDefaultValueProperties(@DefaultValue String name) {
		this.name = name;
	}
        ....
}

This won't generate a default value at all (no-op).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: noteworthy A noteworthy issue to call out in the release notes type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants