Introduction

PicoContainer is very simple container for very simple components.  It honors the Inversion of control pattern (IoC) in a way that we calling it type 3 IoC.  See below for types. Components are typically going to live inside the same JVM. The idea is that this might scale from embedded containers for simple beans to enterprise and distributed applications.



PicoContainer components declare their component and configuration needs in their (single) constructor.  One of many types of PicoContainer implementations would resolve the needs of the components at time of their instantiation. 



A component must have a single constructor.

We have a restriction in that PicoContainer components must only have one constructor. We don't think that is a bad restriction. Especially as the XP "change it when you need to" has not delivered a use case that would preclude this design yet.  In respect of the single constructor, we like the NullObject pattern and use that with some parameter-reducing extending class to the component in question.



Components and configuration as parameters

The parameters in the constructor of a component can be elements of configuration as well as other components. The arguments detailing configuration need special processing. If is best to read about this on the

configuration

page.



Primitives

We also can't allow primitives in the constructor (yet). We're not counting java.lang.String and java.lang.Integer (etc) as primitives of course.



Simple Example

In skeletal form, here is the simplest possible PicoContainer compatible component :



   public class Shop {
       public Shop(StockManager stockManager) {
       }
   }

Imagine there were a component that could satisfy that like so :



   public class StockManager {
   }

Use without a container.

We love the Pico design because we see the components being used without a container :



   new Shop(new StockManager());

This is particularly good for unit testing. In-container testing being one of the most avoided forms of unit testing.  This is where other container design s and specifications fail.



Use with a container

With a PicoContainer :



   PicoContainer pc = SomePicoContainer();
   pc.registerComponent(StockManager.class)
   pc.registerComponent(Shop.class)
   pc.start();

And perhaps some as yet unwritten PicoContainer :



   MoonscapePicoContainer mc = new MoonscapePicoContainer();
   mc.registerAndStart("Shop.class, StockManager.class"); 

It does not matter how things are laced together. It does not matter how tightly bound one PicoContainer is to an individual purpose.  The important thing is actually the components and the numerous and imaginative deployments for them.



Container Interface

PicoContainer's most basic interface is PicoContainer :



   public interface PicoContainer {
       boolean hasComponent(Class componentType);
       Object getComponent(Class componentType);
       Object[] getComponents();
       Class[] getComponentTypes();       void instantiateComponents();
   }

Clearly a PicoContainer is mostly a read-only thing. That's deliberate.  We see many PicoContainers delivering different visions of component lacing and configuration.  Some may exist in chains or trees, such that a tiering model could exist. Such a tiering model could provide hierarchies for reasons of security, kernel hiding or scope control.  Joe has delivered a trinity of PicoContainers to fit inside the Servlet model - Application, Session and Request scoped PicoContainers.



The PicoContainer interface is only of use to container makers, the class that instantiates the container and other containers (in the case of chaining or directive graphs of containers).  It should never be exposed to the components that it hosts (though some of the components may go on to instantiate their own more-fine-grained containers).



ClassRegistrationPicoContainer Interface

This interface allows the instantiating class to register components by class name or class type and and implementing class name (refer Interface/Impl separation)



  public interface ClassRegistrationPicoContainer extends PicoContainer
      void registerComponent(Class componentImplementation) throws PicoRegistrationException;
      void registerComponent(Class componentType, Class componentImplementation) throws PicoRegistrationException;
      void registerComponent(Class componentType, Object component) throws PicoRegistrationException;
      void registerComponent(Object component) throws PicoRegistrationException;
      // Subject to modification.      void addParameterToComponent(Class componentType, Class parameter, Object arg);
  }

This interface also allows the registration of pre-instantiated components. Given they are pre-instantiated, they need not of course be components according to our design at all.  People may also use this as a way of forwarding something from lower tiers of containers.



Sister projects

NanoContainer is not really a single container. It is four PicoContainer compatible containers so far.  One that takes String registration of components, another that takes them via XML. One that aggregates components into a single fat (almost) multiple-inheritance comp. Another that makes

Nanning

(the AOP framework) a PicoContainer compatible proposition.



The big difference between PicoContainers and NanoContainers are that the former have no XML to lace comps together.  Components themselves for Pico and NanoContainers should be interchangeable.



  Type 1

Apache's

Avalon

project has been selling the IoC patten for many years now.  The Avalon-Framework design laces together and configures components like so:



import org.apache.avalon.framework.*;

   public class Shop implements Serviceable, Configurable, Initializable {
    StockManager stockManager;

    String shopZipCode;

    public void service(org.apache.avalon.framework.ServiceManager sm) throws ServiceException {
        stockManager = (StockManager) sm.lookup("StockManager");
    }

    public void configure( final Configuration configuration ) throws ConfigurationException {

        shopZipCode = configuration.getChild( "zipcode" ).getValue();       
    }
    public void initialize() {
        // all service()ing an config()ing done.
    }
}

A component has to have service (component) declarations in some external file. The popular

Phoenix

container had .xinfo files for such needs.  All Avalon container have some mechanism for storing configuration and assembly externally to the class.  Cross referenced against the xinfo files, Phoenix's assembly.xml defines the implementations to be used for component types.  This all Avalon components must be interface/implementation separated. This in itself is not a bad thing in my (Paul) opinion.



Using type 1 components withou a type 1 container.

The downside of the this design is that components can only be used without the container with great difficulty. If at all.  This a proper container is needed at all times, and you have to choose one for different purposes (that not withstanding the efforts of the Avalon team to make a single all-purpose container).  If you do manage to instantiate components without a container, you might miss one of the essential service dependencies.  The component-using class  will continue to compile, but at run time it will be apparent that there are missing dependencies.



  Type 2

Joe Walnes whist working on a book with other luminaries, started a type 2 IoC design. This is marked up with doclet tags (though that is not hard and fast) :



public class Shop {

   StockManager stockManager;
   String shopZipCode;   
   /**

    * @service name="StockManager"       
    */

   public void setStockManager(StockManager stockManager) {

       this.stockManager = stockManager;

   }
   /**
    * @config name="shopZipCode"       
    */
   public void setStockManager(String shopZipCode) {
       this.shopZipCode= shopZipCode;
   }
   // TODO - Joe - how does type 2 do config ? Same way?
   public void initialize() {
       // all setXXX's done
   }
}

The container use the meta-information to resolve all the dependencies.  Components need not be interface/impl separated. Coder's choice.





Using type 2 comps withou a type 2 container.

Type2 components can be used directly, without any container.  The component-using class  will continue to compile, but at run time it will be apparent that there are missing dependencies.  The downside of this is that a developer may miss a setXXX() method invocation if they are using the component directly. That is fairly small as a risk as it would clearly be caught in the development cycle.  Caught in the development cycle, but maybe obscurely as a NullPointerException.

  Type 3

Rachel Davies, while reviewing Joe's book, left a Fermat-like margin note when view a snippet like the above.  "Why not use constructors ?".  Brilliant and simple.



public class Shop {

    StockManager stockManager;

    String shopZipCode;

    public Shop(StockManager stockManager, String shopZipCode) {

        this.stockManager = stockManager;

        this.shopZipCode = shopZipCode;

    }

 }

Note, for this there is no need to declare needs in any other way. No interfaces, no doclet tags, no external XML. Just your simple component and PicoContainer.  No need for post assembly/config initialization either.  If it is constructed (not withstanding some asserts on nulls) it has its needs satisfied.  Components need not be interface/impl separated. Coder's choice.



Using type 3 comps without a type 3 container.

Like type2, the component can be used directly, without any container.  The missing dependancy scenario is not an issue for type3.