PicoContainer

provide target component key to FactoryInjector

Details

  • Type: Improvement Improvement
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: 2.9
  • Component/s: PicoContainer (Java)
  • Labels:
    None
  • Testcase included:
    yes
  • Patch Submitted:
    Yes
  • Number of attachments :
    2

Description

Right now FactoryInjector can make decisions about the object to fabricate based on the Type of the target component. Sometimes several components of the same type can in the same container using distinct keys. When FactoryInjector is used to fabricate a dependency (logger or configuration object) for these it would be useful to provide different dependency to each of these components. This is impossible with the current design. However this feature can be introduced in fully backward compatible and non-invasive way.

Here's the test case extracted from the patch:

public class FactoryInjectorTestCase {
    
    // ...
    
   private static class KeyAwareSwedeFactoryInjector extends FactoryInjector<Swede> {
        public Swede getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException {
        	return getComponentInstance(container, into, null);
        }

    	public Swede getComponentInstance(PicoContainer container, final Type into, final Object targetKey) throws PicoCompositionException {
            // Mauro: you can do anything in here by way of startegy for injecting a specific logger :-)
            return new Swede() {
                public String toString() {
                    return "Swede for " + ((Class) into).getName() + " " + targetKey;
                }
            };
        }
    }

    @Test
    public void testThatFactoryCanUseTargetComponentKey()
    {
        MutablePicoContainer container = new DefaultPicoContainer(new MultiInjection());
        container.addComponent(String.class, "foo");
        container.addComponent("turnip1", Turnip.class);
        container.addComponent("turnip2", Turnip.class);
        container.addAdapter(new KeyAwareSwedeFactoryInjector());
        Turnip turnip1 = (Turnip)container.getComponent("turnip1");
        Turnip turnip2 = (Turnip)container.getComponent("turnip2");
        assertNotNull(turnip1);
        assertNotNull(turnip2);
        assertNotSame(turnip1, turnip2);
        assertNotSame(turnip1.getSwede(), turnip2.getSwede());
        assertEquals("Swede for " + Turnip.class.getName() + " turnip1", turnip1.getSwede().toString());
        assertEquals("Swede for " + Turnip.class.getName() + " turnip2", turnip2.getSwede().toString());        
    }
}

The patch has been created against 2.8.3 release, and it applies cleanly against trunk as of Jun 23rd 2009 / revision 5434.
All tests pass cleanly.
No changes are required to existing implementations of FactoryInjector interface within pico codebase or elsewhere.

  1. picocontainer.patch
    23/Jun/09 10:42 AM
    10 kB
    Rafal Krzewski
  2. picocontainer-gems.patch
    23/Jun/09 10:42 AM
    2 kB
    Rafal Krzewski

Activity

Hide
Paul Hammant added a comment -

Implemented slightly differently:-

http://fisheye.codehaus.org/changelog/picocontainer/java/2.x/trunk/pico?cs=5435

The main PicoContainer API has not changed, but there's a slightly different rule-set for implementors of FactoryInjector

I'd be interested in knowing your real need for this Rafal Turnips and Swede (my own fault) are too abstract.

Show
Paul Hammant added a comment - Implemented slightly differently:- http://fisheye.codehaus.org/changelog/picocontainer/java/2.x/trunk/pico?cs=5435 The main PicoContainer API has not changed, but there's a slightly different rule-set for implementors of FactoryInjector I'd be interested in knowing your real need for this Rafal Turnips and Swede (my own fault) are too abstract.
Hide
Rafal Krzewski added a comment -

Whoa Paul, you are fast! I was also considering using single ThreadLocal with dedicated object, but I didn't think about making it implement Type interface, thus keeping DPC API unchanged. I am impressed

My use case for this is dynamic configuration lookup. A simplified example would be:

public class ConfigurationFactory implements FactoryInjector<Properties> {
  private final File configDirectory;
 
