Content Management SPI Development Guide

     Previous  Next    Open TOC in new window    View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

SPI Implementation Creation

This chapter provides information about creating an SPI implementation. It contains the following sections:

 


VCR SPI Implementation Interaction

When the VCR needs to access a specific repository from the set of application repository configurations, the VCR loads and creates an instance of the configured SPI implementation class, which implements the VCR SPI Repository interface. The VCR invokes methods on the repository implementation to obtain objects, such as a Ticket, which implement other VCR SPI interfaces.

The VCR invokes methods on the Repository and Ticket implementations to query the SPI implementation for its capabilities. (Capabilities are what operations the implementation supports.) The VCR then invokes methods to retrieve the operation interfaces, such as NodeOpsV1, that the SPI exposes. Finally, the VCR invokes methods on the operation interfaces.

 


Primary Classes for a Basic SPI Implementation

The two primary classes for an SPI implementation are:

The Repository class is instantiated directly by the VCR. This class provides anonymous repository access. For example, an SPI implementation can report its version and other basic information. Most importantly, this is the entry point for authenticating to an SPI implementation to obtain a Ticket. Only authenticated users can obtain a Ticket; the Ticket provides access to the important repository operations.

The Ticket class provides authorized repository access, and provides access to the authorized operations interfaces such as NodeOpsV1. Only authenticated users can obtain a Ticket; the Ticket provides access to the important repository operations. This class is instantiated by Repository.connect(…).

The Repository can optionally expose additional authorized ticket operations interfaces, such as NodeOpsV1, SearchOpsV1, via its implementation of getAllInterfaces() and getInterface().

The general flow at runtime is:

 


Repository Guidelines when Creating an SPI Implementation

Use the following design guidelines when creating an SPI implementation:

 


Basic SPI Implementation

To create a basic SPI implementation:

  1. Create a Java class that implements the interface com.bea.content.spi.flexspi.Repository.
  2. This class is a required interface that must be implemented. It is included with WebLogic Portal.

  3. Create a public default constructor.
  4. Implement setProperties() to store the repository configuration properties passed by the VCR. Store in a local variable.
  5. Implement setName() to store the repository configuration name passed by the VCR. Store in a local variable.
  6. Implement getProperties()and getName() to read the local variables.
  7. Implement getDescription() to report the repository description data.
  8. This is essentially a Properties bucket, with some well-defined keys in SPIDescriptionKeys. It includes the vendor, version, and so on.

  9. Implement getRepositoryDefinedCapabilities() to report which custom capabilities the SPI implementation supports.
  10. For a basic SPI implementation, just return Collections.emptySet().

  11. Implement getAllInterfaces() to return all repository (not ticket) operations interfaces.
  12. These are optional interfaces, such as RepositoryConfigOpsV1, that a Repository can implement.

    For a bare-bones SPI implementation, just return an empty HashMap.

  13. Implement getInterface( String interfaceName ) to be consistent with getAllInterfaces().
  14. For a bare-bones SPI implementation, just return null.

  15. Implement getCapabilitySupport( Set<ICapabilityDefinition> ) to report which feature capabilities this SPI implementation supports.
  16. It also report which method capabilities the repository supports across the optional repository operation interfaces.

  17. Implement connect( Credentials ) and Connect( String username, String password ) to allow a caller to obtain a ticket.
  18. connect( username, password ) is called if a username and password are available (generally from the repository configuration data.)

    connect( credentials ) is called if no username/password are available. The credentials includes the caller’s implicit identity.

    For a basic SPI implementation, create and return a new Ticket instance. More advanced implementations may authenticate the credentials, and only return a Ticket instance if successful.

  19. Implement any optional Repository operation interfaces, such as RepositoryConfigOpsV1.
  20. For any repository operation interfaces returned by Repository.getAllInterfaces():

    1. Implement the interface.
    2. Any methods that are not implemented should throw an UnsupportedRepositoryOperationException and the getCapabilitySupport() method should not report this method as supported.

    3. Modify Repository.getAllInterfaces() and getInterface() to return the interface implementation.
    4. Modify Repository.getCapabilitySupport() to report the status (supported or unsupported) of each method on the repository operation interface.
  21. Create a Java class that implements the interface com.bea.content.spi.flexspi.Ticket (supplied with WebLogic Portal)
    1. Implement getAllInterfaces() to return all ticket operations interfaces. These are optional interfaces, such as NodeOpsV1, which a Ticket can implement. For a basic SPI implementation, return an empty HashMap.
    2. Implement getInterface( String interfaceName ) to be consistent with getAllInterfaces(). For a basic SPI implementation, just return null.
    3. Implement getCapabilitySupport( Set<ICapabilityDefinition> ) to report which method capabilities this ticket supports across the optional ticket operation interfaces.
  22. Implement any optional Ticket operation interfaces, such as NodeOpsV1.
  23. For any ticket optional interfaces returned by Ticket.getAllInterfaces():

    1. Implement the interface.
    2. Any methods that are not implemented should throw an UnsupportedRepositoryOperationException and the getCapabilitySupport() method should not report this method as supported.

    3. Modify Ticket.getAllInterfaces() and getInterface() to return the interface implementation.
    4. Modify Ticket.getCapabilitySupport() to report the status (supported or unsupported) of each method on the ticket operation interface.

