001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.mgt;
020
021import org.apache.shiro.cache.CacheManager;
022import org.apache.shiro.cache.CacheManagerAware;
023import org.apache.shiro.event.EventBus;
024import org.apache.shiro.event.EventBusAware;
025import org.apache.shiro.realm.Realm;
026import org.apache.shiro.util.LifecycleUtils;
027
028import java.util.ArrayList;
029import java.util.Collection;
030
031
032/**
033 * Shiro support of a {@link SecurityManager} class hierarchy based around a collection of
034 * {@link org.apache.shiro.realm.Realm}s.  All actual {@code SecurityManager} method implementations are left to
035 * subclasses.
036 *
037 * @since 0.9
038 */
039public abstract class RealmSecurityManager extends CachingSecurityManager {
040
041    /**
042     * Internal collection of <code>Realm</code>s used for all authentication and authorization operations.
043     */
044    private Collection<Realm> realms;
045
046    /**
047     * Default no-arg constructor.
048     */
049    public RealmSecurityManager() {
050        super();
051    }
052
053    /**
054     * Convenience method for applications using a single realm that merely wraps the realm in a list and then invokes
055     * the {@link #setRealms} method.
056     *
057     * @param realm the realm to set for a single-realm application.
058     * @since 0.2
059     */
060    public void setRealm(Realm realm) {
061        if (realm == null) {
062            throw new IllegalArgumentException("Realm argument cannot be null");
063        }
064        Collection<Realm> realms = new ArrayList<Realm>(1);
065        realms.add(realm);
066        setRealms(realms);
067    }
068
069    /**
070     * Sets the realms managed by this <tt>SecurityManager</tt> instance.
071     *
072     * @param realms the realms managed by this <tt>SecurityManager</tt> instance.
073     * @throws IllegalArgumentException if the realms collection is null or empty.
074     */
075    public void setRealms(Collection<Realm> realms) {
076        if (realms == null) {
077            throw new IllegalArgumentException("Realms collection argument cannot be null.");
078        }
079        if (realms.isEmpty()) {
080            throw new IllegalArgumentException("Realms collection argument cannot be empty.");
081        }
082        this.realms = realms;
083        afterRealmsSet();
084    }
085
086    protected void afterRealmsSet() {
087        applyCacheManagerToRealms();
088        applyEventBusToRealms();
089    }
090
091    /**
092     * Returns the {@link Realm Realm}s managed by this SecurityManager instance.
093     *
094     * @return the {@link Realm Realm}s managed by this SecurityManager instance.
095     */
096    public Collection<Realm> getRealms() {
097        return realms;
098    }
099
100    /**
101     * Sets the internal {@link #getCacheManager CacheManager} on any internal configured
102     * {@link #getRealms Realms} that implement the {@link org.apache.shiro.cache.CacheManagerAware CacheManagerAware} interface.
103     * <p/>
104     * This method is called after setting a cacheManager on this securityManager via the
105     * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) setCacheManager} method to allow it to be propagated
106     * down to all the internal Realms that would need to use it.
107     * <p/>
108     * It is also called after setting one or more realms via the {@link #setRealm setRealm} or
109     * {@link #setRealms setRealms} methods to allow these newly available realms to be given the cache manager
110     * already in use.
111     */
112    protected void applyCacheManagerToRealms() {
113        CacheManager cacheManager = getCacheManager();
114        Collection<Realm> realms = getRealms();
115        if (cacheManager != null && realms != null && !realms.isEmpty()) {
116            for (Realm realm : realms) {
117                if (realm instanceof CacheManagerAware) {
118                    ((CacheManagerAware) realm).setCacheManager(cacheManager);
119                }
120            }
121        }
122    }
123
124    /**
125     * Sets the internal {@link #getEventBus  EventBus} on any internal configured
126     * {@link #getRealms Realms} that implement the {@link EventBusAware} interface.
127     * <p/>
128     * This method is called after setting an eventBus on this securityManager via the
129     * {@link #setEventBus(org.apache.shiro.event.EventBus) setEventBus} method to allow it to be propagated
130     * down to all the internal Realms that would need to use it.
131     * <p/>
132     * It is also called after setting one or more realms via the {@link #setRealm setRealm} or
133     * {@link #setRealms setRealms} methods to allow these newly available realms to be given the EventBus
134     * already in use.
135     *
136     * @since 1.3
137     */
138    protected void applyEventBusToRealms() {
139        EventBus eventBus = getEventBus();
140        Collection<Realm> realms = getRealms();
141        if (eventBus != null && realms != null && !realms.isEmpty()) {
142            for(Realm realm : realms) {
143                if (realm instanceof EventBusAware) {
144                    ((EventBusAware)realm).setEventBus(eventBus);
145                }
146            }
147        }
148    }
149
150    /**
151     * Simply calls {@link #applyCacheManagerToRealms() applyCacheManagerToRealms()} to allow the
152     * newly set {@link org.apache.shiro.cache.CacheManager CacheManager} to be propagated to the internal collection of <code>Realm</code>
153     * that would need to use it.
154     */
155    protected void afterCacheManagerSet() {
156        super.afterCacheManagerSet();
157        applyCacheManagerToRealms();
158    }
159
160    @Override
161    protected void afterEventBusSet() {
162        super.afterEventBusSet();
163        applyEventBusToRealms();
164    }
165
166    public void destroy() {
167        LifecycleUtils.destroy(getRealms());
168        this.realms = null;
169        super.destroy();
170    }
171
172}