/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package netscape.ldap; import java.util.*; import netscape.ldap.client.*; import netscape.ldap.client.opers.*; import java.io.*; /** * The results of an LDAP search operation, represented as an enumeration. * Note that you can only iterate through this enumeration once: if you * need to use these results more than once, make sure to save the * results in a separate location. *
* * You can also use the results of a search in progress to abandon that search * operation. *
*
* @version 1.0
* @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean)
* @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchResults)
*/
public class LDAPSearchResults implements Enumeration, java.io.Serializable {
static final long serialVersionUID = -501692208613904825L;
private Vector entries = null;
private LDAPSearchListener resultSource;
private boolean searchComplete = false;
private LDAPConnection connectionToClose;
private LDAPConnection currConn;
private boolean persistentSearch = false;
private LDAPSearchConstraints currCons;
private String currBase;
private int currScope;
private String currFilter;
private String[] currAttrs;
private boolean currAttrsOnly;
private Vector referralResults = new Vector();
private Vector exceptions;
private int msgID = -1;
// only used for the persistent search
private boolean firstResult = false;
/**
* Constructs an enumeration of search results.
* Note that this does not actually generate the results;
* you need to call LDAPConnection.search to
* perform the search and get the results.
* @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean)
*/
public LDAPSearchResults() {
entries = new Vector();
connectionToClose = null;
searchComplete = true;
currCons = new LDAPSearchConstraints();
}
LDAPSearchResults(LDAPConnection conn, LDAPSearchConstraints cons,
String base, int scope, String filter, String[] attrs, boolean attrsOnly) {
this();
currConn = conn;
currCons = cons;
currBase = base;
currScope = scope;
currFilter = filter;
currAttrs = attrs;
currAttrsOnly = attrsOnly;
}
/**
* Constructs an enumeration of search results. Used when returning results
* from a cache.
* @param v the vector containing LDAPEntries
* @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean)
*/
LDAPSearchResults(Vector v) {
this();
entries = (Vector)v.clone();
if ((entries != null) && (entries.size() >= 1)) {
// Each cache value is represented by a vector. The first element
// represents the size of all the LDAPEntries. This needs to be
// removed before we iterate through each LDAPEntry.
entries.removeElementAt(0);
}
}
LDAPSearchResults(Vector v, LDAPConnection conn,
LDAPSearchConstraints cons,
String base, int scope, String filter,
String[] attrs, boolean attrsOnly) {
this(v);
currConn = conn;
currCons = cons;
currBase = base;
currScope = scope;
currFilter = filter;
currAttrs = attrs;
currAttrsOnly = attrsOnly;
}
/**
* Add search entry of referral
* @param msg LDAPSearchResult or LDAPsearchResultReference
*/
void add( LDAPMessage msg ) {
if (msg instanceof LDAPSearchResult) {
entries.addElement( ((LDAPSearchResult)msg).getEntry());
} else if (msg instanceof LDAPSearchResultReference) {
/* convert to LDAPReferralException */
String urls[] = ((LDAPSearchResultReference)msg).getUrls();
if (urls != null) {
if (exceptions == null) {
exceptions = new Vector();
}
exceptions.addElement(new LDAPReferralException(null, 0, urls));
}
}
}
/**
* Add exception
* @param e exception
*/
void add(LDAPException e) {
if (exceptions == null) {
exceptions = new Vector();
}
exceptions.addElement(e);
}
/**
* Prepares to return asynchronous results from a search
* @param l Listener which will provide results
*/
void associate( LDAPSearchListener l) {
resultSource = l;
searchComplete = false;
}
void associatePersistentSearch( LDAPSearchListener l) {
resultSource = l;
persistentSearch = true;
searchComplete = false;
firstResult = true;
}
void addReferralEntries(LDAPSearchResults res) {
referralResults.addElement(res);
}
/**
* For asynchronous search, this mechanism allows the programmer to
* close a connection whenever the search completes.
* @param toClose connection to close when the search terminates
*/
void closeOnCompletion (LDAPConnection toClose) {
if (searchComplete) {
try {
toClose.disconnect();
} catch (LDAPException e) {
}
} else {
connectionToClose = toClose;
}
}
/**
* Basic quicksort algorithm.
*/
void quicksort (LDAPEntry[] toSort, LDAPEntryComparator compare,
int low, int high) {
if (low >= high) {
return;
}
LDAPEntry pivot = toSort[low];
int slow = low-1, shigh = high+1;
while (true) {
do {
shigh--;
} while (compare.isGreater (toSort[shigh], pivot));
do {
slow++;
} while (compare.isGreater (pivot, toSort[slow]));
if (slow >= shigh) {
break;
}
LDAPEntry temp = toSort[slow];
toSort[slow] = toSort[shigh];
toSort[shigh] = temp;
}
quicksort (toSort, compare, low, shigh);
quicksort (toSort, compare, shigh+1, high);
}
/**
* Sets the message ID for this search request. msgID is used
* to retrieve response controls.
* @param msgID Message ID for this search request
*/
void setMsgID(int msgID) {
this.msgID = msgID;
}
/**
* Returns the controls returned with this search result. If any control
* is registered with LDAPControl, an attempt is made to
* instantiate the control. If the instantiation fails, the control is
* returned as a basic LDAPControl.
* @return an array of type LDAPControl.
* @see netscape.ldap.LDAPControl#register
*/
public LDAPControl[] getResponseControls() {
return currConn.getResponseControls(msgID);
}
/**
* Sorts the search results.
*
*
* The comparator (LDAPEntryComparator) determines the
* sort order used. For example, if the comparator uses the uid
* attribute for comparison, the search results are sorted according to
* uid.
*
* * The following section of code sorts results in ascending order, * first by surname and then by common name. * *
* String[] sortAttrs = {"sn", "cn"};
* boolean[] ascending = {true, true};
*
* LDAPConnection ld = new LDAPConnection();
* ld.connect( ... );
* LDAPSearchResults res = ld.search( ... );
* res.sort( new LDAPCompareAttrNames(sortAttrs, ascending) );
*
* NOTE: If the search results arrive asynchronously, the sort
* method blocks until all the results are returned.
* * * If some of the elements of the Enumeration have already been fetched, * the cursor is reset to the (new) first element. *
* * @param compare comparator used to determine the sort order of the results * @see LDAPEntryComparator */ public synchronized void sort(LDAPEntryComparator compare) { while (!searchComplete) { fetchResult(); } // if automatic referral, then add to the entries, otherwise, dont do it // since the elements in referralResults are LDAPReferralException. if (currCons.getReferrals()) { while (referralResults.size() > 0) { Object obj = null; if ((obj=nextReferralElement()) != null) { if (obj instanceof LDAPException) { add((LDAPException)obj); // put it back } else { entries.addElement(obj); } } } } int numEntries = entries.size(); if (numEntries <= 0) { return; } LDAPEntry[] toSort = new LDAPEntry[numEntries]; entries.copyInto (toSort); if (toSort.length > 1) { quicksort (toSort, compare, 0, numEntries-1); } entries.removeAllElements(); for (int i = 0; i < numEntries; i++) { entries.addElement (toSort[i]); } } /** * Returns the next LDAP entry from the search results * and throws an exception if the next result is a referral, or * if a sizelimit or timelimit error occurred. *
*
* You can use this method in conjunction with the
* hasMoreElements method to iterate through
* each entry in the search results. For example:
*
* LDAPSearchResults res = ld.search( MY_SEARCHBASE,
* LDAPConnection.SCOPE_BASE, MY_FILTER,
* null, false );
* while ( res.hasMoreElements() ) {
* try {
* LDAPEntry findEntry = res.next();
* } catch ( LDAPReferralException e ) {
* LDAPUrl refUrls[] = e.getURLs();
* for ( int i = 0; i < refUrls.length; i++ ) {
* // Your code for handling referrals
* }
* continue;
* } catch ( LDAPException e ) {
* // Your code for handling errors on limits exceeded
* continue;
* }
* ...
* }
*
* @return the next LDAP entry in the search results.
* @exception LDAPReferralException A referral (thrown
* if the next result is a referral), or LDAPException
* if a limit on the number of entries or the time was
* exceeded.
* @see netscape.ldap.LDAPSearchResults#hasMoreElements()
*/
public LDAPEntry next() throws LDAPException {
Object o = nextElement();
if ((o instanceof LDAPReferralException) ||
(o instanceof LDAPException)) {
throw (LDAPException)o;
}
if (o instanceof LDAPEntry) {
return (LDAPEntry)o;
}
return null;
}
/**
* Returns the next result from a search. You can use this method
* in conjunction with the hasMoreElements method to
* iterate through all elements in the search results.
* * * Make sure to cast the * returned element as the correct type. For example: *
* LDAPSearchResults res = ld.search( MY_SEARCHBASE,
* LDAPConnection.SCOPE_BASE, MY_FILTER,
* null, false );
* while ( res.hasMoreElements() ) {
* Object o = res.nextElement();
* if ( o instanceof LDAPEntry ) {
* LDAPEntry findEntry = (LDAPEntry)o;
* ...
* } else if ( o instanceof LDAPReferralException ) {
* LDAPReferralException e = (LDAPReferralException)o;
* LDAPUrl refUrls[] = e.getURLs();
* ...
* } else if ( o instanceof LDAPException ) {
* LDAPException e = (LDAPException)o;
* ...
* }
* }
*
* @return the next element in the search results.
* @see netscape.ldap.LDAPSearchResults#hasMoreElements()
*/
public Object nextElement() {
if ( entries.size() > 0 ) {
Object obj = entries.elementAt(0);
entries.removeElementAt(0);
return obj;
}
if (referralResults.size() > 0) {
return nextReferralElement();
}
if ((exceptions != null) && (exceptions.size() > 0)) {
Object obj = exceptions.elementAt(0);
exceptions.removeElementAt(0);
return obj;
}
return null;
}
Object nextReferralElement() {
LDAPSearchResults res =
(LDAPSearchResults)referralResults.elementAt(0);
if ((!res.persistentSearch && res.hasMoreElements()) ||
(res.persistentSearch)) {
Object obj = res.nextElement();
if (obj != null) {
return obj;
}
if ((obj == null) || (!res.hasMoreElements())) {
referralResults.removeElementAt(0);
}
} else {
referralResults.removeElementAt(0);
}
return null;
}
/**
* Returns true if there are more search results
* to be returned. You can use this method in conjunction with the
* nextElement or next methods to iterate
* through each entry in the results. For example:
*
* LDAPSearchResults res = ld.search( MY_SEARCHBASE,
* LDAPConnection.SCOPE_BASE, MY_FILTER,
* null, false );
* while ( res.hasMoreElements() ) {
* LDAPEntry findEntry = (LDAPEntry)res.nextElement();
* ...
* }
*
* @return true if there are more search results.
* @see netscape.ldap.LDAPSearchResults#nextElement()
* @see netscape.ldap.LDAPSearchResults#next()
*/
public boolean hasMoreElements() {
while ((entries.size() == 0) && (!searchComplete)) {
fetchResult();
}
if ((entries.size() == 0) &&
((exceptions == null) || (exceptions.size() == 0))) {
while (referralResults.size() > 0) {
LDAPSearchResults res =
(LDAPSearchResults)referralResults.elementAt(0);
if (res.hasMoreElements())
return true;
else
referralResults.removeElementAt(0);
}
}
return ((entries.size() > 0) ||
((exceptions != null) && (exceptions.size() > 0)));
}
/**
* Returns a count of queued search results immediately available for
* processing.
* A search result is either a search entry or an exception. If the
* search is asynchronous (batch size not 0), this reports the number
* of results received so far.
* @return count of search results immediatly available for processing
*/
public int getCount() {
while (resultSource != null && resultSource.getMessageCount() > 0) {
fetchResult();
}
int count = entries.size();
for ( int i = 0; i < referralResults.size(); i++ ) {
LDAPSearchResults res =
(LDAPSearchResults)referralResults.elementAt(i);
count += res.getCount();
}
if ( exceptions != null ) {
count += exceptions.size();
}
return count;
}
/**
* Returns message ID.
* @return Message ID.
*/
int getMessageID() {
if ( resultSource == null ) {
return -1;
}
return resultSource.getMessageID();
}
/**
* Fetchs the next result, for asynchronous searches.
*/
private synchronized void fetchResult() {
/* Asynchronous case */
if ( resultSource != null ) {
synchronized( this ) {
if (searchComplete || firstResult) {
firstResult = false;
return;
}
LDAPMessage msg = null;
try {
msg = resultSource.nextMessage();
} catch (LDAPException e) {
add(e);
currConn.releaseSearchListener(resultSource);
searchComplete = true;
return;
}
if (msg == null) { // Request abandoned
searchComplete = true;
currConn.releaseSearchListener(resultSource);
return;
} else if (msg instanceof LDAPResponse) {
try {
// check response and see if we need to do referral
// v2: referral stored in the JDAPResult
currConn.checkSearchMsg(this, msg, currCons,
currBase, currScope, currFilter,
currAttrs, currAttrsOnly);
} catch (LDAPException e) {
add(e);
} finally {
currConn.releaseSearchListener(resultSource);
}
searchComplete = true;
if (connectionToClose != null) {
try {
connectionToClose.disconnect ();
} catch (LDAPException e) {
}
connectionToClose = null;
}
return;
} else {
try {
currConn.checkSearchMsg(this, msg, currCons,
currBase, currScope, currFilter, currAttrs, currAttrsOnly);
} catch (LDAPException e) {
add(e);
}
}
}
}
}
}