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.env;
020    
021    import org.apache.shiro.mgt.SecurityManager;
022    import org.apache.shiro.util.Destroyable;
023    import org.apache.shiro.util.LifecycleUtils;
024    
025    import java.util.Map;
026    import java.util.concurrent.ConcurrentHashMap;
027    
028    /**
029     * Simple/default {@code Environment} implementation that stores Shiro objects as key-value pairs in a
030     * {@link java.util.Map Map} instance.  The key is the object name, the value is the object itself.
031     *
032     * @since 1.2
033     */
034    public class DefaultEnvironment implements NamedObjectEnvironment, Destroyable {
035    
036        /**
037         * The default name under which the application's {@code SecurityManager} instance may be acquired, equal to
038         * {@code securityManager}.
039         */
040        public static final String DEFAULT_SECURITY_MANAGER_KEY = "securityManager";
041    
042        protected final Map<String, Object> objects;
043        private String securityManagerName;
044    
045        /**
046         * Creates a new instance with a thread-safe {@link ConcurrentHashMap} backing map.
047         */
048        public DefaultEnvironment() {
049            this(new ConcurrentHashMap<String, Object>());
050        }
051    
052        /**
053         * Creates a new instance with the specified backing map.
054         *
055         * @param seed backing map to use to maintain Shiro objects.
056         */
057        @SuppressWarnings({"unchecked"})
058        public DefaultEnvironment(Map<String, ?> seed) {
059            this.securityManagerName = DEFAULT_SECURITY_MANAGER_KEY;
060            if (seed == null) {
061                throw new IllegalArgumentException("Backing map cannot be null.");
062            }
063            this.objects = (Map<String, Object>) seed;
064        }
065    
066        /**
067         * Returns the application's {@code SecurityManager} instance accessible in the backing map using the
068         * {@link #getSecurityManagerName() securityManagerName} property as the lookup key.
069         * <p/>
070         * This implementation guarantees that a non-null instance is always returned, as this is expected for
071         * Environment API end-users.  If subclasses have the need to perform the map lookup without this guarantee
072         * (for example, during initialization when the instance may not have been added to the map yet), the
073         * {@link #lookupSecurityManager()} method is provided as an alternative.
074         *
075         * @return the application's {@code SecurityManager} instance accessible in the backing map using the
076         *         {@link #getSecurityManagerName() securityManagerName} property as the lookup key.
077         */
078        public SecurityManager getSecurityManager() throws IllegalStateException {
079            SecurityManager securityManager = lookupSecurityManager();
080            if (securityManager == null) {
081                throw new IllegalStateException("No SecurityManager found in Environment.  This is an invalid " +
082                        "environment state.");
083            }
084            return securityManager;
085        }
086    
087        public void setSecurityManager(SecurityManager securityManager) {
088            if (securityManager == null) {
089                throw new IllegalArgumentException("Null SecurityManager instances are not allowed.");
090            }
091            String name = getSecurityManagerName();
092            setObject(name, securityManager);
093        }
094    
095        /**
096         * Looks up the {@code SecurityManager} instance in the backing map without performing any non-null guarantees.
097         *
098         * @return the {@code SecurityManager} in the backing map, or {@code null} if it has not yet been populated.
099         */
100        protected SecurityManager lookupSecurityManager() {
101            String name = getSecurityManagerName();
102            return getObject(name, SecurityManager.class);
103        }
104    
105        /**
106         * Returns the name of the {@link SecurityManager} instance in the backing map.  Used as a key to lookup the
107         * instance.  Unless set otherwise, the default is {@code securityManager}.
108         *
109         * @return the name of the {@link SecurityManager} instance in the backing map.  Used as a key to lookup the
110         *         instance.
111         */
112        public String getSecurityManagerName() {
113            return securityManagerName;
114        }
115    
116        /**
117         * Sets the name of the {@link SecurityManager} instance in the backing map.  Used as a key to lookup the
118         * instance.  Unless set otherwise, the default is {@code securityManager}.
119         *
120         * @param securityManagerName the name of the {@link SecurityManager} instance in the backing map.  Used as a key
121         *                            to lookup the instance. 
122         */
123        public void setSecurityManagerName(String securityManagerName) {
124            this.securityManagerName = securityManagerName;
125        }
126    
127        /**
128         * Returns the live (modifiable) internal objects collection.
129         *
130         * @return the live (modifiable) internal objects collection.
131         */
132        public Map<String,Object> getObjects() {
133            return this.objects;
134        }
135    
136        @SuppressWarnings({"unchecked"})
137        public <T> T getObject(String name, Class<T> requiredType) throws RequiredTypeException {
138            if (name == null) {
139                throw new NullPointerException("name parameter cannot be null.");
140            }
141            if (requiredType == null) {
142                throw new NullPointerException("requiredType parameter cannot be null.");
143            }
144            Object o = this.objects.get(name);
145            if (o == null) {
146                return null;
147            }
148            if (!requiredType.isInstance(o)) {
149                String msg = "Object named '" + name + "' is not of required type [" + requiredType.getName() + "].";
150                throw new RequiredTypeException(msg);
151            }
152            return (T)o;
153        }
154    
155        public void setObject(String name, Object instance) {
156            if (name == null) {
157                throw new NullPointerException("name parameter cannot be null.");
158            }
159            if (instance == null) {
160                this.objects.remove(name);
161            } else {
162                this.objects.put(name, instance);
163            }
164        }
165    
166    
167        public void destroy() throws Exception {
168            LifecycleUtils.destroy(this.objects.values());
169        }
170    }