AspectJ and Reflection

C

Conrad Eaglehill

Hi,

I'm trying to use reflection with AspectJ to identify when a needed
aspect is not in the build. For example, if I have two aspects
MenuBarAspect (with a variable JMenuBar Menubar) and EditMenuAspect
(with a variable JMenu Edit), and I remove MenuBarAspect from the
build, I don't want a compilation error when code within
EditMenuAspect tries to call Menubar.add(...).

The example that was given to me worked perfectly from another article
in this newsgroup (which was much appreciated, by the way!), works
perfectly in every way expected, and the code I'm using below is based
on it:

--------------------------------------------

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Notepad extends JFrame {
JScrollPane scrollpane;
public Notepad(){
setSize(800,600);
Container Notepad.cp = getContentPane();
cp.add(n.textArea = new JTextArea());
cp.add(n.scrollpane = new JScrollPane(n.textArea));

center.nCenter();
}

public static void main(String[] args){
new Notepad();
}
}
--------------------------------------

import javax.swing.*;

public aspect MenuBarAspect {
JMenuBar Notepad.Menubar = null;

pointcut menubar() : set(* Notepad.scrollpane);
before() : menubar() {
Notepad n = (Notepad) thisJoinPoint.getTarget();

if (n.Menubar == null)
n.setJMenuBar(n.Menubar = new JMenuBar());
}
}
--------------------------------------

import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public aspect EditMenuAspect {
JMenu Notepad.ediT = null;

pointcut editMenu() : call(void Notepad.setJMenuBar(..)) &&
if(check(Notepad.class, "Menubar",
JMenuBar.class));

after() : editMenu() {
Notepad n = (Notepad) thisJoinPoint.getTarget();
if (n.ediT == null)
n.Menubar.add(n.ediT = new JMenu("Edit"));
}

static boolean check(Class c, String memberName, Class memberType) {
try {
Field f = c.getField(memberName);
return f.getType().equals(memberType);
} catch (SecurityException e) { e.printStackTrace();
} catch (NoSuchFieldException e) { System.out.println("Check NSFE");
}
return false;
}
}

--------------------------------------

The code compiles without warning/error. When I run the program,
however, I get a NoSuchFieldException when check(..) is executed in
EditMenuAspect; the Menubar appears, but the Edit Menu does not.
Clearly (?), Menubar is declared, everything is spelled correctly, and
the aspects are in the right order. What am I missing?

Thanks in advance for any help,

Conrad Eaglehill
 
S

Sebastian Scheid

Hi Conrad,

Conrad Eaglehill said:
Hi,

I'm trying to use reflection with AspectJ to identify when a needed
aspect is not in the build. For example, if I have two aspects
MenuBarAspect (with a variable JMenuBar Menubar) and EditMenuAspect
(with a variable JMenu Edit), and I remove MenuBarAspect from the
build, I don't want a compilation error when code within
EditMenuAspect tries to call Menubar.add(...).

The example that was given to me worked perfectly from another article
in this newsgroup (which was much appreciated, by the way!), works

You're welcome :)
perfectly in every way expected, and the code I'm using below is based
on it:

[snip]

Your problem is is little bit tricky. The method Class#getField(String) only
searches for public fields (in the class, in subclasses and at last the
superclass. See api-docs). So adding a public modifier to Notepad.Menubar is
a solution. But this is not the whole truth.

Now the tricky part:
I thought another solution would be Class#getDeclaredField(String). This
method only searches fields in the class (not in sub/superclass) but also
gives you private fields. So if you don't want Notepad.Menubar to be public,
this method is your choice, isn't it?
Unfortunatly AspectJ gives introduced fields with visibility other than
public an internal name, so you cannot find them with the name one would
expect. See my supplement in method check().
--------------------------------------------
import javax.swing.*;

public aspect MenuBarAspect {
JMenuBar Notepad.Menubar = null;

Adding the public modifier the solution:
public JMenuBar Notepad.Menubar = null;

public is not so bad here because only aspects can see introduced fields.
pointcut menubar() : set(* Notepad.scrollpane);
before() : menubar() {
Notepad n = (Notepad) thisJoinPoint.getTarget();

if (n.Menubar == null)
n.setJMenuBar(n.Menubar = new JMenuBar());
}
}
--------------------------------------

import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public aspect EditMenuAspect {
JMenu Notepad.ediT = null;

pointcut editMenu() : call(void Notepad.setJMenuBar(..)) &&
if(check(Notepad.class, "Menubar",
JMenuBar.class));

after() : editMenu() {
Notepad n = (Notepad) thisJoinPoint.getTarget();
if (n.ediT == null)
n.Menubar.add(n.ediT = new JMenu("Edit"));

Remember that the aj compiler doesn't accept this if MenuBarAspect doesn't
introduce Notepad.MenuBar. Even if this advice is then never entered. See my
last posting.
}

static boolean check(Class c, String memberName, Class memberType) {
try {

Adding this:

System.out.println("Declared Fields----------------");
Field[] fArr = c.getDeclaredFields();
for(int i=0; i<fArr.length; i++) {
System.out.println(fArr);
}
System.out.println("Fields----------------");
fArr = c.getFields();
for(int i=0; i<fArr.length; i++) {
System.out.println(fArr);
}

gives some interesting information:

1. If Notepad.Menubar is public:

Declared Fields----------------
javax.swing.JScrollPane ajtest2.Notepad.scrollpane
public javax.swing.JMenu ajtest2.Notepad.ajc$interField$ajtest2$ediT
public javax.swing.JMenuBar ajtest2.Notepad.Menubar // everythings
alright here
private static final org.aspectj.lang.JoinPoint$StaticPart
ajtest2.Notepad.ajc$tjp_0
Fields----------------
public javax.swing.JMenu ajtest2.Notepad.ajc$interField$ajtest2$ediT
public javax.swing.JMenuBar ajtest2.Notepad.Menubar // and here too
public static final int javax.swing.JFrame.EXIT_ON_CLOSE
....

2. If Notepad.Menubar has package visibility:

Declared Fields----------------
....
public javax.swing.JMenuBar
ajtest2.Notepad.ajc$interField$ajtest2_MenuBarAspect$Menubar
// AspectJ added a public field with an internal name
....
Fields----------------
....
public javax.swing.JMenuBar
ajtest2.Notepad.ajc$interField$ajtest2_MenuBarAspect$Menubar
// because the field is public getField(String) finds it, too. But who would
have guessed this name?
....

3. Notepad.Menubar is private:
Declared Fields----------------
....
public javax.swing.JMenuBar ajtest2.Notepad.ajc$interField$ajtest2$Menubar
// Again: public field but different name
....
Fields----------------
....
public javax.swing.JMenuBar ajtest2.Notepad.ajc$interField$ajtest2$Menubar
....
Field f = c.getField(memberName);
return f.getType().equals(memberType);
} catch (SecurityException e) { e.printStackTrace();
} catch (NoSuchFieldException e) { System.out.println("Check NSFE");
}
return false;
}
}

Ok, the snippet you posted doesn't compile.

Conclusion: use the public modifier for Menubar. You don't break
encapsulation here because only aspects can see this public field.

Class#getDeclaredField(String) normally gives you private fields. But in
this case you have to know how AspectJ works internally to see that there is
not private field in the bytecode even if you introduced it as private. So
there is no difference between getField() and getDeclaredField() in this
case. AspectJ maps visibilities to internal names and all introduced fields
are public!

Sebastian
 
C

Conrad Eaglehill

Sebastian Scheid said:
Hi Conrad,
Your problem is is little bit tricky. The method Class#getField(String) only
searches for public fields (in the class, in subclasses and at last the
superclass. See api-docs). So adding a public modifier to Notepad.Menubar is
a solution. But this is not the whole truth.

Adding public as a modifier did the trick, so that worked; it let me
compile and gave me the Edit menu.
Now the tricky part:
I thought another solution would be Class#getDeclaredField(String). This
method only searches fields in the class (not in sub/superclass) but also
gives you private fields. So if you don't want Notepad.Menubar to be public,
this method is your choice, isn't it?
Unfortunatly AspectJ gives introduced fields with visibility other than
public an internal name, so you cannot find them with the name one would
expect. See my supplement in method check().

Remember that the aj compiler doesn't accept this if MenuBarAspect doesn't
introduce Notepad.MenuBar. Even if this advice is then never entered. See my
last posting.

How should I rewrite the code to have the aj compiler accept it, like
your example from previous? When I remove the MenuBarAspect, the
compiler gives me the usual errors ("n.Menubar cannot be resolved or
is not a field"). Did I misinterpret your example above?

----- Snipped some code -----
Ok, the snippet you posted doesn't compile.

When I posted this, it was late at night. :-( I'm sure I left some
out, but it was more to give the gist of what I was trying to do.
Conclusion: use the public modifier for Menubar.
Agreed.

You don't break
encapsulation here because only aspects can see this public field.

Really? That's very interesting, and something I did not know. If I
had some class (not aspect) that tried to access the publically
declared MenuBar from MenuBarAspect, would my program fail to compile
because Menubar is not directly in the Notepad.java file?
Class#getDeclaredField(String) normally gives you private fields. But in
this case you have to know how AspectJ works internally to see that there is
not private field in the bytecode even if you introduced it as private. So
there is no difference between getField() and getDeclaredField() in this
case. AspectJ maps visibilities to internal names and all introduced fields
are public!

Ha! That is a piece of info that could be useful later on. Thanks
again for your help, Sebastian!
Sebastian

--Conrad Eaglehill
 
C

Conrad Eaglehill

Sebastian,

Haha!

Ignore the other response to this post. I had forgotten I had removed
(or rather, commented out) the getfield method you gave me by example,
because I was focusing on my NoSuchFieldException. Now that I have
"uncommented" it out, everything works exactly as I'd like--no errors
when I remove a Menu, no errors when I remove a MenuBar. Perfect.

Once again, thanks for the help, and forgive my lapse in memory! :)

--Conrad Eaglehill
 
S

Sebastian Scheid

Conrad Eaglehill said:
Sebastian,

Haha!

Ignore the other response to this post. I had forgotten I had removed
(or rather, commented out) the getfield method you gave me by example,
because I was focusing on my NoSuchFieldException. Now that I have
"uncommented" it out, everything works exactly as I'd like--no errors
when I remove a Menu, no errors when I remove a MenuBar. Perfect.

Mmh. Do you mean, when removing MenubarAspect and leave EditMenuAspect in
your build, then the aj compiler accepts the lines

if (n.ediT == null)
n.Menubar.add(n.ediT = new JMenu("Edit"));
}

in EditMenuAspect even though there doesn't exist the field Notepad.Menubar?
I think it shouldn't! Do I mistake?
Once again, thanks for the help, and forgive my lapse in memory! :)

You're welcome!

Sebastian
 
C

Conrad Eaglehill

Sebastian Scheid said:
Mmh. Do you mean, when removing MenubarAspect and leave EditMenuAspect in
your build, then the aj compiler accepts the lines

if (n.ediT == null)
n.Menubar.add(n.ediT = new JMenu("Edit"));
}

in EditMenuAspect even though there doesn't exist the field Notepad.Menubar?
I think it shouldn't! Do I mistake?

No, you were absolutely right. In the message above, I had so focused
on the error I posted originally, that when I got the solution (make
Menubar public), I just removed MenuBarAspect to test if it would
compile. When it did NOT compile, just as you say, I thought something
was wrong, but by the time I realized I had commented out the code
that would've solved the problem ("JMenuBar RMenubar = (JMenuBar)
getfield(n, Menubar"); RMenubar.add(n.ediT = new JMenu("Edit"));"), I
had just sent the post.

Whoops, as they say.

--Conrad Eaglehill
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top