XIRUSS Design Principles

This document discusses the basic design principles of the XIRUSS system.

HTTP Server Approach and URL Syntax

The SnapCM model is completely generic and the XIRUSS-T repository therefore can be exposed for access in any number of ways. It imposes no constraints or requirements on how you make it visible to clients and humans.

The XIRUSS-T system as delivered includes a simple HTTP server that provides read/write access to the repository using normal HTTP PUT, GET, and POST operations. This is only one of an infinite number of possible ways to expose a XIRUSS-T repository.

This URL scheme is implemented by the org.xiruss.http.jetty.XirussResource class.

The URL schema I've used for this HTTP server attempts to reflect the principles in Hans Reiser's paper on file system naming schemes, available here: http://www.namesys.com/whitepaper.html. If I understand the principle, the basic idea is that everything, including operations that are queries, should be expressed using the same simple naming syntax. That is, everything is a name all names have same syntax. In any case, my URL syntax started as at least an experiement with these principles. In particular, it means that rather than having URL that are really function calls or primarily collections of query parameters, it means that all the repository components can be addressed by normal-looking URLs where the name components are all nouns.

The exception to this is URLs that modify the state of the repository--these do include verb components (createBranch, createVersion), etc. because I couldn't see a way around it. In any case, this is all experimental and there's no particular magic or authority to this particular URL scheme. Nothing about SnapCM itself or the XIRUSS-T SnapCM constrains how you expose the repository in any way, so if you wanted to provide a completely different URL scheme for your repository, you are free to do so.

Note, however, that for a given repository instance, the URL scheme must be consistent as address rewriting on import needs to be able to use URLs that will be reliably resolvable against that repository instance.

HTTP GET URLs

The rules for XIRUSS-T GET URLs are as follows:

  • Branches and resources have top-level directories:
    • /branches
    • /resources
  • The top-level directory /debug provides direct access to all the details of all the repository objects, rather than a "user" or client view. For example, in this view you can see all the properties of a resource, rather than the version of the resouce currently visible.
  • Under "/branches" the URL pattern is
    branchId/snapshotId
  • Under snapshots you can navigate to versions by either giving a resource ID and optional resolution policy as a query parameter or by giving a version ID:
    /branches/br_00000002/snap_00000002/res_00000002
                    /branches/br_00000002/snap_00000002/res_00000002?resolutionpolicy=onSnapshot
                    /branches/br_00000002/snap_00000002/ver_00000001
  • Versions that are Organizer versions are reflected as normal hierarchical URLs as a sequence of version IDs:
    /branches/br_00000002/snap_00000006/ver_00000014/ver_00000016/ver_00000015

HTTP PUT URLs

PUT requests are used to create new versions of resources. From the documentation in the code:

Note that all other objects are created by POST requests because they don't contain data directly nor do they have a notion of being versioned. By contrast, versions can contain data (and most version types exist in order to contain data) and therefore the semantic of "posting to a resource" is the best match for creating a new version of a resource. This also reflects the distinction made in the HTTP spec between POST being handled by one object that then changes the data state of the server, possibly by creating new objects, while PUT is applied directly to a resource in order to change that resource's state (i.e., to set its content).

Thus, while "posting to a resource" results in a new version object, conceptually the (SnapCM) resource is the union of its versions so, semantically, creating a new version of a resource is identical to creating a new version of a file where the new file has the same URL as the old file.

These URLs are constructed by the org.xiruss.http.client.XirussHttpClientHelper class.

The URL pattern is:

/branches/branchId/snapshotId/resourceId[?params]

The parameters are:

