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