KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > slide > store > txjndi > JNDIPrincipalStore


1 /*
2  * $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txjndi/JNDIPrincipalStore.java,v 1.5.2.3 2004/10/27 05:23:36 masonjm Exp $
3  * $Revision: 1.5.2.3 $
4  * $Date: 2004/10/27 05:23:36 $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2004 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24 package org.apache.slide.store.txjndi;
25
26 import java.util.ArrayList JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Hashtable JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.NoSuchElementException JavaDoc;
33 import java.util.StringTokenizer JavaDoc;
34 import java.util.TreeSet JavaDoc;
35 import java.util.Vector JavaDoc;
36
37 import javax.naming.NamingEnumeration JavaDoc;
38 import javax.naming.NamingException JavaDoc;
39 import javax.naming.directory.Attribute JavaDoc;
40 import javax.naming.directory.DirContext JavaDoc;
41 import javax.naming.directory.InitialDirContext JavaDoc;
42 import javax.naming.directory.SearchControls JavaDoc;
43 import javax.naming.directory.SearchResult JavaDoc;
44 import javax.transaction.xa.XAException JavaDoc;
45 import javax.transaction.xa.XAResource JavaDoc;
46 import javax.transaction.xa.Xid JavaDoc;
47
48 import net.sf.ehcache.Cache;
49 import net.sf.ehcache.CacheException;
50 import net.sf.ehcache.CacheManager;
51 import net.sf.ehcache.Element;
52 import net.sf.ehcache.ObjectExistsException;
53
54 import org.apache.slide.common.AbstractXAService;
55 import org.apache.slide.common.NamespaceAccessToken;
56 import org.apache.slide.common.ServiceAccessException;
57 import org.apache.slide.common.ServiceConnectionFailedException;
58 import org.apache.slide.common.ServiceDisconnectionFailedException;
59 import org.apache.slide.common.ServiceInitializationFailedException;
60 import org.apache.slide.common.ServiceParameterErrorException;
61 import org.apache.slide.common.ServiceParameterMissingException;
62 import org.apache.slide.common.ServiceResetFailedException;
63 import org.apache.slide.common.Uri;
64 import org.apache.slide.content.NodeProperty;
65 import org.apache.slide.content.NodeRevisionContent;
66 import org.apache.slide.content.NodeRevisionDescriptor;
67 import org.apache.slide.content.NodeRevisionDescriptors;
68 import org.apache.slide.content.NodeRevisionNumber;
69 import org.apache.slide.content.RevisionAlreadyExistException;
70 import org.apache.slide.content.RevisionDescriptorNotFoundException;
71 import org.apache.slide.content.RevisionNotFoundException;
72 import org.apache.slide.lock.LockTokenNotFoundException;
73 import org.apache.slide.lock.NodeLock;
74 import org.apache.slide.security.NodePermission;
75 import org.apache.slide.store.ContentStore;
76 import org.apache.slide.store.LockStore;
77 import org.apache.slide.store.NodeStore;
78 import org.apache.slide.store.RevisionDescriptorStore;
79 import org.apache.slide.store.RevisionDescriptorsStore;
80 import org.apache.slide.store.SecurityStore;
81 import org.apache.slide.structure.ObjectAlreadyExistsException;
82 import org.apache.slide.structure.ObjectNode;
83 import org.apache.slide.structure.ObjectNotFoundException;
84 import org.apache.slide.structure.SubjectNode;
85 import org.apache.slide.util.CacheManagerFactory;
86 import org.apache.slide.util.logger.Logger;
87
88 /**
89  * <p>
90  * This is a read-only Store implementation for retrieving Slide users
91  * and roles from an LDAP server. It has been tested with Novell's eDirectory
92  * version 8.6.2. Other LDAP servers should work.
93  * </p>
94  *
95  * <h3>Prerequisites</h3>
96  * <p>
97  * To use this Store your app server must be setup to authenticate
98  * users using the LDAP server. For Tomcat 5 see
99  * <a HREF="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/realm-howto.html#JNDIRealm">this</a>.
100  * You cannot use the SlideRealm to authenticate users because this Store
101  * does not expose a <span style="font-style: italic;">password</span>
102  * property.
103  * </p>
104  *
105  * <h3>Store Parameters</h3>
106  * <p>
107  * Parameters used in Domain.xml when setting up the Store.
108  * </p>
109  * <dl>
110  * <dt>cache.refresh.checkrate</dt>
111  * <dd>
112  * How often, in <em>seconds</em>, the cache refresh thread should check for Uris in the cache
113  * that need to be refreshed. The default value is "15".
114  * See <a HREF="#cacherefreshing">Cache Refreshing</a> for more information.
115  * </dd>
116  *
117  * <dt>cache.refresh.rate</dt>
118  * <dd>
119  * How frequently, in <em>seconds</em>, Uris that are marked for refreshing should be refreshed.
120  * This value must be less than the TimeToLive and TimeToIdle (whichever is least) parameters
121  * specified for the EHCache in order for the items to never expire. The default value is "800".
122  * See <a HREF="#caching">Caching</a> and <a HREF="#cacherefreshing">Cache Refreshing</a> for
123  * more information.
124  * </dd>
125  *
126  * <dt>cache.refresh.threshold</dt>
127  * <dd>
128  * The maximum amount of time, in <em>milliseconds</em>, that retrieve* methods can take before
129  * the Uri they are retrieving is scheduled for refreshing. By tuning this parameter you can keep
130  * smaller, infrequently accessed Uris from perpetually remaining in the cache. This may improve
131  * cache performance.
132  * See <a HREF="#cacherefreshing">Cache Refreshing</a> for more information.
133  * </dd>
134  *
135  * <dt>jndi.container</dt>
136  * <dd>
137  * The base LDAP context you wish to search. Example: <em>ou=Users,o=Company</em>
138  * </dd>
139  *
140  * <dt>jndi.attributes.rdn</dt>
141  * <dd>
142  * The attribute used to uniquely identify the objects you're fetching. Usually uid or cn.
143  * </dd>
144  *
145  * <dt>jndi.attributes.userprincipalname</dt>
146  * <dd>
147  * The attribute used to provide a user/role name which is mapped into Slide instead of the
148  * path name. This attribute is optional.
149  * </dd>
150  *
151  * <dt>jndi.search.filter</dt>
152  * <dd>
153  * The filter string to use for the search. Example: <em>(objectClass=inetOrgPerson)</em>.
154  * The default value is <em>(objectClass=*)</em>.
155  * See the <a HREF="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/directory/DirContext.html#search(javax.naming.Name,%20java.lang.String,%20javax.naming.directory.SearchControls)">DirContext.search()</a> javadoc.
156  * </dd>
157  *
158  * <dt>jndi.search.scope</dt>
159  * <dd>
160  * The Scope of the search. Can be one of <em>OBJECT_SCOPE</em>, <em>ONELEVEL_SCOPE</em>,
161  * <em>SUBTREE_SCOPE</em>. The default value is <em>ONELEVEL_SCOPE</em>.
162  * See the <a HREF="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/directory/SearchControls.html#OBJECT_SCOPE">SearchControls</a> javadoc.
163  * </dd>
164  *
165  * <dt>jndi.search.attributes</dt>
166  * <dd>
167  * A comma delimited list of the attributes you want returned with your search results.
168  * Example: <em>givenName, uid, mail</em>. The default value is <em>cn</em>.
169  * </dd>
170  *
171  * <dt>java.naming.*</dt>
172  * <dd>
173  * Parameters for connecting to the LDAP server.
174  * See the <a HREF="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/InitialContext.html">InitialContext</a> javadoc.
175  * </dd>
176  * </dl>
177  *
178  * <h3><a name="caching">Caching</a></h3>
179  * <p>
180  * This Store makes use of <a HREF="http://ehcache.sourceforge.net/">EHCache</a>. You will
181  * need ehcache.jar in order to use this Store. When initialized the default CacheManager is
182  * used to find a Cache named "org.apache.slide.store.txjndi.JNDIPrincipalStore". If there is
183  * no Cache found with this name then a Cache is created with these default values:
184  * </p>
185  * <ul>
186  * <li>name = org.apache.slide.store.txjndi.JNDIPrincipalStore</li>
187  * <li>maxElementsInMemory = 200</li>
188  * <li>eternal = false</li>
189  * <li>timeToIdleSeconds = 900</li>
190  * <li>timeToLiveSeconds = 900</li>
191  * <li>overflowToDisk = true</li>
192  * </ul>
193  * <p>
194  * To override these values you will need to create a configuration file for EHCache with a
195  * cache named "org.apache.slide.store.txjndi.JNDIPrincipalStore" that has the settings you
196  * wish. See the documentation at the <a HREF="http://ehcache.sourceforge.net/">EHCache website</a>
197  * for instructions.
198  * </p>
199  *
200  * <h3><a name="cacherefreshing">Cache Refreshing</a></h3>
201  * <p>
202  * Because the data delivered by this Store is managed externally to Slide the data cache must be
203  * periodically expired to pick up any changes. Because creating object Nodes can take a long time
204  * for LDAP queries that return a lot of objects, larger queries need to be preemptively refreshed
205  * before a user makes a request to an expired object.
206  * </p>
207  * <p>
208  * The retrieveObject() and retrieveNodeDescriptor() methods monitor the amount of time it takes
209  * them to return for each Uri. If the method takes longer than a specified amount of time
210  * (configured with the <em>cache.refresh.threshold</em> parameter) the Uri is marked as needing
211  * to be refreshed. Upon initialization the Store spawns a child thread that periodically checks
212  * for Uris that need to be refreshed.
213  * </p>
214  *
215  * <h3>TODO:</h3>
216  * <ol>
217  * <li>
218  * I'd like to see this implemented as a ResourceManager rather than
219  * a stand-alone Store. I think it would fit into Slide's framework better
220  * that way and mean less duplicated code.
221  * </li>
222  * <li>
223  * I think there's still room for a full-fledged LDAP store. The way
224  * LDAP exposes a directory as a graph-of-objects-with-properties and
225  * Slide exposes a repository as a graph-of-objects-with-properties seems
226  * very similar to me ;). However, adapting the structure of most LDAP
227  * servers to the user/role structure that Slide uses would be a bit of a
228  * pain, so I don't think this kind of Store would be useful for
229  * users/roles in Slide. I have heard of people using LDAP to keep track
230  * of server inventories and things like that, though, and I think it
231  * would work well there.
232  * </li>
233  * </ol>
234  *
235  */

