Instrumentation of JComponent

L

Lethal Possum

Hello everyone,

I am working on a little project to learn how to use the
Instrumentation framework work. I want to append the current class
name in the tooltip of every JComponent. Seems simple enough, right.
So I started by writing a basic ClassFileTransformer that prints every
class name. I also created a very simple GUI to test my transformer.

It all seems to work fine except that it never see the class
JComponent. At some point it prints "javax/swing/JComponent$1" but
never "javax/swing/JComponent". However if I debug my test code, I can
see that if I leave the mouse over the test label, I stop in my
breakpoint in JComponent.getToolTipText(). How is that possible
without loading the JComponent class? Or am I not understanding what's
going on here?

I've copied my source code below. Thanks in advance for your help.

Cheers,

Tom

=== Source code ===

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Premain {

public static void premain(String agentArguments, Instrumentation
instrumentation) {
instrumentation.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
System.out.println(className);
return classfileBuffer;
}
});
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}

private static void createAndShowGUI() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("test");
label.setToolTipText("test");
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}

}
 
J

John B. Matthews

Lethal Possum said:
It all seems to work fine except that it never see the class
JComponent. At some point it prints "javax/swing/JComponent$1" but
never "javax/swing/JComponent". However if I debug my test code, I
can see that if I leave the mouse over the test label, I stop in my
breakpoint in JComponent.getToolTipText(). How is that possible
without loading the JComponent class? Or am I not understanding
what's going on here?

The only anonymous inner class I see in JComponent is in revalidate().

<http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#revalidate()>
 
M

markspace

I am working on a little project to learn how to use the
Instrumentation framework work. I want to append the current class
name in the tooltip of every JComponent.


Yeow. OK, if I go to the java.lang.instrument package description, the
first thing I see is this:

<http://download.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html>


"Package java.lang.instrument Description

Provides services that allow Java programming language agents to
instrument programs running on the JVM. The mechanism for
instrumentation is modification of the byte-codes of methods.

Package Specification

An agent is deployed as a JAR file. An attribute in the JAR file
manifest specifies the agent class which will be loaded to start the
agent. For implementations that support a command-line interface, an
agent is started by specifying an option on the command-line.
Implementations may also support a mechanism to start agents some time
after the VM has started. For example, an implementation may provide a
mechanism that allows a tool to attach to a running application, and
initiate the loading of the tool's agent into the running application.
The details as to how the load is initiated, is implementation dependent.

Command-Line Interface

On implementations with a command-line interface, an agent is started by
adding this option to the command-line:

-javaagent:jarpath[=options]"


So it looks like the whole instrument thing is used by debuggers. Maybe
that's why you only see the effect in the debugger: you have to attach
an "agent" for it all to work. I'm guessing here but that's what it
looks like to me.

Check out the description of the "javaagent" option, and the options for
the Manifest file. Those might tell you more about how to configure and
activate this instrumentation package.
 
L

Lethal Possum

The only anonymous inner class I see in JComponent is in revalidate().

<http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.h...()>

Hello,

Actually javax/swing/JComponent$1 is an anonymous
RequestFocusController stored in the field focusController (static
final) of JComponent. So it looks like the JVM loads a static field of
JCoponent bu not the JComponent class itself. In theory that possible
but in this case I don't think it is so because my breakpoint stops in
JComponent.getToolTipText(). Very confusing...

Thanks,

Tom
 
L

Lethal Possum

I am working on a little project to learn how to use the
Instrumentation framework work. I want to append the current class
name in the tooltip of every JComponent.

Yeow.  OK, if I go to the java.lang.instrument package description, the
first thing I see is this:

<http://download.oracle.com/javase/6/docs/api/java/lang/instrument/pac...>

"Package java.lang.instrument Description

Provides services that allow Java programming language agents to
instrument programs running on the JVM. The mechanism for
instrumentation is modification of the byte-codes of methods.

Package Specification

An agent is deployed as a JAR file. An attribute in the JAR file
manifest specifies the agent class which will be loaded to start the
agent. For implementations that support a command-line interface, an
agent is started by specifying an option on the command-line.
Implementations may also support a mechanism to start agents some time
after the VM has started. For example, an implementation may provide a
mechanism that allows a tool to attach to a running application, and
initiate the loading of the tool's agent into the running application.
The details as to how the load is initiated, is implementation dependent.

Command-Line Interface

On implementations with a command-line interface, an agent is started by
adding this option to the command-line:

-javaagent:jarpath[=options]"