Basic SPI Repository Implementation Code Example

Listing 4-1shows a simple SPI Repository code example of a Repository with the following limitations:

Basic SPI Ticket Implementation Code Example

Listing 4-2 shows a simple SPI Ticket code example of a Ticket that does not support any optional ticket operation interfaces, such as NodeOpsV1 and SearchOpsV1, and therefore does not support any method capabilities

Listing 4-2 Ticket Implementation Code Example
import com.bea.content.spi.flexspi.Ticket;
import com.bea.content.spi.flexspi.Repository;
import com.bea.content.spi.flexspi.common.ISPIMarker;
import com.bea.content.spi.flexspi.common.SPITicketInterfaces;
import com.bea.content.spi.flexspi.common.capability.ICapabilityDefinition;
import com.bea.content.spi.flexspi.common.capability.CapabilityLevel;
import com.bea.content.spi.flexspi.common.capability.MethodCapabilityDefinition;
import java.util.*;
public class FlexTicketImpl  implements Ticket
{
   Repository repository; // the repository this ticket was created from
   //1=flex interface name, 2=flex interface object
   private Map<String, ISPIMarker> advertisedInterfaces;
   public FlexTicketImpl(Repository repository)  {
      this.repository = repository;
      advertisedInterfaces= new HashMap<String,ISPIMarker>();
      // no interfaces yet
   }
   public Map<String, ISPIMarker> getAllInterfaces() {
      return advertisedInterfaces;
   }
   public ISPIMarker getInterface(String interfaceName) {
      return advertisedInterfaces.get(interfaceName);
   }
   public Map<ICapabilityDefinition, CapabilityLevel>
      getCapabilitySupport(Set<ICapabilityDefinition> capabilities)
   {
      // no interfaces, no methods, no capabilities; everything is unsupported
      HashMap<ICapabilityDefinition, CapabilityLevel> capMap 
         =new HashMap<ICapabilityDefinition, CapabilityLevel>();
         // start out with everything not supported; we will mark 
         // individual feature capabilities as supported.
         for (ICapabilityDefinition capDef : capabilities) {
            capMap.put(capDef, CapabilityLevel.NotSupported);
         }
         // here we would override the unsupported value if we supported anything
         // but we don’t…
         // everything unsupported 
         return capMap;
   }
}

 


Optional SPI Interfaces Implementation

You use optional SPI interfaces to expose nodes and types to the VCR. Generally, client code has certain assumptions about the capabilities of the repositories the code runs against, such as read-only access to nodes. As the VCR delegates to the repository, the client code presumes that certain VCR methods work properly.

The optional PSPI interfaces are:

Exposing an Optional SPI Interface

