Seaside packaged as an NT service

VA Smalltalk is a "100% VisualAge compatible" IDE that includes the original VisualAge technology and the popular VA Assist and WidgetKit add-ons.

Moderators: Eric Clayberg, wembley, tc, Diane Engles, solveig

Seaside packaged as an NT service

Postby PhotonDemon » Mon Jun 15, 2009 11:53 am

Hi Forum,

I appoligize in advance for the length of this post.

I finally have a "reasonably clean" package of my Seaside app as an NT service. I included as much of Seaside as I could sans the tests. I force the inclusion of some classes and some methods (ask if you want to know which. I also stop the packager from reducing PoolDictionaries, as Seaside needs many of the values that aren't referenced directly. I also used the laze initialization (with the setter method) of many of my component attributes that are connected to input fields in the generated html. Without refering to the setter method, it would have reduced out of the packaged image and I would have to include them manually.

I say "reasonably clean" because there is still a problem but I get around it with a little code. The problem is that the unmanagedNamespace part of the EsSmalltalkNamespace (a dictionary) contains two associations that have both a key and value of nil. When #keysDo: is called it collects the keys in a LookupTable which throws an exception because nil is an invalid key for a LookupTable (not so for a dictionary). I think the associations with a key and value of nil are the result of a packaging problem, a missing class or pool dictionary?

For now I get past the problem with a line of code, that removes the bad associations before any calls to keysDo:.

Code: Select all
   [(smalltalkNamespace unmanagedNamespace removeKey: nil ifAbsent: [#Done]) = #Done] whileFalse.


I am including the start up code for my app, in the hope it may help others. Note, I have extended #TranscriptTTY with the #lf method that outputs just a line feed and not both a carrage return and line feed as it looks cleaner in the log.

Code: Select all
lf
   "Output a line feed to the receivers device."

   Lf outputToTTY.


I have also extended #WASstServerAdaptor to expose #servletEngine to set up a Seaside log.

The start up code:

Code: Select all
starting
   "This is the entry point into the user code for the service.
   First register the application.  This sets the service status to
   RUNNING and establishes the callback for stopping the service application.
   Then write an entry in the event log.
   Then create and start the server object."
   | delay tty seasideLogPath seasideLogger args port portIndex portString |

   "Set up to write to a log."
   tty := TranscriptTTY default.
   "Tell windows we are started and how to stop us."
   AbtNtServiceInterface default
      registerService: self name stopSelector: #stopping;
      writeEventLogInformation: 1.

   "Used in stopping the service.  See below."
   StopServer := false.

   "Get an override value for the port."
   args := System commandLine.
   portIndex := args findFirst: [:arg | '-port:*' match: arg].
   (portIndex > 0) ifTrue: [
      portString := ((args at: portIndex) subStrings: $:) last.
      port := portString asNumber.
   ].
   (port isNil | (port = 0)) ifTrue: [port := 8788].

   "Start the adaptor on the port."
   SstServerAdaptor := WASstServerAdaptor port: port.
   SstServerAdaptor start.

   "Set up a log file for Seaside."
   seasideLogPath := CfsDirectoryDescriptor localPath, 'SeasideLogFile.log'.
   seasideLogger := (SstFileMessageLog for: SstServerAdaptor servletEngine) path: seasideLogPath; open.
   seasideLogger isCfsError
      ifTrue: [tty show: ('Could not open log: %1: %2' bindWith: seasideLogPath with: seasideLogger printString); lf]
      ifFalse: [SstServerAdaptor servletEngine logger: seasideLogger].

   "Register the application with Seaside."
   KscSIPCeSLSRoot initialize.
   tty lf; lf; show: 'KscSIPCeSLS started at: '; show: KscEasternStdTimeZone abtTimestampNow printString; lf.

   "Clean up the nils in the Smalltalk Namespace."
   self fixUpSmalltalkNamespace.

   "This loop keeps this process alive until the service is stopped."
   delay := Delay forSeconds: 300.
   [StopServer] whileFalse: [
      delay wait.
      Processor finalizeCycle.
      System scavenge.
   ].


The loop at the end of the start up code keeps the current process (I think Smalltalk considers it a UI process) alive until the service is stopped. Without the loop the code would fall out of the start up method and the process would end. Smalltalk would find that it didn't have a UI process (bad name, but I think that's what it is call, probably a hang over from GUI code) and start another process that justs keeps dropping into the idle process.

I hope this is helpful to others and I am open to suggestions for improvement.

Lou
Louis LaBrunda
Keystone Software Corp.
SkypeMe callto://PhotonDemon
mailto:Lou@Keystone-Software.com http://www.Keystone-Software.com
PhotonDemon
[|]
 
Posts: 176
Joined: Thu Dec 20, 2007 1:45 pm

Re: Seaside packaged as an NT service

Postby PhotonDemon » Tue Jun 16, 2009 6:27 am

Hi All,

A little followup. I forgot to mention that SstServerAdaptor and StopServer are class variables used to stop the service. And I forgot to show the stop the service code. So, here it is:

Code: Select all
stopping
   "This is run by the system when the service is being stopped.
   Smalltalk will exit after this method is run."
   | exitCode exitBlock tty |

   tty := TranscriptTTY default.
   tty show: 'Stopping server.'; lf.
   AbtNtServiceInterface default writeEventLogInformation: 3.
   StopServer := true.
   exitCode := 0.
   exitBlock := [System exit: exitCode withObject: nil].
"   Fork a process to stop the server if the code trying to do it nicely takes too long."
   [
      (Delay forSeconds: 30) wait.
      System isRuntime ifTrue: exitBlock.
   ] forkReadyAt: Processor highIOPriority named: 'Stop'.

"   Put code here to nicely stop server."
   SstServerAdaptor stop.

   System isRuntime ifTrue: exitBlock.


As I mentioned before, I was still having some packaging problems. I didn't mention one where I got a run time error of a missing class when using ObjectLoader. I had defined the so called missing class in the packaging and the statistics files showed the class as being packaged.

This kind of packaging oddity usually means that the image you are packaging from is not in the best of shape. So, I loaded everything into a clean image. This new image is three and a half meg. smaller (a good sign the old image had problems). After packaging with the new clean image, my remaining problems are gone.

Lou
Louis LaBrunda
Keystone Software Corp.
SkypeMe callto://PhotonDemon
mailto:Lou@Keystone-Software.com http://www.Keystone-Software.com
PhotonDemon
[|]
 
Posts: 176
Joined: Thu Dec 20, 2007 1:45 pm

Re: Seaside packaged as an NT service

Postby rjs » Sun Jun 28, 2009 4:39 am

PhotonDemon wrote:I also stop the packager from reducing PoolDictionaries, as Seaside needs many of the values that aren't referenced directly.

I believe this is something that Instantations should change. In general, the author of an application needs to ensure it will package correctly, by including packaging instructions for its specific needs. Look at the many implementors of #packagingRulesFor: for some examples.


PhotonDemon wrote:The loop at the end of the start up code keeps the current process (I think Smalltalk considers it a UI process) alive until the service is stopped. Without the loop the code would fall out of the start up method and the process would end. Smalltalk would find that it didn't have a UI process (bad name, but I think that's what it is call, probably a hang over from GUI code) and start another process that just keeps dropping into the idle process.

There may be a problem with doing things this way, at least in general. Perhaps this is a safe technique for Seaside. Anything else the caller of #starting is expecting to do will be delayed until you stop the system, possibly resulting in additional start up code executing after you think you are shutting down.

I suspect a better way to do this is to provide your own run-time start up class which is responsible for the running of the system. Check out EsImageStartUp class>>#run and the rest of that hierarchy. In your case, the start up class would provide the equivalent of the main UI.

Unfortunately, I haven't worked with Seaside enough to provide you with a more definitive answer.
Richard
rjs
 
Posts: 39
Joined: Thu Mar 27, 2008 11:07 am
Location: Port Perry, Ontario, Canada


Return to VA Smalltalk 7.0, 7.5 & 8.0

Who is online

Users browsing this forum: Yahoo [Bot] and 1 guest