/* $Id$ */
/**
* 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 org.apache.manifoldcf.crawler.bins;
import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.crawler.interfaces.*;
import org.apache.manifoldcf.crawler.interfaces.CacheKeyFactory;
import org.apache.manifoldcf.crawler.system.ManifoldCF;
import org.apache.manifoldcf.crawler.system.Logging;
import java.util.*;
/** This class manages the docbins table.
* A row in this table represents a document bin. The count that is kept is the
* number of documents in this particular bin that have been assigned a document priority.
*
* <br><br>
* <b>docbins</b>
* <table border="1" cellpadding="3" cellspacing="0">
* <tr class="TableHeadingColor">
* <th>Field</th><th>Type</th><th>Description </th>
* <tr><td>binname</td><td>VARCHAR(255)</td><td>Primary Key</td></tr>
* <tr><td>bincounter</td><td>BIGINT</td><td></td></tr>
* </table>
* <br><br>
*
*/
public class BinManager extends org.apache.manifoldcf.core.database.BaseTable implements IBinManager
{
public static final String _rcsid = "@(#)$Id$";
// Field names
public final static String connectorClassField = "connectorclass";
public final static String binNameField = "binname";
public final static String binCounterField = "bincounter";
/** Constructor.
*@param database is the database handle.
*/
public BinManager(IDBInterface database)
throws ManifoldCFException
{
super(database,"docbins");
}
/** Install or upgrade this table.
*/
@Override
public void install()
throws ManifoldCFException
{
// Standard practice: outer loop for installs
while (true)
{
Map existing = getTableSchema(null,null);
if (existing == null)
{
HashMap map = new HashMap();
// HSQLDB does not like null primary keys!!
map.put(connectorClassField,new ColumnDescription("VARCHAR(255)",false,true,null,null,false));
map.put(binNameField,new ColumnDescription("VARCHAR(255)",false,true,null,null,false));
map.put(binCounterField,new ColumnDescription("FLOAT",false,false,null,null,false));
performCreate(map,null);
}
else
{
// Upgrade
if (existing.get(connectorClassField) == null) {
HashMap map = new HashMap();
map.put(connectorClassField,new ColumnDescription("VARCHAR(255)",false,true,null,null,false));
performAlter(map,null,null,null);
}
}
// Index management goes here
IndexDescription binIndex = new IndexDescription(true,new String[]{connectorClassField,binNameField});
// Get rid of indexes that shouldn't be there
Map indexes = getTableIndexes(null,null);
Iterator iter = indexes.keySet().iterator();
while (iter.hasNext())
{
String indexName = (String)iter.next();
IndexDescription id = (IndexDescription)indexes.get(indexName);
if (binIndex != null && id.equals(binIndex))
binIndex = null;
else if (indexName.indexOf("_pkey") == -1)
// This index shouldn't be here; drop it
performRemoveIndex(indexName);
}
// Add the ones we didn't find
if (binIndex != null)
performAddIndex(null,binIndex);
break;
}
}
/** Uninstall.
*/
@Override
public void deinstall()
throws ManifoldCFException
{
performDrop(null);
}
/** Reset all bins */
@Override
public void reset()
throws ManifoldCFException
{
performDelete("", null, null);
}
/** Get N bin values (and set next one). If the record does not yet exist, create it with a starting value.
* We expect this to happen within a transaction!!.
*@param connectorClass is the class name of the connector
*@param binName is the name of the bin (256 char max)
*@param newBinValue is the value to use if there is no such bin yet. This is the value that will be
* returned; what will be stored will be that value + 1.
*@param count is the number of values desired.
*@return the counter values.
*/
@Override
public double[] getIncrementBinValues(String connectorClass, String binName, double newBinValue, int count)
throws ManifoldCFException
{
double[] returnValues = new double[count];
// SELECT FOR UPDATE/MODIFY is the most common path
ArrayList params = new ArrayList();
String query = buildConjunctionClause(params,new ClauseDescription[]{
new UnitaryClause(connectorClassField,connectorClass),
new UnitaryClause(binNameField,binName)});
IResultSet result = performQuery("SELECT "+binCounterField+" FROM "+getTableName()+" WHERE "+query+" FOR UPDATE",params,null,null);
if (result.getRowCount() > 0)
{
IResultRow row = result.getRow(0);
Double value = (Double)row.getValue(binCounterField);
double rval = value.doubleValue();
if (rval < newBinValue)
rval = newBinValue;
// rval is the starting value; compute the entire array based on it.
for (int i = 0; i < count; i++)
{
returnValues[i] = rval;
rval += 1.0;
}
HashMap map = new HashMap();
map.put(binCounterField,new Double(rval));
performUpdate(map," WHERE "+query,params,null);
}
else
{
for (int i = 0; i < count; i++)
{
returnValues[i] = newBinValue;
newBinValue += 1.0;
}
HashMap map = new HashMap();
map.put(connectorClassField,connectorClass);
map.put(binNameField,binName);
map.put(binCounterField,new Double(newBinValue));
performInsert(map,null);
}
return returnValues;
}
/** Get N bin values (and set next one). If the record does not yet exist, create it with a starting value.
* This method invokes its own retry-able transaction.
*@param connectorClass is the class name of the connector
*@param binName is the name of the bin (256 char max)
*@param newBinValue is the value to use if there is no such bin yet. This is the value that will be
* returned; what will be stored will be that value + 1.
*@param count is the number of values desired.
*@return the counter values.
*/
@Override
public double[] getIncrementBinValuesInTransaction(String connectorClass, String binName, double newBinValue, int count)
throws ManifoldCFException
{
while (true)
{
long sleepAmt = 0L;
beginTransaction();
try
{
return getIncrementBinValues(connectorClass, binName, newBinValue, count);
}
catch (Error e)
{
signalRollback();
throw e;
}
catch (RuntimeException e)
{
signalRollback();
throw e;
}
catch (ManifoldCFException e)
{
signalRollback();
if (e.getErrorCode() == e.DATABASE_TRANSACTION_ABORT)
{
if (Logging.perf.isDebugEnabled())
Logging.perf.debug("Aborted transaction obtaining docpriorities: "+e.getMessage());
sleepAmt = getSleepAmt();
continue;
}
throw e;
}
finally
{
endTransaction();
sleepFor(sleepAmt);
}
}
}
}