To expose an optional SPI interface such as NodeOpsV1.

  1. Create a class to implement the SPI interface.
  2. For example MyNodeOps implements NodeOpsV1. Generally, you should make this a light-weight class, as many objects may be created.

  3. Write implementations of the SPI interface methods.
  4. Note: Any methods that are not implemented should throw an UnsupportedRepositoryOperationException. Additionally, the Ticket.getCapabilitySupport() method should report this method as not supported.
  5. Modify Ticket.getAllInterfaces() and getInterface() to return the interface implementation.
  6. To reduce object creation, create the interfaces when the ticket is created, hold onto them, and then return them in getAllInterfaces() and getInterface(). For example:

    private Map<String,ISPIMarker> ifaces;
    public FlexTicketImpl( Repository repository ) {
       this.repository= repository;
    //init interfaces
       ifaces= new HashMap<String,ISPIMarker>();
       ifaces.put(SPITicketInterfaces.NODE_OPS_V1,
          new MyNodeOps(…);
    . . .
    }

    public Map<String, ISPIMarker> getAllInterfaces() {
       return ifaces;
    }
    public ISPIMarker getInterface( String ifaceName ) {
       return ifaces.get( ifaceName );
    }
  7. Modify Ticket.getCapabilitySupport() to report the status (supported or unsupported) of each method on all the ticket operation interfaces. For example:
  8. public Map<ICapabilityDefinition, CapabilityLevel>
       getCapabilitySupport(Set<ICapabilityDefinition> capabilities)
    {
       Map<ICapabilityDefinition, CapabilityLevel> capMap
          =new HashMap<ICapabilityDefinition, CapabilityLevel>();
       // start out with everything not supported; we will mark 
       // individual feature capabilities as supported.
       for (ICapabilityDefinition capDef : capabilities) {
          capMap.put(capDef, CapabilityLevel.NotSupported);
       }
       // now override the unsupported values where it makes sense
       final String[] supportedNodeOpsMethodNames = new String[] {
          NodeOpsV1.MethodName.getNodeChildren.toString(),
          NodeOpsV1.MethodName.getNodeChildrenAsNodeIds.toString(),
          NodeOpsV1.MethodName.getNodesWithIds.toString(),
          NodeOpsV1.MethodName.getNodeWithId.toString(),
          NodeOpsV1.MethodName.getNodeWithPath.toString(),
       };
       for (String methodName : supportedNodeOpsMethodNames) {
          ICapabilityDefinition capDef
             = new MethodCapabilityDefinition(
                SPITicketInterfaces.NODE_OPS, methodName
                );
             if (capabilities.contains(capDef)) {
                capMap.put(capDef, CapabilityLevel.FullySupported);
          }
       }
       return capMap;
    }

 


SPI Interface Result Collections, Sorting, and Filtering

Optionally, the SPI implementation can include the ability to sort and/or filter results. The collections of items returned by the SPI are returned in a QueryResult object (including an ordered list of results) and a QueryCriteria object that describes how the returned collection is sorted and filtered (if at all). For instance, a QueryResult<Node> may contains a collection of nodes that are both sorted and filtered.

Filtering and Sorting Results with the SPI

Many of the methods that return results collections take a QueryCriteria parameter. This parameter allows the caller to indicate how results should be sorted or filtered or both (if possible). For example, the caller may request that the results be sorted by name. At present, the caller can specify only one sort criteria and one filter criteria.

The SortCriteria contains a property (criteria) and an ascending or descending flag, such as name ascending.

The FilterCriteria contains a property (criteria), a FilterMethod operand, such as unfiltered, equals, not equals, contains, not contains, begins with, ends with, greater than, and less than, and a value. For example, name contains 'foo'.

The sort and filter criteria set of properties are related to the JavaBean properties on the data objects being used. For example, Node contains a JavaBean property for name because it has a getName() method; for createdDate because it has a getCreatedDate() method; and so on.

Native sorting and filtering performs best. The VCR also supports mechanisms for sorting and filtering in-memory if the SPI implementation is unable to service a sort or filter request. Client code (and the VCR) can ask the SPI which properties it can natively sort and filter.

The SPI implementation reports its sorting and filtering capabilities (the properties it can sort and filter on) to the VCR for objects on an interface by implementing methods such as NodeOpsV1.getNativeSortableProperties() and NodeOpsV1.getNativeFilterableProperties(). For example, the SPI may report that it can sort nodes by name and createDate properties.

The sorting and filtering capability reporting is currently done at the interface granularity. In other words, a given interface such as NodeOpsV1 has a primary data object. The primary data object, a Node for example, reports the capabilities for sorting and filtering across the results collection methods in the interface.

Tip: If the SPI does not support native sorting or filtering, it should return an empty set, such as Collections.emptySet() rather than null.

The SPI implementation may receive a QueryCriteria parameter that requests sorting or filtering capabilities beyond what it can support. If this happens, the SPI implementation should not throw an exception. Instead the SPI implementation should do its best to report how the results are currently sorted and filtered. For example, the SPI implementation should create an unsorted and/or unfiltered QueryResult to express what it was unable to sort and/or filter. If the SPI implementation can perform one of the requests, it should do so and report the results appropriately. For instance, if it can sort, the SPI implementation should report the results as sorted, but unfiltered.

If necessary the VCR may sort/filter the query results in memory to ensure the query results are sorted/filtered as the client requests.

Common SPI Interface Objects for Sorting and Filtering

Use the following objects for sorting and filtering:


  Back to Top       Previous  Next