DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • Interrupt Testing: Bulletproof Your App for the Real World
  • How to Secure Your Raspberry Pi and Enable Safe, Resilient Updates
  • Leveraging Seekable OCI: AWS Fargate for Containerized Microservices
  • Analysis of Flubot Malware on Android OS

Trending

  • Stop Building Monolithic AI Brains, Build a Specialist Team Instead
  • Why Traditional CI/CD Falls Short for Cloud Infrastructure
  • How We Broke the Monolith (and Kept Our Sanity): Lessons From Moving to Microservices
  • How to Troubleshoot Common Linux VPS Issues: CPU, Memory, Disk Usage
  1. DZone
  2. Coding
  3. Tools
  4. Step-by-Step Instructions for Integrating DJ Native Swing into NetBeans RCP

Step-by-Step Instructions for Integrating DJ Native Swing into NetBeans RCP

By 
Geertjan Wielenga user avatar
Geertjan Wielenga
·
Oct. 18, 10 · News
Likes (0)
Comment
Save
Tweet
Share
51.7K Views

Join the DZone community and get the full member experience.

Join For Free

Here's how to integrate DJ Native Swing into a NetBeans RCP application. We will create multiple operating-system specific modules, each with the JARs and supporting classes needed for the relevant operating system.

Then, in a module installer, we will enable only the module that is relevant for the operating system in question. I.e., if the user is on Windows, only the Windows module will be enabled, while all other modules will be disabled.

Many thanks to Aljoscha Rittner for all of the code and each of the steps below. Any errors are my own, his instructions are perfect.

1. Download DJ Native Swing.
 
2. Go to download.eclipse.org/eclipse/downloads/drops/R-3.6-201006080911/. There, under the heading "SWT Binary and Source", download and extract the os-specific ZIPs that you want to support.

3. Unzip your downloaded ZIPs. Rename your swt.jar in the unzipped files to the full name of the zip. For example, instead of multiple "swt.jar" files, you'll now have JAR names such as "swt-3.6-gtk-linux-x86_64.jar" and "swt-3.6-win32-win32-x86_64.jar". Because these JARs will be in the same cluster folder in the NetBeans Platform application, they will need to have different names.

4. Let's start with Linux. Put the two DJ Native Swing JARs ("DJNativeSwing.jar" and "DJNativeSwing-SWT.jar") into a NetBeans library wrapper module named "com.myapp.nativeswing.linux64". Also put the "swt-3.6-gtk-linux-x86_64.jar" into the library wrapper module, while checking the "project.xml" and making sure there's a classpath extension entry for each of the three JARs in your library wrapper module.

5. Do the same for all the operating systems you're supporting, i.e., create a new library wrapper module like the above, with the operating-system specific SWT Jar, together with the two DJ Native Swing JARs.

6. Create a new module named "DJNativeSwingAPI", with code name base "com.myapp.nativeswing.api".

7. In the above main package, create a subpackage "browser", where you'll create an API to access the different implementations:

public interface Browser {
    public JComponent getBrowserComponent();
    public void browseTo (URL url);
    public void dispose();
}
public interface BrowserProvider {
    public Browser createBrowser();
}

8. Make the "browser" package public and let all the operating-system specific library wrapper modules depend on the API module.

9. In each of the operating-system specific modules, create an "impl" subpackage with the following content, here specifically for Windows 64 bit:

package com.myapp.nativeswing.windows64.impl;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import com.myapp.nativeswing.api.browser.Browser;
import com.myapp.nativeswing.api.browser.BrowserProvider;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider(service = BrowserProvider.class)
public class Win64BrowserProvider implements BrowserProvider {

    private boolean isInitialized;

    @Override
    public Browser createBrowser() {
        initialize();
        return new Win64Browser();
    }

    private synchronized void initialize() {
        if (!isInitialized) {
            NativeInterface.open();
            isInitialized = true;
        }
    }

}
import chrriis.dj.nativeswing.NSComponentOptions;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;
import com.myapp.nativeswing.api.browser.Browser;
import java.net.URL;
import javax.swing.JComponent;

class Win64Browser implements Browser {

    private JWebBrowser webBrowser;
    public Win64Browser() {

        //If not this, browser component creates exceptions when you move it around,
        //this flag is for the native peers to recreate in the new place:
        webBrowser = new JWebBrowser(NSComponentOptions.destroyOnFinalization());

    }

    public JComponent getBrowserComponent() {
        return webBrowser;
    }

    public void browseTo(URL url) {
        webBrowser.navigate(url.toString());
    }

    public void dispose() {
        webBrowser.disposeNativePeer();
        webBrowser = null;
    }   

}

10. Copy the above two classes into all your other operating-system specific library wrapper modules. Rename the classes accordingly.

