VHP Computing Group. Home Page.  
TechnologiesSpecialistsDownloadLinksSign GuestbookView Guestbook

20 September, 2001
Vladimir Trukhin
Senior Software Engineer
JSC "Votkinsk Hydroelectric Power Plant"
Fax: +7 (34241) 63297
E-mail:
vlt@votges.ru

Remote shutdown of applications

The Problem

The applications are created for the users and are started by the users. The application, being is started by the user, can work since morning and till evening. The user at this time can carefree leave his office to have dinner or to confer in an adjacent department.

But here the ill luck, just in it is high time you, as the manager of system or as the developer, should change structures of the tables or to execute urgent works on a server. What to do with the working application? To reset connection of the user or to shutdown the server, despite of the application which still is working and is using tables? It can result in loss of the data. Should you wait a return of the user? But other clients expecting end of works can suffer from it. Should you run to the user's office and unload the program? The good idea, if for this purpose is not necessary to overcome tens of miles.

The eternal question: «What to do?»

The Solution

Solution, as ever, is evident. It is necessary to provide a sending mechanism of command by the system manager, a receiving of this command by application, and an executing of shutdown.

No, it is not my original idea. You could read that it is enough to the manager to place into some directory some file, and it is enough to the application to locate this file and to shutdown itself without user’s operations.

It would be very easy to realize this, but we must consider the following conditions:

  • Every application has own specificity and it must be unloaded by individual way. Within the framework of one class we must provide an adjustment of the each concrete application.

  • It is possible that the program system consists of some number of the linked or non-linked applications; therefore an opportunity of unloading of all applications in one operation must exist just as an opportunity to unload the selected specific application.

  • The applications should not disappear from the screen suddenly.

  • The user needs to receive the message about the reasons of an unloading of the application or about the prospective time of renewal of work.

  • The user should have a delay of a unloading of the application to have time to make a hand-operated unloading of the application how he does it usually. At the same time he needs see, how many time remained before automatic ending.

An instrument of automatic shutdown of the application must has some abilities to realize these requirements:

  • An instance of a shutdown class must be invisible until the unloading command will be received.

  • The instance of class must react to two events:

1.   The first event is an appearance of file of the unloading command for concrete application in specified directory.

2.   The second event is an appearance of file of the unloading command for all applications in specified directory.

  • The instance of class must store a reference to the procedure of termination of the application. This procedure will emulate user's operations to finish the application.

  • The instance of class must become visible after reception of the unloading command. The command file can contain text of a message, which will be read by this instance. This message will be showed in a notification window for the user.

  • In the notification window we should place a process indicator, which will be show time, which stayed up to a start of the finishing procedure.

  • After the expiration of a waiting time of actions of the user, the instance of class must start the finishing procedure.

  • The instance of class must operate both in the application with main window, and in the application based on top-level form.

Process indicator

It is possible to use any available class of the process indicator or an appropriate ActiveX control. Let's assume, that we have not neither that, nor another. It is not very difficult to create the process indicator. It is enough to use the container, as a base class, and to place in it objects representing the indicator. It can be a collection of objects of the SHAPE class. The first object will show common length of process, another will point to the current value of process. The definition of such class can look like this:

DEFINE CLASS progressbar AS container
** Length of bar of process indicator
     Width = 100
** Height of bar of process indicator
     Height = 20
     BackStyle = 0
     BorderWidth = 0
** Max value of process variable
     MaxValue = (this.Width)
** Current value of process variable
     CurrentValue = 0
     Name = "progressbar"

 ** This object represents common length of process
ADD OBJECT border AS shape WITH ;
     ** To place on all area of the container
     Top = 0, ;
     Left = 0, ;
     Height = (this.parent.Height), ;
     Width = (this.parent.Width), ;
     Name = "Border"

 ADD OBJECT bar AS shape WITH ;
     ** To place on height of the container with zero length
     Top = 0, ;
     Left = 0, ;
     Height = (this.parent.Height), ;
     Width = 0, ;
     BorderStyle = 0, ;
     BorderWidth = 0, ;
     Curvature = 0, ;
     FillStyle = 0, ;
     FillColor = RGB(0,0,255), ;
     Name = "Bar"

PROCEDURE currentvalue_assign
** It is necessary to redraw the object of the current value
** after changing of the current value of parameter
     LPARAMETERS vNewVal
     ** The checking of the value of the accepted parameter
     do case
          case m.vNewVal<0
               THIS.CurrentValue = 0
          case m.vNewVal>0 and m.vNewVal<=THIS.MaxValue
               THIS.CurrentValue = m.vNewVal
          case m.vNewVal>THIS.MaxValue
               THIS.CurrentValue = THIS.MaxValue
     endcase
     ** Redraw object
     THIS.Bar.Width=int(THIS.Width*(THIS.CurrentValue/THIS.MaxValue))