name
The version name (typically the original absolute file path for versions of files or directories, completely arbitrary for all other version types).
userid
The user object ID of the user who is creating this object. Note that XIRUSS-T has a notion of users but no authentication mechanism or security features (because it's a toy) so the user is just for documentation and tracking purposes.

If a user ID is not specified then a new user is created automatically.

Any other URL parameters will be set as properties on the version.

This operation creates the Version object and, if the version object is one that takes data (i.e., an instance of StorageObject), the client can write that data to the newly-created version (see the doPut() method in the XirussHttpClientHelper class).

Note that the snapshot involved must be a mutable snapshot ("sandbox"). It is not possible to create a new version on a snapshot that has been "fixed".

HTTP POST URLs

All repository modification except version creation is via POST requests.

As for PUT, all repository mutation operations take a userid parameter whose value is the object ID of a user.

Each repository mutation operation is identified by a keyword that is the last path component in the URL. Preceding the operation keyword are path components that address the context for operation (i.e., branch, snapshot, resource, etc.) as for GET URLs.

NOTE: The creation of these URLs and their posting is supported by the org.xiruss.http.client.XirussHttpClientHelper class. You can use this class from importer or interactive client code to handle the communication to the XIRUSS-T server.

The POST operation keywords and their parameters are:

commitSnapshot

Commits a mutable snapshot to the repository, making it the latest snapshot on the containing branch.

/branches/br000001/snap000001/commitSnapshot?userid=user001
name
The display name of the snapshot. Snapshot names are arbitrary but can be used like tags in CVS to give meaningful labels to sets of versions or a descriptive name to a mutable snapshot (i.e, "Eliot's sandbox").
createBranch

Creates a new branch in the repository.

/branches/createBranch?name=My%20Branch&userid=user001
name
The display name of the branch. Names should be distinct for each branch but it is not a SnapCM requirement.
createDependency

Creates a new dependency link between a version and a resource

/branches/br000001/snap000002/ver0000006/createDependency?name=doc_01.xml&userid=user001
/branches/br000001/snap000002/ver0000006/createDependency?
                resolutionpolicy=com.example.xiruss.policies.MyResoluitionPolicyClass&
                someparam=somevalue&
                name=doc_01.xml&
                userid=user001
resid
The object ID of the resource that is the target of the dependency.
resolutionpolicy
A version ID or the name of the resolution policy type, "onSnapshot" or an integrator-defined policy implemented through a custom policy handler. Specifying a version ID creates a SpecificVersionResolutionPolicy. If not specified, the policy type is "onSnapshot".
name
Optional. The display name of the dependency instance.

Any other parameters are taken as constructor parameters for the dependency instance.

createResource

Creates a new resource in the repository.

/createResource?name=doc_01.xml
/branches/createResource?name=doc_01.xml&userid=user001
name
The display name of the resource. Resource names are arbitrary and need not be unique in any particular scope.
createSnapshot

Creates a new mutable snapshot in the repository within a specified branch.

/branches/br000001/createSnapshot?name=Snap%201&userid=user001
name
The display name of the snapshot. Snapshot names are arbitrary but can be used like tags in CVS to give meaningful labels to sets of versions or a descriptive name to a mutable snapshot (i.e, "Eliot's sandbox").
createUser

Creates a new user in the repository.

/createUser?name=drmacro
name
The display user name of the user. Must be unique within the repository.

Any other parameters are set as properties of the user object.

setProperty

Sets a property on the repository object addressed by the URL.

/branches/br000001/snap000002/ver0000006/setProperty?userid=user001&propname=somepropname&propval=property%20value
userid
The user ID of the user setting the property
propname
The name of the property to create or set.
propval
The value to set the property to.

Here is a short Jython session that uses the XirussHttpClientHelper to create some things in the repository:

            C:\users\eliot\eclipse\workspace\xiruss-t\build>jython
            Jython 2.1 on java1.5.0_07 (JIT: null)
            Type "copyright", "credits" or "license" for more information.
            >>> from org.xiruss.http.client import XirussHttpClientHelper
            >>> helper = XirussHttpClientHelper("localhost", 9090)
            >>> cn = helper.createNewUser("drmacro")
            >>> cn.getResponseMessage()
            'user_11'
            >>> cn = helper.createNewBranch("test")
            >>> cn.getResponseMessage()
            'br_00000004'
            >>> cn = helper.createNewSnapshot('br_00000003', "snap_00000007", "user_11")
            >>> cn.getResponseMessage()
            'snap_00000007'
            >>> cn = helper.commitSnapshot('br_00000003', "snap_00000007", "user_11", "")
            >>> cn.getResponseMessage()
            'Snapshot committed to branch br_00000003'
            >>>