001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-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.persist;
022    
023    
024    
025    import java.io.Serializable;
026    
027    import com.unboundid.ldap.sdk.Entry;
028    import com.unboundid.ldap.sdk.EntrySource;
029    import com.unboundid.ldap.sdk.LDAPEntrySource;
030    import com.unboundid.ldap.sdk.LDAPException;
031    import com.unboundid.ldap.sdk.SearchResult;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    
035    import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
036    import static com.unboundid.util.Debug.*;
037    import static com.unboundid.util.StaticUtils.*;
038    
039    
040    
041    /**
042     * This class provides a mechanism for iterating through the objects returned
043     * by a search operation performed using one of the {@code search} methods in
044     * the {@link LDAPPersister} class.  However, it has a couple of notable
045     * differences from a standard Java {@code Iterator} object:
046     * <UL>
047     *   <LI>It does not have a {@code hasNext} method.  Instead, the {@link #next}
048     *       method will return {@code null} when there are no more objects in the
049     *       set of results.</LI>
050     *   <LI>The {@link #next} method may throw an exception if a problem occurs
051     *       while trying to read an entry or decode it as an object of the
052     *       appropriate type.  This does not necessarily mean that the search is
053     *       complete, and the {@link #next} method should be called again to see
054     *       if there are any more objects to retrieve.</LI>
055     *   <LI>If you wish to stop iterating through the results before all of them
056     *       have been retrieved, then you must call the {@link #close} method
057     * </UL>
058     *
059     * @param  <T>  The type of object handled by this class.
060     */
061    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
062    public final class PersistedObjects<T>
063           implements Serializable
064    {
065      /**
066       * The serial version UID for this serializable class.
067       */
068      private static final long serialVersionUID = 7430494946944736169L;
069    
070    
071    
072      // The LDAP entry source that will be used to read matching entries.
073      private final EntrySource entrySource;
074    
075      // The LDAP persister that will be used to decode the entries that are
076      // returned.
077      private final LDAPPersister<T> persister;
078    
079    
080    
081      /**
082       * Creates a new {@code PersistedObjects} object that will read entries from
083       * the provided entry source.
084       *
085       * @param  persister    The persister that will be used to decode entries that
086       *                      are returned.
087       * @param  entrySource  The entry source that will be used to read entries
088       *                      returned from the search.
089       */
090      PersistedObjects(final LDAPPersister<T> persister,
091                       final EntrySource entrySource)
092      {
093        this.persister   = persister;
094        this.entrySource = entrySource;
095      }
096    
097    
098    
099      /**
100       * Retrieves the next object returned from the search request.  This method
101       * may block until the necessary information  has been received from the
102       * server.
103       *
104       * @return  The next object returned from the search request, or {@code null}
105       *          if all objects have been read.
106       *
107       * @throws  LDAPPersistException  If a problem occurs while reading the next
108       *                                entry from the server, or when trying to
109       *                                decode that entry as an object.
110       */
111      public T next()
112             throws LDAPPersistException
113      {
114        final Entry entry;
115        try
116        {
117          entry = entrySource.nextEntry();
118        }
119        catch (Exception e)
120        {
121          debugException(e);
122    
123          final Throwable cause = e.getCause();
124          if ((cause != null) && (cause instanceof LDAPException))
125          {
126            throw new LDAPPersistException((LDAPException) cause);
127          }
128          else
129          {
130            throw new LDAPPersistException(
131                 ERR_OBJECT_SEARCH_RESULTS_ENTRY_SOURCE_EXCEPTION.get(
132                      getExceptionMessage(e)), e);
133          }
134        }
135    
136        if (entry == null)
137        {
138          return null;
139        }
140        else
141        {
142          return persister.decode(entry);
143        }
144      }
145    
146    
147    
148      /**
149       * Indicates that you wish to stop iterating through search results and will
150       * not be retrieving any additional objects.  This method MUST be called to
151       * avoid leaking resources if you stop iterating through results before the
152       * {@link #next} method returns {@code null} to indicate that there are no
153       * more objects to retrieve.  This method MAY be called after the search has
154       * completed (including being called multiple times) with no adverse effects.
155       */
156      public void close()
157      {
158        entrySource.close();
159      }
160    
161    
162    
163      /**
164       * Retrieves the search result for the search operation, if available.  It
165       * will not be available until the search has completed (as indicated by a
166       * {@code null} return value from the {@link #next} method), and for some use
167       * cases it may never be available.
168       *
169       * @return  The search result for the search operation, or {@code null} if it
170       *          is not available (e.g., because the search has not yet completed).
171       */
172      public SearchResult getSearchResult()
173      {
174        if (entrySource instanceof LDAPEntrySource)
175        {
176          return ((LDAPEntrySource) entrySource).getSearchResult();
177        }
178        else
179        {
180          return null;
181        }
182      }
183    }