Implementing a Repository Manager (read-only)

SAP Developer Network

Applies To:

Knowledge Management 6.0

Summary

A repository is connected to the repository framework (RF) using its repository manager (RM). The repository manager is responsible for converting the repository’s internal representation of the stored information into the unified aspects of the RF, and vice versa.

By Thilo Brandt
NetWeaver RIG Specialist
24 May 2004

 

Relevant Repository Framework APIs and documentation

A prerequisite for developers starting repository manager implementation is an understanding of the concepts described in Repository Framework Concepts (RCO).

This tutorial describes the usage of the following repository framework APIs that are relevant for implementing your own repository manager.

  • com.sap.netweaver.bc.rf.common.*: Contains repository framework common interfaces

  • com.sap.netweaver.bc.rf.mi.*: Contains repository framework manager interfaces that are necessary for the implementation classes

Considerations for 3rd-Party Repository Manager Implementations

The following prerequisite is relevant for integrating integrate a third-party backend system into the repository framework:

  • A Java API for accessing distinguishable entities of the backend system

Additional considerations for the implementation parts are already mentioned in the Repository Framework Concept (RCO) document (Ch. 6.2.1).

This tutorial refers to a read-only implementation of a file-system repository manager that implements the unified aspects for namespace, content, and property.

Coding in Detail

Implementing a repository manager means implementing certain unified aspects (see RCO, Ch. 2.1). The minimum requirement is that you implement the namespace, content, and property aspect. This example deals only with these mandatory aspects; further aspects can be implemented optionally, and are described in other documentation.

Implementing an IResourceHandle

The IResourceHandle is the mapping object between the third-party backend system and the repository framework. It handles all the data mappings between the objects of the backend system and each unified aspect implementation of the repository manager.

A repository manager has to implement its own representation of the IResourceHandle either by implementing the IResourceHandle interface or by extending the abstract class AbstractHandle:

public class SimpleHandle implements IResourceHandle

The IResourceHandle interface exposes only one interface method for implementation:

IRid getRid();

The getRid() method must return the hierarchical structured identifier (RID, see RCO, Ch. 2.1.2), the backend object’s addressable identifier within the repository framework.

The handle object itself should contain context information from the repository framework, which can be used by the third-party backend system. This context information, for example, user and language, can be passed through the handle’s constructor and stored in member variables.

SimpleHandle(Node node, IUser user, Locale locale) {
    this.m_node = node;
    this.m_user = user;
    this.m_locale = locale;
}

Due to the usage of IResourceHandle objects as keys in maps (see implementation property unified aspects), it is recommend that you override the hashCode() and equals() method of the implementation.

public boolean equals(Object obj) {
    if (obj!=null && obj instanceof SimpleHandle) {
        SimpleHandle o = (SimpleHandle)obj;
        if (o.getRid().equals(this.getRid()) &&
            o.m_locale.equals(this.m_locale) &&
            o.m_user.equals(this.m_user)
        )
        return true;
    }
    return false;
}
				
public int hashCode() {
    return this.getRid().hashCode() + this.m_locale.hashCode() + 
        this.m_user.hashCode();
}

In this sample implementation, the backend objects are represented as Node and FolderNode objects. They contain a wrapper for file system objects such as files and folders. The IResourceHandle implementation (SimpleHandle) in this sample holds a reference to a corresponding Node or FolderNode object in the backend. For direct handling of these objects, the handle also exposes an implementation depend method getNode():

public Node getNode() {
    return this.m_node;
}

Methods to be implemented by an IResourceHandle implementation:

Method

Purpose

getRid()

Returns a hierarchical resource identifier (RID) for identifying the referenced backend object

equals()

Indicates whether any other object is "equal to" this one

hashCode()

Returns a hash code value for the object

Extending AbstractManager

The central repository manager class has to be derived from the abstract class AbstractManager, which already offers some default functionality in the abstract class, such as start-up procedures or configuration providing functionality.

The mandatory method to be implemented by a personal repository manager implementation is lookup(IRid rid):

public IResourceHandle lookup(IRid rid) throws ResourceException {
        if (rid == null) {
        return null;
    }

If the passed IRid object cannot be mapped to a backend object, it could either return null (if the RID can be mapped but the backend object is not accessible, perhaps because the connection has been lost) or throw a ResourceException

The root of the repository manager implementation is also handled by the lookup() method:

        if (rid.equals(this.root.getRid())) {
            return new SimpleHandle(
                this.root,
                this.getUser(rid),
                this.getLocale(rid));
				

For all other cases, you have to distinguish between a collection and a resource representation:

    IResourceHandle parent = this.lookup(rid.parent());
    Node node = ((SimpleHandle) parent).getNode();
    if (node.isCollection()) {
        Node child =
            ((FolderNode) node).getChild(rid.name().getPath());
        SimpleHandle rv = null;
        if (child != null) {
            rv = new SimpleHandle(child,this.getUser(rid),
                this.getLocale(rid));
        }
        return rv;
    }

If a resource exists for the given RID, the lookup() method must return a valid IResourceHandle object that refers to the corresponding backend object, otherwise it returns null.

Additional signatures of the lookup() method are used for mass calls of the repository framework. They could be implemented if support for mass calls must be provided, for example, for searching.

For providing additional backend system specific configuration to the repository manager, you can use the config member variable from the AbstractManager implementation. This member variable is filled through the configuration framework with all configuration data set up for this manager instance (see Configuration & Deployment).

The configuration can be read on repository manager start-up within the method startUpImpl(), or at any time after passing the startUpImpl() method:

    protected void startUpImpl()
        throws ConfigurationException, StartupException {
				
        :
				
        SimpleRepositoryManager.OSRoot = 
            this.config.getAttribute("OSRoot");
				
        this.root = 
new FolderNode(this.getRidPrefix().substring(1), null);
        :
    }
				

We also recommend that you initialize the root resource of this manager in the startUpImpl() method.

The following methods from AbstractManager should be overridden or implemented:

Method

Purpose

lookup(IRid rid)

Return a valid IResourceHandle object to the corresponding RID object

startUpImpl()

Method for handling initialization of the repository manager

Implementing Namespace Unified Aspect – INamespaceManager

The namespace unified aspect takes care on the hierarchical structure of the resources exposed by the repository manager. Namespace means the realm of names the resources fill in the repository. This aspect mainly provides functions for traversing resources in the namespace.

findResources() is the most important method of this interface to be implemented. It comes with two different signatures. It should provide functionality for finding resources for a certain IResourceHandle. This is required for navigating in the repository’s hierarchy.

public List findResources(IResourceHandle handle, 
    IFindResourcesDescriptor desc, int start, int offset, java.lang.Object obj)
    throws ResourceException, OperationNotSupportedException {
				
if (desc instanceof AdvancedChildrenFindResourcesDescriptor)
            throw new OperationNotSupportedException(
                        null,
                        "Operation failed!",
                        true);

Within the findResources() method, you have to distinguish several IFindResourceDescriptors (see RCO, Ch. 6.2.5). This implementation only handles IBasicChildrenFindDescriptor and throws an OperationNotSupportedException if any other IFindResourceDescriptors pass the method.

    if (desc instanceof IBasicChildrenFindResourcesDescriptor) {
        List children = new ArrayList();
        Node node = ((SimpleHandle) handle).getNode();
        if (node instanceof FolderNode) {
            List nodeChildren = ((FolderNode) node).getChildren();
            Iterator iter = nodeChildren.iterator();
            while (iter.hasNext()) {
                Node nodeChild = (Node) iter.next();
                children.add(
                    new SimpleHandle(
                        nodeChild,
                        this.getUser(handle.getRid()),
                    this.getLocale(handle.getRid())));
            }
        }
        return children;
    }
    throw new OperationNotSupportedException(
        null,
        "Operation failed !",
        true);
}

The method must return a List (java.util.List) that contains the child handles from the requested IResourceHandle. If no children are available, it returns an empty list.

public Iterator findResources(IResourceHandle handle, 
IFindResourcesDescriptor desc, int start, int size)
    throws ResourceException, OperationNotSupportedException {
				
:
if (desc instanceof IBasicChildrenFindResourcesDescriptor) {
       List children =
        this.findResources(handle, desc, start, size, null);
       return children.iterator();
    }
    throw new OperationNotSupportedException(
        null,
        "Operation failed!",
        true);
}

The second signature is almost the same, but returns an iterator object (java.util.Iterator) on the list instead of the list itself.

To determine whether a handle is a node or a leaf, the isCollection() method has to be implemented:

    public boolean isCollection(IResourceHandle handle)
        throws ResourceException {
    
            if (handle instanceof SimpleHandle)
                return ((SimpleHandle) handle).isCollection();
				
            throw new OperationNotSupportedException(
                null,
                "IResourceHandle is no SimpleHandle",
                true);
    }
				

The following methods from INamespaceManager must be implemented:

Method

Purpose

findResources(...)

Provides functionality for finding resources for a specific IResourceHandle. We recommend that you implement all signatures of this method properly.

isCollection(IResourceHandle)

Must return ‘true’ if the IResourceHandle passed is a node object, or ‘false’ if it is a leaf.

getCollectionOrderMechanism (IResourceHandle)

Must return a valid OrderMechanism object.

getLinkDescriptor(IResourceHandle)

Must return a valid LinkDescriptor object

Implementing Content Unified Aspect – IContentManager

Every resource can provide content accessible through this unified aspect. Content can be anything that implements the IContent interface.

The interface exposes the getContent() method signature. The implementation has to determine the content provided for a specific IResourceHandle.

    public IContent getContent(IResourceHandle handle)
        throws ResourceException {

If the handle object passed to this method is a collection object, the method must return null.

            Node node = ((SimpleHandle) handle).getNode();
            if (node.isCollection())
                return null;
				

The content from the backend system must be accessible through java.io.InputStream objects. The stream is passed to the Content object together with a mime type (java.lang.String) and the content length (long). If the mime type and/or content length cannot be determined by the backend system, the default values null for mimetype and -1 for contentlength can be provided.

        try {
            return new Content(node.getInputStream(), null, -1);
        } catch (Exception e) {
            throw new ResourceException(handle.getRid(),e);
        }

The following method from IContentManager must be implemented:

Method

Purpose

getContent(IResourceHandle)

Returns the content for the specified IResourceHandle

Implementing Property Unified Aspect - IPropertyManager

Resources usually have metadata that describes the resource. All system metadata, such as content length or creation date, must be made available as properties. Resources with content must also make the content metadata available in the form of properties.

The interface delivers different methods for accessing properties of a particular IResourceHandle:

  • Single property

  • All properties

  • A list of specified properties

Single properties can be requested calling the getProperty() method:

    public IProperty getProperty(IResourceHandle handle, IPropertyName propName)
        throws ResourceException {

Get the properties from the backend system and find the requested property name to be returned:

        List properties = node.getProperties();
				
        IProperty property = null;
        for (int i = 0; i < properties.size(); i++) {
            property = (IProperty) properties.get(i);
            if (propName.equals(property.getPropertyName()))
                return property;
        }
        return null;
				

If the requested property is not found in the backend system, it should return a null value.

The getAllProperties() method returns all available properties for the specified IResourceHandle.

    public Map getAllProperties(IResourceHandle handle)
        throws ResourceException {
        
            Map propertyMap = new HashMap();
				
            :
				
            IProperty prop = null;
            for (int i = 0; i < properties.size(); i++) {
                prop = (IProperty) properties.get(i);
                propertyMap.put(prop.getPropertyName(), prop);
            }
            return propertyMap;
    }
				

If no properties are available, the return map should be empty, otherwise the map should be build up with the property name as key entry and the property as value.

The following methods from IPropertyManager must be implemented:

Method

Purpose

getProperty(IResourceHandle, IPropertyName)

Returns a single IProperty object from the IResourceHandle that matches the specified IPropertyName

getAllProperties (IResourceHandle)

Returns a map containing all properties of the specified IResourceHandle

getListedProperties (IResourceHandle, List)

Returns a map containing all properties specified in the list for the IResourceHandle

Configuration & Deployment

The repository manager implementation needs to be registered as a configurable object in the configuration framework. This allows the repository framework to access the configuration for the repository manager and pass it to the implementation. For this reason, a new class definition and an instance file have to be created during the development phase.

The class definition (com.sap.bc.rf.manager.SimpleRepositoryManager.cc.xml) for this repository manager should look like this example:

<ConfigClass name="com.sap.bc.rf.manager.SimpleRepositoryManager" 
    extends="RepositoryManager" hotReload="false" hotLoad="false" 
    hotUnload="false">
				
    <attribute name="name" type="string"/>
    <attribute name="prefix" type="string" default="/simple"/>
    <attribute name="class" type="class" 
        constant="com.sap.bc.rf.manager.SimpleRepositoryManager"/>
    <attribute name="OSRoot" type="string" default="C:/"/>
				
</ConfigClass>
				

Additional properties such as the following can be specified in the XML structure:

<attribute name="OSRoot" type="string" default="C:/"/>

They can then be evaluated by the repository manager implementation. The properties (attributes) are passed during start-up time and can be fetched within the startUpImpl() method.

The instance (com.sap.bc.rf.manager.SimpleRepositoryManager.co.xml) of the defined classes above can also be specified during the development phase:

<?xml version="1.0"  encoding="UTF-8" ?>
<Configurable configclass="com.sap.bc.rf.manager.SimpleRepositoryManager">
    <property name="name" value="simple" />
    <property name="description" />
    <property name="prefix" value="/simple" />
    <property name="OSRoot" value="D:/" />
    <property name="active" value="true" />
    <property name="sendevents" value="true" />
    <property name="ignorerootdisplayname" value="false" />
    <property name="services" />
    <property name="class" 
        value="com.sap.bc.rf.manager.SimpleRepositoryManager" />
    <property name="namespacemgr.class" 
        value="com.sap.bc.rf.manager.SimpleNamespaceManager" />
    <property name="contentmgr.class" 
        value="com.sap.bc.rf.manager.SimpleContentManager" />
    <property name="propertymgr.class" 
        value="com.sap.bc.rf.manager.SimplePropertyManager" />
    <property name="propertysearchmgr.class" />
    <property name="idmappermgr.class"/>
    <property name="lockmgr.class"/>
    <property name="securitymgr.class" />
    <property name="securitymgr.ref" />
    <property name="securitymgr.w2kcfg.systemid" />
    <property name="securitymgr.aclcfg" />
    <property name="securitymgr.aclcacheid" />
    <property name="typemgr.class" />
    <property name="versioningmgr.class" />
</Configurable>
				

Every implemented unified aspect has to be referenced by the instance file. Otherwise it is not used in the running repository manager.

The class definition and the instance file have to be bundled with the deployable unit of the repository manager implementation.

Sample Repository Manager Eclipse project

The following archive contains a valid and deployable SAP NetWeaver Developer Studio project based on KM API of NW04 SPS 4, which shows the steps described above in a whole example.

Download SimpleRepositoryManager (com.sap.bc.rf.manager.SimpleRepositoryManager.zip)

Recommended literature

[RCO] Repository Framework Concepts, SAP AG, 2003, SDN -> Knowledge Management -> KM 6.0 Articles

Table of Contents



Content Options

Copyright © 2005 SAP AG, Inc. All Rights Reserved. SAP, mySAP, mySAP.com, xApps, xApp, and other SAP products and services mentioned herein as well as their respective logos are trademarks or registered trademarks of SAP AG in Germany and in several other countries all over the world. All other product, service names, trademarks and registered trademarks mentioned are the trademarks of their respective owners.

SAP Developer Network