Contacts Plug-In Update
Another beta version of the Address Book plug–in for RapidWeaver
Here is a new beta of the Contacts plug-in. This release simply corrects a problem running under 10.5 and extends the preview date. No work has been done to enhance the plug-in for use in RapidWeaver 4 or use the new API features.
'List view' will give more reliable results than 'Cards view' but please test thoroughly before using on a public site. Most of the 'display issues' still occur in Internet Explorer 6 and vary depending on the RapidWeaver theme used. Clicking on telephone numbers still gives unpredictable results in Internet Explorer.
Many of the issues with the first version still remain. Public Beta 2 will expire on 25th June 2009.
Safari Kiosk Tabs
An AppleScript to ensure that new Safari windows are opened in a tab
In the Watershed Café/Bar there are a number of ‘Slacker Tables’—public web browser kiosks set in glass tables. Oliver asked about a way to ensure that the instances of Safari running on the tables would open a new tab and not a new window. This is a simple AppleScript, to be saved as an Application and run, in a loop in the background.
This was only a quick, five minute hack but the results are surprisingly usable. Running the script as an application in the background doesn't appear to affect performance or stability over longer periods of time.
Note: this script has only been tested with Safari 3.2.1 in MacOS X 10.5.5
repeatdelay 1tell application "Safari"if the (count of documents) > 1 thenset theURL to the URL of the front documentif theURL is not "" thenclose front documenttell front window-- Safari appears to only let the front window create tabsset theTab to make new tabset current tab to theTabset the URL of theTab to theURLend tellend ifend ifend tellend repeat
Unfuddle
Initial personal responses from using web-based software and project management for a day.
Unfuddle.com provides issue tracking tickets, a documentation wiki, Subversion & Git repositories along with web-based interfaces to all these services. Whilst beginning a new personal iPhone project today, I wanted to test a hosted service to aid with small, non-public development projects.
Sign up was quick and simple; further profile information can be entered at a later date. Next, the user is given the option to create a new repository—a choice of Subversion or Git. I chose Subversion. The repository was available immediately both in local client applications and in the web-based browser. The connection was not the fastest, especially for a Sunday afternoon and I wonder how it will cope midweek. As mentioned, I writing a small, by their nature, iPhone application which shouldn't overwhelm any network connections.
The UI is OK. The interface, especially for tickets is not as nice as lighthouseapp.com which we use at Watershed. The web-based source code browser does support Objective-C highlighting. Which was nice.
An HTTP REST API is available with read & write access to most of the data. At Watershed, a Cocoa bundle uses the REST API to allow end users to send feedback from within the application directly into the Lighthouse database. Repository commit comments can be linked against individual bugs with a rudimentary text formatting pattern in the comment.
A dashboard widget is available for MacOS X. The widget is well designed and shows the latest messages, repository commits and project progress. Clearly designed and concise email reports can be received hourly, or at less frequent intervals. I found, as a solo developer, these two features less useful but small teams may benefit.
Unfuddle could save lots of time, configuring a server, providing network access & off-site backup services, adding repositories and writing Subversion hook scripts.
New Design
A new, easier to read, design for my personal site.
As much as I liked the last design for the site, it just didn't work anymore. The columns were too narrow: the code samples had to be edited to fit the narrow width and images had to be scaled fairly small to fit the narrow colum.
I designed and almost finished the RapidWeaver template almost year ago. Whilst adding recent posts with copious code examples, I saw that I really needed to get the new template used. I wanted the new design to be easy to read longer articles and typographically mature. I do think the site, is perhaps, not as distinctive, but is far less painful to read.
I'm using RapidWeaver 4.0 now, and have made a few small changes, partly due to new features in RapidWeaver, partly due to changes at Watershed and partly because I just haven't completed the template. Comments are back, iChat status and links are gone and so is Twitter messages.
Using RESTful WebObjects
Using requests with the WebObjects REST Handler for HTTP requests.
The previous post described building a new request handler allowing REST behaviour in WebObjects applications. This post will explain how the handler responds to specific requests. The current test application was built by simply, creating a new WebObjects application in Eclipse, adding a business objects framework and adding the REST handler. The test framework is the same framework that provides the foundation for www.watershed.co.uk so the examples will deal with films, screenings, seasons & festivals. The examples below use two Entities from the model, Exhibits and Programmes. Essentially, a Programme represents a season or festival of films. An Exhibit, generally, represents a film. There is a to-many relationship from Programme to Exhibits; i.e. A Programme contains a collection of Exhibits. This can be seen in use at the Watershed site. The reverse relationship is also modelled, so Exhibits have a to-one relationship to a Programme.

