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 }
|