001/*
002 * Copyright 2009-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2014 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.persist;
022
023
024
025import java.io.Serializable;
026
027import com.unboundid.ldap.sdk.Entry;
028import com.unboundid.ldap.sdk.EntrySource;
029import com.unboundid.ldap.sdk.LDAPEntrySource;
030import com.unboundid.ldap.sdk.LDAPException;
031import com.unboundid.ldap.sdk.SearchResult;
032import com.unboundid.util.ThreadSafety;
033import com.unboundid.util.ThreadSafetyLevel;
034
035import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
036import static com.unboundid.util.Debug.*;
037import 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)
062public 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}