/* * Copyright (C) 2005-2007 Alfresco Software Limited. * * 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; either version 2 * of the License, or (at your option) any later version. * 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. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.webservice; import org.alfresco.service.ServiceRegistry; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * An abstract implementation of the query session that keeps track of the paging data. * It provides support for paging of results of Serializable[] instances. * * @author Derek Hulley * @since 2.1 */ public abstract class AbstractQuerySession implements QuerySession { private static Log logger = LogFactory.getLog(AbstractQuerySession.class); private String id; private long maxResults; private long batchSize; private ServerQuery query; /** a transient cache of the query results */ private transient RESULTSET cachedResults; /** * A pointer to the first row to be returned. When the last result is returned, the * position will be out of range of the current results by 1. */ private long position; /** * Keep track of whether the position has previously passed the end of a set of results. */ private boolean expectMoreResults; /** * Common constructor that initialises the session's id and batch size * * @param maxResults * the maximum number of results to retrieve for the query. This is not the page * size, which is normally significantly smaller. * @param batchSize * the batch size this session will use * @param query * the query that generates the results */ public AbstractQuerySession(long maxResults, long batchSize, ServerQuery query) { this.id = GUID.generate(); this.batchSize = batchSize; this.maxResults = maxResults; this.query = query; this.position = 0; this.expectMoreResults = true; } /** * {@inheritDoc} */ public String getId() { return this.id; } /** * {@inheritDoc} */ public ServerQuery getQuery() { return query; } /** * Helper method to get the results. This may be a cached value or may be * freshly retrieved from the query object. * * @param serviceRegistry the * @return the query results, new or cached */ protected RESULTSET getQueryResults(ServiceRegistry serviceRegistry) { if (cachedResults != null) { return cachedResults; } // Get the results and cache them cachedResults = query.execute(serviceRegistry, maxResults); // Done return cachedResults; } /** * {@inheritDoc} */ public boolean haveMoreResults() { return expectMoreResults; } protected abstract RESULTSETROW[] makeArray(int size); /** * Helper method to page through the results. The task of retrieving, unwrapping and * rewrapping the array of results (rows) is left up to the derived implementations. */ protected final RESULTSETROW[] getNextResults(RESULTSETROW[] allResults) { /* * This class can't manipulate the query to get the results because each * query implementation's results (the array of rows) is contained within * a different type of object. This method helps */ long allResultsSize = allResults.length; RESULTSETROW[] batchedResults = null; if (position >= allResultsSize) { // We are already past the last result batchedResults = makeArray(0); // Position is after last position = allResultsSize; } else if (position == 0 && batchSize >= allResultsSize) { // We can give back the original results batchedResults = allResults; // Position is after last position = allResultsSize; } else if ((position + batchSize) >= allResultsSize) { // There isn't an excess of rows remaining, so copy to the last one long lastResultIndex = allResultsSize - 1L; long rowCopyCount = lastResultIndex - position; batchedResults = makeArray((int)rowCopyCount); System.arraycopy(allResults, (int)position, batchedResults, 0, (int)rowCopyCount); // Position is after last position = allResultsSize; } else { // There are an excess of rows remaining batchedResults = makeArray((int)batchSize); System.arraycopy(allResults, (int)position, batchedResults, 0, (int)batchSize); // Position increases by the batch size position += batchSize; } // Keep track of whether we expect more results if (position >= allResultsSize) { expectMoreResults = false; } // Done if (logger.isDebugEnabled()) { logger.debug("\n" + "Fetched next set of results: \n" + " Total results count: " + allResultsSize + "\n" + " Batch size: " + batchedResults.length + "\n" + " New Position: " + position); } return batchedResults; // long allResultsSize = allResults.getTotalRowCount(); // // ResultSet batchedResults = null; // if (position >= allResultsSize) // { // // We are already past the last result // batchedResults = new ResultSet(); // batchedResults.setRows(new ResultSetRow[] {}); // batchedResults.setTotalRowCount(0); // batchedResults.setMetaData(allResults.getMetaData()); // // Position is after last // position = allResultsSize; // } // else if (position == 0 && batchSize >= allResultsSize) // { // // We can give back the original results // batchedResults = allResults; // // Position is after last // position = allResultsSize; // } // else if ((position + batchSize) >= allResultsSize) // { // // There isn't an excess of rows remaining, so copy to the last one // long lastResultIndex = allResultsSize - 1L; // long rowCopyCount = lastResultIndex - position; // ResultSetRow[] batchedRows = new ResultSetRow[(int)rowCopyCount]; // ResultSetRow[] allRows = allResults.getRows(); // System.arraycopy(allRows, (int)position, batchedRows, 0, (int)rowCopyCount); // // Build the results // batchedResults = new ResultSet(); // batchedResults.setRows(batchedRows); // batchedResults.setTotalRowCount(rowCopyCount); // batchedResults.setMetaData(allResults.getMetaData()); // // Position is after last // position = allResultsSize; // } // else // { // // There are an excess of rows remaining // ResultSetRow[] batchedRows = new ResultSetRow[(int)batchSize]; // ResultSetRow[] allRows = allResults.getRows(); // System.arraycopy(allRows, (int)position, batchedRows, 0, (int)batchSize); // // Build the results // batchedResults = new ResultSet(); // batchedResults.setRows(batchedRows); // batchedResults.setTotalRowCount(batchSize); // batchedResults.setMetaData(allResults.getMetaData()); // // Position increases by the batch size // position += batchSize; // } // // Keep track of whether we expect more results // if (position >= allResultsSize) // { // expectMoreResults = false; // } // // Done // if (logger.isDebugEnabled()) // { // logger.debug("\n" + // "Fetched net set of results: \n" + // " Total results count: " + allResultsSize + "\n" + // " Batch size: " + batchedResults.getTotalRowCount() + "\n" + // " New Position: " + position); // } // return batchedResults; } }