1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.shiro.guice;
20
21 import com.google.inject.Key;
22 import com.google.inject.PrivateModule;
23 import com.google.inject.Provider;
24 import com.google.inject.TypeLiteral;
25 import com.google.inject.binder.AnnotatedBindingBuilder;
26 import com.google.inject.binder.LinkedBindingBuilder;
27 import com.google.inject.matcher.Matchers;
28 import com.google.inject.multibindings.Multibinder;
29 import com.google.inject.spi.InjectionListener;
30 import com.google.inject.spi.TypeEncounter;
31 import com.google.inject.spi.TypeListener;
32 import com.google.inject.util.Types;
33 import org.apache.shiro.config.ConfigurationException;
34 import org.apache.shiro.env.Environment;
35 import org.apache.shiro.event.EventBus;
36 import org.apache.shiro.event.EventBusAware;
37 import org.apache.shiro.event.Subscribe;
38 import org.apache.shiro.event.support.DefaultEventBus;
39 import org.apache.shiro.lang.util.ClassUtils;
40 import org.apache.shiro.lang.util.Destroyable;
41 import org.apache.shiro.mgt.DefaultSecurityManager;
42 import org.apache.shiro.mgt.SecurityManager;
43 import org.apache.shiro.realm.Realm;
44 import org.apache.shiro.session.mgt.DefaultSessionManager;
45 import org.apache.shiro.session.mgt.SessionManager;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import javax.annotation.PreDestroy;
50 import java.lang.reflect.Method;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.Set;
55 import java.util.WeakHashMap;
56
57
58
59
60
61
62
63 public abstract class ShiroModule extends PrivateModule implements Destroyable {
64
65 private final Logger log = LoggerFactory.getLogger(ShiroModule.class);
66
67 private final Set<Destroyable> destroyables = Collections.newSetFromMap(new WeakHashMap<Destroyable, Boolean>());
68
69 public void configure() {
70
71 bindSecurityManager(bind(SecurityManager.class));
72 bindSessionManager(bind(SessionManager.class));
73 bindEnvironment(bind(Environment.class));
74 bindListener(BeanTypeListener.MATCHER, new BeanTypeListener());
75 bindEventBus(bind(EventBus.class));
76 bindListener(Matchers.any(), new SubscribedEventTypeListener());
77 bindListener(Matchers.any(), new EventBusAwareTypeListener());
78 final DestroyableInjectionListener.DestroyableRegistry registry = new DestroyableInjectionListener.DestroyableRegistry() {
79 public void add(Destroyable destroyable) {
80 ShiroModule.this.add(destroyable);
81 }
82
83 @PreDestroy
84 public void destroy() {
85 ShiroModule.this.destroy();
86 }
87 };
88 bindListener(LifecycleTypeListener.MATCHER, new LifecycleTypeListener(registry));
89
90 expose(SecurityManager.class);
91 expose(EventBus.class);
92
93 configureShiro();
94 bind(realmCollectionKey())
95 .to(realmSetKey());
96
97 bind(DestroyableInjectionListener.DestroyableRegistry.class).toInstance(registry);
98 BeanTypeListener.ensureBeanTypeMapExists(binder());
99 }
100
101 @SuppressWarnings({"unchecked"})
102 private Key<Set<Realm>> realmSetKey() {
103 return (Key<Set<Realm>>) Key.get(TypeLiteral.get(Types.setOf(Realm.class)));
104 }
105
106 @SuppressWarnings({"unchecked"})
107 private Key<Collection<Realm>> realmCollectionKey() {
108 return (Key<Collection<Realm>>) Key.get(Types.newParameterizedType(Collection.class, Realm.class));
109 }
110
111
112
113
114 protected abstract void configureShiro();
115
116
117
118
119
120
121
122
123 protected final LinkedBindingBuilder<Realm> bindRealm() {
124 Multibinder<Realm> multibinder = Multibinder.newSetBinder(binder(), Realm.class);
125 return multibinder.addBinding();
126 }
127
128
129
130
131
132
133
134
135 protected void bindSecurityManager(AnnotatedBindingBuilder<? super SecurityManager> bind) {
136 try {
137 bind.toConstructor(DefaultSecurityManager.class.getConstructor(Collection.class)).asEagerSingleton();
138 } catch (NoSuchMethodException e) {
139 throw new ConfigurationException("This really shouldn't happen."
140 + " Either something has changed in Shiro, or there's a bug in "
141 + ShiroModule.class.getSimpleName(), e);
142 }
143 }
144
145
146
147
148
149
150
151
152 protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind) {
153 bind.to(DefaultSessionManager.class).asEagerSingleton();
154 }
155
156
157
158
159
160
161
162
163 protected void bindEnvironment(AnnotatedBindingBuilder<Environment> bind) {
164 bind.to(GuiceEnvironment.class).asEagerSingleton();
165 }
166
167
168
169
170
171
172
173
174 protected final <T> void bindBeanType(TypeLiteral<T> typeLiteral, Key<? extends T> key) {
175 BeanTypeListener.bindBeanType(binder(), typeLiteral, key);
176 }
177
178
179
180
181
182
183
184 protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) {
185 bind.to(DefaultEventBus.class).asEagerSingleton();
186 }
187
188
189
190
191
192
193
194 public final void destroy() {
195 for (Destroyable destroyable : destroyables) {
196 try {
197 destroyable.destroy();
198 } catch (Exception e) {
199 log.warn("Error destroying component class: " + destroyable.getClass(), e);
200 }
201 }
202 }
203
204 public void add(Destroyable destroyable) {
205 this.destroyables.add(destroyable);
206 }
207
208 private final class SubscribedEventTypeListener implements TypeListener {
209 @Override
210 public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
211
212 final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class);
213
214 List<Method> methods = ClassUtils.getAnnotatedMethods(typeLiteral.getRawType(), Subscribe.class);
215 if (methods != null && !methods.isEmpty()) {
216 typeEncounter.register(new InjectionListener<I>() {
217 @Override
218 public void afterInjection(Object o) {
219 eventBusProvider.get().register(o);
220 }
221 });
222 }
223 }
224 }
225
226 private final class EventBusAwareTypeListener implements TypeListener {
227 @Override
228 public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
229
230 final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class);
231
232 if (EventBusAware.class.isAssignableFrom(typeLiteral.getRawType())) {
233 typeEncounter.register(new InjectionListener<I>() {
234 @Override
235 public void afterInjection(Object o) {
236 ((EventBusAware) o).setEventBus(eventBusProvider.get());
237 }
238 });
239 }
240 }
241 }
242 }