Rewriting
REST URLs describe the location of the resource in a hierarchy, so arguments are sent mimicking a directory structure: www.watershed.co.uk/cgi-bin/WebObjects/WatershedAPI.woa/REST/exhibits/1527 The request URLs are often written into executable code or synthesised from String components so keeping the URLs simple and short aids their use. URLs generated by the data modules are 'pretty' and incoming requests are rewritten in Apache so the previous URL becomes: api.watershed.co.uk/exhibits/1527.
Tools
Although not particular to our WebObjects REST Handler, using special tools can make testing much more efficient and pleasant. Whilst a web browser can be used to generate GET and POST requests they struggle with the other HTTP methods. Using CURL, from Terminal, can be useful, optionally adding headers to, or, setting the method or body of, the HTTP request. Typing man curl into Terminal will display a page explaining the options. RESTClient is a Java Swing application to test RESTful web services. It can be used to test variety of HTTP communications and has GUI options for most of the HTTP 1.1 specification, including setting the method, authentication values and the message body as well as viewing the response headers. RESTClient is a useful tool for any type of web application development.
REST URLs
URL Structure
Just as a WebObjects' component action URL has a structure, then URLs accepted by the REST Handler have a structure. Each action has a target and a method. The method is part of the HTTP specification and is explained later. The URL structure is essentially:
host/entity_name/primary_key/relationship/...
The entity name and the primary key together define the target. The entity names in the URLs follow a casual, but established, convention and are lower-case, underscore-separated and plural. So the URL /exhibits/1527 will map to an Exhibit Enterprise Object with a primary key value of 1527. Using the model described earlier, we can find the Programme with the primary key of 57 by using the URL: api.watershed.co.uk/programmes/57/ . As the entity name is underscore separated then to set the target for the MediaSet entity with a primary key of 1234, we can use the URL: api.watershed.co.uk/media_sets/1234.
The REST Handler uses the EOModels in the application, or linked frameworks, to map the URL to the correct Entity names and thence to a table in the database. The Handler then retreives the correct Enterprise Objects and delivers these targets to the appropriate data module for output in the response.
Relationships & Key paths
Objects in isolation are often of little practical use. Modelling real world environments requires a hierarchy of objects and containers. Creating a new season isn't useful unless we can insert films into it.
Once the target object has been established key paths defined in the model are then available. The relationship between Exhibit and Programme, illustrated in the diagram above, can now be used. To find the Programme of an Exhibit we use the URL: api.watershed.co.uk/exhibits/1527/programme/. We can follow the key path, defined in the EOModel, even further by using the to-many relationship back to an array of Exhibits: api.watershed.co.uk/exhibits/1527/programme/exhibits/. In a similar fashion to entity names, key paths in the model are renamed to maintain the pretty URLs. Although not illustrated, Exhibit has a to-many relationship to an array of MediaSets, named mediaSets. Using this relationship results in a URL similar to: api.watershed.co.uk/exhibits/1527/media_sets/.
Now that we have URLs that point to relationships, we can use these to add objects into and remove objects from collections.
Model Delegates
Comparing the illustration of the Entities, above, and the actual XML generated by a GET request displays an inconsistency. Although, attributes with null values are not currently listed, the attributes notes and history are never included. The REST Handler and the data modules use the model delegate to provide some information about how to render the data. To hide the notes and history attributes:
public NSArray excludeAttributesForEntity(String entityName) {// Always remove notes and history regardless of// the entityreturn new NSArray(new String[] {"notes", "history"});}
The use of the model delegate is optional and if the delegate is not set, the entity and attribute names will be taken from the model and adjusted.
Varying Data Representations
Outlined in the last post was a technique whereby changing the Accept HTTP header changed the response data format or allowed a client to send updated data to the web application in a variety of formats. Sending a request using cURL in Terminal shows the effect of the accept header.
curl -H 'Accept: text/xml' http://api.watershed.co.uk/exhibits/1530/
produces an XML response, whilst:
curl -H 'Accept: text/plist' http://api.watershed.co.uk/exhibits/1530/
produces a response in Apple's Property List format.
Actions
As the URL specifies the resource then the HTTP method specifies the action to perform on it. The REST Handler responds to the HTTP actions defined in the 1.1 specification. Four of the HTTP methods are mapped to the CRUD actions. The currently deployed application will not receive requests other than GET, HEAD & POST. The FreeBSD web server adapter, for watershed.co.uk applications, was compiled from the Apple source code and does not support the PUT & DELETE methods. The sites will be moving to a new virtual server in the next couple of weeks and we will compile a new adapter from the WebObjects 5.4 adapter source.
To read an Exhibit from the data store:
curl -X GET http://api.watershed.co.uk/exhibits/1530/
To delete an Exhibit from the data store:
curl -X DELETE http://api.watershed.co.uk/exhibits/1530/
To update the title attribute of an Exhibit in the data store:
curl -X PUT -d "
To create a new Exhibit in the data store:
curl -X POST -d "
By following the key paths in the URLs we can insert and remove objects from a relationship. This part is still very much a work in progress and is still not 'quite right'.
To insert a new Exhibit in the data store and add it to the array of exhibits in a programme, specify the array as the target resource:
curl -X POST -d "
http://api.watershed.co.uk/programmes/57/exhibits/
To remove an Exhibit from the to many relationship of a Programme but leave it in the data store:
curl -X DELETE http://api.watershed.co.uk/programmes/57/exhibits/1530/
Notes and Incomplete
As mentioned previously, the adapter installed on the web server does not handle PUT & DELETE requests but we will compile a new version that supports these methods shortly. Manipulating relationship collections feels 'clunky' at the moment and this needs some work. The data modules, both the XML and the Property List, need significant work. A JSON data module would be a welcome addition and would compliment the XML and Property List moduels.
One of the original reasons for REST Handler was to provide an efficient and quick data flow to a new persistence framework for Cocoa applications. We already have a framework that consumes large chunks of data from a database, we need to deliver data in a constant trickle just as it's needed. Large collections, and objects in a collection, should be available in a more terse output to reduce response times and bandwidth.
RESTful WebObjects
Development of a REST request handler for WebObjects applications.
As part of watershed.co.uk's ongoing development, we have a number of immediate requirements:
- Start building a replacement for the FBTFPersistentObjects framework used to provide object persistence and network database connectivity for Communicate.
- Vend XML and other data types using a technology with easier to implement clients than SOAP.
- Transfer data to iPhone applications efficiently using XML or binary plists.
- Do all the above across a number of different databases and business object frameworks.
REST web services vending XML & plist data appeared a good fit for all the above but we didn't want to have to write a whole collection of direct actions and components for each site. So we are in the process of building a REST Request Handler for new and existing applications. This is still a work in progress and is only, a day or two old, but hopefully in the near future there should be useable code alongside the explanation.
So how do we use our REST Request Handler in applications?
First we create a new instance of the handler and assign it to handle particular requests. We do this in Application's constructor in Application.java
RESTHandler restRequestHandler = new RESTHandler();restRequestHandler.setSecurityDelegate(new MyRESTSecurity());registerRequestHandler(restRequestHandler, RESTHandler.REQUEST_HANDLER_KEY);
We also set a security delegate here which is explained below. The request handler key is "REST" so any request sent to www.domain.com/cgi-bin/WebObjects/App.woa/REST/... will be processed by our handler.
Unfortunately, the REST Handler is not quite the simple, drop-in unit we would like. WebObjects will only create and initialise a WORequest with a HTTP GET, HEAD & POST method. Sending a HTTP DELETE or PUT action to a WebObjects application results in an exception. The solution is quite simple, subclass WORequest and return it from a re-implemented createRequest(...) in your Application class.
Security Delegate
The REST Handler can automatically provide public access to any database so as a precaution a security delegate must be implemented and added to the request handler before any requests can be accepted. The security delegate can be any object that implements the few methods declared in the security delegate interface, RESTSecurityDelegate.
public interface RESTSecurityDelegate {public int requiredHTTPAuthentication(WORequest aRequest);public boolean requireSecureTransport();public boolean shouldAllowRequest(WORequest aRequest);}
The security delegate methods are simple to implement and can be varied per request. There are some general REST authentication and authorisation practices in use and these leverage existing features of HTTP: with a secure HTTPS connection to prevent eavesdropping and use, either, HTTP Basic or Digest authentication, or, include a security token as a HTTP header key such as:
GET / HTTP/1.1
User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.watershed.co.uk
X-Watershed-Auth: U2FsdGVkX1zuHyuXFLEBoBjEHtQO.
Accept: */*
Currently only HTTP Basic support is implemented and we will add Digest at a later date. The REST Handler will first check if it is allowed receive requests over plain HTTP by calling requireSecureTransport(). If the return value is true then the handler will redirect the client to a secure connection. Next the REST Handler asks the delegate, using requiredHTTPAuthentication(), if the request will require authentication and, if the client hasn't provided any, will ask the client to provide authentication. Currently, only Basic HTTP authentication is supported. Finally, the delegate decides if the handler should process the request in the shouldAllowRequest(WORequest aRequest) method.
The listing below illustrates a possible implementation of the required methods.
public class SecurityDelegate implements RESTSecurityDelegate {public int requiredHTTPAuthentication(WORequest aRequest) {// Give everyone read-only accessif (aRequest.method().equals("GET")) return SecurityDelegate.AUTHENTICATE_NONE;return SecurityDelegate.AUTHENTICATE_BASIC;}public boolean requireSecureTransport() {return false;}public boolean shouldAllowRequest(WORequest aRequest) {// Give everyone read-only accessif (aRequest.method().equals("GET")) return true;// A user must be authenticated to change resourcesif (aRequest.headerForKey("authorization") != null) {String encodedAuth = aRequest.headerForKey("authorization");String decodedAuth = null;BASE64Decoder decoder = new BASE64Decoder();//encoded string starts after "Basic "encodedAuth=encodedAuth.substring(encodedAuth.indexOf(" ")+1);try{decodedAuth=new String(decoder.decodeBuffer((new ByteArrayInputStream (encodedAuth.getBytes()))));} catch(IOException ex) {}String tokens[] = decodedAuth.split(":");if(tokens.length != 2) return false;// Check the username & passwordif (tokens[0].equals("benjamin") && tokens[1].equals("mypassword")) return true;}return false;}}
Data Modules
Once the security delegate has approved the request, a data module is then invoked with the task of handling of the request. Using a number of installed data modules allow the application to vend REST web services in any number of different data formats. Data modules render Enterprise Objects out in a response and update, or insert, EO's from parsed data in a request. The data module also deletes EO's from the store for some requests. Each data module implements an interface based on HTTP actions and the REST Handler invokes a method passing the original WORequest and the target EO, or array of EO's.
Currently, the REST Handler selects an appropriate data module based on the Accept HTTP header sent in the request by the client. It was assumed that clients would have fine grained control over the request and the client would not always be a browser window so the REST Handler treats each acceptable MIME Type as HTTP 1.1 would, MIME Types are listed in order of preference: most desirable first. There is one exception to the HTTP 1.1 specification; the relative quality parameter is currently ignored. If a header such as Accept: image/jpeg, video/mp4, text/xml;q=0.9, text/json, text/plist, */* were received then the request handler would look first for a module that handled JPEG images and so on.
Each available data module is mapped to one or more MIME types. Currently we are just using a particular XML data module for testing and in the process of writing a plist module for use with Cocoa desktop and iPhone clients. A module is not required to support all four actions create, read update, and delete. Extra modules, such as JSON or vCard can be written relatively quickly and registered with the REST handler before accepting requests. There currently isn't a way to request a particular data format based upon the 'file' suffix in the request.
restRequestHandler.registerDataModuleForMIMEType(new DataModuleXML(), "*/*");
Model Delegate
The REST Handler will pass the request and the target Enterprise Objects to the data module but on some occasions we may not want make every entity available to the public. We may also want to make some attributes, such as private internal notes, unavailable. When a request needs to render 10,000 objects to XML we may not need to include every attribute, just an essential subset. An optional model delegate provides the REST Handler and the data modules with answers and information they need to handle the requests efficiently. If the model delegate is not provided then the handler use the default model group.
public interface RESTModelDelegate {public boolean cacheModelAdjustments();public boolean includeEntity(WORequest aRequest, String entityName);public NSArray removeAttributesForEntity(String entityName);public NSArray includeExtraAttributesForEntity(String entityName);public NSArray attributesForCollection(String entityName);public NSArray attributesForObject(String entityName);public String aliasForEntity(String entityName);public String labelForEntity(String entityName);public String labelForEntities(String entityName);public String labelForProperty(String propertyName);}
To be Implemented…
The REST Handler has only had a handful of train journeys of development so there are plenty of rough edges. It's currently difficult to adjust or influence the target enterprise objects provided to the data modules which would be useful for situations where we may not want to include all the available films, just the ones screening this month. There is no HTTP Digest authentication and the ability to specify query strings, such as commence<2007-05-29 or isPublic=true, in the URL or a request header would be very useful. There is still plenty of work to be done until the REST Handler is complete but it will be a valuable tool as we make architectural, storage and communication changes in watershed.co.uk and dshed.net.
New Beta of Contacts Plug-in
Here is a new beta of the Contacts plug-in. There have been fixes since the last version, primarily with Internet Explorer display issues. It is now possible to use the plug-in in production sites depending on the options selected.
'List view' will give more reliable results than 'Cards view' but please test thoroughly before using on a public site. Most of the 'display issues' occur in Internet Explorer 6 and vary depending on the RapidWeaver theme used. They are also some CSS & Javascript problems with Safari 3 that have yet to be resolved.
Many of the issues with the last version still remain. Public Beta 2 will expire on 25th September 2007.
iPhone SDK
As disappointing as the lack of an iPhone Cocoa SDK was at last week's WWDC and the 'controversial' techniques developers are encouraged to use to write applications for the iPhone, there is an application that appears to substantiate Apple's suggestions.
OneTrip is a shopping list web application specifically styled and built for the iPhone. It uses UI metaphors from the iPhone's applications, is styled sympathetically and is designed with features such as drag-scrolling and screen resolution in mind. Data storage happens on the server with cookies managing session identification so there is no user registration or login.
If all iPhone web applications are like this then maybe the current lack of a SDK it isn't such a disaster.
Contacts Plug-in
There's been (some) progress on the RapidWeaver plug-in that allows users to create a page using cards from Address Book.
I've been pretty busy working on the Church Hall conversion in my spare time so development of this plug-in has been painfully slow and I'm still far from finished but I thought I would release an early Beta for anybody that is interested in seeing progress.
Warning: this plugin is not for production use. It may look fine when previewed in RapidWeaver but things go downhill when the pages are exported and things get even worse when viewed in Internet Explorer.
Some notable omissions include (but not limited to):
- No support for mobile telephone numbers
- Incorrect handling of PNGs, Javascript and CSS in Internet Explorer
- No visual feedback of 'Large Type' telephone numbers; just click them
- Incomplete Microformat support
- Dragging multiple cards, or groups, from the list or in from Address Book results in a single card being added
- Broken pagination links in List view
- Miscalculated pagination when some contacts are disabled in the list
- The Appearance tab in Setup does nothing. Still not sure how I will handle changing the appearance (If at all).
and so it goes on… Anyway, if you haven't been put off and are still interested the download and have a look.
Where is the QuickTime I knew?
Interactive QuickTime is pretty much dead and the QuickTime (.mov) file format is dying.
A short while ago, whilst working on Electric December, I spent some more time working on server generated, interactive video presentation. This was an exact repeat of the previous year's Electric December development and once again I abandoned the work after a couple of weeks.
Yes, I know this type of thing can be done easily enough in Flash but I wanted to do it in QuickTime for a few reasons:
- We want to keep the video content in MPEG4 data files because we don't want to encode very large amounts of our content in a proprietry codec.
- In my opinion, the pereformance of MPEG4 is better than the Flash On2 codec.
- We need to distribute our content in a format that can be played on mobile devices such as iPods, PSPs and of course the iPhone.
In the Applesphere it appears that the QuickTime file format has been beaten down by MPEG4. The Apple TV doesn't appear to support QuickTime files and neither does the iPod. If the ipod doesn't support quicktime files then it is very unlikely the iPhone will. Interactivity and the other advanced features of MPEG4 are not supported in QuickTime and MPEG4 is seen as a linear audio and video format.
Interactivity in QuickTime has been neglected and become an embarrassment for Apple. QuickTime VR is hardly mentioned these days. Have you tried opening a .m4a file with chapter artwork and links in QuickTime Player? How ugly is the result? The chapter implementation in the QuickTime UI is neat and tidy but why draw text using Times, in 'link blue' across the artwork?
Apple recently disabled QuickTime's default ability to play Flash movies embedded inside a movie. Security issues with the existing code led Apple to turn this option off, without warning content producers this was going to happen. Flash playback in QuickTime couldn't have been a high priority. The user can re-activate the functionality, but the settings are hidden many levels down in nested preference panels, and not something you would expect an end user to perform in order to view content.
QuickTime's XML parsing and resource loading performance makes using techniques such as SMIL or wired, umbrella movies very difficult to deploy. How long can it take to display a few, small, simple media resources such as text and still images?
There are so many differences between the implementations of features, that haven't changed, in versions 6 and 7 of QuickTime? A content developer can't distinguish if something is working or not. Well, apart from text rendering which is not great in either. Why does QuickTime 7 install two different versions of the plug-in? One, a Coca/QTKit Webkit plug-in used by Safari, and the other, a Netscape style plug-in that all the other Macintosh browsers use. It would be quite nice if these two plug-ins displayed content and behaved identically. Based on content from Apple's own site, the Quicktime plug-in is used simply to provide basic, interactive playback of linear MPEG4 video controlled by Javascript. The interface is provided by the surrounding HTML.
It appears all the graphics & media engineers at Apple are working on technologies such as such as CoreAnimation, CoreVideo & Quartz Composer. All the advanced media technology work is done on the Mac OS X platform and not QuickTime! This is all great stuff and I love working in CoreVideo and CoreAnimation. They produce spectacular results and I amaze myself every time I click Build & Run. This is nothing we can deploy effectively to the public at large, for which the MS Windows using majority, the QuickTime plug-in is the only bridge there is. QuickTime will become a media generation framework to support Apple's pro & iLife applications, along with third party developer's desktop applications. The browser plug-in will just handle playback of MPEG4 data.
I guess what I'm trying to say is don't waste any more time trying to use QuickTime for interactive work, use it for what it's best at: playback of MPEG4 content and leave the interactivity & interface to Javascript & HTML.