I would not try to solve this from an event perspective. The event system isn't intended to work that way.
I would define an interface (X) that encapsulates interactions with the server. The implementation, Y, would save the parameters of the last server request. After a timeout occurs, and the user has re-authenticated, then I can ask Y to resend the last request.
Extra benefit: since X is an interface, this simplifies testing as Y can be replaced with a mock object to test the GUI, and test code can call Y without a GUI present.
Update:
Here is an approach that uses SwingWorker to run the server interaction on a bg thread. A ChangeEvent is used to return the results to the EDT for processing. SwingWorker's publish/process are used to handle the user interaction for re-auth. A benefit of SwingWorker is that should the server take a long time to respond, the UI can still respond to repaint events.
class Test extends JPanel {
private JButton b;
public Test() {
b = new JButton(new AbstractAction("Do something") {
@Override
public void actionPerformed(ActionEvent e) {
final JButton btn = (JButton) e.getSource();
btn.setEnabled(false);
Object req = new Object(); // Replace w/ apropriate type
new RequestDispatch(req, new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
final Object req = e.getSource();
// Do something with data from 'req'
btn.setEnabled(true);
}
});
}
});
add(b);
}
}
class RequestDispatch extends SwingWorker<Object, Void> {
private enum DispatchState { Ready, Running, Canceled }
private final ChangeListener listener;
private DispatchState dstate = DispatchState.Ready;
private final Object req;
RequestDispatch(Object req, ChangeListener listener)
{
this.req = req;
this.listener = listener;
execute();
}
@Override
protected Object doInBackground() throws Exception {
while (true) {
DispatchState st = getDState();
if (st == DispatchState.Ready)
{
try {
setDstate(DispatchState.Running);
// send request to the server, update req with response
return req;
}
catch (TimeoutException ex) {
this.publish((Void)null);
synchronized (this) {
wait();
}
}
}
if (st == DispatchState.Canceled) {
return req;
}
}
}
@Override
protected void process(List<Void> chunks) {
// Session has timed out
// Ask the user to re-authenticate
int result = JOptionPane.showConfirmDialog(null, "Login");
if (result == JOptionPane.CANCEL_OPTION) {
setDstate(DispatchState.Canceled);
}
else {
setDstate(DispatchState.Ready);
}
}
private synchronized DispatchState getDState() {
return dstate;
}
private synchronized void setDstate(DispatchState dstate) {
this.dstate = dstate;
notifyAll();
}
}