  public ConfigurationFactory(File configDirectory) {
    this.configDirectory = configDirectory;
  }

  public Object getComponentInstance(PicoContainer container, Type into) {
    Object componentKey = ((InjectInto)into).getIntoKey();
    String componentName = key instance of Class<?> ? ((Class)key).getName() : ((InjectInto)into).getIntoClass().getName() + "-" + key.toString();
    try {
      FileInputStream fos = new FileInputStream(new File(configDirectory, componentName + ".properties");
      Properties config = new Properties();
      config.load(fos);
      return config;
    } catch(IOException e) {
      throw new RuntimeException("failed to load configuration for component " + componentName);
    }
  }
}

Then for components like these:

package com.acme;

public class FooService {
  public FooService(Properties config) {
  }
}

public class BarService {
  public FooService(Properties config) {
  }
}

You would put the following files into configuration directory:

com.acme.FooService.properties
com.acme.BarService-local.properties
com.acme.BarService-remote.properties

wire your container like

container.addAdapter(new ConfigurationFactory(new File("/etc/app/config"));
container.addComponent(com.acme.FooService.class);
container.addComponent("local", com.acme.BarService);
container.addComponent("remote", com.acme.BarService);

and enjoy your automagically injected configuration

In my actual use case (ObjectLedge framework) the configurations are XML files that are validated against schema and get parsed into org.jcontainer.dna.Configuration objects. The schemata (actually stored on classpath) and config files are named after the component key rather than implementation class (org.objectledge.someservice.API vs pl.caltha.someservice.ObscureImpelemnation). We are still using Pico 1.1 and I am considering upgrading to 2.x in near future. I'm very glad I'll be able to reimplment configuration lookup functionality that we have without horrible hacks I had to use with old Pico

Show
Rafal Krzewski added a comment - Whoa Paul, you are fast! I was also considering using single ThreadLocal with dedicated object, but I didn't think about making it implement Type interface, thus keeping DPC API unchanged. I am impressed My use case for this is dynamic configuration lookup. A simplified example would be:
public class ConfigurationFactory implements FactoryInjector<Properties> {
  private final File configDirectory;
 
  public ConfigurationFactory(File configDirectory) {
    this.configDirectory = configDirectory;
  }

  public Object getComponentInstance(PicoContainer container, Type into) {
    Object componentKey = ((InjectInto)into).getIntoKey();
    String componentName = key instance of Class<?> ? ((Class)key).getName() : ((InjectInto)into).getIntoClass().getName() + "-" + key.toString();
    try {
      FileInputStream fos = new FileInputStream(new File(configDirectory, componentName + ".properties");
      Properties config = new Properties();
      config.load(fos);
      return config;
    } catch(IOException e) {
      throw new RuntimeException("failed to load configuration for component " + componentName);
    }
  }
}
Then for components like these:
package com.acme;

public class FooService {
  public FooService(Properties config) {
  }
}

public class BarService {
  public FooService(Properties config) {
  }
}
You would put the following files into configuration directory:
com.acme.FooService.properties
com.acme.BarService-local.properties
com.acme.BarService-remote.properties
wire your container like
container.addAdapter(new ConfigurationFactory(new File("/etc/app/config"));
container.addComponent(com.acme.FooService.class);
container.addComponent("local", com.acme.BarService);
container.addComponent("remote", com.acme.BarService);
and enjoy your automagically injected configuration In my actual use case (ObjectLedge framework) the configurations are XML files that are validated against schema and get parsed into org.jcontainer.dna.Configuration objects. The schemata (actually stored on classpath) and config files are named after the component key rather than implementation class (org.objectledge.someservice.API vs pl.caltha.someservice.ObscureImpelemnation). We are still using Pico 1.1 and I am considering upgrading to 2.x in near future. I'm very glad I'll be able to reimplment configuration lookup functionality that we have without horrible hacks I had to use with old Pico

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: