Geertjan is a DZone Zone Leader and has posted 437 posts at DZone. You can read more from them at their website. View Full User Profile

Reusing Existing NetBeans IDE JavaFX Infrastructure in NetBeans IDE Plugins

10.08.2012
| 1986 views |
  • submit to reddit

Let's install the JavaFX WebView in a TopComponent in NetBeans IDE, reusing a lot of JavaFX infrastructure that is already in NetBeans IDE, since the 7.3 release for the support of the official JavaFX browser in 7.3.

That means, no need for JavaFX native libraries, nor for the javafxrt.jar.

The result:

The code:

public final class DeveloperFAQTopComponent extends TopComponent {

    private static URLClassLoader browserCL;

    public DeveloperFAQTopComponent() {
        initComponents();
        setName(Bundle.CTL_DeveloperFAQTopComponent());
        setToolTipText(Bundle.HINT_DeveloperFAQTopComponent());
        WebBrowser browser = createBrowser(null);
        browser.setURL("http://wiki.netbeans.org/NetBeansDeveloperFAQ");
        setLayout(new BorderLayout());
        add(browser.getComponent(), BorderLayout.CENTER);
    }

    private static String[] getFXClassPath() {
        Collection<? extends WebBrowserImplProvider.JFXRuntimePathProvider> pathProviders =
                Lookup.getDefault().lookupAll(WebBrowserImplProvider.JFXRuntimePathProvider.class);
        for (WebBrowserImplProvider.JFXRuntimePathProvider rtPathProvider : pathProviders) {
            String rtPath = rtPathProvider.getJFXRuntimePath();
            if (rtPath == null) {
                continue;
            }
            return new String[]{
                        rtPath + File.separatorChar + "lib" + File.separatorChar + "jfxrt.jar" //NOI18N
                    };
        }
        return null;
    }
   
    static WebBrowser createBrowser(File runtimePath) {
        ClassLoader cl = getBrowserClassLoader(runtimePath);
        try {
            if (cl != null) {
                // test that JavaFX has latest required APIs:
                cl.loadClass("com.sun.javafx.scene.web.Debugger");
                Class platform = cl.loadClass("javafx.application.Platform");
                Method m = platform.getMethod("setImplicitExit", boolean.class);
            }
        } catch (Throwable ex) {
            JOptionPane.showMessageDialog(null, "JavaFX runtime is too old - "
                    + "minimum version required is 2.2.0b20");
        }
        try {
            if (cl != null) {
                Class impl = cl.loadClass("org.netbeans.core.browser.webview.ext.WebBrowserImpl");
                Constructor c = impl.getConstructor(new Class[]{});
                return (WebBrowser) c.newInstance(new Object[]{});
            }
        } catch (Throwable ex) {
            JOptionPane.showMessageDialog(null, ex.getMessage());
        }
        return null;
    }

    private static URLClassLoader createBrowserClassLoader(File runtimePath) {
        synchronized (WebBrowserImplProvider.class) {
            File extjar = InstalledFileLocator.getDefault().locate("modules/ext/core.browser.webview-ext.jar",
                    "org.netbeans.core.browser.webview", false); // NOI18N
            if (extjar == null) {
                return null;
            }
            String[] fxpath = null;
            if (null == runtimePath) {
                fxpath = getFXClassPath();
            } else {
                fxpath = new String[]{new File(new File(runtimePath, "lib"), "jfxrt.jar").getAbsolutePath()};
            }
            if (fxpath == null) {
                return null;
            }
            List<URL> urls = new ArrayList<URL>();
            try {
                urls.add(extjar.toURI().toURL());
                for (String fx : fxpath) {
                    urls.add(new File(fx).toURI().toURL());
                }
                return new URLClassLoader(urls.toArray(new URL[]{}),
                        WebBrowserImplProvider.class.getClassLoader());
            } catch (MalformedURLException m) {
                return null;
            }
        }
    }

    private static ClassLoader getBrowserClassLoader(File runtimePath) {
        synchronized (WebBrowserImplProvider.class) {
            if (null != runtimePath) {
                return createBrowserClassLoader(runtimePath);
            }
            if (browserCL != null) {
                return browserCL;
            }
            browserCL = createBrowserClassLoader(null);
            return browserCL;
        }
    }

    ...
    ...
    ...

Required import statements:

import java.awt.BorderLayout;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.JOptionPane;
import org.netbeans.api.settings.ConvertAsProperties;
import org.netbeans.core.browser.api.WebBrowser;
import org.netbeans.core.browser.webview.WebBrowserImplProvider;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.Lookup;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;

The above code comes from here:

http://hg.netbeans.org/main/file/8029769c5178/core.browser.webview/src/org/netbeans/core/browser/webview/WebBrowserImplProvider.java

Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Emilian Bold replied on Tue, 2012/10/09 - 1:19am

NetBeans has among the best engineered API/SPIs in Java land and Tulach wrote a great book about this.

That bein said, the code above is just horrible and should not be published on major websites like this. We should not encourage people to use reflection and classloaders like this.

If it's really something that people need, let's push for an official API. 

Jirka X1 replied on Thu, 2012/10/11 - 10:11am in response to: Emilian Bold

Do you really mean it? Should not be published? Just don't read it if you do not like it. I am happy it was published. You can file an RFE to make a nice API for it, before that RFE is implemented, I will use this.

Toni Epple replied on Fri, 2012/10/12 - 9:13pm

Yes, it's ugly, but it still means progress. Think of it: NetBeans 7.3 ships with a JavaFX runtime. Right now this is the only way to use it, if you have to (which I do). The NetBeans team already thinks about an API, and I've just filed a RFE:

http://netbeans.org/bugzilla/show_bug.cgi?id=220033

Vote for it, and add your comments/thoughts there!

Geertjan Wielenga replied on Sat, 2012/10/13 - 1:03pm

Indeed. This solution works and, until an official API exists (I'm looking forward to comments from Emilian and others to the issue Toni points to above), I encourage people to use classloaders and reflection exactly as described in this article.

Matt Coleman replied on Fri, 2012/11/09 - 12:34am in response to: Jirka X1

Me too happy we get to see it/published and use it.

what to buy a graphic designer 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.