d772dfafb227aab633f555af24872f357f8fa182
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / arp / provider / BaseArpRepository.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.aa.arp.provider;
18
19 import java.io.IOException;
20 import java.security.Principal;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.log4j.Logger;
28 import org.w3c.dom.Element;
29 import org.xml.sax.SAXException;
30
31 import edu.internet2.middleware.shibboleth.aa.arp.Arp;
32 import edu.internet2.middleware.shibboleth.aa.arp.ArpMarshallingException;
33 import edu.internet2.middleware.shibboleth.aa.arp.ArpRepository;
34 import edu.internet2.middleware.shibboleth.aa.arp.ArpRepositoryException;
35
36 /**
37  * Provides marshalling/unmarshalling functionality common among <code>ArpRepository</code> implementations.
38  * 
39  * @author Walter Hoehn (wassa@columbia.edu)
40  */
41
42 public abstract class BaseArpRepository implements ArpRepository {
43
44         private static Logger log = Logger.getLogger(BaseArpRepository.class.getName());
45         private ArpCache arpCache;
46
47         BaseArpRepository(Element config) throws ArpRepositoryException {
48
49                 String rawArpTTL = config.getAttribute("arpTTL");
50                 long arpTTL = 0;
51                 try {
52                         if (rawArpTTL != null && !rawArpTTL.equals("")) {
53                                 arpTTL = Long.parseLong(rawArpTTL);
54                                 log.debug("ARP TTL set to: (" + arpTTL + ").");
55                         }
56                 } catch (NumberFormatException e) {
57                         log.error("ARP TTL must be set to a long integer.");
58                 }
59
60                 if (arpTTL > 0) {
61                         arpCache = ArpCache.instance();
62                         arpCache.setCacheLength(arpTTL);
63                 }
64         }
65
66         /**
67          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getAllPolicies(Principal)
68          */
69
70         public Arp[] getAllPolicies(Principal principal) throws ArpRepositoryException {
71
72                 log.debug("Received a query for all policies applicable to principal: (" + principal.getName() + ").");
73                 Set<Arp> allPolicies = new HashSet<Arp>();
74                 Arp sitePolicy = getSitePolicy();
75                 if (sitePolicy != null) {
76                         log.debug("Returning site policy.");
77                         allPolicies.add(sitePolicy);
78                 }
79
80                 Arp userPolicy = getUserPolicy(principal);
81                 if (userPolicy != null) {
82                         allPolicies.add(userPolicy);
83                         log.debug("Returning user policy.");
84                 }
85                 if (allPolicies.isEmpty()) {
86                         log.debug("No policies found.");
87                 }
88                 return (Arp[]) allPolicies.toArray(new Arp[0]);
89         }
90
91         /**
92          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getSitePolicy()
93          */
94         public Arp getSitePolicy() throws ArpRepositoryException {
95
96                 try {
97                         if (arpCache != null) {
98                                 Arp cachedArp = arpCache.retrieveSiteArpFromCache();
99                                 if (cachedArp != null) {
100                                         log.debug("Using cached site ARP.");
101                                         return cachedArp;
102                                 }
103                         }
104
105                         Element xml = retrieveSiteArpXml();
106                         if (xml == null) { return null; }
107
108                         Arp siteArp = new Arp();
109                         siteArp.marshall(xml);
110                         if (arpCache != null) {
111                                 arpCache.cache(siteArp);
112                         }
113                         return siteArp;
114                 } catch (ArpMarshallingException ame) {
115                         log.error("An error occurred while marshalling an ARP: " + ame);
116                         throw new ArpRepositoryException("An error occurred while marshalling an ARP.");
117                 } catch (IOException ioe) {
118                         log.error("An error occurred while loading an ARP: " + ioe);
119                         throw new ArpRepositoryException("An error occurred while loading an ARP.");
120                 } catch (SAXException se) {
121                         log.error("An error occurred while parsing an ARP: " + se);
122                         throw new ArpRepositoryException("An error occurred while parsing an ARP.");
123                 }
124         }
125
126         /**
127          * Inheritors must return the site Arp as an xml element.
128          * 
129          * @return Element
130          */
131         protected abstract Element retrieveSiteArpXml() throws IOException, SAXException;
132
133         public void destroy() {
134
135                 if (arpCache != null) {
136                         arpCache.destroy();
137                 }
138         }
139
140         /**
141          * @see edu.internet2.middleware.shibboleth.aa.arp.ArpRepository#getUserPolicy(Principal)
142          */
143         public Arp getUserPolicy(Principal principal) throws ArpRepositoryException {
144
145                 if (arpCache != null) {
146                         Arp cachedArp = arpCache.retrieveUserArpFromCache(principal);
147                         if (cachedArp != null) {
148                                 log.debug("Using cached user ARP.");
149                                 return cachedArp;
150                         }
151                 }
152
153                 try {
154                         Element xml = retrieveUserArpXml(principal);
155                         if (xml == null) { return null; }
156
157                         Arp userArp = new Arp();
158                         userArp.setPrincipal(principal);
159
160                         userArp.marshall(xml);
161                         if (arpCache != null) {
162                                 arpCache.cache(userArp);
163                         }
164                         return userArp;
165                 } catch (ArpMarshallingException ame) {
166                         log.error("An error occurred while marshalling an ARP: " + ame);
167                         throw new ArpRepositoryException("An error occurred while marshalling an ARP.");
168                 } catch (IOException ioe) {
169                         log.error("An error occurred while loading an ARP: " + ioe);
170                         throw new ArpRepositoryException("An error occurred while loading an ARP.");
171                 } catch (SAXException se) {
172                         log.error("An error occurred while parsing an ARP: " + se);
173                         throw new ArpRepositoryException("An error occurred while parsing an ARP.");
174                 }
175         }
176
177         /**
178          * Inheritors must return the user Arp as an xml element.
179          * 
180          * @return Element
181          */
182         protected abstract Element retrieveUserArpXml(Principal principal) throws IOException, SAXException;
183
184 }
185
186 class ArpCache {
187
188         private static ArpCache instance = null;
189         /** Time in seconds for which ARPs should be cached. */
190         private long cacheLength;
191         private Map<Principal, CachedArp> cache = new HashMap<Principal, CachedArp>();
192         private static Logger log = Logger.getLogger(ArpCache.class.getName());
193         private ArpCacheCleaner cleaner = new ArpCacheCleaner();
194
195         protected ArpCache() {
196
197         }
198
199         static synchronized ArpCache instance() {
200
201                 if (instance == null) {
202                         instance = new ArpCache();
203                         return instance;
204                 }
205                 return instance;
206         }
207
208         /** Set time in seconds for which ARPs should be cached. */
209         void setCacheLength(long cacheLength) {
210
211                 this.cacheLength = cacheLength;
212         }
213
214         void cache(Arp arp) {
215
216                 if (arp.isSitePolicy() == false) {
217                         synchronized (cache) {
218                                 cache.put(arp.getPrincipal(), new CachedArp(arp, System.currentTimeMillis()));
219                         }
220                 } else {
221                         synchronized (cache) {
222                                 cache.put(new SiteCachePrincipal(), new CachedArp(arp, System.currentTimeMillis()));
223                         }
224                 }
225         }
226
227         Arp retrieveUserArpFromCache(Principal principal) {
228
229                 return retrieveArpFromCache(principal);
230         }
231
232         Arp retrieveSiteArpFromCache() {
233
234                 return retrieveArpFromCache(new SiteCachePrincipal());
235         }
236
237         private Arp retrieveArpFromCache(Principal principal) {
238
239                 CachedArp cachedArp;
240                 synchronized (cache) {
241                         cachedArp = (CachedArp) cache.get(principal);
242                 }
243
244                 if (cachedArp == null) { return null; }
245
246                 if ((System.currentTimeMillis() - cachedArp.creationTimeMillis) < (cacheLength * 1000)) { return cachedArp.arp; }
247
248                 synchronized (cache) {
249                         cache.remove(principal);
250                 }
251                 return null;
252         }
253
254         /**
255          * @see java.lang.Object#finalize()
256          */
257         protected void finalize() throws Throwable {
258
259                 super.finalize();
260                 destroy();
261         }
262
263         public void destroy() {
264
265                 synchronized (cleaner) {
266                         if (cleaner != null) {
267                                 cleaner.shutdown = true;
268                                 cleaner.interrupt();
269                         }
270                 }
271         }
272
273         private class CachedArp {
274
275                 Arp arp;
276                 long creationTimeMillis;
277
278                 CachedArp(Arp arp, long creationTimeMillis) {
279
280                         this.arp = arp;
281                         this.creationTimeMillis = creationTimeMillis;
282                 }
283         }
284
285         private class SiteCachePrincipal implements Principal {
286
287                 public String getName() {
288
289                         return "ARP admin";
290                 }
291
292                 /**
293                  * @see java.lang.Object#equals(Object)
294                  */
295                 public boolean equals(Object object) {
296
297                         if (object instanceof SiteCachePrincipal) { return true; }
298                         return false;
299                 }
300
301                 /**
302                  * @see java.lang.Object#hashCode()
303                  */
304                 public int hashCode() {
305
306                         return "edu.internet2.middleware.shibboleth.aa.arp.provider.BaseArpRepository.SiteCachePrincipal"
307                                         .hashCode();
308                 }
309         }
310
311         private class ArpCacheCleaner extends Thread {
312
313                 private boolean shutdown = false;
314                 private Thread master;
315
316                 public ArpCacheCleaner() {
317
318                         super("edu.internet2.middleware.shibboleth.aa.arp.provider.BaseArpRepository.ArpCache.ArpCacheCleaner");
319                         master = Thread.currentThread();
320                         setDaemon(true);
321                         if (getPriority() > Thread.MIN_PRIORITY) {
322                                 setPriority(getPriority() - 1);
323                         }
324                         log.debug("Starting ArpCache Cleanup Thread.");
325                         start();
326                 }
327
328                 public void run() {
329
330                         try {
331                                 sleep(60 * 1000); // one minute
332                         } catch (InterruptedException e) {
333                                 log.debug("ArpCache Cleanup interrupted.");
334                         }
335                         while (true) {
336                                 try {
337                                         if (master == null) {
338                                                 log.debug("ArpCache cache cleaner is orphaned.");
339                                                 shutdown = true;
340                                         }
341                                         if (shutdown) {
342                                                 log.debug("Stopping ArpCache Cleanup Thread.");
343                                                 return;
344                                         }
345                                         log.debug("ArpCache cleanup thread searching for stale entries.");
346                                         Set<CachedArp> needsDeleting = new HashSet<CachedArp>();
347                                         synchronized (cache) {
348                                                 Iterator<CachedArp> iterator = cache.values().iterator();
349                                                 while (iterator.hasNext()) {
350                                                         CachedArp cachedArp = iterator.next();
351                                                         if ((System.currentTimeMillis() - cachedArp.creationTimeMillis) > (cacheLength * 1000)) {
352                                                                 needsDeleting.add(cachedArp);
353                                                         }
354                                                 }
355                                         }
356                                         // release the lock to be friendly
357                                         Iterator deleteIterator = needsDeleting.iterator();
358                                         while (deleteIterator.hasNext()) {
359                                                 synchronized (cache) {
360                                                         CachedArp cachedArp = (CachedArp) deleteIterator.next();
361                                                         if (cachedArp.arp.isSitePolicy()) {
362                                                                 log.debug("Expiring site ARP from the Cache.");
363                                                                 cache.remove(new SiteCachePrincipal());
364                                                         } else {
365                                                                 log.debug("Expiring an ARP from the Cache.");
366                                                                 cache.remove(cachedArp.arp.getPrincipal());
367                                                         }
368                                                 }
369                                         }
370
371                                         sleep(60 * 1000); // one minute
372                                 } catch (InterruptedException e) {
373                                         log.debug("ArpCache Cleanup interrupted.");
374                                 }
375                         }
376                 }
377         }
378
379 }