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 IMAP 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.
Security is hard - Jim Fulton
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 dispatcherXML-RPC dispatcherWebDAV 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 more
Feedback: should go to Helge.
Written by
Helge Heß