You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beehive.apache.org by be...@incubator.apache.org on 2004/11/09 15:46:32 UTC

[Apache Beehive Wiki] Updated: Controls/AnnotationBasedFeatures

   Date: 2004-11-09T06:46:31
   Editor: KenTam <ke...@apache.org>
   Wiki: Apache Beehive Wiki
   Page: Controls/AnnotationBasedFeatures
   URL: http://wiki.apache.org/beehive/Controls/AnnotationBasedFeatures

   no comment

Change Log:

------------------------------------------------------------------------------
@@ -1,44 +1,77 @@
-= Implementing Annotation-based Controls Programming Model Features =
+= Implementing Annotation-based Controls Programming Model Features Using Interceptors =
 
-Many interesting controls programming model features are annotation-based; the values of annotations on control interfaces and extensions drive significant runtime behaviour differences.  Examples of such features include async control operations via message buffering and many security features (run-as, role authorization enforcement, etc).
+Many interesting controls programming model features are annotation-based; the presence and values of annotations on control interfaces and extensions drive significant runtime behaviour differences.  Examples of such features include async control operations via message buffering and a variety of security features (run-as, role authorization enforcement, etc).
 
-This proposal describes an general mechanism for implementing such annotation-based features.
+This proposal describes a general interceptor mechanism for implementing such annotation-based features.
 
 == Summary ==
 
-Implementors are required to:
- * Define the annotations that control the desired feature.  Those annotations are meta-annotated with the @ControlAnnotation, which indicates to the infrastructure that this is a controls-related annotation, and specifies a JavaBeans contextual service interface that identifies implementations of the feature.
- * Provide at least one implementation of their contextual services, possibly specific to a particular container.  These implementations extend the InterceptorContext interface, which defines the contract that the controls runtime has with these feature implementations.
- * Register (via addService()) their contextual service implementations in the appropriate control container.  The base container (ControlContainerContext) may register some implementations of some of these features already; downstream container providers (for WLS, Tomcat, Geronimo, etc), may override and/or provide additional services.
-
-The controls infrastructure does the following:
- * The controls compilation process analyzes controls and code-gens implicit references to any contextual services required to support the features those controls use.  It also code-gens initialization for those implicit references.
- * The controls runtime invokes (via the InterceptorContext interface and the implicit references defined in 1) the contextual services at the appropriate times.  For example, when a method has annotations backed by a contextual service implementation, that implementation is called during the control operation's pre and post invoke stages.
+Provide the ability to associate a J''''''avaBeans service interface with an annotation to define its runtime feature behaviour.  These interfaces are treated as "interceptors" in the controls runtime, which will automatically instantiate and execute implementations of them at the appropriate execution points (pre/post invocation of a control operation, etc).
+
+Feature implementors are required to:
+ * Define a unique J''''''avaBeans service interface for the desired feature.  It must extend the {{{InterceptorContext}}} interface, which defines the contract that the controls runtime has with interceptors.
+ * Define the annotation(s) that control the desired feature.  Those annotations are meta-annotated with the {{{@InterceptorAnnotation}}}, which indicates to the infrastructure that this is a controls-related annotation, and specifies the  J''''''avaBeans service interface (declared above) that maps to implementation(s) of the feature.
+ * Provide an apt annotation processor that at minimum implements the check() method of the {{{TwoPhaseAnnotationProcessor}}} abstract class and enforces the compile-time semantics of the new annotation(s).  This annotation processor must be configured to be discoverable by apt during controls compilation.
+ * Provide at least one implementation of their J''''''avaBeans service interface, typically using features specific to a particular controls container environment.
+ * Register (via addService()) their service implementation in the appropriate control container.  The base container {{{ControlContainerContext}}} may register some implementations of some of these features already; downstream container providers (for WLS, Tomcat, Geronimo, etc), may override and/or provide additional services.
+
+Clients of the feature may then:
+ * Annotate control types (interfaces and extensions) with the newly defined annotations.  
+
+The controls compilation process does the following:
+ * Runs interceptor-related annotation processors, producing the appropriate diagnostics with respect to usage of interceptor-related annotations.
+ * Analyzes controls and code-gens (into the generated {{{ControlBean}}} class) implicit references to any interceptor services required to support the features those controls use, initialization code for those implicit references, and calls to the services based on the location(s) of the annotation(s) in the controls.  For example, if the interceptor-based annotation is annotating a control method, then there would be a code-generated call to that interceptor pre and post invocation of the actual impl method.
+
+The controls runtime:
+ * Provides support code (mostly in the {{{ControlBean}}} base class) to help manage interceptor lifecycle,
+persistence (ie, ensuring that the aren't serialized, but are re-init'ed on load), interceptor priority/ordering, and actual interceptor execution.
 
 == Details ==
 
-Let's consider @MessageBuffer as an example, and walk through the code involved.
+For the purposes of a detailed example, we'll define an annotation called {{{@MessageBuffer}}} and show how to associate it with a control interceptor to provide asynchronous buffering of control operation invocation.
 
-The interface that context service implementations must conform to:
+{{{InterceptorContext}}} -- the interface that interceptor services must extend:
 
 {{{
+package org.apache.beehive.controls.api.context;
+
 public interface InterceptorContext
 {
-    // Exact signature TBD
-    public preInvoke( Method m, Object [] args, ControlBeanContext cbc, ControlBean bean );
-    public postInvoke( Method m, Object [] args, ControlBeanContext cbc, ControlBean bean );
+    /* NOTE: Could have InterceptorContext extend InvokeListener to pick up preInvoke/postInvoke,
+             but the signatures on InvokeListener (rightly?) exclude ControlBeanContext */
+
+    /** Called before a control operation is invoked */
+    public preInvoke( Method m, Object [] args, ControlBeanContext cbc );
+    /** Called after a control operation is invoked */
+    public postInvoke( Method m, Object [] args, ControlBeanContext cbc );
+
+    /** Called before a control event is fired (through a client proxy) */
+    public preEvent( Class eventSet, Method m, Object [] args, ControlBeanContext cbc );
+    /** Called after a control event is fired (through a client proxy) */
+    public postEvent( Class eventSet, Method m, Object [] args, ControlBeanContext cbc );    
+
+    /** Called when a control impl instance is created */
+    public onControlCreate( ControlBeanContext cbc );
+    /** Called before a control (bean?) is persisted */
+    public onControlSerialize( ControlBeanContext cbc );
+    /** Called after a control (bean?) is read from persistent storage */
+    public onControlDeserialize( ControlBeanContext cbc );
 }
 }}}
 
-The MessageBuffer contextual service interface
+'''TBD:''' allow interceptor services to extend context eventsets?  For example, this would allow an interceptor service to extend {{{ControlBeanContext.LifeCycle}}} or {{{ResourceContext.ResourceEvents}}} and the controls framework would auto-register the impls as the appropriate listener(s).  This could get confusing though.. and what is the limitation on which eventsets behave in this manner?
+
+The {{{MessageBuffer}}} interceptor service interface
 
 {{{
+package org.apache.beehive.controls.api.context;
+
 public interface MessageBufferContext extends InterceptorContext
 {
 }
 }}}
 
-A particular implementation of the MessageBuffer contextual service.
+A particular implementation of the {{{MessageBuffer}}} interceptor service.
 
 {{{
 public class MessageBufferImpl implements MessageBufferContext
@@ -48,7 +81,7 @@
         //.. boilerplate
     }
 
-    public preInvoke( Method m, Object [] args, ControlBeanContext cbc, ControlBean bean )
+    public preInvoke( Method m, Object [] args, ControlBeanContext cbc )
     {
         // Thread-local check for whether we are being invoked on initial call (in which case
         // we enqueue) or on the post-dequeue call (in which case we do nothing)
@@ -71,72 +104,132 @@
 //
 }}}
 
