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