So it looks like the whole instrument thing is used by debuggers.  Maybe
that's why you only see the effect in the debugger:  you have to attach
an "agent" for it all to work.  I'm guessing here but that's what it
looks like to me.

Check out the description of the "javaagent" option, and the options for
the Manifest file.  Those might tell you more about how to configure and
activate this instrumentation package.

Hi Mark,

I knew that, sorry if I wasn't clear. I put my class in a jar where I
point both the Main-Class and Premain-Class attributes to my Premain
class. Then I run it using:

java -javaagent:premain.jar -jar Premain.jar

That's how I get it to print the loaded classes but no JComponent
anywhere.

Thanks,

Tom
 
T

Thomas Leplus

On 9/20/2010 2:29 AM, Lethal Possum wrote:
Yeow.  OK, if I go to the java.lang.instrument package description, the
first thing I see is this:

"Package java.lang.instrument Description
Provides services that allow Java programming language agents to
instrument programs running on the JVM. The mechanism for
instrumentation is modification of the byte-codes of methods.
Package Specification
An agent is deployed as a JAR file. An attribute in the JAR file
manifest specifies the agent class which will be loaded to start the
agent. For implementations that support a command-line interface, an
agent is started by specifying an option on the command-line.
Implementations may also support a mechanism to start agents some time
after the VM has started. For example, an implementation may provide a
mechanism that allows a tool to attach to a running application, and
initiate the loading of the tool's agent into the running application.
The details as to how the load is initiated, is implementation dependent.
Command-Line Interface
On implementations with a command-line interface, an agent is started by
adding this option to the command-line:
-javaagent:jarpath[=options]"

So it looks like the whole instrument thing is used by debuggers.  Maybe
that's why you only see the effect in the debugger:  you have to attach
an "agent" for it all to work.  I'm guessing here but that's what it
looks like to me.
Check out the description of the "javaagent" option, and the options for
the Manifest file.  Those might tell you more about how to configure and
activate this instrumentation package.

Hi Mark,

I knew that, sorry if I wasn't clear. I put my class in a jar where I
point both the Main-Class and Premain-Class attributes to my Premain
class. Then I run it using:

java -javaagent:premain.jar -jar Premain.jar

That's how I get it to print the loaded classes but no JComponent
anywhere.

Thanks,

Tom

Also I wondered if the fact that JComponent is abstract could be the
issue but I don't think so because I don't see JLabel either in the
list. Yet it is not abstract and it must be loaded since I explicitly
use it in createAndShowGUI().

Anyone as an idea?

Cheers,

Tom
 
J

John B. Matthews

Lethal Possum said:
The only anonymous inner class I see in JComponent is in revalidate().

<http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.h...()>
[...]
Actually javax/swing/JComponent$1 is an anonymous
RequestFocusController stored in the field focusController (static
final) of JComponent. So it looks like the JVM loads a static field
of JCoponent bu not the JComponent class itself. In theory that
possible but in this case I don't think it is so because my
breakpoint stops in JComponent.getToolTipText(). Very confusing...

I see what you mean; JComponent$2 must be the Runnable in revalidate().

$ jar tf classes.jar | grep JComponent
javax/swing/JComponent$ActionStandin.class
javax/swing/JComponent$ReadObjectCallback.class
javax/swing/JComponent$IntVector.class
javax/swing/JComponent$KeyboardState.class
javax/swing/JComponent$AccessibleJComponent$AccessibleContainerHandle...
javax/swing/JComponent$AccessibleJComponent$AccessibleFocusHandler.class
javax/swing/JComponent$2.class
javax/swing/JComponent$AccessibleJComponent.class
javax/swing/JComponent$1.class
javax/swing/JComponent.class

FWIW, I can see other classes loading as required, e.g.
JComponent$KeyboardState, using the the -verbose option.

$ java -verbose -cp build/classes NewJavaGUI | grep JComponent
[Loaded javax.swing.JComponent from /System/.../classes.jar]
[Loaded javax.swing.JComponent$1 from /System/.../classes.jar]
[Loaded javax.swing.JComponent$IntVector from /System/.../classes.jar]
 
M

markspace

I knew that, sorry if I wasn't clear. I put my class in a jar where I
point both the Main-Class and Premain-Class attributes to my Premain
class.


OK, so you've read the docs at least. No I don't know what is going on.
Can I ask you what you hope to get out of this? Are you actually
thinking of making your own instrumentation or is this a way to domore
general runtime processing on classes?
 
T

Thomas Leplus

OK, so you've read the docs at least.  No I don't know what is going on..
  Can I ask you what you hope to get out of this?  Are you actually
thinking of making your own instrumentation or is this a way to domore
general runtime processing on classes?

