I have an application that in the back-end deploys a lightweight HTTP server (Jetty).
The servle开发者_高级运维ts basically update a MySQL database. I have wired everything using Spring.So far ok.
I have a UI that I want to use to interact with the server.
If the UI runs locally I want it to display in aJTree
the currently logged-in users.
So I decided to start the server i.e. start Spring when launching the main frame.
On top of that, in order to update the UI's JTree to show all the logged-in users I thought to use the Observer-Observable and have the code that accepts connections be an Observable
.
A ConnectionListener
would be notified of incoming connections storing them in an ArrayBlockingQueue
and background thread (having a reference to the DefaultMutableTreeNode
root) of the Jtree
would update it -in the EDT thread of course via SwingUtilities
.
My problem is the following:
If I do:
public class AdminFrame extends JFrame {
public static ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("myBeans");
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
public void run() {
//Show GUI
}
}
}
The GUI is not drawn but I can understand that since once Spring start and the server starts the thread does not return.
So I start Spring in a back-end thread as follows:
public class ServerThread extends Thread {
@Override
public void run() {
ClassPathXmlApplicationContext ctx = ApplicationCtx.getApplicationCtx();
}
}
and
public class ApplicationCtx {
private static ClassPathXmlApplicationContext ctx;
private ApplicationCtx(){}
public static synchronized ClassPathXmlApplicationContext getApplicationCtx(){
if(ctx == null){
ctx = new ClassPathXmlApplicationContext("myBeans.xml");
}
return ctx;
}
}
The GUI now shows up and seems ok BUT I am missing the notification.
PROBLEM:
It is not possible to get notification for the new connections:
1) If I register for notification as follows:
public class AdminFrame extends JFrame {
public static void main(String[] args) {
ServerThread lightweightServer = new ServerThread();
lightweightServer.start();
Thread.sleep(9000);//Temporary solution to make sure the server is started up before GUI
ConnectHandler connectHandler = (ConnectHandler) ApplicationContext.getApplicationCtx().getBean("connectHandler");
connectHandler.addObserver(new ConnectionListener());
EventQueue.invokeLater(new Runnable() {
public void run() {
//SHOW GUI
}
}
The GUI never shows up. If I remove the line:
ConnectHandler connectHandler = (ConnectHandler) ApplicationContext.getApplicationCtx().getBean("connectHandler");
The GUI shows up.
2) If I register inside the EDT i.e. inside the constructor of AdminFrame
the GUI does not show up either.
public class AdminFrame extends JFrame {
public static void main(String[] args) {
ServerThread lightweightServer = new ServerThread();
lightweightServer.start();
Thread.sleep(9000);//Temporary solution to make sure the server is started up before
EventQueue.invokeLater(new Runnable() {
public void run() {
AdminFrame frame = new AdminFrame();
//Other code
}
Again if I remove the following line the GUI shows up but this way I can not register to get notifications:
ConnectHandler connectHandler = (ConnectHandler) ApplicationContext.getApplicationCtx().getBean("connectHandler");
This must be a thread issue but I can not understand what is the problem.
Why is the second call to Spring application context to get theConnectHandler
make the thread not return?
What am I doing wrong here?
UPDATE:
The problem is solved if I add in the Spring configuration file the attribute lazy-init="true"
to the bean that starts the Jetty server.
Thanks
Serializable
orObservate
required wraping GUI rellated code into (in most cases)invokeAndWait()
output from
SwingWorker
(since is guaranteed but I saw some cases that not works as I expecting) orRunnable#Thread
required raping GUI rellated code intoinvokeLater()
you can prepare your GUI (then there EDT exists) before, and visibility
.
JFrame#pack();
JFrame#setVisible(true);
.
for this Container
you have to invoke inside invokeXxx();
or best would be from javax.swing.Action()
If setting up lazy-init to true solve your problem, i think its not solved completely.
By default, all beans initialized at startup so you get the problem in starting if any of the bean is not initialized correctly.
If you set it to false, bean will not be invoked and you will not face any isssue. So the problem is not solved but deferred.
精彩评论