Pool Manager: An Automation server Sample

An Automation server exposes properties and methods that can be set and called from other Automation-capable applications.

All you need to do to create an Automation server is :

  1. Create a project.

  2. Add a .vcx file with an OLE Public class or a .prg file that defines a class with the OLEPUBLIC keyword.

  3. Create a .dll or an .exe file.

An Automation server can be deployed locally or remotely, exposing business functionality through methods that can be called and properties that can be set.

This is an example of a Pool Manager implemented in a custom Automation server. A client can send jobs to this Pool Manager, which can be located on a remote computer, and have it handle the job processing. This allows you to continue working because the resources of the pool manager handle all the work. The jobs in this sample are dummy simulation jobs.

To open the project for the pool manager sample

To run the pool manager sample

  1. Open Pool.pjx and choose Build.

  2. In the Build Options dialog box, select Build Executable and choose OK to create Pool.exe. The server is registered when you build the executable file.

  3. Run Pool.scx.

Pool Init Event

The first code to execute when you run the Pool form is the Init event, which creates a poolmgr object:

* Pool Init
SET PROC TO jobmgr.prg
THIS.oPoolMgr = CreateObject('Pool.Poolmgr')

When the oPoolMgr object is created, an array is dimensioned to keep track of jobs and a timer object, based on the PoolTimer class, is created to check for new or completed jobs.

Pool AddJob Method

When the user chooses the Print, Fax, or Excel Chart buttons on the pool form, the AddJob method of the form is called. AddJob performs the following:

  1. Redimensions an array to keep track of outstanding jobs

  2. Creates an object based on a specific subclass of NewJob: FaxJob, PrintJob, or GraphJob. These classes are defined in JOBMGR.prg, and take a reference to the form as an argument.

  3. Passes the job object to the NewJob method of the PoolMgr object:
    PARAMETER cJobType
    LOCAL oNewJobRef
    IF ALEN(THIS.aJobs)>1 OR TYPE('THIS.aJobs[1]') = 'O'
    DIMENSION THIS.aJobs[ALEN(THIS.aJobs)+1]
    ENDIF
    oNewJobRef = CreateObject(m.cJobType,THIS)
    THIS.oPoolMgr.NewJob(m.oNewJobRef)
    

PoolMgr NewJob Method

The NewJob method of the PoolMgr object redimensions the two dimensional PoolMgr job array and stores a reference to the job object in the first element of the new row and 0 to the second element, indicating that this is a new job.

PARAMETER oNewJob
IF TYPE('oNewJob') # 'O' OR ISNULL(m.oNewJob)
   RETURN .F.
ENDIF
IF ALEN(THIS.aJobObjs,1) > 1 ;
  OR ISNULL(THIS.aJobObjs[1])
   DIMENSION THIS.aJobObjs[ALEN(THIS.aJobObjs,1)+1,2]
ENDIF
THIS.aJobObjs[ALEN(THIS.aJobObjs,1),1] = oNewJob
THIS.aJobObjs[ALEN(THIS.aJobObjs,1),2] = 0

PoolTimer Timer Event

Every three seconds, the PoolTimer timer event code is executed. This code loops through the job array. If the second element is 0, indicating a new job, then a new PrintJob, FaxJob, or GraphJob object (subclasses of Job in poolmgr.prg) is created to manage the job. For example, the following line creates an object based on PrintJob and stores a reference to it in the timer’s aJobs array:

THIS.aJobs[ALEN(THIS.aJobs)] = CREATEOBJECT('Pool.PrintJob')

The second element in the array for that row is then set to 1 to indicate that the job has been started and the SetupJob method of the new object is called. A reference to the job object is passed to the SetupJob method:

THIS.aJobs[ALEN(THIS.aJobs)].SetupJob(THIS.Parent.aJobObjs[m.i,1])

The Job Class

The Job class is defined in Poolmgr.prg:

DEFINE CLASS Job AS FORM

When an object based on the Job class, or any of its subclasses, is created, the StartJob method of the object is called.

PROCEDURE SetupJob
   PARAMETER oJob
   THIS.oJob = m.oJob
   THIS.Caption = THIS.oJob.Jobtype
   THIS.Visible = .T.
ENDPROC

The PrintJob class, for example, is a subclass of Job. If the object created is based on PrintJob, the StartJob method does the default processing of the parent class StartJob method, setting the caption of the form and displaying a label. Then the INKEY( ) function is called to set a timeout of 10 seconds. In a functional implementation of this sample, instead of the INKEY( ) function, you would include code at this point to process the print job.

DEFINE CLASS PrintJob AS Job OLEPublic
   PROCEDURE StartJob
      DoDefault()
      =INKEY(10)
      THIS.EndJob()
   ENDPROC
ENDDEFINE

The EndJob method of the object calls the JobDone method of the original job object and releases the PrintJob, FaxJob, or GraphJob object.

PROCEDURE EndJob
   THIS.lbl1.caption = 'Ending job...'
   THIS.oJob.JobDone()
   THIS.oJob = .null.
   THIS.Visible = .F.
   THISFORM.Release 
ENDPROC

Code in the JobDone method of the original job object notifies the form that the job has been completed by calling the JobDone method of the form.

PROCEDURE jobdone
   IF TYPE('THIS.oFormRef')='O'
      THIS.oFormRef.JobDone(THIS.JobType)
   ENDIF
ENDPROC

Finally, the JobDone method of the form displays the status of the job in the list box on the form:

PARAMETER cJob
THIS.lstJobs.AddItem(m.cJob+' job is complete.')

The Life of the Job Object

The job object (based on the PrintJob, FaxJob, or GraphJob class defined in JOBMGR.prg) is: