2

I have written simple container that registers a class and it's interface and has a method to create object from that information like this:

public class DIContainer {
    protected static DIContainer instance;
    protected Hashtable<Class<?>, Class<?>> classMap;

    protected DIContainer(){
        this.classMap = new Hashtable<Class<?>, Class<?>>();
    }

    public static DIContainer getInstance(){
        if (DIContainer.instance == null)
            DIContainer.instance = new DIContainer();
        return DIContainer.instance;
    }

    public void regClass(Class<?> interf, Class<?> classToReg){
        this.classMap.put(interf, classToReg);
    }

    public Object create(Class<?> interf) throws Exception{
        if(!this.classMap.containsKey(interf))
            throw new Exception("No such class registered with "+interf.getName()+" interface");
        return this.classMap.get(interf).newInstance();

    }
}

But I want before creating new instance to bypass it to proxy, for it to create, so I have this proxy class:

public class MyProxy implements InvocationHandler
{
  private Map map;
  private Object obj;
  public static Object newInstance(Map map, Object obj, Class[] interfaces)
  {
    return Proxy.newProxyInstance(map.getClass().getClassLoader(),
                                  interfaces,
                                  new MyProxy(map, obj));
  }
  public MyProxy(Map map, Object obj)
  {
    this.map = map;
    this.obj = obj;
  }
  public Object invoke(Object proxy, Method m, Object[] args) throws
Throwable
  {
    try {
      return m.invoke(obj, args);
    } catch (NoSuchMethodError e)
    { 
    //Object result;
    String methodName = m.getName();
    if (methodName.startsWith("get"))
    {
      String name = methodName.substring(methodName.indexOf("get")+3);
      return map.get(name);
    }
    else if (methodName.startsWith("set"))
    {
      String name = methodName.substring(methodName.indexOf("set")+3);
      map.put(name, args[0]);
      return null;
    }
    else if (methodName.startsWith("is"))
    {
      String name = methodName.substring(methodName.indexOf("is")+2);
      return(map.get(name));
    }
    return null;
  }
}

}

But for proxy class I need to provide type of class and it's interface, but I only have it's information with X.class. Can get the type (for example if it's class X) X, when I have X.class? Maybe I'm doing this the wrong way and I need to change something in order for it to work, but right now I figured I need to get that class type, so then I could provide it for proxy? Because if I would right something like this:

X.class x; I would get error. So I need to write like this X x;, but I only have X.class

Update:

To explain it simply, is it possible to get this:

X obj;

when you only have X.class (with X.class.newInstance it would instantiate it (like with new?), but I need not instantiated obj yet).

Update2

I tried this:

Object x = (Object) MyProxy.newInstance(map, this.classMap.get(interf).newInstance(), new Class[] {this.classMap.get(interf)});

But then I get this error:

Exception in thread "main" java.lang.IllegalArgumentException: class lab.X is not visible from class loader

My class X looks like this:

public class X implements I{
    String name;
    X(){}

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }


}

and it's interface looks like this:

public interface I {
    public String getName();
    public void setName(String name);
}
Andrius
  • 19,658
  • 37
  • 143
  • 243
  • what does your `Proxy.newProxyInstance` code look like ? – kiruwka Jan 01 '14 at 15:54
  • @kiruwka it is not changed. It takes that method from Proxy class which is implemented in Java Proxy class. – Andrius Jan 01 '14 at 16:00
  • what is `map` member of `MyProxy` is supposed to have ? Can you Show what you pass as a map when you try to call `X x = (X) MyProxy.newInstance(map, new Class[] {X.class})` – kiruwka Jan 01 '14 at 16:15
  • @kiruwka I'm following this example http://www.javaworld.com/article/2076233/java-se/explore-the-dynamic-proxy-api.html?page=2. There it uses empty initialized Hashmap, where it puts methods from class that is being used (like `setter` methods etc. You can see in a link example, where proxy.newInstance is being called with Hashmap as one of arguments in a method. – Andrius Jan 01 '14 at 16:26

3 Answers3

0

If I understand correctly, you are trying to instantiate an element of the class X.class? If that is the case, all you need to do is call X.class.newInstance().

Epiglottal Axolotl
  • 1,048
  • 8
  • 17
  • Well actually I need to pass it as an argument to proxy `newInstance` for example like this: `X x = (X) MyProxy.newInstance(map, new Class[] {X.class})`, but I can't get just it's type as `X` from `X.class`, because I can have `interf` parameter which is `X.class`. So that line gives an error when I need to assign variable `x` with type `X`(as I don't have that one and don't know how to get it if it's possible). And that thing you suggested I already do with `this.classMap.get(interf).newInstance`. But with this approach I skip proxy class, which I need to pass to first. – Andrius Jan 01 '14 at 15:06
  • You could declare it as an `Object`, but I'm not sure how you would typecast it back into something else... – Epiglottal Axolotl Jan 01 '14 at 16:03
  • So what would be the best way to use Inversion control container with Dynamic proxy? – Andrius Jan 01 '14 at 16:05
  • I really don't know - I've told you everything I can think of. – Epiglottal Axolotl Jan 01 '14 at 16:12
0

java.lang.IllegalArgumentException: class lab.X is not visible from class loader

Isn't this error message quite clear? You need to make sure that the same class loader is being used.

In here:

public static Object newInstance(Map map, Object obj, Class[] interfaces)
{
    return Proxy.newProxyInstance(map.getClass().getClassLoader(),
                                  interfaces,
                                  new MyProxy(map, obj));
}

you shouldn't be using the class loader of the map, I'd think you should use class loader of the target object or pass the proper class loader as a separate argument.

I think there are other problems in your approach as well, such as not synchronizing your container creation and not using generics for your proxy type.

eis
  • 51,991
  • 13
  • 150
  • 199
0

For the first part, class DIContainer, it would be better to use:

protected final Map<Class<?>, Class<?>> classMap;

protected DIContainer() {
    classMap = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
}

public <I> void regClass(Class<I> interf, Class<? extends I> classToReg) {
    this.classMap.put(interf, classToReg);
}

public <T> T create(Class<T> interf) throws Exception {
    Class<?> implClass = classMap.get(interf);
    if (implClass == null) {
        throw new Exception("No such class registered with " + interf.getName()
            + " interface");
    }
    Constructor<?> c = implClass.getConstructor();
    c.setAccessible(true); // If default constructor not public
    return interf.cast(c.newInstance());
}
  • Safe typing, though still partly at run-time.

  • More or less obsolete Hashtable replaced by equivalent

  • Calling newInstance on the class bypasses exceptions thrown on getting the default constructor and doing that one's newInstance.

The second part of the question: I fail to understand it; the interface class(es) is what is proxied. In the create above you could easily proxy Class<T> and yield a (seemingly) T object. And you could delegate in the proxy class to an T object created as in the create above.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Using your suggestion I get this error: `Exception in thread "main" java.lang.NoSuchMethodException: lab.X.()`, even though my X class has empty constructor. Also in `create` method that T is a typo (... T...), because It does not find any T. – Andrius Jan 01 '14 at 16:43
  • I have added `setAccessible`; as in X the constructor is not public. T is not a typo: `I obj = DIContainter.getInstance().create(I.class);` should do; the `T` type variable becomes the I interface. – Joop Eggen Jan 01 '14 at 17:21
  • Also should I use `` in regClass as I interface is just one example, there can be any interface, not just `I`. – Andrius Jan 01 '14 at 17:40
  • The ` ...` is a generic type parameter. It could have been any name, but the convention generally is taking one letter. In this case I should have used a distinguishing A or B. – Joop Eggen Jan 01 '14 at 19:49