001    /*
002     * Copyright 2010-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2010-2015 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     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.util.Arrays;
026    import java.util.Collection;
027    import java.util.Iterator;
028    
029    import com.unboundid.util.ThreadSafety;
030    import com.unboundid.util.ThreadSafetyLevel;
031    
032    import static com.unboundid.ldap.sdk.LDAPMessages.*;
033    import static com.unboundid.util.Debug.*;
034    import static com.unboundid.util.StaticUtils.*;
035    import 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)
109    public 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    }