You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by le...@apache.org on 2002/11/22 22:48:51 UTC

cvs commit: jakarta-avalon-excalibur/interceptor/src/xdocs thoughts-on-interceptors-email-1.txt

leosimons    2002/11/22 13:48:51

  Added:       interceptor/src/xdocs thoughts-on-interceptors-email-1.txt
  Log:
  Some initial thoughts on interceptor architecture. Submitted by Peter Donald
  
  Revision  Changes    Path
  1.1                  jakarta-avalon-excalibur/interceptor/src/xdocs/thoughts-on-interceptors-email-1.txt
  
  Index: thoughts-on-interceptors-email-1.txt
  ===================================================================
  From peter@apache.org Fri Nov 22 13:29:33 2002
  Return-path:
  	<av...@jakarta.apache.org>
  Envelope-to: mail@leosimons.com
  Delivery-date: Fri, 22 Nov 2002 13:29:33 -0800
  Received: from [63.251.56.143] (helo=icarus.apache.org) by mx.mailix.net
  	with smtp (Exim 3.33 #1) id 18FLMj-0005YN-00 for mail@leosimons.com; Fri,
  	22 Nov 2002 13:29:33 -0800
  Received: (qmail 72882 invoked by uid 1291); 22 Nov 2002 21:29:24 -0000
  Delivered-To: leosimons@locus.apache.org
  Received: (qmail 72708 invoked from network); 22 Nov 2002 21:29:21 -0000
  Received: from daedalus.apache.org (HELO apache.org) (63.251.56.142) by
  	icarus.apache.org with SMTP; 22 Nov 2002 21:29:21 -0000
  Received: (qmail 11804 invoked by uid 500); 22 Nov 2002 21:29:21 -0000
  Delivered-To: apmail-leosimons@apache.org
  Received: (qmail 11714 invoked from network); 22 Nov 2002 21:29:19 -0000
  Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by
  	daedalus.apache.org with SMTP; 22 Nov 2002 21:29:19 -0000
  Received: (qmail 4443 invoked by uid 97); 22 Nov 2002 21:30:23 -0000
  Mailing-List: contact avalon-dev-help@jakarta.apache.org; run by ezmlm
  Precedence: bulk
  List-Unsubscribe: <ma...@jakarta.apache.org>
  List-Subscribe: <ma...@jakarta.apache.org>
  List-Help: <ma...@jakarta.apache.org>
  List-Post: <ma...@jakarta.apache.org>
  List-Id: "Avalon Developers List" <avalon-dev.jakarta.apache.org>
  Reply-To: "Avalon Developers List" <av...@jakarta.apache.org>
  Delivered-To: mailing list avalon-dev@jakarta.apache.org
  Received: (qmail 4406 invoked by uid 98); 22 Nov 2002 21:30:23 -0000
  X-Antivirus: nagoya (v4218 created Aug 14 2002)
  From: Peter Donald <pe...@apache.org>
  To: "Avalon Developers List" <av...@jakarta.apache.org>
  Subject: Re: On Multiple Containers
  Date: Sat, 23 Nov 2002 08:38:17 +1100
  User-Agent: KMail/1.4.2
  References: <NB...@devtech.com>
  In-Reply-To: <NB...@devtech.com>
  X-Notice: Duplication and redistribution prohibited without consent of the
  	author.
  X-Copyright: (C) 2002 Peter Donald.
  X-Wisdom: A right is not what someone gives you; it's what no one can take
  	from you.
  MIME-Version: 1.0
  Content-Type: Multipart/Mixed; boundary="------------Boundary-00=_TRYZD154NMRHB48CF3GE"
  Message-Id: <20...@apache.org>
  X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N
  X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N
  X-Spam-Rating: icarus.apache.org 1.6.2 0/1000/N
  X-Evolution-Source:
  	pop://leosimons.com%40leosimons.com@pop.namezero.com/inbox
  
  
  --------------Boundary-00=_TRYZD154NMRHB48CF3GE
  Content-Type: text/plain; charset="iso-8859-1"
  Content-Transfer-Encoding: 8bit
  
  On Sat, 23 Nov 2002 06:00, Noel J. Bergman wrote:
  > > I actually think it is closer now than at any time before because we have
  > > found which approaches don't work
  >
  > Good, then you should be willing to help avoid the past errors --
  > constructively.  But it is not constructive to say "that doesn't work"
  > without offering a constructive suggestion about what might work.  It might
  > be hard, but it is the right thing to try.
  
  The problem is that we don't know what works - we just know what doesn't work. 
  And even if it doesn't work - sometimes people need to see it for themselves 
  and sometimes practical realities creep in. 
  
  For example - prior to our first release we were deciding on a number of 
  things. For example whether ComponentSelectors should exist, whether marker 
  interfaces should be used for metadata, whether marker interfaces should be 
  used for concepts etc.
  
  It was pointed out how they would suck and why they sucked but 3/4 of 
  committers disagreed so we adopted them (or to be more accurate Cocoon 
  adopted them). Two years later most people think they suck and they will be 
  deprecated as soon as there is a migration strategy.
  
  The same thing will happen again and again. I think the lifecycle stuff bites 
  as it has been done before and we spent the good part of a year backpedalling 
  - some remanents remain. I also outlined the strategy that would work IMO (ie 
  interceptors). However those who were implementing it didn't believe me so I 
  predict that history will repeat - though I could be wrong ;)
  
  People must be allowed to do things and experiment in different ways - even if 
  you consider them mistakes. Everytime in the past when we have tried to 
  create an uber container we have come across problems - the next attempt 
  didn't have the same problems (but usually had a different set of em). It may 
  be that when we try the same strategies again it may work.
  
  > > Combine that with compiled interceptor chains and
  > > we have a viable container IMO that satisfies all
  > > our needs.
  >
  > Now you just need to explain that to the class ( :-) ), and illustrate the
  > role it plays in addresses the disparate needs, and why everyone should
  > follow your line of reasoning.  It provides a realm of constructive debate.
  
  It is scattered across a bunch of lists and emails. I will try to write up a 
  basic summary sometime soon and maybe create a strawman implementation. 
  However in the meantime I have attached a doc I wrote a while back when I was 
  prototyping a forrest site. It is not accurate or complete or even 
  representative of the ideas I currently hold but it gives a good background. 
  There is also some of the stuff I added into bugzilla. See
  
  http://nagoya.apache.org/bugzilla/show_bug.cgi?id=12405
  
  However the best source is probably to go look at it in action. See the way 
  JBoss/other ejb servers handle things or how  nanning (or whatever "aspect" 
  stuff was that recently got added to commons). Almost all mature "enterprise" 
  architectures have it in some way (ie Servlets have filters, CORBA has its 
  version of interceptors, etc).
  
  Why do I think it rocks?
  
  Well all the concerns relating to all our desires become implementable as a 
  simple interceptor. We get rid of all those concerns we mix (ie Using CM as a 
  resource manager via ComponentManager.release()). We satisfy everyones needs 
  for remoting, transparency, lifecycle extensions - you name it.
  
  The negative of interceptors being that they add overhead to each and every 
  call. This overhead can be minimized and wont have a significant effect on 
  components written for things like Phoenix but it will have a significant 
  effect on the way things like Cocoon operates because the overhead would not 
  be acceptable in these cases.
  
  > > Now [component info] can be cross container relatively easy.
  > > [Component assembly] is likely to be about 70% portable
  > > between containers
  > > [component runtime state] will most likely be completely
  > > container dependent.
  >
  > You've just pointed out more code that you think can be shared, and a few
  > where you think there will be differences.  These points can be discussed,
  > and acted upon as the community agrees.  And the third point can be
  > discussed separately to see if there really might be more commonality than
  > you currently perceive.  But first I would start with the low-hanging
  > fruit, and get to the harder ones as more and more code merges.
  >
  > With respect to the assembly issues, perhaps the 30% that isn't shared can
  > be abstracted behind pluggable interfaces?  I don't know. 
  
  Not by interfaces but it should be possible to do via extension. This was 
  mostly discussed back in August. Basically it comes down to how to handle 
  different "layered" components which are usually "typed". ie Interceptors are 
  initialized before other components. Listeners are also initialized before 
  other components.
  
  > > After that stage I think we just let time & darwin do it's job.
  >
  > Nope.  And you're really using the wrong example with me in the audience
  > for this debate.  I don't think that random mutation and natural selection
  > is the answer to this problem at all.  Ideas may compete within the
  > community, but the community must engage in consciously design.  If there
  > is a communal desire to test ideas, then those need to be isolated
  > modularly from the rest of the shared base, the same as any other area of
  > differing functionality. And in this area I do invoke the notion of
  > evolution: there will likely be different morphologies to satisfy different
  > niches.  But a beak is still a beak.
  
  I am not sure what you are advocating but I think it is the same as what I 
  want. Think of the IETF or OpenGL arb based approach. They define a core 
  standard. Then there may be a set of "approved" extensions and then a set of 
  "experimental" or vendor specific extensions. If Leo commits the patch I sent 
  you will be able to see it detailed in a document.
  
  Basically it comes down to something like the following. We define extensions 
  - containers or groups or individuals can do it. After a period of testing (6 
  months?) in real containers they may be revised and voted upon to be accepted 
  as "approved" extensions. In some cases we may decide to promote an 
  "approved" extension to core.
  
  So say I wanted to define an extension to represent Phoenix ability to export 
  to JMX/management systems. First I would name it; x-mx if others where 
  involved or else phoenix-mx if it was phoenix only. Then I would define the 
  set of javadoc tags/attributes it supported (like 
  http://jakarta.apache.org/avalon/phoenix/mx/xdoctags.html) and the general 
  concept docs (like http://jakarta.apache.org/avalon/phoenix/mx). 
  
  At which point it would become an "expermental" extension. After six months of 
  deployment its status will be re-evaluated and it may get promoted to a 
  "approved" extension (with minor modifications if necessary) using new name 
  "mx".
  
  I doubt it would ever be promoted to the core but if it did then we may wait 
  another 6 months to a year and then get it to be part of core (and thus 
  supported by all containers).
  
  
  -- 
  Cheers,
  
  Peter Donald
  *------------------------------------------------------*
  | Despite your efforts to be a romantic hero, you will |
  | gradually evolve into a postmodern plot device.      |
  *------------------------------------------------------* 
  
  --------------Boundary-00=_TRYZD154NMRHB48CF3GE
  Content-Type: text/xml; charset="iso-8859-1"; name="interceptor.xml"
  Content-Disposition: attachment; filename="interceptor.xml"
  Content-Transfer-Encoding: 8bit
  
  <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "document-v11.dtd">
        <document> 
          <header> 
            <title>Dynamic Interceptor Chains</title> 
          </header> 
          <body> 
            <section>
              <title>Introduction</title>
              <p>
                Interceptors are objects that sit between the implementation of a method 
                and the interface via which that method is called. is An Interceptor is a 
                component through which a call to a method will pass. The method invocation 
                will first pass from Caller to the Interceptor and then from the Interceptor 
                to the Target method and then back through the Interceptor to the Caller. 
              </p>
              <p>
                An Interceptor Chain or Stack is a series of interceptors through which 
                an invocation will pass on way down to the method. After the method 
                completes the invocation will pass back through the chain of Interceptors 
                in the reverse order of which they were called. 
              </p>
              <p>
                Figure 1 displays such a situation. The Caller invokes a method, it 
                passes through several Interceptors before invoking the Target method and 
                then it passes back through all the Interceptors to the Caller.
              </p>
              <figure src="images/interceptor.png" 
                      alt="An Interceptor Chain" 
                      width="454" height="340"/>
              <p>
                An Interceptor can be called simultaneously by multiple threads and by 
                multiple clients. Thus information pertaining to the particular call 
                needs to be stored either in ThreadLocal variables (if it does not need 
                to be shared) or in the InvocationContext (if it may need to be accessed 
                by other Interceptors). The InvocationContext is where all the 
                information relating to a particular call is stored. The Context may also
                give access to information in different scopes (such as per object or
                per session).
              </p>
            </section>
            <section>
              <title>Basic Example</title>
              <p>
                A basic Interceptor is shown in figure 2. It gets the time before and 
                after the call and displays the duration of the call to standard output. 
                It demonstrates the basic format of an Interceptor. Usually an 
                Interceptor will execute some operations before a call is made on Target 
                method and after a call is made.
              </p>
              <source>
  public class MyTimingInterceptor 
    implements Interceptor
  {
    public Object invoke( Invocation invocation, 
                          InvocationContext ctx, 
                          InvocationChain chain )
    {
      final long start = System.currentTimeInMillis();
      final Object result = chain.invokeNext( invocation, ctx );
      final long end = System.currentTimeInMillis();
      System.out.println( "Invocation duration: " + (end - start) );
  
      return result;
    }
  }
              </source>
            </section>
            <section>
              <title>Context Using Example</title>
              <p>
                Another example is shown in figure 2. It sets the ContextClassLoader 
                prior to calling the method and then resets it to original value before 
                returning to caller. Note that this assumes that the Target method is in 
                the same thread as the Interceptor which will be the case unless a 
                Interceptor later in the chain changes threads. This Interceptor also 
                demonstrates that values can be retrieved from the InvocationContext. The 
                specific values that are available to an Interceptor are determined by 
                the host application server. In the case of Phoenix see X.
              </p>
              <source>
  public class MyClassLoaderInterceptor 
    implements Interceptor
  {
    public Object invoke( Invocation invocation, 
                          InvocationContext ctx, 
                          InvocationChain chain )
    {
      //Retrieve the ClassLoader object from context. 
      //Note that the set of keys and values in context is 
      //container dependent. See Container documentation for relevent 
      //set of attributes that are valid
      final ClassLoader classLoader = 
            (ClassLoader)ctx.get( "classLoader" );
      final ClassLoader oldClassLoader = 
  	  Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader( classLoader );
  
      final Object result = chain.invokeNext( invocation, ctx );
  
      Thread.currentThread().setContextClassLoader( oldClassLoader );
  
      return result;
    }
  }
              </source>
            </section>
            <section>
              <title>Constructing Interceptor Chains</title>
              <p>
                There are numerous policies via which Interceptor chains could be created.
                One such mechanism is to construct an interceptor chain based on particular
                objects <link href="attribute.html">Attributes</link>. Other policies include
                constructing chains in preconfigured arrangments or by using configuration 
                files such as;
              </p>
              <source><![CDATA[
  <interceptor-chains>
  
  <intercetor-chain name="MyInterceptorChain">
  
    <!-- Log the call for debugging purposes -->
    <interceptor type="org.apache.avalon.LogInterceptor"/>
  
    <!-- Make sure the call is authorized to execute method 
         and that correct principle has been setup. -->
    <interceptor type="org.apache.avalon.AuthorizeInterceptor"/>
  
    <!-- Charge caller for use of the service -->
    <interceptor type="org.apache.avalon.AccountingInterceptor">
      <!-- configuration passed to the Interceptor. It costs 
           2c per call -->
      <cost>0.02</cost>
    </interceptor>
  
    <!-- Make sure the ThreadContext data (like ContextClassLoader) 
         is setup properly -->
    <interceptor type="org.apache.avalon.ThreadContextInterceptor"/>
  
   </intercetor-chain>
  
  </interceptor-chains>
              ]]></source>
            </section>
            <section>
              <title>Example Interceptors</title>
              <p>
                <strong>Transaction</strong>: Manage transaction state in a way similar to EJB
                declarative transaction "attributes". May have mandatory, incompatible etc and 
                can result in commit or rollback on failure etc.
              </p>
              <p>
                <strong>Security</strong>: Make sure the caller has the right permissions,
                the caller has principle correctly setup and the method is invoked as correct subject.
              </p>
              <p>
                <strong>Audit</strong>: Record who, when, what and where a method is called.
              </p>
              <p>
                <strong>Application Isolation</strong>: Make sure caller context does not interfere with
                context of called method. This includes managing things like thread names, ContextClassLoader 
                etc.
              </p>
              <p>
                <strong>Stale References</strong>: Make sure stale references are not used. ie If an object has 
                been disposed of make sure that no one trys to call the object again.
              </p>
              <p>
                <strong>Pool Objects</strong>: Objects may be pooled with a particular policy. ie The target object 
                may be retrieved from a pool prior to method invocation and then returned to pool after invocation.
              </p>
              <p>
                <strong>Passivate/Activate Objects</strong>: Objects may be passivated (serialized to disk) if not used
                recently and then activated (deserialized from disk) when needed.
              </p>
              <p>
                <strong>Lazy Creation</strong>: Make sure objects are created and properly initialized before 
                they can be accessed.
              </p>
              <p>
                <strong>Binding Objects</strong>: Bind objects into a name service or registry (ie JMX, JNDI, LDAP, 
                RMI registry etc) the first time they are accessed.
              </p>
              <p>
                <strong>Remoting Objects</strong>: Make sure object is remoted via RMI, SOAP, AltRMI.
              </p>
              <p>
                <strong>Sub-Component Activator</strong>: Make sure that the first time a component is accessed,
                that all it's sub-components are activated.
              </p>
            </section>
          </body>
      </document>
  
  
  --------------Boundary-00=_TRYZD154NMRHB48CF3GE
  Content-Type: text/plain; charset=us-ascii
  Content-Transfer-Encoding: 8bit
  
  --
  To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
  For additional commands, e-mail: <ma...@jakarta.apache.org>
  --------------Boundary-00=_TRYZD154NMRHB48CF3GE--
  
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>