Previous Topic: How you Implement doLookupNext Topic: How you Test the Search Operation


How You Implement doSearch

You should be able to search and retrieve the corresponding objects on an endpoint system, provided with the search information details.

  1. Distinguish the search object type

    If multiple object types can be searched and your connector returns true from isBehaviourSearchSingleClass(), you can distinguish which type is being requested by examining filterInfo.getObjectClassMapping().getConnectorClassName() which yields the connectorMapTo defined in the metadata for this object. Where your connector does not return true from isBehaviourSearchSingleClass(), the list of object classes matching the provided search filter can instead be accessed using filterInfo. getObjectClassMappings().

  2. Determine the scope of the search

    The scope on search controls can be OBJECT, ONE-LEVEL or SUBTREE with appropriate logic for the types of objects that are returned. For flat connectors (like SDK/JDBC) this logic is taken care of for you, but needs to be addressed for hierarchical connectors like JNDI-based ones.

  3. Use the search filter

    Write your connector to support search filtering as supported by the endpoint system API to improve performance. You can get the ExprNode by calling filterInfo.getMappedFilter() or convert it to a string representation in LDAP filter syntax. For example, (|(name=f*)(memberOf=*))) using filterInfo.getMappedFilterString().

    If necessary, the mappedFilter can be converted to a different syntax or in-memory representation using a class deriving from com.ca.jcs.filter.SimpleFilterVisitor (or perhaps org.apache.directory.shared.ldap.filter.FilterVisitor), which you would then invoke using filterInfo.getMappedFilter().accept(myFilterVisitor).

    For an example, see LDAPFilterToFileSearchVisitor in the SDK sample.

The com.ca.jcs.filter.SimpleLDAPFilterToMapVisitor provides a useful utility visitor for connectors that only implement basic filter semantics, and utility methods like com.ca.jcs.filter.FilterUtil.toSimpleMap(FilterInfo) use this visitor under the covers to convert a FilterInfo into a simple map of attribute values mentioned in the filter. Connectors making use of such visitors could probably also benefit from using the isConnectorFilterable Boolean metadata setting on any attributes that can appear in customer-provided filters but which the connector cannot process the filtering for. When it is used, the CA IAM CS framework automatically revaluates search results returned by the connector against the provided filter, and throws away any that do not match it before they are returned to the client. This post-filtering has no negative impact on search result streaming (if implemented by the connector), but does incur a slight performance impact.

  1. Retrieve the return attributes

    If possible given the API of the endpoint system, it is best to return only the attributes named in searchControls.getReturningAttributes() to maximize performance. The default behavior for CA IAM CS is to not return attributes which have been flagged with the isExpensive metadata setting (like photos or associations) in one-level and subtree searches, unless they are explicitly requested in the return attributes. CA IAM CS also does not return attributes when a search specifies a null return attributes array, which usually is interpreted to mean all attributes.

    This can be controlled using the ConnectorConfig.setSearchExpensiveAttrs(boolean) method for your connector's configuration, typically set using your connector’s conf/connector.xml file.

  2. Base classes for NamingEnumeration objects returned from search operations can be found in the com.ca.jcs.enumeration package. In particular, RawNamingEnumeration takes care of handling size and time limits for its derived classes. AppendingNamingEnumeration can be used to wrap a prepared collection of results or multiple subenumerations are stepped through in order.
  3. Streaming search support

    The ApacheDS and the CA IAM CS frameworks support a streaming search mechanism, which is marginally harder to implement but has considerable scalability advantages (that is, search results are passed back to the client as soon as they become available and peak memory usage during searches is greatly reduced).

    To stream search, implement your own NamingNumeration which processes and returns each SearchResult object one at a time, and return this NamingNumeration from doSearch(), rather than caching all search results in memory before returning. CA IAM CS then processes and passes back search object one by one after the doSearch() call has already finished executing, rather than waiting for all results to become available before any are passed back to the client application.

    SDKAttributeStyleOpProcessor conditionally uses com.ca.jcs.sdk.SDKSearchEnumeration when streaming is enabled (configured by setting eTDYNDirectory.eTDYN-str-multi-ca-01=1, because it has connectorMapTo=isStreaming in sdkdyn_metadata.xml). For an example, see com.ca.jcs.sdk.SDKSearchEnumeration in the CA IAM CS Javadoc in the CA IdentityMinder bookshelf.

    In some cases, it can make sense for your search method to support both normal search and streaming search mechanisms, in which case the ConnectorConfig.getStreamingQueryThreshold() threshold number which is configurable by connector.xml file can provide a useful comparison point. In particular, if the number of objects is bigger than the threshold number, you could use a streaming search resulting in higher scalability, otherwise you could use the nonstreaming search for possibly better runtime performance. Use of this threshold approach assumes there is a way to determine efficiently the number of results a search can possibly return or did actually return, or somehow tracking the total number of objects of a type that can exist based on a rough calculation during connector activate() and keeping running totals.

    The streaming search can make debugging and connection management more difficult, because the actual querying takes place after the doSearch() method has returned. If you plan to support both modes, it is recommended that you get the nonstreaming implementation working first. When debugging search problems, the following setting in cs-home /conf/log4j.properties can be useful:

    # When above setting is active, comment out if you want every search result logged (lots of output)  
    
    log4j.logger.org.apache.directory.server.ldap.support.SearchHandler.logEveryResult=ERROR
    

Note: If it is necessary to implement your own enumeration, then see the CA IAM CS Javadoc for these classes of interest: