It's easier to do it from the get-go, rather than (like Tapestry) trying to loop back 
and do it
after the fact.

--
Howard M. Lewis Ship
Creator, Tapestry: Java Web Components
http://jakarta.apache.org/tapestry
http://jakarta.apache.org/commons/sandbox/hivemind/
http://javatapestry.blogspot.com

> -----Original Message-----
> From: Harish Krishnaswamy [mailto:[EMAIL PROTECTED] 
> Sent: Thursday, September 11, 2003 5:19 PM
> To: Jakarta Commons Developers List
> Subject: Re: cvs commit: 
> jakarta-commons-sandbox/hivemind/xdocs interceptors.xml navigation.xml
> 
> 
> Got to tell you again, the amount of documentation you provide is 
> amazing! Bravo!
> 
> -Harish
> 
> [EMAIL PROTECTED] wrote:
> 
> >hlship      2003/09/11 13:52:05
> >
> >  Modified:    hivemind/xdocs navigation.xml
> >  Added:       hivemind/xdocs interceptors.xml
> >  Log:
> >  Add documentation on creating new interceptors.
> >  
> >  Revision  Changes    Path
> >  1.19      +2 -1      
> jakarta-commons-sandbox/hivemind/xdocs/navigation.xml
> >  
> >  Index: navigation.xml  
> > ===================================================================
> >  RCS file: 
> > /home/cvs/jakarta-commons-sandbox/hivemind/xdocs/navigation.xml,v
> >  retrieving revision 1.18
> >  retrieving revision 1.19
> >  diff -u -r1.18 -r1.19
> >  --- navigation.xml 10 Sep 2003 16:54:52 -0000      1.18
> >  +++ navigation.xml 11 Sep 2003 20:52:05 -0000      1.19
> >  @@ -23,6 +23,7 @@
> >                     <item name="Localization" 
> href="/localization.html"/>
> >                     <item name="Inversion of Control" 
> href="/ioc.html"/>
> >                     <item name="Multi-Threading" 
> href="/multithreading.html"/>
> >  +                  <item name="Creating New Interceptors" 
> href="/interceptors.html"/>
> >                     <item name="Overriding Services" 
> href="/override.html"/>
> >                     <item name="Case Study #1: Application 
> Startup/Shutdown"
> >                             href="case1.html"/>
> >  
> >  
> >  
> >  1.1                  
> jakarta-commons-sandbox/hivemind/xdocs/interceptors.xml
> >  
> >  Index: interceptors.xml  
> > ===================================================================
> >  <?xml version="1.0"?>
> >  <!-- $Id: interceptors.xml,v 1.1 2003/09/11 20:52:05 
> hlship Exp $ -->  
> > <!DOCTYPE document [
> >     <!ENTITY % common-links SYSTEM "../common/links.xml">
> >     %common-links;
> >     ]>
> >  <document>
> >     <properties>
> >             <title>Creating New Interceptors</title>
> >             <author email="[EMAIL PROTECTED]">Howard M. 
> Lewis Ship</author>
> >     </properties>
> >     <body>
> >             <section name="Introduction">
> >  
> >  <p>
> >  Interceptors are used to add behavior to a HiveMind 
> service after the 
> > fact.  An interceptor sits between  the client code and the core 
> > service implementation; it implements the service 
> interface. For each method  in the service interface, the 
> interceptor will re-invoke the method on the
> >  next object in the chain ... either another interceptor, 
> or the core service implementation.   
> >  </p>
> >  
> >  <p>
> >  That's not useful ... but when the interceptor does 
> something before 
> > and/or after re-invoking the  method, it can easily add 
> quite a bit of 
> > useful, robust functionality.  </p>
> >  
> >  <p>
> >  In fact, if you've heard about "Aspect Oriented 
> Programming", interceptors are simply one kind of aspect, a 
> >  method introduction, based on service interface.   
> >  </p>
> >  
> >  <p>
> >  Be warned; interceptors are an example of programs writing 
> other programs; it's a whole new level of abstraction and
> >  requires a bit of getting used to. 
> >  </p>
> >  
> >  </section>
> >  
> >  <section name="Interceptor Factories">
> >     
> >  <p>
> >  Interceptors are created, at runtime, by interceptor 
> factories.  An interceptor factory builds a custom class at runtime
> >  using the Javassist library. The class is then instantiated.       
> >  </p>
> >  
> >  <p>
> >  Interceptor factories are HiveMind services which implement the
> >  <a 
> href="apidocs/org/apache/commons/hivemind/ServiceInterceptorFa
> ctory.html">ServiceInterceptorFactory</a>     
> >  interface. This interface has a single method, 
> > <code>createInterceptor()</code>, which is passed:  <ul>  
> <li>The <a 
> > 
> href="apidocs/org/apache/commons/hivemind/InterceptorStack.html">Inter
> > ceptorStack</a> (an object used to manage the process of creating 
> > interceptors for a service)</li>  <li>The <a 
> > href="apidocs/org/apache/commons/hivemind/Module.html">Module</a> 
> > which invoked the interceptor factory</li>  <li>A list of 
> > parameters</li>  </ul>  </p>
> >     
> >  <p>
> >  Like service implementation factories, interceptor 
> factories may take parameters; they may define a &parameters-schema;
> >  which is used to convert any XML enclosed by the 
> &interceptor; element into Java objects.      Many interesting
> >  interceptors can be created without needing parameters to 
> guide the fabrication of the interceptor class.
> >  </p>       
> >  
> >  </section>
> >  
> >  <section name="Implementing the NullInterceptor">
> >  
> >  <p>
> >  To demonstrate how easy it is to create an interceptor, 
> we'll start 
> > with a NullInterceptor.  NullInterceptor  does not add any 
> > functionality, it simply re-invokes each method on its 
> <em>inner</em>.  
> > The <em>inner</em> is  the next interceptor, or the core service 
> > implementation ... an interceptor doesn't know or care which.  </p>
> >  
> >  <p>
> >  Simple interceptors, those which do not take any parameters, are 
> > implemented by subclassing  <a 
> > 
> href="apidocs/org/apache/commons/hivemind/service/impl/Abstrac
> tServiceInterceptorFactory.html">AbstractServiceInterceptorFac
> tory</a>.
> >  It does most of the work, organizing the process of 
> creating the class and methods ... even adding a
> >  <code>toString()</code> method implementation automatically.
> >  </p>
> >  
> >  <subsection name="NullInterceptor Class">
> >  
> >  <p>
> >  Most of the work for creating a standard service 
> interceptor factory is taken care of by the 
> AbstractServiceInterceptorFactory
> >  base class.        All that's left is to define what 
> happens for each method in the service interface.
> >  
> >  <source><![CDATA[
> >  package com.example.impl;
> >  
> >  import java.lang.reflect.Modifier;
> >  
> >  import org.apache.commons.hivemind.service.BodyBuilder;
> >  import org.apache.commons.hivemind.service.ClassFab;
> >  import 
> > 
> org.apache.commons.hivemind.service.impl.AbstractServiceInterc
> eptorFactory;
> >  
> >  public class NullInterceptor extends 
> > AbstractServiceInterceptorFactory  {
> >  
> >      protected void addServiceMethodImplementation(
> >          ClassFab classFab,
> >          String methodName,
> >          Class returnType,
> >          Class[] parameterTypes,
> >          Class[] exceptionTypes)
> >      {
> >          BodyBuilder builder = new BodyBuilder();
> >  
> >          builder.begin();
> >          builder.add("return ($r) _inner.");
> >          builder.add(methodName);
> >          builder.add("($$);");
> >          builder.end();
> >  
> >          classFab.addMethod(
> >              Modifier.PUBLIC,
> >              methodName,
> >              returnType,
> >              parameterTypes,
> >              exceptionTypes,
> >              builder.toString());
> >      }
> >  
> >  }
> >  ]]></source>
> >  </p>       
> >  
> >  <p>
> >  The <code>addServiceMethodImplementation()</code>   method 
> is invoked for each service method.
> >  It is passed the
> >  <a 
> > 
> href="apidocs/org/apache/commons/hivemind/service/ClassFab.html">Class
> > Fab</a>, an object which represents a class being fabricated, which 
> > allows new fields, methods and constructors  to be added.  </p>
> >  
> >  <p>
> >  ClassFab and friends are just a wrapper around the Javassist 
> > framework, a library used for runtime bytecode enhancement 
> and  other 
> > aspect oriented programming tasks.  HiveMind uses only a small 
> > fraction of the capabilities of Javassist.  Javassist's greatest 
> > feature is how new code is specified ... as enhanced Java 
> code!  </p>
> >  
> >  <p>
> >  The 
> >  <a 
> href="apidocs/org/apache/commons/hivemind/service/BodyBuilder.
> html">BodyBuilder</a> class helps
> >  with assembling method bodies in bits and pieces.  The 
> > <code>_inner</code> variable is a private instance variable,  the 
> > inner for this interceptor.  The <code>($r)</code> reference means 
> > "cast to the return type for this method", and properly 
> handles  void 
> > methods.  The <code>$$</code> is a placeholder for a 
> comma-seperated list of all the parameters to the method.  </p>
> >  
> >  <p>AbstractServiceInterceptorFactory is responsible for 
> creating the <code>_inner</code> variable and
> >     building the constructor which sets it, as well as 
> invoking the constructor on the
> >     completed interceptor class.
> >     </p>
> >     
> >  </subsection>
> >  
> >  <subsection name="Declaring the Service">
> >  
> >  <p>
> >  To use a service, it is necessary to declare the service 
> in a module 
> > deployment descriptor.  The  
> AbstractServiceInterceptorFactory expects 
> > two properties to be set when the service is constructed,  
> > <code>extensionId</code> and <code>factory</code>:
> >  
> >  <source><![CDATA[
> >  
> >     <service id="NullInterceptor" 
> interface="org.apache.commons.hivemind.ServiceInterceptorFactory">
> >             <invoke-factory service-id="hivemind.BuilderFactory">
> >               <construct class="com.example.impl.NullInterceptor"
> >                     point-id-property="extensionId">
> >                     <set-service property="factory" 
> service-id="hivemind.ClassFactory"/>
> >               </construct>  
> >             </invoke-factory>
> >     </service>
> >  
> >  
> >  ]]></source>
> >  </p>
> >     
> >  </subsection>
> >     
> >  </section>
> >  
> >  <section name="Implementing the 
> hivemind.LoggingInterceptor service">
> >     
> >  <p>
> >  A more involved example is the LoggingInterceptor service, 
> which adds logging capabilities to
> >  services.  It's a bit more involved than NullInterceptor, 
> and so overrides more methods of
> >  AbstractServiceInterceptorFactory.
> >  </p>
> >  
> >  <subsection name="AbstractLoggingInterceptor base class">  <p>
> >  In most cases, an abstract base class for the interceptor 
> is provided; in this case, it is
> >  <a 
> href="apidocs/org/apache/commons/hivemind/service/impl/Abstrac
> tLoggingInterceptor.html">AbstractLoggingInterceptor</a>.
> >  This class provides several protected methods used by 
> fabricated interceptors.  To help ensure
> >  that there are no conflicts between the method of the 
> service interface and the methods
> >  provided by the super-class, the provided methods are 
> named with a leading underscore.  These methods are:
> >  <ul>
> >  <li><code>_logEntry()</code> to log entry to a method</li> 
> >  <li><code>_logExit()</code> to log exit from a method, 
> with return value</li>
> >  <li><code>_logVoidExit()</code> to log exit from a void 
> method (no return value)</li>
> >  <li><code>_logException()</code> to log an exception 
> thrown when the method is executed</li>
> >  <li><code>_isDebugEnabled()</code> to determine if 
> debugging is enabled or disabled</li>
> >  </ul>
> >  </p>
> >  
> >  <p>
> >  In addition, there's a protected constructor, which takes 
> an instance 
> > of <code>org.apache.commons.logging.Log</code>
> >  that must be invoked from the fabricated subclass.
> >  </p>
> >  
> >  <p>
> >  Method <code>getInterceptorSuperclass()</code>      is used to tell
> >  AbstractServiceInterceptorFactory which class to use as the base:  
> > <source>
> >      protected Class getInterceptorSuperclass()
> >      {
> >          return AbstractLoggingInterceptor.class;
> >      }
> >  </source>
> >  </p>
> >  
> >  </subsection>
> >  
> >  <subsection name="Creating the infrastructure">
> >     
> >  <p>
> >  The method <code>createInfrastructure()</code> is used to 
> add fields 
> > and constructors to the  interceptor class.  <source>
> >      protected void createInfrastructure(InterceptorStack 
> stack, ClassFab classFab)
> >      {
> >          Class topClass = stack.peek().getClass();
> >  
> >          classFab.addField("_inner", topClass);
> >  
> >          classFab.addConstructor(
> >              new Class[] { Log.class, topClass },
> >              null,
> >              "{ super($1); _inner = $2; }");
> >      }
> >  </source>
> >  </p>
> >  
> >  <p>
> >  Since, when a interceptor is created, the inner object has already 
> > been created, we can  use its <em>actual type</em> for the 
> > <code>_inner</code> field. This results in a much more  efficient 
> > method invocation than if <code>_inner</code>'s type was 
> the service 
> > interface.  </p>
> >     
> >  
> >  </subsection>
> >  
> >  <subsection name="Instantiating the Instance">
> >  
> >  <p>
> >  The method <code>instantiateInterceptor()</code> is used 
> to create a 
> > new instance  from the fully fabricated class.  <source>
> >      protected Object 
> instantiateInterceptor(InterceptorStack stack, Class interceptorClass)
> >          throws Exception
> >      {
> >          Object stackTop = stack.peek();
> >          Class topClass = stackTop.getClass();
> >  
> >          Log log = 
> > 
> LogFactory.getLog(stack.getServiceExtensionPoint().getExtensio
nPointId());
> >  
> >          Constructor c = 
> interceptorClass.getConstructor(new Class[] { 
> > Log.class, topClass });
> >  
> >          return c.newInstance(new Object[] { log, stackTop });
> >      }      
> >  </source>
> >  </p>
> >  
> >  <p>
> >  This implementation gets the top object from the stack 
> (the inner object for this
> >  interceptor) and the correct <code>Log</code>       
> instance (based on the service extension point id ...
> >  for the service being extended with the interceptor).  The 
> > constructor, created  by <code>createInfrastructure()</code> is 
> > accessed and invoked to create the interceptor.  </p>
> >     
> >  </subsection>
> >  
> >  <subsection name="Adding a Service Methods">
> >  
> >  <p>
> >  The last, and most complex, part of this is the    method 
> which actually creates each
> >  service method.
> >  
> >  <source><![CDATA[
> >      protected void addServiceMethodImplementation(
> >          ClassFab classFab,
> >          String methodName,
> >          Class returnType,
> >          Class[] parameterTypes,
> >          Class[] exceptions)
> >      {
> >          boolean isVoid = (returnType == void.class);
> >  
> >          BodyBuilder builder = new BodyBuilder();
> >  
> >          builder.begin();
> >          builder.addln("boolean debug = _isDebugEnabled();");
> >  
> >          builder.addln("if (debug)");
> >          builder.add("  _logEntry(");
> >          builder.addQuoted(methodName);
> >          builder.addln(", $args);");
> >  
> >          if (!isVoid)
> >          {
> >              
> builder.add(ClassFabUtils.getJavaClassName(returnType));
> >              builder.add(" result = ");
> >          }
> >  
> >          builder.add("_inner.");
> >          builder.add(methodName);
> >          builder.addln("($$);");
> >  
> >          if (isVoid)
> >          {
> >              builder.addln("if (debug)");
> >              builder.add("  _logVoidExit(");
> >              builder.addQuoted(methodName);
> >              builder.addln(");");
> >          }
> >          else
> >          {
> >              builder.addln("if (debug)");
> >              builder.add("  _logExit(");
> >              builder.addQuoted(methodName);
> >              builder.addln(", ($w)result);");
> >              builder.addln("return result;");
> >          }
> >  
> >          builder.end();
> >  
> >          MethodFab methodFab =
> >              classFab.addMethod(
> >                  Modifier.PUBLIC,
> >                  methodName,
> >                  returnType,
> >                  parameterTypes,
> >                  exceptions,
> >                  builder.toString());
> >  
> >          builder.clear();
> >  
> >          builder.begin();
> >          builder.add("_logException(");
> >          builder.addQuoted(methodName);
> >          builder.addln(", $e);");
> >          builder.addln("throw $e;");
> >          builder.end();
> >  
> >          String body = builder.toString();
> >  
> >          int count = exceptions == null ? 0 : exceptions.length;
> >  
> >          for (int i = 0; i < count; i++)
> >          {
> >              methodFab.addCatch(exceptions[i], body);
> >          }
> >  
> >          // Catch and log any runtime exceptions, in addition to the
> >          // checked exceptions.
> >  
> >          methodFab.addCatch(RuntimeException.class, body);
> >      }
> >  ]]></source>
> >  </p>       
> >     
> >  <p>
> >  When you implement logging in your own classes, you often 
> invoke the  
> > method <code>Log.isDebugEnabled()</code> multiple times ... 
> but in the 
> > fabricated class,  the method is only invoked once and 
> cached for the 
> > duration of the call ... a little efficiency gained back.  </p>
> >  
> >  <p>
> >  Likewise, if a method can throw an exception or return from the 
> > middle, its hard to be assured that you've logged  every 
> exit, or overy thrown exception; taking this code out into an 
> interceptor class ensures that
> >  its done consistently and properly.        
> >  </p>
> >     
> >  </subsection>
> >  
> >  </section>
> >  
> >  <section name="Implementing Interceptors with Parameters">
> >  
> >  <p>
> >  Interceptor factories may take parameters ... but then their 
> > implementation can't be based  on 
> AbstractServiceInterceptorFactory. 
> > The unit tests for HiveMind includes an  <a 
> > 
> href="xref-test/hivemind/test/services/impl/FilterLoggingInterceptor.h
> > tml">example</a> of  such a factory. The basic approach is the same 
> > ... you just need a little extra work to  validate, 
> interpret and use 
> > the parameters.  </p>
> >  
> >  <p>
> >  When would such as thing be useful?  One example is 
> declarative security; you could specify, on a
> >  method-by-method basis, which methods were restricted to 
> which roles.  
> >  </p>
> >     
> >  </section>
> >  
> >  <section name="Conclusion">
> >     
> >  <p>
> >  Interceptors, and interceptor factories, are a powerful 
> concept that 
> > allow you to add consistent, efficient,  robust behavior to your 
> > services. It takes a little while to wrap your brain around 
> the idea 
> > of  classes writing the code for other classes ... but once 
> you do, a 
> > whole world of advanced techniques  opens up to you!  </p>
> >  
> >  </section>
> >  
> >  
> >     </body>
> >  </document>
> >  
> >  
> >  
> >
> >---------------------------------------------------------------------
> >To unsubscribe, e-mail: [EMAIL PROTECTED]
> >For additional commands, e-mail: [EMAIL PROTECTED]
> >
> >
> >  
> >
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to