Page 1 of 1

A CXP Story

Posted: Mon Dec 21, 2015 11:14 am
by rdonnay
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:

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
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:

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 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.

Re: A CXP Story

Posted: Mon Dec 21, 2015 11:51 am
by bwolfsohn
Roger,

nice...

needing iis or apache has always been a show-stopper for me... there are already too many cooks in the kitchen !!

Re: A CXP Story

Posted: Tue Dec 22, 2015 2:31 am
by skiman
Hi Roger,
This program runs as an application, not as a service and it has no problem creating the report.
How is this program started after a reboot of the server, and without login of a user?

Re: A CXP Story

Posted: Tue Dec 22, 2015 3:44 am
by rdonnay
How is this program started after a reboot of the server, and without login of a user?
It requires a login of a user.
We have been running 2 other apps on the server this way for almost 10 years.
Trying to run as a service has been difficult because we need the user interfaces.

One app runs 7 different timed services.
One is a mail server that sends out emails that are put into a database by the application.
One is the CXP server which is used mostly for this very special purpose.
Even with the small amount of interaction required (for login after a server reboot), it saves the computer operators hours of time per day by eliminating interaction with the drivers.

There are no anonymous users. Everything is custom.

We do have an IIS server running too for other web services.

This server rarely needs to be rebooted and when it does, one of us logs in and starts up the 3 apps with another shell app.

Re: A CXP Story

Posted: Tue Dec 22, 2015 7:43 am
by bwolfsohn
I could never get a UI with a service... what we ended up doing was creating a "client/server"... the server runs as a service, and the client, which controls it runs as an app with a user..

the service has default settings so it can respond after a reboot..