| 
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  */
 016
 017 package org.jdesktop.swinghelper.debug;
 018
 019 import org.slf4j.Logger;
 020 import org.slf4j.LoggerFactory;
 021
 022 import javax.swing.JComponent;
 023 import javax.swing.RepaintManager;
 024 import javax.swing.SwingUtilities;
 025 import java.applet.Applet;
 026 import java.awt.Component;
 027 import java.awt.Dimension;
 028 import java.awt.Image;
 029 import java.awt.Rectangle;
 030 import java.awt.Window;
 031 import java.lang.ref.WeakReference;
 032
 033 import static griffon.core.GriffonExceptionHandler.sanitize;
 034
 035
 036 /**
 037  * <p>This class is used to detect Event Dispatch Thread rule violations<br>
 038  * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How to Use Threads</a>
 039  * for more info</p>
 040  * <p/>
 041  * <p>This is a modification of original idea of Scott Delap<br>
 042  * Initial version of ThreadCheckingRepaintManager can be found here<br>
 043  * <a href="http://www.clientjava.com/blog/2004/08/20/1093059428000.html">Easily Find Swing Threading Mistakes</a>
 044  * </p>
 045  * <p/>
 046  * <p>Links</ul>
 047  * <li>https://swinghelper.dev.java.net</li>
 048  * <li>http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html</li>
 049  * </ul></p>
 050  *
 051  * @author Scott Delap
 052  * @author Alexander Potochkin
 053  * @author Andres Almiray
 054  */
 055 public class CheckThreadViolationRepaintManager extends RepaintManager {
 056     private static final Logger LOG = LoggerFactory.getLogger(CheckThreadViolationRepaintManager.class);
 057     // it is recommended to pass the complete check
 058     private boolean completeCheck = true;
 059     private WeakReference<JComponent> lastComponent;
 060     private final RepaintManager delegate;
 061
 062     public CheckThreadViolationRepaintManager() {
 063         this(new RepaintManager());
 064     }
 065
 066     public CheckThreadViolationRepaintManager(RepaintManager delegate) {
 067         if (delegate == null || delegate instanceof CheckThreadViolationRepaintManager) {
 068             throw new IllegalArgumentException();
 069         }
 070         this.delegate = delegate;
 071     }
 072
 073     public boolean isCompleteCheck() {
 074         return completeCheck;
 075     }
 076
 077     public void setCompleteCheck(boolean completeCheck) {
 078         this.completeCheck = completeCheck;
 079     }
 080
 081     public synchronized void addInvalidComponent(JComponent component) {
 082         checkThreadViolations(component);
 083         delegate.addInvalidComponent(component);
 084     }
 085
 086     public void addDirtyRegion(JComponent component, int x, int y, int w, int h) {
 087         checkThreadViolations(component);
 088         delegate.addDirtyRegion(component, x, y, w, h);
 089     }
 090
 091     private void checkThreadViolations(JComponent c) {
 092         if (!SwingUtilities.isEventDispatchThread() && (completeCheck || c.isShowing())) {
 093             boolean repaint = false;
 094             boolean fromSwing = false;
 095             boolean imageUpdate = false;
 096             StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
 097             for (StackTraceElement st : stackTrace) {
 098                 if (repaint && st.getClassName().startsWith("javax.swing.") &&
 099                     // for details see
 100                     // https://swinghelper.dev.java.net/issues/show_bug.cgi?id=1
 101                     !st.getClassName().startsWith("javax.swing.SwingWorker")) {
 102                     fromSwing = true;
 103                 }
 104                 if (repaint && "imageUpdate".equals(st.getMethodName())) {
 105                     imageUpdate = true;
 106                 }
 107                 if ("repaint".equals(st.getMethodName())) {
 108                     repaint = true;
 109                     fromSwing = false;
 110                 }
 111             }
 112             if (imageUpdate) {
 113                 //assuming it is java.awt.image.ImageObserver.imageUpdate(...)
 114                 //image was asynchronously updated, that's ok
 115                 return;
 116             }
 117             if (repaint && !fromSwing) {
 118                 //no problems here, since repaint() is thread safe
 119                 return;
 120             }
 121             //ignore the last processed component
 122             if (lastComponent != null && c == lastComponent.get()) {
 123                 return;
 124             }
 125             lastComponent = new WeakReference<>(c);
 126             violationFound(c, stackTrace);
 127         }
 128     }
 129
 130     protected void violationFound(JComponent c, StackTraceElement[] stackTrace) {
 131         stackTrace = sanitize(stackTrace);
 132         StringBuilder sb = new StringBuilder("EDT violation detected").append('\n');
 133         sb.append(c).append('\n');
 134         for (StackTraceElement st : stackTrace) {
 135             sb.append("\tat ").append(st).append('\n');
 136         }
 137         if (LOG.isWarnEnabled()) {
 138             LOG.warn(sb.toString());
 139         }
 140     }
 141
 142
 143     // -- delegate methods
 144
 145     public static RepaintManager currentManager(Component component) {
 146         return RepaintManager.currentManager(component);
 147     }
 148
 149     public static RepaintManager currentManager(JComponent jComponent) {
 150         return RepaintManager.currentManager(jComponent);
 151     }
 152
 153     @Override
 154     public Rectangle getDirtyRegion(JComponent jComponent) {
 155         return delegate.getDirtyRegion(jComponent);
 156     }
 157
 158     @Override
 159     public Dimension getDoubleBufferMaximumSize() {
 160         return delegate.getDoubleBufferMaximumSize();
 161     }
 162
 163     @Override
 164     public Image getOffscreenBuffer(Component component, int i, int i1) {
 165         return delegate.getOffscreenBuffer(component, i, i1);
 166     }
 167
 168     @Override
 169     public Image getVolatileOffscreenBuffer(Component component, int i, int i1) {
 170         return delegate.getVolatileOffscreenBuffer(component, i, i1);
 171     }
 172
 173     @Override
 174     public boolean isCompletelyDirty(JComponent jComponent) {
 175         return delegate.isCompletelyDirty(jComponent);
 176     }
 177
 178     @Override
 179     public boolean isDoubleBufferingEnabled() {
 180         return delegate.isDoubleBufferingEnabled();
 181     }
 182
 183     @Override
 184     public void markCompletelyClean(JComponent jComponent) {
 185         delegate.markCompletelyClean(jComponent);
 186     }
 187
 188     @Override
 189     public void markCompletelyDirty(JComponent jComponent) {
 190         delegate.markCompletelyDirty(jComponent);
 191     }
 192
 193     @Override
 194     public void paintDirtyRegions() {
 195         delegate.paintDirtyRegions();
 196     }
 197
 198     @Override
 199     public void removeInvalidComponent(JComponent jComponent) {
 200         delegate.removeInvalidComponent(jComponent);
 201     }
 202
 203     public static void setCurrentManager(RepaintManager repaintManager) {
 204         RepaintManager.setCurrentManager(repaintManager);
 205     }
 206
 207     @Override
 208     public void setDoubleBufferingEnabled(boolean b) {
 209         delegate.setDoubleBufferingEnabled(b);
 210     }
 211
 212     @Override
 213     public void setDoubleBufferMaximumSize(Dimension dimension) {
 214         delegate.setDoubleBufferMaximumSize(dimension);
 215     }
 216
 217     @Override
 218     public String toString() {
 219         return delegate.toString();
 220     }
 221
 222     @Override
 223     public void validateInvalidComponents() {
 224         delegate.validateInvalidComponents();
 225     }
 226
 227     @Override
 228     public void addDirtyRegion(Window window, int i, int i1, int i2, int i3) {
 229         delegate.addDirtyRegion(window, i, i1, i2, i3);
 230     }
 231
 232     @Override
 233     public void addDirtyRegion(Applet applet, int i, int i1, int i2, int i3) {
 234         delegate.addDirtyRegion(applet, i, i1, i2, i3);
 235     }
 236 }
 |