Skip Headers
Oracle® Fusion Middleware Content Management SPI Development Guide for Oracle WebLogic Portal
10g Release 3 (10.3.4)

Part Number E14231-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

4 Implementing an SPI

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

4.1 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.

4.2 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:

4.3 Repository Guidelines when Creating an SPI Implementation

Use the following design guidelines when creating an SPI implementation:

4.4 Basic SPI Implementation

To create a basic SPI implementation:

  1. Create a Java class that implements the interface com.bea.content.spi.flexspi.Repository.

    This class is a required interface that must be implemented. It is included with WebLogic Portal.

  2. Create a public default constructor.

  3. Implement setProperties() to store the repository configuration properties passed by the VCR. Store in a local variable.

  4. Implement setName() to store the repository configuration name passed by the VCR. Store in a local variable.

  5. Implement getProperties()and getName() to read the local variables.

  6. Implement getDescription() to report the repository description data.

    This is essentially a Properties bucket, with some well-defined keys in SPIDescriptionKeys. It includes the vendor, version, and so on.

  7. Implement getRepositoryDefinedCapabilities() to report which custom capabilities the SPI implementation supports.

    For a basic SPI implementation, just return Collections.emptySet().

  8. Implement getAllInterfaces() to return all repository (not ticket) operations interfaces.

    These are optional interfaces, such as RepositoryConfigOpsV1, that a Repository can implement.

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

  9. Implement getInterface( String interfaceName ) to be consistent with getAllInterfaces().

    For a bare-bones SPI implementation, just return null.

  10. Implement getCapabilitySupport( Set<ICapabilityDefinition> ) to report which feature capabilities this SPI implementation supports.

    It also report which method capabilities the repository supports across the optional repository operation interfaces.

  11. Implement connect( Credentials ) and Connect( String username, String password ) to allow a caller to obtain a ticket.

    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.

  12. Implement any optional Repository operation interfaces, such as RepositoryConfigOpsV1.

    For any repository operation interfaces returned by Repository.getAllInterfaces():

    1. Implement the interface.

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

    2. Modify Repository.getAllInterfaces() and getInterface() to return the interface implementation.

    3. Modify Repository.getCapabilitySupport() to report the status (supported or unsupported) of each method on the repository operation interface.

  13. 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.

  14. Implement any optional Ticket operation interfaces, such as NodeOpsV1.

    For any ticket optional interfaces returned by Ticket.getAllInterfaces():

    1. Implement the interface.

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

    2. Modify Ticket.getAllInterfaces() and getInterface() to return the interface implementation.

    3. Modify Ticket.getCapabilitySupport() to report the status (supported or unsupported) of each method on the ticket operation interface.

4.4.1 Basic SPI Repository Implementation Code Example

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

  • It does not authenticate.

  • It does not support any feature capabilities.

  • It does not support any optional repository operation interfaces, such as RepositoryConfigOpsV1, and therefore does not support any method capabilities.

  • It does not provide any repository defined capabilities.

Example 4-1 Basic SPI Implementation

import com.bea.content.spi.flexspi.Repository;
import com.bea.content.spi.flexspi.Ticket;
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.FeatureCapabilityDefinition;
import com.bea.content.spi.flexspi.common.ISPIMarker;
import com.bea.content.spi.flexspi.common.SPIRepositoryInterfaces;
import com.bea.content.spi.flexspi.common.SPIDescriptionKeys;
import com.bea.content.capability.NodeFeatureCapability;
import com.bea.content.*;
import java.util.*;
public class FlexRepositoryImpl implements Repository
{
   // VCR configures these before connect() is called
   private String repositoryName;
   private Properties props;
 
