Discussion:
Generic factory injectors
Martin Fuzzey
2008-06-13 15:11:11 UTC
Permalink
Hi,

I'm trying to use a generic factory injector like this:

public <T> void addService(final Class<T> interface_, Class
implementationClass, String instanceId) {
pico.addComponent(makeKey(interface_, instanceId), implementationClass);

class ServiceConnectionInjector extends FactoryInjector<T> {
public T getComponentInstance(PicoContainer container, Type into) {
System.out.println("**** injector called for " + into);
return null;
}
}
pico.addAdapter(new ServiceConnectionInjector());
}

Unfortunately I get an exception in the addAdapter method :
sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast
to java.lang.Class
at org.picocontainer.injectors.FactoryInjector.<init>(FactoryInjector.java:28)
at javadi.PicoContainerBasedServiceManager$1ServiceConnectionInjector.<init>(PicoContainerBasedServiceManager.java:29)
at javadi.PicoContainerBasedServiceManager.addService(PicoContainerBasedServiceManager.java:36)
at javadi.PicoContainerBasedServiceManagerTest.simpleInternalDependence(PicoContainerBasedServiceManagerTest.java:28)


It works ok with "directly" typed FactoryInjector subcleasses (like
the log example in the docs)
Looking at the source it seems the problem is that it assumes
getActualTypeArguments() will return the class and not another
parameterised type)
Has anyone else had / solved this problem? (unfortunately I'm not
currently very up to date with java generics having spent the last few
years working in python...)

Thanks,

Martin

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Paul Hammant
2008-06-13 20:42:09 UTC
Permalink
Martin,

I'll try to make a testcase from your code ...

.. then fix. Yes, you;re right, generics are head-scratchingly hard
sometimes.

- Pau
Post by Martin Fuzzey
Hi,
public <T> void addService(final Class<T> interface_, Class
implementationClass, String instanceId) {
pico.addComponent(makeKey(interface_, instanceId),
implementationClass);
class ServiceConnectionInjector extends FactoryInjector<T> {
public T getComponentInstance(PicoContainer container, Type into) {
System.out.println("**** injector called for " + into);
return null;
}
}
pico.addAdapter(new ServiceConnectionInjector());
}
sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast
to java.lang.Class
at
org
28)
at javadi.PicoContainerBasedServiceManager
$
1ServiceConnectionInjector
.<init>(PicoContainerBasedServiceManager.java:29)
at
javadi
.PicoContainerBasedServiceManager
.addService(PicoContainerBasedServiceManager.java:36)
at
javadi
.PicoContainerBasedServiceManagerTest
28)
It works ok with "directly" typed FactoryInjector subcleasses (like
the log example in the docs)
Looking at the source it seems the problem is that it assumes
getActualTypeArguments() will return the class and not another
parameterised type)
Has anyone else had / solved this problem? (unfortunately I'm not
currently very up to date with java generics having spent the last few
years working in python...)
Thanks,
Martin
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Paul Hammant
2008-06-14 13:52:46 UTC
Permalink
Martin,

If I have a TestCase that contains. The following I can reproduce it.
I can most likely stop it failing, but what should it do?

As I debug the test, the FactoryInjector knows it pertains to 'T' but
there's nothing apparently accessible for 'Map'

What are you expecting? I'm intrigued - how are you using this
relatively special setup ?

- Paul

- - -

private class Footle<T> {
private class ServiceConnectionInjector extends
FactoryInjector<T> {
public T getComponentInstance(PicoContainer container,
Type into) {
System.out.println("**** injector called for " + into);
return null;
}
}
private void addAdapter(MutablePicoContainer mpc) {
mpc.addAdapter(new ServiceConnectionInjector());
}
}

public static interface Tree {
String leafColor();
}

public static class OakTree implements Tree {
private String leafColor;
public OakTree(String leafColor) {
this.leafColor = leafColor;
}
public String leafColor() {
return leafColor;
}
}

@Test public void
ensureSophistcatedFactorInjectorCaseIsPossible() {

DefaultPicoContainer pico = new DefaultPicoContainer();
pico.addConfig("leafColor", "green");
pico.addComponent(Tree.class, OakTree.class);

Footle<Map> ft = new Footle<Map>();

ft.addAdapter(pico);

Tree tree = pico.getComponent(Tree.class);
}
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Martin Fuzzey
2008-06-16 10:27:00 UTC
Permalink
Paul,
Thank you for ypur quick reply.
If I have a TestCase that contains. The following I can reproduce it. I can
most likely stop it failing, but what should it do?
I think the test case doesn't go far enough to show the usage of the
object created by the factory.
See a better example below.
As I debug the test, the FactoryInjector knows it pertains to 'T' but
there's nothing apparently accessible for 'Map'
Yes I couldn't figure it out either.
What are you expecting? I'm intrigued - how are you using this relatively
special setup ?
Indeed maybe a little explanation is in order :)

I currently have a legacy custom application framework which
dynamically loads "services" (which are classes that have to extend a
special base class) and provides a factory mechanism to obtain proxies
to these services (which may be remote in some cases).
I am trying to extend this to allow POJOs to be used as services (with
the POJOs being managed by pico). These pojos may require access to
other services (implemented either via other POJOs OR as legacy
services for migration) hence when pico injects a reference to a
service I need to use a custom factory to link in to our existing
mechanism for compatibility.

The following simplified code may explain it better.
ComplexFactoryExample.java:

package javadi.complexExample;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import javadi.pico.MyFactoryInjector;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoBuilder;
import org.picocontainer.PicoCompositionException;
import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.FactoryInjector;

public class ComplexFactoryExample {

public static void main(String[] args) {
// setup the world outside picocontainer (simulated and simplified)
OutsideResourceFactory outside = new OutsideResourceFactory();
outside.register(Edible.class, new Apple());
outside.register(Drinkable.class, new Beer());

MutablePicoContainer pico = new PicoBuilder().withCaching().build();
makeAccessibleToPico(outside, pico, Edible.class);
makeAccessibleToPico(outside, pico, Drinkable.class);

pico.addComponent(Person.class, Fred.class);

Person p = pico.getComponent(Person.class);
System.out.println(p.haveMeal()); // Prints "Ahhcroc"
}

private static <T> void makeAccessibleToPico(final
OutsideResourceFactory outside, MutablePicoContainer pico, final
Class<T> interface_) {
// This one fails with ClassCast exception
class OutsideInjector1 extends FactoryInjector<T> {
@Override
public T getComponentInstance(PicoContainer container, Type clazz) {
return outside.obtainFromOutside(interface_);
}
}

/* This one works - MyFactoryInjector is a copy of
FactoryInjector with just
the constructor changed thus:
* public MyFactoryInjector(Class key) throws
PicoCompositionException {
* this.key = key;
* }
*/
class OutsideInjector2 extends MyFactoryInjector<T> {
public OutsideInjector2() throws PicoCompositionException {
super(interface_);
}

@Override
public T getComponentInstance(PicoContainer container, Type clazz) {
return outside.obtainFromOutside(interface_);
}
}
pico.addAdapter(new OutsideInjector1());
//pico.addAdapter(new OutsideInjector2());
}

}

// This is an existing non picocontainer aware class responsible for obtaining
// access to distant resources based on their interface
class OutsideResourceFactory {
private Map<Class, Object>_resources = new HashMap();

public void register(Class interface_, Object implementation) {
_resources.put(interface_, implementation);
}

public <T> T obtainFromOutside(Class<T> interface_) {
// in reality this is much more complicated (remote proxies and stuff)
return (T)_resources.get(interface_);
}
}

// A few dummy external services
interface Edible {
public String eat();
}

class Apple implements Edible {
public String eat() { return "croc"; }
}

interface Drinkable {
public String drink();
}

class Beer implements Drinkable {
public String drink() { return "Ahh"; }
}


Person.java:
package javadi.complexExample;

interface Person {
public String haveMeal();
}


Fred.java:
package javadi.complexExample;

