avalon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From leosu...@apache.org
Subject cvs commit: jakarta-avalon-excalibur/microcontainer/src/etc overview.html
Date Thu, 27 Jun 2002 09:46:06 GMT
leosutic    2002/06/27 02:46:06

  Added:       microcontainer/src/etc overview.html
  Log:
  no message
  
  Revision  Changes    Path
  1.1                  jakarta-avalon-excalibur/microcontainer/src/etc/overview.html
  
  Index: overview.html
  ===================================================================
  <body> 
      <p>MicroContainer - a container designed to work in any environment and with
      a minimum of committment to Avalon architecture. </p>
      
      <h3>Overview / Purpose</h3>
      
      <p>One of the barriers of entry to Avalon is the fact that you are buying
      into an entire architecture. Instead of just getting a set of classes
      that you can use as you were used to, you get a set of Avalon Components
      that must be put in a container and managed. Often this involves XML config
      files and other niceties that, while providing amazing flexibility, makes
      it difficult to lure unsuspecting developers into Avalon. We lose the "first
      component is for free" / "I can see that you're special, so I got something
      special for you" opportunity.</p>
      
      <p>Stefano mentioned that several potential new users of Avalon had tried to
      start a component by doing:</p>
      
      <pre><code>
      ComponentInterface comp = new ComponentImpl ();        
      </code></pre>
      
      <p>and then nothing more, because that was what they were familiar with.</p>
      
      <p>The purpose of Avalon Microcontainer is to make something that is as
      close as possible to the above possible. Specifically:</p>
      
      <ul>
          <li><p>Components should be able to be used with the Microcontainer
without any 
          XML and any config files.</p></li>
          <li><p>All configuration is handled programmatically.</p></li>
          <li><p>As far as possible, usage of components should follow standard
Java idioms.</p></li>
      </ul>
      
      <h3>Basic Idea</h3>
      
      <p>Define a container that holds <b>one</b> component. Yep, that's
right, one single 
      component for each container. Solve composition by linking these one-component
      containers together.
      Solve usage of components by letting the container expose the component's
      interface. This may seem difficult to understand, but see the case study below
      to see how I reached the currect way of doing it.</p>
      
      <h4>Rationale Behind the Idea</h4>
      
      <p>The case I'm looking at has two components, the Supplier and the Composer.</p>
      
      <p>Their interfaces are defined in Supplier.java and Composer.java, respectively,
      and their implementations are in SupplierImpl.java and ComposerImpl.java.</p>
      
      <p>Let me list some issues:</p>
      
      <ul>
          <li><p>
          ComposerImpl accesses the Supplier via a ComponentManager interface. It
          uses lookup("supplier") to get the Supplier.
          </p></li>
          <li><p>
          ComposerImpl may require more than one Supplier during each transaction.
          </p></li>
          <li><p>
          SupplierImpl may or may not be threadsafe.
          </p></li>
          <li><p>
          The user may want to access Supplier directly.
          </p></li>
          <li><p>
          The user definitely wants to access Composer directly.
          </p></li>
      </ul>
      
      <p>
      So we have a component, ComposerImpl, that needs a Supplier. Had this been
      straight Java, we would expect to do something like this:
      </p>
      
      <pre><code>
      ...
      
      SupplierImpl supplier = new SupplierImpl();
      ComposerImpl composer = new ComposerImpl(supplier);
      
      supplier.someMethod();
      composer.someMethod();
      
      ...
      </code></pre>
      
      <p>So let's introduce an ObjectFactory class for construction:</p>
      
      <pre><code>
      ...
      
      Supplier supplier = (Supplier) ObjectFactory.newInstance( 
                                         SupplierImpl.class 
                                     );
      
      // Pass the supplier in with role "supplier"
      Composer composer = (Composer) ObjectFactory.newInstance( 
                                         ComposerImpl.class, 
                                         new Role[]{ new Role("supplier", supplier) }
                                     );
      
      supplier.someMethod();
      composer.someMethod();
      
      ...
      </code></pre>
      
      <p>But now we have a problem. Even though we do give ComposerImpl a Supplier
      with the correct role name, we have forgotten that ComposerImpl may do
      *several* lookups on "supplier", and that SupplierImpl may not be
      reusable. As it looks now, ComposerImpl will get the same instance of
      SupplierImpl all the time, and that may not work.</p>
      
      <p>So let's introduce a ObjectFactoryFactory. Give it a component implementation
      class and it will produce a factory that you can use to create new components.
      Then we pass *that* one to ComposerImpl (hidden behind a ComponentManager
      interface) and we're done, right?</p>
      
      <pre><code>
      ...
      
      ObjectFactory supplierFactory = ObjectFactoryFactory.newInstance( 
                                          SupplierImpl.class 
                                      );
      
      // Pass the supplier in with role "supplier"
      Composer composer = ObjectFactory.newInstance( 
                              ComposerImpl.class, 
                              new Role[]{ new Role("supplier", supplierFactory) }
                          );
      
      
      supplier.someMethod();  // !!!!!!!!!!! This won't work anymore!
      
      composer.someMethod(); 
      
      ...
      </code></pre>
      
      <p>Guess not.</p>
      
      <p>The problem is that sometimes, we want to use the returned object as a factory
      (when it is used with Composable components) and sometimes we just want the
      "raw" component. The ideal would be something that exposes the interface of the
      component, but also exposed a factory interface. We would use the factory 
      interface of a component when giving it to another component that is a 
      composer, but use the ordinary interface otherwise.</p>
      
      <p>Solution: Dynamic proxies.</p>
      
      <p>When you do:</p>
      
      <pre><code>
      Supplier supplier = (Supplier) ObjectFactory.newInstance( 
                                         SupplierImpl.class 
                                     );
      </code></pre>
      
      <p>we should give you back an object that implements all of SupplierImpl's
      interfaces, but also an ObjectFactory interface that we can use to create
      new instances of SupplierImpl.</p>
      
      <p>This ObjectFactory interface will be used when combining the supplier with
      the composer, and the user who wants to use the returned SupplierImpl via the
      Supplier interface can do so as well.</p>
      
      <p>The end result of this is in the code, and below I will do a short 
      walkthrough.</p>
      
      
      <h3>Walkthrough</h3>
      
      <h4>Component Usage</h4>
      
      <p>Below is acurrent demonstration class. It sets up a Supplier and a
      Composer using that Supplier, and then invokes methods on the Composer.</p>
      
      <pre><code>
      public class MC 
      {
          public static void main( String[] args ) throws Exception 
          {
              Supplier supplier = SupplierImpl.getInstance();
              Composer comp = ComposerImpl.getInstance( supplier );
      
              comp.method();
      
              MicroContainer.release( comp );
              MicroContainer.release( supplier );
          }    
      }
      </code></pre>
      
      <p>From top to bottom:</p>
      
      <ul>
          <li><p>
          You notice that the Supplier is retrieved via a static method in the 
          implementation class, and that the same goes for the Composer.
          These are just convenience methods, and I'll show them in just a
          moment.
          </p></li>
          <li><p>    
          The returned Composer instance is used just as you would expect to 
          use it.
          </p></li>
          <li><p>
          Note that you are required to release components. This can be done
          either by casting, or you can use a static method in MicroContainer
          that will do the job for you.
          </p></li>
      </ul>
      
      <p>Now, let's look at the <code>SupplierImpl.getInstance()</code>
method:</p>
      
      <pre><code>
      public static Supplier getInstance() 
      {
          return (Supplier) new MicroContainer( SupplierImpl.class )
                                    .sharedInstance( true )
                                    .create();
      }
  </code></pre>
      
      <p>This command creates a new MicroContainer for SupplierImpl. It also
      specifies that SupplierImpl can be shared among several clients - 
      this is, for example, when Composer does multiple lookups on it. In terms
      of the usage described in the case study above, the "factory" aspect of
      the returned object will return the same singleton instance all the time.</p>
      
      <p>Here is the corresponding instance for ComposerImpl:</p>
      
      <pre><code>
      public static Composer getInstance( Supplier supplier ) 
      {
      return (Composer) new MicroContainer( ComposerImpl.class )
                                .addComponent( Supplier.ROLE, supplier )
                                .sharedInstance( true )
                                .create();
      }
  </code></pre>
      
      <p>
      Similar, but note the addComponent() call - this sets up supplier as the
      component for the Supplier role.
      </p>
      
      <p>
      Also note that the getInstance method is type-safe: You must pass it a 
      supplier. If MicroContainer had used generic ObjectFactories this would
      not have been possible.
      </p>
      
      
      <h4>Impact on Component Developers</h4>
      
      <p>
      Developers can, if they feel like it, add a getInstance method for use
      with MicroContainer. However, it is not required. 
      </p>
      
      
      <h4>Impact on Component Users</h4>
      
      <p>MicroContainer has no pooling of components, and provides no pluggability
      in itself. That is, when creating a container, you must specify the 
      implementation class.</p>
      
      <p>
      These two tradeoffs were made since MicroContainer would otherwise grow
      and become something like Fortress or ECM - a bit more than originally 
      envisioned.
      </p>
      
  </body>
  
  

--
To unsubscribe, e-mail:   <mailto:avalon-cvs-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:avalon-cvs-help@jakarta.apache.org>


Mime
View raw message