-
Notifications
You must be signed in to change notification settings - Fork 41.4k
Add auto-configuration for WebServiceTemplate #12707
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
Conversation
1ea6f3c
to
2055f9c
Compare
838aa6f
to
77e2258
Compare
I've removed the Jaxb2MarshallerBuilder from this PR. |
b2dda3b
to
381aaf7
Compare
@philwebb |
@nosan I've been primarily focusing on bugs and regressions in the 2.0.x line so feature PRs haven't had much of my attention lately. The code looks very complete but I guess my primary concern is with how many people are currently using I'm probably not going to have time to give much feedback on this in the near-term, but as a team we'll be looking again at PRs once work on 2.1 starts to ramp up. |
@philwebb Thank you for your reply. Looking forward to hearing from you soon. |
Out of curiosity: Aren't there other libraries already providing the feature to create webserivce client stubs like CXF? Or do I misunderstand the idea of WebServiceTemplateBuilder ? |
516af8f
to
931bb37
Compare
@snicoll I saw that you have assigned a review for yourself, thank you for this. FYI I have done some small changes on this one (I removed reflections where it is possible). I hope I didn't break your review. |
063d52c
to
86ca383
Compare
@rfelgent In the future, it would be great to have metrics for it as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the PR. I've started to polish and spend quite some time on it but they were a few items I'd like to discuss before going any further.
See the individual comments. I've started a polish here so I might update it based on our discussion rather than rebasing on your branch.
* @return a new builder instance | ||
* @see WebServiceTemplate#setInterceptors(ClientInterceptor[]) | ||
*/ | ||
public WebServiceTemplateBuilder setInterceptors(ClientInterceptor... interceptors) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer if we keep the same style as what we're doing with RestTemplate
(i.e. interceptors
and additionalInterceptors
). I've already started a polish on my fork so need to act on it.
*/ | ||
|
||
public WebServiceTemplateBuilder setWebServiceMessageSenders( | ||
Collection<? extends Supplier<? extends WebServiceMessageSender>> webServiceMessageSenderSuppliers) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed the Supplier in my polish commit. I didn't found a single use of it and would argue that it is something you can determine when you build the template
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't do this because of 'setReadTimeout' and 'setConnectionTimeout'. This test will not work.
HttpUrlConnectionMessageSender sender = new HttpUrlConnectionMessageSender();
WebServiceTemplate ws = this.builder
.setConnectionTimeout(5000)
.setReadTimeout(2000)
.messageSenders(sender)
.build();
WebServiceTemplate ws1 = this.builder
.setConnectionTimeout(4000)
.setReadTimeout(3000)
.build();
assertThat(ReflectionTestUtils.getField(ws.getMessageSenders()[0], "connectionTimeout"))
.isEqualTo(Duration.ofMillis(5000));
assertThat(ReflectionTestUtils.getField(ws.getMessageSenders()[0], "readTimeout"))
.isEqualTo(Duration.ofMillis(2000));
//
assertThat(ReflectionTestUtils.getField(ws1.getMessageSenders()[0], "connectionTimeout")).isEqualTo(Duration.ofMillis(4000));
assertThat(ReflectionTestUtils.getField(ws1.getMessageSenders()[0], "readTimeout"))
.isEqualTo(Duration.ofMillis(3000));
With Suppliers
you will not have this issue, except this case () -> WebServiceMessageSender
instead of WebServiceMessageSender::new | () -> new WebServiceMessageSender()
. Also, this issue will be manifest themselves only for custom WebServiceMessageSender
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO that's one more argument to remove that feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only for custom WebServiceMessagaSenders
public WebServiceTemplateBuilder setCheckConnectionForFault( | ||
boolean checkConnectionForFault) { | ||
return new WebServiceTemplateBuilder(this.interceptors, | ||
append(this.internalCustomizers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this pattern. If you call the setter several times, you'll append this customizer and rely on the fact the last one called will set the expected value. I guess you've done this that way to avoid adding too much parameters to the builder? I think we need to find a different option for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it could be handled by Boolean
type, but you still need a custom customizer for FaultMessageResolver
.
Yes, I did this to avoid adding too many parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the meantime I've seen we're doing that with RestTemplateBuilder
and I see you're using a separate collection. Retrospectively, it's not that bad at all.
* @see BeanUtils#instantiateClass(Class) | ||
*/ | ||
|
||
public WebServiceTemplateBuilder setWebServiceMessageSender( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are too many flavours to set a WebServiceMessageSender
. Specifying a vararg of instance and a Class
looks wrong to me. I've removed that in my fork.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the was an idea to have an extra method with a Class
instead of a Supplier
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it but I think the reason why a Supplier
was added is wrong.
private ClientHttpRequestFactory getRequestFactory( | ||
ClientHttpRequestMessageSender source) { | ||
ClientHttpRequestFactory requestFactory = source.getRequestFactory(); | ||
if (!(requestFactory instanceof AbstractClientHttpRequestFactoryWrapper)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't looked in detail but I am wondering why you need to resort to reflection for a builder pattern that should "just" do what the regular template can do. Perhaps this feature should be removed in benefit from something more advanced? I don't like the idea to use reflection in production code.
* @throws java.lang.IllegalStateException if the underlying source doesn't support a | ||
* connection timeout. | ||
*/ | ||
public WebServiceTemplateBuilder setConnectionTimeout(int connectionTimeout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that we may have multiple WebServiceMessageSender
, I don't think we should expose this method here. You can provide a configured WebServiceMessageSender
, this feels weird to me that all of them are reconfigured behind the scenes.
* @throws java.lang.IllegalStateException if the underlying source doesn't support a | ||
* read timeout. | ||
*/ | ||
public WebServiceTemplateBuilder setReadTimeout(int readTimeout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also go away.
webServiceTemplate.setMessageFactory(this.messageFactory); | ||
} | ||
|
||
if (!CollectionUtils.isEmpty(this.customizers)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the last step
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completely agree, this should be the last step, I did this in favor of RestTemplateBuilder
algorithm.
public <T extends RestTemplate> T configure(T restTemplate) {
configureRequestFactory(restTemplate);
if (!CollectionUtils.isEmpty(this.messageConverters)) {
restTemplate.setMessageConverters(new ArrayList<>(this.messageConverters));
}
if (this.uriTemplateHandler != null) {
restTemplate.setUriTemplateHandler(this.uriTemplateHandler);
}
if (this.errorHandler != null) {
restTemplate.setErrorHandler(this.errorHandler);
}
if (this.rootUri != null) {
RootUriTemplateHandler.addTo(restTemplate, this.rootUri);
}
if (this.basicAuthorization != null) {
restTemplate.getInterceptors().add(this.basicAuthorization);
}
if (!CollectionUtils.isEmpty(this.restTemplateCustomizers)) {
for (RestTemplateCustomizer customizer : this.restTemplateCustomizers) {
customizer.customize(restTemplate);
}
}
restTemplate.getInterceptors().addAll(this.interceptors);
return restTemplate;
}
That was weird to me as well...
} | ||
|
||
if (!CollectionUtils.isEmpty(this.webServiceMessageSenderCustomizers)) { | ||
if (!ObjectUtils.isEmpty(webServiceTemplate.getMessageSenders())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks weird to me. Why do we check if the template has existing messageSenders?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can easily override a WebServiceTemplate
and change this logic.
@snicoll Thanks a lot. |
@snicoll But still, If you think the current implementation is within its scope, then I will not interfere. It would be great to reconsider this method. |
@nosan Another aspect to consider is that you have you wrote a custom |
@gregturn As I mentioned before it would be better to remove aspecific functionality from |
3bd6597
to
2dc0ce3
Compare
@snicoll I have updated my PR and I have removed all the controversial things in my opinion. Please take a look. Thanks in advance. |
2dc0ce3
to
99ba35f
Compare
That's exactly what I've seen you've edited your comment in the meantime as you've probably realized that We'll continue based on my original polish as I've already spent a significant amount of time on it. I am considering moving the detection of a suitable HTTP-based |
@snicoll Thank you.
I have never seen |
Yes, I have checked |
Sorry about that, my brain mixed the two concepts, here is the dependency section I intended initially.
The reference documentation describes transports for HTTP, JMS, email and XMPP so we can't have a |
I see, thank you, so I was wrong on the beginning. Sorry about that.
Do you need some help with that? |
@snicoll While it's true that SOAP supports all these various protocols, since we're talking Spring Boot which is based on autoconfiguring for the 80% case, perhaps we could assume HTTP (or trigger off some web-based criteria) and supply that. HTTP is probably 80% or more of what people are doing with Spring WS, let alone the SOAP community. |
That wasn’t my point. My point is that we can’t assume message sender is HTTP based as both its contract and the doc states otherwise. See the discussion above for more details. |
* pr/12707: Polish "Add auto-configuration for WebServiceTemplate" Extract ClientHttpRequestFactory detection to its own class Add auto-configuration for WebServiceTemplate
Closed by c973488 |
@snicoll Thank you a lot for your time. |
Added the WebServiceTemplateBuilder (similar to the RestTemplateBuilder) helper class for building WebServiceTemplate.
In a typical auto-configured Spring Boot application this builder will be available as bean and can be injected whenever a WebServiceTemplate is needed.