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