-The meta-annotation used to identify annotations that have controls contextual services, and to
-provide the mapping to that service.
+{{{InterceptorAnnotation}}} -- meta-annotation used to identify annotations that are interceptor-based, and bind an interceptor service interface to those annotations:
 
 {{{
+package org.apache.beehive.controls.api.bean;
+
+@Documented
+@RetentionPolicy(RetentionPolicy.RUNTIME)
 @Target(ElementType.ANNOTATION_TYPE)
-public @interface ControlAnnotation
+public @interface InterceptorAnnotation
 {
-    Class<? extends InterceptorContext> contextService();
+    Class<? extends InterceptorContext> service();
 }
 }}}
 
-The MessageBuffer annotation.
+The {{{MessageBuffer}}} annotation that will be used by control authors:
 
 {{{
-@ControlAnnotation( MessageBufferContext.class )
-@Target(ElementType.METHOD)
+package org.apache.beehive.controls.api.bean;
+
+import org.apache.beehive.controls.api.context.MessageBufferContext;
+
+@InterceptorAnnotation( service=MessageBufferContext.class )
+@Documented
 @Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
 public @interface MessageBuffer
 {
     public boolean value() default true;
 }
 }}}
 
-A client (.jcx in this case) that uses @MessageBuffer.
+The annotation processor that will enforce the semantics of {{{MessageBuffer}}}:
 
 {{{
-@ControlExtension
-public interface MyWebService extends ServiceControl
+public class MessageBufferAnnotationProcessor extends TwoPhaseAnnotationProcessor
 {
-    @MessageBuffer
-    public String myWebOperation( int a );
+    // ...
+
+    public void check( Declaration decl )
+    {
+        // MessageBuffer allowed only on methods with void return type
+        if ( decl instanceof MethodDeclaration )
+        {
+            MethodDeclaration methodDecl = (MethodDeclaraton) decl;
+            if ( !(methodDecl.getReturnType() instanceof VoidType) )
+                printError( "Message buffered methods must return void" );
+        }
+
+        // ... more semantic checks
+    }
 }
 }}}
 
-Additional content generated into the ControlBean:
+A client (.jcx in this case) that uses {{{@MessageBuffer}}}.
 
 {{{
-public class MyWebServiceBean
+@ControlExtension
+public interface MyWebService extends ServiceControl
 {
-    //... section for generated references to implicit contextual services
-    
-    private InterceptorContext[] _implicitContexts;
-
-    // ...
+    @MessageBuffer
+    public void myWebOperation( int a );
 }
 }}}
 
