Java Getriebe

Java und NetBeans

ImportantFiles 4 Jar

Vor einiger Zeit schon habe ich ein kleines NetBeans Plugin entwickelt, um einen „ImportantFiles“ Node für normale Java Projekte anzuzeigen.
Screenshot_ImpFiles
Das Plugin ist auch über das UpdateCenter erhältlich und wurde schon einige male heruntergeladen. Jens wies mich dann darauf hin, dass es ’ne ganz coole Sache wäre dieses Tool so zu erweitern, dass man noch weitere, individuell einstellbare, Dateien registrieren kann. Diese Änderung ist nun in der aktuellsten Version (voraussichtlich ab 27.07.2015 im UpdateCenter, ab 21.07.2015 im Plugin Portal) verfügbar. Aus diesem Anlass möchte ich hier nun ein kleines Tutorial schreiben, welches die Erweiterung der ImportantFiles erklärt.

Vorraussetzung

Vorrausgesetzt wird, dass die Plugins ImportantFiles4Jar und „NetBeans Plugin Developement“ heruntergeladen, installiert und aktiviert sind.

Anlegen des Entwicklungsmodul

Zuerst wird ein neues NetBeans Module Projekt erstellt. Dieses Projekt bekommt dann einen Namen und eine entsprechende Code Name Base.

NewProjekt

Im Anschluss daran, fügt man eine neue layer.xml in das Projekt ein. (Rechtsklick > New > Other > Module Developement > XML Layer).

NewLayer

Erweitern der XML Layer

Zunächst sollten wir das Grundgerüst der layer.xml Datei aufbauen, sodass die Datei folgendermaßen aussieht:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC
       "-//NetBeans//DTD Filesystem 1.2//EN"
       "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
  <folder name="de-nigjo-kll-important">
    <folder name="Files">
    </folder>
    <folder name="FileListener">
    </folder>
  </folder>
</filesystem>

Zur Erklärung:

In dem Ordner Files werden alle Dateien eingetragen, die als „Important File“ später angezeigt werden sollen, sofern sie denn vorhanden sind. Zusätzlich kann man im Ordner FileListener Listener definieren, die auf rename, remove und add Events horchen. Dabei kann ein Listener auf einer Datei oder einem Ordner angemeldet werden, und bei einem Ordner können auch noch alle Kindelemente dieses Ordners mit einem Listener versehen werden. (Dies geht theoretisch auch bei Dateien, macht aber so ziemlich überhaupt keinen Sinn 😉 ).

Registrieren einer Datei

Wir werden zunächst einmal auf das Registrieren von Dateien eingehen. Dazu nehmen wir an, dass wir die Datei Wichtige Datei.wichtich registrieren wollen. Diese Datei soll im Wurzelverzeichnis der Quellcodedateien liegen, d.h. im Ordner src eines Projektes. Um das zu erreichen muss ein neuer Eintrag im Ordner Files erstellt werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
  <folder name="de-nigjo-kll-important">
    <folder name="Files">
      <file name="de-nigjo-kll-important-test-wichtich">
        <attr name="displayName" stringvalue="Wichtige Datei"/>
        <attr name="position" intvalue="99"/>
        <attr name="projectFile" stringvalue="src/Wichtige Datei.wichtich"/>
      </file>
    </folder>
    <folder name="FileListener">
    </folder>
  </folder>
</filesystem>

Wenn wir uns nun die NBM Datei erzeugen (Rechtsklick auf das Projekt > Create NBM) können wir unser Modul in der IDE installieren (Tools > Plugins > Downloaded > Add Plugins > Unsere erzeugte NBM wählen > Install). Um den Effekt dann zu sehen, müssen wir unser Java Projekt einmal schließen und wieder öffnen, oder die IDE neu starten. Anschließend sollten wir dann in etwa sowas erhalten:
Screenshot_Wichtige_Datei

Erklärungen

Mit dem file-Tag wird eine neue Datei registriert. Der Name dieser Datei sollte immer eineindeutig sein. Da auch die Code Name Base des Moduls eineindeutig ist empfiehlt es sich diese auch als Teil des Namen zu verwenden, allerdings nicht mit „.“ sondern „-“ separiert.

Die registrierte Datei muss das Attribut Namens projectFile besitzen, welches als stringvalue dann den Pfad zur registrierten Datei übergeben bekommt. Dabei ist der Pfad relativ zum Projektordner.

Das Positionsattribut ist optional. Wird es aber angegeben ist darauf zu achten, dass die Werte 100, 200, 300, 400, 500 und 600 bereits belegt sind. Der Wert kann aber im Intervall [1; MAX_INT] liegen, daher ist genug Platz.

Wenn das Attribut displayName angegeben ist, so wird der dort eingetragene Name zur Anzeige verwendet. Ist das Attribut nicht angegeben, so wird einfach der Name der Datei angezeigt. Der displayName kann natürlich auch ein lokalisierter Text sein, dazu muss in dem Modul eine Bundle.properties Datei (entsprechende Lokalisierungen könnten dann sein Bundle_de.properties, Bundle_es.properties, usw.) vorhanden sein.
In unserem Beispielmodul wird die schon vorhandene Bundle.properties, in der der DisplayName des Projekts gespeichert ist, dafür verwendet. Hier wird ein neuer Eintrag hinzugefügt, der sich vom bisherigen Namen (Wichtige Datei) unterscheidet:

1
2
OpenIDE-Module-Name=MyImportantFiles
wichtige_datei=VIF (Very Important File)

Nun müssen wir den Eintrag in der layer ein wenig abändern, sodass dort auch der lokalisierte Text verwendet wird.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
  <folder name="de-nigjo-kll-important">
    <folder name="Files">
      <file name="de-nigjo-kll-important-test-wichtich">
        <attr name="displayName" bundlevalue="de.nigjo.kll.important.test.Bundle#wichtige_datei"/>
        <attr name="position" intvalue="99"/>
        <attr name="projectFile" stringvalue="src/Wichtige Datei.wichtich"/>
      </file>
    </folder>
    <folder name="FileListener">
    </folder>
  </folder>
</filesystem>

Das Zeichen # trennt dabei die Datei vom Schlüssel ab. Die Datei wird als vollständiger Klassenpfad zur Hauptdatei (ohne „.properties“) angegeben. Diese Datei wird immer als Fallback verwendet, wenn für die aktuelle Sprache keine lokalisierte Bundle Datei enthalten ist. Sprich: Haben wir ne deutsche IDE, so ist das zu suchende Bundle Bundle_de. Ist das nicht da wird Bundle verwendet. Für jede andere Sprache läuft das ganze genauso.
Nach diesen Änderungen wird erneut die NBM erzeugt und in der IDE installiert. Das Ergebnis ist dann folgendermaßen:
Screenshot_VIF

Registrieren von FileListenern

Die Möglichkeit neben Dateien auch noch FileListener zu registrieren bieten einem eine erhöhte Flexibilität. Die Standard Listener sind auf dem Ordner „nbproject“ (rekursiv) und auf den Dateien „manifest.mf“ und „build.xml“ registriert. Der Listener sorgt dafür, dass beim hinzufügen oder löschen einer Datei, die Liste der Dateien in der IDE aktualisiert wird.
Nehmen wir also für unser Beispiel an, dass die Datei Wichtige Datei.wichtich erst während der Bearbeitung des Projekts hinzukommt. Das bedeutet, dass wir einen FileListener auf dem Ordner „src“ anmelden müssen um die Änderung mitzukriegen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
  <folder name="de-nigjo-kll-important">
    <folder name="Files">
      <file name="de-nigjo-kll-important-test-wichtich">
        <attr name="displayName" bundlevalue="de.nigjo.kll.important.test.Bundle#wichtige_datei"/>
        <attr name="position" intvalue="99"/>
        <attr name="projectFile" stringvalue="src/Wichtige Datei.wichtich"/>
      </file>
    </folder>
    <folder name="FileListener">
      <file name="de-nigjo-kll-important-test-srcListener">
        <attr name="recursive" boolvalue="true"/>
        <attr name="relativePath" boolvalue="true"/>
        <attr name="path" stringvalue="src"/>
      </file>
    </folder>
  </folder>
</filesystem>

Über das Attribut relativePath lässt sich steuern, ob der pathrelativ zum Projektpfad ist (true), oder absolut angegeben wird (false)(Diese Möglichkeit wird demnächst auch bei Dateien eingebaut). Mit dem Attribut recursive kann entschieden werden, ob der Listener nur auf dem definierten Objekt angemeldet wird (false) oder aber ob der Listener auf dem Objekt und allen Kindelementen und Kindeskindelementen (und … ) angemeldet wird (true). Der „srcListener“ ist rekursiv, damit wir auch das Löschen der Datei mitbekommen können. Das kann man allerdings auch mit einem nicht rekursiven Listener erreichen, denn man muss dazu lediglich einen zweiten Listener registrieren, der auf unsere wichtige Datei hört.
Wenn wir nun die NBM erzeugen und installieren können wir die Datei Wichtige Datei.wichtich in den entsprechenden Ordner einfügen nachdem alle „Important Files“ bereits angezeigt werden und wir sehen ziemlich zeitnah die Änderung im Nodebaum.

Das war’s dann auch eigentlich was es aktuell zu dem Thema zu sagen gibt. Das Modul ist natürlich noch nicht zu 100% ausgereift und wird bestimmt noch das ein oder andere Update erfahren. Für Anregungen bin ich auch jeder Zeit offen und für diese Sachen verweise ich an dieser Stelle auf die Sourceforge Seite des Projekts, wo ich über weitere Updates zum Thema der hier angesprochenen eigenen Erweiterung informieren werde, und Tickets eingereicht werden können.

Schöne Grüße
Daniel Koll

Die NetBeans Laufzeitkonfiguration auf einen Blick

