001/* 002 * This library is free software; you can redistribute it and/or 003 * modify it under the terms of the GNU Lesser General Public 004 * License as published by the Free Software Foundation; either 005 * version 2.1 of the License, or (at your option) any later version. 006 * 007 * This library is distributed in the hope that it will be useful, 008 * but WITHOUT ANY WARRANTY; without even the implied warranty of 009 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 010 * Lesser General Public License for more details. 011 * 012 * You should have received a copy of the GNU Lesser General Public 013 * License along with this library; if not, write to the Free Software 014 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 015 */ 016package org.openstreetmap.josm.gui.util; 017 018import java.lang.ref.WeakReference; 019 020import javax.swing.JComponent; 021import javax.swing.RepaintManager; 022import javax.swing.SwingUtilities; 023 024import org.openstreetmap.josm.tools.Logging; 025 026/** 027 * <p>This class is used to detect Event Dispatch Thread rule violations</p> 028 * 029 * <p>This is a modification of original idea of Scott Delap.</p> 030 * 031 * @author Scott Delap 032 * @author Alexander Potochkin 033 */ 034public class CheckThreadViolationRepaintManager extends RepaintManager { 035 private WeakReference<JComponent> lastComponent; 036 037 @Override 038 public synchronized void addInvalidComponent(JComponent component) { 039 checkThreadViolations(component); 040 super.addInvalidComponent(component); 041 } 042 043 @Override 044 public void addDirtyRegion(JComponent component, int x, int y, int w, int h) { 045 checkThreadViolations(component); 046 super.addDirtyRegion(component, x, y, w, h); 047 } 048 049 private void checkThreadViolations(JComponent c) { 050 if (!SwingUtilities.isEventDispatchThread()) { 051 boolean repaint = false; 052 boolean fromSwing = false; 053 boolean imageUpdate = false; 054 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 055 for (StackTraceElement st : stackTrace) { 056 if (repaint && st.getClassName().startsWith("javax.swing.") && 057 // for details see https://swinghelper.dev.java.net/issues/show_bug.cgi?id=1 058 !st.getClassName().startsWith("javax.swing.SwingWorker")) { 059 fromSwing = true; 060 } 061 if (repaint && "imageUpdate".equals(st.getMethodName())) { 062 imageUpdate = true; 063 } 064 if ("repaint".equals(st.getMethodName())) { 065 repaint = true; 066 fromSwing = false; 067 } 068 if ("read".equals(st.getMethodName()) && "javax.swing.JEditorPane".equals(st.getClassName())) { 069 // Swing reads html from a background thread 070 return; 071 } 072 } 073 if (imageUpdate) { 074 //assuming it is java.awt.image.ImageObserver.imageUpdate(...) 075 //image was asynchronously updated, that's ok 076 return; 077 } 078 if (repaint && !fromSwing) { 079 //no problems here, since repaint() is thread safe 080 return; 081 } 082 //ignore the last processed component 083 if (lastComponent != null && c == lastComponent.get()) { 084 return; 085 } 086 lastComponent = new WeakReference<>(c); 087 violationFound(c, stackTrace); 088 } 089 } 090 091 protected static void violationFound(JComponent c, StackTraceElement[] stackTrace) { 092 Logging.error(""); 093 Logging.error("EDT violation detected"); 094 Logging.error(c.toString()); 095 for (StackTraceElement st : stackTrace) { 096 Logging.error("\tat " + st); 097 } 098 } 099}