11. In the DJ Native Swing API module, create a new subpackage named "utils", with this class, which programmatically enables/disables modules using the NetBeans AutoUpdate API:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.netbeans.api.autoupdate.OperationContainer;
import org.netbeans.api.autoupdate.OperationContainer.OperationInfo;
import org.netbeans.api.autoupdate.OperationException;
import org.netbeans.api.autoupdate.OperationSupport;
import org.netbeans.api.autoupdate.OperationSupport.Restarter;
import org.netbeans.api.autoupdate.UpdateElement;
import org.netbeans.api.autoupdate.UpdateManager;
import org.netbeans.api.autoupdate.UpdateUnit;
import org.openide.LifecycleManager;
import org.openide.modules.ModuleInfo;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;

/**
* Der ModuleHandler ist eine Hilfsklasse zum programatischen (de)aktivieren
* von Modulen und der Analyse von installierten aktiven Modulen.
* @author rittner
*/
public class ModuleHandler {

  private boolean restart = false;
  private OperationContainer<OperationSupport> oc;
  private Restarter restarter;
  private final boolean directMode;


  public ModuleHandler() {
    this (false);
  }
  public ModuleHandler(boolean directMode) {
    this.directMode = directMode;
  }

  /**
   * Gibt eine sortierte Liste der Codename-Base aller aktiven installierten
   * Module zurück.
   * <p>
   * Es handelt sich dabei explizit um einen aktuellen Zwischenstand, der sich
   * jeder Zeit verändern kann.
   * @param startFilter Es werden nur die Module zurückgegeben, die mit dem Startfilter-Namen anfangen (oder null für alle)
   * @param includeDisabled Wenn true, werden auch alle inaktiven Module ermittelt.
   * @return Sortierte Liste der Codename-Base
   */
  public List<String> getModules(String startFilter, boolean includeDisabled) {
    List<String> activatedModules = new ArrayList<String>();
    Collection<? extends ModuleInfo> lookupAll = Lookup.getDefault().lookupAll(ModuleInfo.class);
    for (ModuleInfo moduleInfo : lookupAll) {
      if (includeDisabled || moduleInfo.isEnabled()) {
        if (startFilter == null || moduleInfo.getCodeNameBase().startsWith(startFilter)) {
          activatedModules.add(moduleInfo.getCodeNameBase());
        }
      }
    }
    Collections.sort(activatedModules);
    return activatedModules;
  }


  /**
   * Führt einen Neustart der Anwendung durch, wenn der vorherige setModulesState
   * ein Flag dafür gesetzt hat. mit force, kann der Restart erzwungen werden.
   * <p>
   * Man sollte nicht davon ausgehen, dass nach dem Aufruf der Methode
   * zurückgekehrt wird.
   * @param force
   */
  public void doRestart(boolean force) {
    if (force || restart) {
      if (oc != null && restarter != null) {
        try {
          oc.getSupport().doRestart(restarter, null);
        } catch (OperationException ex) {
          Exceptions.printStackTrace(ex);
        }
      } else {
        LifecycleManager.getDefault().markForRestart();
        LifecycleManager.getDefault().exit();
      }
    }
  }

  /**
   * Aktiviert oder deaktivert die Liste der Module
   * @param enable
   * @param codeNames
   * @return true, wenn ein Neustart zwingend erforderlich ist
   */
  public boolean setModulesState (boolean enable, Set<String> codeNames) {
    boolean restartFlag;
    if (enable) {
      restartFlag = setModulesEnabled(codeNames);
    } else {
      restartFlag = setModulesDisabled(codeNames);
    }
    return restart = restart || restartFlag;
  }

  private boolean setModulesDisabled(Set<String> codeNames) {
    Collection<UpdateElement> toDisable = new HashSet<UpdateElement>();
    List<UpdateUnit> allUpdateUnits =
            UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE);
    for (UpdateUnit unit : allUpdateUnits) {
      if (unit.getInstalled() != null) {
        UpdateElement el = unit.getInstalled();
        if (el.isEnabled()) {
          if (codeNames.contains(el.getCodeName())) {
            toDisable.add(el);
          }
        }
      }
    }


