A CXP Story
Posted: Mon Dec 21, 2015 11:14 am
I have been continuing my CXP education and trying to simplify some projects.
Here is the scenario:
Many Taxi drivers in New York are managed by Bobby Drakos Medallion program.
Many of them call the management office to get a payment receipt for one or more of their lease payments. They need to have some proof to show the TLC (NYC regulating organization) for whatever reason. These receipts are generated by a very complicated reporting system that saves the receipt as a PDF file and then emails it to the driver. A lot of interaction with the drivers is required to accomplish this task, many times per day. It has become necessary to provide an online website to give the drivers the ability to view or print any receipt(s) for any day by just logging in to the website.
The problem with web servers is that they always run as an Adminstrative service and therefore it becomes nearly impossible for a CXP program to generate the required report due to security issues related to printer drivers. To get around this problem, I had to have another program that constantly ran, not as a service, and it's task was to run the report and email it. The CXP program only wrote info to a database about which report(s) to build and email. This required a lot more support code than I originally had planned for.
I finally solved the problem by creating a CxpHttpServer.Exe in which I linked in the Medallion libraries and then run the CXP program from an Xbase++ program rather than IIS or Apache. This program runs as an application, not as a service and it has no problem creating the report. Also, debugging the program was much easier because I could now put WTF commands in the CXP code. I had to work around 2 issues that dogged me when trying to do this many months ago.
1. When a CXP program was updated, it failed to reload because the old *.cxp.dll was still in memory.
I solved this by running a thread in the server program that restarts the server when it sees an updated CXP file. Trying to unload the .DLL caused an IDSC, so I had to find another solution.
Here's the code that I added to CxpHttpServer.Prg:
2. Errors in the CXP program were not being sent back to the browser in the same way as when running under IIS or Apache.
I solved this by creating a new class named MyCxpProcesser which inherits from CxpProcessor. The Run() method captures errors and sends them back to the browser.
Here's the code:
I feel that I have finally developed a CxpHttpServer.Exe that is truly useful for large projects and I am looking forward to doing more CXP work.
I have updated all the sample programs in \exp20\samples\web20 with lots of new changes.
If you are working with CXP or other Xbase++ 2.0 web technologies, I recommend that you replace all the files in that folder with those in the attached zip file. Make sure to blank out the entire \exp20\samples\web20 folder first.
Here is the scenario:
Many Taxi drivers in New York are managed by Bobby Drakos Medallion program.
Many of them call the management office to get a payment receipt for one or more of their lease payments. They need to have some proof to show the TLC (NYC regulating organization) for whatever reason. These receipts are generated by a very complicated reporting system that saves the receipt as a PDF file and then emails it to the driver. A lot of interaction with the drivers is required to accomplish this task, many times per day. It has become necessary to provide an online website to give the drivers the ability to view or print any receipt(s) for any day by just logging in to the website.
The problem with web servers is that they always run as an Adminstrative service and therefore it becomes nearly impossible for a CXP program to generate the required report due to security issues related to printer drivers. To get around this problem, I had to have another program that constantly ran, not as a service, and it's task was to run the report and email it. The CXP program only wrote info to a database about which report(s) to build and email. This required a lot more support code than I originally had planned for.
I finally solved the problem by creating a CxpHttpServer.Exe in which I linked in the Medallion libraries and then run the CXP program from an Xbase++ program rather than IIS or Apache. This program runs as an application, not as a service and it has no problem creating the report. Also, debugging the program was much easier because I could now put WTF commands in the CXP code. I had to work around 2 issues that dogged me when trying to do this many months ago.
1. When a CXP program was updated, it failed to reload because the old *.cxp.dll was still in memory.
I solved this by running a thread in the server program that restarts the server when it sees an updated CXP file. Trying to unload the .DLL caused an IDSC, so I had to find another solution.
Here's the code that I added to CxpHttpServer.Prg:
Code: Select all
FUNCTION CxpLoop(oCxpHttpEndpoint)
LOCAL cCxpFile, aDir, cDateTime, cPath, cFileName, cDll, aDir2, ;
cDateTime2, i, aCxpDir
DO WHILE .t.
aCxpDir := Directory('*.cxp')
FOR i := 1 TO Len(aCxpDir)
cCxpFile := DC_CurPath() + '\' + aCxpDir[i,1]
aDir := Directory(cCxpFile)
cDateTime := DtoS(aDir[1,3]) + aDir[1,4]
cPath := DC_Path(cCxpFile)
cFileName := DC_Path(cCxpFile,.t.)
cDll := cPath + 'cxp-application\' + cFileName + '.dll'
aDir2 := Directory(cDll)
IF !Empty(aDir2)
cDateTime2 := DtoS(aDir2[1,3]) + aDir2[1,4]
IF cDateTime > cDateTime2 .AND. DllInfo(cDll) > 0
oCxpHttpEndpoint:stop()
RunShell('',AppName(.t.),.t.,.t.)
QUIT
ENDIF
ENDIF
NEXT
Sleep(50)
ENDDO
RETURN nil
I solved this by creating a new class named MyCxpProcesser which inherits from CxpProcessor. The Run() method captures errors and sends them back to the browser.
Here's the code:
Code: Select all
CLASS CxpHttpEndPoint FROM HttpEndPoint
EXPORTED:
METHOD DefaultProcessors()
METHOD DefaultConfig()
ENDCLASS
METHOD CxpHttpEndPoint:DefaultProcessors()
LOCAL aRet := SUPER:DefaultProcessors()
AAdd( aRet , "MyCxpProcessor" )
AAdd( aRet , "HtmlFileProcessor" )
RETURN(aRet)
METHOD CxpHttpEndPoint:DefaultConfig(cToken)
LOCAL oDO := DataObject():New()
cToken := Lower(AllTrim(cToken))
IF(cToken=="session")
oDO:Provider := "CxpCookieSessionManager"
oDO:Timeout := 15
ELSEIF(cToken=="storage")
oDO:Provider := "AppStorageManager"
oDO:Store := "cxp-data\application-storage.dbf"
ENDIF
RETURN(oDO)
* ------------
CLASS MyCxpProcessor FROM CxpProcessor
EXPORTED:
INLINE CLASS METHOD Run( cCxpFile, oWebAppHost, oCxpApplication, ;
oHttpRequest, oHttpResponse )
LOCAL x, oError
BEGIN SEQUENCE
x := SUPER:run( cCxpFile, oWebAppHost, oCxpApplication, ;
oHttpRequest, oHttpResponse )
RECOVER USING oError
oHttpResponse:contentType := 'text/html'
oHttpResponse:writeStr( oError:cargo )
oHttpResponse:send()
END SEQUENCE
RETURN x
ENDCLASS
I have updated all the sample programs in \exp20\samples\web20 with lots of new changes.
If you are working with CXP or other Xbase++ 2.0 web technologies, I recommend that you replace all the files in that folder with those in the attached zip file. Make sure to blank out the entire \exp20\samples\web20 folder first.