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