SOPE stands for "SKYRiX Object Publishing Environment" and is a framework for
mapping dynamic objects into the HTTP infrastructure, that is, the web. A lot of ideas in SOPE are actually inspired by ZOPE (the Z Object Publishing Environment), but the implementation and details are quite different.
Simplified, SOPE is a framework for writing clever web servers driven by objects.
Mapping URLs to Objects
The core idea behind SOPE is addressing objects using URLs and in turn making them available using HTTP based protocols. For example to address an appointment in the SKYRiX web groupware you could use an address like:
http://skyrix/legolas/appointments/23984
This URL could specify the appointment 23984 of user legolas. Of course objects do not need to map to a "physical" object like a database record for the appointment, but rather can be queries. For example to query a range of appointments we could use the URL
http://skyrix/legolas/appointments/week/10
The "week/10" would actually refer to an object representing the fetch specification for the "10th week of the current year". Operations performed on this object, like "delete" or "fetch" would operate on the whole set of objects matching the query.
Obviously mapping URLs to objects works best when used on hierachical stores of objects, for example it's very easy to map LDAP records into the web:
http://ldap.skyrix.com/dc=com/dc=skyrix/cn=users/uid=legolas
or IMAP4 messages:
http://imap.skyrix.com/legolas/INBOX/Sent/512.eml
But as shown in the initial examples, this is not a requirement - path hierarchy often can be usefully mapped to queries.
Performing Operations on Objects
After we have located a dynamic object using a URL, we need to perform some kind of information on them, otherwise they would be pretty useless. There is a variety of possible operations available, like:
- HTTP and WebDAV Methods
- Method Objects bound to URLs
- XML-RPC Methods
The most common method that is used is certainly the HTTP GET method. The GET method is invoked if we want to fetch a representation of the object. For example this method is used by a usual webbrowser to fetch a HTML representation of the resource corresponding to the URL entered by the user. Besides the fixed methods defined by HTTP and WebDAV, like GET, PUT, PROPFIND and DELETE, we can get full flexibility in method declaration by mapping own methods to either URLs or to XML-RPC methods. For example by using such an URL:
http://skyrix/legolas/appointments/new
we could map the "new" path component to a method object (or more exactly: method invocation object) in the "appointments" collection object. If SOPE detects that a method during object location by URL, it (usually) calls the method and returns the result of the method instead of the method itself. Notably a method does not need to be an Objective-C method, it can be any kind of object which conforms to a certain protocol.
So, SOPE is also about generalizing all the different ways of invoking a method using the web.
What are SOPE Objects ?
Of course SOPE is not intended to map any object contained in the application to the web. While it certainly has the capability to do so (be using reflection and application internal object references), this would not be wise from neither a security perspective nor from a web API perspective.
Instead with SOPE you usually map a "controller" object to an URL to have full control over security and method invocation. Often the controller will control access to a single model object, like in the appointment example (/legolas/appointments/12345) and sometimes the control will control access
to some virtual constructs, like a query (/legolas/appointments/week/10).
Since we are mapping objects to the web, we must follow the web's paradigm of representing objects. In HTTP the things located by an URL are called "resources". A resource usually has a typed serialization format (eg HTML) and with WebDAV, a set of associated properties.
SOPE has operations for accessing properties of a resource by asking the object mapped to it's URL and for serialization and deserialization of objects. Since we are not dealing with a closed system, but the web, SOPE also has modules for content negotiation (selecting the appropriate serializer for the client) and for mapping property names and types.
While SOPE gives all the flexibility if required, one usually maps "controllers" to URLs. Controllers control content and property access to the actual object representation and thereby define the "web API" of the object (how it looks in the web and what operations we can run on it).
And Security ?
Basically any web application needs at least some kind of security. More often, a very dynamic, complex and fine grained security is required. For example access to a resource may be granted based on ownership, based on the type of client program accessing the resource, based on the IP address of the client, based on the bank account, on the moon phase, etc.
To summarize: SOPE also provides infrastructure to implement fine grained and
dynamic access control to the objects published on the web.
You should never underestimate the complexities of security. When doing sloppy declarations you can quickly make a mistake and publish objects to people which should have no permission for access or restrict access for people which should have it.
That does not mean that you should not publish objects on the web, just that you put some amount of thought into how you secure the system. Per default almost any access in SOPE is restricted, so a common error is not to open something for the web.
Protect your objects and properly test the object protections !
Processing Stages
When a web request is received, the requests run through a set of stages in SOPE before the response is going to be delivered:
- locate the object
- select the call dispatcher
- dispatch the call
- select a renderer for the result
- render the result
All of the stages are explained in more detail below.
How is an Object mapped to a URL ?
If a web request arives at the SOPE application server, the SOPE server extracts the path of the URL and looks up the object for processing based on that. The class responsible for doing the lookup is the SoObjectRequestHandler.
The request-handler first decodes the path into an array of path components. Each path component is the name of a child object, therefore the handler performs a series of lookups using the "-lookupName:inContext:acquire:" Objective-C method.
Example: The path
/legolas/appointments/12345
... is turned into an array of names ...
- "legolas"
- "appointments"
- "12345"
... which are looked up in sequence starting at the application object:
UserController= [Application lookupName:@"legolas" ...];
AptsController= [UserController lookupName:@"appointments" ..."];
AptController = [AptsController lookupName:@"12345"];
Since the lookup itself is in the full responsibility of the controller, the controller can do whatever it likes. For example it can return an intermediate cache object.
So why are we usually mapping to controllers instead of model objects ? One reason is that still some mapping needs to be done. For example the AptsController for managing a set of appointments needs to transform the string ID "12345" into a valid global-id/primary-key usable for lookup in the backend database.
In the simplest form this could look like:
- (id)lookupName:(NSString *)_name ... { int primaryKey = [_name intValue]; id modelObject = [database fetchObject:primaryKey]; return [self controllerForModelObject:modelObject]; }
Of course a real implementation would also include error handling (invalid IDs could be passed to the object), some caching and, of course, security checks. Speaking of security, notably any "name" is run through the security manager before and after it is looked up in the object, so you can decouple lookup code from security code to at least some degree.
TODO: Say something about :method and ?Cmd special methods.
What is a Dispatcher ?
The dispatcher is used to abstract the different protocols for invoking a method. Currently there are three different dispatchers:
- UI dispatcher
- XML-RPC dispatcher
- WebDAV dispatcher
All of them are different ways to invoke SOPE methods on objects. For example WebDAV carries the method to be called in the HTTP method while XML-RPC carries the method name in the content of the HTTP request.
SOPE bases the decision which dispatcher to use on several aspects, like the HTTP user-agent, the request content type and content, the HTTP method, etc.
So, dispatchers disassemble a HTTP request into a SOPE method call.
Serializing an Object to the Web
An object is just a binary representation of some data associated with methods in the RAM of the host computer. So if we want to transfer the object to another host or process, we need to serialize that object. Most often we are going to serialize to HTML for display in web browsers, but we can also serialize to other formats, for example to iCalendar files if an iCalendar applications is accessing our objects.
Because there are so many different applications waiting to access our application, we need to have at least some support for content-negotiation, that is, the capability to find out what serializer we are going to use for a given client application.
In the default SOPE setup, we have serializers for web browsers, for XML-RPC clients and for WebDAV browsers. Even among those broad categories you will often need to check the HTTP user-agent to further personalize the serialization to the client application.
Todo: write even moreFeedback: should go to Helge.