use the new session manager interface
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / common / provider / SharedMemoryShibHandle.java
1 /*
2  * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.common.provider;
18
19 import java.security.Principal;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.Map.Entry;
26
27 import org.apache.log4j.Logger;
28 import org.opensaml.SAMLConfig;
29 import org.opensaml.SAMLException;
30 import org.opensaml.SAMLNameIdentifier;
31 import org.w3c.dom.Element;
32
33 import edu.internet2.middleware.shibboleth.common.IdentityProvider;
34 import edu.internet2.middleware.shibboleth.common.InvalidNameIdentifierException;
35 import edu.internet2.middleware.shibboleth.common.LocalPrincipal;
36 import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
37 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
38 import edu.internet2.middleware.shibboleth.common.ServiceProvider;
39
40 /**
41  * {@link NameIdentifierMapping}implementation that uses an in-memory cache to store mappings between principal names
42  * and Shibboleth Attribute Query Handles.
43  * 
44  * @author Walter Hoehn
45  */
46 public class SharedMemoryShibHandle extends AQHNameIdentifierMapping implements NameIdentifierMapping {
47
48         protected HandleCache cache = HandleCache.instance();
49         private static Logger log = Logger.getLogger(SharedMemoryShibHandle.class.getName());
50         private static SAMLConfig config = SAMLConfig.instance();
51
52         public SharedMemoryShibHandle(Element config) throws NameIdentifierMappingException {
53
54                 super(config);
55         }
56
57         /*
58          * (non-Javadoc)
59          * 
60          * @see edu.internet2.middleware.shibboleth.common.NameIdentifierMapping#getNameIdentifier(edu.internet2.middleware.shibboleth.common.LocalPrincipal,
61          *      edu.internet2.middleware.shibboleth.common.ServiceProvider,
62          *      edu.internet2.middleware.shibboleth.common.IdentityProvider)
63          */
64         public SAMLNameIdentifier getNameIdentifier(LocalPrincipal principal, ServiceProvider sProv, IdentityProvider idProv)
65                         throws NameIdentifierMappingException {
66
67                 if (principal == null) {
68                         log.error("A principal must be supplied for Attribute Query Handle creation.");
69                         throw new IllegalArgumentException("A principal must be supplied for Attribute Query Handle creation.");
70                 }
71                 try {
72                         String handle = new String(config.getDefaultIDProvider().getIdentifier());
73                         log.debug("Assigning handle (" + handle + ") to principal (" + principal.getName() + ").");
74                         synchronized (cache.handleEntries) {
75                                 cache.handleEntries.put(handle, createHandleEntry(principal));
76                         }
77
78                         SAMLNameIdentifier nameid = SAMLNameIdentifier.getInstance(getNameIdentifierFormat().toString());
79                         nameid.setName(handle);
80                         nameid.setNameQualifier(idProv.getProviderId());
81                         return nameid;
82
83                 } catch (SAMLException e) {
84                         throw new NameIdentifierMappingException("Unable to generate Attribute Query Handle: " + e);
85                 }
86
87         }
88
89         /*
90          * (non-Javadoc)
91          * 
92          * @see edu.internet2.middleware.shibboleth.common.NameIdentifierMapping#getPrincipal(org.opensaml.SAMLNameIdentifier,
93          *      edu.internet2.middleware.shibboleth.common.ServiceProvider,
94          *      edu.internet2.middleware.shibboleth.common.IdentityProvider)
95          */
96         public Principal getPrincipal(SAMLNameIdentifier nameId, ServiceProvider sProv, IdentityProvider idProv)
97                         throws NameIdentifierMappingException, InvalidNameIdentifierException {
98
99                 verifyQualifier(nameId, idProv);
100
101                 synchronized (cache.handleEntries) {
102                         if (!cache.handleEntries.containsKey(nameId.getName())) {
103                                 log.debug("The Name Mapping Cache does not contain an entry for this Attribute Query Handle.");
104                                 throw new InvalidNameIdentifierException(
105                                                 "The Name Mapping Cache does not contain an entry for this Attribute Query Handle.", errorCodes);
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.", errorCodes);
120                 } else {
121                         log.debug("Attribute Query Handle recognized.");
122                         return handleEntry.principal;
123                 }
124         }
125
126         public void destroy() {
127
128                 cache.destroy();
129         }
130
131 }
132
133 class HandleCache {
134
135         protected Map handleEntries = new HashMap();
136         private static HandleCache instance;
137         protected MemoryRepositoryCleaner cleaner = new MemoryRepositoryCleaner();
138         private static Logger log = Logger.getLogger(HandleCache.class.getName());
139
140         protected HandleCache() {
141
142         }
143
144         public static synchronized HandleCache instance() {
145
146                 if (instance == null) {
147                         instance = new HandleCache();
148                         return instance;
149                 }
150                 return instance;
151         }
152
153         protected void finalize() throws Throwable {
154
155                 super.finalize();
156                 destroy();
157         }
158
159         protected void destroy() {
160
161                 synchronized (cleaner) {
162                         if (cleaner != null) {
163                                 cleaner.shutdown = true;
164                                 cleaner.interrupt();
165                         }
166                 }
167         }
168
169         private class MemoryRepositoryCleaner extends Thread {
170
171                 private boolean shutdown = false;
172                 private Thread master;
173
174                 public MemoryRepositoryCleaner() {
175
176                         super(
177                                         "edu.internet2.middleware.shibboleth.common.provider.SharedMemoryShibHandle.HandleCache.MemoryRepositoryCleaner");
178                         this.master = Thread.currentThread();
179                         setDaemon(true);
180                         if (getPriority() > Thread.MIN_PRIORITY) {
181                                 setPriority(getPriority() - 1);
182                         }
183                         log.debug("Starting memory-based shib handle cache cleanup thread.");
184                         start();
185                 }
186
187                 public void run() {
188
189                         try {
190                                 sleep(60 * 1000); // one minute
191                         } catch (InterruptedException e) {
192                                 log.debug("Memory-based shib handle cache cleanup interrupted.");
193                         }
194                         while (true) {
195                                 try {
196                                         if (!master.isAlive()) {
197                                                 shutdown = true;
198                                                 log.debug("Memory-based shib handle cache cleaner is orphaned.");
199                                         }
200                                         if (shutdown) {
201                                                 log.debug("Stopping Memory-based shib handle cache cleanup thread.");
202                                                 return;
203                                         }
204                                         log.debug("Memory cache handle cache cleanup thread searching for stale entries.");
205                                         Set needsDeleting = new HashSet();
206                                         synchronized (handleEntries) {
207                                                 Iterator iterator = handleEntries.entrySet().iterator();
208                                                 while (iterator.hasNext()) {
209                                                         Entry entry = (Entry) iterator.next();
210                                                         HandleEntry handleEntry = (HandleEntry) entry.getValue();
211                                                         if (handleEntry.isExpired()) {
212                                                                 needsDeleting.add(entry.getKey());
213                                                         }
214                                                 }
215                                                 // release the lock to be friendly
216                                                 Iterator deleteIterator = needsDeleting.iterator();
217                                                 while (deleteIterator.hasNext()) {
218                                                         synchronized (handleEntries) {
219                                                                 log.debug("Expiring an Attribute Query Handle from the memory cache.");
220                                                                 handleEntries.remove(deleteIterator.next());
221                                                         }
222                                                 }
223                                         }
224                                         sleep(60 * 1000); // one minute
225                                 } catch (InterruptedException e) {
226                                         log.debug("Memory-based shib handle cache cleanup interrupted.");
227                                 }
228                         }
229                 }
230         }
231
232 }