简体   繁体   中英

Java as external component using Swing: main thread never exits

I am trying to use a Java component inside another program. The program is a Web Application running on Tomcat, and my component uses Swing to open a web browser. The user is prompted to navigate to a specific page, and using controls on the JFrame, the page is saved locally in HTML format. This locally saved file will be used later for further elaboration. The problem is that, when executed from Eclipse, my code works without flaws. When I use the component inside the Application running on Tomcat, however, it's not running correctly. The window appears, navigation is working, and the data gets retrieved without problems. The thing is that the main app (the one running on Tomcat) has to resume its execution after the component (Java) has done its work. Evidently, something goes wrong, as the web app simply hangs, and never continues.

In my component, I use two classes: a Browser and a Helper. The Browser is based upon the DJ NativeSwing libraries and contains the logic to initialize and display a simple web browser. The Helper class provides me with a "front-end" method which allows me to execute the browser in its own thread. I am using SwingUtilities.invokeAndWait instead of invokeLater, as it's not quite clear to me how to use wait() and notify(). I tried and failed.

Summing up the code, it would look something like this:

import javax.swing.SwingUtilities;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;

public class Helper {
    public String Browse() {
    Browser vb = new Browser();
    try {
        Browser.log("This is Helper, now initiating.");
        NativeInterface.open();
        Thread th = new Thread(vb);
        Browser.log("Helper is invoking thread...");
        SwingUtilities.invokeAndWait(th);
        NativeInterface.runEventPump();
        Browser.log("Thread exited! Back to Helper!");
        System.out.println(vb.getData());
        Browser.log("Returning data to caller: \n" + vb.getData());
        return vb.getData();
    }catch (Exception ex) {
        ex.printStackTrace();
        Browser.log("MainFail: " + ex.getMessage());
        return ex.getMessage();
    }
}

And the Browser class:

import java.awt.BorderLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import chrriis.dj.nativeswing.NativeSwing;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;

public class Browser extends JFrame implements ActionListener, Runnable {

    private static final long serialVersionUID = 1L;
    private JButton btn;
    private JWebBrowser webBrowser;
    private JPanel panel;
    private String data = "";

    public static void log(String s) {
        try {
            BufferedWriter ostr = new BufferedWriter(new FileWriter("F:/java_out.log", true));
            Date dt = new Date();
            SimpleDateFormat formatter = new SimpleDateFormat("hh:mm:ss");
            String str = formatter.format(dt) + "\t -> " + s + "\n";
            ostr.write(str);
            ostr.close();
        } catch(Exception ex) {
            System.out.println("Logger: " + ex.getMessage());
        }
    }

    public Browser() {
        Browser.log("Initializing...");
        NativeSwing.initialize();
        NativeInterface.open();

        panel = new JPanel();
        btn = new JButton();
        btn.setText("Save Page");
        btn.addActionListener(this);
        btn.setMargin(new Insets(5, 5, 5, 5));
        VisureBrowser.log("Button is ready!");

        JPanel webBrowserPanel = new JPanel(new BorderLayout());
        webBrowserPanel.setBorder(BorderFactory.createTitledBorder("Navigate and save"));
        webBrowser = new JWebBrowser();
        webBrowser.setButtonBarVisible(true);
        webBrowser.setMenuBarVisible(false);
        webBrowser.setLocationBarVisible(true);
        VisureBrowser.log("Panel is ready!");

        this.setTitle("Simple Browser");
        this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        this.setSize(800, 600);
        this.setLayout(new BorderLayout());
        this.setLocationByPlatform(true);
        this.setLocationRelativeTo(null);
        VisureBrowser.log("Frame is ready!");

        this.add(btn, BorderLayout.BEFORE_FIRST_LINE);
        btn.setVisible(true);
        webBrowserPanel.add(webBrowser, BorderLayout.CENTER);
        webBrowser.setVisible(true);
        this.add(webBrowserPanel, BorderLayout.CENTER);
        panel.setVisible(true);
        this.setVisible(true);

        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
            SwingUtilities.updateComponentTreeUI(this);
        } catch (Exception e) {
            Browser.log("Constructor: " + e.getMessage());
            Browser.log(e.getStackTrace().toString());
            e.printStackTrace();
        }

        Browser.log("Look and Feel ready as well! Off to work...");
    }


    public String getData() {
        return this.data;
    }


    public void close() {
        WindowEvent wev = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev);
    }


    public void run() {
        Browser.log("Navigating to the index page...");
        webBrowser.navigate("http://www.google.com/");
        Browser.log("Navigation successful!");
    }


    @Override
    public void actionPerformed(ActionEvent e) {
        Browser.log("An action has been performed!");
        Object src = e.getSource();
        if (src.equals(btn)) {
            try {
                Browser.log("Getting data...");
                this.data = webBrowser.getHTMLContent();
                Browser.log("Closing...");
                this.close();
            } catch( Exception ex) {
                Browser.log("ClickFail: " + ex.getMessage());
                Browser.log(ex.getStackTrace().toString());
            }
        }
    }   
}

The output I am getting is, from Eclipse:

12:28:54     -> Initializing...
12:28:55     -> Button is ready!
12:28:55     -> Panel is ready!
12:28:55     -> Frame is ready!
12:28:56     -> Look and Feel ready as well! Off to work...
12:28:56     -> This is Helper, now initiating.
12:28:56     -> Helper is invoking thread...
12:28:56     -> Navigating to the index page...
12:28:56     -> Navigation successful!
12:29:04     -> An action has been performed!
12:29:04     -> Getting data...
12:29:04     -> Closing...
12:29:06     -> Thread exited! Back to Helper!
12:29:06     -> Returning data to caller:
.....here comes all the data.....

and the data I am getting when executing inside Tomcat is the following:

12:28:54     -> Initializing...
12:28:55     -> Button is ready!
12:28:55     -> Panel is ready!
12:28:55     -> Frame is ready!
12:28:56     -> Look and Feel ready as well! Off to work...
12:28:56     -> This is Helper, now initiating.
12:28:56     -> Helper is invoking thread...
12:28:56     -> Navigating to the index page...
12:28:56     -> Navigation successful!
12:29:04     -> An action has been performed!
12:29:04     -> Getting data...
12:29:04     -> Closing...

Yes, I used the same lines, but the output is this, it only goes until the closing line. The thing that I did not expect is the delay between the closing of the window and the resume of the main activity: around 2-3 seconds per run. I immagine it has something to do with the secondary (GUI) thread not exiting properly, but I am at a loss as to how to proceed.

Any help will be greatly appreciated! :)

You're doing it wrong.

Tomcat is a web application server and should not be popping-up any Swing components. If the server were running elsewhere, every user would end up launching Swing windows on the server and the user would never see them. What you are experiencing (Tomcat launching a Swing window and being able to actually see it) is just a coincidence because your server is running on localhost .

What you want to do is have your users start from a web browser, then navigate to the correct site to consume your content. That is, the web browser should already be available: you shouldn't have to launch one on your users' behalf.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM