View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.guice;
20  
21  import com.google.common.collect.Sets;
22  import com.google.inject.Key;
23  import com.google.inject.PrivateModule;
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.multibindings.Multibinder;
28  import com.google.inject.util.Types;
29  import org.apache.shiro.config.ConfigurationException;
30  import org.apache.shiro.env.Environment;
31  import org.apache.shiro.mgt.DefaultSecurityManager;
32  import org.apache.shiro.mgt.SecurityManager;
33  import org.apache.shiro.realm.Realm;
34  import org.apache.shiro.session.mgt.DefaultSessionManager;
35  import org.apache.shiro.session.mgt.SessionManager;
36  import org.apache.shiro.util.Destroyable;
37  
38  import javax.annotation.PreDestroy;
39  import java.util.Collection;
40  import java.util.Set;
41  import java.util.WeakHashMap;
42  
43  
44  /**
45   * Sets up Shiro lifecycles within Guice, enables the injecting of Shiro objects, and binds a default
46   * {@link org.apache.shiro.mgt.SecurityManager} and {@link org.apache.shiro.session.mgt.SessionManager}.  At least one realm must be added by using
47   * {@link #bindRealm() bindRealm}.
48   */
49  public abstract class ShiroModule extends PrivateModule implements Destroyable {
50  
51      private Set<Destroyable> destroyables = Sets.newSetFromMap(new WeakHashMap<Destroyable, Boolean>());
52  
53      public void configure() {
54          // setup security manager
55          bindSecurityManager(bind(SecurityManager.class));
56          bindSessionManager(bind(SessionManager.class));
57          bindEnvironment(bind(Environment.class));
58          bindListener(BeanTypeListener.MATCHER, new BeanTypeListener());
59          final DestroyableInjectionListener.DestroyableRegistry registry = new DestroyableInjectionListener.DestroyableRegistry() {
60              public void add(Destroyable destroyable) {
61                  ShiroModule.this.add(destroyable);
62              }
63  
64              @PreDestroy
65              public void destroy() throws Exception {
66                  ShiroModule.this.destroy();
67              }
68          };
69          bindListener(LifecycleTypeListener.MATCHER, new LifecycleTypeListener(registry));
70  
71          expose(SecurityManager.class);
72  
73          configureShiro();
74          bind(realmCollectionKey())
75                  .to(realmSetKey());
76  
77          bind(DestroyableInjectionListener.DestroyableRegistry.class).toInstance(registry);
78          BeanTypeListener.ensureBeanTypeMapExists(binder());
79      }
80  
81      @SuppressWarnings({"unchecked"})
82      private Key<Set<Realm>> realmSetKey() {
83          return (Key<Set<Realm>>) Key.get(TypeLiteral.get(Types.setOf(Realm.class)));
84      }
85  
86      @SuppressWarnings({"unchecked"})
87      private Key<Collection<Realm>> realmCollectionKey() {
88          return (Key<Collection<Realm>>) Key.get(Types.newParameterizedType(Collection.class, Realm.class));
89      }
90  
91      /**
92       * Implement this method in order to configure your realms and any other Shiro customization you may need.
93       */
94      protected abstract void configureShiro();
95  
96      /**
97       * This is the preferred manner to bind a realm.  The {@link org.apache.shiro.mgt.SecurityManager} will be injected with any Realm bound
98       * with this method.
99       *
100      * @return a binding builder for a realm
101      */
102     protected final LinkedBindingBuilder<Realm> bindRealm() {
103         Multibinder<Realm> multibinder = Multibinder.newSetBinder(binder(), Realm.class);
104         return multibinder.addBinding();
105     }
106 
107     /**
108      * Binds the security manager.  Override this method in order to provide your own security manager binding.
109      * <p/>
110      * By default, a {@link org.apache.shiro.mgt.DefaultSecurityManager} is bound as an eager singleton.
111      *
112      * @param bind
113      */
114     protected void bindSecurityManager(AnnotatedBindingBuilder<? super SecurityManager> bind) {
115         try {
116             bind.toConstructor(DefaultSecurityManager.class.getConstructor(Collection.class)).asEagerSingleton();
117         } catch (NoSuchMethodException e) {
118             throw new ConfigurationException("This really shouldn't happen.  Either something has changed in Shiro, or there's a bug in " + ShiroModule.class.getSimpleName(), e);
119         }
120     }
121 
122     /**
123      * Binds the session manager.  Override this method in order to provide your own session manager binding.
124      * <p/>
125      * By default, a {@link org.apache.shiro.session.mgt.DefaultSessionManager} is bound as an eager singleton.
126      *
127      * @param bind
128      */
129     protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind) {
130         bind.to(DefaultSessionManager.class).asEagerSingleton();
131     }
132 
133     /**
134      * Binds the environment.  Override this method in order to provide your own environment binding.
135      * <p/>
136      * By default, a {@link GuiceEnvironment} is bound as an eager singleton.
137      *
138      * @param bind
139      */
140     protected void bindEnvironment(AnnotatedBindingBuilder<Environment> bind) {
141         bind.to(GuiceEnvironment.class).asEagerSingleton();
142     }
143 
144     /**
145      * Binds a key to use for injecting setters in shiro classes.
146      * @param typeLiteral the bean property type
147      * @param key the key to use to satisfy the bean property dependency
148      * @param <T>
149      */
150     protected final <T> void bindBeanType(TypeLiteral<T> typeLiteral, Key<? extends T> key) {
151         BeanTypeListener.bindBeanType(binder(), typeLiteral, key);
152     }
153 
154     /**
155      * Destroys all beans created within this module that implement {@link org.apache.shiro.util.Destroyable}.  Should be called when this
156      * module will no longer be used.
157      *
158      * @throws Exception
159      */
160     public final void destroy() throws Exception {
161         for (Destroyable destroyable : destroyables) {
162             destroyable.destroy();
163         }
164     }
165 
166     public void add(Destroyable destroyable) {
167         this.destroyables.add(destroyable);
168     }
169 }