javax.swing.TransferHandler.HasGetTransferHandler
.
*
* @see JDKProxyFactory#createProxy(Object, Evaluator)
*/
public void testNonAccessibleInterface() {
getFactory().createProxy(new JFrame() {
}, createEvaluator());
}
} libspin-java-1.5/src/test/java/spin/AbstractProxyFactoryTest.java 0000644 0001750 0001750 00000007007 10602561074 024535 0 ustar gregoa gregoa /**
* Spin - transparent threading solution for non-freezing Swing applications.
* Copyright (C) 2002 Sven Meier
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package spin;
import junit.framework.TestCase;
import spin.Evaluator;
import spin.Invocation;
import spin.ProxyFactory;
/**
* Abstract base class for tests of proxy factories.
*/
public abstract class AbstractProxyFactoryTest extends TestCase {
protected abstract ProxyFactory getFactory();
/**
* Test running through a proxy.
*/
public void testRun() {
final RunnableBean runnable = new RunnableBean();
Evaluator evaluator = new Evaluator() {
public void evaluate(Invocation invocation) throws Throwable {
assertEquals(false, runnable.evaluated);
assertEquals(false, runnable.run);
runnable.evaluated = true;
invocation.evaluate();
assertEquals(true, runnable.run);
}
};
Runnable proxy = (Runnable) getFactory().createProxy(runnable,
evaluator);
proxy.run();
}
/**
* Test reflexivity.
*
* @throws Exception
*/
public void testReflexive() throws Exception {
Runnable runnable = new RunnableBean();
Runnable proxy = (Runnable) getFactory().createProxy(runnable,
createEvaluator());
assertTrue("equals() is not reflexive", proxy.equals(proxy));
}
/**
* Test equality of two proxies of the same bean.
*
* @throws Exception
*/
public void testTwoProxiesOfSameBeanAreEqual() throws Exception {
Runnable runnable = new RunnableBean();
Runnable proxy1 = (Runnable) getFactory().createProxy(runnable,
createEvaluator());
Runnable proxy2 = (Runnable) getFactory().createProxy(runnable,
createEvaluator());
assertTrue("two proxies of same bean are not equal", proxy1
.equals(proxy2));
}
/**
* Test non-equality of two proxies of different beans.
*
* @throws Exception
*/
public void testTwoProxiesOfDifferentBeansAreNotEqual() throws Exception {
Runnable runnable1 = new RunnableBean();
Runnable runnable2 = new RunnableBean();
Runnable proxy1 = (Runnable) getFactory().createProxy(runnable1,
createEvaluator());
Runnable proxy2 = (Runnable) getFactory().createProxy(runnable2,
createEvaluator());
assertTrue("two proxies of different beans are equal", !proxy1
.equals(proxy2));
}
/**
* Runnable mock.
*/
public static class RunnableBean implements Runnable {
/**
* Was this runnable evaluated.
*/
public boolean evaluated = false;
/**
* Was this runnable run.
*/
public boolean run = false;
public void run() {
run = true;
}
}
protected Evaluator createEvaluator() {
return new Evaluator() {
public void evaluate(Invocation invocation) throws Throwable {
invocation.evaluate();
};
};
}
} libspin-java-1.5/src/test/java/spin/CGLibProxyFactoryTest.java 0000644 0001750 0001750 00000002137 10572560326 023716 0 ustar gregoa gregoa /**
* Spin - transparent threading solution for non-freezing Swing applications.
* Copyright (C) 2002 Sven Meier
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package spin;
import spin.ProxyFactory;
import spin.proxy.CGLibProxyFactory;
/**
* Test of CGLib proxies.
*/
public class CGLibProxyFactoryTest extends AbstractProxyFactoryTest {
protected ProxyFactory getFactory() {
return new CGLibProxyFactory();
}
} libspin-java-1.5/src/test/java/spin/off/ 0000755 0001750 0001750 00000000000 10602562664 017451 5 ustar gregoa gregoa libspin-java-1.5/src/test/java/spin/off/AWTReflectDispatcherTest.java 0000644 0001750 0001750 00000002617 10523447304 025124 0 ustar gregoa gregoa /**
* Spin - transparent threading solution for non-freezing Swing applications.
* Copyright (C) 2002 Sven Meier
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package spin.off;
import javax.swing.SwingUtilities;
import junit.framework.TestCase;
public class AWTReflectDispatcherTest extends TestCase {
public void test() throws Exception {
AWTReflectDispatcherFactory factory = new AWTReflectDispatcherFactory();
final Dispatcher dispatcher = factory.createDispatcher();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
dispatcher.start();
} catch (Throwable throwable) {
fail(throwable.getMessage());
}
}
});
Thread.sleep(2000);
dispatcher.stop();
}
}
libspin-java-1.5/src/test/java/spin/off/SpinOffTest.java 0000644 0001750 0001750 00000004635 10523447300 022517 0 ustar gregoa gregoa /**
* Spin - transparent threading solution for non-freezing Swing applications.
* Copyright (C) 2002 Sven Meier
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package spin.off;
import javax.swing.SwingUtilities;
import junit.framework.TestCase;
import spin.Spin;
public class SpinOffTest extends TestCase {
private static final int DELAY = 3000;
private class Timer {
private long start;
public Timer() {
start = System.currentTimeMillis();
}
public long elapsed() {
return System.currentTimeMillis() - start;
}
}
public static interface OneIntProperty {
int getInt();
}
public void testEDTNotBlockedDuringInvocation() throws Exception {
class Flag {
public boolean flag1, flag2;
Throwable exception;
}
final Flag flag = new Flag();
OneIntProperty target = new OneIntProperty() {
public int getInt() {
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
}
return 0;
}
};
final OneIntProperty proxy = (OneIntProperty) Spin.off(target);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
proxy.getInt();
} catch (Throwable t) {
flag.exception = t;
} finally {
flag.flag1 = true;
}
}
});
Timer timer = new Timer();
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
flag.flag2 = true;
}
});
assertTrue("Second runnable took too long: " + timer.elapsed(), timer
.elapsed() < DELAY);
assertTrue("Second runnable didn't run", flag.flag2);
while (!flag.flag1) {
if (timer.elapsed() > DELAY * 2) {
fail("Original invocation timed out");
}
Thread.sleep(10);
}
assertNull("Unexpected exception in original invocation "
+ flag.exception, flag.exception);
}
}
libspin-java-1.5/src/site/ 0000755 0001750 0001750 00000000000 10602562662 014770 5 ustar gregoa gregoa libspin-java-1.5/src/site/xdoc/ 0000755 0001750 0001750 00000000000 10603041062 015710 5 ustar gregoa gregoa libspin-java-1.5/src/site/xdoc/problem.xml 0000644 0001750 0001750 00000046631 10603457436 020124 0 ustar gregoa gregoa
In this section we take a look at a naive GUI implementation that shows how Swing freezes in case the application programmer doesn't take any special actions against it. We also describe the problem of calls into Swing components triggered by any other thread than the event dispatch thread (EDT).
Swing is not designed for multi threading, so let us first recall the single threading rule of every Swing GUI:
Access to a (visible) Swing component has to occur in the event dispatch thread.
The EDT is responsible to process all GUI related events, e.g. notifying listeners of user input, repainting dirty regions or updated areas. All these events are enqueued and treated sequentially - if one of them takes a long time to be processed, all further events will have to wait.
Throughout the next sections we'll put the code of a Swing GUI component and a non visual bean side by side. The bean encapsulates an extensive calculation that is used by multiple threads. Code run on the EDT is shown in green and code called by any other thread is shown in red.
As you can see in the following code, GUI.java
calls the method getValue()
on the bean when an action is performed. The EDT is routed from the GUI to the bean. While it is performing its calculations no further swing events can be processed - the GUI freezes.
One of these queued events is the repaint of the label triggered by label.setText("...")
. When getValue()
returns, the text of the label is changed again before the previous text was painted. So in fact "..."
is never seen:
public void actionPerformed(ActionEvent e)
{
label.setText("...");
label.setText(bean.getValue());
}
public void propertyChange(PropertyChangeEvent ev)
{
label.setText((String)ev.getNewValue());
}
public String getValue()
{
String value;
// extensive calculation
return value;
}
public void setValue(String value)
{
this.value = value;
firePropertyChange(value);
}
What happens if setValue()
is invoked on the bean on another thread. The listeners are notified (the class GUI
implements java.beans.PropertyChangeListener
and is registered as a listener for changes of the bean), triggering a call to propertyChange()
on the GUI. The text of the label is altered on the calling thread, violating the Swing threading rule.
The color distribution gives a hint where to search for problems of this implementation:
Green lines of code in BeanImpl.java
result in a GUI freeze, red lines in GUI.java
show a violation to the Swing threading rule.
One obvious solution to the problems seen in the previous section is to shift the invocation of getValue()
from the EDT to a separate thread. When this method returns we must not propagate the result to a Swing component though. We have to return control to the EDT instead. This can be achieved via SwingUtilities.invokeLater()
which will use our Runnable to correctly change the label's text on the EDT:
public void actionPerformed(ActionEvent e)
{
label.setText("...");
new Thread(new Runnable()
{
public void run()
{
final String value = bean.getValue();
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText(value);
}
});
}
}}).start();
}
public void propertyChange(final PropertyChangeEvent ev)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText((String)ev.getNewValue());
}
});
}
public String getValue()
{
String value;
// extensive calculation
return value;
}
public void setValue(String value)
{
this.value = value;
firePropertyChange(value);
}
Now what happens if the bean informs the GUI about a value-change triggered by another thread? In propertyChange()
we pass a runnable to the EDT via SwingUtiltites.invokeLater()
that can safely alter the label.
Let's take a look at the colors once again: In BeanImpl.java
there are only red lines - so we achieved a non freezing GUI. GUI.java
has almost all lines in green. Since we restrict changes to Swing components to these green lines we are honouring the Swing threading rule too.
But the red lines in GUI.java
make things difficult: The programmer of this class always has to know which thread is stepping through what part of the code - without any visual help of thread-coloring. Any mistake reintroduces the problems mentioned above.
SwingWorker is a utility class that aims to ease the efforts to write a non-freezing GUI. Although not included in the standard Java distribution it is maintained by the Swing team and downloadable at The Swing Connection.
As you can see in the following example a SwingWorker removes some of the visual clutter seen in the previous section. To use it you have to subclass it, placing extensive calculations into method construct()
. In finished()
you can alter the label because this method is called on the EDT. This is similar to our previous solution but this time the threading is handled by the superclass:
public void actionPerformed(ActionEvent e)
{
label.setText("...");
new SwingWorker()
{
public Object construct()
{
return bean.getValue();
}
public void finished()
{
label.setText((String)getValue());
}
}).start();
}
public void propertyChange(final PropertyChangeEvent ev)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText((String)ev.getNewValue());
}
});
}
public String getValue()
{
String value;
// extensive calculation
return value;
}
public void setValue(String value)
{
this.value = value;
firePropertyChange(value);
}
The SwingWorker offers no support for our notification problem so we stick to our previous solution in propertyChange()
.
What about the colors?
The situation hasn't really improved. The indentation of code was minimized but we still have red lines in GUI.java
. So the problem above isn't resolved yet, we're still seeking a better solution.