    if (!toDisable.isEmpty()) {
      oc = directMode ? OperationContainer.createForDirectDisable() : OperationContainer.createForDisable();
      for (UpdateElement module : toDisable) {
        if (oc.canBeAdded(module.getUpdateUnit(), module)) {
          OperationInfo operationInfo = oc.add(module);
          if (operationInfo == null) {
            continue;
          }
          // get all module depending on this module
          Set<UpdateElement> requiredElements =
                  operationInfo.getRequiredElements();
          // add all of them between modules for disable
          oc.add(requiredElements);
        }
      }


      try {
        // get operation support for complete the disable operation
        OperationSupport support = oc.getSupport();
        // If support is null, no element can be disabled.
        if ( support != null ) {
          restarter = support.doOperation(null);
        }
      } catch (OperationException ex) {
        Exceptions.printStackTrace(ex);
      }
    }
    return restarter != null;

  }

  private boolean setModulesEnabled(Set<String> codeNames) {
    Collection<UpdateElement> toEnable = new HashSet<UpdateElement>();
    List<UpdateUnit> allUpdateUnits =
            UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE);
    for (UpdateUnit unit : allUpdateUnits) {
      if (unit.getInstalled() != null) {
        UpdateElement el = unit.getInstalled();
        if (!el.isEnabled()) {
          if (codeNames.contains(el.getCodeName())) {
            toEnable.add(el);
          }
        }
      }
    }


    if (!toEnable.isEmpty()) {
      oc = OperationContainer.createForEnable();
      for (UpdateElement module : toEnable) {
        if (oc.canBeAdded(module.getUpdateUnit(), module)) {
          OperationInfo operationInfo = oc.add(module);
          if (operationInfo == null) {
            continue;
          }
          // get all module depending on this module
          Set<UpdateElement> requiredElements =
                  operationInfo.getRequiredElements();
          // add all of them between modules for disable
          oc.add(requiredElements);
        }
      }


      try {
        // get operation support for complete the enable operation
        OperationSupport support = oc.getSupport();
        if (support != null) {
          restarter = support.doOperation(null);
        }
        return true;
      } catch (OperationException ex) {
        Exceptions.printStackTrace(ex);
      }
    }
    return false;

  }

}
12. Create a ModuleInstall class in the API module. In this class, we need to create a map, connecting all the operating systems to the related code name base of the module relevant to the specific operating system. For this, we use "os.arch" and "os.name". Then we create an enable list and a disable list for the code name base. We create two handlers, one to disable everything, the other to enable just the relevant module.
public class Installer extends ModuleInstall {

    @Override

    public void restored() {

        Map modelMap = new HashMap();
        modelMap.put("Windows.64", "com.myapp.nativeswing.windows64");
        modelMap.put("Linux.64", "com.myapp.nativeswing.linux64");

        String osArch = System.getProperty("os.arch");
        if ("amd64".equals(osArch)) {
            osArch = "64";
        } else {
            osArch = "32";
        }
       
        String osName = System.getProperty("os.name");
        if (osName.startsWith("Windows")) {
            osName = "Windows";
        }
        if (osName.startsWith("Mac")) {
            osName = "Mac";
        }

        Map osNameMap = new HashMap();
        osNameMap.put("Windows", "Windows");
        osNameMap.put("Linux", "Linux");
        osNameMap.put("Mac", "Mac");

        String toEnable = modelMap.get(osNameMap.get(osName) + "." + osArch);
        Set toDisable = new HashSet(modelMap.values());
        if (toEnable != null) {
            toDisable.remove(toEnable);
        }

        ModuleHandler disabler = new ModuleHandler(true);
        disabler.setModulesState(false, toDisable);

        ModuleHandler enabler = new ModuleHandler(true);
        enabler.setModulesState(true, Collections.singleton(toEnable));

    }

}
13. Finally, create yet another module, where the TopComponent will be found that will host the browser from DJ Native Swing. So, create a new module, add a window where the browser will appear, and set a dependency on the DJ Native Swing API module.

In the constructor of the window add the following:
setLayout(new BorderLayout());
BrowserProvider bp = Lookup.getDefault().lookup(BrowserProvider.class);
if (bp!=null){
    Browser createBrowser = bp.createBrowser();
    add(createBrowser.getBrowserComponent(), BorderLayout.CENTER);
}
14. By default, library wrapper modules are set to "1.4" source code level and to "autoload". You will need to change "1.4" to "1.6" (since you're using annotations above). You will also need to change "autoload" to "regular", otherwise they will never be loaded, since no module depends on them.

15. On Linux, at least on Ubuntu, make sure you have done something like this:
export MOZILLA_FIVE_HOME=/usr/lib/mozilla
export LD_LIBRARY_PATH=$MOZILLA_FIVE_HOME 

On Linux (at least on Ubuntu), you also need to set an impl dependency on the "JNA" module.

16. In "platform.properties", add this line:

run.args.extra=-J-Dsun.awt.disableMixing=true

Hurray, you're done, once you run the application:

Note: above I followed these instructions to remove the tab in the browser window.

Rich client platform operating system NetBeans

Opinions expressed by DZone contributors are their own.

Related

  • Interrupt Testing: Bulletproof Your App for the Real World
  • How to Secure Your Raspberry Pi and Enable Safe, Resilient Updates
  • Leveraging Seekable OCI: AWS Fargate for Containerized Microservices
  • Analysis of Flubot Malware on Android OS

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: