Don't startup an ARP Cache if the TTL is 0.
[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) throws ArpRepositoryException {
83                 try {
84                         if (properties
85                                 .getProperty(
86                                         "edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL",
87                                         null)
88                                 != null) {
89                                 if (Long
90                                         .parseLong(
91                                                 properties.getProperty(
92                                                         "edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL",
93                                                         null))
94                                         != 0) {
95                                         arpCache = ArpCache.instance();
96                                         arpCache.setCacheLength(
97                                                 Long.parseLong(
98                                                         properties.getProperty(
99                                                                 "edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL",
100                                                                 null)));
101                                 }
102                         }
103                 } catch (NumberFormatException nfe) {
104                         log.error(
105                                 "Value for (edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL) must be a long integer.");
106                         throw new ArpRepositoryException("Value for (edu.internet2.middleware.shibboleth.aa.arp.BaseArpRepository.ArpTTL) must be a long integer.");
107                 }
108         }
109
110         /**
111          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getAllPolicies(Principal)
112          */
113
114         public Arp[] getAllPolicies(Principal principal) throws ArpRepositoryException {
115                 log.debug(
116                         "Received a query for all policies applicable to principal: ("
117                                 + principal.getName()
118                                 + ").");
119                 Set allPolicies = new HashSet();
120                 Arp sitePolicy = getSitePolicy();
121                 if (sitePolicy != null) {
122                         log.debug("Returning site policy.");
123                         allPolicies.add(sitePolicy);
124                 }
125
126                 Arp userPolicy = getUserPolicy(principal);
127                 if (userPolicy != null) {
128                         allPolicies.add(userPolicy);
129                         log.debug("Returning user policy.");
130                 }
131                 if (allPolicies.isEmpty()) {
132                         log.debug("No policies found.");
133                 }
134                 return (Arp[]) allPolicies.toArray(new Arp[0]);
135         }
136
137         /**
138          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getSitePolicy()
139          */
140         public Arp getSitePolicy() throws ArpRepositoryException {
141
142                 try {
143                         if (arpCache != null) {
144                                 Arp cachedArp = arpCache.retrieveSiteArpFromCache();
145                                 if (cachedArp != null) {
146                                         log.debug("Using cached site ARP.");
147                                         return cachedArp;
148                                 }
149                         }
150
151                         Element xml = retrieveSiteArpXml();
152                         if (xml == null) {
153                                 return null;
154                         }
155
156                         Arp siteArp = new Arp();
157                         siteArp.marshall(xml);
158                         if (arpCache != null) {
159                                 arpCache.cache(siteArp);
160                         }
161                         return siteArp;
162                 } catch (ArpMarshallingException ame) {
163                         log.error("An error occurred while marshalling an ARP: " + ame);
164                         throw new ArpRepositoryException("An error occurred while marshalling an ARP.");
165                 } catch (IOException ioe) {
166                         log.error("An error occurred while loading an ARP: " + ioe);
167                         throw new ArpRepositoryException("An error occurred while loading an ARP.");
168                 } catch (SAXException se) {
169                         log.error("An error occurred while parsing an ARP: " + se);
170                         throw new ArpRepositoryException("An error occurred while parsing an ARP.");
171                 }
172         }
173
174         /**
175          * Inheritors must return the site Arp as an xml element.
176          * @return Element
177          */
178         protected abstract Element retrieveSiteArpXml() throws IOException, SAXException;
179
180         /**
181          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getUserPolicy(Principal)
182          */
183         public Arp getUserPolicy(Principal principal) throws ArpRepositoryException {
184
185                 if (arpCache != null) {
186                         Arp cachedArp = arpCache.retrieveUserArpFromCache(principal);
187                         if (cachedArp != null) {
188                                 log.debug("Using cached user ARP.");
189                                 return cachedArp;
190                         }
191                 }
192
193                 try {
194                         Element xml = retrieveUserArpXml(principal);
195                         if (xml == null) {
196                                 return null;
197                         }
198
199                         Arp userArp = new Arp();
200                         userArp.setPrincipal(principal);
201
202                         userArp.marshall(xml);
203                         if (arpCache != null) {
204                                 arpCache.cache(userArp);
205                         }
206                         return userArp;
207                 } catch (ArpMarshallingException ame) {
208                         log.error("An error occurred while marshalling an ARP: " + ame);
209                         throw new ArpRepositoryException("An error occurred while marshalling an ARP.");
210                 } catch (IOException ioe) {
211                         log.error("An error occurred while loading an ARP: " + ioe);
212                         throw new ArpRepositoryException("An error occurred while loading an ARP.");
213                 } catch (SAXException se) {
214                         log.error("An error occurred while parsing an ARP: " + se);
215                         throw new ArpRepositoryException("An error occurred while parsing an ARP.");
216                 }
217         }
218
219         /**
220          * Inheritors must return the user Arp as an xml element.
221          * @return Element
222          */
223         protected abstract Element retrieveUserArpXml(Principal principal)
224                 throws IOException, SAXException;
225
226 }
227
228 class ArpCache {
229
230         private static ArpCache instance = null;
231         private long cacheLength;
232         private Map cache = new HashMap();
233         private static Logger log = Logger.getLogger(ArpCache.class.getName());
234         private ArpCacheCleaner cleaner = new ArpCacheCleaner();
235
236         protected ArpCache() {
237         }
238
239         static synchronized ArpCache instance() {
240                 if (instance == null) {
241                         return new ArpCache();
242                 }
243                 return instance;
244         }
245
246         void setCacheLength(long cacheLength) {
247                 this.cacheLength = cacheLength;
248         }
249
250         void cache(Arp arp) {
251                 if (arp.isSitePolicy() == false) {
252                         synchronized (cache) {
253                                 cache.put(arp.getPrincipal(), new CachedArp(arp, System.currentTimeMillis()));
254                         }
255                 } else {
256                         synchronized (cache) {
257                                 cache.put(new SiteCachePrincipal(), new CachedArp(arp, System.currentTimeMillis()));
258                         }
259                 }
260         }
261
262         Arp retrieveUserArpFromCache(Principal principal) {
263                 return retrieveArpFromCache(principal);
264         }
265
266         Arp retrieveSiteArpFromCache() {
267                 return retrieveArpFromCache(new SiteCachePrincipal());
268         }
269
270         private Arp retrieveArpFromCache(Principal principal) {
271
272                 CachedArp cachedArp;
273                 synchronized (cache) {
274                         cachedArp = (CachedArp) cache.get(principal);
275                 }
276
277                 if (cachedArp == null) {
278                         return null;
279                 }
280
281                 if ((System.currentTimeMillis() - cachedArp.creationTimeMillis) < cacheLength) {
282                         return cachedArp.arp;
283                 }
284
285                 synchronized (cache) {
286                         cache.remove(principal);
287                 }
288                 return null;
289         }
290
291         /**
292          * @see java.lang.Object#finalize()
293          */
294         protected void finalize() throws Throwable {
295                 super.finalize();
296                 synchronized (cleaner) {
297                         cleaner.shutdown = true;
298                         cleaner.interrupt();
299                 }
300         }
301
302         private class CachedArp {
303                 Arp arp;
304                 long creationTimeMillis;
305
306                 CachedArp(Arp arp, long creationTimeMillis) {
307                         this.arp = arp;
308                         this.creationTimeMillis = creationTimeMillis;
309                 }
310         }
311
312         private class SiteCachePrincipal implements Principal {
313
314                 public String getName() {
315                         return "ARP admin";
316                 }
317
318                 /**
319                  * @see java.lang.Object#equals(Object)
320                  */
321                 public boolean equals(Object object) {
322                         if (object instanceof SiteCachePrincipal) {
323                                 return true;
324                         }
325                         return false;
326                 }
327
328                 /**
329                  * @see java.lang.Object#hashCode()
330                  */
331                 public int hashCode() {
332                         return "edu.internet2.middleware.shibboleth.aa.arp.provider.BaseArpRepository.SiteCachePrincipal"
333                                 .hashCode();
334                 }
335         }
336
337         private class ArpCacheCleaner extends Thread {
338
339                 private boolean shutdown = false;
340
341                 public ArpCacheCleaner() {
342                         super();
343                         log.debug("Starting ArpCache Cleanup Thread.");
344                         start();
345                 }
346
347                 public void run() {
348                         try {
349                                 sleep(5 * 60 * 1000);
350                         } catch (InterruptedException e) {
351                                 log.debug("ArpCache Cleanup interrupted.");
352                         }
353                         while (true) {
354                                 try {
355                                         if (shutdown) {
356                                                 log.debug("Stopping ArpCache Cleanup Thread.");
357                                                 return;
358                                         }
359                                         Set needsDeleting = new HashSet();
360                                         synchronized (cache) {
361                                                 Iterator iterator = cache.values().iterator();
362                                                 while (iterator.hasNext()) {
363                                                         CachedArp cachedArp = (CachedArp) iterator.next();
364                                                         if ((cachedArp.creationTimeMillis - System.currentTimeMillis())
365                                                                 > cacheLength) {
366                                                                 needsDeleting.add(cachedArp);
367                                                         }
368                                                 }
369                                                 //release the lock to be friendly
370                                                 Iterator deleteIterator = needsDeleting.iterator();
371                                                 while (deleteIterator.hasNext()) {
372                                                         synchronized (cache) {
373                                                                 log.debug("Expiring an ARP from the Cache.");
374                                                                 cache.remove(
375                                                                         ((CachedArp) deleteIterator.next()).arp.getPrincipal());
376                                                         }
377                                                 }
378                                         }
379                                         sleep(5 * 60 * 1000);
380                                 } catch (InterruptedException e) {
381                                         log.debug("ArpCache Cleanup interrupted.");
382                                 }
383                         }
384                 }
385         }
386
387 }