summaryrefslogtreecommitdiffstats
path: root/source4/scripting/python/samba/schema.py
Commit message (Expand)AuthorAgeFilesLines
* s4-provision Remove setup_path, setup_dir and find_setup_dirAndrew Bartlett2011-02-071-11/+7
* s4-samba-tool: fixed exception handling in subcommandsAndrew Tridgell2010-11-291-1/+1
* s4-python: Some reformatting for the purpose of pydoctor.Jelmer Vernooij2010-11-281-1/+2
* s4-python: Fix formatting of docstrings for the purpose of pydoctor.Jelmer Vernooij2010-11-281-13/+17
* s4:schema.py - reformat and fix the security descriptorMatthias Dieter Wallnöfer2010-10-231-14/+23
* s4-provision Remove serverdn parameter from Schema()Andrew Bartlett2010-10-191-7/+3
* s4:provision Remove am_rodc from SchemaAndrew Bartlett2010-06-231-2/+3
* pydsdb: Mark all SamDB and Schema methods that are in pydsdb asJelmer Vernooij2010-06-201-2/+2
* Some more formatting fixes, move schema related functions from Ldb to Schema.Jelmer Vernooij2010-06-201-1/+4
* Move convert_to_openldap onto Schema class.Jelmer Vernooij2010-06-201-0/+5
* s4:provision Allow a specific prefix map to be loaded into a new schema provi...Andrew Bartlett2010-06-151-2/+3
* s4:provision Allow both additional and override prefixmaps in SchemaAndrew Bartlett2010-06-121-4/+7
* s4-python: Fix formatting.Jelmer Vernooij2010-06-111-4/+3
* s4-rodc: Set am_rodc flag during provisionAnatoliy Atanasov2010-05-171-2/+2
* s4:provision Pass in the invoication ID and NTDS Settings DN to Schema()Andrew Bartlett2010-04-201-3/+9
* s4-python: Cancel transaction properly in case of exceptions, fix formatting.Jelmer Vernooij2010-04-081-29/+33
* General cleanups of python code, hinted by pyflakes.Jelmer Vernooij2010-03-011-1/+1
* s4-schema: fixed the SDDL for the schema root security descriptorAndrew Tridgell2010-01-091-10/+14
* s4-schema: switch to W2K8-R2 schemaAndrew Tridgell2010-01-081-2/+2
* s4: fix SD update and password change in upgrade scriptMatthieu Patou2009-11-281-2/+2
* s4:provision - Removed dependency on full Samba 3 schema from FDSEndi S. Dewata2009-11-161-5/+14
* s4:provision Add C binding to get at the generate schemaAndrew Bartlett2009-11-121-0/+23
* s4:provision Remove unused 'sambadn' parameterAndrew Bartlett2009-11-121-1/+1
* s4:provision Move 'Schema' into it's own fileAndrew Bartlett2009-11-021-0/+140
3' href='#n113'>113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package com.netscape.cmscore.dbs;

import java.util.Arrays;
import java.util.Vector;

import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPControl;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPSearchConstraints;
import netscape.ldap.LDAPSearchResults;
import netscape.ldap.LDAPSortKey;
import netscape.ldap.controls.LDAPSortControl;
import netscape.ldap.controls.LDAPVirtualListControl;
import netscape.ldap.controls.LDAPVirtualListResponse;

import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.dbs.IDBRegistry;
import com.netscape.certsrv.dbs.IDBVirtualList;
import com.netscape.certsrv.dbs.IElementProcessor;
import com.netscape.certsrv.logging.ILogger;

/**
 * A class represents a virtual list of search results.
 * Note that this class must be used with DS4.0.
 *
 * @author thomask
 * @author mzhao
 * @version $Revision$, $Date$
 */
public class DBVirtualList<E> implements IDBVirtualList<E> {

    private IDBRegistry mRegistry = null;
    private LDAPConnection mConn = null;
    private String mBase = null;
    private String mFilter = null;
    private String mAttrs[] = null;
    // virtual list size
    private int mSize = -1;

    private Vector<E> mEntries = new Vector<E>();
    // mSize is get or not?
    private boolean mInitialized = false;
    private LDAPSortKey[] mKeys;
    private LDAPControl[] mPageControls = null;
    // page buffer size
    private int mPageSize = 10;
    // the top of the buffer
    private int mTop = 0;
    private int mBeforeCount;
    private int mAfterCount;
    // the index of the first entry returned
    private int mSelectedIndex = 0;
    private int mJumpToIndex = 0;
    private int mJumpToInitialIndex = 0; // Initial index hit in jumpto operation
    private int mJumpToDirection = 1; // Do we proceed forward or backwards
    private String mJumpTo = null; // Determines if this is the jumpto case

