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     */
019    package org.apache.shiro.mgt;
020    
021    import org.apache.shiro.cache.CacheManager;
022    import org.apache.shiro.cache.CacheManagerAware;
023    import org.apache.shiro.realm.Realm;
024    import org.apache.shiro.util.LifecycleUtils;
025    
026    import java.util.ArrayList;
027    import java.util.Collection;
028    
029    
030    /**
031     * Shiro support of a {@link SecurityManager} class hierarchy based around a collection of
032     * {@link org.apache.shiro.realm.Realm}s.  All actual {@code SecurityManager} method implementations are left to
033     * subclasses.
034     *
035     * @author Les Hazlewood
036     * @since 0.9
037     */
038    public abstract class RealmSecurityManager extends CachingSecurityManager {
039    
040        /**
041         * Internal collection of <code>Realm</code>s used for all authentication and authorization operations.
042         */
043        private Collection<Realm> realms;
044    
045        /**
046         * Default no-arg constructor.
047         */
048        public RealmSecurityManager() {
049            super();
050        }
051    
052        /**
053         * Convenience method for applications using a single realm that merely wraps the realm in a list and then invokes
054         * the {@link #setRealms} method.
055         *
056         * @param realm the realm to set for a single-realm application.
057         * @since 0.2
058         */
059        public void setRealm(Realm realm) {
060            if (realm == null) {
061                throw new IllegalArgumentException("Realm argument cannot be null");
062            }
063            Collection<Realm> realms = new ArrayList<Realm>(1);
064            realms.add(realm);
065            setRealms(realms);
066        }
067    
068        /**
069         * Sets the realms managed by this <tt>SecurityManager</tt> instance.
070         *
071         * @param realms the realms managed by this <tt>SecurityManager</tt> instance.
072         * @throws IllegalArgumentException if the realms collection is null or empty.
073         */
074        public void setRealms(Collection<Realm> realms) {
075            if (realms == null) {
076                throw new IllegalArgumentException("Realms collection argument cannot be null.");
077            }
078            if (realms.isEmpty()) {
079                throw new IllegalArgumentException("Realms collection argument cannot be empty.");
080            }
081            this.realms = realms;
082            afterRealmsSet();
083        }
084    
085        protected void afterRealmsSet() {
086            applyCacheManagerToRealms();
087        }
088    
089        /**
090         * Returns the {@link Realm Realm}s managed by this SecurityManager instance.
091         *
092         * @return the {@link Realm Realm}s managed by this SecurityManager instance.
093         */
094        public Collection<Realm> getRealms() {
095            return realms;
096        }
097    
098        /**
099         * Sets the internal {@link #getCacheManager CacheManager} on any internal configured
100         * {@link #getRealms Realms} that implement the {@link org.apache.shiro.cache.CacheManagerAware CacheManagerAware} interface.
101         * <p/>
102         * This method is called after setting a cacheManager on this securityManager via the
103         * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) setCacheManager} method to allow it to be propagated
104         * down to all the internal Realms that would need to use it.
105         * <p/>
106         * It is also called after setting one or more realms via the {@link #setRealm setRealm} or
107         * {@link #setRealms setRealms} methods to allow these newly available realms to be given the cache manager
108         * already in use.
109         */
110        protected void applyCacheManagerToRealms() {
111            CacheManager cacheManager = getCacheManager();
112            Collection<Realm> realms = getRealms();
113            if (cacheManager != null && realms != null && !realms.isEmpty()) {
114                for (Realm realm : realms) {
115                    if (realm instanceof CacheManagerAware) {
116                        ((CacheManagerAware) realm).setCacheManager(cacheManager);
117                    }
118                }
119            }
120        }
121    
122        /**
123         * Simply calls {@link #applyCacheManagerToRealms() applyCacheManagerToRealms()} to allow the
124         * newly set {@link org.apache.shiro.cache.CacheManager CacheManager} to be propagated to the internal collection of <code>Realm</code>
125         * that would need to use it.
126         */
127        protected void afterCacheManagerSet() {
128            applyCacheManagerToRealms();
129        }
130    
131        public void destroy() {
132            LifecycleUtils.destroy(getRealms());
133            this.realms = null;
134            super.destroy();
135        }
136    
137    }