EclipseLink/DesignDocs/219683

From Eclipsepedia

Jump to: navigation, search

Design Specification: Interceptors

ER 219683

Document History

Date Author Version Description & Notes
2008-02-21 Gordon Yorke Working Draft
2008-05-05 Gordon Yorke Proposal

Project overview

This feature will introduce Interceptor "interfaces" that can be implemented by users to intercept certain internal EclipseLink component interactions. This will include cache access, query execution and others.

Goals:

  • Provide extension points that intercept internal EcliseLink operations instead of the current options of responding to operations through events.
  • Provide the abiliy for users to redirect these calls perhaps to other caches or to audit calls.

Concepts

Interceptors

Within EclipseLink logical operations like cache access or database writes flow naturally through different components. If points of intercept were added at the logical points of delegation within the EclipseLink architecture these operations could be intercepted and augmented or redirected. Developing these interception points and Interceptor interfaces increases the extensibility of EclipseLink beyond the scope of our current extensibility.

Requirements

Produce configurable interception points and interfaces for interception. The configuration should be simple while providing flexability.


Functionality

Interceptors

The Cache Interceptor

If a developer wishes to intercept EclipseLink's cache access the org.eclipse.persistence.sessions.interceptors.CacheInterceptor class should be extended and those methods that should be intercepted implemented. The user's implementation class should be set on the descriptor that should be intercepted. For Inheritance only set the class on the root of the tree as EclipseLink will store all subclasses in the same Cache. Once set the provided class will receive all caching calls. The existing EclipseLink cache settings will still be used and any calls allowed to continue to the EclipseLink cache will execute against the configured cache.

Query Interceptors

For Query Interception the existing Query Redirectors will be leveraged but the configuration is being updated to allow for default Query Redirectors to be set on the ClassDescriptor. Users will be able to set a default redirector or a Query class specific default redirector (ReadObjectQuery Redirector, ReadAllQuery Redirector, ReportQuery Redirector, InsertObjectQuery Redirector, UpdateObjectQuery Redirector). If a user has set a default redirector then only the more precise redirector will be called. For example if the user was to set both a default redirector and a default ReadObjectQuery redirector then when executing a ReadObjectQuery only the ReadObjectQuery redirector would be called.

There is a helper class org.eclipse.persistence.queries.QueryRedirectorHelper that provides a method for allowing developers of Redirectors to have access to some EclipseLink behaviour like checking the shared cache. Query redirectors preempt the entire query so checking the shared cache would have to requested by the redirector.

Design Constraints

Keep the implementation as generic as possible.

Config files

Configuration is completed through Session or Descriptor customizers using the EclipseLink meta-data APIs. Annotations and XML configuration will be implemented at a later date.

Documentation

Annotations:

/**
 * A CacheInterceptor can be set on an Entity and allows all EclipseLink cache
 * access to be intercepted by the developer provided interceptor. In the case
 * of inheritance, a CacheInterceptor annotation should only be defined on the
 * root of the inheritance hierarchy.
 * 
 * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor
 * 
 * @author Gordon Yorke
 * @since EclipseLink 1.0
 */
@Target({TYPE})
@Retention(RUNTIME)
public @interface CacheInterceptor {
    /**
     * The Class that will be used to intercept EclipseLink's cache access.
     */ 
    Class interceptorClass();
}
 
/**
 * Redirectors allow EclipseLink queries to be intercepted and pre/post processed or redirected.
 * They provide opportunities to extend query functionality beyond standard EclipseLink support.
 * 
 * @see org.eclipse.persistence.queries.QueryRedirector
 * 
 * @author Gordon Yorke
 * @since EclipseLink 1.0
 */
@Target({TYPE})
@Retention(RUNTIME)
public @interface QueryRedirectors {
 
    /**
     * This AllQueries Query Redirector will be applied to any executing object query
     * that does not have a more precise redirector (like the 
     * ReadObjectQuery Redirector) or a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     */
    Class allQueries() default void.class;
 
    /**
     * A Default ReadAll Query Redirector will be applied to any executing
     * ReadAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * For users executing a JPA Query through the getResultList() API this is the redirector that will be invoked
     *      */
    Class readAll() default void.class;
 
