December 5, 2010

Hybrid Qt applications with PySide and Django

This article describes some thoughts and experiments on developing applications targeted for mobile systems, especially MeeGo. I am trying to imagine a secure and agile way to write applications that are robust yet elegant.

About the author – I have worked with Ruby on Rails, Django and Qt. I think that web run-time technologies (HTML5, JavaScript and CSS3 – called WRT in this article) can provide a flexible way to build user interfaces. Many developers are familiar with these technologies, and maximizing WRT percentage in the codebase has potential to better portability for devices from different manufacturers. Personally to me MeeGo is the most interesting mobile operating system target. I am trying not to completely forget about business models, but then again, I have a software libre bias.

I have experimented of "hybridizing" Qt with Django. PySide provides Qt bindings for Python in LGPL licensing. Django is a CMS-oriented web framework written also in Python. Using Python instead of C++ has a drawback in runtime efficiency, but in some cases developer productivity and codebase maintainability is more important. Python has become one of my favourite languages.

This approach interests me because an application from the same codebase can be deployed both on desktop and on (a Nokia) mobile device. Alternatively a desktop/server Django application could serve plain WRT components also for non-Qt clients (but I can't comment how Apple would respond to this sort of application for the App Store). Android Market - well - they probably would not have a problem with it. That's all I have to say about marketing and business politics.

Nokia N900 is a fabulous platform for prototyping, on which I wrote an experimental application for controlling a TV set within the local area network (LAN). Figure 1 displays an event cycle in which the whole application is self-contained on the mobile device.

Fig 1. The software components on N900

Not depicted in figure 1 for clarity, the Python process contains an implementation of a finite-state machine. User actions are sent as events to the backend process handling logical states, while the WRT defines slots that update the UI.

This approach is reminescent of cloud computing, but with an important difference in that the data that moves between the server an the mobile device would not be stored on servers the user does not have control over. The data would never pass to the ISP either. Consider a Windows PC with a media repository was running PySide/Django in the LAN, and an iPhone owner could control the media stream onto a MeeGo TV set? Switch Windows for Linux and iPhone for a MeeGo device and it still would work without a heavy rewrite of the codebase. The PC would serve an ad-hoc "cloud" that could be operated from the hand-held device, acting as a remote controller. That is the core of this vision.

It makes sense to browse local data (from the PC) and "stream" it onto the mobile device. You wouldn't carry the precious data around in your phone, but it still would be reachable from home over a secure network connection. This is a data privacy issue. Photos are a good example. A major concern in any networked application is security and I've foolishly ignored many gaping secury holes in this prototype. As for my defense I can only say I am experimenting the feasibility and hardware performance so far.

 
Fig 2. Cloud computing within the LAN with MeeGo

The prototype application I've written (called MxManager) is a remote controller for Maximum T-8000 personal video recorder (PVR) that runs on embedded Linux. Its prorietary firmware provides a simple HTTP interface that mimics the physical remote controller. This prototype uses that API to set recordings from the EPG and play recordings on the TV. Here is a screenshot of the application (on MacBook desktop) and a blurry video of it running on N900.

Fig 3. MxManager UI


Qt hybrid on N900 on Vimeo. Sorry about the quality. It was shot up close, I guess I would have needed to strap on a macro lens.


The screen and remote control UI are individual QWebView elements and their content is composed by Django views. Events from the UI are triggered in JavaScript and events back from PySide are injected as JavaScript function calls into the page. PySide is running a finite-state machine (using QStateMachine) that holds the internal state of the application. Changes in UI are triggered by state change events. I wrote about this in more detail in a past blog post. The source code is released under the BSD, so you are welcome to have a look and give an opinion.

Initial experiments with QML appeared subjectively much faster, nearing smooth animation along the button press, but in the past I had a problem in sending signals into QML. This seems to have been fixed in PySide 1.0.0. This is a solid direction to experiment - perhaps Django could be used to compose QML for the QDeclarativeView..

Another video of the application in action with the TV set!

While building the web stack on well-proven frameworks, security issues still need to be considered carefully. Web sockets would be better designed for such eventing purpose than Ajax, with a better security model. I do repeat, this is not production-ready code.
If the mobile phone supported QtMobility, it could be possible to use mobile accelerometer and orientation to control playback. Turn the phone upside down to pause, for example. The next video (by perlinet, not me) portrays using QtMobility API in PySide with QML.


PySide is great technology and it's inclusion to MeeGo opens up many ways for developers to bring their applications to the platform. There are definite points to focus on this basis:
  • security implications
  • web sockets
  • QML
  • QtMobility experimentation
Especially intriguing for me would be to hook up digiKam database into a desktop-based Django app for browsing photos on a mobile device / tablet computer.

What's unique here is the merging of the Qt and web frameworks to operate within the same OS process, which makes it easier to separate the presentation and business logic. Personally I feel this technology seems feasible, and perhaps I will continue to investigate it further in 2011. There are web services that operate on a similar business model, and maybe open source software is following the trend.

November 19, 2010

Recipe: handling database communication breakdown in Rails and Django

This recipe is for users who manually handle persistent (shared) database connection, with Oracle, although it's probably easy to adapt for other database backends with small adjustments. I needed to implement this both in Ruby (Rails' ActiveRecord) and Python (django.db) daemons, both working in the same setting. I thought the solution was elegant enough to be cleaned up into a recipe.

Automated database connection pooling for Python (nor Ruby) is not supported with Oracle 10g. Only 11g introduced Database Resident Connection Pooling (DRCP). It probably makes this recipe redundant for Python users with 11g. Oracle has official support for using Python with Oracle Database 11g, using the cx_Oracle driver. I'm not sure but I think Oracle doesn't have any form of official support for Ruby, but the de-facto ruby-oci8 driver works well with ActiveRecord.

Without automated connection pooling you have two decent options. Either open and close a new connection for each request, or save open connections into shared memory. The latter has obvious performance benefits if the expected request rate is high, but exposes a number of other problems this recipe aims to address. A connection can abruptly terminate because of e.g. a network glitch or maintenance downtime, and the sysadmins deploying your software will be happier having to deal with less services to reboot.

