Updated stale javadoc. Bugzilla #316.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / common / provider / SharedMemoryShibHandle.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3  * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4  * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5  * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
6  * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
7  * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
8  * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
9  * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10  * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
11  * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12  * products derived from this software without specific prior written permission. For written permission, please contact
13  * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
14  * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
15  * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
16  * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
18  * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
19  * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 package edu.internet2.middleware.shibboleth.common.provider;
27
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.Map.Entry;
34
35 import org.apache.log4j.Logger;
36 import org.opensaml.SAMLConfig;
37 import org.opensaml.SAMLException;
38 import org.opensaml.SAMLNameIdentifier;
39 import org.w3c.dom.Element;
40
41 import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
42 import edu.internet2.middleware.shibboleth.common.IdentityProvider;
43 import edu.internet2.middleware.shibboleth.common.InvalidNameIdentifierException;
44 import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
45 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
46 import edu.internet2.middleware.shibboleth.common.ServiceProvider;
47
48 /**
49  * {@link NameIdentifierMapping}implementation that uses an in-memory cache to store mappings between principal names
50  * and Shibboleth Attribute Query Handles.
51  * 
52  * @author Walter Hoehn
53  */
54 public class SharedMemoryShibHandle extends AQHNameIdentifierMapping implements NameIdentifierMapping {
55
56         protected HandleCache cache = HandleCache.instance();
57         private static Logger log = Logger.getLogger(SharedMemoryShibHandle.class.getName());
58         private static SAMLConfig config = SAMLConfig.instance();
59
60         public SharedMemoryShibHandle(Element config) throws NameIdentifierMappingException {
61
62                 super(config);
63         }
64
65         /*
66          * (non-Javadoc)
67          * 
68          * @see edu.internet2.middleware.shibboleth.common.NameIdentifierMapping#getNameIdentifier(edu.internet2.middleware.shibboleth.common.AuthNPrincipal,
69          *      edu.internet2.middleware.shibboleth.common.ServiceProvider,
70          *      edu.internet2.middleware.shibboleth.common.IdentityProvider)
71          */
72         public SAMLNameIdentifier getNameIdentifier(AuthNPrincipal principal, ServiceProvider sProv, IdentityProvider idProv)
73                         throws NameIdentifierMappingException {
74
75                 if (principal == null) {
76                         log.error("A principal must be supplied for Attribute Query Handle creation.");
77                         throw new IllegalArgumentException("A principal must be supplied for Attribute Query Handle creation.");
78                 }
79                 try {
80                         String handle = new String(config.getDefaultIDProvider().getIdentifier());
81                         log.debug("Assigning handle (" + handle + ") to principal (" + principal.getName() + ").");
82                         synchronized (cache.handleEntries) {
83                                 cache.handleEntries.put(handle, createHandleEntry(principal));
84                         }
85
86                         return new SAMLNameIdentifier(handle, idProv.getProviderId(), getNameIdentifierFormat().toString());
87                 } catch (SAMLException e) {
88                         throw new NameIdentifierMappingException("Unable to generate Attribute Query Handle: " + e);
89                 }
90
91         }
92
93         /*
94          * (non-Javadoc)
95          * 
96          * @see edu.internet2.middleware.shibboleth.common.NameIdentifierMapping#getPrincipal(org.opensaml.SAMLNameIdentifier,
97          *      edu.internet2.middleware.shibboleth.common.ServiceProvider,
98          *      edu.internet2.middleware.shibboleth.common.IdentityProvider)
99          */
100         public AuthNPrincipal getPrincipal(SAMLNameIdentifier nameId, ServiceProvider sProv, IdentityProvider idProv)
101                         throws NameIdentifierMappingException, InvalidNameIdentifierException {
102
103                 verifyQualifier(nameId, idProv);
104
105                 synchronized (cache.handleEntries) {
106                         if (!cache.handleEntries.containsKey(nameId.getName())) {
107                                 log.debug("The Name Mapping Cache does not contain an entry for this Attribute Query Handle.");
108                                 throw new InvalidNameIdentifierException(
109                                                 "The Name Mapping Cache does not contain an entry for this Attribute Query Handle.", errorCodes);
110                         }
111                 }
112
113                 HandleEntry handleEntry;
114                 synchronized (cache.handleEntries) {
115                         handleEntry = (HandleEntry) cache.handleEntries.get(nameId.getName());
116                 }
117
118                 if (handleEntry.isExpired()) {
119                         log.debug("Attribute Query Handle is expired.");
120                         synchronized (cache.handleEntries) {
121                                 cache.handleEntries.remove(nameId.getName());
122                         }
123                         throw new InvalidNameIdentifierException("Attribute Query Handle is expired.", errorCodes);
124                 } else {
125                         log.debug("Attribute Query Handle recognized.");
126                         return handleEntry.principal;
127                 }
128         }
129
130         public void destroy() {
131
132                 cache.destroy();
133         }
134
135 }
136
137 class HandleCache {
138
139         protected Map handleEntries = new HashMap();
140         private static HandleCache instance;
141         protected MemoryRepositoryCleaner cleaner = new MemoryRepositoryCleaner();
142         private static Logger log = Logger.getLogger(HandleCache.class.getName());
143
144         protected HandleCache() {
145
146         }
147
148         public static synchronized HandleCache instance() {
149
150                 if (instance == null) {
151                         instance = new HandleCache();
152                         return instance;
153                 }
154                 return instance;
155         }
156
157         protected void finalize() throws Throwable {
158
159                 super.finalize();
160                 destroy();
161         }
162
163         protected void destroy() {
164
165                 synchronized (cleaner) {
166                         if (cleaner != null) {
167                                 cleaner.shutdown = true;
168                                 cleaner.interrupt();
169                         }
170                 }
171         }
172
173         private class MemoryRepositoryCleaner extends Thread {
174
175                 private boolean shutdown = false;
176                 private Thread master;
177
178                 public MemoryRepositoryCleaner() {
179
180                         super(
181                                         "edu.internet2.middleware.shibboleth.common.provider.SharedMemoryShibHandle.HandleCache.MemoryRepositoryCleaner");
182                         this.master = Thread.currentThread();
183                         setDaemon(true);
184                         if (getPriority() > Thread.MIN_PRIORITY) {
185                                 setPriority(getPriority() - 1);
186                         }
187                         log.debug("Starting memory-based shib handle cache cleanup thread.");
188                         start();
189                 }
190
191                 public void run() {
192
193                         try {
194                                 sleep(60 * 1000); // one minute
195                         } catch (InterruptedException e) {
196                                 log.debug("Memory-based shib handle cache cleanup interrupted.");
197                         }
198                         while (true) {
199                                 try {
200                                         if (!master.isAlive()) {
201                                                 shutdown = true;
202                                                 log.debug("Memory-based shib handle cache cleaner is orphaned.");
203                                         }
204                                         if (shutdown) {
205                                                 log.debug("Stopping Memory-based shib handle cache cleanup thread.");
206                                                 return;
207                                         }
208                                         log.debug("Memory cache handle cache cleanup thread searching for stale entries.");
209                                         Set needsDeleting = new HashSet();
210                                         synchronized (handleEntries) {
211                                                 Iterator iterator = handleEntries.entrySet().iterator();
212                                                 while (iterator.hasNext()) {
213                                                         Entry entry = (Entry) iterator.next();
214                                                         HandleEntry handleEntry = (HandleEntry) entry.getValue();
215                                                         if (handleEntry.isExpired()) {
216                                                                 needsDeleting.add(entry.getKey());
217                                                         }
218                                                 }
219                                                 // release the lock to be friendly
220                                                 Iterator deleteIterator = needsDeleting.iterator();
221                                                 while (deleteIterator.hasNext()) {
222                                                         synchronized (handleEntries) {
223                                                                 log.debug("Expiring an Attribute Query Handle from the memory cache.");
224                                                                 handleEntries.remove(deleteIterator.next());
225                                                         }
226                                                 }
227                                         }
228                                         sleep(60 * 1000); // one minute
229                                 } catch (InterruptedException e) {
230                                         log.debug("Memory-based shib handle cache cleanup interrupted.");
231                                 }
232                         }
233                 }
234         }
235
236 }