d954efe7b9a2f6928cb88e7aea6b0192005c1b13
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / arp / provider / BaseArpRepository.java
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 package edu.internet2.middleware.shibboleth.aa.arp.provider;
51
52 import java.io.IOException;
53 import java.security.Principal;
54 import java.util.HashMap;
55 import java.util.HashSet;
56 import java.util.Iterator;
57 import java.util.Map;
58 import java.util.Properties;
59 import java.util.Set;
60
61 import org.apache.log4j.Logger;
62 import org.w3c.dom.Element;
63 import org.xml.sax.SAXException;
64
65 import edu.internet2.middleware.shibboleth.aa.arp.Arp;
66 import edu.internet2.middleware.shibboleth.aa.arp.ArpMarshallingException;
67 import edu.internet2.middleware.shibboleth.aa.arp.ArpRepository;
68 import edu.internet2.middleware.shibboleth.aa.arp.ArpRepositoryException;
69
70 /**
71  * Provides marshalling/unmarshalling functionality common among 
72  * <code>ArpRepository</code> implementations.
73  * 
74  * @author Walter Hoehn (wassa@columbia.edu)
75  */
76
77 public abstract class BaseArpRepository implements ArpRepository {
78
79         private static Logger log = Logger.getLogger(BaseArpRepository.class.getName());
80         private ArpCache arpCache;
81
82         BaseArpRepository(Properties properties) {
83                 if (properties
84                         .getProperty(
85                                 "edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL",
86                                 null)
87                         != null) {
88                         arpCache = ArpCache.instance();
89                         arpCache.setCacheLength(
90                                 Long.parseLong(
91                                         properties.getProperty(
92                                                 "edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL",
93                                                 null)));
94                 }
95         }
96
97         /**
98          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getAllPolicies(Principal)
99          */
100
101         public Arp[] getAllPolicies(Principal principal) throws ArpRepositoryException {
102                 log.debug(
103                         "Received a query for all policies applicable to principal: ("
104                                 + principal.getName()
105                                 + ").");
106                 Set allPolicies = new HashSet();
107                 Arp sitePolicy = getSitePolicy();
108                 if (sitePolicy != null) {
109                         log.debug("Returning site policy.");
110                         allPolicies.add(sitePolicy);
111                 }
112
113                 Arp userPolicy = getUserPolicy(principal);
114                 if (userPolicy != null) {
115                         allPolicies.add(userPolicy);
116                         log.debug("Returning user policy.");
117                 }
118                 if (allPolicies.isEmpty()) {
119                         log.debug("No policies found.");
120                 }
121                 return (Arp[]) allPolicies.toArray(new Arp[0]);
122         }
123
124         /**
125          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getSitePolicy()
126          */
127         public Arp getSitePolicy() throws ArpRepositoryException {
128
129                 try {
130                         if (arpCache != null) {
131                                 Arp cachedArp = arpCache.retrieveSiteArpFromCache();
132                                 if (cachedArp != null) {
133                                         log.debug("Using cached site ARP.");
134                                         return cachedArp;
135                                 }
136                         }
137
138                         Element xml = retrieveSiteArpXml();
139                         if (xml == null) {
140                                 return null;
141                         }
142
143                         Arp siteArp = new Arp();
144                         siteArp.marshall(xml);
145                         if (arpCache != null) {
146                                 arpCache.cache(siteArp);
147                         }
148                         return siteArp;
149                 } catch (ArpMarshallingException ame) {
150                         log.error("An error occurred while marshalling an ARP: " + ame);
151                         throw new ArpRepositoryException("An error occurred while marshalling an ARP.");
152                 } catch (IOException ioe) {
153                         log.error("An error occurred while loading an ARP: " + ioe);
154                         throw new ArpRepositoryException("An error occurred while loading an ARP.");
155                 } catch (SAXException se) {
156                         log.error("An error occurred while parsing an ARP: " + se);
157                         throw new ArpRepositoryException("An error occurred while parsing an ARP.");
158                 }
159         }
160
161         /**
162          * Inheritors must return the site Arp as an xml element.
163          * @return Element
164          */
165         protected abstract Element retrieveSiteArpXml() throws IOException, SAXException;
166
167         /**
168          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getUserPolicy(Principal)
169          */
170         public Arp getUserPolicy(Principal principal) throws ArpRepositoryException {
171
172                 if (arpCache != null) {
173                         Arp cachedArp = arpCache.retrieveUserArpFromCache(principal);
174                         if (cachedArp != null) {
175                                 log.debug("Using cached user ARP.");
176                                 return cachedArp;
177                         }
178                 }
179
180                 try {
181                         Element xml = retrieveUserArpXml(principal);
182                         if (xml == null) {
183                                 return null;
184                         }
185
186                         Arp userArp = new Arp();
187                         userArp.setPrincipal(principal);
188
189                         userArp.marshall(xml);
190                         if (arpCache != null) {
191                                 arpCache.cache(userArp);
192                         }
193                         return userArp;
194                 } catch (ArpMarshallingException ame) {
195                         log.error("An error occurred while marshalling an ARP: " + ame);
196                         throw new ArpRepositoryException("An error occurred while marshalling an ARP.");
197                 } catch (IOException ioe) {
198                         log.error("An error occurred while loading an ARP: " + ioe);
199                         throw new ArpRepositoryException("An error occurred while loading an ARP.");
200                 } catch (SAXException se) {
201                         log.error("An error occurred while parsing an ARP: " + se);
202                         throw new ArpRepositoryException("An error occurred while parsing an ARP.");
203                 }
204         }
205
206         /**
207          * Inheritors must return the user Arp as an xml element.
208          * @return Element
209          */
210         protected abstract Element retrieveUserArpXml(Principal principal)
211                 throws IOException, SAXException;
212
213 }
214
215 class ArpCache {
216
217         private static ArpCache instance = null;
218         private long cacheLength;
219         private Map cache = new HashMap();
220         private static Logger log = Logger.getLogger(ArpCache.class.getName());
221         private ArpCacheCleaner cleaner = new ArpCacheCleaner();
222
223         protected ArpCache() {
224         }
225
226         static synchronized ArpCache instance() {
227                 if (instance == null) {
228                         return new ArpCache();
229                 }
230                 return instance;
231         }
232
233         void setCacheLength(long cacheLength) {
234                 this.cacheLength = cacheLength;
235         }
236
237         void cache(Arp arp) {
238                 if (arp.isSitePolicy() == false) {
239                         synchronized (cache) {
240                                 cache.put(arp.getPrincipal(), new CachedArp(arp, System.currentTimeMillis()));
241                         }
242                 } else {
243                         synchronized (cache) {
244                                 cache.put(new SiteCachePrincipal(), new CachedArp(arp, System.currentTimeMillis()));
245                         }
246                 }
247         }
248
249         Arp retrieveUserArpFromCache(Principal principal) {
250                 return retrieveArpFromCache(principal);
251         }
252
253         Arp retrieveSiteArpFromCache() {
254                 return retrieveArpFromCache(new SiteCachePrincipal());
255         }
256
257         private Arp retrieveArpFromCache(Principal principal) {
258
259                 CachedArp cachedArp;
260                 synchronized (cache) {
261                         cachedArp = (CachedArp) cache.get(principal);
262                 }
263
264                 if (cachedArp == null) {
265                         return null;
266                 }
267
268                 if ((System.currentTimeMillis() - cachedArp.creationTimeMillis) < cacheLength) {
269                         return cachedArp.arp;
270                 }
271
272                 synchronized (cache) {
273                         cache.remove(principal);
274                 }
275                 return null;
276         }
277
278         /**
279          * @see java.lang.Object#finalize()
280          */
281         protected void finalize() throws Throwable {
282                 super.finalize();
283                 synchronized (cleaner) {
284                         cleaner.shutdown = true;
285                         cleaner.interrupt();
286                 }
287         }
288
289         private class CachedArp {
290                 Arp arp;
291                 long creationTimeMillis;
292
293                 CachedArp(Arp arp, long creationTimeMillis) {
294                         this.arp = arp;
295                         this.creationTimeMillis = creationTimeMillis;
296                 }
297         }
298
299         private class SiteCachePrincipal implements Principal {
300
301                 public String getName() {
302                         return "ARP admin";
303                 }
304
305                 /**
306                  * @see java.lang.Object#equals(Object)
307                  */
308                 public boolean equals(Object object) {
309                         if (object instanceof SiteCachePrincipal) {
310                                 return true;
311                         }
312                         return false;
313                 }
314
315                 /**
316                  * @see java.lang.Object#hashCode()
317                  */
318                 public int hashCode() {
319                         return "edu.internet2.middleware.shibboleth.aa.arp.provider.BaseArpRepository.SiteCachePrincipal"
320                                 .hashCode();
321                 }
322         }
323
324         private class ArpCacheCleaner extends Thread {
325
326                 private boolean shutdown = false;
327
328                 public ArpCacheCleaner() {
329                         super();
330                         log.debug("Starting ArpCache Cleanup Thread.");
331                         start();
332                 }
333
334                 public void run() {
335                         try {
336                                 sleep(5 * 60 * 1000);
337                         } catch (InterruptedException e) {
338                                 log.debug("ArpCache Cleanup interrupted.");
339                         }
340                         while (true) {
341                                 try {
342                                         if (shutdown) {
343                                                 log.debug("Stopping ArpCache Cleanup Thread.");
344                                                 return;
345                                         }
346                                         Set needsDeleting = new HashSet();
347                                         synchronized (cache) {
348                                                 Iterator iterator = cache.values().iterator();
349                                                 while (iterator.hasNext()) {
350                                                         CachedArp cachedArp = (CachedArp) iterator.next();
351                                                         if ((cachedArp.creationTimeMillis - System.currentTimeMillis())
352                                                                 > cacheLength) {
353                                                                 needsDeleting.add(cachedArp);
354                                                         }
355                                                 }
356                                                 //release the lock to be friendly
357                                                 Iterator deleteIterator = needsDeleting.iterator();
358                                                 while (deleteIterator.hasNext()) {
359                                                         synchronized (cache) {
360                                                                 log.debug("Expiring an ARP from the Cache.");
361                                                                 cache.remove(
362                                                                         ((CachedArp) deleteIterator.next()).arp.getPrincipal());
363                                                         }
364                                                 }
365                                         }
366                                         sleep(5 * 60 * 1000);
367                                 } catch (InterruptedException e) {
368                                         log.debug("ArpCache Cleanup interrupted.");
369                                 }
370                         }
371                 }
372         }
373
374 }