RF Extensions


As illustrated in RF Concepts, the following types of RF extensions can be plugged into the RF:

As all these extensions dwell inside the RF, these different extension types will be described in more detail.



Table of Contents

Repository Managers

Implementation Considerations

Some Rules of Thumb

Example Scenario

Mass Calls

Components of a Repository Manager

Mapping RF Objects to Backend Objects

RIDs, Lookup, and Resource Context

RIDs and Lookup

Resource Access Context

Namespace Manager

Content Manager

Property Manager

Advanced Aspects

Ordered Collections

Collators

Property Queries

Unique ID

Security

Locking

Events

Expert Aspects

Type Handling

Versioning

Repository Filters and Filter Managers

Namespace Filter

Property Filter

Content Filter

Repository Service

Global Service


Repository Manger

Since the new API is used to build a repository manager, all packages described in this section are located beneath com.sap.netweaver.bc.rf. To shorten references to the classes and interfaces, com.sap.netweaver.bc.rfis omitted from the package names.



Implementation Considerations

As explained in RF Concepts, a repository manager is responsible for the mapping of backend objects and their operations to the RF's unified resources and their aspects.

One of the tasks involved in implementing a repository manager is designing this mapping. This involves the following issues:

Designing the backend implementation constitutes another task. How this is done depends very much on the specific requirements of the backend system. Things to consider are:

The following sections give hints on designing the mapping. It does not cover how to design the backend connection, since this is probably too specific to each backend system.




Some Rules of Thumb

These "rules of thumb" should only be considered as guidance on how to design a repository manager. These are no "hard rules" that have to be adhered to in every repository manager design:

*) Might not be an issue if no end user navigation is required and only search and query-based taxonomies have to be supported.




Example Scenario

To illustrate some of the design issues, consider the following scenario:

A company's phone book is to be made available in SAP Enterprise Portal.

KM's flexible UI is to be used for navigation; KM's search and subscription features are to be used as well.

In order to achieve this, a repository manager for the phone book's objects has to be implemented.

The phone book contains users and groups. The user data contains name, office, phone number, mobile phone number, fax, e-mail, the user's manager and a picture of the user. The group data contains the list of users and groups that belong to the group (groups can contain other groups), an e-mail distribution list, and a responsible administrator for the group.

When designing the hierarchy, it seems obvious that groups - since they contain other groups and users - should be modeled as collections. Users are a little bit trickier: Modeling them as resources implies that all attributes of a user have to be modeled either as properties or content, preventing navigation for them. This might not be a problem if no further navigation is required, but if, for example, navigation to the user's manager is required, the user has to be modeled as a collection that contains a link to its manager (see "rule of thumb" for modeling relations between objects).

These issues show how the namespace design affects the data representation: If the user is modeled as a collection, its picture could then be modeled as a resource in a "user collection", while its attributes might be modeled as the collection's properties. This also adheres to the "rules of thumb" on stream data (for the picture) and on editable attributes (for the other attributes, for example, e-mail). If the user contains other data streams, for example, picture and curriculum vitae, the "user collection" would then contain two "virtual" resources, one for the "picture" data and one "vitae" resource, providing the curriculum vitae as its content.
It might also be possible to expose the attributes as XML elements in an XML file as another "virtual" resource inside the "user collection".

Following the "rule of thumb" for modeling relations, the user's manager is an example of how a link should be used: The manager is already represented in the hierarchy using its "primary" path (as a "normal" user); the link references it from somewhere else via a "second" path (as another user's manager).




Mass Calls

For the sake of a better overview, the mass calls have been ignored in the following descriptions of the repository manager and its components.

Nevertheless, implementing an interface requires that all interface methods be implemented. A repository manager should therefore do one of the following:




Components of a Repository Manager

As shown in RF Concepts, repository managers are registered with the RF's manager registry, which forwards the RF's requests to the manager.

A repository manager has to be derived from mi.AbstractManager. This base class handles the various interfaces that are required for interaction with the component runtime and handles registration with the manager registry.

The AbstractManager's methods startUpImpl() and shutDownImpl()can be overwritten by the repository manager if something has to be initialized on start-up (for example, if a connection to the backend system has to be established) or has to be de-initialized on shutdown (for example, when the connection has to be closed again).

In order to implement the functions for the various aspects of resources, the repository manager might contain several submanagers, one for each aspect of a resource. As explained in RF Concepts, all these aspects - and therefore the submanagers that implement the aspect's functionality - are optional.

The repository manager's submanagers have to be derived from mi.AbstractSubManager. As for the mi.AbstractManager, the mi.AbstractSubManager's methods startUpImpl() and shutDownImpl() can be overwritten by the submanager implementation, if initialization or de-initialization are required during start-up or shutdown.


The repository manager adheres to the following:

Each submanager adheres to the following:




Mapping RF Objects to Backend Objects

As explained in How to use the RF's Client API, applications deal with IResource and ICollection, which are the representation for RF objects from an application's point of view. They represent the unified aspects of the RF's objects.

A repository manager can implement an internal reference for the backend objects, which are passed to the RF as IResourceHandle implementations. The RF encapsulates these objects from the repository manager in its internal IResource (new API) implementations. The RF's IResource implementation acts as a proxy for the repository manager's IResourceHandle implementation (see below), preventing applications from using the repository-manager-specific implementations but enhancing the IResourceHandle (from which IResource is derived), with additional unified operations.

The common.IResourceHandle serves as an abstract pointer for linking the repository manager's internal representation of a backend object to the RF's unified representation. It provides just one method, getRid(), which returns the RID assigned to the RF object to which this handle refers. In the new API, the RID is represented as a common.IRid.

If a client requests a lookup for a given resource's RID, the RF determines the responsible repository manager and forwards the lookup request to this manager. The repository manager retrieves its internal representation of the resource from the backend and returns its implementation of IResourceHandle to the RF, which then references the manager's backend object.

The RF in turn wraps this handle into its implementation of an IResource and exposes its unified representation of the resource to the client.

When the client requests that an operation be performed on the IResource object, the RF determines the responsible repository's submanager for the relevant aspect, checks whether the submanager is available, and whether it supports the requested operation. If the submanager exists and supports the operation, the RF forwards the request to the submanager, passing the repository manager's implementation of IResourceHandle back again to the submanager. The submanager uses the reference from IResourceHandle to retrieve its internal backend object representation of the resource.

When designing the internal representation of an IResourceHandle, the following UML diagram might give an idea of how to manage the several RF objects internally:

The repository adheres to the following:

A sample implementation of a handle might look like the following:

