Re: [picocontainer-dev] Looking for advices on dynamic Component Providers
Paul Hammant <[email protected]> Tue, 7 Jul 2009 01:51:22 -0500
| Newsgroups | gmane.comp.java.picocontainer.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi Fabio,
On Jul 6, 2009, at 1:19 PM, Fabio Kung wrote:
> 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.
Provider<T> is very different from a full abstracted DI layer in the
JDK.
>
> 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?
Perhaps. Send us a testcase that is an elegant demo of the feature
you;re needing.
>
> 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.
OK, so there's one more thing about ProviderAdapter ... if you
override getComponentImplementation() you can ( I think ) bypass the
typecheck for the return of the provide(..) method. Look at the last
inner class in ProviderTestCase.
- Paul
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email