View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.collective;
18  
19  
20  import org.apache.ldap.server.DirectoryServiceConfiguration;
21  import org.apache.ldap.server.interceptor.BaseInterceptor;
22  import org.apache.ldap.server.interceptor.NextInterceptor;
23  import org.apache.ldap.server.configuration.InterceptorConfiguration;
24  import org.apache.ldap.server.partition.DirectoryPartitionNexus;
25  import org.apache.ldap.server.schema.AttributeTypeRegistry;
26  import org.apache.ldap.server.subtree.SubentryService;
27  import org.apache.ldap.server.invocation.InvocationStack;
28  import org.apache.ldap.server.invocation.Invocation;
29  import org.apache.ldap.server.enumeration.SearchResultFilteringEnumeration;
30  import org.apache.ldap.server.enumeration.SearchResultFilter;
31  import org.apache.ldap.common.filter.ExprNode;
32  import org.apache.ldap.common.name.LdapName;
33  import org.apache.ldap.common.schema.AttributeType;
34  import org.apache.ldap.common.message.LockableAttributeImpl;
35  
36  import javax.naming.NamingException;
37  import javax.naming.NamingEnumeration;
38  import javax.naming.Name;
39  import javax.naming.directory.Attributes;
40  import javax.naming.directory.SearchControls;
41  import javax.naming.directory.Attribute;
42  import javax.naming.directory.SearchResult;
43  import java.util.Map;
44  import java.util.Set;
45  import java.util.HashSet;
46  import java.util.Collections;
47  
48  
49  /***
50   * An interceptor based service dealing with collective attribute
51   * management.  This service intercepts read operations on entries to
52   * inject collective attribute value pairs into the response based on
53   * the entires inclusion within collectiveAttributeSpecificAreas and
54   * collectiveAttributeInnerAreas.
55   *
56   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
57   * @version $Rev$
58   */
59  public class CollectiveAttributeService extends BaseInterceptor
60  {
61      /***
62       * the search result filter to use for collective attribute injection
63       */
64      private final SearchResultFilter SEARCH_FILTER = new SearchResultFilter()
65      {
66          public boolean accept( Invocation invocation, SearchResult result, SearchControls controls )
67                  throws NamingException
68          {
69                  return filter( result.getAttributes() );
70          }
71      };
72  
73      private AttributeTypeRegistry registry = null;
74      private DirectoryPartitionNexus nexus = null;
75  
76  
77      public void init( DirectoryServiceConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
78      {
79          super.init( factoryCfg, cfg );
80          nexus = factoryCfg.getPartitionNexus();
81          registry = factoryCfg.getGlobalRegistries().getAttributeTypeRegistry();
82      }
83  
84  
85      /***
86       * Adds the set of collective attributes contained in subentries referenced
87       * by the entry.  All collective attributes that are not exclused are added
88       * to the entry from all subentries.
89       *
90       * @param entry the entry to have the collective attributes injected
91       * @throws NamingException if there are problems accessing subentries
92       */
93      private void addCollectiveAttributes( Attributes entry ) throws NamingException
94      {
95          Attribute subentries = entry.get( SubentryService.COLLECTIVE_ATTRIBUTE_SUBENTRIES );
96  
97          if ( subentries == null )
98          {
99              return;
100         }
101 
102         /*
103          * Before we proceed we need to lookup the exclusions within the
104          * entry and build a set of exclusions for rapid lookup.  We use
105          * OID values in the exclusions set instead of regular names that
106          * may have case variance.
107          */
108         Attribute collectiveExclusions = entry.get( "collectiveExclusions" );
109         Set exclusions;
110         if ( collectiveExclusions != null )
111         {
112             if ( collectiveExclusions.contains( "2.5.18.0" ) ||
113                  collectiveExclusions.contains( "excludeAllCollectiveAttributes" ) )
114             {
115                 return;
116             }
117 
118             exclusions = new HashSet();
119             for ( int ii = 0; ii < collectiveExclusions.size(); ii++ )
120             {
121                 AttributeType attrType = registry.lookup( ( String ) collectiveExclusions.get( ii ) );
122                 exclusions.add( attrType.getOid() );
123             }
124         }
125         else
126         {
127             exclusions = Collections.EMPTY_SET;
128         }
129 
130         /*
131          * For each collective subentry referenced by the entry we lookup the
132          * attributes of the subentry and copy collective attributes from the
133          * subentry into the entry.
134          */
135         for ( int ii = 0; ii < subentries.size(); ii++ )
136         {
137             String subentryDnStr = ( String ) subentries.get( ii );
138             Name subentryDn = new LdapName( subentryDnStr );
139             Attributes subentry = nexus.lookup( subentryDn );
140             NamingEnumeration attrIds = subentry.getIDs();
141             while ( attrIds.hasMore() )
142             {
143                 String attrId = ( String ) attrIds.next();
144                 AttributeType attrType = registry.lookup( attrId );
145 
146                 // skip the addition of this collective attribute if it is excluded
147                 if ( exclusions.contains( attrType.getOid() ) )
148                 {
149                     continue;
150                 }
151 
152                 /*
153                  * If the attribute type of the subentry attribute is collective
154                  * then we need to add all the values of the collective attribute
155                  * to the entry making sure we do not overwrite values already
156                  * existing for the collective attribute in case multiple
157                  * subentries add the same collective attributes to this entry.
158                  */
159 
160                 if ( attrType.isCollective() )
161                 {
162                     Attribute subentryColAttr = subentry.get( attrId );
163                     Attribute entryColAttr = entry.get( attrId );
164 
165                     // if entry does not have attribute for colattr then create it
166                     if ( entryColAttr == null )
167                     {
168                         entryColAttr = new LockableAttributeImpl( attrId );
169                         entry.put( entryColAttr );
170                     }
171 
172                     // add all the collective attribute values in the subentry to entry
173                     for ( int jj = 0; jj < subentryColAttr.size(); jj++ )
174                     {
175                         entryColAttr.add( subentryColAttr.get( jj ) );
176                     }
177                 }
178             }
179         }
180     }
181 
182 
183     /***
184      * Filter that injects collective attributes into the entry.
185      *
186      * @param attributes the resultant attributes with added collective attributes
187      * @return true always
188      */
189     private boolean filter( Attributes attributes ) throws NamingException
190     {
191         addCollectiveAttributes( attributes );
192         return true;
193     }
194 
195 
196     private void filter( Name dn, Attributes entry, String[] ids ) throws NamingException
197     {
198         filter( entry );
199 
200         // still need to return collective attrs when ids is null
201         if ( ids == null )
202         {
203             return;
204         }
205 
206         // now we can filter out even collective attributes from the requested return ids
207         if ( dn.size() == 0 )
208         {
209             HashSet idsSet = new HashSet( ids.length );
210 
211             for ( int ii = 0; ii < ids.length; ii++ )
212             {
213                 idsSet.add( ids[ii].toLowerCase() );
214             }
215 
216             NamingEnumeration list = entry.getIDs();
217 
218             while ( list.hasMore() )
219             {
220                 String attrId = ( ( String ) list.nextElement() ).toLowerCase();
221 
222                 if ( !idsSet.contains( attrId ) )
223                 {
224                     entry.remove( attrId );
225                 }
226             }
227         }
228 
229         // do nothing past here since this explicity specifies which
230         // attributes to include - backends will automatically populate
231         // with right set of attributes using ids array
232     }
233 
234 
235     // ------------------------------------------------------------------------
236     // Interceptor Method Overrides
237     // ------------------------------------------------------------------------
238 
239 
240     public Attributes lookup( NextInterceptor nextInterceptor, Name name ) throws NamingException
241     {
242         Attributes result = nextInterceptor.lookup( name );
243         if ( result == null )
244         {
245             return null;
246         }
247         filter( result );
248         return result;
249     }
250 
251 
252     public Attributes lookup( NextInterceptor nextInterceptor, Name name, String[] attrIds ) throws NamingException
253     {
254         Attributes result = nextInterceptor.lookup( name, attrIds );
255         if ( result == null )
256         {
257             return null;
258         }
259 
260         filter( name, result, attrIds );
261         return result;
262     }
263 
264 
265     public NamingEnumeration list( NextInterceptor nextInterceptor, Name base ) throws NamingException
266     {
267         NamingEnumeration e = nextInterceptor.list( base );
268         Invocation invocation = InvocationStack.getInstance().peek();
269         return new SearchResultFilteringEnumeration( e, new SearchControls(), invocation, SEARCH_FILTER );
270     }
271 
272 
273     public NamingEnumeration search( NextInterceptor nextInterceptor,
274             Name base, Map env, ExprNode filter,
275             SearchControls searchCtls ) throws NamingException
276     {
277         NamingEnumeration e = nextInterceptor.search( base, env, filter, searchCtls );
278         Invocation invocation = InvocationStack.getInstance().peek();
279         return new SearchResultFilteringEnumeration( e, searchCtls, invocation, SEARCH_FILTER );
280     }
281 }