//-------------------------------------------------------------------------
// Desc: Class for displaying a index keys in HTML in a web page.
// Tabs: 3
//
// Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// 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, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: imonix.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
#define INDEX_LIST_FORM_NAME "IndexListForm"
#define REFERENCE_DRN_FIELD "DRNField"
#define CONTAINER_FIELD "ContainerField"
#define REFERENCES_PER_ROW 15
#define NO_CONTAINER_NUM 0xFFFF
#define MAX_RECORDS_TO_OUTPUT 100
#define KEY_LIST_INCREASE_SIZE 1024
#define REF_LIST_INCREASE_SIZE 4096
FSTATIC FLMUINT getIndexContainer(
HFDB hDb,
FLMUINT uiIndex);
FSTATIC void freeIndexListStatus(
IXLIST_STATUS * pIxListStatus,
FLMBOOL bFreeStructure);
FSTATIC void copyIndexListStatus(
IXLIST_STATUS * pDestIxListStatus,
IXLIST_STATUS * pSrcIxListStatus,
FLMBOOL bTransferKeyList);
FSTATIC RCODE imonDoIndexList(
IF_Thread * pThread);
/****************************************************************************
Desc: Prints the web page for listing index keys.
****************************************************************************/
RCODE F_IndexListPage::display(
FLMUINT uiNumParams,
const char ** ppszParams)
{
RCODE rc = FERR_OK;
char * pszErrType = NULL;
RCODE runRc = FERR_OK;
F_Session * pFlmSession = m_pFlmSession;
HFDB hDb;
FLMUINT uiIndex;
FLMUINT uiContainer;
char szTmp [32];
char * pszTmp;
char * pszOperation = NULL;
F_NameTable * pNameTable = NULL;
FLMBOOL bPerformIndexList = FALSE;
FLMBOOL bStopIndexList = FALSE;
FLMUINT uiIndexListThreadId;
IXLIST_STATUS IndexListStatus;
char szDbKey [F_SESSION_DB_KEY_LEN];
FlmRecord * pFromKey = NULL;
FlmRecord * pUntilKey = NULL;
FLMBOOL bHadFromKey = FALSE;
FLMBOOL bHadUntilKey = FALSE;
f_memset( &IndexListStatus, 0, sizeof( IXLIST_STATUS));
IndexListStatus.bIndexListRunning = FALSE;
IndexListStatus.bHaveIndexListStatus = FALSE;
// Acquire a FLAIM session
if (!pFlmSession)
{
rc = RC_SET( m_uiSessionRC);
goto ReportErrorExit;
}
// Get the database handle, if any
if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams,
ppszParams, pFlmSession, &hDb, szDbKey)))
{
goto ReportErrorExit;
}
if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable)))
{
goto ReportErrorExit;
}
// Get the index, if any - look in the form first - because
// it can be in both the form and the header. The one in the form
// takes precedence over the one in the header.
szTmp [0] = '\0';
uiIndex = 0;
pszTmp = &szTmp [0];
if (RC_BAD( getFormValueByName( "index",
&pszTmp, sizeof( szTmp), NULL)))
{
if( RC_BAD( ExtractParameter( uiNumParams, ppszParams,
"index", sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
}
if (szTmp[ 0])
{
uiIndex = f_atoud( szTmp);
}
// Get the container, if any.
szTmp [0] = '\0';
uiContainer = NO_CONTAINER_NUM;
if( RC_BAD( ExtractParameter( uiNumParams, ppszParams,
"container", sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
if (szTmp[ 0])
{
uiContainer = f_atoud( szTmp);
}
// Get the from and until keys and drns.
bHadFromKey = getKey( hDb, uiIndex, &pFromKey, FO_FIRST);
bHadUntilKey = getKey( hDb, uiIndex, &pUntilKey, FO_LAST);
// Get the value of the Operation field, if present.
getFormValueByName( "Operation",
&pszOperation, 0, NULL);
if( pszOperation)
{
if (f_stricmp( pszOperation, OPERATION_INDEX_LIST) == 0)
{
bPerformIndexList = TRUE;
}
else if (f_stricmp( pszOperation, OPERATION_STOP) == 0)
{
bStopIndexList = TRUE;
}
}
// See if we had an index list running. Get the index list thread ID
// if any.
szTmp [0] = '\0';
uiIndexListThreadId = 0;
if (RC_OK( ExtractParameter( uiNumParams, ppszParams,
"Running", sizeof( szTmp), szTmp)))
{
if (szTmp [0])
{
uiIndexListThreadId = f_atoud( szTmp);
IndexListStatus.bIndexListRunning = TRUE;
}
}
if (bPerformIndexList)
{
// Better not have both bIndexListRunning and bPerformIndexList set!
flmAssert( !IndexListStatus.bIndexListRunning);
if (bHadFromKey && bHadUntilKey)
{
// Run the index list.
if (RC_BAD( runRc = runIndexList( hDb, uiIndex,
pFromKey, pUntilKey,
&uiIndexListThreadId)))
{
pszErrType = (char *)"RUNNING INDEX LIST";
}
else
{
IndexListStatus.bIndexListRunning = TRUE;
}
}
}
// Stop the index list, if requested, or get the status.
if (IndexListStatus.bIndexListRunning)
{
// getIndexListStatus could change IndexListStatus.bIndexListRunning
// to FALSE.
getIndexListStatus( uiIndexListThreadId, bStopIndexList,
&IndexListStatus);
}
// Output the web page.
if (!IndexListStatus.bIndexListRunning && IndexListStatus.bHaveIndexListStatus)
{
// If we have index keys, output a page for viewing them.
printDocStart( "Index Key Results");
popupFrame();
}
else if (!IndexListStatus.bIndexListRunning)
{
printDocStart( "Run Index List");
if (pszErrType)
{
fnPrintf( m_pHRequest,
" ERROR %04X (%s) %s
\n");
printRecordStyle();
printStyle();
// Output html that will cause a refresh to occur.
fnPrintf( m_pHRequest,
""
"Index List\n",
m_pszURLString,
(unsigned)uiIndexListThreadId, szDbKey,
(unsigned)uiIndex, (unsigned)uiContainer);
fnPrintf( m_pHRequest, "\n"
"\n");
}
// Output the form for entering the index or keys
// and index list status, if the index list is running.
outputIndexListForm( hDb, szDbKey, uiIndex, uiContainer,
uiIndexListThreadId, pNameTable, &IndexListStatus);
// End the document
printDocEnd();
Exit:
fnEmit();
if (pszOperation)
{
f_free( &pszOperation);
}
if (pFromKey)
{
pFromKey->Release();
}
if (pUntilKey)
{
pUntilKey->Release();
}
freeIndexListStatus( &IndexListStatus, FALSE);
return( FERR_OK);
ReportErrorExit:
printErrorPage( rc);
goto Exit;
}
/****************************************************************************
Desc: Get a from or until key from the form.
****************************************************************************/
FLMBOOL F_IndexListPage::getKey(
HFDB hDb,
FLMUINT uiIndex,
FlmRecord ** ppKey,
FLMUINT uiKeyId)
{
FDB * pDb = NULL;
FLMBOOL bDummy;
IXD * pIxd;
IFD * pIfd;
FLMUINT uiLoop;
FLMUINT uiFieldCounter;
char szTmp [32];
char szFieldName [64];
char * pszTmp;
FLMUINT uiContainer;
FLMUINT uiRefDrn = 0;
FLMUINT uiValueLen;
FlmRecord * pKey;
void * pvFld;
FLMBOOL bHadFormData = FALSE;
*ppKey = NULL;
// Lookup the index definition
pDb = (FDB *)hDb;
if (RC_BAD( fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0,
&bDummy)))
{
goto Exit;
}
if (RC_BAD( fdictGetIndex(
pDb->pDict, pDb->pFile->bInLimitedMode,
uiIndex, NULL, &pIxd, TRUE)))
{
goto Exit;
}
// See if there is a reference DRN in the form
pszTmp = &szTmp [0];
szTmp [0] = 0;
f_sprintf( (char *)szFieldName, "%s_%u",
REFERENCE_DRN_FIELD, (unsigned)uiKeyId);
getFormValueByName( szFieldName, &pszTmp, sizeof( szTmp), NULL);
if (szTmp [0])
{
uiRefDrn = f_atoud( szTmp);
bHadFormData = TRUE;
}
// See if there is a container field in the form
uiContainer = NO_CONTAINER_NUM;
pszTmp = &szTmp [0];
szTmp [0] = 0;
f_sprintf( (char *)szFieldName, "%s_%u",
CONTAINER_FIELD, (unsigned)uiKeyId);
getFormValueByName( szFieldName, &pszTmp, sizeof( szTmp), NULL);
if (szTmp [0])
{
uiContainer = f_atoud( szTmp);
bHadFormData = TRUE;
}
// Allocate a key record
if( (pKey = f_new FlmRecord) == NULL)
{
goto Exit;
}
*ppKey = pKey;
if (uiContainer != NO_CONTAINER_NUM)
{
pKey->setContainerID( uiContainer);
}
pKey->setID( uiRefDrn);
if (RC_BAD( pKey->insertLast( 0, FLM_KEY_TAG, FLM_CONTEXT_TYPE, NULL)))
{
goto Exit;
}
uiFieldCounter = (FLMUINT)((uiKeyId == FO_FIRST)
? (FLMUINT)0
: (FLMUINT)FLM_FREE_TAG_NUMS);
for (uiLoop = 0, pIfd = pIxd->pFirstIfd;
uiLoop < pIxd->uiNumFlds;
uiLoop++, uiFieldCounter++, pIfd++)
{
pszTmp = NULL;
f_sprintf( (char *)szFieldName, "field%u", (unsigned)uiFieldCounter);
if (RC_OK( getFormValueByName( szFieldName, &pszTmp, 0, &uiValueLen)))
{
fcsDecodeHttpString( pszTmp);
bHadFormData = TRUE;
}
if (RC_OK( flmBuildKeyPaths( pIfd, pIfd->uiFldNum,
IFD_GET_FIELD_TYPE( pIfd), TRUE,
pKey, &pvFld)))
{
// Put the data from the form into the field in the key.
if (pszTmp && *pszTmp)
{
FLMUNICODE * puzBuf = NULL;
FLMBYTE * pucBuf = NULL;
FLMUINT uiBufSize;
FLMINT iVal;
FLMUINT uiVal;
FLMUINT uiLen;
switch (IFD_GET_FIELD_TYPE( pIfd))
{
case FLM_TEXT_TYPE:
uiBufSize = 0;
if (RC_OK( tokenGetUnicode( pszTmp, (void **)&puzBuf, &uiLen,
&uiBufSize)))
{
(void)pKey->setUnicode( pvFld, puzBuf);
f_free( &puzBuf);
}
break;
case FLM_NUMBER_TYPE:
// If this is a negative value, then we will store it as an INT
if (*pszTmp == '-')
{
iVal = f_atoi( pszTmp);
(void)pKey->setINT( pvFld, iVal);
}
else
{
uiVal = f_atoud( pszTmp);
(void)pKey->setUINT( pvFld, uiVal);
}
break;
case FLM_BINARY_TYPE:
if (RC_OK( f_alloc( f_strlen( pszTmp) / 2 + 1, &pucBuf)))
{
FLMBOOL bHaveFirstNibble = FALSE;
FLMBYTE ucVal = 0;
FLMUINT uiNibble;
char * pszVal = pszTmp;
FLMBYTE * pucTmp = pucBuf;
while (*pszVal)
{
if (*pszVal >= '0' && *pszVal <= '9')
{
uiNibble = (FLMUINT)(*pszVal - '0');
}
else if (*pszVal >= 'a' && *pszVal <= 'f')
{
uiNibble = (FLMUINT)(*pszVal - 'a' + 10);
}
else if (*pszVal >= 'A' && *pszVal <= 'F')
{
uiNibble = (FLMUINT)(*pszVal - 'A' + 10);
}
else
{
pszVal++;
continue;
}
if (bHaveFirstNibble)
{
ucVal += (FLMBYTE)uiNibble;
*pucTmp++ = ucVal;
bHaveFirstNibble = FALSE;
}
else
{
ucVal = (FLMBYTE)(uiNibble << 4);
bHaveFirstNibble = TRUE;
}
pszVal++;
}
// See if we ended on an odd number of nibbles.
if (bHaveFirstNibble)
{
*pucTmp++ = ucVal;
}
if (pucTmp > pucBuf)
{
(void)pKey->setBinary( pvFld, (void *)pucBuf,
(FLMUINT)(pucTmp - pucBuf));
}
f_free( &pucBuf);
}
break;
case FLM_CONTEXT_TYPE:
uiVal = f_atoud( pszTmp);
(void)pKey->setRecPointer( pvFld, uiVal);
break;
default:
break;
}
}
}
f_free( &pszTmp);
}
Exit:
fdbExit( pDb);
return( bHadFormData);
}
/****************************************************************************
Desc: Output a from or until key.
****************************************************************************/
void F_IndexListPage::outputKey(
const char * pszKeyName,
HFDB hDb,
FLMUINT uiIndex,
FLMUINT uiContainer,
F_NameTable * pNameTable,
FlmRecord * pKey,
FLMUINT uiRefCnt,
FLMBOOL bReadOnly,
FLMUINT uiKeyId)
{
RCODE rc = FERR_OK;
FDB * pDb = NULL;
FLMBOOL bDummy;
IXD * pIxd;
IFD * pIfd;
FLMUINT uiLoop;
FLMUINT uiLoop2;
FLMBOOL bHighlight = FALSE;
char szName [128];
void * pvFld;
FLMUINT uiFieldCounter;
FLMBOOL bAllocatedKey = FALSE;
// Get a default key if one is not passed in.
if (!pKey)
{
if (RC_BAD( rc = FlmKeyRetrieve( hDb, uiIndex, 0, pKey, 0, uiKeyId,
&pKey, NULL)))
{
if (rc == FERR_EOF_HIT || rc == FERR_BOF_HIT)
{
pKey = NULL;
rc = FERR_OK;
}
else
{
goto Exit;
}
}
bAllocatedKey = TRUE;
}
// Lookup the index in the dictionary.
pDb = (FDB *)hDb;
if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0,
&bDummy)))
{
goto Exit;
}
if (RC_BAD( rc = fdictGetIndex(
pDb->pDict, pDb->pFile->bInLimitedMode,
uiIndex, NULL, &pIxd, TRUE)))
{
goto Exit;
}
if (!uiRefCnt)
{
printStartCenter();
}
// Output a value for each defined field.
printTableStart( pszKeyName, 2, 75);
// Column headers
printTableRowStart();
printColumnHeading( "Field Name", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 35);
printColumnHeading( "Value", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 65);
printTableRowEnd();
// Row for Reference (DRN) or reference count
if (uiRefCnt)
{
printTableRowStart( bHighlight = !bHighlight);
printTableDataStart( TRUE, JUSTIFY_LEFT, 35);
fnPrintf( m_pHRequest, "Reference Count");
printTableDataEnd();
printTableDataStart( TRUE, JUSTIFY_LEFT, 65);
fnPrintf( m_pHRequest, "%lu",
(unsigned long)uiRefCnt);
printTableDataEnd();
printTableRowEnd();
}
else if (pKey && pKey->getID())
{
printTableRowStart( bHighlight = !bHighlight);
printTableDataStart( TRUE, JUSTIFY_LEFT, 35);
fnPrintf( m_pHRequest, "Reference (DRN)");
printTableDataEnd();
printTableDataStart( TRUE, JUSTIFY_LEFT, 65);
if (bReadOnly)
{
fnPrintf( m_pHRequest, "%lu",
(unsigned long)pKey->getID());
}
else
{
f_sprintf( szName, "%s_%u",
REFERENCE_DRN_FIELD, (unsigned)uiKeyId);
fnPrintf( m_pHRequest,
"", szName,
(unsigned long)pKey->getID());
}
printTableDataEnd();
printTableRowEnd();
}
// Row for Container - if index is on all containers
// uiContainer == 0
if (!uiContainer)
{
printTableRowStart( bHighlight = !bHighlight);
printTableDataStart( TRUE, JUSTIFY_LEFT, 35);
fnPrintf( m_pHRequest, "Container");
printTableDataEnd();
printTableDataStart( TRUE, JUSTIFY_LEFT, 65);
if (bReadOnly)
{
fnPrintf( m_pHRequest, "%lu",
(unsigned long)pKey->getContainerID());
}
else
{
f_sprintf( szName, "%s_%u",
CONTAINER_FIELD, (unsigned)uiKeyId);
fnPrintf( m_pHRequest,
"",
szName, (unsigned long)pKey->getContainerID());
}
printTableDataEnd();
printTableRowEnd();
}
uiFieldCounter = (FLMUINT)((uiKeyId == FO_FIRST)
? (FLMUINT)0
: (FLMUINT)FLM_FREE_TAG_NUMS);
for (uiLoop = 0, pIfd = pIxd->pFirstIfd;
uiLoop < pIxd->uiNumFlds;
uiLoop++, uiFieldCounter++, pIfd++)
{
printTableRowStart( bHighlight = !bHighlight);
printTableDataStart( TRUE, JUSTIFY_LEFT, 35);
for (uiLoop2 = 0; pIfd->pFieldPathPToC [uiLoop2]; uiLoop2++)
{
if (uiLoop2)
{
fnPrintf( m_pHRequest, ".");
}
// Get the field name.
if (!pNameTable ||
!pNameTable->getFromTagNum(
pIfd->pFieldPathPToC [uiLoop2], NULL,
szName,
sizeof( szName)))
{
f_sprintf( szName, "TAG_%u",
(unsigned)pIfd->pFieldPathPToC [uiLoop2]);
}
printEncodedString( szName, HTML_ENCODING);
fnPrintf( m_pHRequest, "(%u)",
(unsigned)pIfd->pFieldPathPToC [uiLoop2]);
}
printTableDataEnd();
// Retrieve the field from the key.
//VISIT: Verify and find full path, not just leaf field.
pvFld = (void *)((pKey)
? pKey->find( pKey->root(), pIfd->uiFldNum)
: NULL);
printTableDataStart( TRUE, JUSTIFY_LEFT, 65);
if (pvFld && pKey->getDataLength( pvFld))
{
switch (IFD_GET_FIELD_TYPE( pIfd))
{
case FLM_TEXT_TYPE:
printTextField(
pKey, pvFld, uiFieldCounter, bReadOnly);
break;
case FLM_NUMBER_TYPE:
printNumberField(
pKey, pvFld, uiFieldCounter, bReadOnly);
break;
case FLM_BINARY_TYPE:
printBinaryField(
pKey, pvFld, uiFieldCounter, bReadOnly);
break;
case FLM_CONTEXT_TYPE:
printContextField(
pKey, pvFld, uiFieldCounter, bReadOnly);
break;
case FLM_BLOB_TYPE:
printBlobField(
pKey, pvFld, uiFieldCounter, bReadOnly);
break;
default:
printDefaultField(
pKey, pvFld, uiFieldCounter, bReadOnly);
break;
}
}
else if (!bReadOnly)
{
fnPrintf( m_pHRequest, "",
(unsigned)uiFieldCounter);
}
else
{
printSpaces( 1);
}
printTableDataEnd();
printTableRowEnd();
}
printTableEnd();
if (!uiRefCnt)
{
printEndCenter( FALSE);
}
Exit:
if (pKey && bAllocatedKey)
{
pKey->Release();
}
if (RC_BAD( rc))
{
fnPrintf( m_pHRequest,
" ERROR %04X (%s) outputting %s
\n",
(unsigned)rc, FlmErrorString( rc), pszKeyName);
}
fdbExit( pDb);
}
/****************************************************************************
Desc: Get an index's container number.
****************************************************************************/
FSTATIC FLMUINT getIndexContainer(
HFDB hDb,
FLMUINT uiIndex
)
{
FDB * pDb;
FLMBOOL bDummy;
IXD * pIxd;
FLMUINT uiContainer = NO_CONTAINER_NUM;
pDb = (FDB *)hDb;
if (RC_BAD( fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0,
&bDummy)))
{
goto Exit;
}
if (RC_BAD( fdictGetIndex(
pDb->pDict, pDb->pFile->bInLimitedMode,
uiIndex, NULL, &pIxd, TRUE)))
{
goto Exit;
}
uiContainer = pIxd->uiContainerNum;
Exit:
fdbExit( pDb);
return( uiContainer);
}
/****************************************************************************
Desc: Output the form for the user to input index and query keys.
****************************************************************************/
void F_IndexListPage::outputIndexListForm(
HFDB hDb,
const char * pszDbKey,
FLMUINT uiIndex,
FLMUINT uiContainer,
FLMUINT uiIndexListThreadId,
F_NameTable * pNameTable,
IXLIST_STATUS * pIndexListStatus)
{
char * pszName;
char szName [128];
FLMUINT uiLoop;
FLMUINT uiLoop2;
FLMUINT uiRefCnt;
FLMUINT * puiRefList;
FlmRecord * pKey;
// Get the container, if we don't have it yet and we have the index.
if (uiIndex && uiContainer == NO_CONTAINER_NUM)
{
uiContainer = getIndexContainer( hDb, uiIndex);
}
fnPrintf( m_pHRequest, "\n");
// Output index list status, if we have any
if (pIndexListStatus->bHaveIndexListStatus)
{
printStartCenter();
if (pIndexListStatus->bIndexListRunning)
{
printTableStart( "INDEX LIST PROGRESS", 2, 50);
}
else
{
printTableStart( "INDEX LIST RESULTS", 2, 50);
}
// Column headers
printTableRowStart();
printColumnHeading( "Key Count", JUSTIFY_RIGHT);
printColumnHeading( "Reference Count", JUSTIFY_RIGHT);
printTableRowEnd();
printTableRowStart( TRUE);
printTableDataStart( TRUE, JUSTIFY_RIGHT);
fnPrintf( m_pHRequest, "%u", (unsigned)pIndexListStatus->uiKeyCount);
printTableDataEnd();
printTableDataStart( TRUE, JUSTIFY_RIGHT);
fnPrintf( m_pHRequest, "%u", (unsigned)pIndexListStatus->uiRefCount);
printTableDataEnd();
printTableRowEnd();
printTableEnd();
printEndCenter( FALSE);
fnPrintf( m_pHRequest, " \n");
if (!pIndexListStatus->bIndexListRunning && pIndexListStatus->uiKeyCount)
{
printTableStart( "Keys RETRIEVED", 1, 100);
printTableEnd();
fnPrintf( m_pHRequest, " \n");
for (uiLoop = 0; uiLoop < pIndexListStatus->uiKeyCount; uiLoop++)
{
uiRefCnt = pIndexListStatus->pKeyList [uiLoop].uiRefCnt;
pKey = pIndexListStatus->pKeyList [uiLoop].pKey;
f_sprintf( szName, "Key #%u", (unsigned)(uiLoop + 1));
outputKey( szName, hDb, uiIndex, uiContainer, pNameTable,
pKey, uiRefCnt, TRUE, 0);
puiRefList = &pIndexListStatus->puiRefList [
pIndexListStatus->pKeyList [uiLoop].uiRefStartOffset];
for (uiLoop2 = 0; uiLoop2 < uiRefCnt; uiLoop2++, puiRefList++)
{
if (uiLoop2)
{
if (uiLoop2 % REFERENCES_PER_ROW != 0)
{
if (fnPrintf( m_pHRequest, ",") != 0)
{
goto Exit;
}
}
else
{
if (fnPrintf( m_pHRequest, " \n") != 0)
{
goto Exit;
}
}
}
if (fnPrintf( m_pHRequest, "%u\n",
m_pszURLString, pszDbKey, (unsigned)(*puiRefList),
(unsigned)pKey->getContainerID(),
(unsigned)(*puiRefList)) != 0)
{
goto Exit;
}
}
if (fnPrintf( m_pHRequest, "
\n") != 0)
{
goto Exit;
}
}
}
}
Exit:
return;
}
/****************************************************************************
Desc: Run an index key list.
****************************************************************************/
RCODE F_IndexListPage::runIndexList(
HFDB hDb,
FLMUINT uiIndex,
FlmRecord * pFromKey,
FlmRecord * pUntilKey,
FLMUINT * puiIndexListThreadId
)
{
RCODE rc = FERR_OK;
IXLIST_STATUS * pIndexListStatus = NULL;
IF_Thread * pThread;
FDB * pDb = NULL;
// Open the database for the thread - so it doesn't have
// to worry about the handle going away. The thread will close the
// new handle when it exits.
if (RC_BAD( rc = flmOpenFile( ((FDB *)hDb)->pFile, NULL, NULL, NULL,
0, TRUE, NULL, NULL,
(((FDB *)hDb)->pFile)->pszDbPassword, &pDb)))
{
goto Exit;
}
// Create an object to track the query.
if (RC_BAD( rc = f_calloc( sizeof( IXLIST_STATUS), &pIndexListStatus)))
{
goto Exit;
}
// Get the index container
pIndexListStatus->hDb = (HFDB)pDb;
pIndexListStatus->uiIndex = uiIndex;
if (pFromKey)
{
if ((pIndexListStatus->pFromKey = pFromKey->copy()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
if (pUntilKey)
{
if ((pIndexListStatus->pUntilKey = pUntilKey->copy()) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
pIndexListStatus->bIndexListRunning = TRUE;
pIndexListStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER();
// If browser does not check query status at least every 15 seconds, we will
// assume it has gone away and the thread will terminate itself.
pIndexListStatus->uiIndexListTimeout = FLM_SECS_TO_TIMER_UNITS( 15);
// Start a thread to do the query.
if( RC_BAD( rc = f_threadCreate( &pThread, imonDoIndexList,
"WEB INDEX LIST",
gv_uiDbThrdGrp, 1,
(void *)pIndexListStatus, (void *)hDb)))
{
goto Exit;
}
*puiIndexListThreadId = pThread->getThreadId();
// Set pIndexListStatus to NULL so it won't be freed below. The thread
// will free it when it stops.
pIndexListStatus = NULL;
// Set pDb to NULL so it won't be closed below. The thread will
// close it when it stops.
pDb = NULL;
Exit:
if (pThread)
{
pThread->Release();
}
if (pIndexListStatus)
{
freeIndexListStatus( pIndexListStatus, TRUE);
}
if (pDb)
{
FlmDbClose( (HFDB *)&pDb);
}
return( rc);
}
/****************************************************************************
Desc: Free an IXLIST_STATUS structure.
****************************************************************************/
FSTATIC void freeIndexListStatus(
IXLIST_STATUS * pIxListStatus,
FLMBOOL bFreeStructure
)
{
FLMUINT uiLoop;
// Free the from and until keys.
if (pIxListStatus->pFromKey)
{
pIxListStatus->pFromKey->Release();
}
if (pIxListStatus->pUntilKey)
{
pIxListStatus->pUntilKey->Release();
}
// Free the key list and reference list
if (pIxListStatus->pKeyList)
{
for (uiLoop = 0; uiLoop < pIxListStatus->uiKeyCount; uiLoop++)
{
pIxListStatus->pKeyList [uiLoop].pKey->Release();
}
f_free( &pIxListStatus->pKeyList);
}
if (pIxListStatus->puiRefList)
{
f_free( &pIxListStatus->puiRefList);
}
// Free the structure
if (bFreeStructure)
{
f_free( &pIxListStatus);
}
}
/****************************************************************************
Desc: Copy an IXLIST_STATUS structure's content.
****************************************************************************/
FSTATIC void copyIndexListStatus(
IXLIST_STATUS * pDestIxListStatus,
IXLIST_STATUS * pSrcIxListStatus,
FLMBOOL bTransferKeyList
)
{
f_memcpy( pDestIxListStatus, pSrcIxListStatus, sizeof( IXLIST_STATUS));
pDestIxListStatus->pFromKey = NULL;
pDestIxListStatus->pUntilKey = NULL;
// Copy the from and until keys.
if (pSrcIxListStatus->pFromKey)
{
pDestIxListStatus->pFromKey = pSrcIxListStatus->pFromKey->copy();
}
if (pSrcIxListStatus->pUntilKey)
{
pDestIxListStatus->pUntilKey = pSrcIxListStatus->pUntilKey->copy();
}
// The bTransferKeyList, if TRUE, indicates that we are transferring
// the key list and the reference list from the source to the
// destination, meaning we no longer want it in the source.
// Otherwise, it should remain in the source and we
// need to NULL it out of the destination.
if (bTransferKeyList)
{
pSrcIxListStatus->pKeyList = NULL;
pSrcIxListStatus->puiRefList = NULL;
}
else
{
pDestIxListStatus->pKeyList = NULL;
pDestIxListStatus->puiRefList = NULL;
}
}
/****************************************************************************
Desc: Output the current thread status to the web page.
****************************************************************************/
void F_IndexListPage::getIndexListStatus(
FLMUINT uiIndexListThreadId,
FLMBOOL bStopIndexList,
IXLIST_STATUS * pIndexListStatus)
{
FLMUINT uiThreadId;
IF_Thread * pThread = NULL;
IXLIST_STATUS * pThreadIndexListStatus;
FLMBOOL bMutexLocked = FALSE;
flmAssert( !pIndexListStatus->bHaveIndexListStatus);
// See if the thread is still running.
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
uiThreadId = 0;
for (;;)
{
if (RC_BAD( gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread,
gv_uiDbThrdGrp, &uiThreadId)))
{
pIndexListStatus->bIndexListRunning = FALSE;
goto Exit;
}
if (uiThreadId == uiIndexListThreadId)
{
// If the app ID is zero, the thread is on its way out or already
// out. Can no longer get thread status.
if (!pThread->getThreadAppId())
{
pIndexListStatus->bIndexListRunning = FALSE;
goto Exit;
}
// Found thread, get its query data
pThreadIndexListStatus = (IXLIST_STATUS *)pThread->getParm1();
pThreadIndexListStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER();
// Tell the thread to stop the query before telling it
// to stop. This is so we can get partial results.
if (bStopIndexList)
{
pThreadIndexListStatus->bStopIndexList = TRUE;
// Go into a while loop, waiting for the thread
// to finish its query.
while (pThreadIndexListStatus->bIndexListRunning)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
f_sleep( 200);
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
// If the thread app ID goes to zero, it has been
// told to shut down, and has either already gone
// away or is in the process of doing so, in which
// case pThreadIndexListStatus has either already been
// deleted, or will be - so it is not safe to access
// it any more!
if (!pThread->getThreadAppId())
{
pIndexListStatus->bIndexListRunning = FALSE;
goto Exit;
}
}
}
break;
}
pThread->Release();
pThread = NULL;
}
// Mutex better still be locked at this point.
flmAssert( bMutexLocked);
// If the query is not done, return everything except the DRN list.
// Note that we test pThreadIndexListStatus->bIndexListRunning BEFORE
// doing the memcpy. This is because puiDrnList is not guaranteed
// to be set until bIndexListRunning is FALSE. If bIndexListRunning is TRUE,
// we will NULL out whatever got copied into puiDrnList.
if (!pThreadIndexListStatus->bIndexListRunning)
{
// Transfer the lists.
copyIndexListStatus( pIndexListStatus, pThreadIndexListStatus, TRUE);
// Need to unlock the mutex so that the thread can stop.
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
pThread->stopThread();
}
else
{
// Don't transfer the lists.
copyIndexListStatus( pIndexListStatus, pThreadIndexListStatus, FALSE);
// Set bIndexListRunning to TRUE. This takes care of a race
// condition of pThreadIndexListStatus->bIndexListRunning getting
// set to FALSE by the index list thread after we test it above.
// we make the test on pThreadIndexListStatus->bIndexListRunning. We will
// simply get that fact next time we get status.
pIndexListStatus->bIndexListRunning = TRUE;
}
pIndexListStatus->bHaveIndexListStatus = TRUE;
Exit:
if (bMutexLocked)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
}
if (pThread)
{
pThread->Release();
}
}
/****************************************************************************
Desc: Thread to perform a query for a web page.
****************************************************************************/
FSTATIC RCODE imonDoIndexList(
IF_Thread * pThread)
{
RCODE rc;
IXLIST_STATUS * pIndexListStatus = (IXLIST_STATUS *)pThread->getParm1();
HFDB hDb = pIndexListStatus->hDb;
FLMUINT uiIndex = pIndexListStatus->uiIndex;
FlmRecord * pSrchKey = NULL;
FLMUINT uiSrchDrn;
FLMUINT uiSrchFlag = 0;
FLMUINT uiCurrTime;
FLMUINT uiLastTimeSetStatus = 0;
FLMUINT ui20SecsTime;
FLMBOOL bTransStarted = FALSE;
FLMBOOL bNewKey = FALSE;
FLMBYTE * pucUntilKeyBuf = NULL;
FLMUINT uiUntilKeyLen;
FLMUINT uiUntilDrn;
FLMBYTE * pucFoundKeyBuf = NULL;
FLMUINT uiFoundKeyLen;
char * pszEndStatus = &pIndexListStatus->szEndStatus [0];
pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);
ui20SecsTime = FLM_SECS_TO_TIMER_UNITS( 20);
// Start a transaction.
if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0)))
{
f_sprintf( pszEndStatus, "Trans Error %04X", (unsigned)rc);
goto Quit_List;
}
bTransStarted = TRUE;
uiSrchFlag = FO_INCL;
if (pIndexListStatus->pFromKey)
{
uiSrchDrn = pIndexListStatus->pFromKey->getID();
if ((pSrchKey = pIndexListStatus->pFromKey->copy()) == NULL)
{
f_strcpy( pszEndStatus, "Could not copy from key");
goto Quit_List;
}
}
// Allocate key buffers for the until key and the found key so we
// can do comparisons.
if( RC_BAD( rc = f_alloc( MAX_KEY_SIZ * 2, &pucUntilKeyBuf)))
{
f_strcpy( pszEndStatus, "Could not allocate key buffers");
goto Quit_List;
}
pucFoundKeyBuf = &pucUntilKeyBuf [MAX_KEY_SIZ];
// Get the collated until key.
if (!pIndexListStatus->pUntilKey)
{
f_memset( pucUntilKeyBuf, 0xFF, MAX_KEY_SIZ);
uiUntilKeyLen = MAX_KEY_SIZ;
uiUntilDrn = 0;
}
else
{
uiUntilDrn = pIndexListStatus->pUntilKey->getID();
if (RC_BAD( rc = FlmKeyBuild( hDb, uiIndex,
pIndexListStatus->pUntilKey->getContainerID(),
pIndexListStatus->pUntilKey, 0,
pucUntilKeyBuf, &uiUntilKeyLen)))
{
f_sprintf( pszEndStatus, "Until Key Build Error %04X", (unsigned)rc);
goto Quit_List;
}
}
bNewKey = TRUE;
for (;;)
{
// See if we should shut down.
if (pThread->getShutdownFlag())
{
pIndexListStatus->bIndexListRunning = FALSE;
// Transaction will be aborted below
pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);
goto Exit;
}
// See if the browser quit asking for status.
uiCurrTime = FLM_GET_TIMER();
if (FLM_ELAPSED_TIME( uiCurrTime,
pIndexListStatus->uiLastTimeBrowserChecked) >=
pIndexListStatus->uiIndexListTimeout)
{
if (pIndexListStatus->bIndexListRunning)
{
pThread->setThreadStatus( "Timed out, KeyCnt=%u, RefCnt=%u",
(unsigned)pIndexListStatus->uiKeyCount,
(unsigned)pIndexListStatus->uiRefCount);
pIndexListStatus->bIndexListRunning = FALSE;
}
// Transaction will be aborted below
goto Exit;
}
// If the query is not running, just pause one second at a time
// until we are told to shut down or until we time out.
if (!pIndexListStatus->bIndexListRunning)
{
f_sleep( 1000);
continue;
}
// See if we should stop the query.
if (pIndexListStatus->bStopIndexList)
{
f_sprintf( pszEndStatus, "User halted, KeyCnt=%u, RefCnt=%u",
(unsigned)pIndexListStatus->uiKeyCount,
(unsigned)pIndexListStatus->uiRefCount);
goto Quit_List;
}
// Get the next key/reference.
if (RC_BAD( rc = FlmKeyRetrieve( hDb, uiIndex,
pSrchKey->getContainerID(),
pSrchKey, uiSrchDrn, uiSrchFlag,
&pSrchKey, &uiSrchDrn)))
{
if (rc == FERR_EOF_HIT)
{
if (bNewKey)
{
f_sprintf( pszEndStatus, "Index list done, KeyCnt=%u, RefCnt=%u",
(unsigned)pIndexListStatus->uiKeyCount,
(unsigned)pIndexListStatus->uiRefCount);
goto Quit_List;
}
uiSrchFlag = FO_EXCL;
bNewKey = TRUE;
rc = FERR_OK;
continue;
}
else
{
f_sprintf( pszEndStatus, "Read Error %04X, KeyCnt=%u, RefCnt=%u",
(unsigned)rc, (unsigned)pIndexListStatus->uiKeyCount,
(unsigned)pIndexListStatus->uiRefCount);
goto Quit_List;
}
}
pSrchKey->setID( uiSrchDrn);
if (bNewKey)
{
FLMINT iCmp;
FLMUINT uiCmpLen;
// See if we have gone past the until key.
if (RC_BAD( rc = FlmKeyBuild( hDb, uiIndex,
pSrchKey->getContainerID(),
pSrchKey, 0,
pucFoundKeyBuf, &uiFoundKeyLen)))
{
f_sprintf( pszEndStatus, "Error Building Key Buf %04X",
(unsigned)rc);
goto Quit_List;
}
if ((uiCmpLen = uiUntilKeyLen) > uiFoundKeyLen)
{
uiCmpLen = uiFoundKeyLen;
}
iCmp = f_memcmp( pucFoundKeyBuf, pucUntilKeyBuf, uiCmpLen);
if ((iCmp > 0) ||
(iCmp == 0 && uiFoundKeyLen > uiUntilKeyLen))
{
f_sprintf( pszEndStatus, "Index list done, KeyCnt=%u, RefCnt=%u",
(unsigned)pIndexListStatus->uiKeyCount,
(unsigned)pIndexListStatus->uiRefCount);
goto Quit_List;
}
// Save a new key to the list.
if (pIndexListStatus->uiKeyCount == pIndexListStatus->uiKeyListSize)
{
KEY_ELEMENT * pTmpKeyList;
if( RC_BAD( rc = f_alloc( sizeof( KEY_ELEMENT) *
(pIndexListStatus->uiKeyListSize + KEY_LIST_INCREASE_SIZE),
&pTmpKeyList)))
{
f_strcpy( pszEndStatus, "Could not allocate key list");
goto Quit_List;
}
if (pIndexListStatus->pKeyList)
{
f_memcpy( pTmpKeyList, pIndexListStatus->pKeyList,
sizeof( KEY_ELEMENT) * pIndexListStatus->uiKeyCount);
f_free( &pIndexListStatus->pKeyList);
}
pIndexListStatus->pKeyList = pTmpKeyList;
pIndexListStatus->uiKeyListSize += KEY_LIST_INCREASE_SIZE;
}
if ((pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount].pKey =
pSrchKey->copy()) == NULL)
{
f_strcpy( pszEndStatus, "Could not allocate key");
goto Quit_List;
}
pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount].uiRefCnt = 0;
pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount].uiRefStartOffset =
pIndexListStatus->uiRefCount;
pIndexListStatus->uiKeyCount++;
bNewKey = FALSE;
uiSrchFlag = FO_EXCL | FO_KEY_EXACT;
}
// VISIT: Need to see if we have gone past the UNTIL drn if we are on
// the until key.
// Save the DRN to the reference list.
if (pIndexListStatus->uiRefCount == pIndexListStatus->uiRefListSize)
{
FLMUINT * puiTmpRefList;
if( RC_BAD( rc = f_alloc( sizeof( FLMUINT) *
(pIndexListStatus->uiRefListSize + REF_LIST_INCREASE_SIZE),
&puiTmpRefList)))
{
f_strcpy( pszEndStatus, "Could not allocate reference list");
goto Quit_List;
}
if (pIndexListStatus->puiRefList)
{
f_memcpy( puiTmpRefList, pIndexListStatus->puiRefList,
sizeof( FLMUINT) * pIndexListStatus->uiRefCount);
f_free( &pIndexListStatus->puiRefList);
}
pIndexListStatus->puiRefList = puiTmpRefList;
pIndexListStatus->uiRefListSize += REF_LIST_INCREASE_SIZE;
}
pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount - 1].uiRefCnt++;
pIndexListStatus->puiRefList [pIndexListStatus->uiRefCount] = uiSrchDrn;
pIndexListStatus->uiRefCount++;
// Update thread status every 20 seconds. Also start a
// new transaction.
uiCurrTime = FLM_GET_TIMER();
if (FLM_ELAPSED_TIME( uiCurrTime, uiLastTimeSetStatus) >=
ui20SecsTime)
{
pThread->setThreadStatus( "KeyCnt=%u, RefCnt=%u",
(unsigned)pIndexListStatus->uiKeyCount,
(unsigned)pIndexListStatus->uiRefCount);
uiLastTimeSetStatus = uiCurrTime;
bTransStarted = FALSE;
(void)FlmDbTransCommit( hDb);
if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0)))
{
f_sprintf( pszEndStatus, "Trans Error %04X", (unsigned)rc);
goto Quit_List;
}
bTransStarted = TRUE;
}
continue;
Quit_List:
pThread->setThreadStatus( pszEndStatus);
if (bTransStarted)
{
bTransStarted = FALSE;
// Only a read transaction - don't care if committed or aborted
(void)FlmDbTransCommit( hDb);
}
// Close the database.
FlmDbClose( &hDb);
pIndexListStatus->bIndexListRunning = FALSE;
if (pSrchKey)
{
pSrchKey->Release();
pSrchKey = NULL;
}
if (pucUntilKeyBuf)
{
f_free( &pucUntilKeyBuf);
}
// Continue until told to shut down or until we
// timeout.
continue;
}
Exit:
// Abort the transaction if we still have one going.
if (bTransStarted)
{
(void)FlmDbTransAbort( hDb);
}
// Close the database.
if (hDb != HFDB_NULL)
{
FlmDbClose( &hDb);
}
if (pSrchKey)
{
pSrchKey->Release();
pSrchKey = NULL;
}
if (pucUntilKeyBuf)
{
f_free( &pucUntilKeyBuf);
}
// Set the thread's app ID to 0, so that it will not
// be found now that the thread is terminating (we don't
// want getIndexListStatus() to find the thread).
pThread->setThreadAppId( 0);
// Free the index list status. Must do inside mutex lock so
// that it doesn't go away after getIndexListStatus finds the
// thread.
f_mutexLock( gv_FlmSysData.hShareMutex);
freeIndexListStatus( pIndexListStatus, TRUE);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
return( FERR_OK);
}