Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The framework has it's own Filter implementation which is really strict/limited. The OR/AND Filters are organised into a binary tree and the Filter implementation itself is force the case sensitive comparison which is incompatible with most of the resource. I suggest a more flexible implementation with Visitor pattern instead of a FilterTranslator where the framework wants to do too much. See QueryFilter

...

See below the proposed API samples:

What's new:

New QueryResultHandler class has two methods, #handleResource to handle the ConnectorObject and the #handleResult to signal the Framework the operation is finished.
The #handleError method is https://connid.atlassian.net/wiki/display/BASE/Exception+Management to discuss
In 1.1 if the operation is finished then it cut's the QueryResultHandler so if the third party library supports the async query the dispatcher thread must be blocked. Now the call of #handleError or #handleResult would finish the operation and not block the dispatcher thread.

New SortKey to which can be used to specify the order in which ConnectorObject should be handled by the QueryResultHandler
Modify the OperationOptions or use the QueryOptions to support the pagination.

The #executeQuery(QueryFilter query, QueryResultHandler handler, QueryOptions options) method does not have the ObjectClass to support the polymorphic query. The QueryFilter use the Visitor patter and no need for the Query Translator any more. We need to support Case-Sensitive and Case-Insensitive search and the Framework can provide two Visitor implementation to do so and a third one to do a SchemaAware filtering if we add to the AttributeInfo the case-sensitive flag.

As we provide these two implementation the connector implementor can implement its own Visitor to check the Resource object before handle it or as now the FilterTranslator it can translate to NativeQuery. It gives more freedom to the Connector Developer to select any of these options and make simpler implement then now.

...

languagejava

...

Common Pagination Options

The Search will be able to utilise four new OperationOptions.

Code Block
languagejava
titleOperation Options
    /**
     * An option to use with {@link SearchApiOp} that specifies an opaque cookie
     * which is used by the resource providerconnector to
         *         track its position in the set of query results,
or          *         {@code null} if paged results are not requested (when the
         *         page size is 0), or if the first page of results is being
         *         requested (when the page size is non-zero).
         * @see #getPageSize()
  
      * @see #getPagedResultsOffset()
         */
    public static  final String getPagedResultsCookie() {
            return "1234";
        }OP_PAGED_RESULTS_COOKIE = "PAGED_RESULTS_COOKIE";
        /**
         * ReturnsAn the index within the result set of the first result which
         * should be returned. Paged results will be enabled if and only if the
         * page size is non-zero. If the parameter is not present or a value
         * less than 1 is specified then then the page following the previous
         * page returned will be returned. A value equal to or greater than 1
         * indicates that a specific page should be returned starting from the
 option to use with {@link SearchApiOp} that specifies the index within
       * position specified.
         *
         * @return The index within the result set of the first result which
         *         should be returned.
         * @see #getPageSize()
         * @see #getPagedResultsCookie()
         */
    public static final  int getPagedResultsOffset() {
            return 0;
String OP_PAGED_RESULTS_OFFSET = "PAGED_RESULTS_OFFSET";
       };
        /**
   
     * ReturnsAn theoption requestedto page results page size or {@code 0} if paged
         * results are not required. For all paged result requests other than
         * the initial request, a cookie should be provided with the query
         * request. Seeuse with {@link #getPagedResultsCookie()SearchApiOp} forthat morespecifies information.the requested
        *
         * @return The requested page results page size or.
{@code 0} if paged          **/
    public static final String results are not required.OP_PAGE_SIZE = "PAGE_SIZE";
          * @see #getPagedResultsCookie()
         * @see #getPagedResultsOffset()/**
           */ An option to      int getPageSize()use with {
            return 100;
        };
        /**
         * Returns@link SearchApiOp} that specifies the sort keys which should be used for ordering the JSON
         * resources returned by this query request. The returned list may be
         * modified if permitted by this query request.
   
     *
         * @return The sort keys which should be used for ordering the JSON
         *         resources returned by this query request (never {@code null}
         *         ).
         */
        List<SortKey> getSortKeys() {
            return Collections.emptyList();
        };
    }
    /**
     * ConnectorFacade calls this method once for each native query.
     *
     * @param query
     *            The native query to run. A value of null means
     *            "return every instance of the given object class".
     * @param handler
     *            Results should be returned to this handler
     * @param options {@link ConnectorObject} returned by
     *            Additional options that impact the way this operationsearch is runrequest.
     *            If the caller passes null, the framework will convert this
     *            into an empty set of options, so SPI need not guard against
     *            options being null.
     */
    public voidstatic executeQuery(QueryFilter query, QueryResultHandler handler, QueryOptions options);
}

...

It allows to do a multi ObjectClass syncronization and because the OperationOptions is extended with the pageSize sortKey it allows to customise more the orders or related events. (Sounds too extreme but I have real use-case where is would solve the problem)

To allow simple migration from 1.1 to 1.4 There will be a util method:

...

final String OP_SORT_KEYS = "SORT_KEYS";

 

Search Operation can return with SearchResult

The final result of a query request returned after all connector objects matching the request have been returned. In addition to indicating that no more objects are to be returned by the search, the search result will contain page results state information if result paging has been enabled for the search.

Code Block
languagejava
titlePaged Search Implementation
    /**    String cookie * Extracts the ObjectClass, SyncToken pairs from the filter parameter.
     *
     * This util method in FrameworkUtils gets the ObjectClass, SyncToken form
     * the query and the old 1.1 API can be used still.
     *
     * @param filter
     * @return
     */
    public Set<Pair<ObjectClass, SyncToken>> extract(QueryFilter filter);
Code Block
languagejava
package org.identityconnectors.framework.spi.operations;
import java.util.Map;
import java.util.Set;
import org.identityconnectors.common.Pair;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.ResultHandler;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.filter.QueryFilter;
/**
 * Poll for synchronization events--i.e., native changes to target objects.
 *
 */
public interface SynchronizeOp extends SPIOperation = options.getPagedResultsCookie();
        for (int i = 0; i < 100; i++ ) {
    /**
     * A completion handler for consuming the results of a synchronization
     * request.
     * <p>
     * A synchronization result completion handler may be specified when
     * performing synchronization requests using a
     * {@link org.identityconnectors.framework.api.ConnectorFacade} object. The
     * {@link #handleSyncDelta} method is invoked each time a matching
     * {@link SyncDelta} resource is returned, followed by {@link #handleResult}
     * or {@link #handleError} indicating that no more ConnectorObject resources
     * will be returned.
     * <p>
     * Implementations of these methods should complete in a timely manner so as
     * to avoid keeping the invoking thread from dispatching to other completion
     * handlers.
     */
    public interface SyncResultHandler extends ResultHandler<SyncToken> {
        /**
         * {@inheritDoc}
         */
        void handleError(RuntimeException error.handle(results[i]);
        /**}
         * Invoked each time a matching ConnectorObject is returned from a query
         * request.
         *
         * @param resource
         *            The matching ConnectorObject.
         * @return {@code true} if this handler should continue to be notified
         *         of any remaining matching ConnectorObjects, or {@code false}if (handler instanceof SearchResultsHandler) {
          *         if the remaining ConnectorObjects should be skipped for some
         *         reason (e.g. a client side size limit has been reached).
         */
        boolean handleSyncDelta(SyncDelta resource);
        /**
         * {@inheritDoc}
         *
         * @param result
         *            The query result indicating that no more resources are to
         *            be returned and, if applicable, including information
         *            which should be used for subsequent paged results query
         *            requests.
         */
        void handleResult(SyncToken result);
    }
    /**
     * Request synchronization events--i.e., native changes to target objects.
     * <p>
     * This method will call the specified
     * {@linkplain org.identityconnectors.framework.common.objects.SyncResultsHandler#handle
     * handler} once to pass back each matching
     * {@linkplain org.identityconnectors.framework.common.objects.SyncDelta
     * synchronization event}. Once this method returns, this method will no
     * longer invoke the specified handler.
     * <p>
     * Each
     * {@linkplain org.identityconnectors.framework.common.objects.SyncDelta#getToken()  ((SearchResultsHandler)handler).handleResult(new SearchResult(cookie, 100)); 
    * synchronization event contains a token} that can be used to resume
     * reading events <i>starting from that point in the event stream</i>. In
     * typical usage, a client will save the token from the final
     * synchronization event that was received from one invocation of this
     * {@code sync()} method and then pass that token into that client's next
     * call to this {@code sync()} method. This allows a client to
     * "pick up where he left off" in receiving synchronization events. However,
     * a client can pass the token from <i>any</i> synchronization event into a
     * subsequent invocation of this {@code sync()} method. This will return}

 

Sync Operation can return with the latest SyncToken

The operation should return with the latest sync token even if there is no change to avoid to iterate over and over again the irrelevant changes.

Code Block
languagejava
titleSync Implementation
        *for synchronization events (thatSyncDelta represent native changes that occurred)
     * immediately subsequent to the event from which the client obtained the
     * token.
     * <p>
     * A client that wants to read synchronization events "starting now" can
     * call {@link #getLatestSyncToken} and then pass that token into this
     * {@code sync()} method.
     *
     * @param filter
     *            The filter which used to select which ConnectorObject should
     *            be included. It includes the ObjectClass and the Token pair
     *            where token representing the last token from the previous
     *            sync. The {@code SyncResultsHandler} will return any number ofdelta: Collections.<SyncDelta>emptyList()) {
           *            {@linkplain org.identityconnectors.framework.common.objects.SyncDelta}
     *            objects, each of which contains a token. Should be
     *            {@code null} if this is the client's first call to the
     *            {@code sync()} method for this connector.
     * @param handler
     *      handler.handle(delta);
      The result handler. Must not be null.
     * @param options
     *            Options that affect the way this operation is run. If the
     *            caller passes {@code null}, the framework will convert this
     *            into an empty set of options, so an implementation need not
     *            guard against this being null.
  
  * @throws IllegalArgumentException      *             if {@code objectClass} or {@code handler} is null or if any
     *             argument is invalid.
     */
    public void sync(QueryFilter filter, SyncResultsHandler handler, OperationOptionsinstanceof optionsSyncTokenResultsHandler);   {
 /**      * Returns the token corresponding to the most recent synchronization event.
     * <p>
     * An application that wants to receive synchronization events
     * "starting now" --i.e., wants to receive only native changes that occur
     * after this method is called-- should call this method and then pass the
     * resulting token into {@linkplain #sync the sync() method}.
     *
     * @param objectClass
     *            the class of object for which to find the most recent
     *            synchronization event (if any). Must not be null.
     * @return A token if synchronization events exist; otherwise {@code null}.
     * @throws IllegalArgumentException
     *             if {@code objectClass} is null or is invalid.
     */ ((SyncTokenResultsHandler)handler).handleResult(lastToken);
       public SyncToken getLatestSyncToken(ObjectClass objectClass);
}

 

 

...