Swing application architecture question.

D

Daniel Pitts

I'm starting a new project and was considering several different ways to
model and render my data.

1st way is it to use Swing models directly (TreeModel, TableModel,
etc...) in my domain. This seems like the worst way possible ;-)

The 2nd way is to implement a Swing model interface in a way that wraps
my domain model (eg, write a class MyTreeModel implements TreeModel that
is a bridge to MyTreeLikeStructure). This has the benefits of the data
always being "in sync" because the backing model is my domain model.
The down side is that the threading model for my domain must support the
threading model of Swing. This seems unsatisfactory to me.

A 3rd way would be to have a bridge between my Domain model, and a
default Swing model (eg, has a MyTreeLikeStructureListener which will
update the DefaultTreeModel instance as things change). This seems
like the winner, but I'm not sure about the development overhead in
this. Does anyone have experience with any of the three approaches?

Thanks,
Daniel.

x-posted to cljg and cljp
 
M

Mark Space

Daniel said:
A 3rd way would be to have a bridge between my Domain model, and a
default Swing model (eg, has a MyTreeLikeStructureListener which will
update the DefaultTreeModel instance as things change). This seems
like the winner, but I'm not sure about the development overhead in
this. Does anyone have experience with any of the three approaches?


Interestingly, this C# pattern uses a Bridge for GUI work that seems
similar to yours:

<http://www.informit.com/articles/article.aspx?p=30297>
 
T

Tom Anderson

I'm starting a new project and was considering several different ways to
model and render my data.

1st way is it to use Swing models directly (TreeModel, TableModel, etc...) in
my domain. This seems like the worst way possible ;-)

The 2nd way is to implement a Swing model interface in a way that wraps my
domain model (eg, write a class MyTreeModel implements TreeModel that is a
bridge to MyTreeLikeStructure). This has the benefits of the data always
being "in sync" because the backing model is my domain model. The down side
is that the threading model for my domain must support the threading model of
Swing. This seems unsatisfactory to me.

A 3rd way would be to have a bridge between my Domain model, and a default
Swing model (eg, has a MyTreeLikeStructureListener which will update the
DefaultTreeModel instance as things change). This seems like the winner,
but I'm not sure about the development overhead in this. Does anyone have
experience with any of the three approaches?

None whatsoever, but here's my opinion:

Go the second way. Find a way to insulate the domain from Swing thread
weirdness.

As you say, the first way is not even worth thinking about. The third way
could be made to work, and would leave the domain clean, but maintaining
two copies of a single state is the kind of thing that always leads to
pain and bugs. Also, does you domain model have event listeners naturally?
If not, you'd have to add them to it to support the synchronising bridge,
and then you can say goodbye to purity.

Threads, on the other hand - famously easy!

So, what's the problem with the threading model? I basically know nothing
about Swing. I know that you have to do all mutation of Swing objects from
the EDT, and that you shouldn't use the EDT to do any long-running work. I
assume that the way a model works is that Swing calls in to get values,
and the model returns them, rather than that the model makes calls into
Swing - is that right? Is the problem then that your model does heavy
lifting under the hood, and you don't want to do that in the EDT? Hmm. If
that's the case, i think i might have to rescind my advice, since there's
no useful way to turn a get-a-value call into something suitably
asynchronous.

tom
 
K

Karsten Lentzsch

Daniel said:
I'm starting a new project and was considering several different ways to
model and render my data. [...]

Google "Organizing Presentation Logic" and then study
MVP vs. "Presentation Model". The latter is my favourite
pattern for desktop Java.

See also the slides "Desktop Patterns & Data Binding"
at www.jgoodies.com/articles/ once you've read Fowler's
article mentioned above.

-Karsten
 
L

Lew

Tom said:
None whatsoever, but here's my opinion:

Go the second way. Find a way to insulate the domain from Swing thread
weirdness.

As you say, the first way is not even worth thinking about. The third
way could be made to work, and would leave the domain clean, but
maintaining two copies of a single state is the kind of thing that
always leads to pain and bugs. Also, does you domain model have event
listeners naturally? If not, you'd have to add them to it to support the
synchronising bridge, and then you can say goodbye to purity.

Threads, on the other hand - famously easy!

So, what's the problem with the threading model? I basically know
nothing about Swing. I know that you have to do all mutation of Swing
objects from the EDT, and that you shouldn't use the EDT to do any
long-running work. I assume that the way a model works is that Swing
calls in to get values, and the model returns them, rather than that the
model makes calls into Swing - is that right? Is the problem then that

Actually, it's more like Swing widgets generate events to indicate desired
model actions and changes, a listener catches the events and calls into the
model via SwingWorker, on a separate thread (via SwingWorker) the model does
its thing and either back through SwingWorker or via InvokeLater changes the
GUI state.
your model does heavy lifting under the hood, and you don't want to do
that in the EDT? Hmm. If that's the case, i think i might have to
rescind my advice, since there's no useful way to turn a get-a-value
call into something suitably asynchronous.

The GUI asynchronously triggers model actions. The model asynchronously sets
GUI state.
 
T

Tom Anderson

Actually, it's more like Swing widgets generate events to indicate desired
model actions and changes, a listener catches the events and calls into the
model via SwingWorker, on a separate thread (via SwingWorker) the model does
its thing and either back through SwingWorker or via InvokeLater changes the
GUI state.


The GUI asynchronously triggers model actions. The model asynchronously
sets GUI state.

Okay. That sounds like it should be fairly easy to do with a stateless
bridge of the kind i advocated - exactly as you say, either entirely via
SwingWorkers, or by using SwingWorker or ExecutorService on the way in,
and invokeLater on the way out.

You have to write a separate class for every call in or out (although they
can be anonymous classes), but i don't think that can be any more work
than creating a full state-replicating bridge.

tom
 
S

Seamus MacRae

Tom said:
So, what's the problem with the threading model? I basically know
nothing about Swing. I know that you have to do all mutation of Swing
objects from the EDT, and that you shouldn't use the EDT to do any
long-running work. I assume that the way a model works is that Swing
calls in to get values, and the model returns them, rather than that the
model makes calls into Swing - is that right? Is the problem then that
your model does heavy lifting under the hood, and you don't want to do
that in the EDT? Hmm. If that's the case, i think i might have to
rescind my advice, since there's no useful way to turn a get-a-value
call into something suitably asynchronous.

Perhaps it would help to start by tackling a relatively simple case:
say, "if the user clicks a button, a panel should display some
information about a domain object, and that information takes some time
to obtain or compute".

My approach in this instance would be: button displays panel; panel when
first displayed shows some sort of "working" animation or whatever and
launches a SwingWorker; the SwingWorker's doInBackground() method
performs, or triggers and waits for, the needed computation, eventually
returning the result; and the SwingWorker's done() method uses get() to
get the result, then alters the panel to display the result.

The doInBackground() method can invoke the domain objects without those
objects needing to themselves be Swing-aware.

It gets more complicated if the user should be able to close the panel
again before it's displayed anything and, in that case, the calculation
should be aborted and without having had any side effects.

It gets even more complicated when the user also may modify the domain
model state.

However, it is a starting point.

(Another complication is exception handling, particularly of exceptions
caused in doInBackground(). Returning an out of band value, such as
null, after catching an exception, and storing the exception in an
instance variable of the SwingWorker subclass, one can communicate it to
the done() method, which can then update the panel with an error
message, pop up an alert, write a log entry, or take some other
appropriate action or a combination of actions. The get() method, called
from in done(), should establish a happens-before relationship with the
return from doInBackground() and therefore ensure that the exception is
in the instance variable if this variable is read in done() after get()
returns whichever out-of-band value signals an error.)
 
D

Daniel Pitts

jebblue said:
I get a form editor, place the controls on the form and use member
variables in the form class to retain in memory the data until an
action is taken to save the form to disk as a custom file or in
a database. This approach works across platforms, languages, teams, etc.
Perhaps for simple form applications. What I'm working on is more like
a mail program or newsreader. There is plenty to worry about beyond CRUD
operations.
 
D

Daniel Pitts

Karsten said:
Daniel said:
I'm starting a new project and was considering several different ways
to model and render my data. [...]

Google "Organizing Presentation Logic" and then study
MVP vs. "Presentation Model". The latter is my favourite
pattern for desktop Java.

See also the slides "Desktop Patterns & Data Binding"
at www.jgoodies.com/articles/ once you've read Fowler's
article mentioned above.

-Karsten
I'll look into it, thanks.
 
M

Mark Space

Daniel said:
A 3rd way would be to have a bridge between my Domain model, and a
default Swing model (eg, has a MyTreeLikeStructureListener which will
update the DefaultTreeModel instance as things change). This seems
like the winner, but I'm not sure about the development overhead in
this. Does anyone have experience with any of the three approaches?

One thing I've seen other folks mention is to queue up events like this,
and process them in a batch on the EDT. You don't want to call the EDT
for every little thing. If you call it within about 250 milliseconds,
users won't notice the delay, and if you queue up many events in that
time, then you can update a bunch of objects in one go. Supposedly much
faster this way.

I'd consider some queue of Runnables, that every 250 milliseconds you
wake up and drain the queue on the EDT. Bonus points if the EDT task
isn't called unless there's at least one Runnable in the queue.
 
L

