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.env; 020 021import org.apache.shiro.mgt.SecurityManager; 022import org.apache.shiro.util.Destroyable; 023import org.apache.shiro.util.LifecycleUtils; 024 025import java.util.Map; 026import 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 */ 034public 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}