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
"Output a line feed to the receivers device."
I have also extended #WASstServerAdaptor to expose #servletEngine to set up a Seaside log.
The start up code:
- Code: Select all
"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."
registerService: self name stopSelector: #stopping;
"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.
"Set up a log file for Seaside."
seasideLogPath := CfsDirectoryDescriptor localPath, 'SeasideLogFile.log'.
seasideLogger := (SstFileMessageLog for: SstServerAdaptor servletEngine) path: seasideLogPath; open.
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."
tty lf; lf; show: 'KscSIPCeSLS started at: '; show: KscEasternStdTimeZone abtTimestampNow printString; lf.
"Clean up the nils in the Smalltalk Namespace."
"This loop keeps this process alive until the service is stopped."
delay := Delay forSeconds: 300.
[StopServer] whileFalse: [
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.