Differences between revisions 1 and 7 (spanning 6 versions)
Revision 1 as of 2014-11-03 10:54:31
Size: 4348
Editor: KaiJaeger
Comment:
Revision 7 as of 2017-01-24 09:30:52
Size: 3224
Editor: KaiJaeger
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= ServiceState = = ServiceState: interface between SCM and APL =
Line 9: Line 9:
{{{ServiceState}}} is a namespace that offers an interface between the Windows SCM (Service Control Manager) and a Dyalog APL application running as a service. {{{ServiceState}}} is a namespace script that offers an interface between the Windows SCM (Service Control Manager) and a Dyalog APL application running as a service.
Line 11: Line 11:
In you application workspace you need to have a function on `⎕LX` like this: In you application workspace you need to have a function on `⎕LX` similar to this:
Line 14: Line 14:
      Start
[1] #.WinFile.PolishCurrentDir
[2] {}#.ServiceState.Init 1 3
[3] '#'⎕WS'Event'#.ServiceState.Events.QuitDQ
[4] :If #.ServiceState.IsRunningAsService
[5] {}#.ServiceState.EstablishServiceCallback ⍬
[6] {#.TestService.Run ⍵}&⍬
[7] ⎕DQ'.'
[8] #.TestService.Off
[9] :Else
[10] #.TestService.Run ⍬
[11] :EndIf
      Start;ps
[1] ps←#.ServiceState.CreateParmSpace
[2] ps.ride←1
[3] #.ServiceState.Init ps
[4] :If #.ServiceState.IsRunningAsService
[5] {#.TestService.Run ⍵}&⍬
[6] ⎕DQ'.'
[7] #.TestService.Off
[8] :Else
[9] #.TestService.Run ⍬
[10] :EndIf
Line 28: Line 27:
Note that this example focuses just on getting the application running as a service. Note that this example focuses on getting the application running as a service. For the purpose of demonstrating how to overwrite defaults (no RIDE) it shows how to create a parameter space with default settings (line [1]) and then overwrite a setting (line [2]).

If you are happy with the defaults (which you can check by executing `#.ServiceState.CreateParmSpace.∆List`) then you can specify `⍬` as the right argument of `#.ServiceState.Init` in line [3].
Line 32: Line 33:
'''[1]''': This makes sure that if the workspace carries a path the work (or current) directory is set accordingly. '''[4]''': Checks whether the application is actually running as a Windows Service. This allows to execute the application in its own thread in case its running as a service but not otherwise - that makes debugging the application significantly easier.
Line 34: Line 35:
'''[2]''': This initializes the `ServiceState` interface. There are two arguments: '''[5]''': Here we start the real application. Note that this '''must''' run in its own thread because otherwise the application might well become unresponsive.
Line 36: Line 37:
 1. is a boolean that indicates whether the service shall connect to Ride (1) or not (0).
 2. is a positive integer specifying the number of seconds until `ServiceState` gives up waiting for the application to confirm a state change requested by the SCM (timeout).
'''[6]''': We need to execute `⎕DQ '.'` in order to make sure that the interpreter processes all events.
Line 39: Line 39:
Note that the two values are considered as constants; they are therefore established as niladic functions. For example, after having executed line [2] of the aforementioned `Start` function you can say:

{{{
      #.ServiceState.States.ride
1
      #.ServiceState.States.timeout
3
}}}

'''[3]''': `#.ServiceState.Events.QuitDQ` is a variable carrying an (any) integer. The application is supposed to send the same integer to `#` (when it wants to stop) in order to quit the `⎕DQ` on line 7.

'''[4]''': Checks whether the application is actually running as a Windows Service.

'''[5]''': This establishes `#.ServiceState.EstablishServiceCallback` as a callback that will handle all messages send by the Windows SCM.

'''[6]''': Here we start the real application. Note that this '''must''' run in its own thread because otherwise the application might well become unresponsive.

'''[7]''': We need to execute `⎕DQ '.'` in order to make sure that the interpreter processes all events.

'''[8]''': At this stage we need to execute `⎕OFF`; that's what `#.TestService.Off` is doing.
'''[7]''': At this stage we need to execute `⎕OFF`; that's what `#.TestService.Off` is doing.
Line 62: Line 43:
The application must make sure that it checks whether a state change was requested. This can be done by calling certain helpers as shown in this example: The application must make sure in its main loop that it checks for state changes, and act accordingly. This is all done by the Operator `#.ServiceState.CheckServiceMessages` which takes the name of a logging function as operand:
Line 65: Line 46:
      MainLoop dummmy;buffer;data;event;obj;rc;r;msg;cs
[1] Log INI.CONFIG.appshortname,' server started'
[2] :While 1
[3] ⎕DL 1
[4] :If #.ServiceState.ShallServicePause
[5] Log'The service is pausing'
[6] {}#.ServiceState.WaitForContinue ⍬
[7] Log'The service is resuming'
[8] :EndIf
[9] :If #.ServiceState.ShallServiceQuit
[10] Log'The service is in the process of shutting down...'
[11] :Leave
[12] :EndIf
[13] :EndWhile
[14] ⎕NQ'#'#.ServiceState.Events.QuitDQ
  MainLoop dummmy;buffer;data;event;obj;rc;r;msg;cs
[1] :Repeat
[2] ⎕DL 1 ⍝ What is reasonable here depends on the application
[3] :If (Log #.ServiceState.CheckServiceMessages)#.ServiceState.IsRunningAsService
[4] Log'The service is in the process of shutting down...'
[5] :Leave
[6] :EndIf
[7] :Until 0
Line 84: Line 58:
'''[14]''': This makes sure that the `⎕DQ` in `Start[7]` quits.

Apart from `ShallServicePause` and `ShallServiceQuit` there is also a function `ShallServiceContinue` but this is called by this function:

{{{
      {r}←WaitForContinue dummy
[1] ⍝ Returns always ⍬.
[2] ⍝ Consider calling this in case ShallServicePause has returned a 1 and you don't _
[3] ⍝ have to do any particular gymnastics but just want to wait for a state change.
[4] ⍝ It just waits until the status changes, although not necessarily to "continue".
[5] r←⍬
[6] :While 0=ShallServiceContinue
[7] ⎕DL 1
[8] :EndWhile
}}}
Line '''[3]''' checks for any changes signalled from the Service Control Manager (SCM). If a "Pause" is detected, then the function loops until either a "Continue" or a "Stop" is detected. The explicit result is 1 in case a "Stop" is detected and 0 otherwise.
Line 112: Line 72:
CategoryAplTree [[CategoryAPLTreeProject]] CategoryAplTree

ServiceState: interface between SCM and APL

(Hide table-of-contents)

ServiceState is part of the CategoryAplTree project.

Overview

ServiceState is a namespace script that offers an interface between the Windows SCM (Service Control Manager) and a Dyalog APL application running as a service.

In you application workspace you need to have a function on ⎕LX similar to this:

      Start;ps
[1]      ps←#.ServiceState.CreateParmSpace
[2]      ps.ride←1
[3]      #.ServiceState.Init ps
[4]   :If #.ServiceState.IsRunningAsService
[5]      {#.TestService.Run ⍵}&⍬
[6]      ⎕DQ'.'
[7]     #.TestService.Off
[8]  :Else
[9]     #.TestService.Run ⍬
[10] :EndIf

Note that this example focuses on getting the application running as a service. For the purpose of demonstrating how to overwrite defaults (no RIDE) it shows how to create a parameter space with default settings (line [1]) and then overwrite a setting (line [2]).

If you are happy with the defaults (which you can check by executing #.ServiceState.CreateParmSpace.∆List) then you can specify as the right argument of #.ServiceState.Init in line [3].

Remarks:

[4]: Checks whether the application is actually running as a Windows Service. This allows to execute the application in its own thread in case its running as a service but not otherwise - that makes debugging the application significantly easier.

[5]: Here we start the real application. Note that this must run in its own thread because otherwise the application might well become unresponsive.

[6]: We need to execute ⎕DQ '.' in order to make sure that the interpreter processes all events.

[7]: At this stage we need to execute ⎕OFF; that's what #.TestService.Off is doing.

The application

The application must make sure in its main loop that it checks for state changes, and act accordingly. This is all done by the Operator #.ServiceState.CheckServiceMessages which takes the name of a logging function as operand:

  MainLoop dummmy;buffer;data;event;obj;rc;r;msg;cs
[1]   :Repeat
[2]       ⎕DL 1                     ⍝ What is reasonable here depends on the application
[3]       :If (Log #.ServiceState.CheckServiceMessages)#.ServiceState.IsRunningAsService
[4]           Log'The service is in the process of shutting down...'
[5]           :Leave
[6]       :EndIf
[7]   :Until 0

Remarks:

Line [3] checks for any changes signalled from the Service Control Manager (SCM). If a "Pause" is detected, then the function loops until either a "Continue" or a "Stop" is detected. The explicit result is 1 in case a "Stop" is detected and 0 otherwise.

Project Page

For bug reports, future enhancements and a full version history see ServiceState/ProjectPage

Version Information

Original author:

KaiJaeger

Responsible:

KaiJaeger

Email:

kai@aplteam.com


CategoryAplTree

ServiceState (last edited 2018-03-03 11:39:17 by KaiJaeger)