The handler should catch exceptions only related to network or database resource failures, whereas in other cases (SQL typo?) the original exception should not be reacted upon. Figuring out the proper ORA error codes to use was an exciting task, I hope I got them right ;). Those guys at Oracle sure love to make things easy. Since I use a regular expression to match the error message string, a multitude of errors can be matched with a single rule. Fortunately, all errors dealing with Transparent Network Substrate are marked by prefix "TNS". In my tests most errors were indeed caught with the single "ORA-.....: TNS" regexp.

Python and Ruby have much in common, both could let me write this very expressively with about the same LOC. There is one small detail I'd like to bring up. These two expressions both compare the codes listed with the exception (error message in variable "message"), and return true if the message matches to one of the given ORA error codes.

Python

# Example message="ORA-03113: end-of-file on communication channel"
any(filter(lambda oracode: re.search('ORA-'+oracode,message), re.split(' *\n *| +(?=\d)',"""
    .....: TNS
    01000 01001 01014 01033 01034 01037 01089 01090
    011.. 015.. 016..
    031..
    28547
    30678
    """)[1:-1]))
Ruby
# Example message="ORA-01034: ORACLE not available"
%w{ .....:\ TNS
    01000 01001 01014 01033 01034 01037 01089 01090
    011.. 015.. 016..
    031..
    28547
    30678
}.select {|oracode| message[/ORA-#{oracode}/]}.any?
Especially take notice of Ruby's %w{multiline string} versus Python's re.split(' *\n *| +(?=\d)',"""multiline string""")[1:-1]. This just for the sake of the visual appearance of input data on the screen. Ruby makes it easier to write aesthetic code. Perhaps Python has an idiom for this, though, it just didn't pass my mind.

example_oci_exception_handler.py

# -*- coding: utf-8 -*-
"""
You may use this file under the terms of the BSD license as follows:

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL  OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of copyright holder.
"""
import re
import sys
from time import sleep
from myapp.settings import settings # your django settings module
from django.core.management import setup_environ
setup_environ(settings)
import cx_Oracle
from django.db import connection
from django.db.utils import DatabaseError
from logging import getLogger
log = getLogger(__name__)

class ExampleOCIExceptionHandler():
    """Recipe for handling lost Oracle database connection.
    
    Depends on Django ORM, and that proper settings.py exist and is initialized properly.
    
    Selects the network and connection errors, waits until the server is reachable again,
    and calls the method again during upon which the exception happened.
    
    This is very useful if the connection, for some reason, is shared between requests.
    Oracle 10g does not support connection pooling with cx_Oracle.
    """

    @staticmethod
    def dispatch(message_data):
        """Processes request.

        In case of database connection failure, the waitForOCIConnection() loop is called
        and connection should be re-established. In case of other Oracle errors,
        the error is raised again.
        """
        try:
            # do some database operations...

        except (cx_Oracle.Error, DatabaseError):
            """
            Catch lost database connection.

            Handles all TNS errors:
              ORA-xxxxx: TNS errors

            And all errors from these ranges:
              ORA-011xx: Database file errors
              ORA-015xx: Execution errors
              ORA-016xx: Execution errors
              ORA-031xx: communication errors

            Along with these specific errors:
              ORA-01000: maximum open cursors exceeded
              ORA-01001: invalid cursor
              ORA-01014: ORACLE shutdown in progress
              ORA-01033: ORACLE initialization or shutdown in progress
              ORA-01034: ORACLE not available
              ORA-01037: cannot allocate sort work area cursor; too many cursors
              ORA-01089: immediate shutdown in progress - no operations are permitted
              ORA-01090: shutdown in progress - connection is not permitted
              ORA-28547: connection to server failed, probable Oracle Net admin error
              ORA-30678: too many open connections

            """
            msg = str(sys.exc_info()[1]).rstrip()

            # is the error about broken connection?
            if any(filter(lambda oracode: re.search('ORA-'+oracode,msg), re.split(' *\n *| +(?=\d)',"""
                .....: TNS
                01000 01001 01014 01033 01034 01037 01089 01090
                011.. 015.. 016..
                031..
                28547
                30678
                """)[1:-1]) # ignore outermost items as they are empty strings
            ):
                log.error(msg)
                ExampleOCIExceptionHandler.waitForOCIConnection()
                # enter recursion and call this method again..
                sleep(2)
                return ExampleOCIExceptionHandler.dispatch(message_data)

            else:
                log.debug(msg)
                log.debug("Error seems to be not about lost connection, raising again ..")
                raise

    @staticmethod
    def waitForOCIConnection(time_to_wait=60):
        """Creates a new database connection, loops until one is established."""
        # nullify the connection first, since it can't discover that the socket is gone
        connection.connection = None
        while not connection._valid_connection():
            try:
                log.info("Attempt to establish OCI connection to %s ..." % [
                    settings.DATABASES['default']['NAME']])
                # in django parlance, calling _cursor() opens the database connection
                cursor = connection._cursor()
                sleep(2)
                return connection._valid_connection()

            except (cx_Oracle.Error, DatabaseError):
                exctype, message = sys.exc_info()[:2]
                log.error(str(message).rstrip())
                log.info("Retrying OCI connection in %i seconds" % time_to_wait)
                sleep(time_to_wait)
                return ExampleOCIExceptionHandler.waitForOCIConnection(time_to_wait)


example_oci_exception_handler.rb
# encoding: utf-8
=begin
You may use this file under the terms of the BSD license as follows:

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL  OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of copyright holder.
=end
require 'rubygems'
require 'active_record'

# Recipe for handling lost Oracle database connection with ActiveRecord.
#
# Depends on RAILS_ROOT and RAILS_ENV variables to be set and RAILS_ROOT/config/database.yml file to exist.
#
# Selects the network and connection errors, waits until the server is reachable again,
# and calls the method again during upon which the exception happened.
#
# This is very useful if the connection, for some reason, is shared between requests.
class ExampleOCIExceptionHandler

  # Processes request.
  #
  # In case of database connection failure, the waitForOCIConnection() loop is called
  # and connection should be re-established. In case of other Oracle errors,
  # the error is raised again.
  def self.dispatch(message_data)
    begin
      # do some database operations...

    # Catch lost database connection.
    #
    # Handles all TNS errors:
    #   ORA-xxxxx: TNS errors
    #
    # And all errors from these ranges:
    #   ORA-011xx: Database file errors
    #   ORA-015xx: Execution errors
    #   ORA-016xx: Execution errors
    #   ORA-031xx: communication errors
    #
    # Along with these specific errors:
    #   ORA-01000: maximum open cursors exceeded
    #   ORA-01001: invalid cursor
    #   ORA-01014: ORACLE shutdown in progress
    #   ORA-01033: ORACLE initialization or shutdown in progress
    #   ORA-01034: ORACLE not available
    #   ORA-01037: cannot allocate sort work area cursor; too many cursors
    #   ORA-01089: immediate shutdown in progress - no operations are permitted
    #   ORA-01090: shutdown in progress - connection is not permitted
    #   ORA-28547: connection to server failed, probable Oracle Net admin error
    #   ORA-30678: too many open connections
    #
    rescue OCIError, ActiveRecord::StatementInvalid
      msg = $!.message
      
      # is the error about broken connection?
      if %w{
        .....: TNS
        01000 01001 01014 01033 01034 01037 01089 01090
        011.. 015.. 016..
        031..
        28547
        30678
      }.select{|oracode| msg[/ORA-#{oracode}/]}.any?
      
        logger.error msg
        waitForOCIConnection()
        # enter recursion and call this method again..
        return dispatch(message_data)

      else
        logger.debug msg
        logger.debug("Error seems to be not about lost connection, raising again ..")
        raise $!
      end
    end
  end


  # Attempts to establish database connection.
  # Loops until ActiveRecord is connected.
  #
  # Set time_to_wait in seconds.
  def self.waitForOCIConnection(time_to_wait=60)
    begin
      database_configuration = YAML.load_file(File.join(RAILS_ROOT,"config","database.yml"))
      ActiveRecord::Base.configurations = database_configuration
      logger.info(
        "Attempt to establish OCI connection to %s ..." % database_configuration[RAILS_ENV]['database'])
      ActiveRecord::Base.establish_connection(database_configuration[RAILS_ENV])
      ActiveRecord::Base.connection # essential to open connection
      return ActiveRecord::Base.connected?

    rescue OCIError, ActiveRecord::StatementInvalid
      logger.error($!.message)
      logger.info("Retrying OCI connection in %i seconds" % time_to_wait)
      sleep time_to_wait
      return waitForOCIConnection(time_to_wait)
    end
  end
end

October 15, 2010

Interacting with legacy Oracle database in Python

I needed to use an Oracle database in Python. It all went rather smoothly with the guides, hints and examples, but I was missing a walkthrough. This memo is about setting up Oracle with Python and how to use legacy database with Django. Better yet, use Django Models without any of the HTTP functionality.

Oracle more or less has official support for cx_Oracle, and it was fairly easy to set up. cx_Oracle needs Oracle headers to compile. From the Oracle download site get, according to your OS and arch, instantclient-basic, instantclient-sqlplus and instantclient-sdk. The Oracle download site requires registration.

Select a working path and extract the Instant Client packages.

$ cd /opt
$ unzip ~/Downloads/instantclient-basic-10.2.0.4.0-macosx-x64.zip
$ unzip ~/Downloads/instantclient-sdk-10.2.0.4.0-macosx-x64.zip
$ unzip ~/Downloads/instantclient-sqlplus-10.2.0.4.0-macosx-x64.zip

It is necessary to set the environment variables ORACLE_HOME, LD_LIBRARY_PATH and DYLD_LIBRARY_PATH. Put these into a convenient file and update the environment.

~/.oraenv

export ORACLE_HOME=/opt/instantclient_10_2
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME
export DYLD_LIBRARY_PATH=$ORACLE_HOME

On OS X, you need to create one symlink for the build to succeed:

$ cd /opt/instantclient_10_2/
$ ln -s libclntsh.dylib.10.1 libclntsh.dylib

Now you are ready to build and install the cx_Oracle driver.

$ cd /opt
$ tar xzf ~/Downloads/cx_Oracle-5.0.4.tar.gz
$ cd cx_Oracle-5.0.4
$ source ~/.oraenv # load environment
$ python setup.py build
$ python setup.py install

Assuming the database is already running somewhere, enter the connection parameters to this python script and test the connection:

test_connection.py

# -*- coding: utf-8 -*-
import cx_Oracle
connection = cx_Oracle.connect(
    "user",
    "pass",
    "127.0.0.1:1524/some.oraservice"
    )
cursor = connection.cursor()
cursor.execute("select  * from v$version")
for column_1 in cursor:
    print "Values:", column_1
You should see something like this:
$ python test_connection.py
Values: ('Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi',)
Values: ('PL/SQL Release 10.2.0.4.0 - Production',)
Values: ('CORE\t10.2.0.4.0\tProduction',)
Values: ('TNS for Linux: Version 10.2.0.4.0 - Production',)
Values: ('NLSRTL Version 10.2.0.4.0 - Production',)
BUT if you get:
cx_Oracle.DatabaseError: ORA-12737: Instant Client Light: unsupported server character set WE8ISO8859P15
You took the BASICLITE package!!! Go back to download the proper package and start from the beginning.

Python can connect to the database with cx_Oracle, and Django models offer a clean way to make use of that connection. If you happen to have schema read privileges to the database, Django can generate models.py from it. Now, this is pretty nice feature when it comes to interacting with legacy database, as we shall see.

myapp/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.oracle',
        'NAME': '127.0.0.1:1524/some.oraservice',
        'USER': 'user',
        'PASSWORD': 'pass',
    }
}

myapp/manage.py

from django.core.management import execute_manager
import settings
if __name__ == "__main__":
    execute_manager(settings)

The database SQL shell is useful to have and to test the connection. You can get greeted with the friendly SQL*Plus console by command dbshell:

$ source ~/.oraenv # remember environment!
$ python myapp/manage.py dbshell
SQL*Plus: Release 10.2.0.4.0 - Production on Fri Oct 15 12:56:05 2010

Copyright (c) 1982, 2007, Oracle. All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL>

Then you are ready to inspect the schema and generate the Python classes:

$ python myapp/manage.py inspectdb > myapp/models.py
Check the new file and assign "primary_key=True" to a column you choose, for each table you need.

Say your legacy db has a table called OraAbbOy1001 you need to manipulate. You may have a start script bin/start.py (as you don't run this with django-manage) in which you setup Django and do whatever you do to init your program. In myapp/logic.py you import the models and operate with the database.

bin/start.py

# -*- coding: utf-8 -*-
import myapp.settings
from django.core.management import setup_environ
setup_environ(myapp.settings)
# before do something else,
# the three lines above here is your first operation.
You can then operate on the models by finding or creating instances (selecting and inserting rows) through the ORM. It is also possible to mix in raw SQL, like in this example. I had trouble figuring out how to set a custom sequence name (legacy value) so I opted to select it manually.

myapp/logic.py

# -*- coding: utf-8 -*-
import time
from django.db import models, connection
from myapp.models import OraAbbOy1001

id = # comes somewhere
try:
    user = OraAbbOy1001.objects.get(id=id)

except OraAbbOy1001.DoesNotExist:

    # select nextval from ID sequence
    seq_name = 'q1001user'
    cursor = connection.cursor()
    cursor.execute("SELECT %s.NEXTVAL FROM DUAL" % seq_name)
    userid = cursor.fetchone()[0]

    # create new user
    user = OraAbbOy1001.objects.create(
        id=userid,
        name='Vic Video',
        create_date=time.strftime('%Y-%m-%d',time.localtime()),
    )

    log.info("OraAbbOy1001 user id %i (%s) created" % (
        user.id,
        user.name
        )
    )

SQLAlchemy looks very interesting. If you need to have more control over the queries, look up for it.

BTW, I'm not affiliated with O'Reilly, I just like the illustrations. They do have many good books.

September 11, 2010

QML signals on PySide

I studied QML with signal-slot communication across to Python a bit.
If you are looking for examples in source code, see

The latter one use PySide but not QML, and has a preview video elsewhere in this blog. Here are my earlier experiments.

This video shows a very small application, consisting of two files - webview.qml and webview-qml.py.
The first three signals originate from QML and Python opens up a QMessageBox to display them, along by printing to stdout. The third signal will emit a fourth signal from Python to QML, with an url as a parameter, and the url will open inside a WebView.

This app evolves the idea a bit further. The HTML5 canvas demos seen in the video are not by me, they are from the web. There is a state machine working behind the scenes.

September 10, 2010

Compiling PySide on OS X

Binary packages of PySide are available from developer.qt.nokia.com/wiki/PySideBinariesMacOSX. You only need to compile it yourself if you have explicit reason to.

However, if for any reason you would like to compile it yourself, read on. First of all, download Qt SDK. After it has been installed you can use this Makefile script to download, compile and package PySide from git master.

$ git clone git://gitorious.org/~lamikae/pyside/lamikae-pyside-packaging.git
$ cd lamikae-pyside-packaging/osx/
$ make package

Maybe after an hour or more you should have a .pkg file, which has PySide dynamically linked to Qt installed from official dmg.
Install it with the command:

$ sudo installer -pkg pyside-<version>.pkg -target "/"
See if it works (if there is no error, it does =) and try out some of the official PySide examples.
$ python -c "from PySide import QtCore"
You can inspect the package contents by
$ xar -xf pyside-<version>.pkg Bom && lsbom package.pkg/Bom

May 12, 2010

Gource animation of Rails-portlet development

Gource is a fabulous program that can visualize common version control repository history. This is the development of Rails-portlet for the last few years, in a few seconds.

I had to write a small Python script that parses logs from Git submodules. The script is at Gist and may be freely used.

April 15, 2010

Testing JavaScript & XHR with Python

I am working on a library for Django - which I'll describe later - and for building this project I need to test the behavior of XHR on the HTML page. That is, I have a page, inject some JavaScript (that may do a request on the server), and see how it changes the document. The Django application the library is designed for creates some parts of the original page and processes some parts of the server-side XHR.

Python is great for this. I've mentioned already in this blog how I like Qt4, and it proves to be very useful again. I could not get PySide running on OS X just yet, so I opted for PyQt4.

Four tests; four evenings working with them – each having an intricate problem to solve ;) Two tests work on the local document, the other two connect to the Portlet test bench, running on localhost. These tests trigger onclick submit actions from the XHR test page, simulating a user clicking a button.

This way it is possible to test the effect of JavaScript injection onto any web page in the wild. At least the WebKit response.. Firefox could be tested by using python-gtkmozembed. QWebKit also has methods for binding Python variables to the page (it is possible for JavaScript to trigger a Python callback function) and render an image snapshot of the page.

If you wish to run the tests, download the file, start up the Rails server with the test bench and run

$ python xhr_tests.py

April 5, 2010

Comparison of Web Proxy Portlet, Portletbridge and html2jsr286

There are at least three different JSR 168 / 286 portlet projects that have a similar target: to allow custom web apps and arbitrary web content be accessible in portlets.

I am the author of "Rails-portlet" and it's alive - I just updated the setup guide. It all started when I needed to develop a webapp for Liferay, and I wanted to do it with Rails. html2jsr286 is not an indication of the NIH syndrome, I just now learned of their existance. I inspected their source code, and this is a quick run-down of their features and pondering of future direction.

These are the common features in all three projects:

  • Getting content from a downstream site
  • Proxying of remote resources (e.g. images, Flash etc.)
  • Regular expression defining which URL's are in the portlet and which should be regular links
  • Rewriting CSS urls
  • Moving Javascript and CSS links out of the head
  • Session cookie support
All three have the same shortcoming concerning Ajax: the urls are not being rewritten so that XHR from the browser is not catched by the portlet.

WebProxyPortlet is being developed mainly for uPortal, and

..it provides mechanisms for connecting to and rendering HTML with options for clipping, maintaining session information, handling cookies. Proxied content is rendered within the portlet window. Web Proxy Portlet is often used to incorporate web content or applications that are built and run in non-Java environments allowing a site flexibility for integrating with many different technologies.

Sources of portletbridge

$ mkdir portletbridge
$ cd portletbridge/
$ cvs -z3 -d:pserver:anonymous@portletbridge.cvs.sourceforge.net:/cvsroot/portletbridge co -P .
Sources of WebProxyPortlet
$ svn co https://www.ja-sig.org/svn/portlets/WebproxyPortlet/trunk/ WebproxyPortlet
Sources of html2jsr286
$ git clone git@github.com:lamikae/html2jsr286.git

My impression after a while looking through the sources is that WebProxyPortlet is the most advanced, and has a number of nice features, especially in ways of user authentication and portlet configuration through the user interface.

Considering code cohesion and the number of Java classes: WebProxyPortlet has 58, Portletbridge 51 and html2jsr286 only 10 Java source files. Here are links to their main view methods:

void renderContent(final RenderRequest request, final RenderResponse response) in WebProxyPortlet.java
void doView(final RenderRequest request, final RenderResponse response) in BridgeViewPortlet.java
void render(RenderRequest request, RenderResponse response) in Rails286Portlet.java

WebProxyPortlet works in JSR 168 containers and supports a variety of authentication techniques, handles caching, has pluggable Java classes for custom request filtering and page processing. On top of this is uses a whole lot of JavaBeans and JSPs that make the portlet configurable from the GUI in the portlet config mode. I am not sure how much runtime overhead this accouts to, and how useful it is to let users select the HTML parser the portlet is going to use. Page processing is implemented by chaining up SAX filters.

Some criticism must be given; there are virtually no unit tests and there seems to be some code repetition and lots of "boilerplate" code (which is quite difficult to neutralize in Java) and somehow it just hurts me that the renderContent() method is 374 lines long. It could use some refactoring.

Portletbridge seems to have stalled active development some years ago, but there seems to be some recent code cleanup in progress, according to CVS logs.. It probably is doing what it's supposed to, feature-wise it seems to be close on par with html2jsr286, and it runs on JSR 168 portals. Page processing is done by XSLT, a feature that I really like.

html2jsr286 and Rails-portlet are strong in that they offer a toolchain for a Rails developer to get quickly into deployment with as much automation as possible. The other two projects look like they are more tuned to serve the portal administrator instead of the development team. The Rails-portlet project tools can also work without Rails, but then the deployment gets more encumbered, requiring more dirty work with Java and XML. Rails-portlet is targeting for web app developers who want to keep a healthy distance to the J2EE stack and "get things done" quickly.

html2jsr286 passes the Liferay UID in request headers, with a unique authentication mechanism. It can also handle Liferay GID – feature that is missing from the other two portlets. It is, however, restricted for JSR 286 portals, as it makes use of portlet filters, which were introduced only in JSR 286. The rationale behind this is to improve code coherence (there are no technical reasons that would demand using filters). JSR 286 container will, however, be needed for the XMLPortletRequest feature. The portlet is being used on at least two production sites, and in practise is doing fairly well. Page processing is done by HTML Parser, which does a decent job but has shortcomings that I would like to address with XSLT, and this is what Portletbridges is doing.

Nonetheless, I am considering a rational move to leverage WebProxyPortlet in Rails-portlet, and it seems that the first step is to get it running on Liferay and uPortal, hosting the portlet test bench.

Next big step would be to work on Ajax support, by carrying out ideas from these posts to implement XMLPortletRequest.

April 4, 2010

Flash problems after upgrading from Hardy to Lucid

I upgraded Ubuntu 8.04 (Hardy Heron) to 10.04 (Lucid Lynx) a few days ago. While it is still in "RC" stage, the graphical update-manager already offered me the upgrade. Which I did. I had to resolve to an ugly fix after the upgrade to get the proprietary Adobe Flash working. No matter what apt-get command I entered, there was this error message:
Package is in a very bad inconsistent state - you should reinstall it before attempting a removal.
There were a few others too who had similar problems on Ubuntu forums and this solution finally worked for me: remove all occurances of "flashplugin-installer" and "flashplugin-nonfree" from /var/lib/dpkg/status.

March 30, 2010

Mobile SDKs

I met a nice guy who has a web page that presents an award-winning grill he's designed. He briefly mentioned the site that runs entirely on Flash and I offered to upgrade it for HTML, CSS and Javascript. I wanted to learn some CSS3 and MooTools on a real-world problem. The design consists of a menu, a large image and a caption. Images are switched either by timing or by user clicking next/back buttons, complete with fade out and fade in. As it came together I tested it on Firefox, Chrome, Opera, Arora, Safari, Konqueror and IEs 6-8. I decided to try out the popular mobile SDKs as well then. Namely, Nokia Maemo 5, Google Android and Apple iPhone. This is an overview and a quick, superficial evaluation of the mobile developer toolkits.

My aim was not to work on any platform-specific code, but to test out how these devices would display the page and the image transitions. I very much appreciate the ease of installation, good documentation and in general getting to the point very quickly with a minimum amount of hassle. Happy developers make better software. Even better when they needn't to fight with the toolchain. :)

Maemo 5 was the first one I picked since being Debian-based it seems to be the most open platform. I remember briefly using a Nokia N770 in 2006 and apparently Nokia thinks it's a product line worth upgrading by N810 and now N900 released in 2009. Unfortunately even with the backing of Nokia and several years of development, the documentation is not quite as organized as I'd like it to be, with all respect. From the main Maemo.org developer page, it takes five clicks through a Nokia site on another host, back to Maemo wiki, and to the cryptic installation notes that remind me of Gentoo manuals. Choosing those five clicks wasn't quite trivial either.

Thankfully some fellow Maemo developers kindly guided me to a hidden Nokia page hosting a prepared Ubuntu virtual image with the SDK and the rest of the tools. Apparently even hosted at nokia.com this is an unofficial image. I let the 1.7 GB image to download overnight and launched it in VirtualBox the next morning without a hitch. It brought up the Ubuntu desktop, with no assistance or an emulator icon around. Here be dragons indeed.. Spells and incantations will apparently be needed. Now, somewhere in the middle of the installation spellbook it says "Xephyr" needs to be started:

$ Xephyr :2 -host-cursor -screen 800x480x16 -dpi 96 -ac -kb &

Mm-hm. This launches up an empty, gray X window. Now, the commands to start the "scratchbox environment" and inside it, the Maemo startup script:

$ setxkbmap fi
$ /scratchbox/login
> export DISPLAY=:2
> af-sb-init.sh start

Success! I still think I shouldn't need to know any of this, but it is running with a fair amount of work. Unfortunately none of the apps were installed and the virtual device seemed quite empty – all there was this Application Manager:

Some extra packages need to be manually installed into the scratchbox environment:

> apt-get install nokia-binaries nokia-apps

Then I had the Maemo browser, which displayed the page correctly. Happiness!

I wonder what Intel does to the SDK now after merging Moblin and Maemo to MeeGo...

Next, testing Google Android. The SDK is a ~20 MB zip file that is a Java GUI which downloads the actual SDK. The size after downloading platforms v1.6 and v2.1 is 343 MB.

Creating an Android virtual device was very easy with the GUI.

And it renders correctly on version 2.1!

Finally, since I work on OS X, I can also try out the iPhone SDK. It required a registration and another overnight download of the whopping 2.8 GB package that comes with bundled Xcode. After downloading, QuickSilver finds the simulator quite nicely and it looks sleek.

I have only a few times used a real iTouch device, but I see the appeal in their design. iPhone with its Safari browser can display my test page correctly.

Overall, considering these mobile SDKs, Apple provides the most elegant solution, Google being in the middle ground and (now-depracated) Maemo the worst experience. While these being developer tools, the user is expected to have some technical knowledge. This is only a very superficial evaluation, just from the perspective of a web developer who wanted to test how his page renders on these platforms. Speaking of the devices, personally I cannot justify their price to their capabilities just yet, but it is nice to evaluate them by their SDKs. Maemo/MeeGo has the Debian sex appeal for the better and the worse. I feel that Nexus One won't be the first really big model even being technologically superior to the iPhone..

March 18, 2010

Python Qt4 soup - quick way to cook up a cross-platform GUI

A friend of mine asked me to do a very quick and dirty throwaway application that parses data off a standard HTML file into an Excel spreadsheet. I chose to do it in Python and spent about an hour to decide which backend libraries to use, and opted for BeautifulSoup and xlwt to do the hard work. The core app – one function to parse input and another function to create the Excel workbook – was written in under two hours. It was only running from the command line – so I cooked him up a GUI:


This friend is a Windows user who dislikes Linux and Mac, and he needs a clean GUI that is dead simple to run and operate. After one sleepless night of furious hacking, he got one. In a Windows exe. Developed on OSX, also running under Linux. This is the power of Python combined with Qt4. The sources are online for those interested to see the details. To emphasize the throwoff nature of the app, all labels are hard-coded to Finnish.

I already had some experience on PyQt4. If you're reading this and decide to look it up, do not pass PySide – it is a newer Python Qt4 bindings library endorsed by Nokia et al. Either one you choose, you can't pass QtDesigner, which is a nice tool to design the UI and comes bundled in the Qt developer packages. In this case all I did was to add a QTableView element with some margin for the tool and status bars.



QtDesigner creates an .ui file (XML) that is translated to Python via pyuic4. With a text editor (XCode) I wrote a subclass of the UI model that extends it with a toolbar with two functions and a status bar for messages. Using file dialogs is easy. The most tricky bit is to make the data from the input file visible in the table view. PyQt4 handles that by a custom subclass of QAbstractTableModel with specific methods the Qt4 API will call to update the table contents.

Quite simple until you get to signals, slots and threading, and it doesn't hurt too much to write and maintain. Overall, about 120 lines of code for the UI, 50 lines for the core.

Now all is left is to test and package it for Windows for my friend to run. I've gotten over my worst fear and loathing of Windows, but I still have a very uneasy feeling every time I boot the OS, even in VirtualBox. Text editor and the console are my friends, and cmd.exe does not quite meet my standards, even on Windows 7. Perhaps I'll look up TCC, but honestly I'd expect a better act on behalf of Microsoft. I had a better time with 4DOS 20 years ago running on MS-DOS 5. Oh, don't even get me started on "edit.com"..



    Si si, I'm a terminal snob.

Seems that py2exe is just what I need. It installed fine via easy_install into XP. Their wiki houses the information how to construct the setup.py for py2exe to build it. Mainly, name of the main script for the executable and which libraries to bundle. It creates a whole number of files, totalling 26 MB. I was unable to generate a single executable file, but this does the trick.


February 14, 2010

Introduction to Haskell and Erlang

I am reading Masterminds of Programming, by O'Reilly. It is a very good book I will get back to in other posts. Reading up on the thoughts of Haskell core language developers made me very interested in the language and functional programming in general. Haskell and Erlang are functional languages, so it takes a while to get into the mindset but seems like it's all worth the while.

I looked up some Haskell resources: http://www.haskell.org/haskellwiki/Haskell_in_5_steps

Then I saw the Haskell "Hello World"

Prelude> "Hello, World!"
"Hello, World!"
And immediately fell in love. Is there a simpler way to say "Hello, World!"?

This Haskell hacking video is also a great source of inspiration :)

There is also a nice guidebook called Learn You a Haskell for Great Good! with cutesy pictures and writing that takes you by the hand. Very nice. Reminds me of the Poignant Guide to Ruby. :) Erlang has got another one named Learn You Some Erlang for Great Good!, and you can't go wrong with one that has got a bearded squid on the cover =D

Erlang indeed looks to be a very nice language. This is an example from the book:

1> Weather = [
  {toronto, rain},
  {montreal, storms},
  {london, fog},
  {paris, sun},
  {boston, fog},
  {vancouver, snow}].

2> FoggyPlaces = [X || {X, fog} <- Weather].
[london,boston]

In fact this is even more compact syntax than in Ruby. Erlang atoms behave much in the same way as Ruby symbols. In which the equivalent would be something like:

>> weather = [
  {'Toronto' => :rain},
  {'Montreal' => :storms},
  {'London' => :fog},
  {'Paris' => :sun},
  {'Boston' => :fog},
  {'Vancouver' => :snow}]

>> foggy_places = weather.collect{|x| x.keys[0] if x.values[0]==:fog }.compact
=> ["London", "Boston"]
Or..
>> foggy_places = weather.select{|x| x.values[0]==:fog }.collect(&:keys).flatten
=> ["London", "Boston"]

Either case, I cannot think of a better way to say this in Ruby and Erlang has a cleaner way.

Joe Armstrong is one of the original Ericsson engineers who designed Erlang and wrote the first implementation. He describes using Erlang to feed data onto the page by web sockets. I am completely astonished. I need dig deeper into this...

January 29, 2010

EJB to Ruby proxying and a little bit on programming languages

