Česky   |  Deutsch   |  English   |  Español   |  Français   |  Indonesia   |  日本語   |  한글   |  Polski   |  Português (BR)   |  Türkçe   |  中文   |  正體中文   |  Your Language  
PlanetNetbeans
Planet NetBeans ist eine Sammlung aller NetBeans-relevanten Gedanken aus der ganzen Blogosphäre.
Feeds
[RSS 1.0 Feed] [RSS 2.0 Feed]
[FOAF Subscriptions] [OPML Subscriptions]
Feed Abonnenten

Powered by:    Planet

Last updated:
November 21, 2008 03:14 PM
All times are UTC

sponsored by Sun Microsystems

visit NetBeans website
Seapegasus Blog - November 19, 2008 05:52 PM
NetBeans6.5_de

Wie Ihr sicher schon gesehen habt, ist NetBeans IDE 6.5 (Download) draußen. :) Aber wo ist die deutsche Community-Lokalisierung? Im Sprachen-Menü sind nur die 100%-fertigen aufgelistet (Chinesisch etc). Um die Community-Lokalisierungen wie Deutsch, Franzöisch, Spanisch, Italienisch, Polnisch, u.a. runterzuladen, klickt Ihr "More Languages..."

Der Direktlink ist: NetBeans Multilingual Download

Welche Module sind übersetzt? Auf der Statusseite seht Ihr, dass die gesamte Basis-IDE (Platform, Profiler, Versionsverwaltung), und auch Java SE, C/C++, PHP, Profiler, und Ruby, fertig sind. Das sind stolze 52%! Wir arbeiten noch an Java EE/Web App, Java ME, Groovy, SOA/XML.

Dank dafür an das ganze Team vor allem an Christian P. und Peter H.! Sobald die Lieferung bei mir ankommt, schick ich Euch das neue TranslatedFiles-T-Shirt. :)

Wie kann ich Übersetzungsfehler melden?

JNBB - Joschs NetBeans Blog - November 07, 2008 11:36 AM
NetBeans Demo Camp München - 18. November 2008

In Zusammenarbeit mit der Münchner NetBeans User Group (NUGM) wird das erste NetBeans Demo Camp in München am 18 November 2008 ausgerichtet. Mit Geertjan Wielenga vom NetBeans Team in Prag, Tanja Drüke von Genomatix in München, den NetBeans Dream Team Mitgliedern Sven Reimers, Anton Epple und meine Wenigkeit gibt es eine Menge spannender Vorträge, Informationen und die Möglichkeit uns auch einmal offline zu treffen.

Mit dem Release von NetBeans 6.5, dem 10ten Geburtstag von NetBeans gibt es auch eine Menge zu feiern.

Das Demo-Camp spezialisiert sich auf die Vorstellung von NetBeans als Rich Client Platform. Unternehmen und Einzelpersonen wurden eingeladen, ihre Produkte und ihre Erfahrungen austauschen. Als besondere Gäste werden Mitglieder des NetBeans Dream Team und des NetBeans-Team in Prag erwartet. Sie werden die neuesten Plattform-Funktionen von NetBeans 6.5 vorstellen.

Themen:

  • "New Features in NetBeans Platform 6.5"
    Geertjan Wielenga, NetBeans Guru, Sun Microsystems Inc., Prag.
  • "Demosession: ChipInspector - NetBeans APIs im Einsatz"
    Tanja Drüke, Genomatix Software GmbH, Munich, Germany.
  • "RCP Entwicklung in der Praxis: Third-party libraries und ClassLoader"
    Anton Epple, NetBeans Dream Team Member, Eppleton, Munich, Germany.
  • "Portierung von Swing Applikationen auf die NetBeans Platform"
    Aljoscha Rittner, CEO, Sepix GmbH, Hann. Münden, Germany.
  • "Grosseinsatz - NetBeans RCP im Grossprojekt"
    Sven Reimers, NetBeans Dream Team Member, ND SatCom Defence GmbH, Immenstadt, Germany.

Hier kann man sich kostenlos registrieren. Bis zum 18. November in München!


Beste Grüße
  Josch.

 

JNBB - Joschs NetBeans Blog - November 03, 2008 12:15 PM
Source Talk Tage 2008 - Aufzeichnungen

Die Veranstaltungsaufzeichnungen zu den Source Talk Tagen sind inzwischen verfügbar. Auch der Vortrag von Toni Epple zu NetBeans als Rich Client Platform ist dort zu sehen. Eine Übersicht des Vortrags in Folien findet sich hier. Der Vortrag ohne Video (24MB) und mit Video (68MB) stehen zum Download (als ZIP-Archive) bereit.

Eine Herausforderung wird es aber sein, die Medien abzuspielen. Hier ein Installationstutorial zum TeleTeachingTool. Beherzte Java-Entwickler werden aber die Grundvorraussetzungen (Java, Java Media Framework) schon installiert haben.

Viel Spaß damit.


Beste Grüße,
  Josch.

Seapegasus Blog - October 20, 2008 02:19 PM
Handelsreisende mit NetBeans im Gepäck

Geertjan hat ein interessantes Interview mit "unserem" Aljoscha Rittner über dessen neue Kundendatenverwaltung gemacht. Die Anwendung stellt die Kundenstandorte auf einer annotierten Landkarte dar (inklusive z.B. Radarfallen), und ein Handelsvertreter oder Geschäftsmensch lässt sich damit die optimale Route anzeigen, um auf seiner Fahrt so viele Kunden wie möglich abzuklappern. Sehr schön mit den NetBeans-Platform-APIs implementiert, inklusive Hilfesystem und Assistenten, davon kann man viel lernen.

Auf Englisch ist das Interview als Porting a Sales System to the NetBeans Platform auf der NetBeans Zone erhältlich. Alle Antworten auf Deutsch gibt es zusätzlich als PDF. Lest auch Joschs neuste Platform-Erkenntnisse in seinem Blog.

PS: Herzlichen Glückwunsch zum 10. Geburtstag, NetBeans!

JNBB - Joschs NetBeans Blog - September 11, 2008 06:21 AM
BeanDev: 3rd-Party Bibliotheken und Module

Trivial ist es nicht, manche Fremdbibliotheken in NetBeans zu integrieren. Insbesondere wenn die Libraries über eigene Classloader oder auch nur den lokalen Kontext-Classloader Klassen nachladen wollen. Hat man nämlich diese Bibliothek in ein Library-Wrapper-Modul gepackt, ist diese gut abgeschottet und könnte nur Klassen aus Modulen laden, zu denen Abhängikeiten definiert wurden.

Nun liegt es aber in der Natur der Sache, dass man ja diese (in ein Modul eingepackte) Bibliothek selber in Abhängigkeit einbinden möchte.

  1. Library Wrapper mit public packages <- nutzt <- Modul XYZ
  2. Modul XYZ mit public packages -> stellt Klassen zur Verfügung -> *
  3. Library Wrapper lädt per Classloader Klassen von Modul XYZ

Spätestens Punkt 3 scheitert, weil der Library-Wrapper sich Modul XYZ nicht in Abhängigkeit setzen kann. Es käme zu einer zyklischen Referenz, die das Build-System von NetBeans nicht auflösen kann.

Solche Probleme bekommt man z.B. mit dem Java Media Framework (Media Registry) oder Xalan (Digester).

Da man diese 3rd-Party-Bibliotheken nicht umprogrammieren kann (damit sie z.B. den System Classloader verwenden), muss man sich etwas anderes einfallen lassen.

Zwar unterscheidet NetBeans in seinem "New Project"-Assistenten zwischen Library-Wrapper Module und Module, tatsächlich gibt es keine technische Unterscheidung. Auch ein NetBeans-Modul mit Sourcecode darf Fremdbibliotheken einbinden. Grundsätzlich gibt es auch keine Einschränkung bei der Menge an Fremdbibliotheken (auch wenn es der Assistent des Library-Wrapper Modules suggeriert).

Wenn man in dem eigenen Modul-Projekt die Fremdbibliotheken importiert hat, werden die eigenen Klassen und die Klassen der JAR-Dateien in dem selben Module-Classloader geladen. Außerdem gibt es keine Notwendigkeit zyklische Abhängigkeiten zu deklarieren, weil mit so einem Aufbau schon die Bindung zueinander definiert wurde. Nach außen (aus der Sicht anderer Module) erscheint so ein hybrides Modul aus eigenen Klassen und Fremdklassen als eine Bibliothek mit (i.d.R.) unterschiedlichen Root-Packages.

Was ist also zu tun, um die JAR-Dateien erstmal in dem eigenen Projekt zu aufzunehmen?

Zunächst wechselt man vom Projects-Fenster zum Files-Fenster, damit man dort Ordner auf Projekt-Ebene anlegen kann. Direkt unter dem Projekt-Hauptordner legt man einen Unterordner release an. Allein dieser Ordner sorgt dafür, dass das Build-Script eine Sonderbehandlung für Fremdbiblioteken startet. Wenn dieser Ordner existiert, muss in diesem mindestens der Unterordner modules und darin der Ordner ext existieren (sonst kommt es zu einem Build-Fehler).

Also so sollte es beispielsweise Aussehen:

  • XYZ Module
    • nbproject
    • release
      • modules
        • ext
    • src
    • test

In den ext-Ordner kommen die JAR-Dateien, die benötigt werden.

Das Build-Script kümmert sich nun darum, dass die JAR-Dateien mit dem Projekt in die NBM-Installationsdatei aufgenommen wird. 

Es wird aber noch in der project.xml noch eine Classpath-Referenz benötigt:

In der "Projects-Ansicht" unter "Important Files" die "Project Metadata" öffnen und dann unter den Abschnitt:

<project xmlns="http://www.netbeans.org/ns/project/1">
    <type>org.netbeans.modules.apisupport.project</type>
    <configuration>
        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
            <code-name-base>de.example.xyz</code-name-base>

folgende Classpath-Erweiterung angeben:

            <class-path-extension>
            <runtime-relative-path>ext/3rdparty1.jar</runtime-relative-path>
            <binary-origin>release/modules/ext/3rdparty1
.jar</binary-origin>
            </class-path-extension>
            <class-path-extension>
            <runtime-relative-path>ext/3rdparty2.jar</runtime-relative-path>
            <binary-origin>release/modules/ext/3rdparty2
.jar</binary-origin>
            </class-path-extension>
            [...]
 

Das genügt, damit die JAR-Dateien im NBM-Modul installiert werden können. Außerdem werden damit in den Projekt-Eigenschaften die Pakete der Bibliotheken auswählbar. Das ist auch zwingend notwendig, weil auch die eigenen Sourcen nur so auf die Klassen der JARs zugreifen können.

Also die Projekteingeschaften öffnen, zu "API Versioning" wechseln und nun die "Public Packages" auswählen, die innerhalb des Moduls benötigt werden.

Das war es auch schon. Noch eine Empfehlung zum Schluss: Die so verwendeten JAR-Dateien sollten ggf. immer um die vollständige Versionnummer ergänzt werden. Also nicht nur 3rdpart.jar verwenden, sondern diese Datei in 3rdparty_1-5-6.jar umbenennen. Alle fremden JAR-Dateien einer Module-Suite werden in den selben ext-Ordner kopiert. Das kann zu Überschreibungen und Versionkonflikten führen.

Beste Grüße,
  Josch.

 

 

 

JNBB - Joschs NetBeans Blog - August 28, 2008 04:20 PM
Source Talk Tage 2008 in Göttingen

Dieses Jahr ist es wieder soweit. Die Source Talk Tage im Mathematischen Institut in Göttingen laufen an. Von Dienstag, 23. September bis Donnerstag, 25. September 2008 gibt es wieder spannende Themen wie eLearning, OpenSolaris und Java.

Es werden einige "bekannte Gesichter" rund um die Blog Scene der NetBeans Community anwesend sein. Roman Strobl (er war schon mal mit Geertjan da) will uns zeigen, was OpenSolaris als Betriebssystem so spannend macht und Anton Epple führt in die RCP Programmierung mit NetBeans 6.5 ein.

Ich selbst werde auch am Mittwoch und Donnerstag rein schauen. Würde mich freuen eine volles Haus vorzufinden :-)

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - August 28, 2008 03:48 PM
PHP - welche IDE wird es werden?

Nun ist es raus, PDT for Eclipse wird auf Dezember verschoben. Zu viele Probleme sind da gewachsen und das PDT soll produktiv noch nicht einsetzbar sein.

Vernünftige PHP-Entwicklungsumgebungen gab es ja bisher noch gar nicht (meine persönliche Meinung). Nur Zend bot etwas an. Einige IDEs boten Integrationen an, die meistens über das Syntax Highlighting nicht hinauskamen und als Höhepunkt gerade mal eine Verknüpfung zur Online Doku boten.

Mit NetBeans erwächst langsam vielleicht ein Licht am Streifen des Horizonts nach der Sonnenfinsternis ;-) - Es mag noch nicht so viele Features geben wie in Eclipse PDT, doch eines kristalisiert sich heraus: Das was geht wirkt robust, einheitlich und sauber in die IDE integriert.

PHP ist nicht meine Primärsprache, aber NetBeans ist inzwischen auch für PHP meine Primär-IDE geworden. Dank an die Entwickler, die das Projekt so voran treiben.


Beste Grüße,
  Josch.

Links: Code templates in PHP, Semantic(!) coloring, FTP support, PHP Interpreter frei wählbar

 

Seapegasus Blog - August 27, 2008 03:23 PM
Continuous Integration Im Kampf Gegen Den Golem

Es ist vielleicht eine vergessene Tatsache, aber wenn bei NetBeans ein Engineer es schafft, trotz aller Tests und Checks die Build zu vermurksen, dann wird derjenige zur Strafe von dem furchterregenden Prager Golem heimgesucht! (Nein, ich weiss nicht wer ihn gerade zu Besuch hat, wir harmlosen Tech Writer machen ja nie nie nie was an der Build kaputt... :-D)

Hat Eure Firma auch so nette Traditionen? Falls Ihr Euch für Eure wachsenden Team-Projekte gerade nach Werkzeugen für automatisierte Builds und automatisierte Tests umseht, dann lege ich Euch diesen Vortrag zum Stichwort Permanente Integration ans Herz:

In München findet am 19. September das erste Continuous Integration Camp statt. Veranstalter ist die NetBeans User Gruppe der JUG München. Mit dem Meeting wollen wir die Möglichkeit zum Erfahrungsaustausch und zu Diskussionen darüber bieten, was möglich und was sinnvoll ist. Weitere Informationen findet Ihr im WIKI der JUGM.

Es gibt einen Vortrag von Toni Epple und eine konkrete Fallstudie. Einfach per Email anmelden und dann am 19.9. (Freitag Nachmittag) ab nach Heimstetten. Gebt dem Golem keine Chance! ;-)

Seapegasus Blog - August 22, 2008 01:54 PM
GlassFish-Vortrag in Berlin

Wie Arun schon auf Englisch angekündigt hat, gibt es im September einen Vortrag über den GlassFish Application Server zusammen mit der Berlin-Brandenburg JUG.

Mittwoch, 3. September
17:30 - ca. 19:30
Zimmer FR5516, Technische Universität Berlin
Franklinstrasse 28/29 (Stadtplan)
10587 Berlin

Bitte meldet Euch vorher via XING an! Dort findet Ihr auch die Beschreibung. Arun hält den Vortrag auf Englisch.

JNBB - Joschs NetBeans Blog - August 08, 2008 07:29 AM
BeanDev: AutoUpdateCenter per Ant-Script updaten - Teil II

Ich hatte vergangenen Monat darüber berichtet, wie man ein UpdateCenter automatisch per Ant-Script über ftp auf den Server pushen kann.

Ich hatte da schon Bedenken geäußert das ftp-Kennwort im Build-Script zu hinterlegen und zeigte die Möglichkeit auf das Kennwort in den private.properties einzupflegen. Damit fließt das Kennwort nicht in die Versionierung und bleibt (so lange lokal kein fremder Zugriff besteht) geheim.

Wenn man das Kennwort gar nicht speichern will, gibt es den input-Task, mit dem das Ant-Script stoppt, ein Abfrage-Diaolg startet und damit das Kennwort setzen kann.

Man braucht nur drei Zeilen dem Build-Script hizuzufügen und das Target distribute-dev bei depends anpassen:

    <target name="-distribute-prompt-for-ftppass" unless="dev.ftp.password">
        <input addproperty="dev.ftp.password" message="Kennwort für das Update-Center:"/>
    </target>

<target name="distribute-dev" depends="-distribute-prompt-for-ftppass" description="Distributes to developers and testers">

Das war es auch schon. Wenn das Kennwort nicht in einer Property-Datei hinterlegt wurde, kommt der Dialog. Bei richtig hinterlegtem Kennwort oder bei korrekter Eingabe wird dann alles notwendige auf den Server kopiert.

Man sollte nur beachten, dass die Kennworteingabe nicht durch ein Password-Field erfolgt. D.h. bei der Eingabe sieht man das Kennwort!

Mit einem eigenen InputHandler wäre das lösbar. Hier findet sich eine Quelle, die einen PasswordInputHandler implementiert hat (Download-Datei ist antinput.zip).

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 07, 2008 05:27 AM
BeanDev: Plattform Übersetzung als Plugin

Wie ich schon in einem älteren Blog-Eintrag geschrieben hatte, ist es möglich eine Programm-Suite mit einem Branding so zu versehen, dass man eine Übersetzung der Plattform hat.

