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.      |
*------------------------------------------------------* 
<?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>

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

Reply via email to