ENDPROC

 PROCEDURE Init
     ** An adjustment of objects on the size of the container
     this.Bar.Height=this.Height
     this.Bar.Width=0
     this.Border.Height=this.Height
     this.Border.Width=this.Width
     ENDPROC

 ENDDEFINE

Notification window of user

The notification window must appear in screen after reception of the unloading command. There are the process indicator, the notifying text and red blinking light in this window. We will use this light to attract an attention of user.

After this event the process indicator must be started. The finishing procedure will have been started when the process indicator will have reached its max value.

Approximate code for this class:

DEFINE CLASS alarmwindow AS form

Height = 218
Width = 298
Desktop = .T.
DoCreate = .T.
AutoCenter = .T.
BorderStyle = 2
Caption = "Remote Shutdown"
TitleBar = 1
AlwaysOnTop = .T.
BackColor = RGB(255,255,128)
MaxValue = (this.Progress.Width)
CurrentValue = 0
lightFlash = 0
Name = "alarmwindow"
ParentRef = .F.

     ADD OBJECT label1 AS label WITH ;
     FontBold = .F., ;
     FontSize = 9, ;
     WordWrap = .T., ;
     Alignment = 2, ;
     BackStyle = 0, ;
     Caption = "Attantion! “+;
               “This application will be shutdowned. “+;
               “Save a data and exit!", ;
     Height = 33, ;
     Left = 26, ;
     Top = 5, ;
     Width = 265, ;
     Name = "Label1"

** The light is ON
ADD OBJECT lighton AS image WITH ;
     Picture = "red.bmp", ;
     BackStyle = 0, ;
     Height = 12, ;
     Left = 11, ;
     Top = 8, ;
     Width = 12, ;
     Name = "LightOn"

** The light is OFF
ADD OBJECT lightoff AS image WITH ;
     Picture = "grey.bmp", ;
     BackStyle = 0, ;
     Enabled = .T., ;
     Height = 12, ;
     Left = 11, ;
     Top = 8, ;
     Visible = .F., ;
     Width = 12, ;
     Name = "LightOff"

** The timer of a waiting time of the user
ADD OBJECT timer AS timer WITH ;
     Top = 11, ;
     Left = 270, ;
     Height = 23, ;
     Width = 23, ;
     Enabled = .F., ;
     Interval = 500, ;
     Name = "Timer"

** An object of the process indicator
ADD OBJECT progress AS progressbar WITH ;
     Top = 45, ;
     Left = 6, ;
     Width = 285, ;
     Height = 20, ;
     Name = "Progress", ;
     Border.DefHeight = "", ;
     Border.DefWidth = "", ;
     Border.BackStyle = 0, ;
     Border.Name = "Border", ;
     Bar.DefHeight = "", ;
     Bar.Name = "Bar"

ADD OBJECT inform AS editbox WITH ;
     FontBold = .F., ;
     Alignment = 0, ;
     BackStyle = 1, ;
     BorderStyle = 1, ;
     Enabled = .F., ;
     Height = 138, ;
     Left = 6, ;
     ReadOnly = .F., ;
     Top = 78, ;
     Width = 288, ;
     DisabledBackColor = RGB(255,255,128), ;
     DisabledForeColor = RGB(0,0,0), ;
     Name = "Inform"

PROCEDURE maxvalue_access
     RETURN THIS.Progress.MaxValue
ENDPROC

PROCEDURE maxvalue_assign
     LPARAMETERS vNewVal
     THIS.Progress.MaxValue = m.vNewVal
ENDPROC

PROCEDURE currentvalue_access
     RETURN THIS.Progress.CurrentValue
ENDPROC

PROCEDURE currentvalue_assign
     LPARAMETERS vNewVal
     THIS.Progress.CurrentValue = m.vNewVal
ENDPROC

 PROCEDURE start
     this.Timer.Enabled=.T.
ENDPROC

PROCEDURE Init
     LPARAMETERS lnTimeOut
     if type('lnTimeOut')!='N'
          this.MaxValue=lnTimeOut
     endif
ENDPROC

 PROCEDURE timer.Timer
     this.parent.LightOn.Visible=!this.parent.LightOn.Visible
     this.parent.LightOff.Visible=!this.parent.LightOff.Visible
     local lnLightFlash
     lnLightFlash=this.parent.LightFlash
     this.parent.LightFlash=this.parent.LightFlash+1
     this.parent.CurrentValue=this.parent.CurrentValue+this.Interval
     if this.parent.CurrentValue>=this.parent.MaxValue
          local loShutDown
          loShutDown=this.parent.ParentRef
          loShutDown.TimeOut()
          this.parent.Release()
     endif
ENDPROC

PROCEDURE inform.Init
     this.Value='The application will have been shutdowned to update it'
ENDPROC 

ENDDEFINE

The notification window and an application based on Top Level Form.

Now we shall define a subclass of notification window. The goal of it is a working in an application based on Top Level Form, without VFP's main window.

