Re: [picocontainer-dev] Looking for advices on dynamic Component Providers
Fabio Kung <[email protected]> Tue, 7 Jul 2009 12:17:23 -0300
| Newsgroups | gmane.comp.java.picocontainer.devel |
|---|---|
| Message-ID | <[email protected]> |
Thanks guys. I ended up solving the issue as Mark did. I didn't realize I could directly use a custom ComponentAdapter, completely avoiding pico Providers. (although I think it would be more natural to use pico providers). On Tue, Jul 7, 2009 at 7:27 AM, Mark Hobson<[email protected]> wrote: > I've also written an in-house abstraction layer around IoC > configuration with Pico, Spring and Guice implementations. I > encountered the same problem when trying to support a Provider<T> > concept but simply wrote a custom adapter to implement it. Something > like this should do the trick (off the top of my head): > > public class ProviderAdapter<T> extends AbstractAdapter<T> { > private final Provider<T> provider; > public ProviderAdapter(Object key, Class<T> implementation, > Provider<T> provider) { > super(key, implementation); > this.provider = provider; > } > public T getComponentInstance(PicoContainer container, Type into) > throws PicoCompositionException { > return provider.get(); > } > public void verify(PicoContainer container) throws > PicoCompositionException { > return true; > } > public String getDescriptor() { > return "ProviderAdapter"; > } > } > > Mark > > 2009/7/6 Fabio Kung <[email protected]>: >> Hi all, >> I'm involved in a project which needs to be independent from IoC vendors. We >> ended building a thin anti-corruption layer which translates our needs to >> several IoC providers and we already have implementations for Pico and >> Spring, with Guice coming. >> My problem appeared when trying to support what we call >> "ComponentFactories". I believe Pico calls them "Providers". Here is a small >> example: >> @Component // causes it to be registered in the container >> public class HibernateProvider implements ComponentFactory<Session> { >> private SessionFactory factory; >> public HibernateProvider(SessionFactory regularConstructorInjection) { >> // this component is managed, then it may receive any available >> dependency >> } >> public Session getInstance() { >> // this is similar to a Pico's provide() method >> return ...; // hibernate session for current scope >> } >> } >> We are doing a work very similar to what JSR-330 (Dependency Injection for >> Java) is trying to achieve, abstracting IoC providers. Hopefully, I also >> believe that some of you guys are involved in this JSR and probably had (or >> will have to) deal with the same problem, because the spec has a Provider<T> >> interface. >> My idea is to register a ProviderAdapter inside Pico, for each >> ComponentFactory. The main issue is that Pico statically checks for the >> Provider targetType, taking the return type of the provide() method. So, it >> would require a new class for each ComponentFactory, as every associated >> ProviderAdapter must have a provide() method with a different return type. >> I know I could use ASM and build a new ProviderAdapter class at runtime for >> each ComponentFactory, with a different provide() method. But I really >> wanted to avoid dealing with ASM, bytecode manipulation and ClassLoaders if >> it could be solved in an easier manner. >> Then, my second thought was to create a "generic ProviderAdapter" for Pico, >> that doesn't require target types to be specified as return types of >> provide() methods. It simply receives the target type as a regular >> dependency: >> public class GenericProviderAdapter implements Injector, Adapter { >> private static final Method provideMethod; >> static { >> try { >> provideMethod = >> ComponentFactoryAdapter.class.getMethod("provide", Container.class); >> } catch (NoSuchMethodException e) { >> throw new ExceptionInInitializerError(e); >> } >> } >> private final Class<? extends ComponentFactory> factoryType; >> private final Class<?> targetType; >> public GenericProviderAdapter(Class<? extends ComponentFactory> >> factoryType) { >> this.factoryType = factoryType; >> // not relevant and specific to my case, but I'm taking the >> targetType from >> // the generic type definition: XYZ implements >> ComponentFactory<Session> => targetType = Session.class >> this.targetType = new >> ComponentFactoryIntrospector().targetTypeFor(factoryType); >> } >> public Object provide(Container container) { >> // the ComponentFactory is managed, then I need the container to >> retrieve an instance >> // note: Container interface is implemented with Pico in this >> example >> return container.instanceFor(factoryType).getInstance(); >> } >> public Object getComponentInstance(PicoContainer container, Type into) >> throws PicoCompositionException { >> return new Reinjector(container).reinject(targetType, >> this.getClass(), this, Characteristics.NONE, new >> MethodInjection(provideMethod)); >> } >> // ... >> } >> My idea is that for every ComponentFactory in the application, one >> ProviderAdapter is registered inside pico: >> pico.addComponent(new GenericProviderAdapter(componentFactoryType)); >> The important pieces of the GenericProviderAdapter are: >> - the return type of the provide() method is generic (Object), so that I >> can use the same ProviderAdapter for every ComponentFactory; >> - the getComponentInstance() method is based on the original code from >> ProviderAdapter class, but it infers the targetType of the Provider from the >> ComponentFactory class, instead of using the return type of the provide() >> method. >> It is almost working, despite the fact that Pico has a type check for >> Provider classes in the AbstractAdapter#checkTypeCompatibility() method. The >> type check throws an exception when the provide() method return type differs >> from the actual targetType, that is my case. >> protected void checkTypeCompatibility() { >> // ... >> if (Provider.class.isAssignableFrom(componentImplementation)) { >> if (!componentType.isAssignableFrom( >> >> ProviderAdapter.getProvideMethod(componentImplementation).getReturnType())) >> { >> throw newCCE(componentType); >> } >> } >> // ... >> } >> Would you mind relaxing the type check? Or does someone have an alternative >> idea/approach? >> By now, I'm just building custom implementations of all Injectors involved >> in the process (ReInjector, MethodInjection, MethodInjector and >> MethodInjector$ByReflectionMethod), overriding the checkTypeCompatibility() >> method. I had to override all injectors, because the type check is being >> called at _every_ injector instantiation (as the call is in the >> AbstractAdapter constructor). >> It would be much easier if you could just drop (or change) the type check >> inside the AbstractAdapter class. Do you think it is reasonable? >> I think it would also be useful to people implementing JSR-330 (which has >> the Provider<T> interface), JSR-299 (which has Producer<T> interface) and >> others, on top of Pico. All of them could be easily mapped one-to-one to a >> Pico GenericProducerAdapter instance, without requiring class generation at >> runtime. >> Thanks! >> Cheers, >> -- >> Fabio Kung >> http://www.fabiokung.com >> >> Caelum - Ensino e Inovação >> http://www.caelum.com.br >> > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > -- Fabio Kung http://www.fabiokung.com Caelum - Ensino e Inovação http://www.caelum.com.br --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email