public class MyHandle
implements IResourceHandle {

  private final MyNode node;
  private final IUser user;

  MyHandle(MyNode node,
          IUser user) {
    this.node = node;
    this.user = user;
  }

  public IRid getRid() {
    return this.node.getRid();
  }

  protected MyNode getNode() {
    return this.node;
  }

  protected IUser getUser() {
    return this.user;
  }

}

To indicate which operations are supported for a given object, the repository manager has to implement the getSupportedOptions() method, which receives an IResourceHandle as a parameter and returns a Set of supported options (see RF Concepts) for the type of object to which the handle refers. If the handle is null, getSupportedOptions() should return all options it supports.

  private final static HashSet collectionOptions;
  private final static HashSet resourceOptions;
  private final static HashSet allOptions;

  static {
    collectionOptions = new HashSet(5);
    collectionOptions.add(….common.property.SupportedOption.GET_PROPERTIES);
    collectionOptions.add(….common.property.SupportedOption.SET_PROPERTIES);
    collectionOptions.add(….common.namespace.SupportedOption.CREATE_RESOURCE);
    collectionOptions.add(….common.namespace.SupportedOption.CREATE_COLLECTION);
    collectionOptions.add(….namespace.SupportedOption.DELETE);
    resourceOptions = new HashSet(5);
    resourceOptions.add(….common.property.SupportedOption.GET_PROPERTIES);
    resourceOptions.add(….common.property.SupportedOption.SET_PROPERTIES);
    resourceOptions.add(….content.SupportedOption.GET_CONTENT);
    resourceOptions.add(….common.content.SupportedOption.UPDATE_CONTENT);
    resourceOptions.add(….common.lock.SupportedOption.LOCK);
    allOptions = new HashSet(collectionOptions);
    allOptions.addAll(resourceOptions);
  }

  public Set getSupportedOptions(IResourceHandle handle) {
    if( handle == null )
      return allOptions;
    }
    if( !(((MyHandle)handle).getNode() instanceof MyCollection) ) {
      return resourceOptions;
    }
    return collectionOptions;
  }

The example above indicates that all types of resources allow properties to be retrieved or to be set. Collections allow new resources and sub-collections to be created or existing resources to be deleted. Only resources that are not collections support locks and the retrieval of content as well as the updating of content.

The example assumes that MyCollection is derived from MyNode.




RIDs, Lookup, and Resource Context


RIDs and Lookup

As explained in RF Concepts, an RID consists of a path, a name, and a query. The repository should expose any restrictions that apply to the path and name of an RID using the IManager's getNameInfo() method. It returns a NameInfo object, which provides information on:

(see How to use the RF's Client API).

As the path's segments refer to the names of collections, the easiest way to implement the lookup() method is a recursive lookup of the parent collections for the requested resource up to the root resource. If the parent collection is found, the requested resource is retrieved as a child (with the name given by the RID) from the parent collection. null is returned if the resource does not exist:

  public IResourceHandle lookup(IRid rid)
                         throws ResourceException {
    try {
      IUser user = getUserFromContext(rid); // see Resource Access Context below
      if( rid == null ) {
        return null; // null is an invalid RID
      }
      if( rid.equals(this.root.getRid()) ) {
        // the given RID is the RID of the repository's root resource
        return new MyHandle(this.root, user);
      }
      // recursive lookup of the parent resource for the given RID
      IResourceHandle parentHandle = this.lookup(rid.parent());
      if( parentHandle == null ) {
        return null; // parent handle not found -> wasn't a valid RID
      }
      MyNode parentNode = ((MyHandle)parentHandle).getNode();
      if( parentNode == null ) {
        return null; // parent node not found -> wasn't a valid RID
      }
      if( !parentNode.isCollection() ) {
        return null; // parent is not a collection -> wasn't a valid RID
      }
      // parent found, requested the resource from the parent for the given name
      MyNode node = ((MyCollection)parentNode).getChild(rid.name().getPath());
      if( node == null ) {
        return null; // no resource exists for the given RID in parent collection
      }
      // the parent returned the resource with the given name
      return new MyHandle(node, user);
    }
    catch( Exception e ) {
      // ignore exception in this sample and treat them as if backend unavailable
      throw new ServiceNotAvailableException(rid, "backend unavailable");
    }
    return null;
  }

If the repository could not access the backend system, it should indicate this with an appropriate exception, not by returning null. Returning null should only be used to indicate that the resource definitively does not exist.

The example above assumes that the repository manager knows its root resource, which is assigned to the repository's prefix. This prefix is returned as a String by calling AbstractManager.getRidPrefix().

Another assumption is that MyCollection provides a getChild() method that returns the child resource specified by a given name from this collection.

The lookup() method is the only required method for a repository manager (all other operations for the various aspects are optional). It is the method used to implement the IResourceFactory's getResource() method by the RF.

However, this is not sufficient for navigation: As mentioned, navigation uses the parent-child relations that are provided through the INamespaceManager's getChildren() method (see RF Concepts).




Resource Access Context

The resource's access context (see RF Concepts) is handled differently in the new API than in the current API:

Before an operation is requested from the repository manager, the resource's access context is pushed to the current thread's context by the RF using a common.context.AccessContextFactory.

The repository manager retrieves the access context through AccessContextFactory.

This technique eliminates the resource's access context as an additional parameter on each call to the manager, making the signatures of the several methods a little less complicated.

This AccessContextFactory is derived from the util.context.AbstractIndependentThreadContextFactory, which handles the stacking of java.lang.Objects within the thread context. The AccessContextFactory extends it with type-safe methods for stacking common.context.IAccessContexts. The AccessContext class provides a default implementation for the IAccessContext.

The repository manager can use this technique to push another IAccessContext to the stack, before calling other methods.

The following example shows how an implementation of getUserFromContext() (as used in the lookup() example above) for retrieving the information from the access context might look:

protected IUser getUserFromContext(IRid rid)
                            throws ResourceException {
  try {
    IAccessContext context = AccessContextFactory.getInstance().getContext();
    return context.getUser();
  }
  catch( ContextUnavailableException e ) {
    trace.errorT("getUserFromContext()", "no context found");
    throw new ResourceException(rid, e);
  }
}




NamespaceManager

The mi.namespace.INamespaceManager interface defines the methods required for a read-only namespace submanager.

The first group of functions are for determining the type of a resource:

boolean isCollection(IResourceHandle handle)
              throws ResourceException;

Returns true if the given handle references a collection, false if not.

boolean isLink(IResourceHandle handle)
        throws ResourceException;

Returns true if the given handle references a link, false if not.

ILinkDescriptor getLinkDescriptor(IResourceHandle handle)
                           throws ResourceDescriptor;

Returns an ILinkDescriptor that holds the LinkType:

It also holds the target's RID (for internal and flexible links) or URI (for external links).

The second group of functions are used for retrieving the children of a resource:

List findResources(IResourceHandle rootHandle,
                   IFindResourcesDescriptor findDescriptor,
                   int offset,
                   int length,
                   Object resultStateDescriptor)
            throws ResourceException,
                   OperationNotSupportedException;

Returns a list of handles for the children of the given rootHandle that match the given findDescriptor. This variation of findResources() is used for navigation (to retrieve all the direct children of a collection).

Iterator findResources(IResourceHandle rootHandle,
                       IFindResourcesDescriptor findDescriptor, 
                       int startIndex, 
                       int fetchSize)
                throws ResourceException,
                       OperationNotSupportedException;

Returns an Iterator instead of a results list. This method is similar to findResources(), returning a List (see above). This variation of findResources() is used by the crawlers when a respository is indexed for searching. The namespace manager might simply return the List.iterator() or implement its own Iterator, which handles the Iterator's next() method more efficiently for the given result.

long countResources(IResourceHandle rootHandle, 
                    IFindResourcesDescriptor findDescriptor)
             throws ResourceException,
                    OperationNotSupportedException;

Returns the number of children for the given rootHandle that will be found when calling findResources() with the given findDescriptor.

The following example illustrates how the RF uses blocked retrieval:

IResourceHandle start = …;
IFindDescriptor descriptor = new BasicChildrenFindResourcesDescriptor();
Object state = null;
int blockSize = 10;
int blockIndex = 0;
int i;
while( true ) {
  // retrieve block
  List result = findResources(start, descriptor, blockIndex, blockSize, state);
  // handle the retrieved block of resources
  if( result.size() == 0 ) break; // no more resources found
  i = 0;
  Iterator iterator = result.iterator();
  while( iterator.hasNext() ) {
    System.out.println(
                      "position " + ((blockIndex * blockSize) + i)
                       + " is " + ((IResourceHandle)iterator.next()).getRid()
                      );
  }
  // goto next block, passing the result state for this block to the next
  blockIndex += blockSize;
  if( List instanceof IResultStateList ) {
    state = ((IResultStateList)list).getResultStateDescriptor();
  }
}

The following UML diagram shows the hierarchy of the various IFindResourcesDescriptors:

The other groups of functions are related to advanced topics and will be discussed later (see Advanced Aspects).

A sample implementation of findResources(), returning a List, might look like the following:

  public List findResources(IResourceHandle startHandle,
                            IFindResourcesDescriptor findDescriptor,
                            int offset,
                            int length,
                            Object resultStateDescriptor)
                     throws ResourceException,
                            OperationNotSupportedException {
    if(   ( findDescriptor instanceof IAdvancedChildrenFindResourcesDescriptor )
       || ( !(findDescriptor instanceof IBasicChildrenFindResourcesDescriptor) )
       || ( offset != 0 )
       || ( length != -1 )
       || ( resultStateDescriptor != null )
      ) {
      throw new OperationNotSupportedException(startHandle.getRid(),
                "only basic find descriptor is supported");
    }
    return ((MyCollection)((MyHandle)startHandle).getNode())
           .getChildren(getUserFromContext(startHandle.getRid()));
  }

The example above assumes, that MyCollection provides a getChildren() method that retrieves all direct child nodes of a collection.

The remaining namespace operations for creating, copying, renaming/moving, and deleting resources are defined by the mi.namespace.IMutableNamespaceManager interface.

These are:

IResourceHandle createResource(ICreateDescriptor createDescriptor)
                        throws ResourceException;

Creates a new resource, as specified by the createDescriptor. Returns the handle of the newly created resource. If the descriptor contained an ILockDescriptor when the method was called (to create a new resource and lock it in one atomic transaction), the descriptor contains an ILockInfo as the result of the atomic lock operation performed during the later creation process.

List deleteResource(IResourceHandle resourceHandle)
             throws ResourceException,
                    OperationNotCompletedException;

Deletes a resource.

Returns a list of handles for the deleted resource/s, where the first element of the list is the handle of the deleted resource specified by resourceHandle. Other entries exist if the resourceHandle specified a collection and the children of this collection were also deleted.

List copyResource(IResourceHandle resourceHandle,
                  ICopyDescriptor copyDescriptor)
           throws ResourceException,
                  OperationNotCompletedException;

Copies a resource, link, or collection to another location, as specified by the copyDescriptor.

Returns a list of handles for the copied resource/s, where the first element of the list is the handle of the copy for the resource that was given by resourceHandle. Other entries exist if the resourceHandle specified a collection and the collection's children also had to be copied.

List moveResource(IResourceHandle resourceHandle,
                  IMoveDescriptor moveDescriptor)
           throws ResourceException,
                  OperationNotCompletedException;

Moves or renames a resource as given by the moveDescriptor (see above).

Returns a list of handles for the moved resource/s, where the first element of the list is the handle of the moved resource that was specified by resourceHandle. Other entries exist if the resourceHandle specified a collection and the collection's children where also moved.

void setLinkDescriptor(IResourceHandle resourceHandle,
                       ILinkDescriptor linkDescriptor)
                  throws ResourceException

Changes the type or target of a link.




Content Manager

The methods for reading content are defined by mi.content.IContentManager:

IContent getContent(IResourceHandle handle)
             throws ResourceException;

Returns the content for a given resource, specified by handle, throws a common.content.ContentUnavailableException if the resource has no content assigned.

In the new API, content is represented by common.content.IContent with common.content.Content as the default implementation. A content submanager can either use the default implementation or implement its own implementation (which might also derive from Content). IContent provides the resource's content as an java.io.InputStream using getInputStream(); the content's metadata is provided using getContentMetadata().

The content's metadata bundles the information about the content (see RF Concepts) and is represented by common.content.IContentMetadata.

common.content.IMutableContent is used to update the content of a resource. The mi.content.IMutableContentManager handles the update of a resource's content:

void setContent(IResourceHandle handle,
                IMutableContent content,
                Boolean compareETags)
         throws ResourceException,
                ContentMetadataMismatchExpception;

Updates the resource's content with the mutable content given. The content's metadata is updated as well. If compareETags is true, the content is only updated if the resource's content has not been modified since the getContent() call that originally retrieved the content. In other words: if compareETag is true, content.getETag() must be equal to the manager's internal ETag for the resource's content. Otherwise, a common.content.ContentMetadataMismatchExcpetion is thrown.

Note: A repository manager that supports content and wants to support queries on content metadata as well has to expose the content metadata as properties. The repository manager must therefore implement a property submanager that handles the exposed content metadata properties.




Property Manager

Although the properties in the new API are built on the current API's properties (see How to use the RF's Client API), the implementation for property objects has changed a little:

