View this PageEdit this Page (locked)Attachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007

Dynamic Content with Comanche


This tutorial assumes that you have comanche installed. You can read more about where and how you should install it here. We will show you how to create a server that serves completely dynamic content. That means that there is no files on the filesystem that are being served. All images, html, text, etc.. are generated.

Back to the top


First of all, you need ModuleAssembly and HttpService objects.
	myAssembly := ModuleAssembly core.
	myAssembly trackSessions.
	myAssembly sessionTimeout: 100sbs.
	myAssembly addPlug: [ :r | self process: r ].

	myHttpService := (HttpService startOn: aPort named: 'test').
	myHttpService module: myAssembly rootModule.

This code is pretty self explanatory. I personally put this in a constructor, so self process is a message in the class, and myAssembly and myHttpService are instance variables. Your mileage might vary depending on your design.

Back to the top


Sessions are a way of uniquely identifying an user (or more accurately, an specific webbrowser window in a computer browsing your site). You can store any state that you need there. For example, say that you are running a server that lets users browse presentations. Different users are going to be on different presentations and on different slides. Sessions allow you to uniquely identify users, as well as store any state. In this case, for example, you could store the current slide number that a user is browsing.

Accessing and storing data in sessions is accomplished through the usage of the 'attributes' Dictionary.

For example, to store the current slide number in the scenario above:
	KomSession current attributes add: #currentSlide->1.

And to retrieve the current slide:
	currentSlide := KomSession current attributes at: #currentSlide.

Pretty simple, but immensely useful.

Back to the top

Knowing what to process (handling commands)

Since the server is not serving files, you have to process what URL gets passed to the server, and act accordingly. This is done in your process message (see Basics).

For example:
	process: req
	"Where the server starts processing. Dispatches to other functions."
        ((req url)=('/')) ifTrue: [
		"generate root: ex: http://localhost:8080/"
		^HttpResponse fromString: self generateRoot.
        ((req url)=('/img')) ifTrue:
                "send an image back"
		^self handleImage: req.

Note that in this example, handleImage does not send a string back. See "Serving generated images" below.

Back to the top

Passing arguments to the server

You can pass arguments to the server several different ways. The easiest is probably using HTTP GET fields.

Say that you have a command URL "go" which goes to some dynamically generated page. Say that you want to have an
argument to this URL to WHICH page you want to go to.

The URL would be in the form "http://localhost:8080/go?p=where

Here, comanche parses this URL as:

The best way to access the queries is to use HttpRequest's getFields accessor and then use something like
getFields' keysAndValuesDo.

For example:
	handleGoToPage: req
		req getFields keysAndValuesDo: [:key :val | 
			(key='p') ifTrue: [	
				self goPage: (val asInteger). ].].

Yes, fields is a dictionary.

Back to the top

Serving generated html

Serving HTML that you generate yourself is pretty simple.
I recommend that you create a variable where you build up the html.

For example, this code will spit out the current Squeak version in an HTML page:

		outHTML := '<html><head><title> 2+2='
		outHTML := outHTML, (2+2) asString,'</title></head><body>'
		outHTML := outHTML, '<h1>Hello Cruel ', Smalltalk version
		outHTML := outHTML, '</h1></body></html>'
		^HttpResponse fromString: (self handleCommand: req).

Back to the top

Serving images of morphs

It is pretty easy to serve images that are essentially live screenshots of morphs. Morph has a nifty accessor called "imageForm" that represents an image (form) of a morph. From that, you need to send the data represented by the form. You can do this using a stream.

For example, if you want to send a PNG:
	aStream := (RWBinaryOrTextStream on: '').
	PNGReadWriter putForm: (aMorph imageForm) onStream: aStream.
	mimeType := 'image/png'.
	aStream reset.

	^ HttpResponse fromStream: aStream contentType: mimeType.

Or if you're feeling groovy, an JPEG instead:
	aStream := (RWBinaryOrTextStream on: '').
	JPEGReadWriter2 putForm: (aMorph imageForm) onStream: aStream.
	mimeType := 'image/jpeg'.
	aStream reset.

	^ HttpResponse fromStream: aStream contentType: mimeType.

Back to the top

Links to this Page