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>