// This is an object to be managed by picocontainer needing access to
external service
public class Fred implements Person {
private Drinkable drinkable;
private Edible edible;

public Fred(Drinkable drinkable, Edible edible) {
this.drinkable = drinkable;
this.edible = edible;
}

public String haveMeal() {
return drinkable.drink() + edible.eat();
}


As you see I did make it work (by creating a modified version of
FactoryInjector) but I had to manually pass the key information by
adding a constructor argument.

Martin

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Jörg Schaible
2008-06-16 11:48:20 UTC
Permalink
Hi Martin,
Post by Martin Fuzzey
Paul,
Thank you for ypur quick reply.
Post by Paul Hammant
If I have a TestCase that contains. The following I can reproduce
it. I can most likely stop it failing, but what should it do?
I think the test case doesn't go far enough to show the usage of the
object created by the factory.
See a better example below.
Post by Paul Hammant
As I debug the test, the FactoryInjector knows it pertains to 'T' but
there's nothing apparently accessible for 'Map'
Yes I couldn't figure it out either.
Post by Paul Hammant
What are you expecting? I'm intrigued - how are you using this
relatively special setup ?
Indeed maybe a little explanation is in order :)
I currently have a legacy custom application framework which
dynamically loads "services" (which are classes that have to extend a
special base class) and provides a factory mechanism to obtain proxies
to these services (which may be remote in some cases).
I am trying to extend this to allow POJOs to be used as services (with
the POJOs being managed by pico). These pojos may require access to
other services (implemented either via other POJOs OR as legacy
services for migration) hence when pico injects a reference to a
service I need to use a custom factory to link in to our existing
mechanism for compatibility.
The following simplified code may explain it better.
package javadi.complexExample;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import javadi.pico.MyFactoryInjector;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoBuilder;
import org.picocontainer.PicoCompositionException;
import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.FactoryInjector;
public class ComplexFactoryExample {
public static void main(String[] args) {
// setup the world outside picocontainer (simulated
and simplified)
OutsideResourceFactory outside = new OutsideResourceFactory();
outside.register(Edible.class, new Apple());
outside.register(Drinkable.class, new Beer());
MutablePicoContainer pico = new
PicoBuilder().withCaching().build();
makeAccessibleToPico(outside, pico, Edible.class);
makeAccessibleToPico(outside, pico, Drinkable.class);
pico.addComponent(Person.class, Fred.class);
Person p = pico.getComponent(Person.class);
System.out.println(p.haveMeal()); // Prints "Ahhcroc" }
private static <T> void makeAccessibleToPico(final
OutsideResourceFactory outside, MutablePicoContainer pico, final
Class<T> interface_) { // This one fails with ClassCast
exception class OutsideInjector1 extends FactoryInjector<T> {
@Override public T getComponentInstance(PicoContainer
container, Type clazz) {
return outside.obtainFromOutside(interface_);
} }
/* This one works - MyFactoryInjector is a copy of
* public MyFactoryInjector(Class key) throws
PicoCompositionException {
* this.key = key;
* }
*/
class OutsideInjector2 extends MyFactoryInjector<T> {
public OutsideInjector2() throws
PicoCompositionException {
super(interface_);
}
@Override
public T getComponentInstance(PicoContainer
container, Type clazz) {
return outside.obtainFromOutside(interface_);
} }
pico.addAdapter(new OutsideInjector1());
//pico.addAdapter(new OutsideInjector2());
}
}
// This is an existing non picocontainer aware class
responsible for obtaining
// access to distant resources based on their interface
class OutsideResourceFactory {
private Map<Class, Object>_resources = new HashMap();
public void register(Class interface_, Object implementation) {
_resources.put(interface_, implementation); }
public <T> T obtainFromOutside(Class<T> interface_) {
// in reality this is much more complicated (remote
proxies and stuff)
return (T)_resources.get(interface_);
}
}
// A few dummy external services
interface Edible {
public String eat();
}
class Apple implements Edible {
public String eat() { return "croc"; }
}
interface Drinkable {
public String drink();
}
class Beer implements Drinkable {
public String drink() { return "Ahh"; }
}
package javadi.complexExample;
interface Person {
public String haveMeal();
}
package javadi.complexExample;
// This is an object to be managed by picocontainer needing access to
external service public class Fred implements Person {
private Drinkable drinkable;
private Edible edible;
public Fred(Drinkable drinkable, Edible edible) {
this.drinkable = drinkable;
this.edible = edible;
}
public String haveMeal() {
return drinkable.drink() + edible.eat();
}
As you see I did make it work (by creating a modified version of
FactoryInjector) but I had to manually pass the key information by
adding a constructor argument.
well, maybe you can take adifferent approach and create an OutsideResourceFactory that implements PicoContainer and use that one as parent?

MutablePicoContainer pico = new PicoBuilder(new OutsideResourcePicoContainer()).withCaching().build();

In this case the pico will lookup any non-present component in your implementation that is used as container parent. The PicoContainer interface defines only non-mutable methods and a lot of them can be delegated by using default arguments (e.g. null for the "binding" args). When those legacy services have been gone once, you can simply drop this special container parent ...

- Jörg

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Paul Hammant
2008-06-16 12:04:16 UTC
Permalink
I've turned that into a testcase ...
Paul Hammant
2008-06-16 12:22:43 UTC
Permalink
Martin,

Here's the same testcase rewritten. I'm using a custom PicoContainer
to model your external-service getting injection requirements.

OutsideResourceContainer isA PicoContainer and hasA
DefaultPicoContainer.
It could just as easily be OutsideResourceContainer isA
DefaultPicoContainer.
Or OutsideResourceContainer isA PicoContainer and (and stores its own
set of InstanceAdapters).

This sidesteps the FactoryInjector issue you're finding. I'm sure
your case is not so simple, can you help modify the testcase to be
more illustrative ?

Cheers,

- Paul
Martin Fuzzey
2008-06-16 17:02:40 UTC
Permalink
Paul,
Thank you, I've looked at your test case
The problem is that in my case the access to the external service
requires a dynamic lookup in a registry.
I don't know all the remote services in advance.
Hence the outside.register() stuff is just a simple way to set it up
for the test and doesn't really mimic the real world.

Is it enough to just implement the getComponent and
getComponentAdapter methods (whilst never calling addComponnet() on
the delegate??)

Martin
Post by Paul Hammant
Martin,
Here's the same testcase rewritten. I'm using a custom PicoContainer to
model your external-service getting injection requirements.
OutsideResourceContainer isA PicoContainer and hasA DefaultPicoContainer.
It could just as easily be OutsideResourceContainer isA
DefaultPicoContainer.
Or OutsideResourceContainer isA PicoContainer and (and stores its own set of
InstanceAdapters).
This sidesteps the FactoryInjector issue you're finding. I'm sure your case
is not so simple, can you help modify the testcase to be more illustrative ?
Cheers,
- Paul
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Paul Hammant
2008-06-16 17:07:26 UTC
Permalink
Its OK for you to add Components to the OutsideResourceContainer as
and when they are first needed.

As long as you can tell the difference between them and bogus
components (where you shoukld throw and exception)

Each one you make should be wrapped in a ComponentAdapter
(InstanceAdapter is a good one to use for this) and stored internally
- implicit caching.

So, yes you could implement PicoContainer interface directly, rather
than extend AbstractDelgatingPicoContainer.

- Paul
Post by Martin Fuzzey
Paul,
Thank you, I've looked at your test case
The problem is that in my case the access to the external service
requires a dynamic lookup in a registry.
I don't know all the remote services in advance.
Hence the outside.register() stuff is just a simple way to set it up
for the test and doesn't really mimic the real world.
Is it enough to just implement the getComponent and
getComponentAdapter methods (whilst never calling addComponnet() on
the delegate??)
Martin
Post by Paul Hammant
Martin,
Here's the same testcase rewritten. I'm using a custom
PicoContainer to
model your external-service getting injection requirements.
OutsideResourceContainer isA PicoContainer and hasA
DefaultPicoContainer.
It could just as easily be OutsideResourceContainer isA
DefaultPicoContainer.
Or OutsideResourceContainer isA PicoContainer and (and stores its own set of
InstanceAdapters).
This sidesteps the FactoryInjector issue you're finding. I'm sure your case
is not so simple, can you help modify the testcase to be more
illustrative ?
Cheers,
- Paul
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Loading...