While the current API offers only one implementation for all types of properties, the new API offers specific implementation for the several combinations of types with single-/multivalued properties.

These classes are located in common.property for the client applications. common.property.PropertyName defines the property names that are known to the RF (for example, CREATEDBY - see the table at the end of this section).

A property submanager uses property classes derived from those in common.property, located in mi.property. The mi.property property implementations provide additional constructors that allow for the attributes of the property to be set.

The mi.property.IPropertyManager handles the read-only property requests:

IProperty getProperty(IResourceHandle handle,
                      IPropertyName propertyName)
               throws ResourceException;

Returns the property for the given property's name and the specified resource or null if it does not exist.

Map getAllProperties(IResourceHandle handle)
              throws ResourceException;

Returns all the existing properties of the specified resource in a Map, indexed by the property names as key entries.

Map getListedProperties(IResourceHandle handle,
                        List propertyNameList)
                 throws ResourceException;

Returns existing properties specified in the propertyNameList for the given resource.

The result is a Map, where the properties are the entries and the keys are the property names.

The RF offers the mi.property.SystemPropertyFactory for helping system properties to be created more efficiently by the property submanager (see the table at the end of this section).

The corresponding write requests are defined by the mi.property.IMutablePropertyManager:

void updateProperty(IResourceHandle handle,
                    IPropertUpdateDescriptor propertyUpdateDescriptor)
             throws ResourceException;

Updates a resource property as specified by the propertyUpdateDescriptor.

void updateProperties(IResourceHandle handle,
                      List propertyUpdateDescriptorList)
               throws ResourceException;

Updates several properties as specified by the propertyUpdateDescriptors contained in the propertyUpdateDescriptorList.

A common.property.IPropertyUpdateDescriptor is either of type PropertyUpdateType.SET or PropertyUpdateType.REMOVE, implemented by one of the following classes:

When a client updates a property, it retrieves it, creates a copy by calling getMutable(), changes the property's value, and then passes the changed mutable property as a parameter to the updateProperty() method (see How to use the RF's Client API).

Because properties are also used for property queries (see Property-Queries), some system properties, which describe a resource and content, are predefined by the RF. The following table lists those system properties' names from common.property.PropertyName that a repository's property submanager should be able to provide:


property name

description
determined on or by

resource

content

properties

application

type

COLLECTION

true, if the resource is a collection
Resource creation

x

 

 

 

single boolean

CONTENTENCODING

Encoding of the content
Resource creation or content update

 

x

 

 

single String

CONTENTLANGUAGE

The language the content is for
Resource creation or content update

 

x

 

 

single String

CONTENTLENGTH

Content's length in bytes
Resource creation or content update

 

x

 

 

single long

CONTENTTYPE

MIME type of the content
Resource creation or content update

 

x

 

 

single String

CREATED

Resource's creation timestamp
Resource creation

x

 

 

 

single Date

CREATEDBY

User ID of the resource's creator
Resource creation

x

 

 

 

single String

DESCRIPTION

Description of the resource
Application

 

 

 

x

single String

DISPLAYNAME

Display name of the resource
Application

 

 

 

x

single String

ETAG

Entity tag for the content
Resource creation or content update

 

x

 

 

single String

HIDDEN

true, if the resource is hidden
Application

 

 

 

x

single boolean

LINKTYPE

Code of the resource's link type
rRsource creation or link update

x

 

 

 

single int

MODIFIED

Timestamp of the resource's last modification
Resource update or content/property update

+

x

x

 

single Date

MODIFIEDBY

User ID with the resource's last modifier
Resource update or content/property update

+

x

x

 

single String

READONLY

true, if the resource is read-only
Application

 

 

 

x

single boolean

RESOURCENAME

Name part of the resource's RID
Resource's RID

x

 

 

 

single String

RESOURCETYPE

Resource's type
Application

 

 

 

x

single String


As mentioned, the SystemPropertyFactory can be used to construct these properties.

The rows 3 through 6 in the table above indicate the "area" in which the property is used:

These "areas" only describe intended usage - any property submanager can provide these properties as immutable, using the backend's values for them instead.




Advanced Aspects


Ordered Collections

In order to support ordered collections, the IMutableNamespace submanager must support common.namespace.ICollectionCreateDescriptors and consider their common.namespace.OrderMechanism as well as handling the setCollectionOrderMechanism() method:

void setCollectionOrderMechanism(IResourceHandle handle,
                                 OrderMechanism orderMechanism)
                          throws ResourceExcpetion;

A submanager that does not support ordered collections only allows OrderMechanismType.NONE. A submanager that does support ordered collections has to support OrderMechanismType.MANUAL or OrderMechanismType.SERVER, or both (see RF Concepts).

If a submanager supports manual ordering, it must be able to handle the reorder method:

void reorderCollection(IResourceHandle handle,
                       List orderPositions)
                throws ResourceException;

