Problem: Calling methods of dynamically loaded inner classes at runtime

Discussion in 'Java' started by Thea, Aug 1, 2006.

  1. Thea

    Thea Guest

    Hello
    I need some help ^^

    While trying to dynamically load class and use it I have come across a
    problem that prevents any progress.
    I have a class that contains some inner classes (and I can't change
    this design). From outer class (called Test) I need to call inner
    classes at runtime. (inner classes are called XXXTest)

    I am using following code to read name of a class to be loaded and load
    it.
    Name of a class comes as a String in form of "package.Test$XXXTest"
    from configuration file.

    Map attrs = page.getAttributes(); //here I get names of
    classes to be loaded
    //names are obtained correctly, checked that (correctly==as provided in
    config file)

    for (Object o : attrs.keySet())
    {
    try {
    Class attributeClass = Class.forName(o.toString());
    Constructor attributeCon =
    attributeClass.getConstructor();
    Object attributeObject =
    attributeCon.newInstance();

    //making sure that calling execute routine makes sense
    if (attributeObject instanceof Module)
    ((Module)attributeObject).execute(page, request,
    response);

    } catch (Exception e) {
    e.getMessage();
    }
    }
    //This code is executed in class Test, and all loded classes are inner
    to Test

    Problem is that though compiler shows no error, it doesn't seem to run
    'execute' method of the inner class (any of them). I set some debug
    info in each inner class 'execute' method, but it simply won't appear
    on output and page context is empty (results are put out on page)
    'Funny' thing is that when I call it this way:
    CP2Test cp2 = new CP2Test();
    cp2.execute(page,request, response);
    it works... (CP2Test is one of the inner classes)
    But I can't do this... it must be done dynamically.
    I've been through FAQ but could find nothing...
    Is there something wrong with my code? Did I miss something important?
    Thank you for time you spent reading my question.
     
    Thea, Aug 1, 2006
    #1
    1. Advertising

  2. Thea

    Chris Uppal Guest

    Thea wrote:

    > } catch (Exception e) {
    > e.getMessage();
    > }


    You seem to be discarding errors silently -- not the best way to find out
    what's going wrong ;)


    > //making sure that calling execute routine makes sense
    > if (attributeObject instanceof Module)
    > ((Module)attributeObject).execute(page, request,


    Again, you are supressing errors silently. The guard test may make sense for
    deployed code but it is /currently/ preventing you from seeing any errors.
    Actually, unless the design requires cases where the loaded classes may not
    implement Module, then the test would probably be unwise even in deployed
    code -- for exactly the same reason.


    > Map attrs = page.getAttributes(); //here I get names of
    > classes to be loaded
    > //names are obtained correctly, checked that (correctly==as provided in
    > config file)


    It's possible that you are specifying the wrong names for the inner classes.
    The /real/ name of a class defined as:

    package my.stuff;
    class Outer
    {
    class Inner { }
    }

    is something like my.stuff.Outer$Inner. See the name of the classfile to see
    what you /should/ be providing.


    Also:

    > Constructor attributeCon = attributeClass.getConstructor();
    > Object attributeObject = attributeCon.newInstance();


    If your "inner" classes really are inner, rather than just static nested
    classes, then none of them will have a no-args constructor. All non-static
    nested classes require a pointer to their outer object, and that is passed in
    as a (hidden) parameter to each constructor, which is added by javac. When you
    call the constructor in normall code the compiler automatically adds a ref to
    the outer object to the parameters you pass -- but if can't do that when you
    are using reflection.

    Given that you expect the above to work at all, it sounds as if your design
    really calls for the nested classes to be static, in which case your use of
    Constructors should work as is. If not then you'll have to change the
    reflective code accordingly.

    -- chris
     
    Chris Uppal, Aug 1, 2006
    #2
    1. Advertising

  3. Thea

    Thea Guest

    > You seem to be discarding errors silently -- not the best way to find out
    I changed catch block so that it handles each error separately and
    removed the condition.
    Still, no error appeared

    > It's possible that you are specifying the wrong names for the inner classes.

    Checked that too, here everything is fine...

    > Also:
    >
    > Constructor attributeCon = attributeClass.getConstructor();
    > Object attributeObject = attributeCon.newInstance();
    >
    > If your "inner" classes really are inner, rather than just static nested
    > classes, then none of them will have a no-args constructor. All non-static
    > nested classes require a pointer to their outer object, and that is passed in
    > as a (hidden) parameter to each constructor, which is added by javac. When you
    > call the constructor in normall code the compiler automatically adds a ref to
    > the outer object to the parameters you pass -- but if can't do that when you
    > are using reflection.


    Well, classes are inner, and I rather cannot make them static, because
    in all cases ececute() is using non-static variables being fields of
    outer class...
    If I try to make inner classes static, environment says that you cannot
    reference non-static variable from static content...
    Do you have any idea how to get that magic pointer to outer object and
    pass it?... Or maybe how to make it accept such reference?...

    I also played a little bit more with the code
    and for such code

    Map attrs = page.getAttributes();
    for (Object o : attrs.keySet())
    {
    try {
    1) Class attributeClass =
    Class.forName(o.toString());
    2) log.info("AAAA"+attributeClass.toString());
    3) Constructor attributeCon =
    attributeClass.getConstructor();
    4) log.info("BBBB"+attributeCon.toString());
    5) Object attributeObject =
    attributeCon.newInstance();
    6) log.info("CCCC"+attributeObject.toString());
    7) ((Module)attributeObject).execute(page, request,
    response);


    }
    catch (InstantiationException e) {e.getMessage();}
    catch (IllegalAccessException e) {e.getMessage();}
    catch (InvocationTargetException e) {e.getMessage();}
    catch (NoSuchMethodException e) {e.getMessage();}
    catch (ClassNotFoundException e) {e.getMessage();}
    }

    got output:
    [2006-08-01 12:20:33,570] INFO [Test] AAAAclass
    com.cp2portal.util.Test$CP2Test
    [2006-08-01 12:20:33,576] INFO [Test] AAAAclass
    com.cp2portal.util.Test$MailTest
    It seems not to execute code from line 3-7. Anything that is outside
    try-catch block works fine.No errors appeared...
    I'm really confused.
     
    Thea, Aug 1, 2006
    #3
  4. On 1 Aug 2006 03:24:01 -0700, Thea wrote:
    > I changed catch block so that it handles each error separately and
    > removed the condition. Still, no error appeared


    [...]

    > catch (InstantiationException e) {e.getMessage();}
    > catch (IllegalAccessException e) {e.getMessage();}
    > catch (InvocationTargetException e) {e.getMessage();}
    > catch (NoSuchMethodException e) {e.getMessage();}
    > catch (ClassNotFoundException e) {e.getMessage();}


    That's because e.getMessage() does not display anything, as you seem
    to think it does. Try using e.printStackTrace() instead.

    /gordon

    --
    [ don't email me support questions or followups ]
    g o r d o n + n e w s @ b a l d e r 1 3 . s e
     
    Gordon Beaton, Aug 1, 2006
    #4
  5. Thea

    Chris Uppal Guest

    Thea wrote:

    > I changed catch block so that it handles each error separately and
    > removed the condition.
    > Still, no error appeared


    But you still seem just to be calling e.getMessage() -- that has no effect
    except to return a String, which you discard. Try logging the returned value,
    or using e.printStackTrace() instead, or something.


    > Do you have any idea how to get that magic pointer to outer object and
    > pass it?... Or maybe how to make it accept such reference?...


    You have to /decide/ which instance(s) of the outer class you want these
    objects to refer to. That to say: /which/ outer object's fields you want them
    to have access to. Please do note that this is an /extremely/ important
    aspect of your design -- it isn't just a trivial messing around in order to
    satisfy petty-fogging restrictions in the compiler!

    Once you have decided which object(s) you want the inner class instance to
    refer to, you just pass a reference to it (or them) to the inner classes'
    constructors.

    -- chris
     
    Chris Uppal, Aug 1, 2006
    #5
  6. Thea

    Thea Guest

    e.printStackTrace() worked fine ^^ thanks for this advice ^^ Now I'll
    remember that getMessage() is not the best choice ever ^^'.
    I got NoSuchMethodException...
    I suppose it's because of getting the constructor - error appears on
    line

    Constructor attributeCon = attributeClass.getConstructor();

    I have decided which fields of outer class will be accessed and turned
    them static.
    I also had to add static reference to outer class, as inner class uses
    one of the outer class inherited methods...
    Now I'm not sure how to pass arguments in line above. I don't really
    understand notation used in documentation:

    public Constructor<T> getConstructor(Class... parameterTypes)

    it says it should be array of parameters... though I have no idea how
    to create it (I mean, what type(s) should I use) I tried several things
    but I don't seem to catch the idea...
     
    Thea, Aug 1, 2006
    #6
  7. Thea

    Chris Uppal Guest

    Thea

    Please don't take this as an insult, but you seem to be a little out of your
    depth. Reflective programming requires a good understanding of the
    fundamentals of Java, which perhaps you are not as strong on yet as you need to
    be.

    Anyway, I've put together a little working example, which maybe will help you
    to sort this stuff out. I'll put it at the end of this message.

    > Constructor attributeCon = attributeClass.getConstructor();


    One potential problem here (and I admit it's a bit obscure -- I had forgotten
    all about it until I tried it just now), is that if your nested or inner
    classes have a default constructor (you don't define one explicitly) the one
    that javac creates for you is not public unless the nested classes are public
    themselves. So, if that's the situation, you will have to use
    Class.getDeclaredConstructor() instead of Class.getConstructor(), since the
    latter only lists public constructors.

    Javac always does create a default constructor if none is defined explicitly,
    for any kind of class (not just nested), but it takes its access from the
    class. If the class is nested /and/ declared private, then javac does
    something even more odd and inconvenient -- which I hope you won't need to
    worry about; we'll deal with that if we have to.


    > public Constructor<T> getConstructor(Class... parameterTypes)
    >
    > it says it should be array of parameters... though I have no idea how
    > to create it (I mean, what type(s) should I use) I tried several things
    > but I don't seem to catch the idea...


    If you are passing an array then the elements should be the java.lang.Class
    objects corresponding to the parameter types. If you know the class in advance
    then its easiest to use a "class literal", something like
    String.class
    which evaluates to the class object for java.lang.String. Since
    getConstructor() had been retrofitted to use variadic parameter lists (which
    just means that the compiler builds and initialises the array for you from the
    parameters you supply when you call it), you can write, for instance:
    cl.getConstructor(Class1.class, Class2.class, Integer.class);
    rather than having to create an array explicitly.

    -- chris


    ========== Outer.java ============
    interface Doable
    {
    void doit();
    }

    public class Outer
    {
    private static String staticField = "(static in class Outer)";
    private String instanceName;

    Outer(String str)
    {
    instanceName = str;
    }

    static class StaticNested
    implements Doable
    {
    public void
    doit()
    {
    System.out.printf("\tdoit called from %s%n", this);
    System.out.printf("\tstaticField = %s%n", staticField);
    System.out.println();
    }
    }

    class Inner
    implements Doable
    {
    public void
    doit()
    {
    System.out.printf("\tdoit() called from %s%n", this);
    System.out.printf("\tstaticField = %s%n", staticField);
    System.out.printf("\tinstanceName = %s%n", instanceName);
    System.out.println();
    }
    }

    void
    example()
    throws Throwable // only to keep the example short !!
    {
    System.out.printf("*** I am %s ***%n", instanceName);

    System.out.println("exampleWithoutReflection()");
    exampleWithoutReflection();
    System.out.println("exampleWithReflection()");
    exampleWithReflection();

    System.out.println("**** End ***");
    }

    void
    exampleWithoutReflection()
    {
    new StaticNested().doit();
    new Inner().doit();
    }

    void
    exampleWithReflection()
    throws Throwable // only to keep the example short !!
    {
    Class cl;
    java.lang.reflect.Constructor ctor;
    Doable doable;

    // NB: we have to use getDeclaredConstructor()
    // instead of getConstructor() since the implicit
    // ctors for the nested classes are not public

    // static nested class
    cl = Class.forName("Outer$StaticNested");
    ctor = cl.getDeclaredConstructor();
    doable = (Doable)ctor.newInstance();
    doable.doit();

    // inner class, we have to pass ourself to the ctor
    cl = Class.forName("Outer$Inner");
    ctor = cl.getDeclaredConstructor(Outer.class);
    doable = (Doable)ctor.newInstance(this);
    doable.doit();
    }

    public static void
    main(String[] args)
    throws Throwable // only to keep the example short !!
    {
    Outer joan = new Outer("Joan");
    Outer fred = new Outer("Fred");

    joan.example();
    fred.example();
    }
    }
    ==========================
     
    Chris Uppal, Aug 1, 2006
    #7
  8. Thea

    Thea Guest

    Solved: Calling methods of dynamically loaded inner classes at runtime

    Chris, I'm not going to take truth as an insult ^^'
    I really didn't get this all as I should... (btw. I had to chceck this
    phrase ^^'' ). I have programmed in Java some stuff, but never needed
    those mechanisms...
    And, to be honest that was my first trial to do such thing...

    Thank you for example.
    That was what I needed to better understand what I am trying to do.

    I thought I will have to make inner classes static AND private, but
    with your example I could ommit static which made it much simpler.
    So finally it works ^^

    Thanks again
    Thea
     
    Thea, Aug 2, 2006
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Neil Zanella

    Java inner classes and static methods

    Neil Zanella, Oct 27, 2003, in forum: Java
    Replies:
    1
    Views:
    2,648
    Chris Smith
    Oct 27, 2003
  2. lonelyplanet999
    Replies:
    1
    Views:
    2,227
    VisionSet
    Nov 13, 2003
  3. Udo Corban
    Replies:
    0
    Views:
    3,854
    Udo Corban
    Jan 23, 2004
  4. Carlo v. Dango
    Replies:
    14
    Views:
    1,038
    Alex Martelli
    Oct 19, 2003
  5. Pyenos
    Replies:
    2
    Views:
    389
    Pyenos
    Dec 27, 2006
Loading...

Share This Page