Geertjan hatte vor ein paar Tagen schon einen Artikel „Viewing the NetBeans Central Registry“ in seinem Blog eingestellt in dem er mit einen mehr oder weniger Einzeiler das aktuelle „SystemFileSystem“ anzeigen kann. Diese „Zentralen Einstellungen“ (bzw „XML Layer File System“) ist DER Mechanismus der NetBeans Platform um die Modularität der RCP Anwendungen sicher zu stellen. Ohne ihn wäre der deklarative Ansatz der Platform nicht möglich.

Innerhalb der IDE bietet NetBeans zwar die Ansicht „Important Files / XML Layer / <this layer in context>“ an, aber diese Ansicht berücksichtigt nur die aktuelle Suite (und ein paar seiner Abhängigkeiten) um diesen „Context“ zu definieren. Arbeitet man mit mehreren Suiten die nicht zwingend direkte Abhängigkeiten zueinander haben, ist dieser „Context“ leider viel zu oft nicht vollständig. Aus diesem Grund ist es oft viel interessanter den „XML Layer“ so zu sehen, wie NetBeans ihn zur Laufzeit tatsächlich verwendet. Read More

@ServiceProvider in einem JavaSE Projekt

Ich bin im Moment auf einem Annotation-Trip. Die Dinger sind cool. Vor allem in Zusammenhang mit ihren Processor Implementierungen. NetBeans macht es in seinen neusten Versionen selbst vor. So cool die XML Layer Strukturen zur Laufzeit auch sind, so uncool sind sie in dem Quellen zu pflegen. Dank des LayerGeneratingProcessors ist es aber verdammt einfach geworden Annotations (die ja bekanntlich direkt im Quelltext bei den betroffenen Klassen stehen) selber zu schreiben, die die notwendigen Einträge in der layer.xml erstellen.

Ein ähnliches leidiges Thema wie die XML Layer sind Services, die man selber in einer Textdatei unter META-INF/services pflegen darf. Auch hier bietet NetBeans von eine Annotation samt Processor bereit um diese Einträge direkt im Quelltext zu schreiben: @ServiceProvider.

In einer NetBeans RCP Anwendung ohne weiteres nutzbar und sogar zu bevorzugendes Mittel. Allerdings wäre so etwas in einer eigenen Standard Java Anwendung auch cool ((Um damit zum Beispiel eigene Processor Implementierungen zu registrieren.)). Ich hatte so etwas zwar auch schon mal selber geschrieben ((leider finde ich gerade die Quellen dazu nicht)) aber ganz so gut wie die NetBeans Implementierung war das dann doch nicht. Allerdings ist es relativ einfach möglich die Annotation von NetBeans in den eigenen Java SE Projekten zu nutzen: Read More

Annotations statt Layereinträge

„Der neue Besen kehrt gut, bevor er voll Staub ist.“ — Freidank ((http://de.wikiquote.org/wiki/Freidank))

Geertjan schreibt in seinem Blog, dass die Programmbeispiele von NetBeans in der Version 7.0 überarbeitet wurden. Vor allem die direkten Einträge im XML Layer wurden durch Annotations direkt in den entsprechenden Klassen ersetzt.

So nett ich die Idee hinter den Annotations auch finde. Die Screenshots in dem Blogbeitrag lassen mich allerdings auch ein wenig daran zweifeln, ob sie der Weisheit letzter Schluss sind. Zumindest in Sachen „Übersicht“ sind sie meiner Meinung nach ein Schritt zurück. Unbestritten sehe ich allerdings auch den Vorteil, dass die Annotations das Refactoring der Klassen einfacher (und auch Sicherer) machen und dass sie potentielle Fehleingaben im XML Layer vermindern.

Ich tue mich aber etwas schwer mit der „Darstellung“ die sich aus der Syntax einer Annotation ergibt. Es lässt sich (auf den ersten Blick in den Quelltext) nur schwer erkennen, welche korrespondierenden Einträge in der Layer zu diesen Annotations gehören. Sicherlich kann man in der generates-layer.xml nachsehen, aber das ist ja auch nicht Sinn der Sache. Hier würde eine Art „Editor“ oder zumindest „Viewer“ für den Annotationblock hilfreich sein. Allerdings muss ich auch gestehen, dass mir im Moment keine passende Darstellung dafür einfällt.

Vielleicht hat jemand ja eine Idee, wie man die Übersicht bei den Annotation/Layereintägen zurück gewinnen kann.

Jens

Möglichkeiten für eine methodvalue Implemetierung

Schon länger hatte ich mich gefragt, welche Möglichkeiten man bei einem methodvalue Attribut in der layer.xml hat. Ziel ist es ja, dass man einem <attr> Eintrag ein bestimmtes Objekt zuweisen kann und dieses Objekt durch einen Methodenaufruf erzeugt. Man hat also in dieser Methode alle Möglichkeiten das gewünschte Objekt zu beeinflussen. Aber wie muss die Methodensignatur dazu aussehen? Read More