Bei dem Verfahren gibt es einen bedeutenden Nachteil: Nur per ZIP-Distribution wird die Übersetzung installiert. Der Anwender kann nicht darüber entscheiden, ob er diese Sprache haben will und kann das auch nicht zurücknehmen (außer er löscht die JAR-Dateien aus dem ./locale-Ordner.

Tonny Kohar zeigt in seinem Blog, wie man für eigene Projekte ein Plugin erstellen kann, um eine Sprachvariante installierbar zu machen.

Das beschriebene Build-Script ist aber sehr auf das Projekt zugeschnitten und skizziert nur den Aufbau. Ich habe mich mal der Sache angenommen und ein Build-Script erstellt, dass generell für alle Projekte verwendet werden kann. Außerdem halte ich mich an die Suite-Branding Konvention die lokalisierten Bundle.properties im branding-Ordner (und nicht unter src) zu halten. Das vereinfacht die Integration in das bestehende Build-System von NetBeans.

Wie Tonny auch schon schreibt, reicht es, erst ein normales Modul zu erstellen. Ich gebe dem Modul immer den Namen der Suite mit der Endung -l10n.Ggf. kann man aber auch das Sprachkennzeichen verwenden (z.B. deDE), damit man später unterschieldiche Übersetzungen in getrennten Installationen anbieten kann.

In der Files Ansicht des Explorers lege ich dann den branding-Ordner an. Im Gegensatz zur Suite wird der branding-Ordner in einem Module nicht automatisch vom Build-Script erkannt. Suite-Projekte starten ein speziellen Build-Abschnitt, wenn der branding-Ordner existiert.

Unter dem branding-Ordner werden nun die Abschnitte der Übersetzungen für die Module abgelegt. Core weicht da ein wenig vom Standard ab, aber die Modul-Übersetzungen der Plattform heißen vom Ordner immer wie die zu erzeugenden Übersetzungs-JAR-Dateien:

Unter core und modules werden nun für die Platform-Module die Bundle_xx_XX.properties-Dateien, ein paar Helpset-Dateien und Bildchen eingefügt. Ich hatte schon mal was vorbereitet (allerdings ohne _de_DE-Anhängsel an den Bundle-Dateien): Ein ZIP-Archiv findet sich hier.

Im Prinzip sollte es so aussehen:

Jetzt geht es ans Eingemachte. Das Build-Script muss erweitert werden.

Dafür nun Build.xml öffnen (findet sich am schnellsten in der Projektansicht unter Important Files).

Direkt unter dem ersten import geht es los. Zunächst benötigen wir die Eigenschaften aus dem Suite-Projekt, damit wir den Application-Namen bekommen:

    <property file="${suite.dir}/nbproject/project.properties" prefix="prjsuite"/>

Dann erzeugen wir ein kleines Makro, damit die Tipparbeit für die Module nicht ausufert:

    <macrodef name="sub-jar">
      <attribute name="dep"/>
      <attribute name="module" default="modules"/>
      <sequential>
        <jar jarfile="${locale.dir}/@{dep}_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
        </jar>
      </sequential>
    </macrodef>

Nun  hängen wir uns in das Target build-init ein, um zwei Variablen zu füllen:

    <target name="build-init" depends="harness.build-init">
      <property name="locale.dir" value="${basedir}/build/cluster/modules/locale/"/>
      <property name="app.name" value="${prjsuite.app.name}"/>
    </target>

Dabei ist locale.dir das Zielverzeichnis für die Übersetzungen und app.name holt sich einfach den Wert aus den Suite-Properties.

Nun hängen wir uns in das jar-Target, erzeugen das locale.dir und basteln schon für das core.jar die erste Übersetzung. Da kann ich das Makro nicht verwenden, weil der Zielname der Übersetzung anders als die JAR-Datei heißen soll:

   <target name="jar" depends="projectized-common.jar">
        <mkdir dir="${locale.dir}"/>

        <!-- Sonderbehandlung für das core-package -->
        <jar jarfile="${locale.dir}/org-netbeans-core_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/core/core.jar/"/>
        </jar>

Nun folgen die restlichen Übersetzungen aus dem modules-Ordner. Das funktioniert sehr gut mit dem Makro. Hier ein Auszug:

        <!-- Lokalisierung für die modules packages -->
        <sub-jar dep="org-jdesktop-layout"/>
        <sub-jar dep="org-netbeans-api-progress"/>
        <sub-jar dep="org-netbeans-api-visual"/>
        <sub-jar dep="org-netbeans-core-execution"/>

Als letztes in diesem Target kopiere ich die fertigen Archive in das Suite-Projekt. Das ist nicht unbedingt notwendig, erleichtert aber das Erstellen von ZIP-Distributionen mit fertig übersetzten Archiven:

        <!-- Kopieren der JAR-Dateien in das Suite locale Verzeichnis-->
        <copy todir="${cluster}/modules/locale/" >
            <fileset dir="${locale.dir}">
                <exclude name="${module.jar.basename}"/>
            </fileset>   
        </copy>
    </target>

Somit ist zumindest die Erstellung der übersetzten Dateien automatisiert in ein extra Modul implementiert. Aber installierbar ist das noch nicht. Ich verlasse mich da ganz auf die Fähigkeiten von Tonny und übernehme sein erweitertes Target nbm:

    <target name="nbm" depends="projectized-common.nbm">
<property name="nbm-expand" value="nbm-expand"/>

<!-- 1: NBM Datei extrahieren -->
<unjar src="build/${nbm}" dest="build/${nbm-expand}">
<patternset>
<exclude name="META-INF/**"/>
</patternset>
</unjar>

<!-- 2: Übersetzugen in den passenden Ordner packen -->
<copy todir="build/${nbm-expand}/netbeans/modules/locale/" >
<fileset dir="${cluster}/modules/locale/" />
</copy>

<!-- 3: Nun wieder als JAR zusammensetzen -->
<jar jarfile="build/${nbm}" compress="true">
<fileset dir="build/${nbm-expand}" />
</jar>

<!-- 4: Signieren, wenn gewünscht -->
<antcall target="sign-nbm" />
</target>   

Für das optionale Signieren fehlt noch:

    <target name="sign-nbm" if="keystore">
        <signjar jar="build/${nbm}" keystore="${keystore}" storepass="${storepass}" alias="${nbm_alias}" />
    </target>

Und das war es auch schon!

Das Modul kann nun ganz normal im Update-Center publiziert werden. Es können weitere Sprachen hinzugefügt werden. Nur funktioniert es nicht, dass man mehrere Module mit unterschiedlichen Sprachen installieren kann. Dazu müsste man das Makro umprogrammieren und dort ein Lokalisierungskennzeichen (z.B. _deDE) hinzufügen:

        <jar jarfile="${locale.dir}/@{dep}_${app.name}_deDE.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
       </jar>

Vielleicht kann man sich dazu auch ein Symbol in den project.properties anlegen. Man darf nur die Sonderbehandlung von core.jar nicht vergessen!

 

Hier noch mal das komplette Build.xml Script, wo alle Module der NetBeans-Plaform übersetzt werden.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 07, 2008 05:27 AM
BeanDev: Plattform Übersetzung als Plugin

Wie ich schon in einem älteren Blog-Eintrag geschrieben hatte, ist es möglich eine Programm-Suite mit einem Branding so zu versehen, dass man eine Übersetzung der Plattform hat.

Bei dem Verfahren gibt es einen bedeutenden Nachteil: Nur per ZIP-Distribution wird die Übersetzung installiert. Der Anwender kann nicht darüber entscheiden, ob er diese Sprache haben will und kann das auch nicht zurücknehmen (außer er löscht die JAR-Dateien aus dem ./locale-Ordner.

Tonny Kohar zeigt in seinem Blog, wie man für eigene Projekte ein Plugin erstellen kann, um eine Sprachvariante installierbar zu machen.

Das beschriebene Build-Script ist aber sehr auf das Projekt zugeschnitten und skizziert nur den Aufbau. Ich habe mich mal der Sache angenommen und ein Build-Script erstellt, dass generell für alle Projekte verwendet werden kann. Außerdem halte ich mich an die Suite-Branding Konvention die lokalisierten Bundle.properties im branding-Ordner (und nicht unter src) zu halten. Das vereinfacht die Integration in das bestehende Build-System von NetBeans.

Wie Tonny auch schon schreibt, reicht es, erst ein normales Modul zu erstellen. Ich gebe dem Modul immer den Namen der Suite mit der Endung -l10n.Ggf. kann man aber auch das Sprachkennzeichen verwenden (z.B. deDE), damit man später unterschieldiche Übersetzungen in getrennten Installationen anbieten kann.

In der Files Ansicht des Explorers lege ich dann den branding-Ordner an. Im Gegensatz zur Suite wird der branding-Ordner in einem Module nicht automatisch vom Build-Script erkannt. Suite-Projekte starten ein speziellen Build-Abschnitt, wenn der branding-Ordner existiert.

Unter dem branding-Ordner werden nun die Abschnitte der Übersetzungen für die Module abgelegt. Core weicht da ein wenig vom Standard ab, aber die Modul-Übersetzungen der Plattform heißen vom Ordner immer wie die zu erzeugenden Übersetzungs-JAR-Dateien:

Unter core und modules werden nun für die Platform-Module die Bundle_xx_XX.properties-Dateien, ein paar Helpset-Dateien und Bildchen eingefügt. Ich hatte schon mal was vorbereitet (allerdings ohne _de_DE-Anhängsel an den Bundle-Dateien): Ein ZIP-Archiv findet sich hier.

Im Prinzip sollte es so aussehen:

Jetzt geht es ans Eingemachte. Das Build-Script muss erweitert werden.

Dafür nun Build.xml öffnen (findet sich am schnellsten in der Projektansicht unter Important Files).

Direkt unter dem ersten import geht es los. Zunächst benötigen wir die Eigenschaften aus dem Suite-Projekt, damit wir den Application-Namen bekommen:

    <property file="${suite.dir}/nbproject/project.properties" prefix="prjsuite"/>

Dann erzeugen wir ein kleines Makro, damit die Tipparbeit für die Module nicht ausufert:

    <macrodef name="sub-jar">
      <attribute name="dep"/>
      <attribute name="module" default="modules"/>
      <sequential>
        <jar jarfile="${locale.dir}/@{dep}_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
        </jar>
      </sequential>
    </macrodef>

Nun  hängen wir uns in das Target build-init ein, um zwei Variablen zu füllen:

    <target name="build-init" depends="harness.build-init">
      <property name="locale.dir" value="${basedir}/build/cluster/modules/locale/"/>
      <property name="app.name" value="${prjsuite.app.name}"/>
    </target>

Dabei ist locale.dir das Zielverzeichnis für die Übersetzungen und app.name holt sich einfach den Wert aus den Suite-Properties.

Nun hängen wir uns in das jar-Target, erzeugen das locale.dir und basteln schon für das core.jar die erste Übersetzung. Da kann ich das Makro nicht verwenden, weil der Zielname der Übersetzung anders als die JAR-Datei heißen soll:

   <target name="jar" depends="projectized-common.jar">
        <mkdir dir="${locale.dir}"/>

        <!-- Sonderbehandlung für das core-package -->
        <jar jarfile="${locale.dir}/org-netbeans-core_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/core/core.jar/"/>
        </jar>

Nun folgen die restlichen Übersetzungen aus dem modules-Ordner. Das funktioniert sehr gut mit dem Makro. Hier ein Auszug:

        <!-- Lokalisierung für die modules packages -->
        <sub-jar dep="org-jdesktop-layout"/>
        <sub-jar dep="org-netbeans-api-progress"/>
        <sub-jar dep="org-netbeans-api-visual"/>
        <sub-jar dep="org-netbeans-core-execution"/>

Als letztes in diesem Target kopiere ich die fertigen Archive in das Suite-Projekt. Das ist nicht unbedingt notwendig, erleichtert aber das Erstellen von ZIP-Distributionen mit fertig übersetzten Archiven:

        <!-- Kopieren der JAR-Dateien in das Suite locale Verzeichnis-->
        <copy todir="${cluster}/modules/locale/" >
            <fileset dir="${locale.dir}">
                <exclude name="${module.jar.basename}"/>
            </fileset>   
        </copy>
    </target>

Somit ist zumindest die Erstellung der übersetzten Dateien automatisiert in ein extra Modul implementiert. Aber installierbar ist das noch nicht. Ich verlasse mich da ganz auf die Fähigkeiten von Tonny und übernehme sein erweitertes Target nbm:

    <target name="nbm" depends="projectized-common.nbm">
<property name="nbm-expand" value="nbm-expand"/>

<!-- 1: NBM Datei extrahieren -->
<unjar src="build/${nbm}" dest="build/${nbm-expand}">
<patternset>
<exclude name="META-INF/**"/>
</patternset>
</unjar>

<!-- 2: Übersetzugen in den passenden Ordner packen -->
<copy todir="build/${nbm-expand}/netbeans/modules/locale/" >
<fileset dir="${cluster}/modules/locale/" />
</copy>

<!-- 3: Nun wieder als JAR zusammensetzen -->
<jar jarfile="build/${nbm}" compress="true">
<fileset dir="build/${nbm-expand}" />
</jar>

<!-- 4: Signieren, wenn gewünscht -->
<antcall target="sign-nbm" />
</target>   

Für das optionale Signieren fehlt noch:

    <target name="sign-nbm" if="keystore">
        <signjar jar="build/${nbm}" keystore="${keystore}" storepass="${storepass}" alias="${nbm_alias}" />
    </target>

Und das war es auch schon!

Das Modul kann nun ganz normal im Update-Center publiziert werden. Es können weitere Sprachen hinzugefügt werden. Nur funktioniert es nicht, dass man mehrere Module mit unterschiedlichen Sprachen installieren kann. Dazu müsste man das Makro umprogrammieren und dort ein Lokalisierungskennzeichen (z.B. _deDE) hinzufügen:

        <jar jarfile="${locale.dir}/@{dep}_${app.name}_deDE.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
       </jar>

Vielleicht kann man sich dazu auch ein Symbol in den project.properties anlegen. Man darf nur die Sonderbehandlung von core.jar nicht vergessen!

 

Hier noch mal das komplette Build.xml Script, wo alle Module der NetBeans-Plaform übersetzt werden.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 05, 2008 06:44 AM
BeanDev: Access-Warnungen in Wrapper-Bibliotheken deaktiveren

Eingebundene Wrapper-Bibliotheken nutzen teilweise andere JAR-Bibliotheken, die man selbst im Wrapper nicht nutzt und damit erst gar nicht einbindet. 

Die NetBeans-Plattform erkennt aber diese Abhängigkeiten und die fehlende Zugriffsmöglichkeit der Bibliothek im Wrapper.

Im Build-Log erhält man dann beispielsweise folgende Warnungen:

Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PermissionID
Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PolicyEngine
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PermissionID
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PolicyEngine

Wenn diese fehlenden Zugriffsmöglichkeiten bewusst akzeptiert werden sollen und man die Warnungen aus dem Log entfernt haben möchte, muss man die project.properties anpassen.

Zuständig für das Deaktivieren der Warnungen ist die Eigenschaft module.jar.verifylinkageignores und erwartet einen Regulären Ausdruck.

Beispiel (z.B. für das Java Media Framework):

module.jar.verifylinkageignores=(com.(ibm|sun).media..*)|(javax.media.rtp.rtcp.SourceDescription)

Diese Eigenschaft wird übrigens vom Ant-Target "verify-class-linkage" im Build-Script verwendet. Der Task "verifyclasslinkage" nutzt den zugewiesenen Wert als ignore-Liste der Klassennamen.

 

 

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 05, 2008 06:44 AM
BeanDev: Access-Warnungen in Wrapper-Bibliotheken deaktiveren

Eingebundene Wrapper-Bibliotheken nutzen teilweise andere JAR-Bibliotheken, die man selbst im Wrapper nicht nutzt und damit erst gar nicht einbindet. 

Die NetBeans-Plattform erkennt aber diese Abhängigkeiten und die fehlende Zugriffsmöglichkeit der Bibliothek im Wrapper.

Im Build-Log erhält man dann beispielsweise folgende Warnungen:

Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PermissionID
Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PolicyEngine
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PermissionID
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PolicyEngine

Wenn diese fehlenden Zugriffsmöglichkeiten bewusst akzeptiert werden sollen und man die Warnungen aus dem Log entfernt haben möchte, muss man die project.properties anpassen.

Zuständig für das Deaktivieren der Warnungen ist die Eigenschaft module.jar.verifylinkageignores und erwartet einen Regulären Ausdruck.

Beispiel (z.B. für das Java Media Framework):

module.jar.verifylinkageignores=(com.(ibm|sun).media..*)|(javax.media.rtp.rtcp.SourceDescription)

Diese Eigenschaft wird übrigens vom Ant-Target "verify-class-linkage" im Build-Script verwendet. Der Task "verifyclasslinkage" nutzt den zugewiesenen Wert als ignore-Liste der Klassennamen.

 

 

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 31, 2008 09:17 AM
BeanDev: AutoUpdate Dateien automatisch kopieren

Das Build System mit Ant ist so vielfältig zu konfigurieren, da klappt auch ein FTP-Upload der AutoUpdate-Dateien auf den eigenen Server.


Zunächst muss man die Apache Commons Net in den Ant-Classpath hinzufügen. Das geht seit NetBeans 6.0 sehr leicht über den Options-Dialog. Die Binary-Versions von commons-net extrahieren und in den gewünschten Pfad kopieren.

Nun in NetBeans den Options-Dialog öffnen, auf Miscellaneous wechseln und in der Ant-Registerzunge dem Classpath die JAR-Datei hinzufügen.

Von nun an hat man die zusätzlichen Tasks, die in commons-net-x.x.x.jar definiert sind. Ein Task davon ist ftp.

Nun das Build-Script der gewünschten NetBeans Application Platform öffnen (findet sich unter Important Files) und folgendes Target hinzufügen:

    <target name="distribute-dev" description="Distri to developers">
      <tstamp/>
      <ftp
          server="ftp.myserver.de"
          userid="thebuilduser"
          password="theuserpwd"
          remotedir="/updates/dev/myapp"
          verbose="true"
          passive="true">
           
        <fileset dir="build/updates" casesensitive="yes">
          <include name="**/*.nbm"/>
          <include name="**/updates.xml"/>
        </fileset>
     
      </ftp>
    </target>

Da das Build-Script aber auch versioniert wird und es ggf. nicht erwünscht ist, dass irgendwo in einem Repository das Kennwort rumschwirrt, sollte man eine Konstante in den private properties dafür anlegen. Diese Eigenschaften findet man in der Files-Ansicht des Projektes unter nbproject/private/private.properties. Dort einfach folgende Zeile hinzufügen:

dev.ftp.password=myv3rys3cr3tpassw0rd

und das Target des Ant-Scriptes anpassen:

          password="${dev.ftp.password}"

(Übrigens hilft hier das Autovervollständigen der NetBeans IDE)

Jetzt muss man zunächst im Kontext-Menü der Application "Create NBMs" aufrufen, damit alle Dateien im build/updates Ordner erzeugt werden.

Nun kann man mit der rechten Maustaste in der Projects-Ansicht auf das Build-Script klicken und Run Target -> distribute-dev aufrufen.

Wenn alle Daten passen, werden die Dateien auf den ftp-Server kopiert.

Möchte man sich den Schritt mit dem Menüaufruf von "Create NBMs" sparen, trägt man in das Target einfach die Abhängigkeit zu dem nbms-Target ein:

    <target name="distribute-dev" depends="nbms" description="Distri to developers">

Somit erzeugt distribute-dev zunächst immer die aktuellen Updater-Dateien und kopiert sie dann auf den Server.

Das war es auch schon und beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 31, 2008 09:17 AM
BeanDev: AutoUpdate Dateien automatisch kopieren

Das Build System mit Ant ist so vielfältig zu konfigurieren, da klappt auch ein FTP-Upload der AutoUpdate-Dateien auf den eigenen Server.


Zunächst muss man die Apache Commons Net in den Ant-Classpath hinzufügen. Das geht seit NetBeans 6.0 sehr leicht über den Options-Dialog. Die Binary-Versions von commons-net extrahieren und in den gewünschten Pfad kopieren.

Nun in NetBeans den Options-Dialog öffnen, auf Miscellaneous wechseln und in der Ant-Registerzunge dem Classpath die JAR-Datei hinzufügen.

Von nun an hat man die zusätzlichen Tasks, die in commons-net-x.x.x.jar definiert sind. Ein Task davon ist ftp.

Nun das Build-Script der gewünschten NetBeans Application Platform öffnen (findet sich unter Important Files) und folgendes Target hinzufügen:

    <target name="distribute-dev" description="Distri to developers">
      <tstamp/>
      <ftp
          server="ftp.myserver.de"
          userid="thebuilduser"
          password="theuserpwd"
          remotedir="/updates/dev/myapp"
          verbose="true"
          passive="true">
           
        <fileset dir="build/updates" casesensitive="yes">
          <include name="**/*.nbm"/>
          <include name="**/updates.xml"/>
        </fileset>
     
      </ftp>
    </target>

Da das Build-Script aber auch versioniert wird und es ggf. nicht erwünscht ist, dass irgendwo in einem Repository das Kennwort rumschwirrt, sollte man eine Konstante in den private properties dafür anlegen. Diese Eigenschaften findet man in der Files-Ansicht des Projektes unter nbproject/private/private.properties. Dort einfach folgende Zeile hinzufügen:

dev.ftp.password=myv3rys3cr3tpassw0rd

und das Target des Ant-Scriptes anpassen:

          password="${dev.ftp.password}"

(Übrigens hilft hier das Autovervollständigen der NetBeans IDE)

Jetzt muss man zunächst im Kontext-Menü der Application "Create NBMs" aufrufen, damit alle Dateien im build/updates Ordner erzeugt werden.

Nun kann man mit der rechten Maustaste in der Projects-Ansicht auf das Build-Script klicken und Run Target -> distribute-dev aufrufen.

Wenn alle Daten passen, werden die Dateien auf den ftp-Server kopiert.

Möchte man sich den Schritt mit dem Menüaufruf von "Create NBMs" sparen, trägt man in das Target einfach die Abhängigkeit zu dem nbms-Target ein:

    <target name="distribute-dev" depends="nbms" description="Distri to developers">

Somit erzeugt distribute-dev zunächst immer die aktuellen Updater-Dateien und kopiert sie dann auf den Server.

Das war es auch schon und beste Grüße,
  Josch.

OlliN's » netbeans - July 27, 2008 07:48 PM
Apropos Profiler

Gestern habe ich kurz den Netbeans-Profiler gezeigt. Der ist übrigens einfach so bei der Installation von Netbeans mit dabei und funktioniert tadellos. Vor einer Zeit habe ich mal versucht den Profiler von Eclipse (TPTP) zum Laufen zu bringen. Ich habe es nach einiger Zeit geschafft auf meiner Gentoo-Linux-Büchse eine Applikation nach Memory-Leaks zu untersuchen. Aber es ist schon ein Unterschied zwischen “funktioniert einfach” bei Netbeans und “Lese mal Installationsanleitung und probiere eine Weile bis es läuft” bei Eclipse.

Auch den Subversion-Client von Eclipse wird standardmäßig nicht installiert. Und auf der Website von Eclipse ist derzeit (Version 0.7.1) aus Lizenzgründen nicht mal ein vollständiger Client vorhanden. Man muss noch auf eine andere Seite um sich von dort noch Teile des Clients zu holen. Ist zwar alles dokumentiert, doch auch hier ist es bei Netbeans via Pluginmanager ganz einfach den Client zu installieren.

Gerade für Neueinsteiger ist also Netbeans besser geeignet. Aber auch die “alten” Eclipse-Hasen sollten mal über den Tellerrand in Richtung Netbeans schauen… da läuft Einiges (siehe Scripting) und zum Teil besser (siehe GUI-Builder).

Ach, wenn Netbeans doch nur so viele schöne Einstellungen für die Codeformatierungen hätte wie Eclipse… dann wäre ich überglücklich. Wenn man nämlich wie ich in einem Projekt arbeitet, welches die automatische Formatierung beim Speichern einer Datei vorschreibt und auch noch viele spezifische Formatierungsoptionen von Eclipse benutzt, dann ärgert man sich ab und zu wenn man mit Netbeans die eine oder andere Formatierung noch per Hand machen muss.

Seapegasus Blog - July 23, 2008 04:43 PM
EJB-Artikel im JavaSpektrum

Auf ans Kiosk: NetBeans-Dream-Team-Mitglied Adam Bien hat einen weiteren berühmt-berüchtigten EJB 3-Artikel geschrieben, diesmal bekommt Ihr ihn im JavaSpektrum-Magazin zu Gesicht.

Eine Zusammenfassung findet Ihr hier online; für den vollen Inhalt (inklusive NetBeans-Bildschirmphotos) kauft Ihr das Magazin — oder Ihr ladet für zwoenhalb Euro das PDF runter.

<WÄÄBUNG>Unterstützt einen Entwickler, kauft ein PDF!</WÄÄBUNG> :-D (Adam, kriegst Du da was von?)

Dass ich so lange nicht geblogt habe, lag übrigens daran dass ich "kurz" in Island war. :) Und danach hat sich der D&D-Floh in mein Ohr gesetzt. Also, d.h. konkret, ich habe mich überreden lassen, es demnächst zumindest mal zu probieren. Ich hatte bisher erst einmal eine Uraltversion vom Schwarzen Auge gespielt (mein armer Typie befindet sich immer noch im Limbo in irgendeinem aventurischen Wald, umzingelt von Orks), und hab mir noch keine endgültige Meinung über modernere Editionen gebildet. Mal sehen! Jetzt erst mal eine suuupi-Hintergrundstory erfinden. Es muss etwas besseres geben als "ich bin Vollwaise, alle hassen mich, und ein Ork hat meine Hausaufgaben gefressen"...! ;-)