   // the description key/values
   private Map<String, String> descMap = new HashMap<String, String>();
   public FlexRepositoryImpl()
   {
      // SPI implementation MUST have a public default ctor
      //standard keys        
      descMap.put(SPIDescriptionKeys.VENDOR_KEY, 
         "Some third party vendor");
      descMap.put(SPIDescriptionKeys. VERSION_KEY, "0.1.1");    
      descMap.put(SPIDescriptionKeys. DESCRIPTION_KEY, "Simple SPI");

      //custom keys can also be added
      descMap.put("InternalVersion", "Build 31141");
   }
   public Ticket connect(Credentials credentials) 
      throws AuthenticationException, RepositoryException 
   {
       // this SPI does not perform authentication
       return new FlexTicketImpl(this);
   }
   public Ticket connect(String username, String password) 
      throws AuthenticationException, RepositoryException 
   {
      // this SPI does not perform authentication
       return new FlexTicketImpl(this);
   }
   public String getName() {
      return repositoryName;
   }
   public void setName(String name) {
      repositoryName = name;
   }
   public Properties getProperties() {
      return props;
   }
   public void setProperties(Properties properties)  {
      props = properties;
   }
   public Set<ICapabilityDefinition> getRepositoryDefinedCapabilities() {
      return Collections.emptySet();
   }
   public Map<String, String> getDescription() {
       return descMap;
   }
   public Map<String, ISPIMarker> getAllInterfaces() {
       // no repository operation interfaces
      return new HashMap<String,ISPIMarker>();   
   }
   public ISPIMarker getInterface(String interfaceName) {
       // no repository operation interfaces
      return null;    
   }
   public Map<ICapabilityDefinition, CapabilityLevel>
      getCapabilitySupport(Set<ICapabilityDefinition> capabilities)
   {
      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;   
   }
}

4.4.2 Basic SPI Ticket Implementation Code Example

Example 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.

Example 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; 
}
}

4.5 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:

4.5.1 Exposing an Optional SPI Interface

To expose an optional SPI interface such as NodeOpsV1.

  1. Create a class to implement the SPI interface.

    For example MyNodeOps implements NodeOpsV1. Generally, you should make this a light-weight class, as many objects may be created.

  2. Write implementations of the SPI interface methods.

    Note:

    Any methods that are not implemented should throw an UnsupportedRepositoryOperationException. Additionally, the Ticket.getCapabilitySupport() method should report this method as not supported.
  3. Modify Ticket.getAllInterfaces() and getInterface() to return the interface implementation.

    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 );
    }
    
  4. Modify Ticket.getCapabilitySupport() to report the status (supported or unsupported) of each method on all the ticket operation interfaces. For example:

    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;
    }
    

4.6 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.

4.6.1 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.

4.6.2 Common SPI Interface Objects for Sorting and Filtering

Use the following objects for sorting and filtering:

  • QueryCriteria – Some methods pass a QueryCriteria object as a means for a caller to request sorting and filtering of results. These methods include SortCriteria and FilterCriteria objects. Currently, only a single SortCriteria or FilterCriteria can be specified; multi-criteria sorting or filtering is not supported.

  • SortCriteria – Provides the ability for ascending or descending sorting on a specific property (criteria). A sortResults flag indicates whether the results are sorted or unsorted. SortCriteria also includes a property that is one of the JavaBean properties on the data object.

  • FilterCriteria – Provides the ability to filter on a specific property (criteria). A filterResults flag indicates whether the results are filtered or unfiltered. FilterCriteria includes a property that is one of the JavaBean properties on the data object, and a filterMethod that indicates the filter operation, such as begins with, contains, equals, greater than, unfiltered, and so on.

  • QueryResult – Represents a set of results returned by the SPI implementation. QueryResult also includes a QueryCriteria object that describes how the results are sorted and filtered (or neither), plus an ordered list of the data objects.

4.7 Configuring Security

This section describes the available options for configuring security with a third-party repository through the SPI.

4.7.1 Authorization and Identity Management Overview

The SPI security model lets you configure both authorization and identity management options. You can configure authorization to be managed by either WLP or the native (third-party) repository. With WLP-managed authorization, data is secured by WLP, and WLP content entitlements apply. With native authorization, data is secured through the third-party repository. WLP content entitlements do not apply.

When user identity is configured globally, all WLP application users who log in to the repository share the same user credentials and privileges. If the third-party repository supports the RepositoryMultipleUsers capability, it is possible on a per-WLP-user basis to log in to the third-party repository with "individual" credentials (typically called "mapped credentials").

