/
LettuceCdiExtension.java
171 lines (149 loc) · 6.55 KB
/
LettuceCdiExtension.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
169
170
171
/*
* Copyright 2011-2020 the original author or authors.
*
* 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
*
* https://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 io.lettuce.core.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.spi.*;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.internal.LettuceSets;
import io.lettuce.core.resource.ClientResources;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
* A portable CDI extension which registers beans for lettuce. If there are no RedisURIs there are also no registrations for
* {@link RedisClient RedisClients}. The extension allows to create {@link RedisClient} and {@link RedisClusterClient}
* instances. Client instances are provided under the same qualifiers as the {@link RedisURI}. {@link ClientResources} can be
* shared across multiple client instances (Standalone, Cluster) by providing a {@link ClientResources} bean with the same
* qualifiers as the {@link RedisURI}.
*
* <h3>Example</h3>
*
* <pre class="code">
* public class Producers {
* @Produces
* public RedisURI redisURI() {
* return RedisURI.Builder.redis("localhost", 6379).build();
* }
*
* @Produces
* public ClientResources clientResources() {
* return DefaultClientResources.create()
* }
*
* public void shutdownClientResources(@Disposes ClientResources clientResources) throws Exception {
* clientResources.shutdown().get();
* }
* }
* </pre>
*
*
* <pre class="code">
* public class Consumer {
*
* @Inject
* private RedisClient client;
*
* @Inject
* private RedisClusterClient clusterClient;
*
* }
* </pre>
*
* @author Mark Paluch
*/
public class LettuceCdiExtension implements Extension {
private static final InternalLogger LOGGER = InternalLoggerFactory.getInstance(LettuceCdiExtension.class);
private final Map<Set<Annotation>, Bean<RedisURI>> redisUris = new ConcurrentHashMap<>();
private final Map<Set<Annotation>, Bean<ClientResources>> clientResources = new ConcurrentHashMap<>();
public LettuceCdiExtension() {
LOGGER.info("Activating CDI extension for lettuce.");
}
/**
* Implementation of a an observer which checks for RedisURI beans and stores them in {@link #redisUris} for later
* association with corresponding repository beans.
*
* @param <T> The type.
* @param processBean The annotated type as defined by CDI.
*/
@SuppressWarnings("unchecked")
<T> void processBean(@Observes ProcessBean<T> processBean) {
Bean<T> bean = processBean.getBean();
for (Type type : bean.getTypes()) {
if (!(type instanceof Class<?>)) {
continue;
}
// Check if the bean is an RedisURI.
if (RedisURI.class.isAssignableFrom((Class<?>) type)) {
Set<Annotation> qualifiers = LettuceSets.newHashSet(bean.getQualifiers());
if (bean.isAlternative() || !redisUris.containsKey(qualifiers)) {
LOGGER.debug(String.format("Discovered '%s' with qualifiers %s.", RedisURI.class.getName(), qualifiers));
redisUris.put(qualifiers, (Bean<RedisURI>) bean);
}
}
if (ClientResources.class.isAssignableFrom((Class<?>) type)) {
Set<Annotation> qualifiers = LettuceSets.newHashSet(bean.getQualifiers());
if (bean.isAlternative() || !clientResources.containsKey(qualifiers)) {
LOGGER.debug(
String.format("Discovered '%s' with qualifiers %s.", ClientResources.class.getName(), qualifiers));
clientResources.put(qualifiers, (Bean<ClientResources>) bean);
}
}
}
}
/**
* Implementation of a an observer which registers beans to the CDI container for the detected RedisURIs.
* <p>
* The repository beans are associated to the EntityManagers using their qualifiers.
*
* @param beanManager The BeanManager instance.
*/
void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
int counter = 0;
for (Entry<Set<Annotation>, Bean<RedisURI>> entry : redisUris.entrySet()) {
Bean<RedisURI> redisUri = entry.getValue();
Set<Annotation> qualifiers = entry.getKey();
String clientBeanName = RedisClient.class.getSimpleName();
String clusterClientBeanName = RedisClusterClient.class.getSimpleName();
if (!containsDefault(qualifiers)) {
clientBeanName += counter;
clusterClientBeanName += counter;
counter++;
}
Bean<ClientResources> clientResources = this.clientResources.get(qualifiers);
RedisClientCdiBean clientBean = new RedisClientCdiBean(redisUri, clientResources, beanManager, qualifiers,
clientBeanName);
register(afterBeanDiscovery, qualifiers, clientBean);
RedisClusterClientCdiBean clusterClientBean = new RedisClusterClientCdiBean(redisUri, clientResources, beanManager,
qualifiers, clusterClientBeanName);
register(afterBeanDiscovery, qualifiers, clusterClientBean);
}
}
private boolean containsDefault(Set<Annotation> qualifiers) {
return qualifiers.stream().filter(input -> input instanceof Default).findFirst().isPresent();
}
private void register(AfterBeanDiscovery afterBeanDiscovery, Set<Annotation> qualifiers, Bean<?> bean) {
LOGGER.info(String.format("Registering bean '%s' with qualifiers %s.", bean.getBeanClass().getName(), qualifiers));
afterBeanDiscovery.addBean(bean);
}
}