1
1
package io .quarkus .vertx .runtime ;
2
2
3
+ import static io .quarkus .vertx .runtime .storage .QuarkusLocalStorageKeyVertxServiceProvider .REQUEST_SCOPED_LOCAL_KEY ;
4
+
3
5
import java .lang .annotation .Annotation ;
4
- import java .util .Collections ;
5
6
import java .util .List ;
7
+ import java .util .concurrent .ConcurrentMap ;
6
8
import java .util .concurrent .CopyOnWriteArrayList ;
9
+ import java .util .concurrent .atomic .AtomicBoolean ;
10
+
11
+ import jakarta .enterprise .context .RequestScoped ;
7
12
8
13
import io .netty .util .concurrent .FastThreadLocal ;
9
14
import io .quarkus .arc .CurrentContext ;
14
19
import io .smallrye .common .vertx .VertxContext ;
15
20
import io .vertx .core .Context ;
16
21
import io .vertx .core .Vertx ;
22
+ import io .vertx .core .impl .ContextInternal ;
17
23
18
24
public class VertxCurrentContextFactory implements CurrentContextFactory {
19
25
20
26
private static final String LOCAL_KEY_PREFIX = "io.quarkus.vertx.cdi-current-context" ;
21
27
22
28
private final List <String > keys ;
23
- private final List < String > unmodifiableKeys ;
29
+ private final AtomicBoolean requestScopedKeyCreated ;
24
30
25
31
public VertxCurrentContextFactory () {
26
32
// There will be only a few mutative operations max
27
33
this .keys = new CopyOnWriteArrayList <>();
28
- // We do not want to allocate a new object for each VertxCurrentContextFactory#keys() invocation
29
- this .unmodifiableKeys = Collections .unmodifiableList (keys );
34
+ this .requestScopedKeyCreated = new AtomicBoolean ();
30
35
}
31
36
32
37
@ Override
33
38
public <T extends InjectableContext .ContextState > CurrentContext <T > create (Class <? extends Annotation > scope ) {
39
+ if (scope == RequestScoped .class ) {
40
+ if (!requestScopedKeyCreated .compareAndSet (false , true )) {
41
+ throw new IllegalStateException (
42
+ "Multiple current contexts for the same scope are not supported. Current context for "
43
+ + scope + " already exists!" );
44
+ }
45
+ return new VertxCurrentContext <T >(REQUEST_SCOPED_LOCAL_KEY );
46
+ }
34
47
String key = LOCAL_KEY_PREFIX + scope .getName ();
35
48
if (keys .contains (key )) {
36
49
throw new IllegalStateException (
@@ -41,20 +54,52 @@ public <T extends InjectableContext.ContextState> CurrentContext<T> create(Class
41
54
return new VertxCurrentContext <>(key );
42
55
}
43
56
44
- /**
45
- *
46
- * @return an unmodifiable list of used keys
47
- */
48
- public List <String > keys () {
49
- return unmodifiableKeys ;
57
+ public ContextInternal duplicateContextIfContainsAnyCreatedScopeKeys (ContextInternal vertxContext ) {
58
+ if (!containsAnyCreatedScopeKeys (vertxContext )) {
59
+ return vertxContext ;
60
+ }
61
+ // Duplicate the context, copy the data, remove the request context
62
+ var duplicateCtx = vertxContext .duplicate ();
63
+ // TODO this is not copying any ContextLocal<?> from the original context to the new one!
64
+ var duplicateCtxData = duplicateCtx .localContextData ();
65
+ duplicateCtxData .putAll (vertxContext .localContextData ());
66
+ keys .forEach (duplicateCtxData ::remove );
67
+ if (requestScopedKeyCreated .get ()) {
68
+ duplicateCtx .removeLocal (REQUEST_SCOPED_LOCAL_KEY );
69
+ }
70
+ VertxContextSafetyToggle .setContextSafe (duplicateCtx , true );
71
+ return duplicateCtx ;
72
+ }
73
+
74
+ private boolean containsAnyCreatedScopeKeys (ContextInternal vertxContext ) {
75
+ boolean requestScopedKeyCreated = this .requestScopedKeyCreated .get ();
76
+ if (requestScopedKeyCreated && vertxContext .getLocal (REQUEST_SCOPED_LOCAL_KEY ) != null ) {
77
+ return true ;
78
+ }
79
+ if (keys .isEmpty ()) {
80
+ return false ;
81
+ }
82
+ ConcurrentMap <Object , Object > local = vertxContext .localContextData ();
83
+ if (keys .size () == 1 ) {
84
+ // Very often there will be only one key used
85
+ return local .containsKey (keys .get (0 ));
86
+ } else {
87
+ for (String key : keys ) {
88
+ if (local .containsKey (key )) {
89
+ return true ;
90
+ }
91
+ }
92
+ }
93
+ return false ;
50
94
}
51
95
52
96
private static final class VertxCurrentContext <T extends ContextState > implements CurrentContext <T > {
53
97
54
- private final String key ;
55
- private final FastThreadLocal <T > fallback = new FastThreadLocal <>();
98
+ // It allows to use both ContextLocalImpl and String keys
99
+ private final Object key ;
100
+ private volatile FastThreadLocal <T > fallback ;
56
101
57
- private VertxCurrentContext (String key ) {
102
+ private VertxCurrentContext (Object key ) {
58
103
this .key = key ;
59
104
}
60
105
@@ -64,7 +109,24 @@ public T get() {
64
109
if (context != null && VertxContext .isDuplicatedContext (context )) {
65
110
return context .getLocal (key );
66
111
}
67
- return fallback .get ();
112
+ return fallback ().get ();
113
+ }
114
+
115
+ private FastThreadLocal <T > fallback () {
116
+ var fallback = this .fallback ;
117
+ if (fallback == null ) {
118
+ fallback = getOrCreateFallback ();
119
+ }
120
+ return fallback ;
121
+ }
122
+
123
+ private synchronized FastThreadLocal <T > getOrCreateFallback () {
124
+ var fallback = this .fallback ;
125
+ if (fallback == null ) {
126
+ fallback = new FastThreadLocal <>();
127
+ this .fallback = fallback ;
128
+ }
129
+ return fallback ;
68
130
}
69
131
70
132
@ Override
@@ -80,7 +142,7 @@ public void set(T state) {
80
142
}
81
143
82
144
} else {
83
- fallback .set (state );
145
+ fallback () .set (state );
84
146
}
85
147
}
86
148
@@ -91,7 +153,7 @@ public void remove() {
91
153
// NOOP - the DC should not be shared.
92
154
// context.removeLocal(key);
93
155
} else {
94
- fallback .remove ();
156
+ fallback () .remove ();
95
157
}
96
158
}
97
159
0 commit comments