First let me say.... Glorp rocks! Kudos to Alan Knight for this framework. I'm really liking it, having written two home brew O/R frameworks in the past (mostly to learn how, and in less capable languages than Smalltalk), I can appreciate the flexibility of its design. This is going on my list of programs to read thoroughly from time to time. It's a very well written and very nice example of a well written OO system that anyone could learn a lot from. I'd add both Seaside and Magritte to that list as well. Reading great code is a lost art too few programmers do these days.
Glorp really gives me that object oriented feel and allows me to at least pretend I'm working with an object database, while keeping all the benefits of a relational database like constraints, indexing, and random queries. It's far more capable than I thought it'd be and totally pluggable if you need to add any capabilities. It'll do things Rails couldn't dream of as far as mapping and querying goes, and it does it in native Smalltalk syntax.
OK, enough of the Glorp envy. I've been working to get Glorp, Seaside, and Magritte all tied together so I can have a full stack framework to work with that allows me to work in Smalltalk at every level. I've used many languages and nothing comes close to the productivity I feel in Smalltalk, so naturally, I'm looking forward to finally using it from top to bottom, html, biz objects, and sql queries, all in Smalltalk.
While working on an implementation of a sort of ActiveRecord, but using Magritte for the meta data instead of the database, I quickly found I needed to mary one Glorp session to one Seaside session to keep everything simple and intuitive. It's a good match, however, I don't want to keep a connection to PostgreSQL, they're too valuable a resource to keep sitting idle and unused for 10 minutes while a session times out.
I played around a bit and found, at least so far, that within Glorp, the Squeak and Postgres adapters don't really maintain any state and can be swapped in and out of an existing GlorpSession. I decided that I'd write a connection pool for Glorp's SqueakDatabaseAccessor allowing me to tie a PostgreSQL connection to a request allowing much more scalability in the web scenario without risking running out of connections during peak loads. So, after a bit of playing around, I came up with a class called MGConnectionPool. I'm using the class side for all this code, taking advantage of the simple fact that in Smalltalk a class "is" a singleton ensuring there's only one instance of the pool in the image.
MGConnectionPool class>>initialize lock := Monitor new. connections := Dictionary new. MGConnectionPool class>>poolTimeout ^30 seconds MGConnectionPool class>>withUser: aUser password: aPassword server: aServer database: aDatabase in: aBlock | connection result expired | "Grab a connection from the pool util you find one that isn't expired, logout the expired ones" expired := true. [expired] whileTrue: [connection := self getConnectionUser: aUser password: aPassword server: aServer database: aDatabase. expired := DateAndTime now - connection value > self poolTimeout. expired ifTrue: [connection key logout]]. "pass the connection through the block, which will be the page request, and ensure it's returned to the pool when done" [result := aBlock value: connection key] ensure: [self returnConnection: connection forKey: (self makeKeyUser: aUser password: aPassword server: aServer database: aDatabase)]. ^result MGConnectionPool class>>getConnectionUser: aUser password: aPassword server: aServer database: aDatabase | key matchingConnections | ^ lock critical: [key := self makeKeyUser: aUser password: aPassword server: aServer database: aDatabase. matchingConnections := connections at: key ifAbsentPut: [OrderedCollection new]. matchingConnections ifEmpty: [matchingConnections add: (self newLoginForUser: aUser password: aPassword server: aServer database: aDatabase) -> DateAndTime now]. matchingConnections removeFirst] MGConnectionPool class>>makeKeyUser: aUser password: aPassword server: aServer database: aDatabase ^aUser , '~' , aPassword , '~' , aServer , '~' , aDatabase MGConnectionPool class>>newLoginForUser: aUser password: aPassword server: aServer database: aDatabase ^ (SqueakDatabaseAccessor forLogin: (Login new database: PostgreSQLPlatform new; username: aUser; password: aPassword; connectString: aServer , '_' , aDatabase)) login; yourself MGConnectionPool class>>returnConnection: aConnection forKey: aKey lock critical: [aConnection value: DateAndTime now. (connections at: aKey) add: aConnection]
This allows me to create a subclass of a Seaside session and glue Glorp and Seaside together like this.
MGGlorpSession>>commit: aBlock ^database inUnitOfWorkDo: aBlock MGGlorpSession>>execute: aQuery ^database execute: aQuery MGGlorpSession>>register: anObject ^database register: anObject MGGlorpSession>>ensureGlorpSessionOn: aDbAccessor database ifNil: [database := GlorpSession new system: (SBGlorpDescriptions forPlatform: aDbAccessor currentLogin database); accessor: aDbAccessor; yourself] MGGlorpSession>>responseForRequest: aRequest ^ MGConnectionPool withUser: (self application preferenceAt: #glorpUserName) password: (self application preferenceAt: #glorpPassword) server: (self application preferenceAt: #glorpServer) database: (self application preferenceAt: #glorpDatabase) in: [:dbAccessor | self ensureGlorpSessionOn: dbAccessor. database accessor: dbAccessor. super responseForRequest: aRequest]
Reading in the authentication from the current application config. Now I can feel safe using Glorp from Seaside, and from within any Seaside component, run Glorp queries with ease in a scalable manner. The code is totally generic and can reused for every future application by simply subclassing. Programming is getting more fun by the day; it's going to be a good year for Seaside. All the necessary frameworks exist to build a truly awesome and "fully" object oriented web stack that doesn't drag you down into the request response cycle inherent to other frameworks, even Rails. Glorp + PostgreSQL + Seaside + Magritte + Scriptaculous + Albatross + a little glue == Slick totally object oriented full stack Ajax web framework of the future, today!