18
18
19
19
import java .util .List ;
20
20
import java .util .concurrent .atomic .AtomicBoolean ;
21
- import java .util .function .UnaryOperator ;
22
21
23
22
import io .micrometer .tracing .otel .bridge .EventPublishingContextWrapper ;
24
23
import io .micrometer .tracing .otel .bridge .OtelTracer .EventPublisher ;
42
41
/**
43
42
* {@link ApplicationListener} to add an OpenTelemetry {@link ContextStorage} wrapper for
44
43
* {@link EventPublisher} bean support. A single {@link ContextStorage} wrapper is added
45
- * as early as possible then updated with {@link EventPublisher} beans as needed.
44
+ * on the {@link ApplicationStartingEvent} then updated with {@link EventPublisher} beans
45
+ * as needed.
46
+ * <p>
47
+ * The {@link #addWrapper()} method may also be called directly if the
48
+ * {@link ApplicationStartingEvent} isn't called early enough or isn't fired.
46
49
*
47
50
* @author Phillip Webb
51
+ * @since 3.4.0
52
+ * @see OpenTelemetryEventPublisherBeansTestExecutionListener
48
53
*/
49
- class OpenTelemetryEventPublisherApplicationListener implements GenericApplicationListener {
54
+ public class OpenTelemetryEventPublisherBeansApplicationListener implements GenericApplicationListener {
50
55
51
56
private static final boolean OTEL_CONTEXT_PRESENT = ClassUtils .isPresent ("io.opentelemetry.context.ContextStorage" ,
52
57
null );
53
58
54
59
private static final boolean MICROMETER_OTEL_PRESENT = ClassUtils
55
60
.isPresent ("io.micrometer.tracing.otel.bridge.OtelTracer" , null );
56
61
62
+ private static final AtomicBoolean added = new AtomicBoolean ();
63
+
57
64
@ Override
58
65
public int getOrder () {
59
66
return Ordered .HIGHEST_PRECEDENCE ;
@@ -69,11 +76,11 @@ public boolean supportsEventType(ResolvableType eventType) {
69
76
70
77
@ Override
71
78
public void onApplicationEvent (ApplicationEvent event ) {
72
- if (!OTEL_CONTEXT_PRESENT || ! MICROMETER_OTEL_PRESENT ) {
79
+ if (!isInstallable () ) {
73
80
return ;
74
81
}
75
82
if (event instanceof ApplicationStartingEvent ) {
76
- EventPublisherBeansContextWrapper . addWrapperIfNecessary ();
83
+ addWrapper ();
77
84
}
78
85
if (event instanceof ContextRefreshedEvent contextRefreshedEvent ) {
79
86
ApplicationContext applicationContext = contextRefreshedEvent .getApplicationContext ();
@@ -83,92 +90,105 @@ public void onApplicationEvent(ApplicationEvent event) {
83
90
.stream ()
84
91
.map (EventPublishingContextWrapper ::new )
85
92
.toList ();
86
- EventPublisherBeansContextWrapper .instance .put (applicationContext , publishers );
93
+ Wrapper .instance .put (applicationContext , publishers );
87
94
}
88
95
if (event instanceof ContextClosedEvent contextClosedEvent ) {
89
- EventPublisherBeansContextWrapper .instance .remove (contextClosedEvent .getApplicationContext ());
96
+ Wrapper .instance .remove (contextClosedEvent .getApplicationContext ());
90
97
}
91
98
}
92
99
93
100
/**
94
- * The single {@link ContextStorage} wrapper that delegates to {@link EventPublisher}
95
- * beans.
101
+ * {@link ContextStorage#addWrapper(java.util.function.Function) Add} the
102
+ * {@link ContextStorage} wrapper to ensure that {@link EventPublisher} are propagated
103
+ * correctly.
96
104
*/
97
- static class EventPublisherBeansContextWrapper implements UnaryOperator <ContextStorage > {
105
+ public static void addWrapper () {
106
+ if (isInstallable () && added .compareAndSet (false , true )) {
107
+ Wrapper .instance .addWrapper ();
108
+ }
109
+ }
110
+
111
+ private static boolean isInstallable () {
112
+ return OTEL_CONTEXT_PRESENT && MICROMETER_OTEL_PRESENT ;
113
+ }
98
114
99
- private static final AtomicBoolean added = new AtomicBoolean ();
115
+ /**
116
+ * Single instance class used to add the wrapper and manage the {@link EventPublisher}
117
+ * beans.
118
+ */
119
+ static final class Wrapper {
100
120
101
- private static final EventPublisherBeansContextWrapper instance = new EventPublisherBeansContextWrapper ();
121
+ static Wrapper instance = new Wrapper ();
102
122
103
- private final MultiValueMap <ApplicationContext , EventPublishingContextWrapper > publishers = new LinkedMultiValueMap <>();
123
+ private final MultiValueMap <ApplicationContext , EventPublishingContextWrapper > beans = new LinkedMultiValueMap <>();
104
124
105
- private volatile ContextStorage delegate ;
125
+ private volatile ContextStorage storageDelegate ;
106
126
107
- static void addWrapperIfNecessary () {
108
- if (added .compareAndSet (false , true )) {
109
- ContextStorage .addWrapper (instance );
110
- }
127
+ private Wrapper () {
111
128
}
112
129
113
- @ Override
114
- public ContextStorage apply (ContextStorage contextStorage ) {
115
- return new EventPublisherBeansContextStorage (contextStorage );
130
+ private void addWrapper () {
131
+ ContextStorage .addWrapper (Storage ::new );
116
132
}
117
133
118
134
void put (ApplicationContext applicationContext , List <EventPublishingContextWrapper > publishers ) {
119
135
synchronized (this ) {
120
- this .publishers .addAll (applicationContext , publishers );
121
- this .delegate = null ;
136
+ this .beans .addAll (applicationContext , publishers );
137
+ this .storageDelegate = null ;
122
138
}
123
139
}
124
140
125
141
void remove (ApplicationContext applicationContext ) {
126
142
synchronized (this ) {
127
- this .publishers .remove (applicationContext );
128
- this .delegate = null ;
143
+ this .beans .remove (applicationContext );
144
+ this .storageDelegate = null ;
129
145
}
130
146
}
131
147
132
- private ContextStorage getDelegate (ContextStorage parent ) {
133
- ContextStorage delegate = this .delegate ;
148
+ ContextStorage getStorageDelegate (ContextStorage parent ) {
149
+ ContextStorage delegate = this .storageDelegate ;
134
150
if (delegate == null ) {
135
151
synchronized (this ) {
136
152
delegate = parent ;
137
- for (List <EventPublishingContextWrapper > publishers : this .publishers .values ()) {
153
+ for (List <EventPublishingContextWrapper > publishers : this .beans .values ()) {
138
154
for (EventPublishingContextWrapper publisher : publishers ) {
139
155
delegate = publisher .apply (delegate );
140
156
}
141
157
}
158
+ this .storageDelegate = delegate ;
142
159
}
143
160
}
144
161
return delegate ;
145
162
}
146
163
147
164
/**
148
- * The wrapped {@link ContextStorage} that delegates to the
149
- * {@link EventPublisherBeansContextWrapper}.
165
+ * {@link ContextStorage} that delegates to the {@link EventPublisher} beans.
150
166
*/
151
- class EventPublisherBeansContextStorage implements ContextStorage {
167
+ class Storage implements ContextStorage {
152
168
153
169
private final ContextStorage parent ;
154
170
155
- EventPublisherBeansContextStorage (ContextStorage wrapped ) {
156
- this .parent = wrapped ;
171
+ Storage (ContextStorage parent ) {
172
+ this .parent = parent ;
157
173
}
158
174
159
175
@ Override
160
176
public Scope attach (Context toAttach ) {
161
- return getDelegate (this . parent ).attach (toAttach );
177
+ return getDelegate ().attach (toAttach );
162
178
}
163
179
164
180
@ Override
165
181
public Context current () {
166
- return getDelegate (this . parent ).current ();
182
+ return getDelegate ().current ();
167
183
}
168
184
169
185
@ Override
170
186
public Context root () {
171
- return getDelegate (this .parent ).root ();
187
+ return getDelegate ().root ();
188
+ }
189
+
190
+ private ContextStorage getDelegate () {
191
+ return getStorageDelegate (this .parent );
172
192
}
173
193
174
194
}
0 commit comments