001/*
002 * Copyright 2010-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2010-2017 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk;
022
023
024
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.Iterator;
028
029import com.unboundid.util.ThreadSafety;
030import com.unboundid.util.ThreadSafetyLevel;
031
032import static com.unboundid.ldap.sdk.LDAPMessages.*;
033import static com.unboundid.util.Debug.*;
034import static com.unboundid.util.StaticUtils.*;
035import static com.unboundid.util.Validator.*;
036
037
038
039/**
040 * This class provides an {@link EntrySource} that will retrieve entries
041 * referenced by a provided set of DNs.  The connection will remain open after
042 * all entries have been read.
043 * <BR><BR>
044 * It is not necessary to close this entry source when it is no longer needed,
045 * although there is no cost or penalty in doing so.  Any exceptions thrown by
046 * the {@link #nextEntry()} method will have the {@code mayContinueReading}
047 * value set to {@code true}.
048 * <H2>Example</H2>
049 * The following example demonstrates the process for retrieving a static group
050 * entry and using a {@code DNEntrySource} to iterate across the members of that
051 * group:
052 * <PRE>
053 * Entry groupEntry =
054 *      connection.getEntry("cn=My Group,ou=Groups,dc=example,dc=com");
055 * String[] memberValues = groupEntry.getAttributeValues("member");
056 * int entriesReturned = 0;
057 * int exceptionsCaught = 0;
058 *
059 * if (memberValues != null)
060 * {
061 *   DNEntrySource entrySource =
062 *        new DNEntrySource(connection, memberValues, "cn");
063 *   try
064 *   {
065 *     while (true)
066 *     {
067 *       Entry memberEntry;
068 *       try
069 *       {
070 *         memberEntry = entrySource.nextEntry();
071 *       }
072 *       catch (EntrySourceException ese)
073 *       {
074 *         // A problem was encountered while attempting to obtain an entry.
075 *         // We may be able to continue reading entries (e.g., if the problem
076 *         // was that the group referenced an entry that doesn't exist), or
077 *         // we may not (e.g., if the problem was a significant search error
078 *         // or problem with the connection).
079 *         exceptionsCaught++;
080 *         if (ese.mayContinueReading())
081 *         {
082 *           continue;
083 *         }
084 *         else
085 *         {
086 *           break;
087 *         }
088 *       }
089 *
090 *       if (memberEntry == null)
091 *       {
092 *         // We've retrieved all of the entries for the given set of DNs.
093 *         break;
094 *       }
095 *       else
096 *       {
097 *         entriesReturned++;
098 *       }
099 *     }
100 *   }
101 *   finally
102 *   {
103 *     entrySource.close();
104 *   }
105 * }
106 * </PRE>
107 */
108@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
109public final class DNEntrySource
110       extends EntrySource
111{
112  // The iterator to use to access the DNs.  It will either be across DN or
113  // String objects.
114  private final Iterator<?> dnIterator;
115
116  // The connection to use to communicate with the directory server.
117  private final LDAPInterface connection;
118
119  // The set of attributes to include in entries that are returned.
120  private final String[] attributes;
121
122
123
124  /**
125   * Creates a new DN entry source with the provided information.
126   *
127   * @param  connection  The connection to the directory server from which the
128   *                     entries will be read.  It must not be {@code null}.
129   * @param  dns         The set of DNs to be read.  It must not be
130   *                     {@code null}.
131   * @param  attributes  The set of attributes to include in entries that are
132   *                     returned.  If this is empty or {@code null}, then all
133   *                     user attributes will be requested.
134   */
135  public DNEntrySource(final LDAPInterface connection, final DN[] dns,
136                       final String... attributes)
137  {
138    ensureNotNull(connection, dns);
139
140    this.connection = connection;
141    dnIterator = Arrays.asList(dns).iterator();
142
143    if (attributes == null)
144    {
145      this.attributes = NO_STRINGS;
146    }
147    else
148    {
149      this.attributes = attributes;
150    }
151  }
152
153
154
155  /**
156   * Creates a new DN entry source with the provided information.
157   *
158   * @param  connection  The connection to the directory server from which the
159   *                     entries will be read.  It must not be {@code null}.
160   * @param  dns         The set of DNs to be read.  It must not be
161   *                     {@code null}.
162   * @param  attributes  The set of attributes to include in entries that are
163   *                     returned.  If this is empty or {@code null}, then all
164   *                     user attributes will be requested.
165   */
166  public DNEntrySource(final LDAPInterface connection, final String[] dns,
167                       final String... attributes)
168  {
169    this(connection, Arrays.asList(dns), attributes);
170  }
171
172
173
174  /**
175   * Creates a new DN entry source with the provided information.
176   *
177   * @param  connection  The connection to the directory server from which the
178   *                     entries will be read.  It must not be {@code null}.
179   * @param  dns         The set of DNs to be read.  It must not be
180   *                     {@code null}.
181   * @param  attributes  The set of attributes to include in entries that are
182   *                     returned.  If this is empty or {@code null}, then all
183   *                     user attributes will be requested.
184   */
185  public DNEntrySource(final LDAPInterface connection,
186                       final Collection<String> dns, final String... attributes)
187  {
188    ensureNotNull(connection, dns);
189
190    this.connection = connection;
191    dnIterator = dns.iterator();
192
193    if (attributes == null)
194    {
195      this.attributes = NO_STRINGS;
196    }
197    else
198    {
199      this.attributes = attributes;
200    }
201  }
202
203
204
205  /**
206   * {@inheritDoc}
207   */
208  @Override()
209  public Entry nextEntry()
210         throws EntrySourceException
211  {
212    if (! dnIterator.hasNext())
213    {
214      return null;
215    }
216
217    final String dn = String.valueOf(dnIterator.next());
218    try
219    {
220      final Entry e = connection.getEntry(dn, attributes);
221      if (e == null)
222      {
223        throw new EntrySourceException(true,
224             ERR_DN_ENTRY_SOURCE_NO_SUCH_ENTRY.get(dn),
225             new LDAPException(ResultCode.NO_RESULTS_RETURNED));
226      }
227      else
228      {
229        return e;
230      }
231    }
232    catch (final LDAPException le)
233    {
234      debugException(le);
235      throw new EntrySourceException(true,
236           ERR_DN_ENTRY_SOURCE_ERR_RETRIEVING_ENTRY.get(dn,
237                getExceptionMessage(le)),
238           le);
239    }
240  }
241
242
243
244  /**
245   * {@inheritDoc}
246   */
247  @Override()
248  public void close()
249  {
250    // No implementation is required.
251  }
252}