You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by hl...@apache.org on 2003/09/11 22:52:05 UTC

cvs commit: jakarta-commons-sandbox/hivemind/xdocs interceptors.xml navigation.xml

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="hlship@apache.org">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/ServiceInterceptorFactory.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">InterceptorStack</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/AbstractServiceInterceptorFactory.html">AbstractServiceInterceptorFactory</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.AbstractServiceInterceptorFactory;
  
  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">ClassFab</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/AbstractLoggingInterceptor.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().getExtensionPointId());
  
          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.html">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>
  
  
  

RE: cvs commit: jakarta-commons-sandbox/hivemind/xdocs interceptors.xml navigation.xml

Posted by "Howard M. Lewis Ship" <hl...@comcast.net>.
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:hkrishnaswamy@comcast.net] 
> 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
> 
> hlship@apache.org 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="hlship@apache.org">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: commons-dev-unsubscribe@jakarta.apache.org
> >For additional commands, e-mail: commons-dev-help@jakarta.apache.org
> >
> >
> >  
> >
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
> 


Re: cvs commit: jakarta-commons-sandbox/hivemind/xdocs interceptors.xml navigation.xml

Posted by Harish Krishnaswamy <hk...@comcast.net>.
Got to tell you again, the amount of documentation you provide is 
amazing! Bravo!

-Harish

hlship@apache.org 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="hlship@apache.org">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/ServiceInterceptorFactory.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">InterceptorStack</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/AbstractServiceInterceptorFactory.html">AbstractServiceInterceptorFactory</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.AbstractServiceInterceptorFactory;
>  
>  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">ClassFab</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/AbstractLoggingInterceptor.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().getExtensionPointId());
>  
>          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.html">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: commons-dev-unsubscribe@jakarta.apache.org
>For additional commands, e-mail: commons-dev-help@jakarta.apache.org
>
>
>  
>