I just want to understand how instrumentation work in Java, get a
feeling of what it can do and how easy it is to use.

I prefer to learn by example so I thought I would use this as an
opportunity to create a simple tool that I always wished I had for my
day job: a tool that would allow me to figure out quickly where in the
code of an application is a given piece of GUI.

What I mean is that the application I work on has thousands of window,
frames and panel classes. So as you can imagine, it is sometimes hard
to figure out let say what is the class name of the panel you are
looking at. My idea was to create an instrumentation agent that would
add some code to JComponent.getToolTipText() so that if I let my mouse
a few seconds over a GUI element, it would the tooltip's owner class,
the class of the parent panel and so on.

There is probably easier ways to do all that but I'd like to do it
myself from scratch using instrumentation as an exercise. And so far I
find instrumentation interesting but I am stuck because my agent never
get access to the one class I'd like to instrument, JComponent!

Thanks,

Tom
 
T

Thomas Leplus

 Lethal Possum said:
The only anonymous inner class I see in JComponent is in revalidate()..
<http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.h...()>
[...]
Actually javax/swing/JComponent$1 is an anonymous
RequestFocusController stored in the field focusController (static
final) of JComponent. So it looks like the JVM loads a static field
of JCoponent bu not the JComponent class itself. In theory that
possible but in this case I don't think it is so because my
breakpoint stops in JComponent.getToolTipText(). Very confusing...

I see what you mean; JComponent$2 must be the Runnable in revalidate().

$ jar tf classes.jar | grep JComponent
javax/swing/JComponent$ActionStandin.class
javax/swing/JComponent$ReadObjectCallback.class
javax/swing/JComponent$IntVector.class
javax/swing/JComponent$KeyboardState.class
javax/swing/JComponent$AccessibleJComponent$AccessibleContainerHandle...
javax/swing/JComponent$AccessibleJComponent$AccessibleFocusHandler.class
javax/swing/JComponent$2.class
javax/swing/JComponent$AccessibleJComponent.class
javax/swing/JComponent$1.class
javax/swing/JComponent.class

FWIW, I can see other classes loading as required, e.g.
JComponent$KeyboardState, using the the -verbose option.

$ java -verbose -cp build/classes NewJavaGUI | grep JComponent
[Loaded javax.swing.JComponent from /System/.../classes.jar]
[Loaded javax.swing.JComponent$1 from /System/.../classes.jar]
[Loaded javax.swing.JComponent$IntVector from /System/.../classes.jar]

You bring up an interesting point here, I ran my test code with -
verbose option and it listed 1329 classes loaded while my agent is
only printing 764. So clearly I was mistaken to think that all the
classes loaded by the JVM where passed through the agent. The question
now is which classes are, and which are not. What are the rules?

Cheers,

Tom
 
M

markspace

I just want to understand how instrumentation work in Java, get a
feeling of what it can do and how easy it is to use.


Experimentation is fine of course. It just seemed to be a really
esoteric thing to use if you're not actually planning on writing a
debugger or profiler or something similar.

There is probably easier ways to do all that but I'd like to do it
myself from scratch using instrumentation as an exercise.


Check out a book called "Swing Hacks" by O'Reilly. They have a class
that highlights GUI components you mouse over, and dumps some stats to
the glass pane so you ca look at them too. Pretty short, already
debugged, and work decently ime.
 
L

Lethal Possum

Experimentation is fine of course.  It just seemed to be a really
esoteric thing to use if you're not actually planning on writing a
debugger or profiler or something similar.


Check out a book called "Swing Hacks" by O'Reilly.  They have a class
that highlights GUI components you mouse over, and dumps some stats to
the glass pane so you ca look at them too.  Pretty short, already
debugged, and work decently ime.

Thanks for the tip. I found it on Google Books. It's an interesting
idea but you have to wrap your frame in the glass pane. In my case, I
have hundreds if not thousands of frames!

To be honest, usually I know which frame I am looking at, just not
which panel or component (they are loaded dynamically depending on the
context) so I could use the glass pane trick most of the time.

But what I liked with the instrumentation, aside from learning
something new, is that once it's done, I can plug it in at any time
without any change to the code. As you said it would be a sort of
custom purpose (read very limited) debugger.

Thanks anyway,

Tom
 
A

Alessio Stalla

Hello everyone,

I am working on a little project to learn how to use the
Instrumentation framework work. I want to append the current class
name in the tooltip of every JComponent. Seems simple enough, right.
So I started by writing a basic ClassFileTransformer that prints every
class name. I also created a very simple GUI to test my transformer.