Reorders the children of the collection, specified by the given handle, according to the common.namespace.OrderPositions in the List. (See How to use the RF's Client API for an example of how the feature is used with the current API.) Please note that the sequence of OrderPositions in the list is significant to the result of the reorder operation.

The new API's class OrderPosition contains all information on the current API's interfaces IPositioning and IPosition (see How to use the RF's Client API): The type specifies whether the position is either absolutely the first or the last element, or if it is a relative position before or after another resource. The method getResourceHandleName() gets the name part of the resource's RID that this position is for. For the relative positions BEFORE and AFTER, the method getReferencePointName() returns the name part of the resource's RID that the position relates to.




Collators

Since collators are not yet used by the client applications, they will be described here only briefly:

An ICollator extends the List interface and contains several ICollatorEntrys. Each ICollatorEntry specifies one ordering criteria: It is the name of the property to sort by and the flag to show whether sorting is ascending or descending.

In order to support collators, the INamespace submanager must support those common.namespace.IAdvancedChildrenFindResourceDescriptors that return a common.namespace.ICollator on getCollator().




Property Queries

In order to support property queries, the INamespace submanager has to return an implementation of common.namespace.IQueryBuilder.

The submanager must also observe IAdvancedChildrenFindResourcesDescriptors, which supply a query expression on getQueryExpression(). The submanager can choose to accept only its own IQueryExpression implementations, built from its specific IQueryBuilder implementation.

An example of how a client uses the query builder to construct a query is given in How to use the RF's Client API. The following example shows what the relevant methods of a query builder might look like:


   private IQueryExpression buildExpression(IPropertyName name
                                            String sqlOperand,
                                            String value) {
    if( name == null ) {
      return null; // name must be specified
    }
    if( PropertyName.CONTENTTYPE.equals(name) {
      return new MyQueryExpression("contenttype " + sqlOperand + " ?", value);
    }
    if( PropertyName.CREATEDBY.equals(name) {
      return new MyQueryExpression("author " + sqlOperand + " ?", value);
    }
    return null; // query builder doesn't handle the given property
  }

  public IQueryExpression like(IPropertyName name,
                               String value) {
    return buildExpression(name, "like", value);
  }

  public IQueryExpression eq(IPropertyName name,
                               String value) {
    return buildExpression(name, "=", value);
  }

The example above uses the following MyQueryExpression, which shows how a query expression might be implemented:

public class MyQueryExpression {

  private String sql;
  private List values;

  private MyQueryExpression(MyQueryExpression part1,
                            MyQueryExpression part2,
                            String sql) {
    this.sql = " (" + part1.sql + ") " + sql + " (" + part2.sql + ")";
    this.values = part1.values.clone();
    this.values.addAll(part2.values);
  }

  public MyQueryExpression(String sql;
                           String value) {
    this.sql = sql;
    this.values = new ArrayList();
    if( value == null ) {
      this.values.add("");
    } else {
      this.values.add(value);
    }
  }

  protected PreparedStatement getSqlStatement(Connection connection) {
    String sql = "SELECT rid FROM myTable WHERE " + this.sql;
    PreparedStatement statement = connection.prepareStatement(sql);
    for( int i = 0; i < this.values.size(); i++ ) {
      statement.setString(i, (String)this.values.get(i));
    }
    return statement;
  }

  public IQueryExpression and(IQueryExpression expression) {
    if( expression instanceof MyQueryExpression ) {
      return new MyQueryExprssion(this, (MyQueryExpression)expression, "and");
    }
    throw new OperationNotSupportedException();
  }

  public IQueryExpression or(IQueryExpression expression) {
    if( expression instanceof MyQueryExpression ) {
      return new MyQueryExprssion(this, (MyQueryExpression)expression, "or");
    }
    throw new OperationNotSupportedException();
  }

}

This query expression sample collects the WHERE clauses and the relevant parameter values. The namespace submanager uses getSqlStatement() to get the appropriate java.sql.Statement for a fictitious database table myTable in order to retrieve the RID's of the relevant resources.

This table might have been created as follows:

create table myTable (
  rid varchar(512) not null,
  contenttype varchar(128) not null,
  author varchar(128) not null,
  …
)

To support property queries on content metadata, the repository manager should be able to handle the appropriate property names in its query expressions (see Property-Queries).




Unique ID

Although unique IDs are provided by a global service, a repository's backend system can support unique IDs on its own.

Note: In the current API, unique IDs are only provided by a global service. See How to use the RF's Global Services for details. As a result, repository managers do not currently have to support this.

In order to expose a backend's unique IDs, a submanager that implements the mi.idmapper.IIdMapperManager interface has to be implemented. The submanager's interface specifies the following methods:

String getUniqueId(IResourceHandle handle)
            throws ResourceException;

Retrieves the mapped unique ID for a resource handle.

IResourceHandle lookup(String uniqueId)
                throws ResourceException;

Retrieves the resource handle for a mapped unique ID.

An ID mapping submanager that also implements the IMutableIdMapperManager interface has to offer the following methods:

void assignToResource(IResourceHandle handle,
                      String uniqueId)
               throws ResourceException;

This method assigns a mapping between the given resource handle and the specified unique ID and an existing resource.

void assignToRid(IRid rid,
                 String uniqueId)
          throws ResourceException;

Assigns a mapping between the given RID and the specified unique ID. The RID does not have to refer to an existing resource.

These methods should be implemented if a backend system is capable of assigning (foreign) unique IDs to a resource or an RID.




Security

A specific security manager implementation is not necessary if all security constraints are defined in their entirety in the backend system. The repository manager can then rely on the backend's security checks and only forward access violations as common.security.AccessDeniedExceptions to the RF.

However, an implementation might be required if:

These two issues are described in the following sections:



Using the ISecurityManager

When performing a security check, the repository's submanagers have to check whether the user requesting the operation has the appropriate permissions.

The RF's permissions are specified by the constants in common.security.Permission. They correspond to those mentioned in RF Concepts.

The following list specifies which checks have to be performed for which operation:

The remaining operations use the checks specified above:

Methods for the RF's aspects other than those listed above (especially lookup()) should not check for permissions.

A sample implementation of such a check() method might look like the following:


  private MyRepositoryManager manager = (MyManager)this
                                        .repositoryManager;
  private ISecurityManager securityManager = this.manager.getSecurityManager();
  public void checkGetContent(IResourceHandle handle)
                       throws AccessDeniedException,
                              ResourceException {
    IUser user = getUserFromContext(handle.getRid();
    if( !this.securityManager.isAllowed(handle, user, Permission.GET_CONTENT) ) {
      throw new AccessDeniedException(
                                     handle.getRid(),
                                     Permission.GET_CONTENT.getName(),
                                     user.getId()
                                     );
    }
  }

The example above uses the getUserFromContext() method as shown in Resource Access Context.



Implementing a security submanager

Because the KM commands that are used by the KM UI to perform locking require a security submanager, a repository manager might implement the mi.security.ISecurityManager submanager. It offers:

boolean isAllowed(IResourceHandle handle,
                  IPrincipal principal,
                  IAclPermission permission)
           throws ResourceException;

Checks whether the specified permission is granted to the given principal for the resource to which the handle refers.

To do this, the submanager has to map the backend's permissions to those of the RF (see above) and forward this check to the backend's security checking mechanism.

List getSupportedPermissions(IResourceHandle handle)
                      throws ResourceException;

Returns a List of all the IPermissions for the given resource (see RF Concepts and above for an overview of the RF permissions and the resource's object types used for permissions).

The RF offers mi.security.Permission as an implementation of IPermission.



Implementing a ACL submanager

If a repository manager wants to expose the backend's ACLs to allow them to be edited by the KM's ACL editor, note that the RF's permission are mapped again to only three permissions displayed in the KM's ACL editor:

In order to expose a backend's ACLs, a mi.security.acl.IAclSecurityManager has to be implemented. It offers the methods for retrieving, creating, and removing IAcls. The mechanism is very similar to the IResourceAclManager, as described in How to use the RF's Client API. The only differences are the names of the interfaces and their packages:

com.sap.netweaver.bc.rf.common.security: Permission and IPermission com.sap.netweaver.bc.rf.common.security.acl: IAclPermission, IAcl and IAclEntry com.sap.netweaver.bc.rf.mi.security: Permission com.sap.netweaver.bc.rf.mi.security.acl: IAclSecurityManager

The ACL submanager has to implement its own IAcl and IAclEntry implementations (for example, MyAcl and MyAclEntry), which are passed through the RF to the client and back again.

An IAclSecurityManager might therefore only accept its own ACL and ACE implementations, except for the assignAcl() method, which has to accept a (foreign) ACL.

An IAclSecuritManager should apply security constraints on ACLs itself: Usualy, only owners of an ACL are usually allowed to change it.




Locking

The ILockManager interface only contains this method:

List getLocks(IResourceHandle handle)
       throws ResourceException;

Returns the list of ILockInfos for the given resource.

In order to support locking as described in How to use the RF's Client API, the submanager must also implement the IMutableLockManager interface, which offers:

ILockInfo lock(IResourceHandle handle,
               ILockDescriptor lockDescriptor)
        throws ResourceException;

Locks the resource specified by the handle with a lock, as specified by the ILockDescriptor, for the current user.

ILockInfo refreshInfo(IResourceHandle handle,
                      String lockToken)
               throws ResourceException;

Refreshes a lock and resets the remaining time for a lock's timeout if such a timeout is set for the current user.

void unlock(IResourceHandle handle,
            String lockToken)
     throws ResourceException;

Unlocks the specified lock for the current user.

The RF provides a default implementation LockInfo for the ILockInfo interface in mi.lock, whereas the default implementation for ILockDescriptor is located in common.lock.

Note: Due to the current implementation of KM's locking command, which is used within the KM UI, locking requires a security submanager to be attached to the repository manager.




Events

Usually, a repository manager does not need to send events because the RF handles the appropriate events being generated. However, the RF is only able to handle this if resources are accessed through the RF.

If resources are manipulated in the backend without the RF being involved, the RF is not able to generate the corresponding events.

To avoid this, a repository manager can send events on its own to indicate changes made in the backend, using the mechanism as described in How to use the RF's Client API: The mi.AbstractManager implements an IResourceEventSender that is already registered at its own event broker. The event broker is returned by the AbstractManager's method getEventBroker().

To send an event, the repository manager implements the following:

  protected void sendEvent(IResourceEvent event) {
    this.getEventBroker().send(event, this);
  }

In addition, the event sender's methods getEvents() and getEvents(IResource) have to be overloaded to return the appropriate list of events supported by the repository manager.

Repository manager or submanagers should not implement an IResourceEventReceiver, because using a resource from a resource event might cause other events that might in the end trigger an avalanche of events.





Expert Aspects


Type Handling

The ISemanticObjectFactoryRegistry (see RF Concepts) offers a generic way for registering ISemanticObjectFactorys, which are able to "cast" an IResource onto a semantic object.

Since a repository usually already "knows" which semantic objects it provides, an easier and faster way of exposing these "casting" abilities would be nice - that is what the ITypeManager is for:

The mi.type.ITypeManager offers only two of the ISemanticObjectFactory's methods:

public boolean isA(IResourceHandle handle,
                   Class objectClass)
            throws ResourceException;

Checks whether the object identified by the given handle could be "casted" onto an object of the requested class.

public Object as(IResourceHandle handle,
                 Class objectClass)
          throws ResourceException;

Returns the object identified by the given handle as an instance of the requested class.

If a client calls an IResource's isA() or as() methods, the RF forwards this call to the resource's repository ITypeManager first. If the type handling submanager is not able to "cast" the object (or if doesn't exist), the call is then forwarded to the ISemanticObjectFactoryRegistry.

The mi.type.IMutableTypeManager is not yet used.




Versioning

As described in RF Concepts, the versioning in the RF comes in different flavors: Basic versioning and advanced versioning. Versioning offers options for workspaces, branches, labels, and versioned collections.

The interfaces for the versioning submanager are divided accordingly and are briefly described here. For further details, see the RF's JavaDoc.

Note: KM's versioning command as used by the KM's UI issues a lock request before it performs a checkout on a resource. If KM's UI is to be used to access the repositories versioning features, the IMutableLockManager must also be implemented (see Advanced Aspects).



Basic Versioning

The mi.version.IBasicVersioningManager interface specifies the methods required for a read-only basic versioning submanager. It provides methods to retrieve the (linear) version history of a versioned resource as well as the checked in and checked out version of a VCR:

boolean isVersionControlEnabled(IResourceHandle resourceHandle)
                         throws ResourceException

Returns true if the given resource handle refers to a VCR.

boolean isCheckedOut(IResourceHandle vcrHandle)
              throws ResourceException

Returns true if the given resource handle refers to a VCR in check-out state "checked out".

IResourceHandle getCheckedOutVersion(IResourceHandle vcrHandle)
                              throws ResourceException

Returns the handle for the resource from which the given checked out VCR has been checked out.

boolean isCheckedIn(IResourceHandle vcrHandle)
             throws ResourceException

Returns true if the given resource handle refers to a VCR in the check-out state "checked in".

IResourceHandle isCheckedInVersion(IResourceHandle vcrHandle)
                            throws ResourceException

Returns the handle for the resource the given checkedin VCR is based on.

List getVersionHistory(IResourceHandle resourceHandle)
                throws ResourceException

Returns the version history of the given resource if the resource handle refers to a VCR and if the version history is a linear version history. The list of versions in the version history is in ascending order, where the first element is the oldest version and the last element the newest.

Set getPredecessorVersions(IResourceHandle resourceHandle)
                    throws ResourceException

Retrieves the predecessor for the version given by the resource handle. With basic versioning, the Set contains only the single predecessor in the linear version history for this version.

Set getSuccessorVersions(IResourceHandle resourceHandle)
                  throws ResourceException

Retrieves the successor for the version given by the resource handle. With basic versioning, the Set contains only the single successor in the linear version history for this version.

The mi.version.IMutableBasicVersioningManager specifies the according methods for in-place check-out and check-in:

void setVersionControlEnabled(IResourceHandle resourceHandle,
                              boolean enabled)
                       throws ResourceException

Enables or disables version control on the resource referred to by the resource handle, according to the enabled parameter.

ICheckOutInfo checkOutInPlace(IResourceHandle vcrHandle,
                              boolean forkOk)
                       throws ResourceException

Performs an in-place check-out for the given VCR that changes the VCR's check-out state to "checked out". It optionally returns an object that implements the ICheckOutInfo if the server supports the expectedCheckInRid feature (see checkIn() below).

The forkOk parameter is only used with advanced versioning and should be ignored.

ICheckInInfo checkIn(IResourceHandle resourceHandle,
                     IRid expectedCheckInRid,
                     boolean forkOk,
                     boolean keepCheckedOut)
              throws ResourceException

Performs a check-in for a checked out VCR, creating a new version from the current version-controlled state of the VCR and returns an instance of ICheckInInfo. This ICheckOutInfo object contains the RID and the revision ID of the newly created version. If the keepCheckOut flag was set, it also contains the ICheckOutInfo from the implicit check-out operation (see below).

If the exepctedCheckInRid is set (is not null), the method must fail if the server cannot assign the given expectedCheckInRid to the new version, created from the VCR.

As with checkOutInPlace the forkOk parameter should be ignored with basic versioning.

The keepCheckOut parameter specifies whether the given VCR should be checked out again immediately after the check-in succeeds.

Set undoCheckOut(IResourceHandle vcrHandle)
          throws ResourceException

Rolls back a previous check-out operation. The specified VCR's version-controlled state (its content and dead properties) is updated from its checked out version first (the version that has the same version-controlled state as the VCR had when the check-out was requested). The VCR is then set to the check-out state "checked in".

Set updateFromVersion(IResourceHandle vcrHandle,
                      IResourceHandle versionHandle)
               throws ResourceException

Updates the given VCR's checked in version's version-controlled state from the version-controlled state of the given version (the VCR's content and properties are updated from the version specified). With basic versioning, it returns the updated VCR in the Set.




Advanced Versioning: Working Resources and Branches

The mi.version.IMutableAdvancedVersioningManager contains the method for creating a working resource:

ICheckOutInfo checkOutWorkingResource(IResourceHandle resourceHandle,
                                      boolean applyToVersion,
                                      boolean forkOk)
                               throws ResourceException

Creates a working resource for the given version.

If the resource handle refers to a VCR and applyToVersion is true, the version referred by the VCR's checked in version (see getCheckedInVersion() and updateFromVersion() above) is used. If it refers to a VCR but applyToVersion is false, the VCR is automatically updated from the newly created version at check-in (in addition to the behavior when applyToVersion is true).

If the forkOk parameter is true, subsequent forks in the version history are allowed at check-in (depending on getCheckOutForkBehaviour(), see below).

Using forkOk true enables forks or branches in the version history (see below).

Within the mi.version.IMutableBasicVersioningManager, the following methods also respect the forkOk parameter:

ICheckOutInfo checkOutInPlace(IResourceHandle vcrHandle,
                              boolean forkOk)
                       throws ResourceException

As with checkOutWorkingResource, if the forkOk parameter is true, subsequent forks in the version history are allowed at check-in (depending on getCheckOutForkBehaviour(), see below).

ICheckInInfo checkIn(IResourceHandle resourceHandle,
                     IRid expectedCheckInRid,
                     boolean forkOk,
                     boolean keepCheckedOut)
              throws ResourceException

If the forkOk parameter is true, a fork in the version history is allowed (depending on getCheckInForkBehaviour())

The methods for checking the server's fork behavior at check-out and check-in are specified in the mi.version.IAdvancedVersioningManager interface:

Set getCheckOutForkBehaviour(IResourceHandle versionHandle)
                      throws ResourceException

Returns as Set (for compatibility with WebDAV), which contains an IName object describing the fork behavior at check-out for the given version.

Set getCheckOutForkBehaviour(IResourceHandle versionHandle)
                      throws ResourceException

Returns as Set (for compatibility with WebDAV), which contains an IName object describing the fork behavior at check-in for the given version.

The IName is one of the constants defined in the current API's com.sapportals.wcm.repository.IVersionResource:

As advanced versioning allows forks, the semantics for the version history became more complex. The relevant methods in mi.version.IBasicVersioningManager might then return a Set, containing several handles instead of just one:

Set getPredecessorVersions(IResourceHandle resourceHandle)
                    throws ResourceException

With advanced versioning, this method retrieves the predecessors for the version given by the resource handle, even if it's not a linear version history. The Set contains all predecessors in the version history for this version.

Set getSuccessorVersions(IResourceHandle resourceHandle)
                  throws ResourceException

With advanced versioning, this method retrieves the successors for the version given by the resource handle, even if it's not a linear version history. The Set contains all successors in the version history for this version.

In addition, the mi.version.IAdvancedVersioningManager offers the following methods:

IResourceHandle getVersionHistoryResource(IResourceHandle versionHandle)
                                   throws ResourceException

Returns the version history of a version as a resource handle, instead of returning the version history as a list of versions, as the IBasicVersioningManger's getVersionHistory() does.

To traverse a version history resource, getPredecessorVersion() and getSuccessorVersion() are used. To retrieve the starting node, the following method is provided:

IResourceHandle getRootVersion(IResourceHandle versionHistoryHandle)
                        throws ResourceException

It returns the root version of the version history specified by the given version history resource handle. The version returned is the first version in the version history (without any predecessors).




Advanced Versioning: Labels

Another advanced versioning feature is label support. The interface mi.version.IAdvancedVersioningManager specifies the following methods:

Set getLabelSet(IResourceHandle versionHandle)
         throws ResourceException

Returns the Set of labels for the given version.

IResourceHandle getVersionResourceByLabel(IResourceHandle versionHistoryHandle,
                                          String label)
                                   throws ResourceException

Retrieves the version for the given label from the specified version history resource.

The corresponding methods for assigning and removing labels are specified in the mi.version.IMutableAdvancedVersionManager interface:

void addLabel(IResourceHandle versionHandle,
              String label)
       throws ResourceException

Tags the given version with the given label. If the label is already used as tag for another version in the same version history, the method fails.

void setLabel(IResourceHandle versionHandle,
              String label)
       throws ResourceException

Tags the given version with the given label. This method does not fail if the label is already used to tag another version in the same version history - it removes the label from the other version instead.

void removeLabel(IResourceHandle versionHandle,
                 String label)
          throws ResourceException

Removes the given label from the specified version if the label exists as a tag for the given version.




Advanced Versioning: Workspaces

The mi.version.IWorkspaceManager interface contains the methods for retrieving workspaces and checked out versions in a workspace:

Set getWorkspaceCollectionSet(IResourceHandle resourceHandle)
                       throws ResourceException

Returns a Set of collections in which workspaces can be created (see createWorkspace() below) for the given resource.

IResourceHandle getWorkspaceResource(IResourceHandle resourceHandle)
                              throws ResourceException

Returns the handle of the workspace the specified resource belongs to if it's a workspace-controlled resource (null, if not).

Set getCheckedOutResources(IResourceHandle workspaceHandle)
                    throws ResourceException

Returns the Set of all checked out VCRs belonging to the workspace as specified by the given handle.

The mi.version.IMutableWorkspaceManager then contains the methods for creating workspaces and version-controlled resources in a workspace:

IResourceHandle createWorkspace(IResourceHandle collectionHandle,
                                String name)
                         throws ResourceException

Creates a workspace in a collection (returned from getWorkspaceCollectionSet()) with the given name.

IResourceHandle createVersionControlledResource(IResourceHandle collectionHandle,
                                                IResourceHandle baseVersionHandle,
                                                 String name)
                                         throws ResourceException

Creates a new VCR, based on the specified base version in the collection referenced by collectionHandle, for the given name. The collectionHandle has to refer to a workspace-controlled collection (a collection in a workspace). If the workspace already contains another VCR for the same version history, the method fails.




Auto-Versioning and Automatic Version Control

To check auto versioning and automatic version control for a collection, the mi.version.IBasicVersioningManager specifies the following two methods:

boolean isAutoChildVersionControlEnabled(IResourceHandle collectionHandle)
                                  throws ResourceException

Returns true if auto versioning is enabled for the children of the specified collection.

IName getAutoVersioningMode(IResourceHandle resourceHandle)
                     throws ResourceException

Returns the IName for the server's auto-versioning mode of the given resource or null if no auto-versioning mode is defined.

The following modes are possible (constants defined in the current API's com.sapportals.wcm.repository.IVersionControlledResource):

These reflect the modes specified in "Versioning Extensions to WebDAV"; IETF RFC 3253 Section 3.2.2.

Auto versioning can be switched using the method specified in mi.version.IMutableBasicVersioningManager:

Set setAutoChildVersionControlEnabled(IResourceHandle collectionHandle,
                                      boolean modifyPlainChildren,
                                      boolean returnModified,
                                      boolean enabled)
                               throws ResourceException

Enables or disables auto versioning for the specified collection depending on the enabled flag.




Advanced Versioning: Versioned Collections

Since version.controlled collections (VCCs) are treated as other version-controlled resources (VCRs), the only "special" methods are specified by mi.version.VersionedCollectionManager:

Set getEclipsedSet(IResourceHandle vcrHandle)
            throws ResourceException

Returns the Set of names for those children of the specified version-controlled collection that are eclipsed by other children of the VCC.






Repository Filter and Filter Manager

Repository filters modify the resources as they are passed through the RF. Several filters can be cascaded in a chain:

When a client requests a read operation, the RF retrieves the specified resource data from the repository manager. It then retrieves the last filter to apply from the read filter managers by requesting their filters. Then the RF requests the last filter to apply itself to the data. In order to retrieve the data, the filter then requests the filtered data from its predecessor, resulting in all filters in the chain being called by their successors. Finally the filtered data is returned to the client.

The same is true for write operations, where the RF first applies the write filters and then sends the filtered data to the resource's repository manager.

The repository filter manager decides when to apply a filter. When the RF requests the filters to be applied from the repository filter manager, it uses getFilterForRead() when a read request's data has to be filtered, and getFilterForWrite() when a write request's data is to be filtered. The filter's predecessor in the filter chain is passed as a parameter to the getFilterFor() method.

The filter manager should then decide (based on the resource returned by the predecessor's filter getResource() method) whether to apply its filter or not. If the filter is to be applied, the filter manager should return its own filter (see below). If the filter should not be applied, it must return the predecessor as passed to the getFilter() method. The filter manager must not return null.

If the filter manager returns its own filter, that filter has to contain a reference to its predecessor.

The following types of filter are used, depending on the type of resource data to be filtered:

Filters are located in the com.sapportals.wcm.repository.filter package in the current API.




Namespace Filters

Namespace filters affect the visibility of resources and are applied to collections.

A namespace filter implements INamespaceFilter.

The filter's getCollection() method returns the collection to which the filter is to be applied. This is used by the filter manager for determining whether to apply the filter or not.

The filter's filter() method returns the filtered list of child resources for the collection.




Property Filters

Property filters affect the properties of resources.

A property filter implementation should be derived from AbstractPropertyFilter, which handles all the IPropertyFilter's methods except filter().

The filter's getResource() returns to which the filter is to be applied - it might return null if the filter is called while the resource is being created. In this case, the method getRepositoryManager()returns the repository manager in which the resource is to be created.

The getFilterMode() method returns the current filter mode, which is one of the following values:

If getFilterMode() is SINGLE_PROPERTY or PROPERTY_LIST, the method getPropertyNameList() returns a list with the name(s) of the properties requested.

The getFilterContext() method returns java.util.Properties that can be used to pass context information from filter to filter.




Content Filters

Content filters affect the content of resources.

A content filter implements IContentFilter and IContent.

The filter's getResource() method returns the resource to which the filter is to be applied - it might return null if the filter is called while the resource is being created. In this case, the method getRepositoryManager()returns the repository manager in which the resource is to be created.

The methods for IContent behave as described in How to use the RF's Client API.






Repository Services

A repository services might be implemented for one of the following reasons:

The following UML diagram shows the relevant interfaces and classes:

A repository service implementation (such as MyRepositoryService) must derive from AbstractResourceService (package com.sapportals.wcm.repository.manager), which implements IRepositoryService. This base class already handles all interfaces required for the interaction with the component runtime (see RF Components and their Runtime).

The MyRepositoryService must then overwrite the startUpImpl() and shutDownImpl() methods, to perform initialization on start-up and de-initialization on shutdown.

A repository service is assigned to one (or more, if it's a singleton, see RF Components and their Runtime) repositories. Therefore the list of repository managers to which the repository service is initially assigned is passed as a parameter to startUpImpl().

The methods addRepositoryAssignment() and removeRepositoryAssignment() are only required to be overwritten by hot-reload-enabled repository services: These methods are called by the RF when a new repository is added to the RF or if a repository to which this repository service is assigned is removed.

Finally, the MyRepositoryService should implement the interface(s) that specify the appropriate aspect (for example, IAppProperty when implementing a specific version of the RF's predefined application properties or IMyAspect when implementing a new aspect).






Global Services

Global services add additional "unified" functionality to the RF's resource objects and use the unified aspects.

While repository services might be related to specific repositories only, global services provide functions that are not closely related to the backend system (as aspects are) but that are more general. For example, KM's indexing service (which handles the indexing of resources in order to enable fast searching) works on all resources, regardless of the repository - by using the unified namespace, content and property aspects of the resource.

The following UML diagram shows the relevant interfaces and classes:

A global service implementation (such as MyService) must be derived from AbstractService (package com.sapportals.wcm.service), which in turn implements IService. This base class AbstractService already handles the interaction with the component runtime (see RF Components and their Runtime).

The MyService should then overwrite the startUpImpl() and shutDownImpl() methods, to perform initialization on start-up (for example, connecting to a database) and de-initalization on shutdown (for example, committing all open transactions and closing the connection).

Of course, it also has to implement the interface that specifies the functions provided by that global service (for example, IMyFunctionality).