JNBB - Joschs NetBeans Blog - July 18, 2008 07:05 AM
BeanDev: Vorbelegen von Optionseigenschaften des Autoupdater

Es ist nur eine Kleinigkeit, aber mit der Umstellung in NetBeans 6.0 von der Options-API auf die Preferences-API kann man relativ einfach auch Einstellungen fremder Module beeinflussen.

So brauche ich für meine Plattform-Entwicklungen kürzere Prüfungsintervalle für das Autoupdate. Als Standard ist "Wöchentlich" vorgegeben. Sinnvoller für Anwendungen im Kundenbereich ist aber eine tägliche Prüfung auf Updates.

Folgender 5-Zeiler im Module-Installer der restored() - Methode, löst das Problem, ohne ggf. vom Benutzer eingestellte Werte zu überschreiben

    final String AU = "/org/netbeans/modules/autoupdate";
    final Preferences root = NbPreferences.root();
    if ( root.node (AU).get ("period", null) == null)  {
      root.node (AU).put ("period", "1");
    }

Dabei sind folgende Werte im Quelltext des Autoupdater definiert:

    public static final int EVERY_STARTUP = 0;
    public static final int EVERY_DAY = 1;
    public static final int EVERY_WEEK = 2;
    public static final int EVERY_2WEEKS = 3;
    public static final int EVERY_MONTH = 4;
    public static final int NEVER = 5;

Wenn man schon auf NetBeans 6.1 entwickelt, gibt es noch eine zusätzliche Möglichkeit den Standardwert des Autoupdaters vorzugeben. Man kann das Intervall als System-Eigenschaft dem NetBeans-Launcher mitgeben. Entweder direkt auf der Kommandozeile oder in etc/app.conf (im harness-Ordner):

 

Täglich auf Updates testen:

-J-Dplugin.manager.check.interval=EVERY_DAY 

Die Konstanten sind (wie oben) EVERY_STARTUP, EVERY_DAY, EVERY_WEEK, EVERY_2WEEKS, EVERY_MONTH oder NEVER. Alternativ kann man auch die Minutenzahl angeben (was im Optionsdialog nicht funktioniert). Alle halbe Stunde auf Updates testen:

-J-Dplugin.manager.check.interval=30 

Wenn man nicht nur auf Updates, sondern auch auf neue Plugins testen möchte, muss man folgende Option setzen (seit NetBeans 6.0):

-J-Dplugin.manager.check.new.plugins=true 

Dann erscheint der Balloon-Tooltip auch dann, wenn neue Plugins im Update-Center auftauchen, die noch nicht installiert wurden. Das lässt sich natürlich auch im Module-Installer festlegen (was aber nicht zuverlässig funktionieren muss, wenn das Intervall EVERY_STARTUP ist):

 

public void restored() {
  [...]
  System.getProperties().put ("plugin.manager.check.new.plugins", "true");
}

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 18, 2008 07:05 AM
BeanDev: Vorbelegen von Optionseigenschaften des Autoupdater

Es ist nur eine Kleinigkeit, aber mit der Umstellung in NetBeans 6.0 von der Options-API auf die Preferences-API kann man relativ einfach auch Einstellungen fremder Module beeinflussen.

So brauche ich für meine Plattform-Entwicklungen kürzere Prüfungsintervalle für das Autoupdate. Als Standard ist "Wöchentlich" vorgegeben. Sinnvoller für Anwendungen im Kundenbereich ist aber eine tägliche Prüfung auf Updates.

Folgender 5-Zeiler im Module-Installer der restored() - Methode, löst das Problem, ohne ggf. vom Benutzer eingestellte Werte zu überschreiben

    final String AU = "/org/netbeans/modules/autoupdate";
    final Preferences root = NbPreferences.root();
    if ( root.node (AU).get ("period", null) == null)  {
      root.node (AU).put ("period", "1");
    }

Dabei sind folgende Werte im Quelltext des Autoupdater definiert:

    public static final int EVERY_STARTUP = 0;
    public static final int EVERY_DAY = 1;
    public static final int EVERY_WEEK = 2;
    public static final int EVERY_2WEEKS = 3;
    public static final int EVERY_MONTH = 4;
    public static final int NEVER = 5;

Wenn man schon auf NetBeans 6.1 entwickelt, gibt es noch eine zusätzliche Möglichkeit den Standardwert des Autoupdaters vorzugeben. Man kann das Intervall als System-Eigenschaft dem NetBeans-Launcher mitgeben. Entweder direkt auf der Kommandozeile oder in etc/app.conf (im harness-Ordner):

 

Täglich auf Updates testen:

-J-Dplugin.manager.check.interval=EVERY_DAY 

Die Konstanten sind (wie oben) EVERY_STARTUP, EVERY_DAY, EVERY_WEEK, EVERY_2WEEKS, EVERY_MONTH oder NEVER. Alternativ kann man auch die Minutenzahl angeben (was im Optionsdialog nicht funktioniert). Alle halbe Stunde auf Updates testen:

-J-Dplugin.manager.check.interval=30 

Wenn man nicht nur auf Updates, sondern auch auf neue Plugins testen möchte, muss man folgende Option setzen (seit NetBeans 6.0):

-J-Dplugin.manager.check.new.plugins=true 

Dann erscheint der Balloon-Tooltip auch dann, wenn neue Plugins im Update-Center auftauchen, die noch nicht installiert wurden. Das lässt sich natürlich auch im Module-Installer festlegen (was aber nicht zuverlässig funktionieren muss, wenn das Intervall EVERY_STARTUP ist):

 

public void restored() {
  [...]
  System.getProperties().put ("plugin.manager.check.new.plugins", "true");
}

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 07, 2008 02:54 PM
BeanDev: Neustart der NetBeans-Platform

Leider gibt es laut Mailing-Liste keine offizielle Möglichkeit die NetBeans-Plattform aus dem Programm heraus neu zu starten.

Also muss man sich behelfen und die Sache "zu Fuß" durchziehen.

Am besten erzeugt man sich mit dem Assistenten eine CallableSystemAction mit dem Namen "SystemRestartAction", setzt sie unter die Kategorie "System", und fügt sie als Global Menu Item im Menü "File" hinter den Menüeintrag "Exit" ein.

Wenn man noch ein schönes Icon hat, kann man es noch im Assistenten hinterlegen, nur ein Tastenkürzel würde ich nicht vergeben.

Der Assistent erzeugt nun eine Java-Klasse mit dem Namen (wie sollte es anders sein) SystemRestartAction. Nur die performAction-Methode muss noch implementiert werden:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.openide.LifecycleManager;
import org.openide.util.Exceptions;
  [...]
  public void performAction() {
    String systemName = NbBundle.getMessage(SystemRestartAction.class, "CTL_SystemFileName");
    String homePath = System.getProperty("netbeans.home", ".");
    File base = new File(homePath).getParentFile();
    File bin = new File(base, "bin");
    final File execution = new File(bin, systemName);
    if ( execution.exists() ) {
      Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
          try {
            new ProcessBuilder(execution.getAbsolutePath()).start();
          } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
          }
        }
      }));
      LifecycleManager.getDefault().exit();
    } else {
      Exceptions.printStackTrace(new FileNotFoundException ("Can't find the launcher " + execution.getAbsolutePath()));
    }
  }

Der Bundle.properties-Wert "CTL_SystemFileName" muss den Namen der ausführbaren Launcher-Datei tragen. Unter Windows und für NetBeans wäre das netbeans.exe. Damit sieht man schon die wichtigste Einschränkung dieser Methode: sie ist abhängig von dem installierten Betriebssystem. Das zweite (nicht so gravierende) Problem ist, dass man wissen muss wie der Launcher heißt.

Die OS-Abhängigkeit kann man mildern, wenn man per org.openide.util.Utilities die Betriebssysteme abfragt. Die Methoden wären isMac(), isWindows() und isUnix(). Wobei man isUnix und isMac zusammenfassen kann, da der Launcher für die beiden Systeme identisch ist.

Damit könnte man folgende Abfrage realisieren:

 String systemName = 
NbBundle.getMessage (SystemRestartAction.class,
Utilities.isWindows()
? "CTL_SystemFileName.Windows"
: "CTL_SystemFileName.MacOrUnix");

In der Bundle.properties steht dann für die NetBeans-IDE:

CTL_SystemFileName.Windows=netbeans.exe
CTL_SystemFileName.MacOrUnix=netbeans

Für die eigene Plattform-Anwendung muss man dann natürlich den eigenen Applikationsnamen aus der "Standalone Application" nehmen. Das ist in den Projekt-Eigenschaften der Branding-Name im Abschnitt "Build".

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - July 07, 2008 02:54 PM
BeanDev: Neustart der NetBeans-Platform

Leider gibt es laut Mailing-Liste keine offizielle Möglichkeit die NetBeans-Plattform aus dem Programm heraus neu zu starten.

Also muss man sich behelfen und die Sache "zu Fuß" durchziehen.

Am besten erzeugt man sich mit dem Assistenten eine CallableSystemAction mit dem Namen "SystemRestartAction", setzt sie unter die Kategorie "System", und fügt sie als Global Menu Item im Menü "File" hinter den Menüeintrag "Exit" ein.

Wenn man noch ein schönes Icon hat, kann man es noch im Assistenten hinterlegen, nur ein Tastenkürzel würde ich nicht vergeben.

Der Assistent erzeugt nun eine Java-Klasse mit dem Namen (wie sollte es anders sein) SystemRestartAction. Nur die performAction-Methode muss noch implementiert werden:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.openide.LifecycleManager;
import org.openide.util.Exceptions;
  [...]
  public void performAction() {
    String systemName = NbBundle.getMessage(SystemRestartAction.class, "CTL_SystemFileName");
    String homePath = System.getProperty("netbeans.home", ".");
    File base = new File(homePath).getParentFile();
    File bin = new File(base, "bin");
    final File execution = new File(bin, systemName);
    if ( execution.exists() ) {
      Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
          try {
            new ProcessBuilder(execution.getAbsolutePath()).start();
          } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
          }
        }
      }));
      LifecycleManager.getDefault().exit();
    } else {
      Exceptions.printStackTrace(new FileNotFoundException ("Can't find the launcher " + execution.getAbsolutePath()));
    }
  }

Der Bundle.properties-Wert "CTL_SystemFileName" muss den Namen der ausführbaren Launcher-Datei tragen. Unter Windows und für NetBeans wäre das netbeans.exe. Damit sieht man schon die wichtigste Einschränkung dieser Methode: sie ist abhängig von dem installierten Betriebssystem. Das zweite (nicht so gravierende) Problem ist, dass man wissen muss wie der Launcher heißt.

Die OS-Abhängigkeit kann man mildern, wenn man per org.openide.util.Utilities die Betriebssysteme abfragt. Die Methoden wären isMac(), isWindows() und isUnix(). Wobei man isUnix und isMac zusammenfassen kann, da der Launcher für die beiden Systeme identisch ist.

Damit könnte man folgende Abfrage realisieren:

 String systemName = 
NbBundle.getMessage (SystemRestartAction.class,
Utilities.isWindows()
? "CTL_SystemFileName.Windows"
: "CTL_SystemFileName.MacOrUnix");

In der Bundle.properties steht dann für die NetBeans-IDE:

CTL_SystemFileName.Windows=netbeans.exe
CTL_SystemFileName.MacOrUnix=netbeans

Für die eigene Plattform-Anwendung muss man dann natürlich den eigenen Applikationsnamen aus der "Standalone Application" nehmen. Das ist in den Projekt-Eigenschaften der Branding-Name im Abschnitt "Build".

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - July 02, 2008 07:23 AM
BeanDev: Branding - Shortcuts oder Keymap?

Der deklarative Ansatz, die NetBeans-Plattform zu konfigurieren ist sehr praktisch, hat aber auch so seine Tücken. Der Vorteil ist unbestreitbar. Ohne die compilierten Class-Dateien anfassen zu müssen, kann man in XML- und Bundle-Dateien alle "Branding"-Arbeiten durchführen. Allerdings gibt es inzwischen recht verstreute Einrichtungsmöglichkeiten und sind teilweise auch als historisch zu bezeichnen.

Ein Bereich sind die Tastenkürzel, die man Aktionen der NetBeans-Oberfläche zuweisen kann. Ein populärer Bereich ist, in der layer.xml, der Shortcuts-Folder.

Zunächst benötigt man natürlich eine Action-Klasse, die selbst im Actions-Abschnitt deklariert sein sollte:

<filesystem>
    <folder name="Actions">
        <folder name="Window">
            <file name="de-sepix-demo-QueryAction.instance"/>
        </folder>
    </folder>

Nun kann man im Shortcuts-Folder ein Tastenkürzel zuweisen:

    <folder name="Shortcuts">
        <file name="CS-Q.instance­">
            <attr name="instanceClass­"
                stringvalue="de.sepix.demo.QueryAction"/>
        </file>
    </folder>

Somit ist Strg+Umschalt+Q der Aktion "QueryAction" zugewiesen. Die eingentümliche Syntax für das Tastenkürzel wird in Utilities.stringToKey beschrieben.

Allerdings hat dieses Verfahren einige Nachteile. Das Tastenkürzel ist global einmalig zugewiesen (kann also nicht Tastatur-Profilen zugewiesen werden) und wird für den Anwender nicht sichtbar im Menü angezeigt. 

Wenn man nämlich die QueryAction (hier z.B.) im Window-Menü einfügt:

    <folder name="Menu">
        <folder name="Window">
            <file name="QueryAction.shadow">
                <attr name="originalFile"
                    stringvalue=
                        "Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

erscheint zwar der Menüeintrag, aber ohne die Tastenkürzel-Information, dass der Anwender die Eintrag auch per Strg+Umschalt+Q aufrufen kann.

Eine schönere Methode ist es, dem Anwender die Tastenkürzel über Keymaps zur Verfügung zu stellen. Damit kann der Nutzer die Tastenkürzel bequem im Options-Dialog verwalten und sieht diese auch immer in den Menüs zugewiesen:

    <folder name="Keymaps">
        <folder name="Sepix">
            <file name="CS-Q.shadow">
                <attr name="originalFile"
               stringvalue="Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

          
Nun erscheint automatisch im Options-Dialog zusätzlich zur NetBeans-Keymap auch die Sepix-Keymap.

Aber der Anwender muss erst explizit die Keymap "Sepix" auswählen. Aber auch das kann man vorgeben:

    <folder name="Keymaps">
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit dem Attribut "currentKeymap" wird die Standard-Keymap vorbelegt.

Will man beim Brandig die NetBeans-Keymap vor dem Anwender verstecken, nutzt man dafür das Standard-Verfahren:

    <folder name="Keymaps">
<folder name="NetBeans_hidden"/>
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit diesem Verfahren kann man für den Anwender bequem zu verwaltende Tastaturkürzel einrichten.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - July 02, 2008 07:23 AM
BeanDev: Branding - Shortcuts oder Keymap?

Der deklarative Ansatz, die NetBeans-Plattform zu konfigurieren ist sehr praktisch, hat aber auch so seine Tücken. Der Vorteil ist unbestreitbar. Ohne die compilierten Class-Dateien anfassen zu müssen, kann man in XML- und Bundle-Dateien alle "Branding"-Arbeiten durchführen. Allerdings gibt es inzwischen recht verstreute Einrichtungsmöglichkeiten und sind teilweise auch als historisch zu bezeichnen.

Ein Bereich sind die Tastenkürzel, die man Aktionen der NetBeans-Oberfläche zuweisen kann. Ein populärer Bereich ist, in der layer.xml, der Shortcuts-Folder.

Zunächst benötigt man natürlich eine Action-Klasse, die selbst im Actions-Abschnitt deklariert sein sollte:

<filesystem>
    <folder name="Actions">
        <folder name="Window">
            <file name="de-sepix-demo-QueryAction.instance"/>
        </folder>
    </folder>

Nun kann man im Shortcuts-Folder ein Tastenkürzel zuweisen:

    <folder name="Shortcuts">
        <file name="CS-Q.instance­">
            <attr name="instanceClass­"
                stringvalue="de.sepix.demo.QueryAction"/>
        </file>
    </folder>

Somit ist Strg+Umschalt+Q der Aktion "QueryAction" zugewiesen. Die eingentümliche Syntax für das Tastenkürzel wird in Utilities.stringToKey beschrieben.

