Статьи

Пошаговые инструкции по интеграции DJ Native Swing в RCP NetBeans

Вот как интегрировать DJ Native Swing в RCP-приложение NetBeans. Мы создадим несколько модулей, специфичных для операционной системы, каждый с JAR и поддерживающими классами, необходимыми для соответствующей операционной системы.

Затем в установщике модуля мы включим только тот модуль, который имеет отношение к рассматриваемой операционной системе. То есть, если пользователь находится в Windows, будет включен только модуль Windows, а все остальные модули будут отключены.

Большое спасибо Aljoscha Rittner за весь код и за каждый из шагов ниже. Любые ошибки мои, его инструкции идеальны.

1. Скачать DJ Native Swing .
 
2. Перейдите по адресу download.eclipse.org/eclipse/downloads/drops/R-3.6-201006080911/ . Там, под заголовком «SWT Binary and Source», загрузите и извлеките ZIP-файлы, специфичные для ОС, которые вы хотите поддерживать.

3. Распакуйте загруженные ZIP-файлы. Переименуйте ваш swt.jar в разархивированных файлах на полное имя почтового индекса. Например, вместо нескольких файлов «swt.jar» теперь у вас будут имена JAR, такие как «swt-3.6-gtk-linux-x86_64.jar» и «swt-3.6-win32-win32-x86_64.jar». Поскольку эти JAR-файлы будут находиться в одной папке кластера в приложении платформы NetBeans, они должны иметь разные имена.

4. Начнем с Linux. Поместите два DJ Jative Swing JAR («DJNativeSwing.jar» и «DJNativeSwing-SWT.jar») в модуль оболочки библиотеки NetBeans с именем «com.myapp.nativeswing.linux64». Также поместите «swt-3.6-gtk-linux-x86_64.jar» в модуль оболочки библиотеки, одновременно проверяя «project.xml» и проверяя наличие записи расширения classpath для каждого из трех JAR-файлов в модуле оболочки библиотеки.

5. Сделайте то же самое для всех поддерживаемых вами операционных систем, т. Е. Создайте новый модуль-оболочку библиотеки, как описано выше, с SWT Jar для конкретной операционной системы и двумя DJ Native Swing JAR.

6. Создайте новый модуль с именем «DJNativeSwingAPI» с базой кодовых имен «com.myapp.nativeswing.api».

7. В приведенном выше основном пакете создайте подпакет «браузер», в котором вы создадите API для доступа к различным реализациям:

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

8. Сделайте пакет «браузер» общедоступным, и пусть все модули оболочки библиотеки для конкретной операционной системы зависят от модуля API.

9. В каждом из модулей, специфичных для операционной системы, создайте подпакет «impl» со ​​следующим содержимым, здесь специально для 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. Скопируйте два вышеупомянутых класса во все другие модули-оболочки библиотек, специфичные для вашей операционной системы. Переименуйте классы соответственно.

11. В модуле API DJ Native Swing создайте новый подпакет под названием «utils» с этим классом, который программно включает / отключает модули с помощью API-интерфейса NetBeans AutoUpdate:

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. Создайте класс ModuleInstall в модуле API. В этом классе нам нужно создать карту, соединяющую все операционные системы с соответствующей базой кодовых имен модуля, соответствующей конкретной операционной системе. Для этого мы используем «os.arch» и «os.name». Затем мы создаем список разрешений и список отключений для базы кодовых имен. Мы создаем два обработчика, один для отключения всего, другой для включения только соответствующего модуля.

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. Наконец, создайте еще один модуль, в котором будет найден TopComponent, в котором будет размещен браузер от DJ Native Swing. Итак, создайте новый модуль, добавьте окно, в котором появится браузер, и установите зависимость от модуля API DJ Native Swing.

В конструкторе окна добавьте следующее:

setLayout(new BorderLayout());
BrowserProvider bp = Lookup.getDefault().lookup(BrowserProvider.class);
if (bp!=null){
    Browser createBrowser = bp.createBrowser();
    add(createBrowser.getBrowserComponent(), BorderLayout.CENTER);
}

14. По умолчанию для модулей-оболочек библиотеки установлен уровень исходного кода «1.4» и «автозагрузка». Вам нужно будет изменить «1.4» на «1.6» (поскольку вы используете аннотации выше). Вам также необходимо изменить «автозагрузку» на «обычную», иначе они никогда не будут загружены, так как от них не зависит ни один модуль.

15. В Linux, по крайней мере в Ubuntu, убедитесь, что вы сделали что-то вроде этого:

export MOZILLA_FIVE_HOME=/usr/lib/mozilla
export LD_LIBRARY_PATH=$MOZILLA_FIVE_HOME 

В Linux (по крайней мере, в Ubuntu) вам также необходимо установить зависимость impl от модуля «JNA».

16. В «platform.properties» добавьте эту строку:

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

Ура, все готово, как только вы запустите приложение:

Примечание: выше я следовал этим инструкциям, чтобы удалить вкладку в окне браузера.