/*
* (C) Copyright IBM Corp. 2008
*
* LICENSE: Eclipse Public License v1.0
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.ibm.db2j;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Map;
import java.util.Vector;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.store.access.Qualifier;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.vti.IFastPath;
import org.apache.derby.vti.VTICosting;
import org.apache.derby.vti.VTIEnvironment;
import com.ibm.gaiandb.CachedHashMap;
import com.ibm.gaiandb.GaianChildVTI;
import com.ibm.gaiandb.GaianDBConfig;
import com.ibm.gaiandb.GaianResultSetMetaData;
import com.ibm.gaiandb.Logger;
import com.ibm.gaiandb.diags.GDBMessages;
import com.ibm.gaiandb.searchapis.SearchREST;
import com.ibm.gaiandb.searchapis.SearchSIAPI;
/**
* @author DavidVyvyan
*/
public class DocumentFinder extends VTI60 implements VTICosting, IFastPath, GaianChildVTI {
// Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice.
public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2008";
private static final Logger logger = new Logger( "DocumentFinder", 20 );
private String searchPort = null;
private String searchString = null;
private String collections = null;
// Vector of Integers
private Vector<DataValueDescriptor[]> resultRows = null;
private int index = 0;
// search sring -> latest row count
private static Map<String, Long> estimatedRowCounts =
Collections.synchronizedMap( new CachedHashMap<String, Long>( GaianTable.QRY_METADATA_CACHE_SIZE ) );
// private static final ResultSetMetaData rsmd = new SearchMetaData();
private ResultSetMetaData rsmd = null;
// public DocumentFinder() {
// }
/**
* @param searchString
* @throws Exception
*/
public DocumentFinder(String searchString) throws Exception {
super();
logger.logInfo("Entered DocumentFinder(searchString) constructor");
rsmd = new GaianResultSetMetaData( "DNUM INT, DURL VARCHAR(100), DINFO CLOB(32K)" );
this.searchString = searchString;
}
public DocumentFinder(String searchString, String collections) throws Exception {
super();
logger.logInfo("Entered DocumentFinder(searchString, collections) constructor");
rsmd = new GaianResultSetMetaData( "DNUM INT, DURL VARCHAR(100), DINFO VARCHAR(100)" );
this.searchString = searchString;
this.collections = collections;
}
/* (non-Javadoc)
* @see com.ibm.gaiandb.GaianChildVTI#setArgs(java.lang.String[])
*/
public void setArgs(String[] args) {
if ( 0 < args.length )
this.searchString = args[0];
if ( 1 < args.length )
this.searchPort = args[1];
}
/* (non-Javadoc)
* @see com.ibm.gaiandb.GaianChildVTI#setExtractConditions(org.apache.derby.iapi.store.access.Qualifier[][], int[])
*/
public void setExtractConditions(Qualifier[][] qualifiers, int[] logicalProjection, int[] columnsMapping) {
// No need to set qualifiers - the search string acts as a filter instead.
// Also ignore mappedColumns as column names are expected to be the same in the logical table.
}
// private void setSearchString( String searchString ) {
// this.searchString = searchString;
// }
// public DocumentFinder(final String searchString, String nodeDefName, GaianResultSetMetaData logicalTableRSMD) throws SQLException {
// super( nodeDefName, logicalTableRSMD );
// this.searchString = searchString;
// }
//
// public IFastPath execute(Qualifier[][] qualifiers, int[] projectedColumns) {
//
// DocumentFinder f = new DocumentFinder(searchString);
// }
/* (non-Javadoc)
* @see org.apache.derby.vti.IFastPath#executeAsFastPath()
*/
public boolean executeAsFastPath() throws StandardException, SQLException {
logger.logInfo("Entered executeAsFastPath()");
if ( null != resultRows ) {
logger.logInfo("Document results already in memory");
return true;
}
index = 0;
resultRows = new Vector<DataValueDescriptor[]>();
logger.logInfo("Setting up parameters");
String hostname = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindHostname" );
// if ( null == collections )
// collections = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindCollections" );
int maxResults = 100;
try {
String s = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindMaxResults" );
if ( null == s ) logger.logInfo("No maxResults property defined, using default value " + maxResults);
else maxResults = Integer.parseInt( s );
} catch ( NumberFormatException e ) {
logger.logWarning(GDBMessages.DSWRAPPER_MAX_RESULTS_PROP_ERROR, "Unable to read integer value from maxResults property: " + e);
}
if ( null == hostname ) hostname = "localhost";
if ( null == searchPort )
searchPort = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindRestPort" );
if ( null == searchPort )
searchPort = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindPort" );
String applicationName = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindEEUser" );
String applicationPassword = GaianDBConfig.getVTIProperty( DocumentFinder.class, "omnifindEEPassword" );
// // To build doc id hash values on decoded paths, set this property to anything but 0, false or an empty string.
// String hdp = GaianDBConfig.getVTIProperty( DocumentFinder.class, "hashDecodedPaths" );
// if ( null != hdp ) hdp = hdp.trim();
// boolean hashDecodedPaths = null != hdp && !"".equals(hdp) && !"0".equals(hdp) && !"false".equalsIgnoreCase(hdp);
if ( null == searchPort ) {
logger.logWarning(GDBMessages.DSWRAPPER_OMNIFINDRESTPORT_NOT_SET, "omnifindRestPort VTI property must be set in gaiandb_config.properties (aborting): e.g. " +
DocumentFinder.class.getName() + ".omnifindRestPort=8080");
return true;
}
logger.logInfo("Calling document search using hostname: " + hostname + ", port: " + searchPort +
", collections: " + collections + ", searchString: " + searchString + ", maxResults: " + maxResults +
", usr: " + applicationName + ", pwd: " + (null==applicationPassword?null:"<hidden value not null>"));
if ( null == applicationName || null == applicationPassword )
SearchREST.retrieveDocumentReferences(
resultRows, hostname, searchPort, collections, searchString + "&results=" + maxResults );
else {
try {
getClass().getClassLoader().loadClass(SearchSIAPI.class.getName());
// Referenced by reflection in SearchEnterprise - and a member of esapi.jar which must be in CLASSPATH
getClass().getClassLoader().loadClass(com.ibm.es.api.search.RemoteSearchFactory.class.getName());
} catch ( Throwable e ) {
logger.logWarning(GDBMessages.DSWRAPPER_OMNIFIND_VERIFY_ERROR, "Could not query Omnifind Enterprise (install siapi.jar and esapi.jar in GaianDB install dir): " + e);
return true;
}
SearchSIAPI.retrieveDocumentReferences(
resultRows, hostname, searchPort, collections, searchString, maxResults, applicationName, applicationPassword );
}
logger.logInfo("Document DNUMs loaded in memory: " + resultRows.size());
return true;
}
/* (non-Javadoc)
* @see org.apache.derby.vti.IFastPath#nextRow(org.apache.derby.iapi.types.DataValueDescriptor[])
*/
public int nextRow(DataValueDescriptor[] dvdr) throws StandardException, SQLException {
if ( null == resultRows ) {
logger.logWarning(GDBMessages.DSWRAPPER_NO_ROWS, "No rows to fetch, "+this.getClass().getSimpleName()+" did not execute - resultRows is null");
return IFastPath.SCAN_COMPLETED;
}
if ( index >= resultRows.size() ) {
// Keep track of row count for this search string
Long previousCount = estimatedRowCounts.get( searchString );
if ( null == previousCount || index > previousCount.longValue() )
estimatedRowCounts.put( searchString, new Long( index ) );
index = 0;
return IFastPath.SCAN_COMPLETED;
}
DataValueDescriptor[] row = resultRows.get(index++);
dvdr[0].setValue( row[0] );
dvdr[1].setValue( row[1] );
dvdr[2].setValue( row[2] );
return IFastPath.GOT_ROW;
}
/* (non-Javadoc)
* @see org.apache.derby.vti.IFastPath#currentRow(java.sql.ResultSet, org.apache.derby.iapi.types.DataValueDescriptor[])
*/
public void currentRow(ResultSet arg0, DataValueDescriptor[] arg1) throws StandardException, SQLException {
}
/* (non-Javadoc)
* @see org.apache.derby.vti.IFastPath#rowsDone()
*/
public void rowsDone() throws StandardException, SQLException {
close();
}
/**
* Provide the metadata for the query against the given table. Cloudscape
* calls this method when it compiles the SQL-J statement that uses this
* VTI class.
*
* @return the result set metadata for the query
*
* @exception SQLException thrown by JDBC calls
*/
public ResultSetMetaData getMetaData() throws SQLException {
logger.logInfo("DocumentFinder.getMetaData(): " + rsmd);
return rsmd;
}
/**
* Explicitly closes this PreparedStatement class. (Note: Cloudscape
* calls this method only after compiling a SELECT statement that uses
* this class.)<p>
*
* @see java.sql.Statement
*
* @exception SQLException on unexpected JDBC error
*/
public void close() {
logger.logInfo("DocumentFinder.close()");
reinitialise();
}
@Override
public boolean reinitialise() {
logger.logInfo("DocumentFinder.reinitialise()");
if ( null != resultRows ) {
resultRows.clear();
resultRows.trimToSize();
resultRows = null;
}
index = 0;
return true;
}
// /**
// * Return the number of columns in the user-specified table.
// *
// * @exception SQLException Thrown if there is an error getting the
// * metadata.
// */
// public int getColumnCount() throws SQLException {
//
// logger.logInfo("!!!!!!!!!!!!DocumentFinder.getColumnCount()!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// return rsmd.getColumnCount(); //DataSourcesManager.getLogicalTableRSMD( logicalTable ).getColumnCount();
// }
/* (non-Javadoc)
* @see org.apache.derby.vti.VTICosting#getEstimatedRowCount(org.apache.derby.vti.VTIEnvironment)
*/
public double getEstimatedRowCount(VTIEnvironment arg0) throws SQLException {
Long l = estimatedRowCounts.get( searchString );
double val = null == l ? 1000 : l.doubleValue() * 10; // multiply by 10 as this is XML!
logger.logInfo("getEstimatedRowCount() returning: " + val);
return val;
}
/* (non-Javadoc)
* @see org.apache.derby.vti.VTICosting#getEstimatedCostPerInstantiation(org.apache.derby.vti.VTIEnvironment)
*/
public double getEstimatedCostPerInstantiation(VTIEnvironment arg0) throws SQLException {
int rc = 0;
logger.logInfo("getEstimatedCostPerInstantiation() returning: " + rc);
return rc;
}
/* (non-Javadoc)
* @see org.apache.derby.vti.VTICosting#supportsMultipleInstantiations(org.apache.derby.vti.VTIEnvironment)
*/
public boolean supportsMultipleInstantiations(VTIEnvironment arg0) throws SQLException {
// For us to return true, we must ensure that results can be fetched repeatedly.
// i.e. that the cursor is set back to the first row every time we reach the end of it.
boolean rc = true;
logger.logInfo("supportsMultipleInstantiations() returning: " + rc);
return rc;
}
public boolean fetchNextRow(DataValueDescriptor[] row) throws Exception {
return IFastPath.GOT_ROW == nextRow(row);
}
public int getRowCount() throws Exception {
return resultRows.size();
}
public boolean isScrollable() {
return true;
}
@Override
public int getResultSetType() throws SQLException {
return ResultSet.TYPE_FORWARD_ONLY;
}
@Override
public int getResultSetConcurrency() throws SQLException {
return ResultSet.CONCUR_UPDATABLE;
}
public boolean isBeforeFirst() {
return 0 == index;
}
}