236 public class JNDIPrincipalStore
237         extends AbstractXAService
238         implements ContentStore, LockStore, NodeStore, RevisionDescriptorStore,
239             RevisionDescriptorsStore, SecurityStore {
240
241     public static final String JavaDoc CACHE_NAME = JNDIPrincipalStore.class.getName();
242     public static final String JavaDoc CACHE_OBJECT_PREFIX = "object: ";
243     public static final String JavaDoc CACHE_DESCRIPTOR_PREFIX = "descriptor: ";
244     
245     public static final String JavaDoc JNDI_PROPERTY_PREFIX = "java.naming";
246
247     // Parameter keys
248
public static final String JavaDoc PARAM_CACHE_REFRESH_CHECK_RATE = "cache.refresh.checkrate";
249     public static final String JavaDoc PARAM_CACHE_REFRESH_RATE = "cache.refresh.rate";
250     public static final String JavaDoc PARAM_CACHE_REFRESH_THRESHOLD = "cache.refresh.threshold";
251     
252     public static final String JavaDoc PARAM_JNDI_CONTAINER = "jndi.container";
253     public static final String JavaDoc PARAM_JNDI_FILTER = "jndi.search.filter";
254     public static final String JavaDoc PARAM_JNDI_GROUPMEMBERSET = "jndi.attributes.groupmemberset";
255     public static final String JavaDoc PARAM_JNDI_RDN_ATTRIBUTE = "jndi.attributes.rdn";
256     public static final String JavaDoc PARAM_JNDI_SEARCH_ATTRIBUTES = "jndi.search.attributes";
257     public static final String JavaDoc PARAM_JNDI_SEARCH_SCOPE = "jndi.search.scope";
258     public static final String JavaDoc PARAM_JNDI_USERPRINCIPALNAME = "jndi.attributes.userprincipalname";
259     
260     public static final String JavaDoc PARAM_LOG_VALIDATION_ERRORS = "log.validationerrors";
261     
262     // Default values
263
public static final int DEFAULT_CACHE_SIZE = 200;
264     public static final boolean DEFAULT_CACHE_OVERFLOW_TO_DISK = true;
265     public static final boolean DEFAULT_CACHE_ETERNAL = false;
266     public static final long DEFAULT_CACHE_TTL = 900L; // seconds
267
public static final long DEFAULT_CACHE_TTI = 900L; // seconds
268
public static final long DEFAULT_CACHE_REFRESH_CHECK_RATE = 15L; // seconds
269
public static final long DEFAULT_CACHE_REFRESH_RATE = 800L; // seconds
270
public static final long DEFAULT_CACHE_REFRESH_THRESHOLD = 200L; // milliseconds!!
271

272     public static final String JavaDoc DEFAULT_JNDI_SEARCH_ATTRIBUTES = "cn";
273     public static final String JavaDoc DEFAULT_JNDI_FILTER = "(objectClass=*)";
274     
275     public static final String JavaDoc LDAP_NAMESPACE = "LDAP:";
276     
277     public static final String JavaDoc LOG_CHANNEL = JNDIPrincipalStore.class.getName();
278     
279     protected Hashtable JavaDoc ctxParameters;
280
281     protected boolean isConnected = false;
282     protected boolean logValidationErrors = false;
283
284     protected Cache cache;
285     protected TreeSet JavaDoc refreshList;
286     protected long refreshRate = DEFAULT_CACHE_REFRESH_RATE;
287     protected long refreshThreadSleepTime = DEFAULT_CACHE_REFRESH_CHECK_RATE;
288     protected long refreshThreshold = DEFAULT_CACHE_REFRESH_THRESHOLD;
289     protected RefreshThread refresher;
290     
291     protected String JavaDoc container;
292     protected String JavaDoc[] descriptorAttributes;
293     protected String JavaDoc filter;
294     protected String JavaDoc groupMemberSet;
295     protected String JavaDoc rdnAttribute;
296     protected int searchScope;
297     protected String JavaDoc principalNameAttribute;
298
299     private String JavaDoc name;
300     private String JavaDoc usersPath;
301     private Map JavaDoc objectNameMap; // Uri-String -> LDAP lookup name
302

303     
304     public JNDIPrincipalStore() {
305         ctxParameters = new Hashtable JavaDoc();
306         cache = getCache();
307         name = "";
308         refreshList = new TreeSet JavaDoc();
309         refresher = new RefreshThread();
310         objectNameMap = new HashMap JavaDoc();
311     }
312     
313     // ----------------------------------------------------------- Service Methods --------
314

315     public void initialize( NamespaceAccessToken token )
316             throws ServiceInitializationFailedException {
317         name = "JNDIPrincipalStore[" + scope.toString() + "]";
318         getLogger().log( "Initializing " + name, LOG_CHANNEL, Logger.DEBUG );
319         super.initialize( token );
320         
321         usersPath = token.getNamespaceConfig().getUsersPath();
322     }
323
324     public void setParameters( Hashtable JavaDoc parameters )
325             throws ServiceParameterErrorException,
326             ServiceParameterMissingException {
327         
328         getLogger().log( "Setting parameters for " + name, LOG_CHANNEL, Logger.DEBUG );
329         
330         String JavaDoc temp = null;
331
332         // Set ctxParameters
333
Iterator JavaDoc keys = parameters.keySet().iterator();
334         while ( keys.hasNext() ) {
335             temp = (String JavaDoc)keys.next();
336             if ( temp.startsWith( JNDI_PROPERTY_PREFIX ) ) {
337                 ctxParameters.put( temp, parameters.get( temp ) );
338             }
339         }
340         
341         // Set container
342
container = (String JavaDoc)parameters.get( PARAM_JNDI_CONTAINER );
343         if ( container == null || container.length() == 0 ) {
344             getLogger().log(
345                 "Error during " + name + " setup: No value set for " +
346                 PARAM_JNDI_CONTAINER,
347                 LOG_CHANNEL,
348                 Logger.CRITICAL );
349         }
350         
351         // Set filter
352
filter = (String JavaDoc)parameters.get( PARAM_JNDI_FILTER );
353         if ( filter == null || filter.length() == 0 ) {
354             filter = DEFAULT_JNDI_FILTER;
355         }
356         
357         // Set rdnAttribute
358
rdnAttribute = (String JavaDoc)parameters.get( PARAM_JNDI_RDN_ATTRIBUTE );
359         if ( rdnAttribute == null || rdnAttribute.length() == 0 ) {
360             getLogger().log(
361                 "Error during " + name + " setup: No value set for " +
362                 PARAM_JNDI_RDN_ATTRIBUTE,
363                 LOG_CHANNEL,
364                 Logger.CRITICAL );
365         }
366         
367         // Set searchScope
368
temp = (String JavaDoc)parameters.get( PARAM_JNDI_SEARCH_SCOPE );
369         if ( "OBJECT_SCOPE".equalsIgnoreCase( temp ) ) {
370             searchScope = SearchControls.OBJECT_SCOPE;
371         } else if ( "ONELEVEL_SCOPE".equalsIgnoreCase( temp ) ) {
372             searchScope = SearchControls.ONELEVEL_SCOPE;
373         } else if ( "SUBTREE_SCOPE".equalsIgnoreCase( temp ) ) {
374             searchScope = SearchControls.SUBTREE_SCOPE;
375         } else {
376             searchScope = SearchControls.ONELEVEL_SCOPE;
377         }
378         
379         // Set descriptorAttributes and groupMemberSet
380
temp = (String JavaDoc)parameters.get( PARAM_JNDI_SEARCH_ATTRIBUTES );
381         if ( temp == null || temp.length() == 0 ) {
382             temp = DEFAULT_JNDI_SEARCH_ATTRIBUTES;
383         }
384         ArrayList JavaDoc searchAttributesList = new ArrayList JavaDoc();
385         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc( temp, "," );
386         while ( tok.hasMoreTokens() ) {
387             searchAttributesList.add( tok.nextToken().trim() );
388         }
389         String JavaDoc gms = (String JavaDoc)parameters.get( PARAM_JNDI_GROUPMEMBERSET );
390         if ( gms != null ) {
391             searchAttributesList.add( gms );
392             groupMemberSet = gms;
393         } else {
394             groupMemberSet = "";
395         }
396         descriptorAttributes = (String JavaDoc[])searchAttributesList.toArray( new String JavaDoc[0] );
397         
398         // Set refreshRate
399
temp = (String JavaDoc)parameters.get( PARAM_CACHE_REFRESH_RATE );
400         if ( temp != null ) {
401             refreshRate = Long.parseLong( temp ) * 1000L;
402         } else {
403             refreshRate = DEFAULT_CACHE_REFRESH_RATE * 1000L;
404         }
405         
406         // Set refreshThreadSleepTime
407
temp = (String JavaDoc)parameters.get( PARAM_CACHE_REFRESH_CHECK_RATE );
408         if ( temp != null ) {
409             refreshThreadSleepTime = Long.parseLong( temp ) * 1000L;
410         } else {
411             refreshThreadSleepTime = DEFAULT_CACHE_REFRESH_CHECK_RATE * 1000L;
412         }
413         
414         // Set refreshThreshold
415
temp = (String JavaDoc)parameters.get( PARAM_CACHE_REFRESH_THRESHOLD );
416         if ( temp != null ) {
417             refreshThreshold = Long.parseLong( temp );
418         } else {
419             refreshThreshold = DEFAULT_CACHE_REFRESH_THRESHOLD;
420         }
421         
422         // Set logValidationErrors
423
temp = (String JavaDoc)parameters.get( PARAM_LOG_VALIDATION_ERRORS );
424         if ( "true".equalsIgnoreCase( temp ) ) {
425             logValidationErrors = true;
426         }
427
428         //Set attribute which contains the user principal name for authentication
429
principalNameAttribute = (String JavaDoc)parameters.get(PARAM_JNDI_USERPRINCIPALNAME);
430     }
431     
432     public boolean cacheResults() {
433         return false;
434     }
435
436     
437     //------------------------------------------------------ NodeStore Methods ----------
438

439     public void storeObject( Uri uri, ObjectNode object )
440         throws ServiceAccessException, ObjectNotFoundException {}
441
442     public void createObject( Uri uri, ObjectNode object )
443         throws ServiceAccessException, ObjectAlreadyExistsException {}
444
445     public void removeObject( Uri uri, ObjectNode object )
446         throws ServiceAccessException, ObjectNotFoundException {}
447
448     public ObjectNode retrieveObject( Uri uri ) throws ServiceAccessException,
449             ObjectNotFoundException {
450         
451         getLogger().log( name + ": Retrieving Object " + uri.toString() + ".",
452             LOG_CHANNEL, Logger.DEBUG );
453         
454         String JavaDoc cacheKey = CACHE_OBJECT_PREFIX + uri.toString();
455         
456         Element cachedNode = null;
457         try {
458             if ( cache != null ) {
459                 cachedNode = cache.get( cacheKey );
460             }
461         } catch ( CacheException e ) {
462             getLogger().log(
463                 name + ": Error while getting \"" + cacheKey + "\" from cache.",
464                 e,
465                 LOG_CHANNEL,
466                 Logger.ERROR);
467         }
468         if ( cachedNode != null ) {
469             getLogger().log(
470                 name + ": ObjectNode for \"" + uri.toString() + "\" found in cache.",
471                 LOG_CHANNEL,
472                 Logger.DEBUG );
473             return (ObjectNode)cachedNode.getValue();
474         } else {
475             return getObject( uri );
476         }
477         
478     }
479
480     //-------------------------------------------- RevisionDescriptorStore Methods --------
481

482     public void createRevisionDescriptor( Uri uri, NodeRevisionDescriptor revisionDescriptor )
483         throws ServiceAccessException {}
484
485     public void storeRevisionDescriptor( Uri uri, NodeRevisionDescriptor revisionDescriptor )
486         throws ServiceAccessException, RevisionDescriptorNotFoundException {}
487
488     public void removeRevisionDescriptor( Uri uri, NodeRevisionNumber revisionNumber )
489         throws ServiceAccessException {}
490
491     public NodeRevisionDescriptor retrieveRevisionDescriptor( Uri uri,
492             NodeRevisionNumber revisionNumber ) throws ServiceAccessException,
493             RevisionDescriptorNotFoundException {
494
495         getLogger().log( name + ": Retrieving Revision Descriptor for " + uri.toString() + ".",
496             LOG_CHANNEL, Logger.DEBUG );
497         
498         String JavaDoc cacheKey = CACHE_DESCRIPTOR_PREFIX + uri.toString();
499         
500         Element cachedDescriptor = null;
501         try {
502             if ( cache != null ) {
503                 cachedDescriptor = cache.get( cacheKey );
504             }
505         } catch ( CacheException e ) {
506             getLogger().log(
507                 name + ": Error while getting \"" + cacheKey + "\" from cache.",
508                 e,
509                 LOG_CHANNEL,
510                 Logger.ERROR);
511         }
512         if ( cachedDescriptor != null ) {
513             getLogger().log(
514                 name + ": NodeRevisionDescriptor for \"" + uri.toString() + "\" found in cache.",
515                 LOG_CHANNEL,
516                 Logger.DEBUG );
517             return (NodeRevisionDescriptor)cachedDescriptor.getValue();
518         } else {
519             return getRevisionDescriptor( uri );
520         }
521         
522     }
523     
524     // --------------------------------------------- RevisionDescriptorsStore Methods -----
525

526     public NodeRevisionDescriptors retrieveRevisionDescriptors( Uri uri )
527             throws ServiceAccessException, RevisionDescriptorNotFoundException {
528         getLogger().log( name + ": Retrieving Revision Descriptors for " + uri.toString() + ".",
529             LOG_CHANNEL, Logger.DEBUG );
530         
531         NodeRevisionNumber rev = new NodeRevisionNumber( 1, 0 );
532     
533         Hashtable JavaDoc workingRevisions = new Hashtable JavaDoc();
534         workingRevisions.put( "1.0", rev );
535         
536         Hashtable JavaDoc latestRevisionNumbers = new Hashtable JavaDoc();
537         latestRevisionNumbers.put( "1.0", rev );
538     
539         // From looking at NodeRevisionDescriptors.cloneObject I see branchNames is
540
// supposed to store a Vector. I'm guessing the Vector is of revision numbers
541
Vector JavaDoc branches = new Vector JavaDoc();
542         branches.add( rev );
543         Hashtable JavaDoc branchNames = new Hashtable JavaDoc();
544         branchNames.put( "main", branches );
545             
546         return new NodeRevisionDescriptors(
547             uri.toString(),
548             rev,
549             workingRevisions,
550             latestRevisionNumbers,
551             branchNames,
552             false );
553     }
554
555     public void createRevisionDescriptors( Uri uri, NodeRevisionDescriptors revisionDescriptors )
556         throws ServiceAccessException {}
557
558     public void storeRevisionDescriptors( Uri uri, NodeRevisionDescriptors revisionDescriptors )
559         throws ServiceAccessException, RevisionDescriptorNotFoundException {}
560
561     public void removeRevisionDescriptors( Uri uri ) throws ServiceAccessException {}
562     
563     
564     // --------------------------------------------------------- XA Methods --------------
565

566     public void connect() throws ServiceConnectionFailedException {
567         if ( !refresher.isAlive() ) {
568             refresher.start();
569         }
570     }
571
572     public void disconnect() throws ServiceDisconnectionFailedException {
573         if ( refresher.isAlive() ) {
574             refresher.halt();
575         }
576     }
577
578     public void reset() throws ServiceResetFailedException {}
579
580     public boolean isConnected() throws ServiceAccessException {
581         return true;
582     }
583
584     public int getTransactionTimeout() throws XAException JavaDoc {
585         return 0;
586     }
587
588     public boolean setTransactionTimeout( int seconds ) throws XAException JavaDoc {
589         return false;
590     }
591
592     public boolean isSameRM( XAResource JavaDoc rm ) throws XAException JavaDoc {
593         return false;
594     }
595
596     public Xid JavaDoc[] recover( int flag ) throws XAException JavaDoc {
597         return new Xid JavaDoc[0];
598     }
599
600     public int prepare( Xid JavaDoc txId ) throws XAException JavaDoc {
601         return XA_RDONLY;
602     }
603
604     public void forget( Xid JavaDoc txId ) throws XAException JavaDoc {}
605
606     public void rollback( Xid JavaDoc txId ) throws XAException JavaDoc {}
607
608     public void end( Xid JavaDoc txId, int flags ) throws XAException JavaDoc {}
609
610     public void start( Xid JavaDoc txId, int flags ) throws XAException JavaDoc {}
611
612     public void commit( Xid JavaDoc txId, boolean onePhase ) throws XAException JavaDoc {}
613     
614     // -------------------------------------------------- ContentStore Methods ----------
615

616     public NodeRevisionContent retrieveRevisionContent(
617             Uri uri, NodeRevisionDescriptor revisionDescriptor )
618             throws ServiceAccessException, RevisionNotFoundException {
619         NodeRevisionContent nrc = new NodeRevisionContent();
620         nrc.setContent( new char[0] );
621         return nrc;
622     }
623
624     public void createRevisionContent(
625             Uri uri,
626             NodeRevisionDescriptor revisionDescriptor,
627             NodeRevisionContent revisionContent )
628         throws ServiceAccessException, RevisionAlreadyExistException {}
629
630     public void storeRevisionContent(
631             Uri uri,
632             NodeRevisionDescriptor revisionDescriptor,
633             NodeRevisionContent revisionContent )
634         throws ServiceAccessException, RevisionNotFoundException {}
635
636     public void removeRevisionContent( Uri uri, NodeRevisionDescriptor revisionDescriptor )
637         throws ServiceAccessException {}
638     
639     // --------------------------------------------------- Security Store Methods ---------------
640

641     public void grantPermission(Uri uri, NodePermission permission) throws ServiceAccessException {
642         // TODO Auto-generated method stub
643

644     }
645
646     public void revokePermission(Uri uri, NodePermission permission) throws ServiceAccessException {
647         // TODO Auto-generated method stub
648

649     }
650
651     public void revokePermissions(Uri uri) throws ServiceAccessException {
652         // TODO Auto-generated method stub
653

654     }
655
656     /**
657      * Always returns read access for all users.
658      */

659     public Enumeration JavaDoc enumeratePermissions(Uri uri) throws ServiceAccessException {
660         Vector JavaDoc permissions = new Vector JavaDoc();
661         permissions.add( new NodePermission( uri.toString(), "all", "/actions/read" ) );
662         return permissions.elements();
663     }
664     
665     // --------------------------------------------------- LockStore Methods ---------------
666

667     public void putLock(Uri uri, NodeLock lock) throws ServiceAccessException {
668         // TODO Auto-generated method stub
669

670     }
671
672     public void renewLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
673         // TODO Auto-generated method stub
674

675     }
676
677     public void removeLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
678         // TODO Auto-generated method stub
679

