Skip to content

Commit 4a3bab5

Browse files
committed
Hacking
1 parent 1e3fb86 commit 4a3bab5

File tree

6 files changed

+425
-1
lines changed

6 files changed

+425
-1
lines changed

spring-ws-core/src/main/java/org/springframework/ws/client/core/WebServiceTemplate.java

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
import javax.xml.transform.TransformerConfigurationException;
2929
import javax.xml.transform.TransformerException;
3030

31+
import io.micrometer.observation.Observation;
32+
import io.micrometer.observation.ObservationConvention;
33+
import io.micrometer.observation.ObservationRegistry;
3134
import org.apache.commons.logging.Log;
3235
import org.apache.commons.logging.LogFactory;
3336
import org.jspecify.annotations.Nullable;
@@ -44,6 +47,10 @@
4447
import org.springframework.ws.client.WebServiceIOException;
4548
import org.springframework.ws.client.WebServiceTransformerException;
4649
import org.springframework.ws.client.WebServiceTransportException;
50+
import org.springframework.ws.client.core.observation.ClientWebServiceObservationContext;
51+
import org.springframework.ws.client.core.observation.ClientWebServiceObservationConvention;
52+
import org.springframework.ws.client.core.observation.ClientWebServiceObservationDocumentation;
53+
import org.springframework.ws.client.core.observation.DefaultClientWebServiceObservationConvention;
4754
import org.springframework.ws.client.support.WebServiceAccessor;
4855
import org.springframework.ws.client.support.destination.DestinationProvider;
4956
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
@@ -134,6 +141,8 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService
134141
protected static final Log receivedMessageTracingLogger = LogFactory
135142
.getLog(WebServiceTemplate.MESSAGE_TRACING_LOG_CATEGORY + ".received");
136143

144+
private static final ClientWebServiceObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultClientWebServiceObservationConvention();
145+
137146
private @Nullable Marshaller marshaller;
138147

139148
private @Nullable Unmarshaller unmarshaller;
@@ -148,6 +157,10 @@ public class WebServiceTemplate extends WebServiceAccessor implements WebService
148157

149158
private @Nullable DestinationProvider destinationProvider;
150159

160+
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
161+
162+
private @Nullable ClientWebServiceObservationConvention observationConvention;
163+
151164
/** Creates a new {@code WebServiceTemplate} using default settings. */
152165
public WebServiceTemplate() {
153166
initDefaultStrategies();
@@ -360,6 +373,50 @@ public final void setInterceptors(ClientInterceptor[] interceptors) {
360373
this.interceptors = interceptors;
361374
}
362375

376+
/**
377+
* Configure an {@link ObservationRegistry} for collecting spans and metrics for
378+
* request execution. By default, {@link Observation observations} are no-ops.
379+
* @param observationRegistry the observation registry to use
380+
* @since 5.1.0
381+
*/
382+
public void setObservationRegistry(ObservationRegistry observationRegistry) {
383+
Assert.notNull(observationRegistry, "observationRegistry must not be null");
384+
this.observationRegistry = observationRegistry;
385+
}
386+
387+
/**
388+
* Return the configured {@link ObservationRegistry}.
389+
* @since 5.1.0
390+
*/
391+
public ObservationRegistry getObservationRegistry() {
392+
return this.observationRegistry;
393+
}
394+
395+
/**
396+
* Configure an {@link ObservationConvention} that sets the name of the
397+
* {@link Observation observation} as well as its
398+
* {@link io.micrometer.common.KeyValues} extracted from the
399+
* {@link ClientWebServiceObservationContext}. If none set, the
400+
* {@link DefaultClientWebServiceObservationConvention default convention} will be
401+
* used.
402+
* @param observationConvention the observation convention to use
403+
* @since 5.1.0
404+
* @see #setObservationRegistry(ObservationRegistry)
405+
*/
406+
public void setObservationConvention(ClientWebServiceObservationConvention observationConvention) {
407+
Assert.notNull(observationConvention, "observationConvention must not be null");
408+
this.observationConvention = observationConvention;
409+
}
410+
411+
/**
412+
* Return the configured {@link ClientWebServiceObservationConvention}, or
413+
* {@code null} if not set.
414+
* @since 5.1.0
415+
*/
416+
public @Nullable ClientWebServiceObservationConvention getObservationConvention() {
417+
return this.observationConvention;
418+
}
419+
363420
/**
364421
* Initialize the default implementations for the template's strategies:
365422
* {@link SoapFaultMessageResolver},
@@ -622,7 +679,12 @@ public boolean sendAndReceive(String uri, WebServiceMessageCallback requestCallb
622679
@Nullable WebServiceMessageCallback requestCallback, WebServiceMessageExtractor<T> responseExtractor)
623680
throws IOException {
624681
int interceptorIndex = -1;
625-
try {
682+
ClientWebServiceObservationContext observationContext = new ClientWebServiceObservationContext(connection);
683+
Observation observation = ClientWebServiceObservationDocumentation.WEB_SERVICE_CLIENT_EXCHANGES
684+
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
685+
this.observationRegistry)
686+
.start();
687+
try (Observation.Scope scope = observation.openScope()) {
626688
if (requestCallback != null) {
627689
requestCallback.doWithMessage(messageContext.getRequest());
628690
}
@@ -647,6 +709,9 @@ public boolean sendAndReceive(String uri, WebServiceMessageCallback requestCallb
647709
return (T) fallback;
648710
}
649711
WebServiceMessage response = connection.receive(getMessageFactory());
712+
if (response != null) {
713+
observationContext.setResponse(response);
714+
}
650715
messageContext.setResponse(response);
651716
}
652717
logResponse(messageContext);
@@ -678,6 +743,9 @@ public boolean sendAndReceive(String uri, WebServiceMessageCallback requestCallb
678743
triggerAfterCompletion(interceptorIndex, messageContext, ex);
679744
throw ex;
680745
}
746+
finally {
747+
observation.stop();
748+
}
681749
}
682750

683751
/** Sends the request in the given message context over the connection. */
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2005-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.client.core.observation;
18+
19+
import java.io.IOException;
20+
import java.net.URI;
21+
import java.net.URISyntaxException;
22+
23+
import io.micrometer.observation.transport.RequestReplySenderContext;
24+
import org.apache.commons.logging.Log;
25+
import org.apache.commons.logging.LogFactory;
26+
import org.jspecify.annotations.Nullable;
27+
28+
import org.springframework.ws.WebServiceMessage;
29+
import org.springframework.ws.transport.HeadersAwareSenderWebServiceConnection;
30+
import org.springframework.ws.transport.WebServiceConnection;
31+
32+
/**
33+
* Context that holds information for metadata collection during the
34+
* {@link ClientWebServiceObservationDocumentation#WEB_SERVICE_CLIENT_EXCHANGES web
35+
* service clientPexchanges} observations.
36+
*
37+
* @author Stephane Nicoll
38+
* @since 6.1.0
39+
*/
40+
public class ClientWebServiceObservationContext
41+
extends RequestReplySenderContext<WebServiceConnection, WebServiceMessage> {
42+
43+
private static final Log logger = LogFactory.getLog(ClientWebServiceObservationContext.class);
44+
45+
public ClientWebServiceObservationContext(WebServiceConnection connection) {
46+
super(ClientWebServiceObservationContext::setRequestHeader);
47+
setCarrier(connection);
48+
}
49+
50+
private static void setRequestHeader(@Nullable WebServiceConnection connection, String name, String value) {
51+
if (connection instanceof HeadersAwareSenderWebServiceConnection headersAwareConnection) {
52+
try {
53+
headersAwareConnection.addRequestHeader(name, value);
54+
}
55+
catch (IOException ex) {
56+
logger.warn("Failed to add request header '" + name + "'", ex);
57+
}
58+
}
59+
}
60+
61+
public @Nullable URI getUri() {
62+
if (getCarrier() != null) {
63+
try {
64+
return getCarrier().getUri();
65+
}
66+
catch (URISyntaxException ex) {
67+
// ignore
68+
}
69+
}
70+
return null;
71+
}
72+
73+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2005-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.client.core.observation;
18+
19+
import io.micrometer.observation.Observation.Context;
20+
import io.micrometer.observation.ObservationConvention;
21+
22+
/**
23+
* Interface for an {@link ObservationConvention} for
24+
* {@link ClientWebServiceObservationDocumentation#WEB_SERVICE_CLIENT_EXCHANGES web
25+
* service client exchanges}.
26+
*
27+
* @author Stephane Nicoll
28+
* @since 6.1.0
29+
*/
30+
public interface ClientWebServiceObservationConvention
31+
extends ObservationConvention<ClientWebServiceObservationContext> {
32+
33+
@Override
34+
default boolean supportsContext(Context context) {
35+
return context instanceof ClientWebServiceObservationContext;
36+
}
37+
38+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2005-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.client.core.observation;
18+
19+
import io.micrometer.common.docs.KeyName;
20+
import io.micrometer.observation.Observation.Context;
21+
import io.micrometer.observation.ObservationConvention;
22+
import io.micrometer.observation.docs.ObservationDocumentation;
23+
24+
import org.springframework.http.client.ClientHttpRequestFactory;
25+
26+
/**
27+
* Documented {@link io.micrometer.common.KeyValue KeyValues} for
28+
* {@link ClientHttpRequestFactory web service client} observations.
29+
* <p>
30+
* This class is used by automated tools to document KeyValues attached to the web service
31+
* client observations.
32+
*
33+
* @author Stephane Nicoll
34+
* @since 4.1.0
35+
*/
36+
public enum ClientWebServiceObservationDocumentation implements ObservationDocumentation {
37+
38+
/**
39+
* Web Service exchanges observations for clients.
40+
*/
41+
WEB_SERVICE_CLIENT_EXCHANGES {
42+
@Override
43+
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
44+
return DefaultClientWebServiceObservationConvention.class;
45+
}
46+
47+
@Override
48+
public KeyName[] getLowCardinalityKeyNames() {
49+
return LowCardinalityKeyNames.values();
50+
}
51+
52+
@Override
53+
public KeyName[] getHighCardinalityKeyNames() {
54+
return HighCardinalityKeyNames.values();
55+
}
56+
};
57+
58+
public enum LowCardinalityKeyNames implements KeyName {
59+
60+
/**
61+
* Endpoint URL.
62+
*/
63+
LOCATION {
64+
@Override
65+
public String asString() {
66+
return "location";
67+
}
68+
},
69+
70+
/**
71+
* Target namespace of the operation.
72+
*/
73+
NAMESPACE {
74+
@Override
75+
public String asString() {
76+
return "namespace";
77+
}
78+
},
79+
80+
/**
81+
* Name of the protocol used to perform the operation.
82+
*/
83+
PROTOCOL {
84+
@Override
85+
public String asString() {
86+
return "protocol";
87+
}
88+
},
89+
90+
/**
91+
* Name of the operation, as defined by {@code wsdl:operation name}.
92+
*/
93+
OPERATION_NAME {
94+
@Override
95+
public String asString() {
96+
return "operation.name";
97+
}
98+
},
99+
100+
/**
101+
* TODO.
102+
*/
103+
FAULT_CODE {
104+
@Override
105+
public String asString() {
106+
return "fault.code";
107+
}
108+
}
109+
110+
}
111+
112+
public enum HighCardinalityKeyNames implements KeyName {
113+
114+
/**
115+
* Target URI.
116+
*/
117+
URI {
118+
@Override
119+
public String asString() {
120+
return "uri";
121+
}
122+
}
123+
124+
}
125+
126+
}

0 commit comments

Comments
 (0)