    /**
     * A Default ReadObject Query Redirector will be applied to any executing
     * ReadObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * For users executing a JPA Query through the getSingleResult() API or EntityManager.find() this is the redirector that will be invoked     */
    Class readObject() default void.class;
 
    /**
     * A Default ReportQuery Redirector will be applied to any executing
     * ReportQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * For users executing a JPA Query that contains agregate functions or selects multiple entities this is the redirector that will be invoked     */
    Class report() default void.class;
 
    /**
     * A Default Update Query Redirector will be applied to any executing
     * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query.
     * In EclipseLink an UpdateObjectQuery is executed whenever flushing changes to the datasource.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     */ 
    Class update() default void.class;
 
    /**
     * A Default Insert Query Redirector will be applied to any executing
     * InsertObjectQuery that does not have a redirector set directly on the query.
     * In EclipseLink an InsertObjectQuery is executed when persisting an object to the datasource.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     */
    Class insert() default void.class;
 
    /**
     * A Default Delete Object Query Redirector will be applied to any executing
     * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     */
    Class delete() default void.class;
 
}

New APIs on ClassDescriptor:

    /**
     * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction
     * between EclipseLink and the internal cache for that class will pass through the Interceptor.
     * Advanced users could use this interceptor to audit, profile or log cache access.  This Interceptor
     * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism.
     * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor.
     * 
     * As with IdentityMaps an entire class inheritance heirachy will share the same interceptor.
     * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor
     */
    public Class getCacheInterceptorClass()
 
    /**
     * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction
     * between EclipseLink and the internal cache for that class will pass through the Interceptor.
     * Advanced users could use this interceptor to audit, profile or log cache access.  This Interceptor
     * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism.
     * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor.
     * 
     * As with IdentityMaps an entire class inheritance heirachy will share the same interceptor.
     * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor
     */
    public String getCacheInterceptorClassName()
 
    /**
     * PUBLIC:
     * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction
     * between EclipseLink and the internal cache for that class will pass through the Interceptor.
     * Advanced users could use this interceptor to audit, profile or log cache access.  This Interceptor
     * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism.
     * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor.
 
     * As with IdentityMaps an entire class inheritance heirachy will share the same interceptor.
     * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor
     */
    public void setCacheInterceptorClass(Class cacheInterceptorClass)
 
    /**
     * PUBLIC:
     * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction
     * between EclipseLink and the internal cache for that class will pass through the Interceptor.
     * Advanced users could use this interceptor to audit, profile or log cache access.  This Interceptor
     * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism.
     * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor.
 
     * As with IdentityMaps an entire class inheritance heirachy will share the same interceptor.
     * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor
     */
    public void setCacheInterceptorClassName(String cacheInterceptorClassName)
    /**
     * A Default Query Redirector will be applied to any executing object query
     * that does not have a more precise default (like the default
     * ReadObjectQuery Redirector) or a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultQueryRedirector()
 
    /**
     * A Default Query Redirector will be applied to any executing object query
     * that does not have a more precise default (like the default
     * ReadObjectQuery Redirector) or a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultQueryRedirector(QueryRedirector defaultRedirector)
 
    /**
     * A Default ReadAllQuery Redirector will be applied to any executing
     * ReadAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultReadAllQueryRedirector()
 
    /**
     * A Default ReadAllQuery Redirector will be applied to any executing
     * ReadAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultReadAllQueryRedirector(QueryRedirector defaultReadAllQueryRedirector)
 
    /**
     * A Default ReadObjectQuery Redirector will be applied to any executing
     * ReadObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultReadObjectQueryRedirector()
 
    /**
     * A Default ReadObjectQuery Redirector will be applied to any executing
     * ReadObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultReadObjectQueryRedirector(QueryRedirector defaultReadObjectQueryRedirector)
 
    /**
     * A Default ReportQuery Redirector will be applied to any executing
     * ReportQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultReportQueryRedirector()
 
    /**
     * A Default ReportQuery Redirector will be applied to any executing
     * ReportQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultReportQueryRedirector(QueryRedirector defaultReportQueryRedirector)
 
    /**
     * A Default UpdateObjectQuery Redirector will be applied to any executing
     * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultUpdateObjectQueryRedirector()
 
    /**
     * A Default UpdateObjectQuery Redirector will be applied to any executing
     * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultUpdateObjectQueryRedirector(QueryRedirector defaultUpdateQueryRedirector)
 
    /**
     * A Default InsertObjectQuery Redirector will be applied to any executing
     * InsertObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultInsertObjectQueryRedirector()
 
    /**
     * A Default InsertObjectQuery Redirector will be applied to any executing
     * InsertObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultInsertObjectQueryRedirector(QueryRedirector defaultInsertQueryRedirector
    /**
     * A Default DeleteObjectQuery Redirector will be applied to any executing
     * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public QueryRedirector getDefaultDeleteObjectQueryRedirector()
 
    /**
     * A Default DeleteObjectQuery Redirector will be applied to any executing
     * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultDeleteObjectQueryRedirector(QueryRedirector defaultDeleteObjectQueryRedirector)
 
    /**
     * A Default Query Redirector will be applied to any executing object query
     * that does not have a more precise default (like the default
     * ReadObjectQuery Redirector) or a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultQueryRedirectorClassName(String defaultQueryRedirectorClassName)
 
    /**
     * A Default ReadAllQuery Redirector will be applied to any executing
     * ReadAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query exection preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultReadAllQueryRedirectorClassName(String defaultReadAllQueryRedirectorClassName)
 
    /**
     * A Default ReadObjectQuery Redirector will be applied to any executing
     * ReadObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultReadObjectQueryRedirectorClassName(String defaultReadObjectQueryRedirectorClassName)
 
    /**
     * A Default ReportQuery Redirector will be applied to any executing
     * ReportQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query execution preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
   public void setDefaultReportQueryRedirectorClassName(String defaultReportQueryRedirectorClassName)
 
   /**
    * A Default UpdateObjectQuery Redirector will be applied to any executing
    * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query.
    * Query redirectors allow the user to intercept query execution preventing
    * it or alternately performing some side effect like auditing.
    * 
    * @see org.eclipse.persistence.queryframework.QueryRedirector
    */
    public void setDefaultUpdateObjectQueryRedirectorClassName(String defaultUpdateObjectQueryRedirectorClassName)
 
    /**
     * A Default InsertObjectQuery Redirector will be applied to any executing
     * InsertObjectQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query exection preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultInsertObjectQueryRedirectorClassName(String defaultInsertObjectQueryRedirectorClassName)
 
    /**
     * A Default DeleteObjectQuery Redirector will be applied to any executing
     * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query.
     * Query redirectors allow the user to intercept query exection preventing
     * it or alternately performing some side effect like auditing.
     * 
     * @see org.eclipse.persistence.queryframework.QueryRedirector
     */
    public void setDefaultDeleteObjectQueryRedirectorClassName(String defaultDeleteObjectQueryRedirectorClassName)

Design

Cache Interceptors

To eliminate any performance impact on users who are not using interceptors a design pattern where an Abstract class that conforms to the IdentityMap interface was created that could be subclassed by developers who wished to implement an Interceptor. When an interceptor is set on a descriptor EclipseLink will instantiate both the configured IdentityMap and the Interceptor setting the IdentityMap within the Interceptor. Without an Interceptor there are no cycles spent checking for an Inteceptor. This Abstract Class exposes all of the Java public methods of the IdentityMap.

Query Redirectors

Entities, through their ClassDescriptors, can now be assigned default redirectors. Whenever a query is executed it will consult it's reference class's Descriptor to check for a default redirector. If this default redirector exists then EclipseLink will use this redirector to redirect the query.

Redirectors themselves have not been altered.

Open Issues

This section lists the open issues that are still pending that must be decided prior to fully implementing this project's requirements.

Issue # Owner Description / Notes

Decisions

This section lists decisions made. These are intended to document the resolution of open issues or constraints added to the project that are important.

Issue # Description / Notes Decision

Future Considerations

During the research for this project the following items were identified as out of scope but are captured here as potential future enhancements. If agreed upon during the review process these should be logged in the bug system.