680     }
681
682     public void killLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
683         // TODO Auto-generated method stub
684

685     }
686
687     public Enumeration JavaDoc enumerateLocks(Uri uri) throws ServiceAccessException {
688         return new Vector JavaDoc().elements();
689     }
690     
691     // --------------------------------------------------- Worker Methods ---------------
692

693     protected SubjectNode getObject( Uri uri )
694             throws ObjectNotFoundException, ServiceAccessException {
695         
696         long start = System.currentTimeMillis();
697         DirContext JavaDoc ctx = null;
698         try {
699             ctx = getContext();
700         } catch ( ServiceConnectionFailedException e ) {
701             throw new ServiceAccessException(this, e);
702         }
703         Uri parentUri = uri.getParentUri();
704         String JavaDoc objectName = getObjectNameFromUri( uri );
705         
706         Vector JavaDoc parentBindings = new Vector JavaDoc();
707         Vector JavaDoc childBindings = new Vector JavaDoc();
708
709         // As long as this node isn't the root node create a parent binding.
710
// This doesn't appear to do anything, but just in case.
711
if ( !uri.toString().equals( "/" ) ) {
712             parentBindings.add( new ObjectNode.Binding( objectName, parentUri.toString() ) );
713         }
714         
715         SearchControls JavaDoc controls = new SearchControls JavaDoc();
716         controls.setSearchScope( searchScope );
717
718         // If the uri matches the scope create a SubjectNode with bindings for all
719
// of the results from a jndi search
720
if ( uri.isStoreRoot() ) {
721             
722             try {
723                 NamingEnumeration JavaDoc results = ctx.search(
724                     container,
725                     filter,
726                     controls );
727
728                 if ( !results.hasMore() ) {
729                     getLogger().log(
730                         name + ": No objects found in container " + container +
731                         " that match filter " + filter + ".",
732                         LOG_CHANNEL,
733                         Logger.WARNING );
734                 }
735                 while ( results.hasMore() ) {
736                     SearchResult JavaDoc result = null;
737                     try {
738                         result = (SearchResult JavaDoc)results.next();
739                     } catch ( NamingException JavaDoc e ) {
740                         getLogger().log(
741                             name + ": Error getting next search result.",
742                             e, LOG_CHANNEL, Logger.ERROR );
743                     }
744                     String JavaDoc name = result.getName();
745                     if ( !validatePathName( name ) ) {
746                         continue;
747                     }
748                     String JavaDoc value = parseLdapName(name);
749                     if (principalNameAttribute != null) {
750                         String JavaDoc uriValue = ((String JavaDoc)result.getAttributes().get(principalNameAttribute).get()).toLowerCase();
751                         objectNameMap.put(uriValue, value);
752                         value = uriValue;
753                     }
754                     
755                     getLogger().log(
756                         name + ": Creating child binding \"" + value + "\" for \"" +
757                         uri.toString() + "\".",
758                         LOG_CHANNEL, Logger.DEBUG );
759                     
760                     childBindings.add(
761                         new ObjectNode.Binding( value, uri.toString() + "/" + value ) );
762
763                 }
764             } catch ( NamingException JavaDoc e ) {
765                 getLogger().log(
766                     name + ": Error during search.",
767                     e, LOG_CHANNEL, Logger.ERROR );
768             }
769         } else {
770             // If the uri matches the scope + something try to do a lookup
771
// of the "+ something" in LDAP.
772

773             try {
774                 if (principalNameAttribute != null && objectNameMap.get(objectName) == null)
775                     retrieveObject(parentUri);
776                 NamingEnumeration JavaDoc results = ctx.search(
777                     container,
778                     rdnAttribute + "=" + (principalNameAttribute != null ? (String JavaDoc)objectNameMap.get(objectName) : objectName),
779                     controls);
780
781                 if ( !results.hasMore() ) {
782                     if (ctx != null) {
783                         closeContext(ctx);
784                     }
785                     throw new ObjectNotFoundException( uri );
786                 }
787             } catch ( NamingException JavaDoc e ) {
788                 getLogger().log(
789                     name + ": Error retrieving " + uri.toString(),
790                     e, LOG_CHANNEL, Logger.ERROR );
791                 if (ctx != null) {
792                     closeContext(ctx);
793                 }
794                 throw new ServiceAccessException( this, e );
795             }
796         }
797
798         getLogger().log( name + ": Creating SubjectNode for \"" + uri.toString() + "\".",
799             LOG_CHANNEL, Logger.DEBUG );
800         
801         SubjectNode node = new SubjectNode(
802             uri.toString(), childBindings, parentBindings, new Vector JavaDoc() );
803         // Workaround for bug in ObjectNode.validate()
804
node.setUri( uri.toString() );
805
806         if ( cache != null ) {
807             getLogger().log(
808                 name + ": Putting ObjectNode for " + uri.toString() + " to cache.",
809                 LOG_CHANNEL,
810                 Logger.DEBUG );
811             Element cachedNode = new Element( CACHE_OBJECT_PREFIX + uri.toString(), node );
812             cache.put(cachedNode);
813         }
814         
815         long elapsed = System.currentTimeMillis() - start;
816         if ( elapsed > refreshThreshold ) {
817             addRefreshee( uri, Refreshee.REFRESH_OBJECT );
818         }
819         if (ctx != null) {
820             closeContext(ctx);
821         }
822         return node;
823     }
824
825     protected NodeRevisionDescriptor getRevisionDescriptor( Uri uri )
826             throws RevisionDescriptorNotFoundException, ServiceAccessException {
827         
828         long start = System.currentTimeMillis();
829         DirContext JavaDoc ctx = null;
830         try {
831             ctx = getContext();
832         } catch ( ServiceConnectionFailedException e ) {
833             throw new ServiceAccessException(this, e);
834         }
835         
836         String JavaDoc objectName = getObjectNameFromUri( uri );
837         
838         Hashtable JavaDoc props = new Hashtable JavaDoc();
839         
840         String JavaDoc resourceType = "<collection/>";
841         if ( !uri.isStoreRoot() ) {
842             resourceType += "<principal/>";
843         }
844         props.put(
845             "DAV:resourcetype",
846             new NodeProperty( "resourcetype", resourceType, "DAV:", "", false ) );
847         props.put(
848             "DAV:displayname",
849             new NodeProperty( "displayname", (!uri.isStoreRoot() && principalNameAttribute != null?(String JavaDoc)objectNameMap.get(objectName):objectName), "DAV:", "", false ) );
850         
851         // The storeRoot isn't a real object so it doesn't have any parameters to look up
852
if ( !uri.isStoreRoot() ) {
853                         
854             String JavaDoc localFilter = rdnAttribute + "=" + (principalNameAttribute != null?(String JavaDoc)objectNameMap.get(objectName):objectName);
855             
856             SearchControls JavaDoc controls = new SearchControls JavaDoc();
857             controls.setSearchScope( searchScope );
858             controls.setReturningAttributes( descriptorAttributes );
859             
860             try {
861                 NamingEnumeration JavaDoc results = ctx.search(
862                     container,
863                     localFilter,
864                     controls );
865
866                 if ( !results.hasMore() ) {
867                     if (ctx != null) {
868                         closeContext(ctx);
869                     }
870                     throw new RevisionDescriptorNotFoundException( uri.toString() );
871                 }
872                 while ( results.hasMore() ) {
873                     SearchResult JavaDoc result = null;
874                     try {
875                         result = (SearchResult JavaDoc)results.next();
876                     } catch ( NamingException JavaDoc e ) {
877                         getLogger().log(
878                             name + ": Error getting search result with filter: " + localFilter +
879                             " from container: " + container + ".",
880                             LOG_CHANNEL, Logger.ERROR );
881                         if (ctx != null) {
882                             closeContext(ctx);
883                         }
884                         throw new ServiceAccessException( this, e );
885                     }
886                     
887                     NamingEnumeration JavaDoc attributes = result.getAttributes().getAll();
888                     while ( attributes.hasMore() ) {
889                         Attribute JavaDoc attribute = (Attribute JavaDoc)attributes.next();
890                         StringBuffer JavaDoc valueString = new StringBuffer JavaDoc();
891                         boolean isGms = attribute.getID().equals( groupMemberSet );
892                         boolean isMva = attribute.size() > 1;
893                         for ( int i = 0; i < attribute.size(); i++ ) {
894                             try {
895                                 Object JavaDoc value = attribute.get( i );
896                                 if ( !( value instanceof String JavaDoc ) ) {
897                                     getLogger().log(
898                                         name + ": Non-string value found for " +
899                                         attribute.getID() + ".",
900                                         LOG_CHANNEL,
901                                         Logger.DEBUG );
902                                     continue;
903                                 }
904                                 if ( isGms ) {
905                                     valueString.append( "<D:href xmlns:D='DAV:'>" );
906                                     valueString.append( usersPath ).append( "/" );
907                                     String JavaDoc name = parseLdapName(value.toString());
908                                     if (principalNameAttribute != null) {
909                                         // lookup LDAP user entry
910
controls.setReturningAttributes(new String JavaDoc[] { principalNameAttribute });
911                                         NamingEnumeration JavaDoc roleResults =
912                                             ctx.search(container, rdnAttribute + "=" + name, controls);
913                                         if (roleResults.hasMore()) {
914                                             SearchResult JavaDoc userObject = (SearchResult JavaDoc)roleResults.next();
915                                             name = ((String JavaDoc)userObject.getAttributes().get(principalNameAttribute).get()).toLowerCase();
916                                         }
917                                     }
918                                     valueString.append(name);
919                                     valueString.append( "</D:href>" );
920                                 } else {
921                                     if ( isMva ) {
922                                         valueString.append( "<mva xmlns=\"" )
923                                             .append( LDAP_NAMESPACE ).append( "\">" );
924                                         valueString.append( value.toString() );
925                                         valueString.append( "</mva>" );
926                                     } else {
927                                         valueString.append( value.toString() );
928                                     }
929                                 }
930                             } catch ( NamingException JavaDoc e ) {
931                                 getLogger().log(
932                                     name + ": Error fetching next attribute value for attribute " +
933                                         attribute.getID() + ".",
934                                     e, LOG_CHANNEL, Logger.DEBUG );
935                             }
936                         }
937                         
938                         if ( isGms ) {
939                             getLogger().log(
940                                 name + ": Adding property \"group-member-set\" in namespace " +
941                                 "\"DAV:\" with value of \"" + valueString.toString() + "\" to " +
942                                 uri.toString() + ".",
943                                 LOG_CHANNEL, Logger.DEBUG );
944                             
945                             props.put(
946                                 "DAV:group-member-set",
947                                 new NodeProperty(
948                                     "group-member-set",
949                                     valueString.toString(),
950                                     "DAV:" ) );
951                             
952                         } else {
953                             getLogger().log(
954                                 name + ": Adding property \"" + attribute.getID() +
955                                 "\" in namespace \"" + LDAP_NAMESPACE + "\" " +
956                                 "with value of \"" +
957                                 valueString.toString() + "\" to " + uri.toString() + ".",
958                                 LOG_CHANNEL, Logger.DEBUG );
959                             
960                             props.put(
961                                 LDAP_NAMESPACE + attribute.getID(),
962                                 new NodeProperty(
963                                     attribute.getID(),
964                                     valueString.toString(),
965                                     LDAP_NAMESPACE ) );
966                         }
967                     }
968                 }
969             } catch ( NamingException JavaDoc e ) {
970                 getLogger().log(
971                     name + ": Error during search.",
972                     e, LOG_CHANNEL, Logger.ERROR );
973             }
974         }
975
976         NodeRevisionDescriptor descriptor = new NodeRevisionDescriptor(
977             new NodeRevisionNumber( 1, 0 ),
978             "main",
979             new Vector JavaDoc(),
980             props );
981         
982         if ( cache != null ) {
983             getLogger().log(
984                 name + ": Putting NodeRevisionDescriptor for " + uri.toString() + " to cache.",
985                 LOG_CHANNEL,
986                 Logger.DEBUG );
987             Element cachedDescriptor = new Element(
988                 CACHE_DESCRIPTOR_PREFIX + uri.toString(), descriptor );
989             cache.put(cachedDescriptor);
990         }
991
992         long elapsed = System.currentTimeMillis() - start;
993         if ( elapsed > refreshThreshold ) {
994             addRefreshee( uri, Refreshee.REFRESH_DESCRIPTOR );
995         }
996         if (ctx != null) {
997             closeContext(ctx);
998         }
999         return descriptor;
1000    }
1001    
1002    // --------------------------------------------------- Helper Methods ---------------
1003

1004    /**
1005     * Closes a JNDI connection.
1006     * @param ctx the Context to close
1007     */

1008    private void closeContext(DirContext JavaDoc ctx) {
1009        getLogger().log( name + ": Disconnecting from LDAP server.", LOG_CHANNEL, Logger.DEBUG );
1010        try {
1011            ctx.close();
1012        } catch ( NamingException JavaDoc e ) {
1013            getLogger().log( name + ": Error disconnecting from LDAP",
1014                e, LOG_CHANNEL, Logger.WARNING );
1015            ctx = null;
1016        }
1017    }
1018    
1019    /**
1020     * Gets a JNDI Context using the connection parameters specified for
1021     * this Store in the Domain.
1022     * @throws ServiceConnectionFailedException
1023     */

1024    private DirContext JavaDoc getContext() throws ServiceConnectionFailedException {
1025        getLogger().log( name + ": Connecting to LDAP server.", LOG_CHANNEL, Logger.DEBUG );
1026        try {
1027            DirContext JavaDoc ctx = new InitialDirContext JavaDoc( ctxParameters );
1028            if ( ctx != null ) {
1029                return ctx;
1030            } else {
1031                throw new ServiceConnectionFailedException(
1032                    this, "Invalid JNDI connection parameters." );
1033            }
1034        } catch ( NamingException JavaDoc e ) {
1035            getLogger().log( name + ": Error Connecting to LDAP Server",
1036                e,
1037                LOG_CHANNEL,
1038                Logger.CRITICAL );
1039            throw new ServiceConnectionFailedException( this, e );
1040        }
1041    }
1042    
1043    protected String JavaDoc getObjectNameFromUri( Uri uri ) {
1044        String JavaDoc objectName = uri.toString().substring(
1045            uri.toString().lastIndexOf( "/" ) + 1 );
1046        return objectName.toLowerCase();
1047    }
1048    
1049    protected String JavaDoc parseLdapName( String JavaDoc name ) {
1050
1051        // Since attribute values can contain pretty much anything, parsing
1052
// name to get the attribute value isn't terribly accurate.
1053
// The slow way is to find a value for the attribute that matches the
1054
// results from getName(), but that is horribly horribly slow.
1055
// On the assumption that "," is more likely to be in the value than
1056
// "=", this should work most of the time and be faster... I hope.
1057

1058        if (name.equals("")) return name;
1059
1060        int firstEqual = name.indexOf("=");
1061        if ( firstEqual < 0 ) {
1062            firstEqual = 0;
1063        }
1064        
1065        int secondEqual = name.substring( firstEqual + 1 ).indexOf( "=" );
1066        if ( secondEqual < 0 ) {
1067            secondEqual = name.length() - 1;
1068        } else {
1069            secondEqual = secondEqual + firstEqual + 1;
1070        }
1071        
1072        int end = name.substring( 0, secondEqual ).lastIndexOf( "," );
1073        if ( end < 0 ) {
1074            end = name.length();
1075        }
1076        
1077        String JavaDoc value = name.substring( firstEqual + 1, end ).toLowerCase();
1078        return value;
1079        
1080    }
1081
1082    /**
1083     * Checks a String to make sure it is a valid path element.
1084     *
1085     * @param name the name to validate
1086     * @return false if any of the validation checks failed.
1087     */