Developers can use the SPI API to develop a login feature that allows portal users to log in with mapped credentials. For example, if a user logs in using his or her global identity, they might have read-only access to the repository. However, if they log in using their mapped identity, they could have read-write access. In addition, administrators using the Portal Administration Console can log in to a third-party repository with a mapped credential. For details, see "Logging Into a Third-Party Repository" in the Oracle Fusion Middleware Content Management SPI Development Guide for Oracle WebLogic Portal. Again, mapped credential identity management is only possible if the third-party repository supports it. To support this capability in a repository, the repository must indicate it supports the RepositoryMultipleUsers capability. When this is done, the VCR allows users to define personal mapped credentials for this repository, and supplies the credentials to the repository as appropriate with the Ticket.connect(...) methods.

Note:

Currently, Oracle WebCenter Content is the only repository that supports the RepositoryMultipleUsers capability.

4.7.2 No Authentication

Some SPI implementations may not have a concept of authentication. If this is the case, the repository configuration contains no user or password credentials, and all VCR users in the same enterprise application have access to the repository. WebLogic Portal CM entitlements define the data each user is permitted to access. From an SPI implementation perspective, the Repository.connect() methods should always create and return a Ticket implementation object.

4.7.3 WLP-Secured Resource Management

If WLP manages resource authorization, the data is secured through WebLogic Portal and WLP Content Entitlements will apply. Two options for identity management exist for WLP-secured resource management.

4.7.3.1 WLP-Secured/Global User Identity Configuration

In the case of global user identity, all users in the application use the same credentials to connect to the third-party repository instance.

Configure this type of security with these settings in the META-INF/content-config.xml file in the EAR Project, as described in the following table.

Field Value
Username The user name of a valid user of the repository.
useNativeSecurity false

4.7.3.2 WLP Secured/Mapped Credential User Configuration

Mapped credential identity management is only available if the third-party repository supports it. Currently, only the Oracle WebCenter Content repository supports identity mapping. For information on the mapped identity option, see Section 4.7.1, "Authorization and Identity Management Overview."

4.7.4 Natively-Secured Resource Management

If the third-party (native) respository manages resource authorization, the data is secured through the third-party repository and WLP Content Entitlements do not apply. Two options for identity management exist for natively-secured resource management.

4.7.4.1 Natively-Secured/Global User Identity Configuration

All users in the application use the same credentials to connect to the third-party repository instance. Configure this type of security with these settings in the META-INF/content-config.xml file in the EAR Project:

Field Value
Username The user name of a valid user of the repository.
useNativeSecurity true

4.7.4.2 Natively-Secured/Mapped Credential User Configuration

Mapped identity management is only available if the third-party repository supports it. Currently, on the Oracle WebCenter Content repository supports identity mapping. For information on the mapped identity option, see Section 4.7.1, "Authorization and Identity Management Overview."

4.7.5 Identity Propagation with Native Security

With this option, WebLogic Portal and the third-party repository use the same security store, such as a shared LDAP store. Each WLP user sees in the application what they are permitted to see on the third-party repository side. Entitlements (WLP content security) are disabled.

Field Value
Username Do not set a value for this field.
useNativeSecurity true

4.8 Connecting and Logging Into a Third-Party Repository

For detailed information on connecting to third-party repositories, see "Connecting to a Third-Party Repository" in the Oracle Fusion Middleware Content Management Guide for Oracle WebLogic Portal. For information on logging in to a third-party repository (including the procedure for logging in using either global or mapped user credentials), see "Logging Into a Third-Party Repository" in the Oracle Fusion Middleware Content Management Guide for Oracle WebLogic Portal.

4.9 Search Cache Configuration

The VCR manages repository-specific search caches of the form: searchCache.repository_name. These caches can be configured on a per-repository basis. For details, see the Oracle Fusion Middleware Cache Management Guide for Oracle WebLogic Portal.

SPI developers can exclude a repository from the VCR search cache. For example, you might do this if the repository can efficiently serve search results or live search results are required.. This feature is only available if the respository supports the feature capability named SearchFeatureCapability.RepositoryManagedSearchCache.