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