    private ILogger mLogger = CMS.getLogger();

    /**
     * Constructs a virtual list.
     * Be sure to setPageSize() later if your pageSize is not the default 10
     * Be sure to setSortKey() before fetchs
     *
     * param registry the registry of attribute mappers
     * param c the ldap connection. It has to be version 3 and upper
     * param base the base distinguished name to search from
     * param filter search filter specifying the search criteria
     * param attrs list of attributes that you want returned in the search results
     */
    public DBVirtualList(IDBRegistry registry, LDAPConnection c,
            String base, String filter, String attrs[]) throws EBaseException {
        mRegistry = registry;
        mFilter = filter;
        mBase = base;
        mAttrs = attrs;
        CMS.debug("In DBVirtualList filter attrs filter: " + filter
                 + " attrs: " + Arrays.toString(attrs));
        mPageControls = new LDAPControl[2];
        try {
            mConn = (LDAPConnection) c.clone();
        } catch (Exception e) {
            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED",
                        e.toString()));
        }
    }

    /**
     * Constructs a virtual list.
     * Be sure to setPageSize() later if your pageSize is not the default 10
     *
     * param registry the registry of attribute mappers
     * param c the ldap connection. It has to be version 3 and upper
     * param base the base distinguished name to search from
     * param filter search filter specifying the search criteria
     * param attrs list of attributes that you want returned in the search results
     * param sortKey the attributes to sort by
     */
    public DBVirtualList(IDBRegistry registry, LDAPConnection c,
            String base, String filter, String attrs[], String sortKey[])
            throws EBaseException {

        CMS.debug("In DBVirtualList filter attrs sotrKey[]  filter: " + filter
                 + " attrs: " + Arrays.toString(attrs));
        mRegistry = registry;
        mFilter = filter;
        try {
            mConn = (LDAPConnection) c.clone();
        } catch (Exception e) {
            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED",
                        e.toString()));
        }
        mBase = base;
        mAttrs = attrs;
        mPageControls = new LDAPControl[2];
        setSortKey(sortKey);
    }

    /**
     * Constructs a virtual list.
     * Be sure to setPageSize() later if your pageSize is not the default 10
     *
     * param registry the registry of attribute mappers
     * param c the ldap connection. It has to be version 3 and upper
     * param base the base distinguished name to search from
     * param filter search filter specifying the search criteria
     * param attrs list of attributes that you want returned in the search results
     * param sortKey the attribute to sort by
     */
    public DBVirtualList(IDBRegistry registry, LDAPConnection c,
            String base, String filter, String attrs[], String sortKey)
            throws EBaseException {

        CMS.debug("In DBVirtualList filter attrs sortKey   filter: " + filter + " attrs: " + Arrays.toString(attrs));
        mRegistry = registry;
        mFilter = filter;
        try {
            mConn = (LDAPConnection) c.clone();
        } catch (Exception e) {
            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED",
                        e.toString()));
        }
        mBase = base;
        mAttrs = attrs;
        mPageControls = new LDAPControl[2];
        setSortKey(sortKey);
    }

    /**
     * Constructs a virtual list.
     *
     * param registry the registry of attribute mappers
     * param c the ldap connection. It has to be version 3 and upper
     * param base the base distinguished name to search from
     * param filter search filter specifying the search criteria
     * param attrs list of attributes that you want returned in the search results
     * param sortKey the attributes to sort by
     * param pageSize the size of a page. There is a 3*pageSize buffer maintained so
     * pageUp and pageDown won't invoke fetch from ldap server
     */
    public DBVirtualList(IDBRegistry registry, LDAPConnection c,
            String base, String filter, String attrs[], String sortKey[],
            int pageSize) throws EBaseException {

        CMS.debug("In DBVirtualList filter attrs sortKey[] pageSize filter: "
                 + filter + " attrs: " + Arrays.toString(attrs)
                 + " pageSize " + pageSize);
        mRegistry = registry;
        mFilter = filter;
        try {
            mConn = (LDAPConnection) c.clone();
        } catch (Exception e) {
            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED",
                        e.toString()));
        }
        mBase = base;
        mAttrs = attrs;
        mPageControls = new LDAPControl[2];
        setSortKey(sortKey);
        setPageSize(pageSize);
    }

    /**
     * Constructs a virtual list.
     *
     * param registry the registry of attribute mappers
     * param c the ldap connection. It has to be version 3 and upper
     * param base the base distinguished name to search from
     * param filter search filter specifying the search criteria
     * param attrs list of attributes that you want returned in the search results
     * param sortKey the attribute to sort by
     * param pageSize the size of a page. There is a 3*pageSize buffer maintained so
     * pageUp and pageDown won't invoke fetch from ldap server
     */
    public DBVirtualList(IDBRegistry registry, LDAPConnection c,
            String base, String filter, String attrs[], String sortKey,
            int pageSize) throws EBaseException {

        CMS.debug("In DBVirtualList filter attrs sortKey pageSize filter: "
                 + filter + " attrs: " + Arrays.toString(attrs)
                 + " pageSize " + pageSize);
        mRegistry = registry;
        mFilter = filter;
        try {
            mConn = (LDAPConnection) c.clone();
        } catch (Exception e) {
            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED",
                        e.toString()));
        }
        mBase = base;
        mAttrs = attrs;
        mPageControls = new LDAPControl[2];
        setSortKey(sortKey);
        setPageSize(pageSize);
    }

    public DBVirtualList(IDBRegistry registry, LDAPConnection c,
            String base, String filter, String attrs[],
            String startFrom, String sortKey,
            int pageSize) throws EBaseException {

        CMS.debug("In DBVirtualList filter attrs startFrom sortKey pageSize "
                 + "filter: " + filter
                 + " attrs: " + Arrays.toString(attrs)
                 + " pageSize " + pageSize + " startFrom " + startFrom);
        mRegistry = registry;
        mFilter = filter;
        try {
            mConn = (LDAPConnection) c.clone();
        } catch (Exception e) {
            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED",
                        e.toString()));
        }
        mBase = base;
        mAttrs = attrs;
        mPageControls = new LDAPControl[2];
        mJumpTo = startFrom;
        setSortKey(sortKey);
        // setPageSize(pageSize);

        if (pageSize < 0) {
            mJumpToDirection = -1;
        }
        mPageSize = pageSize;

        mBeforeCount = 0;
        mAfterCount = mPageSize;
    }

    /**
     * Set the paging size of this virtual list.
     * The page size here is just a buffer size. A buffer is kept around
     * that is three times as large as the number of visible entries.
     * That way, you can scroll up/down several items(up to a page-full)
     * without refetching entries from the directory.
     *
     * @param size the page size
     */
    public void setPageSize(int size) {

        if (mJumpTo != null) {
            return;
        }

        mPageSize = size;
        mBeforeCount = 0; //mPageSize;
        mAfterCount = mPageSize; // mPageSize + mPageSize;

        //CMS.debug("In setPageSize " + size + " mBeforeCount " + mBeforeCount + " mAfterCount " + mAfterCount);
    }

    /**
     * set the sort key
     *
     * @param sortKey the attribute to sort by
     */
    public void setSortKey(String sortKey) throws EBaseException {
        String keys[] = new String[1];

        keys[0] = sortKey;
        setSortKey(keys);
    }

    /**
     * set the sort key
     *
     * @param sortKey the attributes to sort by
     */
    public void setSortKey(String[] sortKeys) throws EBaseException {
        if (sortKeys == null)
            throw new EBaseException("sort keys cannot be null");
        try {

            mKeys = new LDAPSortKey[sortKeys.length];
            String la[] = mRegistry.getLDAPAttributes(sortKeys);

            for (int j = 0; j < sortKeys.length; j++) {
                mKeys[j] = new LDAPSortKey(la[j]);
            }
        } catch (Exception e) {

            /*LogDoc
             *
             * @phase local ldap search
             * @reason Failed at setSortKey.
             * @message DBVirtualList: <exception thrown>
             */
            mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, ILogger.LL_FAILURE,
                    CMS.getLogMessage("OPERATION_ERROR", e.toString()));
        }
        // Paged results also require a sort control
        if (mKeys != null) {
            mPageControls[0] =
                    new LDAPSortControl(mKeys, true);
        } else {
            throw new EBaseException("sort keys cannot be null");
        }
    }

    /**
     * Retrieves the size of this virtual list.
     * Recommend to call getSize() before getElementAt() or getElements()
     * since you'd better check if the index is out of bound first.
     */
    public int getSize() {
        if (!mInitialized) {
            mInitialized = true;
            // Do an initial search to get the virtual list size
            // Keep one page before and one page after the start
            if (mJumpTo == null) {
                mBeforeCount = 0; //mPageSize;
                mAfterCount = mPageSize; //  mPageSize + mPageSize;
            }
            // Create the initial paged results control
            /* Since this one is only used to get the size of the virtual list;
             we don't care about the starting index. If there is no partial
             match, the first one before (or after, if none before) is returned
             as the index entry. Instead of "A", you could use the other
             constructor and specify 0 both for startIndex and for
             contentCount. */
            LDAPVirtualListControl cont = null;

            if (mJumpTo == null) {
                cont = new LDAPVirtualListControl("A",
                            mBeforeCount,
                            mAfterCount);
            } else {

                if (mPageSize < 0) {
                    mBeforeCount = mPageSize * -1;
                    mAfterCount = 0;
                }
                cont = new LDAPVirtualListControl(mJumpTo,
                            mBeforeCount,
                            mAfterCount);
            }
            mPageControls[1] = cont;
            getJumpToPage();
        }

        CMS.debug("Getting Virtual List size: " + mSize);
        return mSize;
    }

    public int getSizeBeforeJumpTo() {

        if (!mInitialized || mJumpTo == null)
            return 0;

        int size = 0;

        if (mJumpToDirection < 0) {
            size = mTop + mEntries.size();
        } else {
            size = mTop;

        }

        return size;

    }

    public int getSizeAfterJumpTo() {

        if (!mInitialized || mJumpTo == null)
            return 0;

        int size = mSize - mTop;

        return size;

    }

    private synchronized boolean getEntries() {
        // Specify necessary controls for vlist
        // LDAPSearchConstraints cons = mConn.getSearchConstraints();
        LDAPSearchConstraints cons = new LDAPSearchConstraints();

        cons.setMaxResults(0);
        if (mPageControls != null) {
            cons.setServerControls(mPageControls);
            //System.out.println( "setting vlist control" );
        }
        // Empty the buffer
        mEntries.removeAllElements();
        // Do a search
        try {
            //what happen if there is no matching?
            String ldapFilter = mRegistry.getFilter(mFilter);
            String ldapAttrs[] = null;
            LDAPSearchResults result;

            if (mAttrs != null) {
                ldapAttrs = mRegistry.getLDAPAttributes(mAttrs);

                /*
                 LDAPv2.SCOPE_BASE:
                 (search only the base DN)
                 LDAPv2.SCOPE_ONE:
                 (search only entries under the base DN)
                 LDAPv2.SCOPE_SUB:
                 (search the base DN and all entries within its subtree)
                 */
                result = mConn.search(mBase,
                            LDAPConnection.SCOPE_ONE, ldapFilter, ldapAttrs,
                            false, cons);

            } else {
                result = mConn.search(mBase,
                            LDAPConnection.SCOPE_ONE, ldapFilter, null,
                            false, cons);
            }
            if (result == null) {
                return false;
            }
            int damageCounter = 0;

            while (result.hasMoreElements()) {
                LDAPEntry entry = (LDAPEntry) result.nextElement();

                try {
                    //maintain mEntries as vector of LDAPEntry
                    @SuppressWarnings("unchecked")
                    E o = (E) mRegistry.createObject(entry.getAttributeSet());

                    mEntries.addElement(o);
                } catch (Exception e) {

                    CMS.debug("Exception " + e);

                    /*LogDoc
                     *
                     * @phase local ldap search
                     * @reason Failed to get enties.
                     * @message DBVirtualList: <exception thrown>
                     */
                    mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, ILogger.LL_FAILURE,
                            CMS.getLogMessage("CMSCORE_DBS_VL_ADD", e.toString()));
                    // #539044
                    damageCounter++;
                    if (damageCounter > 100) {
                        mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, ILogger.LL_FAILURE,
                                CMS.getLogMessage("CMSCORE_DBS_VL_CORRUPTED_ENTRIES", Integer.toString(damageCounter)));
                        return false;
                    }
                }
            }
        } catch (Exception e) {

            /*LogDoc
             *
             * @phase local ldap search
             * @reason Failed to get enties.
             * @message DBVirtualList: <exception thrown>
             */
            CMS.debug("getEntries: exception " + e);

            mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, ILogger.LL_FAILURE,
                    CMS.getLogMessage("OPERATION_ERROR", e.toString()));
        }
        //System.out.println( "Returning " + mEntries.size() +
        //       " entries" );

        CMS.debug("getEntries returning " + mEntries.size());
        return true;
    }

    public int getCurrentIndex() {
        return mTop;
    }

    private synchronized boolean getJumpToPage() {
        try {
            // Get the actual entries
            if (!getEntries())
                return false;

            // Check if we have a control returned
            LDAPControl[] c = mConn.getResponseControls();
            LDAPVirtualListResponse nextCont = null;

            if (c != null) {
                for (LDAPControl control : c) {
                    if (control instanceof LDAPVirtualListResponse) {
                        nextCont = (LDAPVirtualListResponse)control;
                        break;
                    }
                }
            }

            if (nextCont != null) {
                mSelectedIndex = nextCont.getFirstPosition() - 1;
                mTop = Math.max(0, mSelectedIndex - mBeforeCount);

                CMS.debug("mTop " + mTop);
                if (mJumpTo != null) {
                    mJumpToInitialIndex = mTop;
                }

                // Now we know the total size of the virtual list box
                mSize = nextCont.getContentCount();
                ((LDAPVirtualListControl) mPageControls[1]).setListSize(mSize);
                mInitialized = true;
                //System.out.println( "Virtual window: " + mTop +
                //       ".." + (mTop+mEntries.size()-1) +
                //      " of " + mSize );
            } else {
                mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, ILogger.LL_FAILURE,
                        CMS.getLogMessage("CMSCORE_DBS_VL_NULL_RESPONSE"));
            }
            return true;
        } catch (Exception e) {
            // happens when connection is not available
            return false;
        }
    }

    /**
     * Get a page starting at "first" (although we may also fetch
     * some preceding entries)
     * Recommend to call getSize() before getElementAt() or getElements()
     * since you'd better check if the index is out of bound first.
     *
     * @param first the index of the first entry of the page you want to fetch
     */
    public boolean getPage(int first) {
        CMS.debug("getPage " + first);
        if (!mInitialized) {
            LDAPVirtualListControl cont = new LDAPVirtualListControl(0,
                    mBeforeCount,
                    mAfterCount, 0);

            mPageControls[1] = cont;
        }

        //CMS.debug("about to set range first " + first + " mBeforeCount " + mBeforeCount + " mAfterCount " + mAfterCount);
        ((LDAPVirtualListControl) mPageControls[1]).setRange(first, mBeforeCount, mAfterCount);
        return getPage();
    }

    /**
     * Fetch a buffer
     */
    private boolean getPage() {
        // Get the actual entries
        if (!getEntries())
            return false;

        // Check if we have a control returned
        LDAPControl[] c = mConn.getResponseControls();
        LDAPVirtualListResponse nextCont = null;

        if (c != null) {
            for (LDAPControl control : c) {
                if (control instanceof LDAPVirtualListResponse) {
                    nextCont = (LDAPVirtualListResponse)control;
                    break;
                }
            }
        }

        if (nextCont != null) {
            mSelectedIndex = nextCont.getFirstPosition() - 1;
            mTop = Math.max(0, mSelectedIndex - mBeforeCount);
            //CMS.debug("New mTop: " + mTop + " mSelectedIndex " + mSelectedIndex);
            // Now we know the total size of the virtual list box
            mSize = nextCont.getContentCount();
            ((LDAPVirtualListControl) mPageControls[1]).setListSize(mSize);
            mInitialized = true;
            //System.out.println( "Virtual window: " + mTop +
            //       ".." + (mTop+mEntries.size()-1) +
            //      " of " + mSize );
        } else {

            /*LogDoc
             *
             * @phase local ldap search
             */
            mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, ILogger.LL_FAILURE,
                    CMS.getLogMessage("CMSCORE_DBS_VL_NULL_RESPONSE"));
        }
        return true;
    }

    /**
     * Called by application to scroll the list with initial letters.
     * Consider text to be an initial substring of the attribute of the
     * primary sorting key(the first one specified in the sort key array)
     * of an entry.
     * If no entries match, the one just before(or after, if none before)
     * will be returned as mSelectedIndex
     *
     * @param text the prefix of the first entry of the page you want to fetch
     */
    public boolean getPage(String text) {
        mPageControls[1] =
                new LDAPVirtualListControl(text,
                        mBeforeCount,
                        mAfterCount);
        //System.out.println( "Setting requested start to " +
        //      text + ", -" + mBeforeCount + ", +" +
        //      mAfterCount );
        return getPage();
    }

    /**
     * fetch data of a single list item
     * Recommend to call getSize() before getElementAt() or getElements()
     * since you'd better check if the index is out of bound first.
     * If the index is out of range of the virtual list, an exception will be thrown
     * and return null
     *
     * @param index the index of the element to fetch
     */
    public E getElementAt(int index) {

        /* mSize may not be init at this time! Bad !
         * the caller should really check the index is within bound before this
         * but I'll take care of this just in case they are too irresponsible
         */
        if (!mInitialized)
            mSize = getSize();

        CMS.debug("getElementAt: " + index + " mTop " + mTop);

        //System.out.println( "need entry " + index );
        if ((index < 0) || (index >= mSize)) {
            CMS.debug("returning null");
            return null;
        }

        if (mJumpTo != null) { //Handle the explicit jumpto case

            if (index == 0)
                mJumpToIndex = 0; // Keep a running jumpto index for this page of data
            else
                mJumpToIndex++;

            //CMS.debug("getElementAtJT: " + index  +  " mTop " + mTop + " mEntries.size() " + mEntries.size());

            if ((mJumpToDirection > 0) && (mJumpToInitialIndex + index >= mSize)) // out of data in forward paging jumpto case
            {
                CMS.debug("mJumpTo virtual list exhausted   mTop " + mTop + " mSize " + mSize);
                return null;
            }

            if (mJumpToIndex >= mEntries.size()) // In jumpto case, page of data has been exhausted
            {
                mJumpToIndex = 0; // new page will be needed reset running count

                if (mJumpToDirection > 0) { //proceed in positive direction past hit point
                    getPage(index + mJumpToInitialIndex + 1);
                } else { //proceed backwards from hit point
                    if (mTop == 0) {
                        getPage(0);
                        CMS.debug("asking for a page less than zero in reverse case, return null");
                        return null;
                    }

                    CMS.debug("getting page reverse mJumptoIndex  " + mJumpToIndex + " mTop " + mTop);
                    getPage(mTop);

                }

            }

            if (mJumpToDirection > 0) // handle getting entry in forward direction
            {
                return mEntries.elementAt(mJumpToIndex);
            } else { // handle getting entry in reverse direction
                int reverse_index = mEntries.size() - mJumpToIndex - 1;

                CMS.debug("reverse direction getting index " + reverse_index);

                if (reverse_index < 0 || reverse_index >= mEntries.size()) {
                    CMS.debug("reverse_index out of range " + reverse_index);
                    return null;
                }
                return mEntries.elementAt(reverse_index);
            }
        }

        //CMS.debug("getElementAt noJumpto: " + index);

        if ((index < mTop) || (index >= mTop + mEntries.size())) { // handle the non jumpto case
            //fetch a new page
            //System.out.println( "fetching a page starting at " +
            //        index );
            //   CMS.debug("getElementAt noJumpto: getting page index: " + index + " mEntries.size() " + mEntries.size() + " mTop: " + mTop);
            getPage(index);
        }

        int offset = index - mTop;

        if ((offset < 0) || (offset >= mEntries.size()))
            //XXX
            return null; //("No entry at " + index);
        else
            return mEntries.elementAt(offset);
    }

    public E getJumpToElementAt(int i) {
        return mEntries.elementAt(i);
    }

    /**
     * This function processes elements as soon as it arrives. It is
     * more memory-efficient.
     */
    public void processElements(int startidx, int endidx, IElementProcessor ep)
            throws EBaseException {

        /* mSize may not be init at this time! Bad !
         * the caller should really check the index is within bound before this
         * but I'll take care of this just in case they are too irresponsible
         */
        if (!mInitialized)
            mSize = getSize();

        // short-cut the existing code ... :(
        if (mJumpTo != null) {
            for (int i = startidx; i <= endidx; i++) {
                Object element = getJumpToElementAt(i);

                if (element != null)
                    ep.process(element);
            }
            return;
        }

        //guess this is what you really mean to try to improve performance
        if (startidx >= endidx) {
            throw new EBaseException("startidx must be less than endidx");
        } else {
            setPageSize(endidx - startidx);
            getPage(startidx);
        }

        for (int i = startidx; i <= endidx; i++) {
            Object element = getElementAt(i);

            if (element != null)
                ep.process(element);
        }
    }

    /**
     * get the virutal selected index
     */
    public int getSelectedIndex() {
        return mSelectedIndex;
    }

    /**
     * get the top of the buffer
     */
    public int getFirstIndex() {
        return mTop;
    }
}