-Additional content generated into the control impl initializer:
+Content generated into the C''''''ontrolBean:
 
 {{{
-public class ServiceControlImplInitializer
-{
-    //...
+public class MyWebServiceBean extends ControlBean
+{    
+    //
+    // For each control operation/event, code-gen a list of applicable interceptors
+    // as determined by the annotations on/applying to that operation.
+    //
+    // Use String instead of Class for late binding.
+    //
 
-    public initImplicitServices( ControlBean bean )
+    static
+    {
+        private String [] _myWebOperationInterceptors = 
+            { "org.apache.beehive.controls.api.context.MessageBufferContext" };
+
+        // Allow the container to impose a priority order on interceptor execution.
+        _myWebOperationInterceptors = ControlBeanContext.prioritizeInterceptors( _myWebOperationInterceptors );
+
+        // ... additional entries for each operation
+    }
+
+    //
+    // Each control operation/event already has a method that wraps invocation of the impl method.
+    // For interceptors on pre/post invoke, there's no additional codegen here required, the base class
+    // impl of pre/postInvoke() do the work and we just pass the list of applicable interceptors.
+    //
+
+    public void myWebOperation( int a )
     {
-        ControlBeanContext cbc = bean.getControlBeanContext();
-        // ... reflection setAccessible boilerplate
-        bean._implicitContexts[0] = ControlBeanContext.getService(MessageBufferContext.class);
-        if ( bean._implicitContexts[0] == null )
-            throw RequiredRuntimeServiceNotAvailableException( "MessageBufferContext service not provided by container" ); // could configure to catch & ignore, or fail
-        //
-        // bean._implicitContexts[1] = ControlBeanContext.getService(SecurityContext.class);
-        // ...
         // ...
+        
+        // Pass list of interceptors here
+        preInvoke(_myWebOperationMethod, _argArray, _myWebOperationInterceptors);
+        try
+        {
+            _target.myWebOperation(a);
+        }
+        catch (Throwable t)
+        {
+            //
+            // All exceptions are caught here, so postInvoke processing has visibility into
+            // the exception status.  Errors, RuntimExceptions, or declared checked exceptions will 
+            // be rethrown.
+            //
+            _thrown = t;
+            
+            if (t instanceof Error) throw (Error)t;
+            else if (t instanceof RuntimeException) throw (RuntimeException)t;
+            
+            throw new UndeclaredThrowableException(t);
+        }
+        finally
+        {
+            // Pass list of interceptors here
+            postInvoke(_retval, _thrown, _myWebOperationInterceptors);
+        }
+        return;
     }
+
+    // ...
 }
 }}}
 
@@ -160,41 +253,91 @@
 Downstream controls container service registration/override:
 
 {{{
-public class TomcatControlContainerContext extends ControlContainerContext
+public class MyJ2EEServerControlContainerContext extends ControlContainerContext
 {
     public initialize()
     {
         super.initialize();
 
         // If an existing provider has been registered by a base class, this overrides it
-        addService( MessageBufferContext.class, MessageBufferContextImpl.getProvider() );
+        addService( MessageBufferContext.class, PowerfulMessageBufferContextImpl.getProvider() );
     }
 }
 }}}
 
-Runtime invocation of interceptors:
+Runtime support for management and invocation of interceptors is mostly provided in the {{{ControlBean}}} class:
 
 {{{
 public class ControlBean
 {
-    //...
+    // ...
+
+    //
+    // HashMap to hold interceptor impl instances.
+    // Populated lazily.  Maps interceptor interface name to impl.
+    //
+
+    private final HashMap<String,InterceptorContext> _interceptors = new HashMap<String,InterceptorContext>();
+
+    // ...
+
+    //
+    // Retrieves interceptor instances, creates them lazily.
+    //
+
+    private InterceptorContext ensureInterceptor( String n )
+    {
+        InterceptorContext i = _interceptors.get( n );
+        if ( i == null )
+        {
+            i  = getService( getClassLoader().loadClass( n ) );
+            _interceptors.put( n, i );
+        }
+        return i;
+    }
+
+    // ...
     
-    protected void preInvoke( Method m, Object [] args )
+    protected void preInvoke( Method m, Object [] args, String [] interceptorNames )
     {
         //...
 
-        for ( InterceptorContext c : _implicitContexts )
-            c.preInvoke( m, args, getControlBeanContext(), this );
+        ControlBeanContext cbc = getControlBeanContext();
+
+        for ( String n : interceptorNames )
+        {
+            InterceptorContext i = ensureInterceptor( n );
+            i.preInvoke( m, args, cbc );
+        }
 
         //..
     }
+
+    // ... similarly for postInvoke, etc
 }
 }}}
 
+Ordering of interceptors is configured by {{{ControlBeanContext}}}:
+
+{{{
+public class ControlBeanContext
+{
+    public static String[] prioritizeInterceptors( String [] interceptorNames )
+    {
+        // obtain prioritization from external configuration (load a resource?)
+        // or hardcoding.  Process incoming interceptorNames array and return
+        // an array in the desired order.
+    }
+}
+}}}
+
+'''TBD:''' it may make sense to make this support non-static, and do it in the init/constructor for C''''''ontrolBeans -- this would make it easier to delegate to downstream containers and thus make it easier for the ordering to be container-specific, at the cost of having to do more bookkeeping (instance level instead of static).
+
 == Open Issues ==
 
  * Interceptor contract for events & event handlers?
- * Interaction between different interceptors (@Security & @MessageBuffer, which gets processed first?)
- * Signature of InterceptorContext methods not deeply thought out
+ * Pivot & error semantics for interceptors?
+ * How does container determine interceptor order?  Config file?  Config class?
+ * Signature of {{{InterceptorContext}}} methods not deeply thought out
  * ...