/**************************************************************************
*
* Copyright (c) 2003 Informatica Corporation. This file contains
* material proprietary to Informatica Corporation and may not be copied
* or distributed in any form without the written permission of Informatica
* Corporation
*
**************************************************************************/
/**************************************************************************
* Custom Transformation p_GetSeqVal Procedure File
*
* This file contains code that functions that will be called by the main
* server executable.
*
* For more information on these files,
* see $(PM_HOME)/ExtProc/include/Readme.txt
**************************************************************************/
/**************************************************************************
Includes
**************************************************************************/
#include "p_GetSeqVal.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <stdarg.h>
#include <process.h>
/**************************************************************************
UserDefineMacro
**************************************************************************/
#define DLLNAME "SeqGenerator.dll"
#define PMDTM "pmdtm.exe"
#define EXTPROCDIR "ExtProc\\"
#define SEQFILE "SeqGenerator.tsv"
#define LOCKFILE "SeqGenerator.lock"
#define SEQNAMELEN 100
#define MAXSEQNUM 10000
typedef unsigned long int seqval_t;
#define MSGCHARS 1000
#define MAX_LINE_SIZE 1000
#define MSGMARK ">>>>>>>>>>>>>>>"
#define PROCNAME "SeqGenerator"
/**************************************************************************
SharedVariables
**************************************************************************/
struct seq_t{
char chSeqName[SEQNAMELEN+1];
seqval_t nSeqVal;
};
struct seqlist_t{
struct seq_t arrSequences[MAXSEQNUM];
int nSeqNums;
};
#pragma data_seg("SeqGeneratorShared")
static int nInitFlag=0;
static int nRefCount=0;
static struct seqlist_t strSeqList={0};
#pragma data_seg()
#pragma comment(linker,"/section:SeqGeneratorShared,rws")
/**************************************************************************
Debug switch
**************************************************************************/
//#define DEBUG_MODE
#ifdef DEBUG_MODE
//LogMessage("%s DebugLog:",MSGMARK);
#endif
/**************************************************************************
Global Variables
**************************************************************************/
char chLogMsg[MSGCHARS],chMutexName[MSGCHARS];
TCHAR szPath[MAX_PATH],dllDir[MAX_PATH],SeqFilePath[MAX_PATH],LockFilePath[MAX_PATH];
FILE *fpSeqFile,*fpLockFile;
char *rowSeqName;
char chSeqName[SEQNAMELEN+1];
seqval_t nSeqVal;
struct seqcache_t{
char chSeqName[SEQNAMELEN+1];
seqval_t *ptrSeqVal;
};
struct seqlistcache_t{
struct seqcache_t arrSequences[MAXSEQNUM];
int nSeqNums;
};
struct seqlistcache_t strSeqListCache={0};
//HANDLE hMutex;
/**************************************************************************
Functions
**************************************************************************/
int LogMessage(char *fmt,...){
va_list ptrArg;
va_start(ptrArg,fmt);
vsprintf(chLogMsg,fmt,ptrArg);
va_end(ptrArg);
INFA_CTLogMessageM( eESL_LOG,chLogMsg);
return 0;
}
int GetFileNumRows(FILE* fp){
int i = 0;
char strLine[MAX_LINE_SIZE];
fseek(fp,0,SEEK_SET);
while (fgets(strLine, MAX_LINE_SIZE, fp))
i++;
fseek(fp,0,SEEK_SET);
return i;
}
char * left(char *dst,char *src, int n){
char *p = src;
char *q = dst;
int len = strlen(src);
if(n>len) n = len;
while(n--) *(q++) = *(p++);
*(q++)='\0';
return dst;
}
char * mid(char *dst,char *src, int n,int m) {
char *p = src;
char *q = dst;
int len = strlen(src);
if(m>len) m = len-n;
if(n<0) n=0;
if(n>len) return NULL;
p += n;
while(m--) *(q++) = *(p++);
*(q++)='\0';
return dst;
}
/*
INFA_STATUS mGetMutex(){
while(1){
hMutex=CreateMutex(NULL,FALSE,PROCNAME);
if(hMutex&&GetLastError()==ERROR_ALREADY_EXISTS){
if(hMutex!=NULL){
CloseHandle(hMutex);
}
Sleep(1000);
continue;
}
else if(hMutex!=NULL){
break;
}
else {
return INFA_FAILURE;
}
}
return INFA_SUCCESS;
}
*/
/**************************************************************************
Function: p_GetSeqVal_procInit
Description: Initialization for the procedure. Returns INFA_SUCCESS if
procedure initialization succeeds, else return INFA_FAILURE.
Input: procedure - the handle for the procedure
Output: None
Remarks: This function will get called once for the session at
initialization time. It will be called after the moduleInit function.
**************************************************************************/
INFA_STATUS p_GetSeqVal_procInit( INFA_CT_PROCEDURE_HANDLE procedure)
{
//Sleep(10000);
INFA_CTChangeStringMode( procedure, eASM_MBCS );
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ){
LogMessage("GetModuleFileName failed (%d)\n", GetLastError());
return INFA_FAILURE;
} else {
LogMessage("ModuleFileName is : %s\n", szPath);
mid(dllDir,szPath,0,strlen(szPath)-strlen(PMDTM));
strcat(dllDir,EXTPROCDIR);
LogMessage("ModuleDirectory is : %s\n", dllDir);
strcpy(SeqFilePath,dllDir);
strcat(SeqFilePath,SEQFILE);
LogMessage("Sequence File is : %s\n", SeqFilePath);
strcpy(LockFilePath,dllDir);
strcat(LockFilePath,LOCKFILE);
LogMessage("Lock File is : %s\n", LockFilePath);
}
//CreateMutex(NULL,FALSE,PROCNAME);
//mGetMutex();
//WaitForSingleObject(hMutex, INFINITE);
fpLockFile=fopen(LockFilePath,"w");
while(LockFile(fpLockFile,1,1,1,1)!=0){
Sleep(2000);
}
if(1==++nRefCount){
int nFileNumRows,i;
LogMessage("%s Loading Sequence File\n",MSGMARK);
fpSeqFile=fopen(SeqFilePath,"a+");
nFileNumRows=GetFileNumRows(fpSeqFile);
LogMessage("%s Sequence Objects Nums: %d\n",MSGMARK,nFileNumRows);
strSeqList.nSeqNums=0;
for(i=0;i<nFileNumRows;i++){
fscanf(fpSeqFile,"%s\t%lu\n",chSeqName,&nSeqVal);
strSeqList.nSeqNums++;
strcpy(strSeqList.arrSequences[i].chSeqName,chSeqName);
strSeqList.arrSequences[i].nSeqVal=nSeqVal;
}
if(EOF==fclose(fpSeqFile)){
LogMessage("Close Sequence File Failed!\n" );
return INFA_FAILURE;
}
nInitFlag=1;
//CloseHandle(hMutex);
LogMessage("%s Finish loading Sequence File",MSGMARK);
} else {
LogMessage("%s Wait for loading Sequence File",MSGMARK);
while(1!=nInitFlag){
}
LogMessage("%s Finish loading Sequence File",MSGMARK);
}
UnlockFile(fpLockFile,1,1,1,1);
if(EOF==fclose(fpLockFile)){
LogMessage("Close Lock File Failed!\n" );
return INFA_FAILURE;
}
//ReleaseMutex(hMutex);
return INFA_SUCCESS;
}
/**************************************************************************
Function: p_GetSeqVal_procDeinit
Description: Deinitialization for the procedure. Returns INFA_SUCCESS if
procedure deinitialization succeeds, else return INFA_FAILURE.
Input: procedure - the handle for the procedure
Output: None
Remarks: This function will get called once for the session at
deinitialization time. It will be called before the moduleDeinit
function.
**************************************************************************/
INFA_STATUS p_GetSeqVal_procDeinit( INFA_CT_PROCEDURE_HANDLE procedure, INFA_STATUS sessionStatus )
{
//mGetMutex();
//WaitForSingleObject(hMutex, INFINITE);
fpLockFile=fopen(LockFilePath,"w");
while(LockFile(fpLockFile,1,1,1,1)!=0){
Sleep(2000);
}
if(0==--nRefCount){
int i;
LogMessage("%s Writing Sequence File",MSGMARK);
fpSeqFile=fopen(SeqFilePath,"w");
for(i=0;i<strSeqList.nSeqNums;i++){
fprintf(fpSeqFile,"%s\t%lu\n",strSeqList.arrSequences[i].chSeqName,strSeqList.arrSequences[i].nSeqVal);
}
if(EOF==fclose(fpSeqFile)){
LogMessage("Close Sequence File Failed!\n" );
return INFA_FAILURE;
}
LogMessage("%s Finish Writing Sequence File",MSGMARK);
}
UnlockFile(fpLockFile,1,1,1,1);
if(EOF==fclose(fpLockFile)){
LogMessage("Close Lock File Failed!\n" );
return INFA_FAILURE;
}
//ReleaseMutex(hMutex);
//CloseHandle(hMutex);
return INFA_SUCCESS;
}
/**************************************************************************
Function: p_GetSeqVal_partitionInit
Description: Initialization for the partition. Returns INFA_SUCCESS if
partition deinitialization succeeds, else return INFA_FAILURE.
Input: partition - the handle for the partition
Output: None
Remarks: This function will get called once for each partition for each
transformation in the session.
**************************************************************************/
INFA_STATUS p_GetSeqVal_partitionInit( INFA_CT_PARTITION_HANDLE partition )
{
/*TODO: fill in code here*/
return INFA_SUCCESS;
}
/**************************************************************************
Function: p_GetSeqVal_partitionDeinit
Description: Deinitialization for the partition. Returns INFA_SUCCESS if
partition deinitialization succeeds, else return INFA_FAILURE.
Input: partition - the handle for the partition
Output: None
Remarks: This function will get called once for each partition for each
transformation in the session.
**************************************************************************/
INFA_STATUS p_GetSeqVal_partitionDeinit( INFA_CT_PARTITION_HANDLE partition )
{
/*TODO: fill in code here*/
return INFA_SUCCESS;
}
/**************************************************************************
Function: p_GetSeqVal_inputRowNotification
Description: Notification that a row needs to be processed for an input
group in a transformation for the given partition. Returns INFA_ROWSUCCESS
if the input row was processed successfully, INFA_ROWERROR if the input
row was not processed successfully and INFA_FATALERROR if the input row
causes the session to fail.
Input: partition - the handle for the partition for the given row
group - the handle for the input group for the given row
Output: None
Remarks: This function is probably where the meat of your code will go,
as it is called for every row that gets sent into your transformation.
**************************************************************************/
INFA_ROWSTATUS p_GetSeqVal_inputRowNotification( INFA_CT_PARTITION_HANDLE partition,
INFA_CT_INPUTGROUP_HANDLE inputGroup )
{
const INFA_CT_OUTPUTGROUP_HANDLE* outputGroups = NULL;
const INFA_CT_INPUTPORT_HANDLE* inputGroupPorts = NULL;
const INFA_CT_OUTPUTPORT_HANDLE* outputGroupPorts = NULL;
size_t nNumInputPorts = 0, nNumOutputGroups = 0, nNumPortsInOutputGroup = 0;
int i,j;
outputGroups = INFA_CTGetChildrenHandles(partition,&nNumOutputGroups,OUTPUTGROUPTYPE);
outputGroupPorts = INFA_CTGetChildrenHandles(outputGroups[0],&nNumPortsInOutputGroup,OUTPUTPORTTYPE);
inputGroupPorts = INFA_CTGetChildrenHandles(inputGroup,&nNumInputPorts,INPUTPORTTYPE);
rowSeqName=(char*)INFA_CTGetDataVoid(inputGroupPorts[0]);
for(j=0;j<strSeqListCache.nSeqNums;j++){
if(strcmp(rowSeqName,strSeqListCache.arrSequences[j].chSeqName)==0)
break;
}
if(j==strSeqListCache.nSeqNums){
for(i=0;i<strSeqList.nSeqNums;i++){
if(strcmp(rowSeqName,strSeqList.arrSequences[i].chSeqName)==0)
break;
}
if(i==strSeqList.nSeqNums){
strcpy(strSeqList.arrSequences[i].chSeqName,rowSeqName);
strSeqList.nSeqNums++;
strcpy(strSeqListCache.arrSequences[j].chSeqName,rowSeqName);
strSeqListCache.arrSequences[j].ptrSeqVal=&(strSeqList.arrSequences[i].nSeqVal);
strSeqListCache.nSeqNums++;
}
nSeqVal=++(strSeqList.arrSequences[i].nSeqVal);
strcpy(strSeqListCache.arrSequences[j].chSeqName,rowSeqName);
strSeqListCache.arrSequences[j].ptrSeqVal=&(strSeqList.arrSequences[i].nSeqVal);
strSeqListCache.nSeqNums++;
} else {
nSeqVal=++*(strSeqListCache.arrSequences[j].ptrSeqVal);
}
INFA_CTSetData(outputGroupPorts[0], &nSeqVal);
INFA_CTSetIndicator(outputGroupPorts[0],INFA_DATA_VALID);
return INFA_CTOutputNotification(outputGroups[0]);
}
/**************************************************************************
Function: p_GetSeqVal_eofNotification
Description: Notification that the last row for an input group has already
been seen. Return INFA_FAILURE if the session should fail as a result of
seeing this notification, INFA_SUCCESS otherwise.
Input: partition - the handle for the partition for the notification
group - the handle for the input group for the notification
Output: None
**************************************************************************/
INFA_STATUS p_GetSeqVal_eofNotification( INFA_CT_PARTITION_HANDLE partition,
INFA_CT_INPUTGROUP_HANDLE group)
{
/*TODO: fill in code here*/
return INFA_SUCCESS;
}
/**************************************************************************
Function: p_GetSeqVal_dataBdryNotification
Description: Notification that a transaction has ended. The data
boundary type can either be commit or rollback.
Return INFA_FAILURE if the session should fail as a result of
seeing this notification, INFA_SUCCESS otherwise.
Input: partition - the handle for the partition for the notification
transactionType - commit or rollback
Output: None
**************************************************************************/
INFA_STATUS p_GetSeqVal_dataBdryNotification (
INFA_CT_PARTITION_HANDLE partition,
INFA_CT_DATABDRY_TYPE transactionType)
{
/*TODO: fill in code here*/
return INFA_SUCCESS;
}