001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.extensions;
028 import org.opends.messages.Message;
029
030
031
032 import java.util.Iterator;
033 import java.util.LinkedList;
034 import java.util.Set;
035
036 import org.opends.server.types.DirectoryConfig;
037 import org.opends.server.types.DirectoryException;
038 import org.opends.server.types.DN;
039 import org.opends.server.types.Entry;
040 import org.opends.server.types.MemberList;
041 import org.opends.server.types.MembershipException;
042 import org.opends.server.types.SearchFilter;
043 import org.opends.server.types.SearchScope;
044
045 import static org.opends.server.loggers.debug.DebugLogger.*;
046 import org.opends.server.loggers.debug.DebugTracer;
047 import org.opends.server.types.DebugLogLevel;
048 import static org.opends.messages.ExtensionMessages.*;
049 import static org.opends.server.util.Validator.*;
050
051
052
053 /**
054 * This class provides an implementation of the {@code MemberList} class that
055 * may be used in conjunction when static groups when additional criteria is to
056 * be used to select a subset of the group members.
057 */
058 public class FilteredStaticGroupMemberList
059 extends MemberList
060 {
061 /**
062 * The tracer object for the debug logger.
063 */
064 private static final DebugTracer TRACER = getTracer();
065
066
067
068
069 // The base DN below which all returned members should exist.
070 private DN baseDN;
071
072 // The DN of the static group with which this member list is associated.
073 private DN groupDN;
074
075 // The entry of the next entry that matches the member list criteria.
076 private Entry nextMatchingEntry;
077
078 // The iterator used to traverse the set of member DNs.
079 private Iterator<DN> memberDNIterator;
080
081 // The set of DNs for the users that are members of the associated static
082 // group.
083 private LinkedList<DN> memberDNs;
084
085 // The membership exception that should be thrown the next time a member is
086 // requested.
087 private MembershipException nextMembershipException;
088
089 // The search filter that all returned members should match.
090 private SearchFilter filter;
091
092 // The search scope to apply against the base DN for the member subset.
093 private SearchScope scope;
094
095
096
097 /**
098 * Creates a new filtered static group member list with the provided
099 * information.
100 *
101 * @param groupDN The DN of the static group with which this member list
102 * is associated.
103 * @param memberDNs The set of DNs for the users that are members of the
104 * associated static group.
105 * @param baseDN The base DN below which all returned members should
106 * exist. If this is {@code null}, then all members will
107 * be considered to match the base and scope criteria.
108 * @param scope The search scope to apply against the base DN when
109 * selecting eligible members.
110 * @param filter The search filter which all returned members should
111 * match. If this is {@code null}, then all members will
112 * be considered eligible.
113 */
114 public FilteredStaticGroupMemberList(DN groupDN, Set<DN> memberDNs, DN baseDN,
115 SearchScope scope, SearchFilter filter)
116 {
117 ensureNotNull(groupDN, memberDNs);
118
119 this.groupDN = groupDN;
120 this.memberDNs = new LinkedList<DN>(memberDNs);
121 memberDNIterator = memberDNs.iterator();
122
123 this.baseDN = baseDN;
124 this.filter = filter;
125
126 if (scope == null)
127 {
128 this.scope = SearchScope.WHOLE_SUBTREE;
129 }
130 else
131 {
132 this.scope = scope;
133 }
134
135 nextMatchingEntry = null;
136 nextMembershipException = null;
137 nextMemberInternal();
138 }
139
140
141
142 /**
143 * Attempts to find the next member that matches the associated criteria.
144 * When this method returns, if {@code nextMembershipException} is
145 * non-{@code null}, then that exception should be thrown on the next attempt
146 * to retrieve a member. If {@code nextMatchingEntry} is non-{@code null},
147 * then that entry should be returned on the next attempt to retrieve a
148 * member. If both are {@code null}, then there are no more members to
149 * return.
150 */
151 private void nextMemberInternal()
152 {
153 while (memberDNIterator.hasNext())
154 {
155 DN nextDN = memberDNIterator.next();
156
157
158 // Check to see if we can eliminate the entry as a possible match purely
159 // based on base DN and scope.
160 if (baseDN != null)
161 {
162 switch (scope)
163 {
164 case BASE_OBJECT:
165 if (! baseDN.equals(nextDN))
166 {
167 continue;
168 }
169 break;
170
171 case SINGLE_LEVEL:
172 if (! baseDN.equals(nextDN.getParent()))
173 {
174 continue;
175 }
176 break;
177
178 case SUBORDINATE_SUBTREE:
179 if (baseDN.equals(nextDN) || (! baseDN.isAncestorOf(nextDN)))
180 {
181 continue;
182 }
183 break;
184
185 default:
186 if (! baseDN.isAncestorOf(nextDN))
187 {
188 continue;
189 }
190 break;
191 }
192 }
193
194
195 // Get the entry for the potential member. If we can't, then populate
196 // the next membership exception.
197 try
198 {
199 Entry memberEntry = DirectoryConfig.getEntry(nextDN);
200 if (memberEntry == null)
201 {
202 Message message = ERR_STATICMEMBERS_NO_SUCH_ENTRY.get(
203 String.valueOf(nextDN), String.valueOf(groupDN));
204 nextMembershipException =
205 new MembershipException(message, true);
206 return;
207 }
208
209 if (filter == null)
210 {
211 nextMatchingEntry = memberEntry;
212 return;
213 }
214 else
215 {
216 if (filter.matchesEntry(memberEntry))
217 {
218 nextMatchingEntry = memberEntry;
219 return;
220 }
221 else
222 {
223 continue;
224 }
225 }
226 }
227 catch (DirectoryException de)
228 {
229 if (debugEnabled())
230 {
231 TRACER.debugCaught(DebugLogLevel.ERROR, de);
232 }
233
234 Message message = ERR_STATICMEMBERS_CANNOT_GET_ENTRY.
235 get(String.valueOf(nextDN), String.valueOf(groupDN),
236 String.valueOf(de.getMessageObject()));
237 nextMembershipException =
238 new MembershipException(message, true, de);
239 return;
240 }
241 }
242
243
244 // If we've gotten here, then there are no more members.
245 nextMatchingEntry = null;
246 nextMembershipException = null;
247 }
248
249
250
251 /**
252 * {@inheritDoc}
253 */
254 @Override()
255 public boolean hasMoreMembers()
256 {
257 if (! memberDNIterator.hasNext())
258 {
259 return false;
260 }
261
262 return ((nextMatchingEntry != null) || (nextMembershipException != null));
263 }
264
265
266
267 /**
268 * {@inheritDoc}
269 */
270 @Override()
271 public DN nextMemberDN()
272 throws MembershipException
273 {
274 if (! memberDNIterator.hasNext())
275 {
276 return null;
277 }
278
279 Entry e = nextMemberEntry();
280 if (e == null)
281 {
282 return null;
283 }
284 else
285 {
286 return e.getDN();
287 }
288 }
289
290
291
292 /**
293 * {@inheritDoc}
294 */
295 @Override()
296 public Entry nextMemberEntry()
297 throws MembershipException
298 {
299 if (! memberDNIterator.hasNext())
300 {
301 return null;
302 }
303
304 if (nextMembershipException == null)
305 {
306 Entry e = nextMatchingEntry;
307 nextMatchingEntry = null;
308 nextMemberInternal();
309 return e;
310 }
311 else
312 {
313 MembershipException me = nextMembershipException;
314 nextMembershipException = null;
315 nextMemberInternal();
316 throw me;
317 }
318 }
319
320
321
322 /**
323 * {@inheritDoc}
324 */
325 @Override()
326 public void close()
327 {
328 // No implementation is required.
329 }
330 }
331