DEFINE CLASS alarmwindowtlf AS alarmwindow

Desktop = .F.
ShowWindow = 1
DoCreate = .T.
Name = "alarmwindowtlf"
Label1.Name = "Label1"
LightOn.Height = 12
LightOn.Width = 12
LightOn.Name = "LightOn"
LightOff.Height = 12
LightOff.Width = 12
LightOff.Name = "LightOff"
Timer.Name = "Timer"
PROGRESS.Border.DefHeight = ""
PROGRESS.Border.DefWidth = ""
PROGRESS.Border.Name = "Border"
PROGRESS.Bar.DefHeight = ""
PROGRESS.Bar.Name = "Bar"
PROGRESS.Name = "PROGRESS"
 

ENDDEFINE

Class of the remote shutdown of an application.

So, we can define class of the remote shutdown of an application. Just it must work invisibly all the time when the application is running, and locate the command files, and start the notification window, and unload the application without user's operations, if it is necessary.

DEFINE CLASS powershutdownms AS timer 

Height = 23
Width = 23
Enabled = .F.
** Interval of search of a file (by default)
Interval = 60000
** Name of a file for a unloading of all applications
systemshutdownfile = "ShutDown.txt"
** Name of a file for a unloading of the concrete application
taskshutdownfile = NULL
** Waiting time of actions of the user
shutdownwaittime = 180000
** Name of the finishing procedure
shutdownhandler = NULL
PROTECTED alarmmess
alarmmess = NULL
Name = "powershutdownms"
PROTECTED inform

** Start the notification window
PROCEDURE StartAlarm
     local loAlarm
     this.AlarmMess=createobject('AlarmWindow')
     loAlarm=this.AlarmMess
     loAlarm.Inform.Value=this.Inform
     loAlarm.MaxValue=this.ShutDownWaitTime
     loAlarm.ParentRef=this
     loAlarm.Show()
     loAlarm.Start()
ENDPROC

** Processing of event of end of expectation of the user
PROCEDURE timeout
     if !isnull(this.AlarmMess)
          local loAlarmMess
          loAlarmMess=this.AlarmMess
          if !isnull(this.ShutDownHandler)
               if type('this.ShutDownHandler')='C'
                    local lcShutDownHandler
                   lcShutDownHandler=this.ShutDownHandler
                    do &lcShutDownHandler
               else
                    this.ShutDown()
               endif
          else
               this.ShutDown()
          endif
     endif
ENDPROC 

** Finishing procedure (by default)
PROCEDURE shutdown
     quit
ENDPROC

** Search of a command file
PROCEDURE Timer
     local llShutDown
     llShutDown=.F.
     if file(this.SystemShutDownFile)
          this.Inform=filetostr(this.SystemShutDownFile)
          llShutDown=.T.
     else
          if !isnull(this.TaskShutDownFile)
               if type('this.TaskShutDownFile')='C'
                    if file(this.TaskShutDownFile)
                         this.Inform=filetostr(this.TaskShutDownFile)
                         llShutDown=.T.
                    endif
               endif
          endif
     endif
     if llShutDown
          this.Enabled=.F.
          this.StartAlarm()
     endif
ENDPROC

PROCEDURE Init
     this.Inform=''
ENDPROC

ENDDEFINE

The class of remote shutdown and an application based on Top Level Form.

The difference of this subclass from its parent is only code of method, called StartAlarm, of a starting of the notification window.

DEFINE CLASS powershutdowntlf AS powershutdownms 

Name = "powershutdowntlf" 

PROCEDURE startalarm
     local loAlarm
     this.AlarmMess=createobject('AlarmWindowTLF')
     loAlarm=this.AlarmMess
     loAlarm.Inform.Value=this.Inform
     loAlarm.MaxValue=this.ShutDownWaitTime
     loAlarm.ParentRef=this
     loAlarm.Show()
     loAlarm.Start()
ENDPROC

 ENDDEFINE

The Conclusion

By placing a code for creation of object of this class in the MAIN-module of the application, we shall ensure a remote unloading of the application and we shall untie our hands ourselves.

The example of such code can look as follows:

** MAIN
PROCEDURE MAIN
    PUBLIC goPSD
    SET CLASSLIB TO SHUTDOWN
    goPSD=createobject(‘PowerShutDownMS’)
    ** Interval for search of a command file = 3 minutes
    goPSD.Interval=3*60*1000
    ** Waiting time of actions of the user = 5 minutes
    goPSD.ShutDownWaitTime=5*60*1000
    ** Finishing procedure
    goPSD.ShutDownHandler=’Finish’
    goPSD.Enabled=.T.
    ** …
    ** Code of initialization of the application
    ** …
ENDPROC

PROCEDURE Finish
    ** код завершения приложения
ENDPROC


Home Page | Technologies | Specialists | Download | Links | Sign Guestbook | View Guestbook