This essay goes quite technical at times but the presented idea is pretty simple.

Java and the J2EE stack seem to be used in large-scale enterprise web solutions. Ruby is an odd hippie programming language that is not so often met (yet!) in this enterprise world. Ruby on Rails does not quite have the native equivalent in Java web development toolpack, and there never will be such. One key fundamental difference in Java and Ruby, is their measure of abstractness. Higher abstraction in some cases help to describe and solve the problem in just a few code blocks. Ruby lets you express ideas outside the micromanagement level - right from the beginning of your introduction to the language.

JRuby helps to break a major technical barrier, making possible to leverage the massive amount of Java libraries that now can be used through the Ruby interpreter. This is a description of a real world scenario where Ruby expression describes and solves the problem quite beautifully.

To understand what is happening next you need to know that an EJB is an Enterprise Java Bean. You can imagine that the Bean is an exporter in Brazil. The Java transaction layer RemoteMethodInvocation is built on top TCP/IP infrastructure. Regular citizen Java clients need to contact their national supplier.

It is quite a bureaucratic process. It takes some knowledge of Java to understand how each object should behave. EJB3 makes this a bit easier but to efficiently use the developer tools will take a long time to learn. This is an example of the Java enterprise business model where you develop on elaborate IDEs and large companies have lots of people doing object-oriented chunks of code. After having that much Java your dreams will become XML configurable for a while..

Ruby has a similar technique, DRb - Distributed Ruby. It is built into the Ruby core language. It provides a way to share data objects over the wire. As we will see, it takes only a few lines, perfectly writable even from the vi editor.

Now, let's inspect the supply chain.

bean_context =
      {
        'java.naming.factory.initial'      => 'com.evermind.server.rmi.RMIInitialContextFactory',
        'java.naming.security.principal'   => security_principal,
        'java.naming.security.credentials' => security_credentials,
        'java.naming.provider.url'         => provider_url
      }
This is the Brazil Bean exporter who has the brew I like. His contact is found by jndi_name from the @context. Home represents my national office who will be creating me this @stub guy who actually does the delivery of my order.
@context = InitialContext.new(bean_context)
  @home = @context.lookup(jndi_name)
  @stub = @home.create
EJB home is an object that implements the create() method for requesting the remote EJB and returning the stub object that acts as the EJB interface. It acts a bit like a database table model.

Let's say we want to know more of the company..

@stub.companyID
  => "430146"

  @stub.companyName
  => "Brazil Bean Provider"
Mmmh. So let's make an order..
@order = @stub.createOrder(uid,"Coffea arabica, 1 kg sample")
  => #<#:0x62ee558f @java_object=BBPServices stateless session>  
Ah! It returned an object! One important aspect of the dynamic nature of Ruby (and thus JRuby) is that the callable methods need not be specified beforehand. The method call to the Ruby object is dispatched to the EJB, the return value as well and if possible, JRuby casts it to Ruby native.
@order.items
  => ["Coffea arabica, 1 kg sample"]
This could be usable in Rails, but the architecture should allow dynamic translation of method calls so that the solution would have the most leverage and also to stay clean out of implementation details. Ruby has the sort of inner beauty that is capable of this.

method_missing() is a powerful Ruby kernel method, which has its counterpart in Python and Lisp, possibly other dynamic languages are growing into that direction too. Unless you are not familiar with method_missing(), I will explain it so that when Ruby calls a method on the EjbObject's instance (there is this @stub), and the Ruby object does not find the method, it calls method_missing, and here the call is redirected to the @stub, alas the EJB interface and along the network over TCP/IP. In other words, Ruby does not care what kind of EJB it gets.

This is the core of EjbObject class:

class EjbObject
    def context_environment
      {
        'java.naming.factory.initial'      => 'com.evermind.server.rmi.RMIInitialContextFactory',
        'java.naming.security.principal'   => security_principal,
        'java.naming.security.credentials' => security_credentials,
        'java.naming.provider.url'         => provider_url
      }
    end

    def initialize
      @context = InitialContext.new(context_environment)
      @home = @context.lookup(jndi_name)
      @stub = @home.create
    end

    def method_missing(method, *args, &block)
      @stub.send(method, *args, &block)
    end
  end
EjbObject is used by subclassing it:
class MyEJB < EjbObject
    set_provider_url 'rmi://....'
  end

