-
Notifications
You must be signed in to change notification settings - Fork 77
/
CDI.java
168 lines (150 loc) · 5.52 KB
/
CDI.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
* Copyright 2013, 2015, Red Hat, Inc., and individual contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jakarta.enterprise.inject.spi;
import java.util.Collections;
import java.util.Comparator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
import jakarta.enterprise.inject.Instance;
/**
* Provides access to the current container.
*
* <p>
* CDI implements {@link Instance} and therefore might be used to perform programmatic lookup.
* If no qualifier is passed to {@link #select} method, the <code>@Default</code> qualifier is assumed.
* </p>
*
*
* @author Pete Muir
* @author Antoine Sabot-Durand
* @author John D. Ament
* @since 1.1
* @param <T> type inherited from {@link Instance}. Always Object for CDI
*/
public abstract class CDI<T> implements Instance<T> {
private static final Object lock = new Object();
private static volatile boolean providerSetManually = false;
/** The set of discovered CDIProviders */
protected static volatile Set<CDIProvider> discoveredProviders = null;
/** {@link CDIProvider} set by user or retrieved by service loader */
protected static volatile CDIProvider configuredProvider = null;
/**
* <p>
* Get the CDI instance that provides access to the current container.
* </p>
*
* <p>
* If there are no providers available, an {@link IllegalStateException} is thrown, otherwise the first provider which can
* access the container is used.
* </p>
*
* @throws IllegalStateException if no {@link CDIProvider} is available
* @return the CDI instance
*/
public static CDI<Object> current() {
return getCDIProvider().getCDI();
}
/**
*
* Obtain the {@link CDIProvider} the user set with {@link #setCDIProvider(CDIProvider)} or the last returned
* {@link CDIProvider} if it returns valid CDI container. Otherwise, use service loader mechanism to retrieve the
* {@link CDIProvider} with the highest priority.
*
* @return the {@link CDIProvider} set by user or retrieved by serviceloader
*/
private static CDIProvider getCDIProvider() {
try {
if (configuredProvider != null && configuredProvider.getCDI() != null) {
return configuredProvider;
}
} catch (IllegalStateException e) {
//if the provider is set manually we do not look for a different provider.
if (providerSetManually) {
throw e;
}
}
configuredProvider = null;
// Discover providers and cache
if (discoveredProviders == null) {
synchronized (lock) {
if (discoveredProviders == null) {
findAllProviders();
}
}
}
configuredProvider = discoveredProviders.stream()
.filter(CDI::checkProvider)
.findFirst().orElseThrow(() -> new IllegalStateException("Unable to access CDI"));
return configuredProvider;
}
private static boolean checkProvider(CDIProvider c) {
try {
return c.getCDI() != null;
} catch (IllegalStateException e) {
return false;
}
}
/**
* <p>
* Set the {@link CDIProvider} to use.
* </p>
*
* <p>
* If a {@link CDIProvider} is set using this method, any provider specified as a service provider will not be used.
* </p>
*
* @param provider the provider to use
* @throws IllegalArgumentException if the provided argument is null
*/
public static void setCDIProvider(CDIProvider provider) {
if (provider == null) {
throw new IllegalArgumentException("CDIProvider must not be null");
}
providerSetManually = true;
configuredProvider = provider;
}
private static void findAllProviders() {
ServiceLoader<CDIProvider> providerLoader;
Set<CDIProvider> providers = new TreeSet<>(Comparator.comparingInt(CDIProvider::getPriority).reversed());
providerLoader = ServiceLoader.load(CDIProvider.class, CDI.class.getClassLoader());
if (!providerLoader.iterator().hasNext()) {
throw new IllegalStateException("Unable to locate CDIProvider");
}
try {
providerLoader.forEach(providers::add);
} catch (ServiceConfigurationError e) {
throw new IllegalStateException(e);
}
CDI.discoveredProviders = Collections.unmodifiableSet(providers);
}
// Helper methods
/**
* Get the CDI BeanManager for the current context
*
* @return the {@link BeanManager}
*/
public abstract BeanManager getBeanManager();
/**
* Get the CDI {@link BeanContainer} for the current context.
*
* Default implementation just forwards the call to {@link #getBeanManager()}.
*
* @return the {@link BeanContainer}
*/
public BeanContainer getBeanContainer() {
return getBeanManager();
}
}