73d05c4225b092e1b62076ea76ee01d12825dbf1
[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                         instance = new ArpCache();
242                         return instance;
243                 }
244                 return instance;
245         }
246
247         void setCacheLength(long cacheLength) {
248                 this.cacheLength = cacheLength;
249         }
250
251         void cache(Arp arp) {
252                 if (arp.isSitePolicy() == false) {
253                         synchronized (cache) {
254                                 cache.put(arp.getPrincipal(), new CachedArp(arp, System.currentTimeMillis()));
255                         }
256                 } else {
257                         synchronized (cache) {
258                                 cache.put(new SiteCachePrincipal(), new CachedArp(arp, System.currentTimeMillis()));
259                         }
260                 }
261         }
262
263         Arp retrieveUserArpFromCache(Principal principal) {
264                 return retrieveArpFromCache(principal);
265         }
266
267         Arp retrieveSiteArpFromCache() {
268                 return retrieveArpFromCache(new SiteCachePrincipal());
269         }
270
271         private Arp retrieveArpFromCache(Principal principal) {
272
273                 CachedArp cachedArp;
274                 synchronized (cache) {
275                         cachedArp = (CachedArp) cache.get(principal);
276                 }
277
278                 if (cachedArp == null) {
279                         return null;
280                 }
281
282                 if ((System.currentTimeMillis() - cachedArp.creationTimeMillis) < cacheLength) {
283                         return cachedArp.arp;
284                 }
285
286                 synchronized (cache) {
287                         cache.remove(principal);
288                 }
289                 return null;
290         }
291
292         /**
293          * @see java.lang.Object#finalize()
294          */
295         protected void finalize() throws Throwable {
296                 super.finalize();
297                 synchronized (cleaner) {
298                         cleaner.shutdown = true;
299                         cleaner.interrupt();
300                 }
301         }
302
303         private class CachedArp {
304                 Arp arp;
305                 long creationTimeMillis;
306
307                 CachedArp(Arp arp, long creationTimeMillis) {
308                         this.arp = arp;
309                         this.creationTimeMillis = creationTimeMillis;
310                 }
311         }
312
313         private class SiteCachePrincipal implements Principal {
314
315                 public String getName() {
316                         return "ARP admin";
317                 }
318
319                 /**
320                  * @see java.lang.Object#equals(Object)
321                  */
322                 public boolean equals(Object object) {
323                         if (object instanceof SiteCachePrincipal) {
324                                 return true;
325                         }
326                         return false;
327                 }
328
329                 /**
330                  * @see java.lang.Object#hashCode()
331                  */
332                 public int hashCode() {
333                         return "edu.internet2.middleware.shibboleth.aa.arp.provider.BaseArpRepository.SiteCachePrincipal"
334                                 .hashCode();
335                 }
336         }
337
338         private class ArpCacheCleaner extends Thread {
339
340                 private boolean shutdown = false;
341
342                 public ArpCacheCleaner() {
343                         super();
344                         log.debug("Starting ArpCache Cleanup Thread.");
345                         start();
346                 }
347
348                 public void run() {
349                         try {
350                                 sleep(5 * 60 * 1000);
351                         } catch (InterruptedException e) {
352                                 log.debug("ArpCache Cleanup interrupted.");
353                         }
354                         while (true) {
355                                 try {
356                                         if (shutdown) {
357                                                 log.debug("Stopping ArpCache Cleanup Thread.");
358                                                 return;
359                                         }
360                                         Set needsDeleting = new HashSet();
361                                         synchronized (cache) {
362                                                 Iterator iterator = cache.values().iterator();
363                                                 while (iterator.hasNext()) {
364                                                         CachedArp cachedArp = (CachedArp) iterator.next();
365                                                         if ((cachedArp.creationTimeMillis - System.currentTimeMillis())
366                                                                 > cacheLength) {
367                                                                 needsDeleting.add(cachedArp);
368                                                         }
369                                                 }
370                                         }
371                                                 //release the lock to be friendly
372                                                 Iterator deleteIterator = needsDeleting.iterator();
373                                                 while (deleteIterator.hasNext()) {
374                                                         synchronized (cache) {
375                                                                 log.debug("Expiring an ARP from the Cache.");
376                                                                 cache.remove(
377                                                                         ((CachedArp) deleteIterator.next()).arp.getPrincipal());
378                                                         }
379                                                 }
380
381                                         sleep(5 * 60 * 1000);
382                                 } catch (InterruptedException e) {
383                                         log.debug("ArpCache Cleanup interrupted.");
384                                 }
385                         }
386                 }
387         }
388
389 }