And so, this essentially is the JRuby engine in ejb2rb. The solution wouldn't be complete without the Rails counterpart, ActiveEJB. Conceptually DRb and RMI are very similar.

Ok - even if the code didn't speak to you, think it this way - now JRuby can talk to the remote Java beans, call their methods, and use the return values as other objects that have methods. Some object classes are mapped directly to corresponding Ruby classes, such as Strings, Arrays and Fixnums. The Java-cast beatnik flower child JRuby can talk enterprise jargon. The EJBDispatcher::Instance enables him to open a channel to one EJB for other Ruby VMs that are equally valuable as Ruby implementations but have not developed their Java-fu.

This is how the EJB stub object is shared for DRb clients (the thread is started later by Hydra):

module EJBDispatcher
    class Instance

      self.config = {
        'hostname' => 'localhost',
        'port'     => '9876',
        'class'    => 'MyEJB'
      }

      def initialize
        @uri = "druby://%s:%s" % [config['hostname'],config['port']]
        DRb.start_service(@uri, Kernel.const_get(config['class']).new)
        return DRb.thread
      end
    end
  end
What happens on the Ruby end?
module ActiveEJB
    class Entity < DRb::DRbObject
      def initialize
        uri = 'druby://%s:%i' % [drb_server,drb_port]
        super(nil,uri)
      end
    end
  end