1088    protected boolean validatePathName( String JavaDoc name ) {
1089        boolean valid = true;
1090        if ( name.indexOf("/") > -1 ) {
1091            // Skip names that contain "/". They're evil!! (and they break things)
1092
valid = false;
1093            if ( logValidationErrors ) {
1094                getLogger().log(
1095                    name + ": Skipping child with name \"" + name + "\" because " +
1096                    "it contains a /.",
1097                    LOG_CHANNEL, Logger.ERROR );
1098            }
1099        }
1100        return valid;
1101    }
1102    
1103    // ----------------------------------------------- Cache Methods/Classes ---------------
1104

1105    protected synchronized void addRefreshee( Uri uri, int refreshType ) {
1106        getLogger().log(
1107            name + ": Adding refreshee for \"" + uri.toString() + "\" of type \"" +
1108            (refreshType == Refreshee.REFRESH_OBJECT ? "object" : "descriptor") + "\".",
1109            LOG_CHANNEL,
1110            Logger.DEBUG );
1111        
1112        refreshList.add(
1113            new Refreshee( uri, System.currentTimeMillis() + refreshRate, refreshType ) );
1114    }
1115    
1116    protected Cache getCache() {
1117        CacheManager cacheManager = null;
1118        Cache cache = null;
1119        try {
1120            cacheManager = CacheManagerFactory.getDefaultCacheManager();
1121        } catch ( CacheException e ) {
1122            getLogger().log(
1123                name + ": Error getting default CacheManager.",
1124                e,
1125                LOG_CHANNEL,
1126                Logger.ERROR );
1127            return null;
1128        }
1129        cache = cacheManager.getCache( CACHE_NAME );
1130        if ( cache == null ) {
1131            cache = new Cache(
1132                CACHE_NAME,
1133                DEFAULT_CACHE_SIZE,
1134                DEFAULT_CACHE_OVERFLOW_TO_DISK,
1135                DEFAULT_CACHE_ETERNAL,
1136                DEFAULT_CACHE_TTL,
1137                DEFAULT_CACHE_TTI );
1138            try {
1139                cacheManager.addCache(cache);
1140            } catch ( IllegalStateException JavaDoc e ) {
1141                getLogger().log(
1142                    name + ": Error adding cache \"" + CACHE_NAME + "\" to CacheManager.",
1143                    e,
1144                    LOG_CHANNEL,
1145                    Logger.ERROR);
1146            } catch ( ObjectExistsException e ) {
1147                getLogger().log(
1148                    name + ": Error adding cache \"" + CACHE_NAME + "\" to CacheManager.",
1149                    e,
1150                    LOG_CHANNEL,
1151                    Logger.ERROR);
1152            } catch ( CacheException e ) {
1153                getLogger().log(
1154                    name + ": Error adding cache \"" + CACHE_NAME + "\" to CacheManager.",
1155                    e,
1156                    LOG_CHANNEL,
1157                    Logger.ERROR);
1158            }
1159        }
1160        return cache;
1161    }
1162    
1163    protected synchronized Refreshee getNextRefreshee() {
1164        Refreshee refreshee = null;
1165        try {
1166            refreshee = (Refreshee)refreshList.last();
1167        } catch ( NoSuchElementException JavaDoc e ) {
1168            // expected when the list is emtpy
1169
}
1170        return refreshee;
1171    }
1172    
1173    protected void refreshCache() {
1174        Refreshee oldest = getNextRefreshee();
1175        long now = System.currentTimeMillis();
1176        while ( oldest != null && oldest.getRefreshTime() < now ) {
1177            getLogger().log(
1178                name + ": Refreshing cache for \"" + oldest.getUri().toString() + "\" of type \"" +
1179                (oldest.getRefreshType() == Refreshee.REFRESH_OBJECT ? "object" : "descriptor") +
1180                "\".",
1181                LOG_CHANNEL,
1182                Logger.DEBUG );
1183            
1184            removeRefreshee( oldest );
1185            try {
1186                Cache cache = getCache();
1187                if ( cache != null ) {
1188                    switch( oldest.getRefreshType() ) {
1189                        case Refreshee.REFRESH_OBJECT:
1190                            getObject( oldest.getUri() );
1191                            break;
1192                        case Refreshee.REFRESH_DESCRIPTOR:
1193                            getRevisionDescriptor( oldest.getUri() );
1194                            break;
1195                    }
1196                }
1197            } catch ( ObjectNotFoundException e ) {
1198                getLogger().log(
1199                    name + ": Error refreshing cache for \"" + oldest.getUri().toString() + "\".",
1200                    e,
1201                    LOG_CHANNEL,
1202                    Logger.ERROR );
1203            } catch ( ServiceAccessException e ) {
1204                getLogger().log(
1205                    name + ": Error refreshing cache for \"" + oldest.getUri().toString() + "\".",
1206                    e,
1207                    LOG_CHANNEL,
1208                    Logger.ERROR );
1209            } catch ( RevisionDescriptorNotFoundException e ) {
1210                getLogger().log(
1211                    name + ": Error refreshing cache for \"" + oldest.getUri().toString() + "\".",
1212                    e,
1213                    LOG_CHANNEL,
1214                    Logger.ERROR );
1215            }
1216            oldest = getNextRefreshee();
1217        }
1218    }
1219    
1220    protected synchronized void removeRefreshee( Refreshee refreshee ) {
1221        refreshList.remove( refreshee );
1222    }
1223    
1224    /**
1225     * Implements Comparable so that the older the object, the sooner it needs to be refreshed,
1226     * and therefor the "greater" it is. When used with a SortedSet this ensures objects are
1227     * refreshed in the proper order.
1228     *
1229     */

1230    protected class Refreshee implements Comparable JavaDoc {
1231        
1232        public static final int REFRESH_OBJECT = 0;
1233        public static final int REFRESH_DESCRIPTOR = 1;
1234        
1235        private long refreshTime;
1236        private Uri uri;
1237        private int refreshType;
1238        
1239        // No default constructor
1240
private Refreshee() {}
1241        
1242        public Refreshee( Uri uri, long refreshTime, int refreshType ) {
1243            this.refreshTime = refreshTime;
1244            this.uri = uri;
1245            this.refreshType = refreshType;
1246        }
1247        
1248        public int compareTo( Object JavaDoc object ) {
1249            if ( object instanceof Refreshee ) {
1250                Refreshee other = (Refreshee)object;
1251                int result = compare( this.getRefreshTime(), other.getRefreshTime() );
1252                if ( result != 0 ) {
1253                    return result;
1254                }
1255            }
1256            return compare( hashCode(), object.hashCode() );
1257        }
1258        
1259        public long getRefreshTime() {
1260            return refreshTime;
1261        }
1262        
1263        public int getRefreshType() {
1264            return refreshType;
1265        }
1266        
1267        public Uri getUri() {
1268            return uri;
1269        }
1270        
1271        private int compare( long first, long second ) {
1272            if (first < second) {
1273                return 1;
1274            }
1275            if (second < first) {
1276                return -1;
1277            }
1278            return 0;
1279        }
1280    }
1281    
1282    protected class RefreshThread extends Thread JavaDoc {
1283        
1284        private boolean run;
1285        
1286        public RefreshThread() {
1287            super( "RefreshThread-" + name );
1288            setPriority( Thread.MIN_PRIORITY );
1289            setDaemon( false );
1290        }
1291        
1292        public synchronized void halt() {
1293            run = false;
1294        }
1295        
1296        public void run() {
1297            run = true;
1298            while ( keepRunning() ) {
1299                try {
1300                    Thread.sleep( refreshThreadSleepTime );
1301                } catch ( InterruptedException JavaDoc e ) {}
1302                refreshCache();
1303            }
1304        }
1305        
1306        private synchronized boolean keepRunning() {
1307            return run;
1308        }
1309    }
1310
1311}
Popular Tags