Lew

Mark said:
One thing I've seen other folks mention is to queue up events like this,
and process them in a batch on the EDT. You don't want to call the EDT
for every little thing. If you call it within about 250 milliseconds,
users won't notice the delay, and if you queue up many events in that
time, then you can update a bunch of objects in one go. Supposedly much
faster this way.

I'd consider some queue of Runnables, that every 250 milliseconds you
wake up and drain the queue on the EDT. Bonus points if the EDT task
isn't called unless there's at least one Runnable in the queue.

Doesn't the AWT infrastructure do that on its own if you use invokeLater()?
 
M

Mark Space

Lew said:
Doesn't the AWT infrastructure do that on its own if you use invokeLater()?


My point was to provide an explicit delay. I don't think invokeLater
does that.
 
L

Lew

(f/u set to clj.gui)


Mark said:
My point was to provide an explicit delay. I don't think invokeLater
does that.

Side note: While researching this I encountered this cautionary article:
Here are three Swing urban legends:
* Create threads for long tasks from the event dispatch thread.
* Use SwingUtilities for running tasks on the event dispatch thread.
* Synchronize methods for synchronization.

The first "legend" section discusses thread priority - it tells us that we
need to reduce priority on background threads spun off the EDT. I had never
heard that before.
 
L

Lew

Peter said:
Why? You might argue the topic belonged only in clj.gui in the first
place, but now that it's here, what's the point of moving it? This
message topic is way more on-topic here than plenty of other recent
discussions in cljp.

It's not about whether the message is on-topic; I agree that it's on topic for
both groups. It's to avoid having to read every message twice.

Whatever.
 
E

Eric Sosman

Lew said:
(f/u set to clj.gui)

(c.l.j.programmer restored)
Side note: While researching this I encountered this cautionary article:


The first "legend" section discusses thread priority - it tells us that
we need to reduce priority on background threads spun off the EDT. I
had never heard that before.

Having heard it, my suggestion would be to ignore it.
The underlying assumption that a thread's Java priority is
the only thing that determines which threads run and which
thread wait is flawed. It is quite likely that the O/S
scheduler that ultimately decides "Who runs?" is entirely
unaware of the thread's Java priority: The J.P. is probably
mapped to a platform priority in a platform-specific way.
Also, the scheduler probably uses more inputs than just a
thread's priority: Solaris, for example, also looks at the
recent execution history to dynamically lower the priorities
of CPU-bound threads and raise those of event-bound threads.
(That's in the most common case; Solaris supports multiple
scheduling disciplines with different characteristics.)

Now, it may well turn out that on some platforms it is
desirable to juggle thread priorities to get best behavior
(for suitable values of "best"). Fine, and if so there's
nothing wrong with doing so. But don't do it _a priori_ and
without evidence that it's needed, and even when it's needed
I suggest doing it in platform-specific code.

For general issues (not necessarily Java issues) about
threading, thread priorities, and thread scheduling, visit
comp.programming.threads. There you will find the usual gang
of Usenet idiots, but also a few people who wrote the book on
threading, where "wrote the book" is meant literally.
 
L

Lew

Peter said:
You need a better news reader. I'm surprised Thunderbird doesn't
support even a basic feature like that, but given that it doesn't I'll
suggest it's time for you to get rid of the training wheels. :)

I don't really need a better news reader. Thanks for the suggestion, though.
 
T

Tom Anderson

My point was to provide an explicit delay. I don't think invokeLater
does that.

Point. Although i would have thought that id the EDT was busy, the
invokeLater tasts will pile up at the back of the queue, and the effect
will be much the same. If it's not busy, then speed is not an issue.

tom
 
L

Lew

Peter said:
I'd say that when you are modifying an article's follow-up field for the
sole purpose of making YOUR reading experience better or more
convenient, you really do need a better news reader.

The message fields are there for the benefit of the community at large,
not your personal convenience.

Are you suggesting that narrowing the scope of these comments to one or the
other of the newsgroups would have caused someone inconvenience?
 
D

Daniel Pitts

Peter said:
Really? Thunderbird doesn't handle cross-posted messages correctly? It
doesn't mark a message read in every newsgroup in which it's
cross-posted, once you've read it in one such newsgroup?

You need a better news reader. I'm surprised Thunderbird doesn't
support even a basic feature like that, but given that it doesn't I'll
suggest it's time for you to get rid of the training wheels. :)

Thunderbird does a lot right as far as UI, and a whole lot wrong when it
comes to everything else. My project (the one that spawned this thread,
actually) is to create a news reader that works the way *I* want a news
reader to work ;-)
 

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
474,263
Messages
2,571,062
Members
48,769
Latest member
Clifft

Latest Threads

Top