ActiveEJB::Entity is used by subclassing it:
class MyEJB < ActiveEJB::Entity
    include Singleton

    set_drb_server 'localhost'
    set_drb_port   '9876'
  end

So, in the end, you can use this in native MRI Ruby Rails:

profile = MyEJB.getUserProfile(@username, "PDS")

  contacts = MyEJB2.getCountryContacts(profile.getAddress().getCountry())
  
  contacts.each do |contact|
    ...
  end
Or something .. usually more or less involving enterprise business logic.. The actual implementation can handle multiple concurrent EJB instances.

But, in the end, if we look at the power and sheer expressfulness of Ruby, I think this is a fine example of it working to solve a real world problem. The source code is available to help rubyists with the leverage this technology may give them trying to survive under the serviance of J2EE.

Oh, yes, the order:

@order.delivery.weight
  => 1.108

  BrewerEJB.makeCoffee(
    @order.delivery,
    :espresso,
    :milk => true,
    :sugar => false)

January 23, 2010

Mathematical discourse on the web

Consider explaining mathematics on the web for someone. The tools to use are quite nifty. Algebra needs distinguishable symbols, geometry some other control methods. Even simple formulas are not so simple: I have a friend who recommended I should try to mix MathFlow with webMathematica and offered me a developer platform. This post is not so much as on the display of formulas as it is about my personal experience of mathematical expression on the web. Mathematica feels like a nice language. I had help for learning it from an experienced notebook / jsp writer, another friend. With his teaching I could quickly learn the essentials constructs of Mathematica's functional style to do simple experimentation and mix it in jsp. WebMathematica3 is a Java servlet package with the necessary backend kernel connectivity and a taglib for the developer. Worked out-of-the-box on OSX – Linux server requires 1 extra step for Xvnc. The taglib is quite usable, with three basic types of components: Manipulate is not quite as swift as it is in native Mathematica. The image does not follow the slider before the click is released. The network and server would be stressed for serving many concurrent users..

MathFlow is an input mechanism to essentially decrease the learning curve to input mathematical notation into the web. It is a Java applet that seems to work on all major OSes given that the moon is in correct orientation and system Java has not crucially changed. Icedtea6 Java plugin on Ubuntu worked too. MathFlow is proprietary software, this was an evaluation license. To be able to test it on a private server so that clients other than localhost can see it, was possible by recompiling the jar with a custom evaluation license. I could not find any open source alternatives, and this software seems capable of doing the job right now. Ideally, modern-day alternative could be written in JavaScript.. maybe compatible with MathJax..

The output formula is in MathML, and it can be captured to webMathematica input, in JavaScript, using for example the MooTools toolkit to do an XHR and update the page:

var mml = document.SampleSimpleEditorApplet.getMathML();
   params = 'fun='+URLescape(mml);
   var req = new Request.HTML({
     method: 'post',
     url: '_action.jsp?'+params,
     encoding: 'iso8859-15',
     onRequest: function() {},
     update: $('div.output'),
     onComplete: function(response) {}
   }).send();


This would send the contents of the editor applet to _action.jsp in request parameter fun, and the XHR would update the page. This _action.jsp in webMathematica could..
<%@ page contentType="text/xml"%>
<%@ taglib uri="http://www.wolfram.com/msp" prefix="msp" %>

<msp:evaluate>
  MSPFormat[
    MSPToExpression[$$fun],
    TraditionalForm
    ]
</msp:evaluate>
.. generate an image of the formula in traditional mathematical notation. Notice the $$fun variable, which is parsed by the webMathematica backend. In case where the formula has variables, it is possible to parse it in the "delayed" form:
SetDelayed[
    f[x_],
    Evaluate[
      MSPToExpression[
        $$fun,
        MathMLForm
      ]
    ]
  ];
Or plot it, notice the use of MSPBlock ..
MSPBlock[
    {$fun},
    MSPShow[
      Plot[
  
        $fun,
        {x, -2*Pi, 2*Pi},
        ImageSize->{600,500}

      ]]]

So you can build all sorts of elaborate user interfaces on top of these building blocks. This is just an example of plotting the user input formula. He shouldn't need to learn LaTeX or any other new notation to input the mathematics into the computer.

The mathematica magician can work with the Wolfram Workbench to write Mathematica code, and still work in a familiar notebook environment.

The web developer has a similar view to produce the 3D applet, where the MSPLive3D module creates the HTML code for the Java applet.
Needs["Talo3D`"];

...

  MSPLive3D[
    Graphics3D[
     {
      RGBColor[0, 0, 1],
      Thickness[0.005],
      Map[Line, house]
     },
     Boxed -> False
    ]
  ]

This is LiveGraphics3D generated by webMathematica3 on Safari. The house is rotatible. =) The original .nb was cc'd to me by Jyrki Kajala a few years ago. Quite recently he taught me about Mathematica packages, and .m files, and how the notebook can load the package. The package can now be subjected to functional testing and also included to webMathematica JSP. The tools have a certain good feel of dynamicity. Some issues, here and there, but it would be very interesting to build various use scenarios ..