概要

JXLayerDebugPainterを使用したコンポーネントの再描画を可視化するjavaagentでデバッグを行います。

DebugPainterAgent.png

ソースコード

package org.jdesktop.swinghelper.layer.demo;
import java.awt.*;
import java.lang.instrument.*;
import javax.swing.*;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.ext.DebugRepaintingUI;

public class DebugPainterAgent {
  public static void premain(String args) throws Exception {
    //System.out.println("premain");
    agentmain(args);
  }
  public static void agentmain(String args) throws Exception {
    System.out.println("agentmain start");
    new Thread() {
      @Override public void run() {
        System.out.println("Thread run");
        Frame[] list = Frame.getFrames();
        while (list.length==0) { //FIX ME!
          try {
            sleep(1000);
          } catch (Exception e) {
            e.printStackTrace();
          }
          System.out.print(".");
          list = Frame.getFrames();
        }
        System.out.println(" ");
        System.out.println("Loop out");
        replaceLayer();
        System.out.println("Thread exit");
      }
    } .start();
    System.out.println("agentmain end");
  }
  private static void replaceLayer() {
    for (Frame f:Frame.getFrames()) {
      if (f instanceof JFrame) {
        final JFrame frame = (JFrame)f;
        EventQueue.invokeLater(new Runnable() {
          @Override public void run() {
            System.out.println("replace layer");
            JComponent c = (JComponent)frame.getContentPane();
            JXLayer<JComponent> layer = new JXLayer<JComponent>(c);
            layer.setUI(new DebugRepaintingUI());
            frame.setContentPane(layer);
            frame.pack();
          }
        });
      }
    }
  }
}

解説

例えば、repaintが無限ループしているバグのあるサンプルを以下のように起動すると、JLabelの描画が無駄に繰り返され、CPUの使用率も異常になっている様子がすぐに分かります。

javac RepaintLoopTest.java
java -javaagent:debugpainter.jar RepaintLoopTest
REM java -javaagent:debugpainter.jar -jar example.jar
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class RepaintLoopTest {
  public JComponent makeUI() {
    String[] columnNames = {"String", "Integer", "Boolean"};
    Object[][] data = {
      {"aaa", 12, true}, {"bbb", 5, false},
      {"CCC", 92, true}, {"DDD", 0, false}
    };
    DefaultTableModel model = new DefaultTableModel(data, columnNames) {
      @Override public Class<?> getColumnClass(int column) {
        return getValueAt(0, column).getClass();
      }
    };
    JTable table = new JTable(model);
    table.setAutoCreateRowSorter(true);

    JLabel label = new JLabel("aaaaaaaaa") {
      int h = 10;
      int d = 1;
      @Override protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        repaint(); //BUG: an infinite repaint loop
      }
    };
    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    p.add(label, BorderLayout.SOUTH);
    p.setPreferredSize(new Dimension(320, 240));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new RepaintLoopTest().makeUI());
    f.pack(); //setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}

参考リンク

コメント