flex.messaging.io.PagedRowSet Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flex.messaging.io;
import flex.messaging.util.UUIDUtils;
import javax.sql.RowSet;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A wrapper for a RowSet to make it pageable. This technique is recommended
* if the RowSet is 'too big' to download all at once, AND the developer's
* client-side code is capable of dealing with non-fully-populated recordsets,
* that is, ActionScript RecordSets which are missing some of their data.
*
* @version 1.0
*/
public class PagedRowSet implements PageableRowSet
{
private RowSet rowSet;
private String[] colNames;
private int pageSize = 50; //Default to 50 records a page
private int colCount = 0;
private int rowCount = 0;
private String id = null;
private String serviceName = null;
/**
* Pageable Rowset Service Name.
*/
public static final String DEFAULT_PAGING_SERVICE_NAME = "PageableRowSetCache";
/**
* Constructor
*
* Creates a UUID for this object. Format: `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
*
*
* @param r The RowSet to be paged.
* @param p The initial page size.
*/
public PagedRowSet(RowSet r, int p)
{
serviceName = DEFAULT_PAGING_SERVICE_NAME;
rowSet = r;
pageSize = p;
id = UUIDUtils.createUUID();
init();
}
/**
* Allows the unique id generation of the RowSet to be toggled.
* @see #PagedRowSet(RowSet, int)
*
* @param r the row set
* @param p the page size
* @param createID should we create an id?
*/
public PagedRowSet(RowSet r, int p, boolean createID)
{
serviceName = DEFAULT_PAGING_SERVICE_NAME;
rowSet = r;
pageSize = p;
if (createID)
{
id = UUIDUtils.createUUID();
}
init();
}
private void init()
{
if (rowSet != null)
{
//Initialize columns
initColumns();
//Initialize records
initRecords();
}
else
{
colNames = new String[0];
}
}
private synchronized void initColumns()
{
try
{
ResultSetMetaData rsmd = rowSet.getMetaData();
if (rsmd != null)
{
colCount = rsmd.getColumnCount();
}
}
catch (SQLException ex)
{
colCount = 0;
}
}
private synchronized void initRecords()
{
//Determine rs size
if (rowSet != null)
{
try
{
int currentIndex = rowSet.getRow();
//Go to the end and get that row number
if (rowSet.last())
{
rowCount = rowSet.getRow();
}
//Put the cursor back
if (currentIndex > 0)
{
rowSet.absolute(currentIndex);
}
else
{
rowSet.beforeFirst();
}
}
catch (SQLException ex)
{
//TODO: Decide whether if absolute() not be supported, try first() as a last resort??
try
{
rowSet.first();
}
catch (SQLException se)
{
//we won't try anymore.
}
}
}
}
/**
* List the column names of the result set.
*
* @return String[] An array of the column names as strings, as ordered
* by the result set provider's column number assignment.
*/
public synchronized String[] getColumnNames()
{
// Cache the column names lookup
if (colNames == null)
{
try
{
//Ensure column count is initialized
if (colCount == 0)
{
initColumns();
}
colNames = new String[colCount];
for (int i = 0; i < colCount; i++)
{
//Note: column numbers start at 1
colNames[i] = rowSet.getMetaData().getColumnName(i + 1);
}
}
catch (SQLException ex)
{
colNames = new String[0];
}
}
// Create a copy
String[] ret = new String[colNames.length];
System.arraycopy(colNames, 0, ret, 0, colNames.length);
return ret;
}
/**
* Use this method to get a map of the index used to start the data page,
* and an array of arrays of the actual data page itself.
*
* @param startIndex starting index
* @param count how many records to return
* @return Map A map with two fields, the index of the row to start the page, and an array of
* arrays for the actual data page.
* @throws SQLException if unable to get data from the rowset
*/
public synchronized Map getRecords(int startIndex, int count) throws SQLException
{
List aRecords = new ArrayList(); //Don't initialize with count as it could be Integer.MAX_VALUE
//Ensure column count is initialized
if (colCount == 0)
{
initColumns();
}
//Starting index cannot be less than 1
if (startIndex < 1)
startIndex = 1;
//Populate the page, moving cursor to index
if (rowSet.absolute(startIndex))
{
//Loop over the result set for the count specified
for (int i = 0; i < count; i++)
{
boolean hasNext;
List row;
if (colCount > 0)
{
row = new ArrayList(rowCount + 1);
//Loop over columns to create an array for the row
for (int j = 1; j <= colCount; j++)
{
Object data = rowSet.getObject(j);
if (data instanceof Clob)
{
Clob clob = (Clob) data;
row.add(clob.getSubString(0, (int) clob.length()));
}
else if (data instanceof Blob)
{
Blob blob = (Blob) data;
byte[] bytes = blob.getBytes(1, (int) blob.length());
row.add(bytes);
}
else
row.add(data);
}
}
else //HACK: Handle any ColdFusion Query Objects that have no column metadata!
{
row = new ArrayList();
try
{
//Get as many columns as possible to build the row
//Stop on error or the first null column returned.
for (int j = 1; j <= 50; j++)
{
Object o = rowSet.getObject(j);
if (o != null)
{
row.add(o);
}
else
{
break;
}
}
}
catch (SQLException ex)
{
//Stop looking and just add the row.
}
}
aRecords.add(row.toArray());
hasNext = rowSet.next();
//Cursor beyond last row, stop!
if (!hasNext)
{
break;
}
}
}
Map result = new HashMap(2);
result.put(PAGE, aRecords.toArray());
result.put(CURSOR, Integer.valueOf(startIndex));
return result;
}
/**
* Get the row count.
*
* @return int The total number of rows in the result set.
*/
public int getRowCount()
{
return rowCount;
}
/**
* If this function returns a number >= the total number of rows in the result set,
* then the result set should be simply returned to the client in full. However,
* if it is < the total size, then this object itself is saved in Session data,
* and tagged with a unique ID.
*
* @return the page size
*/
public int getInitialDownloadCount()
{
return pageSize;
}
/**
* Return the id of this row set.
* @return the id
*/
public String getID()
{
return id;
}
/**
* Get the service name.
*
* @return String The name of the service that will manage this paged result.
*/
public String getServiceName()
{
return serviceName;
}
/**
* Set the service name.
*
* @param serviceName Update the name of the service that manages the pages for this query.
*/
public void setServicename(String serviceName)
{
this.serviceName = serviceName;
}
}