It all seems to work fine except that it never see the class
JComponent. At some point it prints "javax/swing/JComponent$1" but
never "javax/swing/JComponent". However if I debug my test code, I can
see that if I leave the mouse over the test label, I stop in my
breakpoint in JComponent.getToolTipText(). How is that possible
without loading the JComponent class? Or am I not understanding what's
going on here?

I've copied my source code below. Thanks in advance for your help.

Cheers,

Tom

=== Source code ===

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Premain {

        public static void premain(String agentArguments, Instrumentation
instrumentation) {
                instrumentation.addTransformer(new ClassFileTransformer() {
                    public byte[] transform(ClassLoader loader,
                                            String className,
                                            Class<?> classBeingRedefined,
                                            ProtectionDomain protectionDomain,
                                            byte[] classfileBuffer)
                            throws IllegalClassFormatException {
                        System.out.println(className);
                        return classfileBuffer;
                    }
                    });
        }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel label = new JLabel("test");
        label.setToolTipText("test");
        frame.getContentPane().add(label);
        frame.pack();
        frame.setVisible(true);
    }

}

Wild guess: since your premain method is in the same class as
createAndShowGUI, chances are that when loading the Premain class the
classes referenced by it are also loaded, before premain is run. And
in turn, the classes referenced by them. That's why you see neither
JComponent nor JLabel (nor, I guess, JFrame). As for JComponent$1,
maybe there's some optimization that makes inner classes only be
loaded when they're first accessed.

Cheers,
Alessio
 
L

Lew

Alessio said:
As for JComponent$1,
maybe there's some optimization that makes inner classes only be
loaded when they're first accessed.

Isn't that the norm for loading classes?

JLS chapter 12: "The initial attempt to execute the method /main/ of
class /Test/ discovers that the class /Test/ is not loaded ..."

I'm pretty sure being an inner or nested class doesn't alter that.
 
L

Lethal Possum

Hello everyone,
I am working on a little project to learn how to use the
Instrumentation framework work. I want to append the current class
name in the tooltip of every JComponent. Seems simple enough, right.
So I started by writing a basic ClassFileTransformer that prints every
class name. I also created a very simple GUI to test my transformer.
It all seems to work fine except that it never see the class
JComponent. At some point it prints "javax/swing/JComponent$1" but
never "javax/swing/JComponent". However if I debug my test code, I can
see that if I leave the mouse over the test label, I stop in my
breakpoint in JComponent.getToolTipText(). How is that possible
without loading the JComponent class? Or am I not understanding what's
going on here?
I've copied my source code below. Thanks in advance for your help.


=== Source code ===
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Premain {
        public static void premain(String agentArguments, Instrumentation
instrumentation) {
                instrumentation.addTransformer(new ClassFileTransformer() {
                    public byte[] transform(ClassLoader loader,
                                            String className,
                                            Class<?> classBeingRedefined,
                                            ProtectionDomain protectionDomain,
                                            byte[] classfileBuffer)
                            throws IllegalClassFormatException {
                        System.out.println(className);
                        return classfileBuffer;
                    }
                    });
        }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
    private static void createAndShowGUI() {
        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel label = new JLabel("test");
        label.setToolTipText("test");
        frame.getContentPane().add(label);
        frame.pack();
        frame.setVisible(true);
    }

Wild guess: since your premain method is in the same class as
createAndShowGUI, chances are that when loading the Premain class the
classes referenced by it are also loaded, before premain is run. And
in turn, the classes referenced by them. That's why you see neither
JComponent nor JLabel (nor, I guess, JFrame). As for JComponent$1,
maybe there's some optimization that makes inner classes only be
loaded when they're first accessed.

Cheers,
Alessio

Yes, I think you are right Alessio! I moved my test code (the methods
main() and createAndShowGUI()) to a separate class and now I see them
being passed through my agent. I guess previously they were loaded
before the agent itself.

That's great, thanks so much to all of you for your help.

Cheers,

Tom
 
A

Alessio Stalla

Isn't that the norm for loading classes?

JLS chapter 12: "The initial attempt to execute the method /main/ of
class /Test/ discovers that the class /Test/ is not loaded ..."

I'm pretty sure being an inner or nested class doesn't alter that.

Yeah, I didn't really think about it. Inner/nested classes are a Java-
only concept, for the JVM all classes are treated the same. The JLS is
explicitly very loose on the exact moment when symbolic references in
classes are resolved, so everything from a completely eager resolution
strategy to a completely lazy one is permitted, and the Sun JVM seems
to be on the lazy side.
 

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,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top