ASF Bugzilla – Attachment 17112 Details for
Bug 37748
JNDIPrincipalStore that uses J2EE Container
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
JndiPrincipalStore
JNDIPrincipalStore.java (text/plain), 36.86 KB, created by
Damien Bastin
on 2005-12-02 03:04:05 UTC
(
hide
)
Description:
JndiPrincipalStore
Filename:
MIME Type:
Creator:
Damien Bastin
Created:
2005-12-02 03:04:05 UTC
Size:
36.86 KB
patch
obsolete
>/* > * $Header: /cvs/projects/easydoc-slide/src/stores/org/apache/slide/store/txjndi/JNDIPrincipalStore.java,v 1.2 2005/10/07 05:53:15 cruise Exp $ > * $Revision: 1.2 $ > * $Date: 2005/10/07 05:53:15 $ > * > * ==================================================================== > * > * Copyright 1999-2004 The Apache Software Foundation > * > * Licensed under the Apache License, Version 2.0 (the "License"); > * you may not use this file except in compliance with the License. > * You may obtain a copy of the License at > * > * http://www.apache.org/licenses/LICENSE-2.0 > * > * Unless required by applicable law or agreed to in writing, software > * distributed under the License is distributed on an "AS IS" BASIS, > * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > * See the License for the specific language governing permissions and > * limitations under the License. > * > */ > >package org.apache.slide.store.txjndi; > >import net.sf.ehcache.Cache; >import net.sf.ehcache.CacheException; >import net.sf.ehcache.CacheManager; >import net.sf.ehcache.Element; >import net.sf.ehcache.ObjectExistsException; >import org.apache.slide.common.ServiceAccessException; >import org.apache.slide.common.ServiceConnectionFailedException; >import org.apache.slide.common.ServiceDisconnectionFailedException; >import org.apache.slide.common.ServiceParameterErrorException; >import org.apache.slide.common.ServiceParameterMissingException; >import org.apache.slide.common.Uri; >import org.apache.slide.content.NodeProperty; >import org.apache.slide.content.NodeRevisionDescriptor; >import org.apache.slide.content.NodeRevisionNumber; >import org.apache.slide.content.RevisionDescriptorNotFoundException; >import org.apache.slide.structure.ObjectNode; >import org.apache.slide.structure.ObjectNotFoundException; >import org.apache.slide.structure.SubjectNode; >import org.apache.slide.util.CacheManagerFactory; >import org.apache.slide.util.logger.Logger; > >import javax.naming.NamingEnumeration; >import javax.naming.NamingException; >import javax.naming.directory.Attribute; >import javax.naming.directory.DirContext; >import javax.naming.directory.InitialDirContext; >import javax.naming.directory.SearchControls; >import javax.naming.directory.SearchResult; >import java.util.ArrayList; >import java.util.HashMap; >import java.util.Hashtable; >import java.util.Iterator; >import java.util.Map; >import java.util.NoSuchElementException; >import java.util.StringTokenizer; >import java.util.TreeSet; >import java.util.Vector; >import java.util.List; > >/** > * <p> > * This is a read-only Store implementation for retrieving Slide users > * and roles from an LDAP server. It has been tested with Novell's eDirectory > * version 8.6.2. Other LDAP servers should work. > * </p> > * > * <h3>Prerequisites</h3> > * <p> > * To use this Store your app server must be setup to authenticate > * users using the LDAP server. For Tomcat 5 see > * <a href="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/realm-howto.html#JNDIRealm">this</a>. > * You cannot use the SlideRealm to authenticate users because this Store > * does not expose a <span style="font-style: italic;">password</span> > * property. > * </p> > * > * <h3>Store Parameters</h3> > * <p> > * Parameters used in Domain.xml when setting up the Store. > * </p> > * <dl> > * <dt>log.validationerrors</dt> > * <dd> > * Whether validation errors will be logged. The default value is > * <em>false</em>. > * </dd> > * > * <dt>cache.name</dt> > * <dd> > * The name of the EHCache cache that should be used for this Store. If more > * than one JNDIPrincipalStore is used in a single Namespace this parameter > * can be used to make each one use a different cache. The default value is > * <em>org.apache.slide.store.txjndi.JNDIPrincipalStore</em>. > * See <a href="#cacherefreshing">Caching</a> for more information. > * </dd> > * > * <dt>cache.refresh.checkrate</dt> > * <dd> > * How often, in <em>seconds</em>, the cache refresh thread should check for Uris in the cache > * that need to be refreshed. The default value is "15". > * See <a href="#cacherefreshing">Cache Refreshing</a> for more information. > * </dd> > * > * <dt>cache.refresh.rate</dt> > * <dd> > * How frequently, in <em>seconds</em>, Uris that are marked for refreshing should be refreshed. > * This value must be less than the TimeToLive and TimeToIdle (whichever is least) parameters > * specified for the EHCache in order for the items to never expire. The default value is "800". > * See <a href="#caching">Caching</a> and <a href="#cacherefreshing">Cache Refreshing</a> for > * more information. > * </dd> > * > * <dt>cache.refresh.threshold</dt> > * <dd> > * The maximum amount of time, in <em>milliseconds</em>, that retrieve* methods can take before > * the Uri they are retrieving is scheduled for refreshing. By tuning this parameter you can keep > * smaller, infrequently accessed Uris from perpetually remaining in the cache. This may improve > * cache performance. > * See <a href="#cacherefreshing">Cache Refreshing</a> for more information. > * </dd> > * > * <dt>jndi.container</dt> > * <dd> > * The base LDAP context you wish to search. Example: <em>ou=Users,o=Company</em> > * </dd> > * > * <dt>jndi.preload</dt> > * <dd> > * Indicates if users/roles should be loaded and cached during initialization. Default is <em>true</em>. > * </dd> > * > * <dt>jndi.attributes.rdn</dt> > * <dd> > * The attribute used to uniquely identify the objects you're fetching. Usually uid or cn. > * </dd> > * > * <dt>jndi.attributes.userprincipalname</dt> > * <dd> > * The attribute used to provide a user/role name which is mapped into Slide instead of the > * path name. This attribute is optional. > * </dd> > * > * <dt>jndi.search.filter</dt> > * <dd> > * The filter string to use for the search. Example: <em>(objectClass=inetOrgPerson)</em>. > * The default value is <em>(objectClass=*)</em>. > * 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. > * </dd> > * > * <dt>jndi.search.scope</dt> > * <dd> > * The Scope of the search. Can be one of <em>OBJECT_SCOPE</em>, <em>ONELEVEL_SCOPE</em>, > * <em>SUBTREE_SCOPE</em>. The default value is <em>ONELEVEL_SCOPE</em>. > * See the <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/directory/SearchControls.html#OBJECT_SCOPE">SearchControls</a> javadoc. > * </dd> > * > * <dt>jndi.search.attributes</dt> > * <dd> > * A comma delimited list of the attributes you want returned with your search results. > * Example: <em>givenName, uid, mail</em>. The default value is <em>cn</em>. > * </dd> > * > * <dt>java.naming.*</dt> > * <dd> > * Parameters for connecting to the LDAP server. > * See the <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/InitialContext.html">InitialContext</a> javadoc. > * </dd> > * </dl> > * > * <h3><a name="caching">Caching</a></h3> > * <p> > * This Store makes use of <a href="http://ehcache.sourceforge.net/">EHCache</a>. > * When initialized the default CacheManager is used to find the Cache named > * in the <em>cache.name</em> parameter. If there is no Cache found with this > * name then a Cache is created with these default values: > * </p> > * <ul> > * <li>name = org.apache.slide.store.txjndi.JNDIPrincipalStore</li> > * <li>maxElementsInMemory = 200</li> > * <li>eternal = false</li> > * <li>timeToIdleSeconds = 900</li> > * <li>timeToLiveSeconds = 900</li> > * <li>overflowToDisk = true</li> > * </ul> > * <p> > * To override these values you will need to create a configuration file for EHCache with > * the cache named by the <em>cache.name</em> parameter that has the settings you > * wish. See the documentation at the <a href="http://ehcache.sourceforge.net/">EHCache website</a> > * for instructions. > * </p> > * > * <h3><a name="cacherefreshing">Cache Refreshing</a></h3> > * <p> > * Because the data delivered by this Store is managed externally to Slide the data cache must be > * periodically expired to pick up any changes. Because creating object Nodes can take a long time > * for LDAP queries that return a lot of objects, larger queries need to be preemptively refreshed > * before a user makes a request to an expired object. > * </p> > * <p> > * The retrieveObject() and retrieveNodeDescriptor() methods monitor the amount of time it takes > * them to return for each Uri. If the method takes longer than a specified amount of time > * (configured with the <em>cache.refresh.threshold</em> parameter) the Uri is marked as needing > * to be refreshed. Upon initialization the Store spawns a child thread that periodically checks > * for Uris that need to be refreshed. > * </p> > * > * <h3>TODO:</h3> > * <ol> > * <li> > * I'd like to see this implemented as a ResourceManager rather than > * a stand-alone Store. I think it would fit into Slide's framework better > * that way and mean less duplicated code. > * </li> > * <li> > * I think there's still room for a full-fledged LDAP store. The way > * LDAP exposes a directory as a graph-of-objects-with-properties and > * Slide exposes a repository as a graph-of-objects-with-properties seems > * very similar to me ;). However, adapting the structure of most LDAP > * servers to the user/role structure that Slide uses would be a bit of a > * pain, so I don't think this kind of Store would be useful for > * users/roles in Slide. I have heard of people using LDAP to keep track > * of server inventories and things like that, though, and I think it > * would work well there. > * </li> > * </ol> > * > */ >public class JNDIPrincipalStore extends AbstractPrincipalStore { > > public static final String LOG_CHANNEL = JNDIPrincipalStore.class.getName(); > > public static final String CACHE_OBJECT_PREFIX = "object: "; > public static final String CACHE_DESCRIPTOR_PREFIX = "descriptor: "; > > public static final String JNDI_PROPERTY_PREFIX = "java.naming"; > > // Parameter keys > public static final String PARAM_CACHE_NAME = "cache.name"; > public static final String PARAM_CACHE_REFRESH_CHECK_RATE = "cache.refresh.checkrate"; > public static final String PARAM_CACHE_REFRESH_RATE = "cache.refresh.rate"; > public static final String PARAM_CACHE_REFRESH_THRESHOLD = "cache.refresh.threshold"; > > public static final String PARAM_JNDI_CONTAINER = "jndi.container"; > public static final String PARAM_JNDI_PRELOAD = "jndi.preload"; > public static final String PARAM_JNDI_FILTER = "jndi.search.filter"; > public static final String PARAM_JNDI_GROUPMEMBERSET = "jndi.attributes.groupmemberset"; > public static final String PARAM_JNDI_RDN_ATTRIBUTE = "jndi.attributes.rdn"; > public static final String PARAM_JNDI_SEARCH_ATTRIBUTES = "jndi.search.attributes"; > public static final String PARAM_JNDI_SEARCH_SCOPE = "jndi.search.scope"; > public static final String PARAM_JNDI_USERPRINCIPALNAME = "jndi.attributes.userprincipalname"; > > // Default values > public static final String DEFAULT_CACHE_NAME = JNDIPrincipalStore.class.getName(); > public static final int DEFAULT_CACHE_SIZE = 200; > public static final boolean DEFAULT_CACHE_OVERFLOW_TO_DISK = true; > public static final boolean DEFAULT_CACHE_ETERNAL = false; > public static final long DEFAULT_CACHE_TTL = 900L; // seconds > public static final long DEFAULT_CACHE_TTI = 900L; // seconds > public static final long DEFAULT_CACHE_REFRESH_CHECK_RATE = 15L; // seconds > public static final long DEFAULT_CACHE_REFRESH_RATE = 800L; // seconds > public static final long DEFAULT_CACHE_REFRESH_THRESHOLD = 200L; // milliseconds!! > > public static final String DEFAULT_JNDI_SEARCH_ATTRIBUTES = "cn"; > public static final String DEFAULT_JNDI_FILTER = "(objectClass=*)"; > > protected Hashtable ctxParameters; > > protected boolean isConnected = false; > > protected Cache cache; > protected TreeSet refreshList; > protected long refreshRate = DEFAULT_CACHE_REFRESH_RATE; > protected long refreshThreadSleepTime = DEFAULT_CACHE_REFRESH_CHECK_RATE; > protected long refreshThreshold = DEFAULT_CACHE_REFRESH_THRESHOLD; > protected RefreshThread refresher; > > protected String container; > protected String groupMemberSet; > protected String[] descriptorAttributes; > protected String filter; > protected String rdnAttribute; > protected boolean preload = true; > protected int searchScope; > protected String principalNameAttribute; > > private String cacheName = DEFAULT_CACHE_NAME; > private String name; > private Map objectNameMap; // Uri-String -> LDAP lookup name > > > public JNDIPrincipalStore() { > ctxParameters = new Hashtable(); > name = ""; > refreshList = new TreeSet(); > refresher = new RefreshThread(); > objectNameMap = new HashMap(); > } > > // ----------------------------------------------------------- Service Methods -------- > > public void setParameters( Hashtable parameters ) > throws ServiceParameterErrorException, > ServiceParameterMissingException { > > super.setParameters(parameters); > String temp; > > // Set ctxParameters > Iterator keys = parameters.keySet().iterator(); > while ( keys.hasNext() ) { > temp = (String)keys.next(); > if ( temp.startsWith( JNDI_PROPERTY_PREFIX ) ) { > ctxParameters.put( temp, parameters.get( temp ) ); > } > } > > // Set container > container = (String)parameters.get( PARAM_JNDI_CONTAINER ); > if ( container == null || container.length() == 0 ) { > getLogger().log( > "Error during " + name + " setup: No value set for " + > PARAM_JNDI_CONTAINER, > LOG_CHANNEL, > Logger.CRITICAL ); > } > > // Set preload > temp = (String)parameters.get( PARAM_JNDI_PRELOAD ); > if ( temp != null || temp.length() > 0 ) { > preload = !("no".equalsIgnoreCase(temp) || > "false".equalsIgnoreCase(temp) || > "off".equalsIgnoreCase(temp)); > } > > // Set filter > filter = (String)parameters.get( PARAM_JNDI_FILTER ); > if ( filter == null || filter.length() == 0 ) { > filter = DEFAULT_JNDI_FILTER; > } > > // Set rdnAttribute > rdnAttribute = (String)parameters.get( PARAM_JNDI_RDN_ATTRIBUTE ); > if ( rdnAttribute == null || rdnAttribute.length() == 0 ) { > getLogger().log( > "Error during " + name + " setup: No value set for " + > PARAM_JNDI_RDN_ATTRIBUTE, > LOG_CHANNEL, > Logger.CRITICAL ); > } > > // Set searchScope > temp = (String)parameters.get( PARAM_JNDI_SEARCH_SCOPE ); > if ( "OBJECT_SCOPE".equalsIgnoreCase( temp ) ) { > searchScope = SearchControls.OBJECT_SCOPE; > } else if ( "ONELEVEL_SCOPE".equalsIgnoreCase( temp ) ) { > searchScope = SearchControls.ONELEVEL_SCOPE; > } else if ( "SUBTREE_SCOPE".equalsIgnoreCase( temp ) ) { > searchScope = SearchControls.SUBTREE_SCOPE; > } else { > searchScope = SearchControls.ONELEVEL_SCOPE; > } > > // Set descriptorAttributes and groupMemberSet > temp = (String)parameters.get( PARAM_JNDI_SEARCH_ATTRIBUTES ); > if ( temp == null || temp.length() == 0 ) { > temp = DEFAULT_JNDI_SEARCH_ATTRIBUTES; > } > ArrayList searchAttributesList = new ArrayList(); > StringTokenizer tok = new StringTokenizer( temp, "," ); > while ( tok.hasMoreTokens() ) { > searchAttributesList.add( tok.nextToken().trim() ); > } > String gms = (String)parameters.get( PARAM_JNDI_GROUPMEMBERSET ); > if ( gms != null ) { > searchAttributesList.add( gms ); > groupMemberSet = gms; > } else { > groupMemberSet = ""; > } > descriptorAttributes = (String[])searchAttributesList.toArray( new String[0] ); > > // Set refreshRate > temp = (String)parameters.get( PARAM_CACHE_REFRESH_RATE ); > if ( temp != null ) { > refreshRate = Long.parseLong( temp ) * 1000L; > } else { > refreshRate = DEFAULT_CACHE_REFRESH_RATE * 1000L; > } > > // Set refreshThreadSleepTime > temp = (String)parameters.get( PARAM_CACHE_REFRESH_CHECK_RATE ); > if ( temp != null ) { > refreshThreadSleepTime = Long.parseLong( temp ) * 1000L; > } else { > refreshThreadSleepTime = DEFAULT_CACHE_REFRESH_CHECK_RATE * 1000L; > } > > // Set refreshThreshold > temp = (String)parameters.get( PARAM_CACHE_REFRESH_THRESHOLD ); > if ( temp != null ) { > refreshThreshold = Long.parseLong( temp ); > } else { > refreshThreshold = DEFAULT_CACHE_REFRESH_THRESHOLD; > } > > //Set attribute which contains the user principal name for authentication > principalNameAttribute = (String)parameters.get(PARAM_JNDI_USERPRINCIPALNAME); > > // Set cacheName > temp = (String)parameters.get( PARAM_CACHE_NAME ); > if ( temp != null ) { > cacheName = temp; > } else { > cacheName = DEFAULT_CACHE_NAME; > } > cache = getCache(); > } > > //------------------------------------------------------ NodeStore Methods ---------- > > public ObjectNode retrieveObject( Uri uri ) throws ServiceAccessException, > ObjectNotFoundException { > > getLogger().log( name + ": Retrieving Object " + uri.toString() + ".", > LOG_CHANNEL, Logger.DEBUG ); > > String cacheKey = CACHE_OBJECT_PREFIX + uri.toString(); > > Element cachedNode = null; > try { > if ( cache != null ) { > cachedNode = cache.get( cacheKey ); > } > } catch ( CacheException e ) { > getLogger().log( > name + ": Error while getting \"" + cacheKey + "\" from cache.", > e, > LOG_CHANNEL, > Logger.ERROR); > } > if ( cachedNode != null ) { > getLogger().log( > name + ": ObjectNode for \"" + uri.toString() + "\" found in cache.", > LOG_CHANNEL, > Logger.DEBUG ); > return (ObjectNode)cachedNode.getValue(); > } else { > return getObject( uri ); > } > > } > > // ------------------------------------------- RevisionDescriptorStore Methods -------- > > public NodeRevisionDescriptor retrieveRevisionDescriptor( Uri uri, > NodeRevisionNumber revisionNumber ) throws ServiceAccessException, > RevisionDescriptorNotFoundException { > getLogger().log( name + ": Retrieving Revision Descriptor for " + uri.toString() + ".", > LOG_CHANNEL, Logger.DEBUG ); > > String cacheKey = CACHE_DESCRIPTOR_PREFIX + uri.toString(); > > Element cachedDescriptor = null; > try { > if ( cache != null ) { > cachedDescriptor = cache.get( cacheKey ); > } > } catch ( CacheException e ) { > getLogger().log( > name + ": Error while getting \"" + cacheKey + "\" from cache.", > e, > LOG_CHANNEL, > Logger.ERROR); > } > if ( cachedDescriptor != null ) { > getLogger().log( > name + ": NodeRevisionDescriptor for \"" + uri.toString() + "\" found in cache.", > LOG_CHANNEL, > Logger.DEBUG ); > return (NodeRevisionDescriptor)cachedDescriptor.getValue(); > } else { > return getRevisionDescriptor( uri ); > } > > } > > // --------------------------------------------------- RevisionDescriptorsStore Methods ----- > > // --------------------------------------------------- XA Methods -------------- > > public void connect() throws ServiceConnectionFailedException { > if ( !refresher.isAlive() ) { > refresher.start(); > } > } > > public void disconnect() throws ServiceDisconnectionFailedException { > if ( refresher.isAlive() ) { > refresher.halt(); > } > } > > // --------------------------------------------------- ContentStore Methods ---------- > > // --------------------------------------------------- Security Store Methods --------------- > > // --------------------------------------------------- LockStore Methods --------------- > > // --------------------------------------------------- Worker Methods --------------- > > protected SubjectNode getObject( Uri uri ) > throws ObjectNotFoundException, ServiceAccessException { > > long start = System.currentTimeMillis(); > DirContext ctx; > try { > ctx = getContext(); > } catch ( ServiceConnectionFailedException e ) { > throw new ServiceAccessException(this, e); > } > Uri parentUri = uri.getParentUri(); > String objectName = getObjectNameFromUri( uri ); > > Vector parentBindings = new Vector(); > Vector childBindings = new Vector(); > > // As long as this node isn't the root node create a parent binding. > // This doesn't appear to do anything, but just in case. > if ( !uri.toString().equals( "/" ) ) { > parentBindings.add( new ObjectNode.Binding( objectName, parentUri.toString() ) ); > } > > SearchControls controls = new SearchControls(); > controls.setSearchScope( searchScope ); > > // If the uri matches the scope create a SubjectNode with bindings for all > // of the results from a jndi search > if ( uri.isStoreRoot() ) { > if ( preload ) { > try { > NamingEnumeration results = ctx.search( > container, > filter, > controls ); > > if ( !results.hasMore() ) { > getLogger().log( > name + ": No objects found in container " + container + > " that match filter " + filter + ".", > LOG_CHANNEL, > Logger.WARNING ); > } > while ( results.hasMore() ) { > SearchResult result = null; > try { > result = (SearchResult)results.next(); > } catch ( NamingException e ) { > getLogger().log( > name + ": Error getting next search result.", > e, LOG_CHANNEL, Logger.ERROR ); > } > String name = result.getName(); > if ( !validatePathName( name ) ) { > continue; > } > String value = parseLdapName(name); > if (principalNameAttribute != null) { > String uriValue = ((String)result.getAttributes().get(principalNameAttribute).get()).toLowerCase(); > objectNameMap.put(uriValue, value); > value = uriValue; > } > > getLogger().log( > name + ": Creating child binding \"" + value + "\" for \"" + > uri.toString() + "\".", > LOG_CHANNEL, Logger.DEBUG ); > > childBindings.add( > new ObjectNode.Binding( value, uri.toString() + "/" + value ) ); > > } > } catch ( NamingException e ) { > getLogger().log( > name + ": Error during search.", > e, LOG_CHANNEL, Logger.ERROR ); > } > } > } else { > // If the uri matches the scope + something try to do a lookup > // of the "+ something" in LDAP. > > try { > if (principalNameAttribute != null && objectNameMap.get(objectName) == null) > retrieveObject(parentUri); > NamingEnumeration results = ctx.search( > container, > rdnAttribute + "=" + (principalNameAttribute != null ? (String)objectNameMap.get(objectName) : objectName), > controls); > > if ( !results.hasMore() ) { > if (ctx != null) { > closeContext(ctx); > } > throw new ObjectNotFoundException( uri ); > } > } catch ( NamingException e ) { > getLogger().log( > name + ": Error retrieving " + uri.toString(), > e, LOG_CHANNEL, Logger.ERROR ); > if (ctx != null) { > closeContext(ctx); > } > throw new ServiceAccessException( this, e ); > } > } > > getLogger().log( name + ": Creating SubjectNode for \"" + uri.toString() + "\".", > LOG_CHANNEL, Logger.DEBUG ); > > SubjectNode node = new SubjectNode( > uri.toString(), childBindings, parentBindings, new Vector() ); > // Workaround for bug in ObjectNode.validate() > node.setUri( uri.toString() ); > > if ( cache != null ) { > getLogger().log( > name + ": Putting ObjectNode for " + uri.toString() + " to cache.", > LOG_CHANNEL, > Logger.DEBUG ); > Element cachedNode = new Element( CACHE_OBJECT_PREFIX + uri.toString(), node ); > cache.put(cachedNode); > } > > long elapsed = System.currentTimeMillis() - start; > if ( elapsed > refreshThreshold ) { > addRefreshee( uri, Refreshee.REFRESH_OBJECT ); > } > if (ctx != null) { > closeContext(ctx); > } > return node; > } > > protected NodeRevisionDescriptor getRevisionDescriptor( Uri uri ) > throws RevisionDescriptorNotFoundException, ServiceAccessException { > > long start = System.currentTimeMillis(); > DirContext ctx; > try { > ctx = getContext(); > } catch ( ServiceConnectionFailedException e ) { > throw new ServiceAccessException(this, e); > } > > String objectName = getObjectNameFromUri( uri ); > > List props = new ArrayList(); > > String resourceType = "<collection/>"; > if ( !uri.isStoreRoot() ) { > resourceType += "<principal/>"; > } > props.add(new NodeProperty( "resourcetype", resourceType, "DAV:", "", false ) ); > props.add(new NodeProperty( "displayname", (!uri.isStoreRoot() && principalNameAttribute != null?(String)objectNameMap.get(objectName):objectName), "DAV:", "", false ) ); > > // The storeRoot isn't a real object so it doesn't have any parameters to look up > if ( !uri.isStoreRoot() ) { > > String localFilter = rdnAttribute + "=" + (principalNameAttribute != null?(String)objectNameMap.get(objectName):objectName); > > SearchControls controls = new SearchControls(); > controls.setSearchScope( searchScope ); > controls.setReturningAttributes( descriptorAttributes ); > > try { > NamingEnumeration results = ctx.search( > container, > localFilter, > controls ); > > if ( !results.hasMore() ) { > if (ctx != null) { > closeContext(ctx); > } > throw new RevisionDescriptorNotFoundException( uri.toString() ); > } > while ( results.hasMore() ) { > SearchResult result; > try { > result = (SearchResult)results.next(); > } catch ( NamingException e ) { > getLogger().log( > name + ": Error getting search result with filter: " + localFilter + > " from container: " + container + ".", > LOG_CHANNEL, Logger.ERROR ); > if (ctx != null) { > closeContext(ctx); > } > throw new ServiceAccessException( this, e ); > } > > NamingEnumeration attributes = result.getAttributes().getAll(); > while ( attributes.hasMore() ) { > Attribute attribute = (Attribute)attributes.next(); > StringBuffer valueString = new StringBuffer(); > boolean isGms = attribute.getID().equals( groupMemberSet ); > boolean isMva = attribute.size() > 1; > for ( int i = 0; i < attribute.size(); i++ ) { > try { > Object value = attribute.get( i ); > if ( !( value instanceof String ) ) { > getLogger().log( > name + ": Non-string value found for " + > attribute.getID() + ".", > LOG_CHANNEL, > Logger.DEBUG ); > continue; > } > if ( isGms ) { > valueString.append( "<D:href xmlns:D='DAV:'>" ); > valueString.append( usersPath ).append( "/" ); > String name = parseLdapName(value.toString()); > if (principalNameAttribute != null) { > // lookup LDAP user entry > controls.setReturningAttributes(new String[] { principalNameAttribute }); > NamingEnumeration roleResults = > ctx.search(container, rdnAttribute + "=" + name, controls); > if (roleResults.hasMore()) { > SearchResult userObject = (SearchResult)roleResults.next(); > name = ((String)userObject.getAttributes().get(principalNameAttribute).get()).toLowerCase(); > } > } > valueString.append(name); > valueString.append( "</D:href>" ); > } else { > if ( isMva ) { > valueString.append( "<mva xmlns=\"" ) > .append( LDAP_NAMESPACE ).append( "\">" ); > valueString.append( value.toString() ); > valueString.append( "</mva>" ); > } else { > valueString.append( value.toString() ); > } > } > } catch ( NamingException e ) { > getLogger().log( > name + ": Error fetching next attribute value for attribute " + > attribute.getID() + ".", > e, LOG_CHANNEL, Logger.DEBUG ); > } > } > > if ( isGms ) { > getLogger().log( > name + ": Adding property \"group-member-set\" in namespace " + > "\"DAV:\" with value of \"" + valueString.toString() + "\" to " + > uri.toString() + ".", > LOG_CHANNEL, Logger.DEBUG ); > > props.add(new NodeProperty( > "group-member-set", > valueString.toString(), > "DAV:" ) ); > > } else { > getLogger().log( > name + ": Adding property \"" + attribute.getID() + > "\" in namespace \"" + LDAP_NAMESPACE + "\" " + > "with value of \"" + > valueString.toString() + "\" to " + uri.toString() + ".", > LOG_CHANNEL, Logger.DEBUG ); > > props.add(new NodeProperty(attribute.getID(), > valueString.toString(), LDAP_NAMESPACE ) ); > } > } > } > } catch ( NamingException e ) { > getLogger().log( > name + ": Error during search.", > e, LOG_CHANNEL, Logger.ERROR ); > } > } > > NodeRevisionDescriptor descriptor = new NodeRevisionDescriptor( > new NodeRevisionNumber( 1, 0 ), > "main", > new Vector(), > props ); > > if ( cache != null ) { > getLogger().log( > name + ": Putting NodeRevisionDescriptor for " + uri.toString() + " to cache.", > LOG_CHANNEL, > Logger.DEBUG ); > Element cachedDescriptor = new Element( > CACHE_DESCRIPTOR_PREFIX + uri.toString(), descriptor ); > cache.put(cachedDescriptor); > } > > long elapsed = System.currentTimeMillis() - start; > if ( elapsed > refreshThreshold ) { > addRefreshee( uri, Refreshee.REFRESH_DESCRIPTOR ); > } > if (ctx != null) { > closeContext(ctx); > } > return descriptor; > } > > // --------------------------------------------------- Helper Methods --------------- > > /** > * Closes a JNDI connection. > * @param ctx the Context to close > */ > private void closeContext(DirContext ctx) { > getLogger().log( name + ": Disconnecting from LDAP server.", LOG_CHANNEL, Logger.DEBUG ); > try { > ctx.close(); > } catch ( NamingException e ) { > getLogger().log( name + ": Error disconnecting from LDAP", > e, LOG_CHANNEL, Logger.WARNING ); > ctx = null; > } > } > > /** > * Gets a JNDI Context using the connection parameters specified for > * this Store in the Domain. > * @throws ServiceConnectionFailedException > */ > private DirContext getContext() throws ServiceConnectionFailedException { > getLogger().log( name + ": Connecting to LDAP server.", LOG_CHANNEL, Logger.DEBUG ); > try { > DirContext ctx = new InitialDirContext( ctxParameters ); > if ( ctx != null ) { > return ctx; > } else { > throw new ServiceConnectionFailedException( > this, "Invalid JNDI connection parameters." ); > } > } catch ( NamingException e ) { > getLogger().log( name + ": Error Connecting to LDAP Server", > e, > LOG_CHANNEL, > Logger.CRITICAL ); > throw new ServiceConnectionFailedException( this, e ); > } > } > > // ----------------------------------------------- Cache Methods/Classes --------------- > > protected synchronized void addRefreshee( Uri uri, int refreshType ) { > getLogger().log( > name + ": Adding refreshee for \"" + uri.toString() + "\" of type \"" + > (refreshType == Refreshee.REFRESH_OBJECT ? "object" : "descriptor") + "\".", > LOG_CHANNEL, > Logger.DEBUG ); > > refreshList.add( > new Refreshee( uri, System.currentTimeMillis() + refreshRate, refreshType ) ); > } > > protected Cache getCache() { > CacheManager cacheManager; > Cache cache; > try { > cacheManager = CacheManagerFactory.getDefaultCacheManager(); > } catch ( CacheException e ) { > getLogger().log( > name + ": Error getting default CacheManager.", > e, > LOG_CHANNEL, > Logger.ERROR ); > return null; > } > cache = cacheManager.getCache( cacheName ); > if ( cache == null ) { > cache = new Cache( > cacheName, > DEFAULT_CACHE_SIZE, > DEFAULT_CACHE_OVERFLOW_TO_DISK, > DEFAULT_CACHE_ETERNAL, > DEFAULT_CACHE_TTL, > DEFAULT_CACHE_TTI ); > try { > cacheManager.addCache(cache); > } catch ( IllegalStateException e ) { > getLogger().log( > name + ": Error adding cache \"" + cacheName + "\" to CacheManager.", > e, > LOG_CHANNEL, > Logger.ERROR); > } catch ( ObjectExistsException e ) { > getLogger().log( > name + ": Error adding cache \"" + cacheName + "\" to CacheManager.", > e, > LOG_CHANNEL, > Logger.ERROR); > } catch ( CacheException e ) { > getLogger().log( > name + ": Error adding cache \"" + cacheName + "\" to CacheManager.", > e, > LOG_CHANNEL, > Logger.ERROR); > } > } > return cache; > } > > protected synchronized Refreshee getNextRefreshee() { > Refreshee refreshee = null; > try { > refreshee = (Refreshee)refreshList.last(); > } catch ( NoSuchElementException e ) { > // expected when the list is emtpy > } > return refreshee; > } > > protected void refreshCache() { > Refreshee oldest = getNextRefreshee(); > long now = System.currentTimeMillis(); > while ( oldest != null && oldest.getRefreshTime() < now ) { > getLogger().log( > name + ": Refreshing cache for \"" + oldest.getUri().toString() + "\" of type \"" + > (oldest.getRefreshType() == Refreshee.REFRESH_OBJECT ? "object" : "descriptor") + > "\".", > LOG_CHANNEL, > Logger.DEBUG ); > > removeRefreshee( oldest ); > try { > Cache cache = getCache(); > if ( cache != null ) { > switch( oldest.getRefreshType() ) { > case Refreshee.REFRESH_OBJECT: > getObject( oldest.getUri() ); > break; > case Refreshee.REFRESH_DESCRIPTOR: > getRevisionDescriptor( oldest.getUri() ); > break; > } > } > } catch ( ObjectNotFoundException e ) { > getLogger().log( > name + ": Error refreshing cache for \"" + oldest.getUri().toString() + "\".", > e, > LOG_CHANNEL, > Logger.ERROR ); > } catch ( ServiceAccessException e ) { > getLogger().log( > name + ": Error refreshing cache for \"" + oldest.getUri().toString() + "\".", > e, > LOG_CHANNEL, > Logger.ERROR ); > } catch ( RevisionDescriptorNotFoundException e ) { > getLogger().log( > name + ": Error refreshing cache for \"" + oldest.getUri().toString() + "\".", > e, > LOG_CHANNEL, > Logger.ERROR ); > } > oldest = getNextRefreshee(); > } > } > > protected synchronized void removeRefreshee( Refreshee refreshee ) { > refreshList.remove( refreshee ); > } > > /** > * Implements Comparable so that the older the object, the sooner it needs to be refreshed, > * and therefor the "greater" it is. When used with a SortedSet this ensures objects are > * refreshed in the proper order. > * > */ > protected class Refreshee implements Comparable { > > public static final int REFRESH_OBJECT = 0; > public static final int REFRESH_DESCRIPTOR = 1; > > private long refreshTime; > private Uri uri; > private int refreshType; > > // No default constructor > private Refreshee() {} > > public Refreshee( Uri uri, long refreshTime, int refreshType ) { > this.refreshTime = refreshTime; > this.uri = uri; > this.refreshType = refreshType; > } > > public int compareTo( Object object ) { > if ( object instanceof Refreshee ) { > Refreshee other = (Refreshee)object; > int result = compare( this.getRefreshTime(), other.getRefreshTime() ); > if ( result != 0 ) { > return result; > } > } > return compare( hashCode(), object.hashCode() ); > } > > public long getRefreshTime() { > return refreshTime; > } > > public int getRefreshType() { > return refreshType; > } > > public Uri getUri() { > return uri; > } > > private int compare( long first, long second ) { > if (first < second) { > return 1; > } > if (second < first) { > return -1; > } > return 0; > } > } > > protected class RefreshThread extends Thread { > > private boolean run; > > public RefreshThread() { > super( "RefreshThread-" + name ); > setPriority( Thread.MIN_PRIORITY ); > setDaemon( false ); > } > > public synchronized void halt() { > run = false; > } > > public void run() { > run = true; > while ( keepRunning() ) { > try { > Thread.sleep( refreshThreadSleepTime ); > } catch ( InterruptedException e ) {} > refreshCache(); > } > } > > private synchronized boolean keepRunning() { > return run; > } > } > >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 37748
: 17112 |
17113
|
17114
|
17115
|
17116
|
17117