progress indicator window with Stop button

C

Composer

My main class extends JFrame.
It includes some user input fields and a Go button:

JButton goButton = new JButton("Go");
// This Runnable allows the actionListener to give work to the
awt thread.
final Runnable doIt = new Runnable()
{ public void run()
{ createResults();
}
};
goButton.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent ae)
{ SwingUtilities.invokeLater(doIt);
}
});

So far, so good.
The user enters input, presses "Go", and createResults() gets
executed.
Within createResults() a lot of work gets done, in loops.
Each loop iteration tests the "stopping" variable.
I display a window (a JFrame, but maybe that's a bad choice?)
which includes a JProgressBar and also a "Stop" button:

progressStopButton = new JButton("Stop");
// This Runnable allows the actionListener to give work to the
awt thread.
final Runnable stopIt = new Runnable()
{ public void run()
{ stopping = true;
JOptionPane.showMessageDialog(null, "You pressed the
button.", "Ya",
JOptionPane.INFORMATION_MESSAGE);
}
};
progressStopButton.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent ae)
{ SwingUtilities.invokeLater(stopIt);
}
});

This does NOT work.

For one thing, the Stop button itself doesn't even appear unless
I repaint it after showing the JFrame:

progressStopButton.update(progressStopButton.getGraphics());

I can live with that. But the more serious problem is that
pressing the Stop button does nothing. It acts as though it's
disabled,
although it is enabled.

Is the problem that I am invoking a Runnable within a Runnable?
Do I need to use sleep()?
Is the problem that I create the progress bar and button in a new
JFrame?
Should I really have to repaint the Stop button after creating it?
Is it necessary to rewrite my code to have 2 or 3 threads?

Sorry for being so inexpert in threads and interrupts.
I had thought that the logic for the Stop button could use the same
approach as the logic for the Go button.

Thanks for any help.
 
N

Nigel Wade

Composer wrote:

This does NOT work.

For one thing, the Stop button itself doesn't even appear unless
I repaint it after showing the JFrame:

progressStopButton.update(progressStopButton.getGraphics());

I can live with that.

Peter has already provided the explanation for why this doesn't work.
But the more serious problem is that
pressing the Stop button does nothing. It acts as though it's
disabled,
although it is enabled.

I suggest you read the Fine Tutorial over at:
http://java.sun.com/docs/books/tutorial/uiswing/TOC.html.
Look at the Concurrency in Swing section:
http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html
and also the section Canceling [sic] Background Tasks:
http://java.sun.com/docs/books/tutorial/uiswing/concurrency/cancel.html

You might also find the section on How to Use Progress Bars useful:
http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html
 
C

Composer

Oh dear. Looking into SwingWorker, it appears to be
something introduced in SE 6. I have been basing my
code for years on SE 1.4.2 documentation, and making
the leap to SE 6 appears daunting.

For one thing, upgrading my JDK might cause something
else to break. Will NetBeans 3.5.1 still work? And
if not, can I live with a later version? (I tried
NetBeans IDE 5.0 and hated it.)

For another thing, I share my applications with other
composers on Windows and Mac platforms. Will they all
be able to run apps I develop with SE 6?

The other scary thing is that I don't even understand
the SE 6 documentation. The headline is, for example,
SwingWorker<T,V> which I think refers to "type parameters"
which were introduced in some release later than 1.4.2.

I could, of course, invest several days in retraining
myself, downloading new JDK and other stuff, ensuring
that all my old stuff still works... but my question is:
will life be that much better, for a guy who writes
one or two Java utility programs per year?

In any case, thanks to Peter and Nigel for getting me
to learn about the need to keep long-running tasks
off the event-dispatching thread. One way or another,
I will do that.
 
L

Lew

Composerwrote:
Why are you basing your code on a dead version of Java that is seven
years old and two years obsolete?

Java 5 is already in End-of-Life.

Besides, SwingWorker is much older than Java 6.
<http://en.wikipedia.org/wiki/SwingWorker#Usage_before_Java_6.0>

You are making a huge mistake if you don't get current. The threading
memory model for Java changed significantly with Java 5. Generics and
other enhancements came then also.

Generics are well worth learning, even mastering. Start with the Sun
tutorial, then use Joshua Bloch's free chapter from /Effective Java/
<http://java.sun.com/docs/books/effective/>
<http://java.sun.com/docs/books/effective/generics.pdf>
 
J

John B. Matthews

Arne Vajhøj said:
It is not new as a concept.

But a relevant fragment from that article is:

"Unfortunately, the JDK 6.0 SwingWorker and the Version 3 SwingWorker
use different method names and are not compatible."

I haven't tried it on 1.4.2 (the OP's current version), but the version
below implements the newer API and runs well on 1.5:

<https://swingworker.dev.java.net/>
 
C

Composer

Thank you all. It took only an hour or two to
(a) learn what I had to do, and (b) restructure
my code to work correctly, using 1.4.2.

I'm glad I didn't invest several days getting SE 6
installed and trying to make it work, because I'm
coding on OS X 10.4 which apparently doesn't support
SE 6.

(And I'm not on OS X 10.5 because the most important
apps I run cannot work with that.)

For me, a "huge mistake" would be to risk my truly
important apps for the sake of keeping up with the
expert Java programmers. I've read Joshua Bloch's
chapter on generics and find that it has made the
Java language significantly more confusing and
forbidding. For example, who would enjoy reading
the following:

"Because generic type information is erased at runtime,
it is illegal to use the instanceof operator on
parameterized types other than unbounded wildcard types."

Give me 1970s mainframe assembler language, in
preference to that!
 
L

Lew

For me, a "huge mistake" would be to risk my truly
important apps for the sake of keeping up with the
expert Java programmers.  I've read Joshua Bloch's

That would be the wrong reason. The right reason to use generics is
to make your code more reliable and risk free.
chapter on generics and find that it has made the
Java language significantly more confusing and
forbidding.

Things do look more confusing until you've learned them. Just think
how Java itself looked before you knew it at all.

It's false logic to think that generics are "confusing and forbidding"
just because you haven't learned them yet.

They provide significant benefit.
For example, who would enjoy reading the following:

"Because generic type information is erased at runtime,
it is illegal to use the instanceof operator on
parameterized types other than unbounded wildcard types."

What does enjoying reading it have to do with anything?

The important thing is that it tells you something useful.

FWIW, I enjoyed reading that, in particular because it helped me
understand the topic. Understanding how to write software makes me
happy.
Give me 1970s mainframe assembler language, in
preference to that!

Nonsense.
 
J

John B. Matthews

Composer said:
Thank you all. It took only an hour or two to
(a) learn what I had to do, and (b) restructure
my code to work correctly, using 1.4.2.

So, does SwingWorker run under 1.4.2 or did you find another way?
I'm glad I didn't invest several days getting SE 6
installed and trying to make it work, because I'm
coding on OS X 10.4 which apparently doesn't support
SE 6.

But it does support Java 1.5. The two versions can coexist side by side,
and the Java Preferences application lets you easily choose which one.
That's why I proposed building SwingWorker from source above.
(And I'm not on OS X 10.5 because the most important
apps I run cannot work with that.)

For me, a "huge mistake" would be to risk my truly
important apps for the sake of keeping up with the
expert Java programmers. I've read Joshua Bloch's
chapter on generics and find that it has made the
Java language significantly more confusing and
forbidding. For example, who would enjoy reading
the following:

"Because generic type information is erased at runtime,
it is illegal to use the instanceof operator on
parameterized types other than unbounded wildcard types."

In my experience, using generics increases compile-time type safety and
reduces the need for the instanceof operator.
Give me 1970s mainframe assembler language, in
preference to that!

I think assembler is a good foundation for learning any programming
language.
 
C

Composer

I'm not here to argue with anyone. I really appreciate the time
and effort you guys spend helping me, and it doesn't hurt me to
hear criticism, which is sometimes useful.

I don't use generics because there is no one supporting my code
but me, therefore I don't worry about someone putting the wrong
kind of object into my collection, and I've been just fine
managing without generics, and maybe some day I'll learn about
them when upgrading my Java skills becomes a priority --
although there are many other things in life that are higher
priorities for me.

I didn't try SwingWorker because I learned that it really wasn't
hard to put my CPU-intensive code into its own thread, once I
realized that inner classes have access to the variables of
outer classes. So I simply put my CPU-intensive methods
into inner classes/threads, which can interrogate the
"stopping" flag regularly.

I'm sure that I would be a better programmer if I learned
generics and SwingWorker, but that's not my goal in life.
I want to write this little utility program, give it to my
colleagues, and move on to things that are important to me.

I think the Java language itself is marvelous -- no other
language I know has such good exception-handling, for example
-- but I've had to spend vast amounts of time dealing with
administrative obstacles; I am currently scratching my head
about the jar file I created via NetBeans which works fine
on a Mac but will not run on Windows XP due to
"Could not find the main class" despite the fact that the
jar file conforms to the standard in every way that I can see.

I am trying...
 
L

Lew

Composer said:
-- but I've had to spend vast amounts of time dealing with
administrative obstacles; I am currently scratching my head
about the jar file I created via NetBeans which works fine
on a Mac but will not run on Windows XP due to
"Could not find the main class" despite the fact that the
jar file conforms to the standard in every way that I can see.

Have you read the tutorial?
<http://java.sun.com/docs/books/tutorial/deployment/jar/appman.html>

What is the "Main-Class:" attribute value in your manifest, as
actually entered inside the JAR?

What is the command that you use to attempt to run the JAR?
 
J

John B. Matthews

Composer said:
I'm not here to argue with anyone. [...]

Sorry if my question seemed confrontational; I was just curious.

[...]
I didn't try SwingWorker because I learned that it really wasn't
hard to put my CPU-intensive code into its own thread, once I
realized that inner classes have access to the variables of outer
classes. So I simply put my CPU-intensive methods into inner
classes/threads, which can interrogate the "stopping" flag
regularly.

That's what I wondered.

[...]
I am currently scratching my head about the jar file I created
via NetBeans which works fine on a Mac but will not run on
Windows XP due to "Could not find the main class" despite the
fact that the jar file conforms to the standard in every way that
I can see.

Here's a simple tool I use to examine the manifest _in_situ_:

<http://sites.google.com/site/drjohnbmatthews/manifesto>

I found it helpful to how NetBeans puts things together

$ java -jar ~/bin/Manifesto.jar dist/scratch.jar
Manifest: dist/scratch.jar
X-COMMENT: Main-Class will be added automatically by build
Class-Path: lib/jscience.jar lib/h2.jar
Created-By: 1.5.0_16-133 (Apple Inc.)
Ant-Version: Apache Ant 1.7.0
Main-Class: cli.RationalTest
Manifest-Version: 1.0

"scratch.jar" lives in the project's "dist" directory, where you'll
also find copies (in "lib") of required jars.
 
C

Composer

Eric, sorry, I don't know the C language, so I've
probably missed a good in-joke.

John, thanks for Manifesto which I've run, with these results:
Manifest: Java projects/raftery/composer/Chainer.jar
Created-By: NetBeans IDE
Main-Class: raftery.composer.Chainer
Manifest-Version: 1.0

(That's on the Mac. When I copy Manifesto.jar to Windows,
it too fails to run.)

Lew, I will indeed answer your three questions.
1. I have read the appman tutorial, thanks.
2. The line in my manifest is Main-Class: raftery.composer.Chainer
3. I was not using a command to run the jar. Instead I was
double-clicking the jar file. On the Mac, this works fine.
On Windows, it produced "Could not find the main class".
But your post spurred me to try running it from a cmd line.
On the Mac, again, this works, although the log shows a msg
that is reportedly chronic under OS X 10.4:

java[632] CFLog (0): CFMessagePort: bootstrap_register(): failed 1103
(0x44f), port = 0x10303, name = 'java.ServiceProvider'
See /usr/include/servers/bootstrap_defs.h for the error codes.
2009-02-26 21:25:47.544 java[632] CFLog (99): CFMessagePortCreateLocal
(): failed to name Mach port (java.ServiceProvider)

I think this is a red herring. This message has always come up
when running under NetBeans, so I'm not surprised to see it
when running from a cmd line. The app does run on the Mac.
But when I run it from a command line on Windows, something
different comes up:

Exception in thread "main" java.lang.UnsupportedClassVersionError:
raftery/composer/Chainer (Unsupported major.minor version 49.0)

The javadocs for this error say:
Thrown when the Java Virtual Machine attempts to read a class file
and determines that the major and minor version numbers in the file
are not supported.

Obviously there is some difference in the Java setup on the
two machines. On Windows, when I look in "Add or Remove Programs",
it says I have Java 2 Runtime Environment, SE v1.4.2_18.
On the Mac, looking under System Profiler at the Frameworks,
it says I have JavaVM version 11.7.0 modified January 2009
(when I coincidentally had some RAM added and upgraded to 10.4).

So have we come full circle, and my problem is the use of outdated
Java software? So... I downloaded JRE version 1.5.0.17 onto Windows,
restarted, double-clicked the jar file, and... it works now.

This is not good news. It means that many of the people I distribute
my utility program to will have to download and install JRE 1.5,
and that is a hassle I don't like to put them through.
I'd rather produce down-level jar files!

Oh well. At least I'm more knowledgeable than I was before,
and that's mostly thanks to you guys. Many thanks.
 
J

John B. Matthews

[...]
John, thanks for Manifesto which I've run, with these results:
Manifest: Java projects/raftery/composer/Chainer.jar
Created-By: NetBeans IDE
Main-Class: raftery.composer.Chainer
Manifest-Version: 1.0

Excellent. That reminded me to attach a source jar, including an ant
script that creates a manifest for Manifesto itself:

<http://sites.google.com/site/drjohnbmatthews/manifesto>

[...]
But your post spurred me to try running it from a cmd line.
On the Mac, again, this works, although the log shows a msg
that is reportedly chronic under OS X 10.4:

java[632] CFLog (0): CFMessagePort: bootstrap_register(): failed 1103
(0x44f), port = 0x10303, name = 'java.ServiceProvider'
See /usr/include/servers/bootstrap_defs.h for the error codes.
2009-02-26 21:25:47.544 java[632] CFLog (99): CFMessagePortCreateLocal
(): failed to name Mach port (java.ServiceProvider)

I think this is a red herring.

Yep. It's Mac OS announcing that there's another JVM running, probably
NetBeans itself. Loquacious but harmless.

[...]
 
L

Lew

Peter said:
[...]
This is not good news. It means that many of the people I distribute
my utility program to will have to download and install JRE 1.5,
and that is a hassle I don't like to put them through.
I'd rather produce down-level jar files!

In Eclipse, you can specify your target JVM version. I would guess that
NetBeans has something similar, probably in some "project settings" UI.

The default is likely whatever the OS-default JVM version you have is,
but it should be able to target earlier JVM versions that are also
installed.

You can also target earlier Java versions with command-line 'javac'.
 
O

Owen Jacobson

Eric, sorry, I don't know the C language, so I've
probably missed a good in-joke.

The C "gets" function is so poorly-specified that it's actually
impossible to use it safely. The calling code has to pass gets a buffer
to write to, but gets will continue writing to that buffer until it
reaches a newline. There's no provision for passing the *size* of the
buffer, or for gets to manage the buffer itself, because the original
programmer didn't think he needed it.
John, thanks for Manifesto which I've run, with these results:
Manifest: Java projects/raftery/composer/Chainer.jar
Created-By: NetBeans IDE
Main-Class: raftery.composer.Chainer
Manifest-Version: 1.0

(That's on the Mac. When I copy Manifesto.jar to Windows,
it too fails to run.)

Lew, I will indeed answer your three questions.
1. I have read the appman tutorial, thanks.
2. The line in my manifest is Main-Class: raftery.composer.Chainer
3. I was not using a command to run the jar. Instead I was
double-clicking the jar file. On the Mac, this works fine.
On Windows, it produced "Could not find the main class".
But your post spurred me to try running it from a cmd line.
On the Mac, again, this works, although the log shows a msg
that is reportedly chronic under OS X 10.4:

java[632] CFLog (0): CFMessagePort: bootstrap_register(): failed 1103
(0x44f), port = 0x10303, name = 'java.ServiceProvider'
See /usr/include/servers/bootstrap_defs.h for the error codes.
2009-02-26 21:25:47.544 java[632] CFLog (99): CFMessagePortCreateLocal
(): failed to name Mach port (java.ServiceProvider)

This message is indeed harmless.
The app does run on the Mac.But when I run it from a command line on
Windows, something different comes up:

Exception in thread "main" java.lang.UnsupportedClassVersionError:
raftery/composer/Chainer (Unsupported major.minor version 49.0)

The javadocs for this error say:
Thrown when the Java Virtual Machine attempts to read a class file
and determines that the major and minor version numbers in the file
are not supported.

Java 1.4's major classfile version is 48.
Java 1.5's major classfile version is 49.
Java 1.6's major classfile version is 50.

(Just for reference.)
Obviously there is some difference in the Java setup on the
two machines. On Windows, when I look in "Add or Remove Programs",
it says I have Java 2 Runtime Environment, SE v1.4.2_18.
On the Mac, looking under System Profiler at the Frameworks,
it says I have JavaVM version 11.7.0 modified January 2009
(when I coincidentally had some RAM added and upgraded to 10.4).

Java 1.4 is no longer supported by Sun. People who are still using that
version should upgrade; there are very few known application
compatability issues, so it should be a drop-in upgrade.

Apple maintains a branch of the Sun VM for OS X. The last JVM revision
available for 10.4 is Java 1.5, which is now in EOL from Sun but still
supported by Apple. For OS X 10.5, Java 1.4, Java 1.5 and Java 1.6 are
available (1.6 is 64-bit only on OS X, though, so browser plugins for
JWS and applets are limited to Java 5 on most Macs). No word yet on
what the situation is with Snow Leopard (OS X 10.6), but given that
there's supposed to be much more pervasive 64-bit-ness, and give that
Sun is starting to EOL 1.5 support, I wouldn't be terribly surprised if
OS X only supported 1.6 in the next release.
So have we come full circle, and my problem is the use of outdated
Java software? So... I downloaded JRE version 1.5.0.17 onto Windows,
restarted, double-clicked the jar file, and... it works now.

This is not good news. It means that many of the people I distribute
my utility program to will have to download and install JRE 1.5,
and that is a hassle I don't like to put them through.
I'd rather produce down-level jar files!

To produce 1.4-compatible JARs, you ideally need a 1.4 SDK. You can use
the 1.5 or 1.6 compiler to produce class files in the 1.4 classfile
format, but that doesn't guarantee that the compiled code doesn't call
methods that didn't exist - for that, you need to compile against a 1.4
runtime library, which the compiler -source and -target arguments don't
do.

Compile your code using the compiler from
/System/Library/Frameworks/JavaVM.framework/Versions/1.4 - your IDE
should be able to pick that up as a valid Java installation and use it,
or you can use it yourself from the terminal. Alternately, install a
1.4 JDK and VM on a secondary machine or virtual machine and build your
app there. *Ideally*, move to 1.5.

Cheers,
-o
 
L

Lew

Owen said:
Java 1.4 is no longer supported by Sun. People who are still using that
version should upgrade; there are very few known application
compatability issues, so it should be a drop-in upgrade.

There are several compatibility issues. The keyword 'enum' is new with Java
5, so code that uses it must change. This applies to some Apache Commons
packages, for example. There is a flaw with some pre-Java-5 code, as in
Apache commons-lang enumerations, where they count on references to the
'class' literal to initialize a class, a bug that Sun fixed with Java 5.
Consequently such code breaks with modern Java; fortunately this only happens
under rather rare scenarios where the 'class' literal is referenced before the
class is initialized. I don't think any pre-Java-5 code relies on the old
somewhat insufficient memory model for multi-threaded programs, so that
shouldn't cause trouble.
 
O

Owen Jacobson

There are several compatibility issues. The keyword 'enum' is new with
Java 5, so code that uses it must change. This applies to some Apache
Commons packages, for example. There is a flaw with some pre-Java-5
code, as in Apache commons-lang enumerations, where they count on
references to the 'class' literal to initialize a class, a bug that Sun
fixed with Java 5. Consequently such code breaks with modern Java;
fortunately this only happens under rather rare scenarios where the
'class' literal is referenced before the class is initialized. I don't
think any pre-Java-5 code relies on the old somewhat insufficient
memory model for multi-threaded programs, so that shouldn't cause
trouble.

To clarify my take on it:

Existing classes compiled against the 1.4 JDK should continue to run
under 1.5 and 1.6 without change in the vast majority of cases. In
particular, 'enum' is still a valid identifier at the JVM level, even
though it's no longer a valid identifier at the Java language level.

Code that compiled against 1.4 will probably compile against 1.5 or
1.6, but there are some easy-to-hit cases where it won't. Code which
uses the word 'enum' under 1.4 will not compile under 1.5 or 1.6.
Implementations of certain SPIs will need modification to implement
methods that were added to the interfaces in 1.5 and 1.6 - JDBC 4, for
example, requires several new methods in driver implementations that
JDBC 3 did not have.

-o
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top