Allerdings hat dieses Verfahren einige Nachteile. Das Tastenkürzel ist global einmalig zugewiesen (kann also nicht Tastatur-Profilen zugewiesen werden) und wird für den Anwender nicht sichtbar im Menü angezeigt. 

Wenn man nämlich die QueryAction (hier z.B.) im Window-Menü einfügt:

    <folder name="Menu">
        <folder name="Window">
            <file name="QueryAction.shadow">
                <attr name="originalFile"
                    stringvalue=
                        "Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

erscheint zwar der Menüeintrag, aber ohne die Tastenkürzel-Information, dass der Anwender die Eintrag auch per Strg+Umschalt+Q aufrufen kann.

Eine schönere Methode ist es, dem Anwender die Tastenkürzel über Keymaps zur Verfügung zu stellen. Damit kann der Nutzer die Tastenkürzel bequem im Options-Dialog verwalten und sieht diese auch immer in den Menüs zugewiesen:

    <folder name="Keymaps">
        <folder name="Sepix">
            <file name="CS-Q.shadow">
                <attr name="originalFile"
               stringvalue="Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

          
Nun erscheint automatisch im Options-Dialog zusätzlich zur NetBeans-Keymap auch die Sepix-Keymap.

Aber der Anwender muss erst explizit die Keymap "Sepix" auswählen. Aber auch das kann man vorgeben:

    <folder name="Keymaps">
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit dem Attribut "currentKeymap" wird die Standard-Keymap vorbelegt.

Will man beim Brandig die NetBeans-Keymap vor dem Anwender verstecken, nutzt man dafür das Standard-Verfahren:

    <folder name="Keymaps">
<folder name="NetBeans_hidden"/>
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit diesem Verfahren kann man für den Anwender bequem zu verwaltende Tastaturkürzel einrichten.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - June 27, 2008 06:05 AM
BeanDev: NodeAction und Selection Management

Eine nette Klasse, um Nodes mit Aktionen zu versehen ist die NodeAction-Klasse. Und soweit man sich an die Standard-Tutorials hält, wird man erstmal keine Probleme haben - insbesondere wenn sich alles in einem View innerhalb TopComponent läuft.

NodeAction funktioniert aber nur, wenn das zugeordnete Modell des ExplorerManager mit dem Selection Management der NetBeans Plattform verknüpft ist. Dabei ist es letztendlich irrelevant, ob z.B. ein BeanTreeView-Eintrag visuell markiert ist. Wenn der ExplorerManager von seiner Umwelt abgeschnitten ist, klappt es nicht mit einer NodeAction.

Sehr schnell bekommt man das mit, wenn man ein ExplorerManager und zugehöriges View in einer schlichten Panel-Komponente hat. Zwar funktioniert alles erstmal wie erwartet, sogar NodeActions die per

  @Override
  public Action[] getActions(boolean context) {
    return new Action[] {myNodeAction}
  }

an einen Knoten gebunden werden, zeigen sich im Popup, sind aktiv und werden sogar ausgeführt.

Leider stellt man dann im Debugger fest, dass das übergebene Node-Array in der performAction-Methode immer leer ist.

    @Override
    protected void performAction(Node[] nodes) {
      for (Node node : nodes) {
        if ( node.canDestroy() ) {
          Node parent = node.getParentNode();
          parent.getChildren().remove(new Node[]{node});
        }
      }
    }

Obige performAction-Methode würde nie etwas ausführen.

Wie bekommt man aber nun NodeAction dazu, zu erkennen, welche Knoten im ExplorerManager ausgewählt wurden?

Hier eine Möglichkeit:

Das Panel sollte das Interface Lookup.Provider implementieren und, ähnlich wie in den Tutorials zu TopComponents, ein Lookup (generiert aus einem ExplorerManager) zurückgeben:

public class MyTestPanel extends JPanel 
          implements ExplorerManager.Provider, Loopup.Provider {
  private ExplorerManager myNodeModel = new ExplorerManager();
  private BeanTreeView tree = new BeanTreeView();
  private Lookup lookup;
  public MyTestPanel () {
    initComponents();
    myNodeModel = new ExplorerManager();
    lookup = ExplorerUtils.createLookup(explorerManager, new ActionMap());
    myNodeModel .setRootContext(new MyRootNode());
  }

  public ExplorerManager getExplorerManager() {
    return myNodeModel;
  }

  public Lookup getLookup() {
    return lookup;
  }
  [...]
}

Das BeanTreeView benötigt das Interface ExplorerManager.Provider, ansonsten hat das View kein Model. Die Implementation von Lookup.Provider nutzen wir nun in der TopComponent, in der wir unser TestPanel einfügen:

public class TestTopComponent extends TopComponent {
  TestPanel tp = new TestPanel();
  public TestTopComponent() {
    add (tp);
    associateLookup(tp.getLookup()));
  }
}

Das war es auch schon, der Rest läuft automatisch. Die Methode associateLookup reicht das erzeugte Lookup aus unserem Panel durch die getLookup-Methode von TestTopComponent heraus (getLookup ist schon in TopComponent implementiert). 

Es wird aber die Regel sein, dass TopComponents eigene Lookups benötigen oder gar mehrere Panels mit Lookups nutzen. Dabei hilft aber ProxyLookup:

public class TestTopComponent extends TopComponent {
TestPanel tp = new TestPanel();
  InstanceContent content = new InstanceContent();
  public TestTopComponent() {
    add (tp);
    associateLookup(
      new ProxyLookup (
        new AbstractLookup (instanceContent),
        tp.getLookup())
    );
  }
}

Jetzt kann man selber in content seine Objekte speichern, die man global zur Verfügung stellen will und verliert nicht die Verbindung zu den selektierten Nodes in TestPanel.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - June 27, 2008 06:05 AM
BeanDev: NodeAction und Selection Management

Eine nette Klasse, um Nodes mit Aktionen zu versehen ist die NodeAction-Klasse. Und soweit man sich an die Standard-Tutorials hält, wird man erstmal keine Probleme haben - insbesondere wenn sich alles in einem View innerhalb TopComponent läuft.

NodeAction funktioniert aber nur, wenn das zugeordnete Modell des ExplorerManager mit dem Selection Management der NetBeans Plattform verknüpft ist. Dabei ist es letztendlich irrelevant, ob z.B. ein BeanTreeView-Eintrag visuell markiert ist. Wenn der ExplorerManager von seiner Umwelt abgeschnitten ist, klappt es nicht mit einer NodeAction.

Sehr schnell bekommt man das mit, wenn man ein ExplorerManager und zugehöriges View in einer schlichten Panel-Komponente hat. Zwar funktioniert alles erstmal wie erwartet, sogar NodeActions die per

  @Override
  public Action[] getActions(boolean context) {
    return new Action[] {myNodeAction}
  }

an einen Knoten gebunden werden, zeigen sich im Popup, sind aktiv und werden sogar ausgeführt.

Leider stellt man dann im Debugger fest, dass das übergebene Node-Array in der performAction-Methode immer leer ist.

    @Override
    protected void performAction(Node[] nodes) {
      for (Node node : nodes) {
        if ( node.canDestroy() ) {
          Node parent = node.getParentNode();
          parent.getChildren().remove(new Node[]{node});
        }
      }
    }

Obige performAction-Methode würde nie etwas ausführen.

Wie bekommt man aber nun NodeAction dazu, zu erkennen, welche Knoten im ExplorerManager ausgewählt wurden?

Hier eine Möglichkeit:

Das Panel sollte das Interface Lookup.Provider implementieren und, ähnlich wie in den Tutorials zu TopComponents, ein Lookup (generiert aus einem ExplorerManager) zurückgeben:

public class MyTestPanel extends JPanel 
          implements ExplorerManager.Provider, Loopup.Provider {
  private ExplorerManager myNodeModel = new ExplorerManager();
  private BeanTreeView tree = new BeanTreeView();
  private Lookup lookup;
  public MyTestPanel () {
    initComponents();
    myNodeModel = new ExplorerManager();
    lookup = ExplorerUtils.createLookup(explorerManager, new ActionMap());
    myNodeModel .setRootContext(new MyRootNode());
  }

  public ExplorerManager getExplorerManager() {
    return myNodeModel;
  }

  public Lookup getLookup() {
    return lookup;
  }
  [...]
}

Das BeanTreeView benötigt das Interface ExplorerManager.Provider, ansonsten hat das View kein Model. Die Implementation von Lookup.Provider nutzen wir nun in der TopComponent, in der wir unser TestPanel einfügen:

public class TestTopComponent extends TopComponent {
  TestPanel tp = new TestPanel();
  public TestTopComponent() {
    add (tp);
    associateLookup(tp.getLookup()));
  }
}

Das war es auch schon, der Rest läuft automatisch. Die Methode associateLookup reicht das erzeugte Lookup aus unserem Panel durch die getLookup-Methode von TestTopComponent heraus (getLookup ist schon in TopComponent implementiert). 

Es wird aber die Regel sein, dass TopComponents eigene Lookups benötigen oder gar mehrere Panels mit Lookups nutzen. Dabei hilft aber ProxyLookup:

public class TestTopComponent extends TopComponent {
TestPanel tp = new TestPanel();
  InstanceContent content = new InstanceContent();
  public TestTopComponent() {
    add (tp);
    associateLookup(
      new ProxyLookup (
        new AbstractLookup (instanceContent),
        tp.getLookup())
    );
  }
}

Jetzt kann man selber in content seine Objekte speichern, die man global zur Verfügung stellen will und verliert nicht die Verbindung zu den selektierten Nodes in TestPanel.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - June 26, 2008 08:45 AM
BeanDev: Minimaler Einsatz für Drag and Drop von Nodes

Es funktioniert fast überall in den aktuellen Versionen der NetBeans IDE: Man kann in den Baumansichten die Elemente anklicken und mit gehaltener linker Maustaste an eine andere Position verschieben.

Wenn man das auch für die eigenen BeanTreeView-Komponenten haben will, muss man ein paar Methoden implementieren bzw. überschreiben, damit das funktioniert.

In diesem "Kochrezept" habe ich ein sehr einfaches Modell gewählt, nur einen Root-Node mit einer einfachen Hierachie. Der Root-Node kann sogar ausgeblendet werden.

Man erzeugt ein JPanel oder TopComponent und implementiert  ExplorerManager.Provider. Die Methode getExplorerManager liefert einen Standard-ExplorerManager:

public class Test extends ToComponent 
implements ExplorerManager.Provider {
  private ExplorerManager em = new ExplorerManager();
  public Test() {
    super();
    initComponenents();
    getExplorerManager().setRootContextNode (new RootNode());
  }

  public ExplorerManager getExplorerManager() {
    return em;
  }

  // ...
}

In initComponents() fügt man dieser Komponente z.B. einem BeanTreeView hinzu, damit man überhaupt etwas von diesem Beispiel hat.

Nun benötigen wir die Klasse RootNode, die von AbstractNode abgeleitet wird und ein spezielles Children-Object an AbstractNode weiterreicht. Dies ist die erste Vorraussetzung, damit die untergeordneten Nodes überhaupt eine benutzerdefinierte Reihenfolge erhalten:

public class RootNode extends AbstractNode {
  public RootNode() {
    super (new RootChildren());
  }
}

Zwar ist RootNode noch nicht fertig, aber werfen wir mal einen Blick auf RootChildren:

public class RootChildren extends Index.ArrayChildren{
  public RootChildren() {
    super();
  }
}

Natürlich hätte oben in RootNode auch ein super (new Index.ArrayChildren()); gereicht (zumal RootChildren schon fertig ist), aber wenn man das für eigene Projekte erweitern will, ist es besser man hat eine größere Kontrolle über das Children-Modell.

Index.ArrayChildren implementiert alle Verwaltungsaufgaben, um Positionen von Knoten zu managen. Das Drag and Drop UI der Views benötigen diese Implementation, um die Knoten anhand von Anwenderaktionen anzuordnen.

Bis jetzt wurde also erst die Wurzel des Baumes definiert, und wie die untergeordneten Knoten im Container (Index.ArrayChildren) dieser RootNode gehalten werden.

Jetzt also zu den ChildNodes:

public class ChildNode extends AbstractNode {
  private InstanceContent content;

  public ChildNode (NodeData data) {
    this (data, new InstanceContent());
  }
 
  public ChildNode (NodeData data, InstanceContent content) {
    super (Children.LEAF, new AbstractLookup (content));
    this.content = content;
    content.add(data);
  }

  public NodeData getData() {
    return getLookup().lookup(NodeData.class);
  }
}

Die obigen Konstruktoren entsprechen meinem Lieblingsaufbau von Nodes, die nur im Arbeitsspeicher gehalten werden. Das ist keine Vorraussetzung, um die Knoten per Drag and Drop zu verschieben.

Allerdings muss nun noch folgende Methode ChildNode hinzugefügt werden:

  @Override
  public boolean canCut() {
    return true;
  }

Wenn ChildNode bei canCut immer false zurückgibt (und das ist in AbstractNode so festgelegt), funktioniert schon der erste Drag-Schritt nicht. In NetBeans wird Drag and Drop im Prinzip als Cut and Paste abgebildet.

Damit wir später auch die Nodes unterscheiden können, implementieren wir getName() neu:

  @Override
  public String getName() {
    return getData().getName();
  }

Um die Sache lauffähig zu machen, brauchen wir noch NodeData:

public class NodeData {
  private String name;
  public NodeData (String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }
}

Jetzt können wir endlich unsere Knoten im Constructor von der Klasse Test anlegen:

  public Test() {
    super();
    initComponenents();
    Node root = new RootNode();
    getExplorerManager().setRootContextNode (root);
  
    root.getChildren().add (new Node[] {
      new ChildNode (new NodeData ("Erster Knoten")),
      new ChildNode (new NodeData ("Zweiter Knoten")),
      new ChildNode (new NodeData ("Dritter Knoten")),
      new ChildNode (new NodeData ("Letzter!!")),
    });
  }

Zwar wird es nicht gern gesehen die add()-Methode zu verwenden, im Falle von Index.Children und diesem Test ist es aber durchaus legitim.

Wenn man nun daraus ein fertiges Beispielprogramm gebastelt hat, wird man feststellen, dass das UI schon grundsätzlich reagiert. Man kann die Knoten per DnD ziehen und dabei zuschauen, wie das BeanTreeView am Zielort eine passende "Einfügemarke" anzeigt. 

Tja, aber das Fallenlassen funktioniert noch nicht. Der Mauszeiger straft uns auch mit einem Verbotszeichen.

Damit das Drag and Drop schlussendlich wirklich funktioniert, muss man dem BeanTreeView über das Cookie-Set des übergeordneten Knotens den Children-Container bekanntgeben. In diesem Fall ist der übergeordnete Knoten natürlich unsere RootNode-Klasse. Dort fügen wir folgende Methode noch ein:

  @Override
  public Cookie getCookie (Class clazz) {
    Children ch = getChildren();

    if (clazz.isInstance(ch)) {
      // hier den Children-Container bekannt machen:
      return (Cookie) ch;
    }
    return super.getCookie(clazz);
  }

Das hätte AbstractNode auch selber machen können...

Jetzt funktioniert es endlich auch mit dem Drop-Teil von Drag and Drop.

Hier nochmal die Zusammenfassung:

Der Children-Container muss ein Index-ArrayChildren sein, damit benutzerdefinierte Positionen festgelegt werden können. Ansonsten wären die Nodes nur nach festen Kriterien sortiert.

Damit der Drag-Teil überhaupt startet, muss in den Nodes canCut überschrieben werden und true zurückgeben.

Und für den Drop-Teil muss das View (hier BeanTreeView) auf den Children-Container zugreifen können, um anhand des Interfaces überhaupt ermitteln zu können, dass Nodes angeordnet werden können.

Das war es auch schon. Viel Spaß mit dem Kochrezept und dem Ergebnis eines modernen UIs.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - June 26, 2008 08:45 AM
BeanDev: Minimaler Einsatz für Drag and Drop von Nodes

Es funktioniert fast überall in den aktuellen Versionen der NetBeans IDE: Man kann in den Baumansichten die Elemente anklicken und mit gehaltener linker Maustaste an eine andere Position verschieben.

Wenn man das auch für die eigenen BeanTreeView-Komponenten haben will, muss man ein paar Methoden implementieren bzw. überschreiben, damit das funktioniert.

In diesem "Kochrezept" habe ich ein sehr einfaches Modell gewählt, nur einen Root-Node mit einer einfachen Hierachie. Der Root-Node kann sogar ausgeblendet werden.

Man erzeugt ein JPanel oder TopComponent und implementiert  ExplorerManager.Provider. Die Methode getExplorerManager liefert einen Standard-ExplorerManager:

public class Test extends ToComponent 
implements ExplorerManager.Provider {
  private ExplorerManager em = new ExplorerManager();
  public Test() {
    super();
    initComponenents();
    getExplorerManager().setRootContextNode (new RootNode());
  }

  public ExplorerManager getExplorerManager() {
    return em;
  }

  // ...
}

In initComponents() fügt man dieser Komponente z.B. einem BeanTreeView hinzu, damit man überhaupt etwas von diesem Beispiel hat.

Nun benötigen wir die Klasse RootNode, die von AbstractNode abgeleitet wird und ein spezielles Children-Object an AbstractNode weiterreicht. Dies ist die erste Vorraussetzung, damit die untergeordneten Nodes überhaupt eine benutzerdefinierte Reihenfolge erhalten:

public class RootNode extends AbstractNode {
  public RootNode() {
    super (new RootChildren());
  }
}

Zwar ist RootNode noch nicht fertig, aber werfen wir mal einen Blick auf RootChildren:

public class RootChildren extends Index.ArrayChildren{
  public RootChildren() {
    super();
  }
}

Natürlich hätte oben in RootNode auch ein super (new Index.ArrayChildren()); gereicht (zumal RootChildren schon fertig ist), aber wenn man das für eigene Projekte erweitern will, ist es besser man hat eine größere Kontrolle über das Children-Modell.

Index.ArrayChildren implementiert alle Verwaltungsaufgaben, um Positionen von Knoten zu managen. Das Drag and Drop UI der Views benötigen diese Implementation, um die Knoten anhand von Anwenderaktionen anzuordnen.

Bis jetzt wurde also erst die Wurzel des Baumes definiert, und wie die untergeordneten Knoten im Container (Index.ArrayChildren) dieser RootNode gehalten werden.

Jetzt also zu den ChildNodes:

public class ChildNode extends AbstractNode {
  private InstanceContent content;

  public ChildNode (NodeData data) {
    this (data, new InstanceContent());
  }
 
  public ChildNode (NodeData data, InstanceContent content) {
    super (Children.LEAF, new AbstractLookup (content));
    this.content = content;
    content.add(data);
  }

  public NodeData getData() {
    return getLookup().lookup(NodeData.class);
  }
}

Die obigen Konstruktoren entsprechen meinem Lieblingsaufbau von Nodes, die nur im Arbeitsspeicher gehalten werden. Das ist keine Vorraussetzung, um die Knoten per Drag and Drop zu verschieben.

Allerdings muss nun noch folgende Methode ChildNode hinzugefügt werden:

  @Override
  public boolean canCut() {
    return true;
  }

Wenn ChildNode bei canCut immer false zurückgibt (und das ist in AbstractNode so festgelegt), funktioniert schon der erste Drag-Schritt nicht. In NetBeans wird Drag and Drop im Prinzip als Cut and Paste abgebildet.

Damit wir später auch die Nodes unterscheiden können, implementieren wir getName() neu:

  @Override
  public String getName() {
    return getData().getName();
  }

Um die Sache lauffähig zu machen, brauchen wir noch NodeData:

public class NodeData {
  private String name;
  public NodeData (String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }
}

Jetzt können wir endlich unsere Knoten im Constructor von der Klasse Test anlegen:

  public Test() {
    super();
    initComponenents();
    Node root = new RootNode();
    getExplorerManager().setRootContextNode (root);
  
    root.getChildren().add (new Node[] {
      new ChildNode