Problem: Calling methods of dynamically loaded inner classes at runtime

T

Thea

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.
 
C

Chris Uppal

Thea said:
} 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
 
T

Thea

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.
 
G

Gordon Beaton

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
 
C

Chris Uppal

Thea said:
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
 
T

Thea

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...
 
C

Chris Uppal

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();
}
}
==========================
 
T

Thea

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top