Wer mit der Project API von NetBeans arbeitet kommt früher oder später zu dem Punkt an dem man eine Datei (oder besser ein FileObject) hat und wissen will “zu welchem Projekt gehört diese Datei denn nun?” Anfangs habe ich das über den ProjectManager geregelt. Dort sind Methoden, die heißen findProject oder auch isProject. Eigentlich naheliegend. [...]
Da auch JavaFX 2 mit einem eigenen Native-Loader kommt, haben wir in einer NetBeans Platform wieder das Problem, die DLL/SO/… Bibliotheken an die richtige Stelle zu bekommen.
Man kann natürlich den Anwender JavaFX installieren und bin-Pfade einrichten lassen. Aber bequemer ist es natürlich, wenn sich die Platform Application darum kümmert.
Hier eine extrem simple (und nicht für alle Plattformen gültige) Lösung.
Es wird davon ausgegangen, dass sich die javafx-2.0.jar in dem Ordner cluster/modules/ext/somename befindet. Cluster ist der suite-Name. Der Pfad modules/ext ist Standard für externe Bibliotheken und somename ist (ggf.) ein Codename-Base-Name der externen Bibliothek oder “lib” (das ist JavaFX aber egal). Ich habe z.B. com.oracle verwendet (die aus der Gruppen-ID meiner Maven-Artifakte kommt).
Will ich nun (nur) Windows in 32bit und 64bit unterstützen, lege ich in dem ext-Order noch bin und darunter amd63 und x86 an. Also: cluster/modules/ext/bin/amd64 und cluster/modules/ext/bin/x86. Der Native Loader von JavaFX sucht immer (von der javafx-2.0.jar) in ../bin. Ich muss also nun die DLL’s kopieren.
Hier ein Beispiel meiner Dateistrukturen:
Da JavaFX fast mit Java7 verheiratet ist, werde ich auch nur mit Java7-nio den Weg beschreiten:
public class Installer extends ModuleInstall {
@Override
public void restored() {
Class c = Installer.class;
URL u = c.getProtectionDomain().getCodeSource().getLocation();
String path = u.getPath();
if ( path.startsWith("file:/") && path.endsWith(".jar!/")) {
path = path.substring(6);
path = path.substring(0, path.length()-2);
int pos = path.lastIndexOf("/");
if ( pos >= 0 ) {
path = path.substring(0, pos);
FileSystem fs = FileSystems.getDefault();
Path dest = fs.getPath(path, "ext", "bin");
Path source = fs.getPath(path, "ext", "bin", System.getProperty("os.arch"));
try {
for (Path toCopy : Files.newDirectoryStream(source)) {
Path destFile = fs.getPath(dest.toString(), toCopy.getFileName().toString());
Files.copy(toCopy, destFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}
Am aufwendigsten ist die Ermittlung des Modulpfades per URL u = c.getProtectionDomain().getCodeSource().getLocation(); mit dem Extrahieren des eigentlichen Pfades (man sieht, WebStart wird nicht unterstützt). Den Path “source” ermittle ich ausschließlich über os.arch. Das ist natürlich nicht wirklich nützlich für Plattformen wie Linux. Wer weitere Plattformen unterstützen will, sollte noch zusätzliche Systemparameter ermitteln und weitere Unterordner anlegen. Ich würde aber explizit die Methoden aus org.openide.util.Utilities (isMac, isUnix, isWindows) verwenden. Die System Properties sind da zu geschwätzig.
Nehmen wir an, es würde alles fehl schlagen (weil wir Bibliotheken nicht mitliefern), dann sollte man noch eine eigene Hilfsklasse basteln, die den erfolgreichen (oder nicht erfolgreichen) Kopiervorgang an andere Module liefern kann (z.B. JavaFXHelper.isJavaFXAvailable()). Wenn man das nämlich nicht abprüft und doch JavaFX verwendet, sind die Abstürze so hart, dass häufig nicht mal das Hauptfenster angezeigt wird. Im Log findet ihr dann Fehler wie: java.lang.UnsatisfiedLinkError: Can’t load library: …
Was allerdings schön ist: mehr ist nicht zu machen. Man muss nicht (wie in einem alten Artikel von mir beschrieben) eine Startup-Klasse der Platform unterschieben, um JavaFX zu nutzen.
Einen Mini-WebBrowser fügt man so in eine TopComponent ein:
final JFXPanel fxPanel = new JFXPanel();
add(fxPanel);
Platform.runLater(new Runnable() {
@Override
public void run() {
Group group = new Group();
Scene scene = new Scene(group);
fxPanel.setScene(scene);
view = new WebView();
view.setMinSize(1024, 768);
view.setPrefSize(1024, 768);
group.getChildren().add (view);
}
});
view ist eine Feldvariable der TopComponent. Mit der Methode:
public void browseTo (final String url) {
Platform.runLater(new Runnable() {
@Override
public void run() {
view.getEngine().load(url);
}
});
}
kann man eine Webseite öffnen (http:// muss aber am Anfang der URL stehen).
Viel Spaß mit JavaFX in der NetBeans Platform.
Seit kurzer Zeit ist ein neues Release von NetBeans fertig und kann hier heruntergeladen werden.
Die Version 7.1.1 bring kleinere Neuerungen:
- Java ME SDK 3.0.5 Unterstützung
- Bundle GlassFish 3.1.2 Update
- JavaFX 2.0.3 SDK Unterstützung
- Maven 3.0.4 Upgrade
Dazu wurden 123 Fehler berichtigt, die noch in 7.1 waren oder bis 7.1.1 entdeckt wurden. Hier eine Liste der P1 Fehler:
Es gibt ein paar kleine Tricks, um mit der IDE etwas schneller arbeiten zu können. Die folgende Liste ist bei weitem nicht vollständig, sondern beschreibt nur die Shortcuts, die ich gerne nutze (auch die Verwendung von einigen Assistenten).
Zwischenablage / Clipboard
Mehr durch Zufall bekommt man mit, dass Strg+C / Ctrl-C die komplette Zeile in die Zwischenablage kopiert, wenn man kein Zeichen selektiert. Das ist sehr praktisch, weil man sich die Tastenkürzel für das Markieren einer kompletten Zeile ersparen kann.
Strg-V / Ctrl-V fügt die Zwischenablage ein. Aber mit der Umschalt/Shift Taste wird der importierte Quelltext noch zugleich formatiert. Umschalt+Strg+V ist damit meine häufigst verwendete Tastaturkombination.
Löschen
Strg+E löscht eine komplette Zeile, ohne sie markieren zu müssen.
Auskommentieren
Schnell muss mal eine Zeile raus aus den Programmablauf, löschen will man sie aber nicht. Toggle Comment [Strg+Umschalt+C] hilft hier, um selektierte Zeilen schnell auszukommentieren oder die Kommentare wieder zu entfernen.
Zeilen bewegen / duplizieren
Mit den Pfeil/Cursor Tasten und Alt+Umschalt / Alt-Shift, kann man die aktuelle Zeile oder die markierten Zeilen nach oben oder unten verschieben, bzw. ein- oder ausrücken.
Sehr praktisch ist auch das Kopieren der aktuellen Zeile (oder der markierten Zeilen). Pfeil / Cursor auf und ab mit Strg+Umschalt / Ctrl+Shift erzeugt eine Kopie über oder unter dem Original. Dabei wird die Zwischenablage / Clipboard nicht verändert.
So kann man z.B. relativ schnell Felder in Klassen duplizieren, um dann nur den Namen zu ändern.
Actions für “Java-Zeile beenden”
Das sind Shortcut-Actions, die auf deutschen Tastaturen nicht funktionieren. Bevor man diese Funktionen nutzen kann, muss man diese in den Options neu zuweisen. Dazu geht man in die Optionen (Tools -> Options), dort auf die Keymap. Im Suchfeld gibt man “Complete Line” ein. Man bekommt zwei Editor Actions die auf Ctrl+SEMICOLON und Ctrl+Shift+SEMICOLON hören. Die eine Action (Complete Line) wird auf ALT+COMMA geändert, die andere (Complete Line and Create New Line) auf Ctrl-Alt-COMMA.
Jetzt kann man mitten im Bearbeiten der Zeile die Shortcuts eingeben. Die IDE fügt dann ein ; an das Ende der Zeile an und fügt (auf Wunsch) eine neue Zeile ein.
Rechteckige Selektion im Editor
Die Texteditoren haben neuerdings zu der klassischen Zeichen/Zeilen-Selektion nun eine rechteckige Selektion. Die kann man mit Strg+Umschalt+R aktivieren und deaktivieren. Man kann dann mit der Maus oder Umschalt+Pfeiltaste einen rechteckigen Bereich markieren. Das Besondere ist nicht das Kopieren des Bereiches, sondern die Eingabe von neuen Daten in den Bereich. Jede Zeile aus dem rechteckigen bereich erhält die neue Eingabe. Somit kann man z.B. sehr schnell einen Bereich von public Feldern in private ändern.
public int a;
public String b;
public double c;
Nun mit Strg+Umschalt+R den Selektionsmodus auf Rechteckig ändern und alle drei public’s selektieren. Dann einfach private eingeben. Alle drei Felddeklarationen haben nun private als Modifier. Es ist zu empfehlen die Selektion nach solchen Operationen wieder zurückzusetzen.
Alles hybsch machen
Mit Strg+Umschalt+F wird der selektierte Bereich (oder alles, wenn keine Markierung existiert) neu durchformatiert.
Quelltextvervollständigung / Codecompletion
Import Statements aufräumen und ergänzen
Die Tastenkombination Strg+Umschalt+I wirft ungenutzte Imports raus, löst *-Imports auf und fügt fehlende Imports hinzu. Wenn Klassennamen doppelt existieren, dann muss man die passenden Klassen in einem Dialog bestätigen. Man braucht die Kombination seltener, seit dem Strg+V und Strg+Umschalt+V automatisch die Imports mit übertragen. Aber manchmal braucht man es doch noch.
Methoden überschreiben
Man kann den Alt-Einf / Alt-Insert Assistenten verwenden, oder direkt den Namen der zu überschreibenden Methode eingeben und dann Strg+Leertaste / Ctrl-SPACE drücken (natürlich im Body der Klasse). Es wird dann vorgeschlagen, eine Methode aus der Liste zum Überschreiben auszuwählen. Mit Enter bestätigen, erzeugt die Methode mit dem super-Aufruf der überschriebenen Methode.
Variablenzuweisungen von der IDE
Ma sollte möglichst nicht mit einer VAriablenzuweisung beginnen, sondern erst den Methodenaufruf schreibe. Beispiel:
getDimension();
Man bleibt mit dem Eingabecursor hinter den Semikolon. Die IDE wird schon jetzt eine gelbe Lanpe (Hint) bei der Zeilennummer einblenden. Nicht mit der Maus anklicken (dauert zu lange), einfach Alt+Enter. Es erscheint idR. nur ein Hinweis: Einen Rückgabewert einer neuen Variable zuordnen. Einfach mit Enter bestätigen und die IDE erzeugt:
Dimension dimension = getDimension();
Dazu den passenden Import. Außerdem ist der Variablenname dimension markiert, so dass man ihn ggf. überschreiben kann.
POJOs erzeugen
Ein sehr einfacher Weg, um Plain Old Java Objects zu erzeugen, ist wieder per Assistent. Erstmal eine Klasse anlegen. Im Body der Klasse nur Alt+Einfügen / Alt+Insert tippen.
In der Auswahlbox “Add Property” auswählen. Der Assistent erlaubt nun neben den Namen und Typ auch weitere nützliche Optionen zu aktivieren. Insbesondere “Bound” erleichtert viel Arbeit, weil damit das PropertyChangeSupport für die set’er Methoden erzeugt wird.
Nie wieder muss man diesen Boilerplate Code schreiben:
/**
* Set the value of name
*
* @param name new value of name
*/
public void setName(String name) {
String oldName = this.name;
this.name = name;
propertyChangeSupport.firePropertyChange(PROP_NAME, oldName, name);
}
Nun kann man mit den Klassen erstmal arbeiten. Wenn man später die Architektur etwas aufmöbeln will, kann man aus der Klasse ein Interface extrahieren (Im Kontextmenü: Refactor -> Extract Interface).
Navigation
Hyperlinks für Klassen, Methoden und Felder
Strg-Taste / CTRL halten und mit dem Mauszeiger über einen Namen (Methode, Feld, Klassenname) gehen. Es erscheint ein Hyperlink. Ein Klick darauf und man springt zur Deklaration (öffnet dabei ggf. einen neuen Editor).
Aktuelle Editor-Datei im Projektfenster markieren
Wenn man keine sofortige Synchronisation aktiviert hat (View -> Synchronize Editor with Views), dann stimmt die markierte Datei im Projektfenster nicht mit der aktuell bearbeiteten Datei im Editor überein. Strg+Umschalt+1 markiert die Datei im Projektfenster (und öffnet ggf. das zugehörige Projekt). Das ist dann besonders nützlich, wenn man in eine Klasse per Strg+Mausklick (wie ein Browser-Hyperlink) über Methodennamen gesprungen ist oder der Debugger die Quelltexte geöffnet hatte.
Klasse suchen
Strg+O (Goto Type) ist ein Suchdialog für Klassennamen. In dem Dialog den Suchbegriff eingeben, Doppelklick auf eines der Ergebnisse öffnet die Datei. Strg-Umschalt-1 zeigt die Datei dann in der Projekts-Struktur.
Zu Zeilennummer springen
Strg+G (Goto Linenumber) öffnet einen Dialog, um eine Zeilennummer einzugeben. Dorthin springt dann der Eingabecursor. Wer Exceptions per Mail zugeschickt bekommt, wird das Feature lieben.
Alle Implementationen eines Interfaces
Vor der Deklarationszeile eines Interfaces, abstrakter Klassen und überschriebener Klassen erscheint in der Zeilennummer ein kleines (i). Klickt man darauf, wird eine Liste aller bekannten Implementationen angezeigt. Per Klick kann man in die einzelnen Implementationen springen. Das ist nützlich, wenn man Interface erweitert (neue Methoden-Signaturen) und damit die Kassen angepasst werden müssen.
Editor maximieren
Eigentlich ist der Editor immer zu klein, zumindest geht es mir so. Der Doppelklick auf die Registerzunge zum Maximieren des Editors ist nett aber umständlich. Das Tastenkürzel Maximize Windows [Strg+ESC] ist da schneller. Entgegen des Namens setzt [Strg+ESC] die vergrößerte Registerzunge auch wieder zurück.
Generelle Probleme mit Tastenkürzel
Wer Ubuntu verwendet, wird mit allen Tastenkürzeln unglücklich werden, die mit Alt+Umschalt / Alt+Shift eingeleitet werden. Das klappt unter Ubuntu nicht, da Alt+Umschalt schon vom System belegt ist. Man muss diese Kürzel unter Tools -> Options -> Keymap ändern.
Nutzer deutscher Tastenlayouts kämpfen mit den Shortcuts, deren Keycodes sowieso per Umschalt / Shift zu erreichen sind (Komma, Semikolon, Stern) oder per AltGr (geschweifte und Eckige Klammern). Da muss man meistens die Zugriffs-Kürzel der Actions ebenfalls unter Tools -> Options -> Keymap anpassen.
Erstellt man eine eigene Anwendung auf Basis der NetBeans Platform erstellt kommt man irgendwann unweigerlich an den Punkt das Resultat auch an die Endanwender verteilen zu wollen. Im Kontextmenü des Suiteprojekt liefert der Eintrag “Package as” alles was man dafür benötigt. Ein Problem gibt es nur, wenn man während der Entwicklung der eigenen Anwendung individuelle [...]
Die neue Version von NetBeans steht kurz vor der Veröffentlichung. Die passende Wiki-Seite mit den Neuerungen ist nun auch online. Am meisten freut mich, dass nun endlich eine “Organize Imports” Funktion direkt in NetBeans integriert sein wird. Mir gefällt das ziemlich gut und deswegen werde ich die Arbeiten an meiner eigenen Version wohl einstellen. Danke [...]
Es ist schon nicht einfach einen eigenen Processor für Annotations zu schreiben, wenn man etwas Fehlerbehandlung einbauen möchte. Aber noch “schwieriger” ist es diesen Processor im Debugger betrachten zu können. Das Problem ist, dass sich die IDE im Grunde genommen selber debuggen muss. Zur IDE hat man in den seltensten Fällen ein Projekt, zu dem [...]
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 [...]
Für eine NetBeans Rich Client Platform Anwendung ist es ein leichtes eine bestehende .jar-Datei in ein Modul zu kapseln. Das so genannte “Library Wrapper Module” steht im “New Project Wizard” zu Verfügung und man muss nur noch eine (oder mehrere) Java Archive auswählen, fertig. Den Rest erledigt die NetBeans IDE für uns. Das klappt prima, [...]
Ich hatte den Entwurf für einen eigenen Artikel schon begonnen. Aber Geertjan war diesmal schneller: Annotation Processor for Main Popup Menus (Geertjan’s Blog).
Meine Version der Übersetzungshilfe geht in die nächste Runde. Ich habe meinen “NetBeans Suite Translator” aktualisiert und im NetBeans Plugin Center hoch geladen. Die wichtigste Neuerung dabei ist ein Export aller Bundle Properties als OmegaT Projekt. Der Export ist eine erste Version die stark auf meine eigenen Bedürfnisse zugeschnitten ist und hat dementsprechend im Moment [...]
I’ve just updated NetBeans. The following dialog appeared:
How many people read those 85 263 lines of text carefully?
Der Code Formatter in NetBeans ist ja ganz nett und stellt auch einen rudimentären Support dar um den Sourcecode nicht komplett “versauen” zu lassen. Aber so richtig viel kann der nicht. Andere Open Source Projekte wie Jalopy, jindent oder Jacobe sind entweder veraltet, nicht vollständig frei oder komplett auf kommerziell umgestellt. Ich vermisse in NetBeans [...]
Auf dem letzten NetBeans Training wurde ich von Geertjan gefragt wie wir bei uns den Continuous Integration Server Jenkins dazu nutzen um unsere WZL Gear Toolbox automatisch generieren zu lassen. Das großen Problem ist, dass Jenkins zwar sehr einfach Maven Builds einbinden kann und zusammen mit Maven auch Suiteprojekte sehr einfach einzufügen ist aber für [...]
Wir nutzen seit über drei Jahren die NetBeans Platform als Basis für unsere eigene Anwendung. Vor knapp zwei Jahren bin ich in Göttingen auf den Source Talk Tagen gewesen und durfte den Ausführungen von Geertjan Wielenga, Toni Epple und Aljoscha Rittner lauschen. Eine Offenbarung. Schnell kam mir die Idee das ganz auch in Aachen zu [...]
Bisher wurden in NetBeans alle geänderten Daten über eine Implementierung von SaveCookie im Lookup markiert. Wurden die Daten verändert, kam der Keks ins Lookup, nach dem Speichern wurde es dort wieder heraus geholt. Die Plattform sorgte dann dafür, dass die SaveAll Action aktiviert wurde und beim Programmende gab es einen netten Dialog, in dem noch [...]
Toni Epple hat für JAXenter einen (längeren) Artikel mit einer Zusammenfassung der Neuerungen in NetBeans 7.0 geschrieben. Sein Fazit: Insgesamt ist NetBeans 7.0 ein solides Update ohne spektakuläre Überraschungen. Das NetBeans-Team hat sich mit dem neuen Release auf die wesentlichen Eigenschaften einer Java-Entwicklungsumgebung konzentriert. Die IDE wurde erwartungsgemäß besser mit dem Oracle-Stack integriert und an [...]
Neues NetBeans, neuer Editor. Und damit war auch eine neue Version meines “Organize Imports” Plugin fällig. Die “Mayor Version” des NetBeans Editors hat sich auf “3″ erhöht und damit sind wieder alle Plugins, die etwas mit selbigem anstellen nicht mehr kompatibel mit NetBeans 7. Im Plugin Center ist die Version 1.2.1 zum Download bereit, die [...]
Mit erscheinen der Version 7 der NetBeans Plattform sind auch einige neue Annotations hinzugekommen. Es scheint als würden die NetBeans Entwickler so langsam das Potential dieses Programmierkonstruktes nutzen wollen. Mit allen Konsequenzen. Eine dieser Neuerungen ist die Annotation @Message. Geertjan hat in seinem Blog bereits einen Kommentar darüber abgegeben. Ziel dieser Annotation ist es (wohl) [...]
Hi!
The new updated Refcard #80 by Heiko Böck, Anton Epple, Miloš Šilhánek, Andreas Stefik, Geertjan Wielenga, and Tom Wheeler for the brand new NetBeans 7 Platform is published.
You can find many informations about the NetBeans Platform, a getting started intro, main features, NetBeans Platform modules, NetBeans Platform APIs, reusable GUI components and more.
In this Refcard, you are introduced to the key concerns of the NetBeans Platform so that you can save years of work when developing robust and extensible applications.
Six pages packed with many informations, download this Refcard #80 now!
Moin!
Bin gerade mit 1001 Sachen beschäftigt und habe mein Blog etwas vernachlässigt. Aber einen kleinen Tipp habe ich gerade zur Hand. In einem älteren Blogeintrag erwähnte ich die Fähigkeit, dass NetBeans in den 7′er Dev-Builds Bundle Einträge in ein Code-Fold bringen kann. Es wird dann der Wert des Standard-Bundle angezeigt.
Es gibt aber noch ein nettes Feature. Man kann mit der Strg-Taste + Mausklick nun auch direkt zur Bundle-Datei zum Schlüssel springen. Es funktioniert genau so, wie man auch zu Klassen, Methoden und Variablen-Deklarationen springen kann.
Mit dem Klick wird die Bundle-Datei geöffnet und der Cursor auf den Schlüssel gesetzt. Wenn man nun die Tastenkombination Strg+1 drückt, wird diese Bundle Datei auch im Project Explorer selektiert (soweit man nicht sowieso eine automatische Selektion aktiviert hat).
Beste Grüße!
Hi!
Here is my next test with the JavaFX 2.0 ea release. I’ve created a 200-liner to capture parts from the desktop. The “Snipper” detects mouse dragging and two different key strokes (Escape and the letter ‘A’). The captured picture is automatically stored in the user.home/snapshot path.
Before I start to explain the code, I show a small picture about the different scene graph nodes:
At the bottom is the Desktop (I use only the primary screen, but it’s possible to detect all additional screens). The stage is created by the JavaFX Launcher class. I modify the stage to a transparent style and fullscreen mode. The scene (embedded in the stage) captures the mouse events (button pressed, released and dragging). The scene itself has a transparent fill color and contains one group with three different nodes. In the KeyPane-Node I capture the key events. The node is focusable and transparent to mouse events (any mouse event sinks down to the scene). GlassPane is a node with a Shape created by the screen bounds with a rectangular hole. This hole is calculated by the mouse gestures from the user. At last I’ve a visual representation for the user interaction: a red rectangular lasso node.
Disclaimer: The solution here is based on the early access release through the JavaFX partner program. This “best practice” may change significantly between now and the final version. However, I’ll show only a concept, not compilable code.
Here the start up:
@Override
public void start(final Stage stage) {
primaryStage = stage;
Group group = new Group();
Scene scene = new Scene(group);
scene.setFill(Color.TRANSPARENT); // default is white
scene.setCursor(Cursor.CROSSHAIR);
stage.setStyle(StageStyle.TRANSPARENT);
stage.setFullScreen(true);
stage.setScene(scene);
stage.setVisible(true);
screenBounds = new Rectangle (
Screen.getPrimary().getBounds().getWidth(),
Screen.getPrimary().getBounds().getHeight()
);
Nothing special. Only few notes: The Scene-fill must be transparent and the Screen class provides more than the primary screen. But this small example captures only from the primary screen.
I have a special helper class named Rebounder. This class creates and manipulates the lasso rectangle and calculates the shape with the hole for the GlassPane. The KeyPane needs the Rebounder for a fullscreen capture. The scene mouse handlers are call the rebounder with the current mouse points.
class Rebounder {
Rectangle lasso;
double px;
double py;
Rebounder() {
lasso = new Rectangle(0, 0);
lasso.setFill(null);
lasso.setSmooth(false);
lasso.setStroke(Color.RED);
lasso.setStrokeWidth(1);
lasso.setStrokeType(StrokeType.OUTSIDE);
lasso.setMouseTransparent(true);
lasso.setVisible(false);
}
Rectangle start(double x, double y) {
lasso.setX(x);
lasso.setY(y);
lasso.setWidth(0);
lasso.setHeight(0);
px = x;
py = y;
lasso.setVisible(true);
return lasso;
}
Rectangle rebound(double x, double y) {
lasso.setWidth(Math.abs(x - px));
lasso.setHeight(Math.abs(y - py));
lasso.setX(Math.min(x, px));
lasso.setY(Math.min(y, py));
return lasso;
}
Rectangle stop(double x, double y) {
rebound(x, y);
lasso.setVisible(false);
return lasso;
}
boolean isStopped() { return !lasso.isVisible(); }
Node getLasso() { return lasso; }
Shape shapeBuilder(Rectangle r) {
return r != null
? Path.subtract(screenBounds, r)
: new Rectangle(screenBounds.getWidth(), screenBounds.getHeight());
}
}
In the constructor I create the lasso node. The three methods start(x,y), rebound(x,y) and stop (x,y) are called by mousePressed, mouseDragged and mouseReleased. The shapeBuilder-method returns a shape for the GlassPane.
Here are my mouse handlers:
scene.setOnMousePressed(new EventHandler() {
@Override public void handle(MouseEvent me) {
glassPane.setShape(
rebounder.shapeBuilder(rebounder.start(me.getX(), me.getY())), false);
}
});
scene.setOnMouseDragged(new EventHandler() {
@Override public void handle(MouseEvent me) {
glassPane.setShape(
rebounder.shapeBuilder(rebounder.rebound(me.getX()+1, me.getY()+1)), false);
}
});
scene.setOnMouseReleased(new EventHandler() {
@Override public void handle(MouseEvent me) {
if ( !rebounder.isStopped() ) {
capture(rebounder.stop(me.getX()+1, me.getY()+1));
}
}
});
A very straight forward implementation.
Ok we miss the KeyPane and the GlassPane. The KeyPane is complete transparent, contains only a screen wide rectangle which is focusable and captures key events:
class KeyPane extends Group {
public KeyPane(final Stage stage, final Rebounder rebounder) {
final Rectangle keyEventPane =
new Rectangle(screenBounds.getWidth(), screenBounds.getHeight());
keyEventPane.setFill(Color.TRANSPARENT);
keyEventPane.setStroke(null);
keyEventPane.setMouseTransparent(true);
keyEventPane.setFocusTraversable(true); // for keyPressed events
keyEventPane.setOnKeyPressed(new EventHandler() {
@Override public void handle(KeyEvent key) {
if (key.getCode() == KeyCode.VK_ESCAPE) {
stage.setVisible(false); // == Close
} else if (key.getCode() == KeyCode.VK_A) {
capture(new Rectangle(screenBounds.getWidth()+1, screenBounds.getHeight()+1));
}
}
});
getChildren().add (keyEventPane);
}
}
In my GlassPane I’ve only the calculated shape and a small help text:
class GlassPane extends Group {
void setShape(Shape shape, boolean showInfo) {
while (!getChildren().isEmpty()) {
getChildren().remove(0);
}
if ( shape != null ) {
double BLUE = 0.95;
Stop[] stops = new Stop[] {
new Stop(0.00, Color.color(BLUE, BLUE, 1, 0.3)),
new Stop(0.2, Color.color(BLUE, BLUE, 1, 0.7)),
new Stop(0.25, Color.color(BLUE, BLUE, 1, 0.5)),
new Stop(0.4, Color.color(BLUE, BLUE, 1, 0.3)),
new Stop(0.70, Color.color(BLUE, BLUE, 1, 0.7)),
new Stop(1.0, Color.color(BLUE, BLUE, 1, 0.3)),
};
LinearGradient lg = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE, stops);
shape.setFill(lg);
shape.setMouseTransparent(true);
shape.setStroke(null);
getChildren().add(shape);
if ( showInfo ) {
Text info = new Text("'ESC' to leave the Snipper\n"
+ "'A' to capture the whole screen\n"
+ "Drag the mouse to capture a rectangle");
info.setFill(Color.WHITE);
info.setTranslateX(200);
info.setTranslateY(60);
info.setScaleX(2);
info.setScaleY(2);
info.setEffect(new DropShadow());
info.setMouseTransparent(true);
getChildren().add (info);
}
}
}
}
I fill the shape with a LinearGradient paint.
In the start method I stick all panes together:
KeyPane keyPane = new KeyPane(stage, rebounder = new Rebounder());
glassPane = new GlassPane();
glassPane.setShape(rebounder.shapeBuilder(null), true);
group.getChildren().addAll(new Node[] {keyPane, glassPane, rebounder.getLasso()});
Ok where is the heart of my Snipper application? The capture method?
The capture method is currently developed with the java.awt.Robot class. So we need to leave the JavaFX thread and dive into the EventQueue. But first we must hide the whole stage window to get an good image from the desktop. But don’t use stage.setVisible (false) (it closes the application). I resize the stage to a zero dimension. Here my capture method:
public void capture(final Rectangle finished) {
primaryStage.setWidth(0);
primaryStage.setHeight(0);
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
if ( (((finished.getWidth()-1) * (finished.getHeight()-1) ) > 0.0d ) ) {
try {
Robot robot = new Robot();
BufferedImage img = robot.createScreenCapture(
new java.awt.Rectangle(
(int)finished.getX(), (int)finished.getY(),
(int)finished.getWidth()-1, (int)finished.getHeight()-1));
File folder = new File (System.getProperty("user.home"), "snapshots");
folder.mkdirs();
File file = File.createTempFile("jfx2_screen_capture", ".jpg", folder);
ImageIO.write(img, "jpg", file);
} catch (Exception ex) {
Logger.getLogger(Snipper.class.getName()).log(Level.SEVERE, null, ex);
}
}
Platform.runLater(new Runnable() {
@Override public void run() {
glassPane.setShape(rebounder.shapeBuilder(null), true);
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
}
});
}
});
}
With this few 200 lines I’ve created a very useful screen capture application. The start up time is incredible: Between the main method and the complete visible stage the JavaFX framework needs only 500ms on my one year old double-core notebook. The complete startup (double-click the jar) is under a second (hard to stop the time). I need only one AWT dependency to the Robot class. I hope this could be changed to increase the startup time (e.g. with the internal FXRobot class).
best regards,
Josh.
Hi!
Alan O’Leary shows in his blog a WebView integration in Swing. It is a not good documented feature, how to integrate JavaFX 2.0 controls into a swing application. But an integration is a main goal for the JavaFX 2.0 release.
As an enthusiastic NetBeans Platform/RCP developer and JavaFX partner, I work since two days to marriage JavaFX 2.0 and a NetBeans Platform Application. And yes, it works
I’ve created a maven based platform application with a special starter Main.class. I need to launch the JavaFX toolkit system before any other module bootstrapping. The solution here is based on the early access release through the JavaFX partner program. This “best practice” may change significantly between now and the final version. However, I’ll show only a concept, not compilable code.
The solution behind the bootstrapping a NetBeans Platform is based on a FAQ by Tom Wheeler. My Main.class is a JavaFX Application class – I need this Application instance to get rid of from invoke exceptions. The created Stage object by the Launcher can be ignored.
public class Main extends Application{
private static final String NB_MAIN_CLASS = "org.netbeans.core.startup.Main";
public static void main(String[] args) throws Exception {
// do whatever you need here (e.g. show a custom login form)
System.out.println("Launch Java FX");
long ms = System.currentTimeMillis();
Launcher.launch(Main.class, args); // This is the main start up for JavaFX 2.0
System.out.println("Launched Java FX in " + (System.currentTimeMillis() - ms) + "ms");
// once you're done with that, hand control back to NetBeans
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
Class mainClass = Class.forName(NB_MAIN_CLASS, true, classloader);
Object mainObject = mainClass.newInstance();
Method mainMethod = mainClass.getDeclaredMethod("main", new Class[]{String[].class});
mainMethod.invoke(mainObject, (Object) args);
}
@Override
public void start(Stage stage) {
// Nothing to do, forget the stage....
}
}
The Main class is in a standard Java archive. This JAR file and all the JavaFX files must be in the platform/core folder.
At runtime all the core-Libs are available to the whole NetBeans Platform modules (and plugins). For the compiler I need a special dependency to the runtime:
<dependency>
<groupId>sun.javafx</groupId>
<artifactId>tools</artifactId>
<version>2.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/../../corelauncher/mainlauncher/src/main/lib/jfxrt.jar</systemPath>
</dependency>
The system path depends on your project structure.
Now I can access all the JavaFX classes in a NetBeans module.
Please note, any scene construction needs to be build up in the JavaFX event queue thread. This is not the EventDispatcher-Thread from Swing!
My favorite call to jump in the JavaFX event thread is: Toolkit.getDefault().defer (Runnable) javafx.application.Platform.runLater (Runnable). But the Toolkit class is in a com.* package. IMHO in the future we get an official way to do this.
The creation of a WebView component is pretty similar to the sample from Alan. But I don’t need a stage object:
Platform.runLater(new Runnable() {
@Override
public void run() {
group = new Group();
Scene scene = new Scene(group);
browser = new WebView(new WebEngine());
browser.getEngine().addChangeListener(PropertyReference.WILDCARD, new ChangeListener() {
@Override
public void handle(Bean paramBean, PropertyReference paramPropertyReference) {
if ("title".equals(paramPropertyReference.getName())) {
EventQueue.invokeLater(new Runnable() {
// Jump to Swing EventDispatcher...
@Override
public void run() {
BrowserTopComponent.this.setDisplayName(browser.getEngine().getTitle());
}
});
}
if ("url".equals(paramPropertyReference.getName())) {
EventQueue.invokeLater(new Runnable() {
// Jump to Swing EventDispatcher...
@Override
public void run() {
String url = browser.getEngine().getUrl();
tfUrl.setText(url);
addHistory(url);
}
});
}
}
});
group.getChildren().add(browser);
group.setScaleX(0.8d);
group.setScaleY(0.8d);
Reflection r = new Reflection();
r.setTopOffset(8);
group.setEffect(r);
scene.setFill(javafx.scene.paint.Color.BLACK);
browser.setWidth(panel.getWidth());
browser.setHeight(panel.getHeight());
panel.setScene(scene);
}
});
panel.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
Platform.runLater (new Runnable() {
// Jump from Swing-EventDispatcher to the JavaFX Thread:
@Override
public void run() {
browser.setWidth(panel.getWidth());
browser.setHeight(panel.getHeight());
}
});
}
});
I’ve added some useful listeners. Please aware the switches between different threads (Swing and JavaFX).
The result is a beautiful NetBeans Platform application with an embedded JavaFX 2.0 WebView:
PS.: I like the Twitter message from Dean Riverson: “Ok, I call a moratorium on rotating and reflecting WebView…” (origin). -
I have to write 100 times:
Nicht nur ich mache mir Gedanken darum, wie man seine Anwendung am besten in einer anderen Sprache angezeigt bekommt. Geertjan hat in seinem Blog an verschiedenen Stellen schon mal damit beschäftigt. In einem aktuellen Beitrag hat er ein Tool aufgetan mit Namen “i18nchecker”. Dieses Tool sammelt aus einem Projekt (Es muss nicht zwingend ein Suiteprojekt [...]
Moin!
Letzten Donnerstag und Freitag war es mal wieder soweit, ein sehr intensives und hoffentlich für die Teilnehmer spannendes Training ist zu Ende gegangen.
In zwei Tagen haben wir (Geertjan und ich) mehr als 20 Teilnehmern, an der Fachhochschule Osnabrück Lingen/Ems, die NetBeans Plattform in einem Trainings-Kurs nahe gebracht. Es hat uns besonders gefreut, dass alle Anwesenden bis 18:00 am Freitag durchgehalten haben. Und nein, die Tür war nicht abgeschlossen
Die Inhalte waren, wie immer, nicht ohne. Windows System API, Lookups, System File System, Nodes, Explorer und Visual Library waren die Kernthemen. Durch all diese Themen zog sich natürlich: “Modulare Softwareentwicklung”. So wurde den Studenten schnell klar, dass es ohne Theorie nicht geht. Es schien auch einige überrascht zu haben, dass es hier nicht nur darum ging, in der NetBeans Plattform ein paar Swing-Programme zu entwickeln. Wir vermitteln immer dazu – völlig vom Produkt losgelöst – wichtige Paradigmen der modularen Entwicklung von Software. Darunter fallen Punkte wie Separation of Concern, Single Responsible Principle, Komponentenorientierung, Kohäsion und Entkopplung.
Mit vielen eingeschobenen Workshops haben wir dieses Thema von der trockenen Grundlage auf das praktische Feld verschoben.
Für einen besonders guten Einstieg in die NetBeans Platform API haben alle Teilnehmer des Kurses eine DZone NetBeans Platform Refcard erhalten. Ich hoffe, dass sich diese 2-seitige DIN-A3 Karte nun immer neben dem Bildschirm befindet.
Ich war begeistert, dass einige Studenten sich am Freitag Abend zu kleinen Entwicklergruppen zusammengeschlossen haben, um erste Projekte in der NetBeans RCP Welt zu realisieren. Ich freue mich auf die Ergebnisse!
Ganz großen Dank an die Organisation des Events und kulinarische Betreuung. Es gab immer genug Kaffee und andere Getränke und vor allem ein fantastisches Buffet. Da kommen wir doch gerne wieder!
Auf unseren edu-Seiten gibt es unsere Präsentationen zum nachlesen und nacharbeiten: Trainings-Präsentationen. Ein sehr wichtiger Anlaufpunkt ist auf jeden Fall die Platform-Seite: platform.netbeans.org.
Fragen im Nachgang werden über die NetBeans Platform Certified Students Mailingliste abgehandelt. Natürlich stehen auch die englisch- und deutschsprachigen NetBeans Foren zur Verfügung.
Beste Grüße!
Eigentlich wollte ich einen neuen Bugreport schreiben, in dem ich vorgeschlagen hätte meine Version mit dem Userdir in “Eigenen Dateien” speichern vorgeschlagen hätte. Und da man ja ein guter Reporter sein möchte sucht man vorher nach schon getätigten Reports um Duplikate zu vermeiden. Dabei ist mir dann ein anderer Eintrag aufgefallen, der zwar schon was [...]
Moin!
Gestern kündigte ich ja die Beta2 zur NetBeans 7.0 IDE an. Zwei Dinge hatte ich da unterschlagen, die hier noch einer besonderen Erwähnung bedürfen.
Git
In der Beta2 kann man nun (ohne Plugin) git als Versionierungssystem nutzen. Der Git Plan im Wiki gibt schon einen recht guten Überblick, was funktioniert und worauf man noch warten muss.
Folgende Features sollten funktionieren:
- Quick overview of file status in IDE
- View_Modifications_(Show_Changes)
- Add
- Commit
- Add oder Exclude bei commit
- Reset Changes (Lokale Änderungen zurücknehmen)
- Diff – Lokale Änderungen vergleichen
- Inline diff -Lokale Änderungen in der Files sidebar
- Repository Init
- Ignore
- Checkout
- Create Branch
- Switch to Branch , siehe Checkout
- Merge from Branch
Bis zum Finale ist noch einiges zu tun, gerade für das Distributing steht noch was aus (Repository Clone, Pull, Push, Fetch), die Vervollständigung der Branching und Merging Tools (Merge from Branch, Export uncommited changes (Local changes), Apply Patch) sowie Search history of files und eine Blame Integration.
JUnit
Trotz der verwirrenden und unterschiedlichen, teils negativen, Meldungen wird JUnit natürlich weiter von NetBeans unterstützt. Allerdings muss JUnit in Zukunft extra aus dem Plugin-Center geladen werden. Fest integriert ist nur ein Bridge-Modul zu JUnit. Unabhängig zu rechtlichen Vorgaben hat das in Zukunft vielleicht sogar Vorteile, wenn es um die Integration von anderen Unit-Tools geht (z.B durch eine einheitliche API).
Ich hoffe, dass heute der Download nicht so überlastet ist wie gestern. Viel Spaß mit NetBeans 7.
Beste Grüße,
Josch.
Moin!
Ab heute ist NetBeans 7.0 Beta 2 verfügbar zum Download.
Die Highlights sind:
- JDK 7 Unterstützung
- Integration des WebLogic Servers
- Verbesserungen, um eine Oracle Datenbank einzubinden
- Glassfish 3.1 Unterstützung
- Maven 3 Support
- Remote URL-JavaDoc Support (JavaDoc muss nicht mehr lokal installiert sein)
- Neuer GridBagLayout Customizer
- Für JavaEE CDI, REST Dienste und Java Persistence Verbesserungen, zudem Bean Validation
- JSF Component Bibliothekenunterstützung, PrimeFaces ist mit an Board
- Verbesserte EL-Unterstützung in JSF
- HTML5
- JSON
- PHPDoc-Generierung
- PHP Rename Refactoring und Safe delete Refactoring
- PHP 5.3 Alias-Unterstützung
- C/C++ kann nun besser Binär-Libs importieren
- C/C++ Projekt-Typ mit Quelltexten auf Remote-Servern
- Endlich für PHP User: Word-Wrap im Editor
- Der Profiler wurde aufgebohrt
- Verbesserte Überprüfung von externen Änderungen an Quelltexten außerhalb der IDE
Auch die NetBeans Platform bekommt Erweiterungen:
- Annotationen für Actions, TopComponents und NbBundle.
- Eigene Annotationen für Maven-Projekte
- Performance Steigerungen und bessere Integration des Profilers
- Und etliche API-Änderungen, hier ein paar schöne Änderungen rausgepickt:
- Icons für Update-Center
- openInstallWizard für den PluginMananger
- QuickSearch im TreeView (de)aktivieren
- OutlineView-Tree-Spalte kann nun eine horizontale Scrollbar erhalten. (Sehr nützlich!)
- Eigene TableCellRenderer
Ich hoffe nur die Maven Artifacts werden für Beta2 zeitnah freigegeben, am SNAPSHOT teste ich ungern.
Die aktuellen Release-Notes (en) stehen natürlich auch zur Verfügung. Sehr umfangreich ist, wie immer, die NewAndNoteworthy Seite.
Beste Grüße,
Josch.
Moin!
Ich habe mal wieder was für meine Ideensammlung: Plugins, die ich schon immer mal programmieren wollte, aber nie die Zeit dazu fand.
Heute der native2ascii Konverter für Dateien, die man im Project-Explorer markiert hat. Kontextmenü mit folgenden Befehlen:
- To ASCII…
- Reverse to native…
- Native to native…
To ASCII wandelt von einem vorgegebenen Encoding in die \u-Notation. Reverse to native fragt nach eine Encoding, in der die Datei konvertiert werden soll und Native to native fast beide Transformationen zusammen.
Vielleicht kann man das dem existierenden kenai.com Projekt NATIVE2ASCII hinzufügen? Ich muss mal Hlavki fragen und mit einem hg-clone einen Prototypen schreiben… wenn ich mal wieder Zeit habe.
Beste Grüße,
Josch.
Moin!
Eines der massiv unterschätzten Themen ist Java auf dem Desktop. Immer wieder höre ich von Entwicklern, dass Web-Oberflächen, Java EE, Server und Backend-Development die Domäne für Java sei.
Ich denke mal, man muss da ein wenig über den Tellerrand hinaus schauen. Sicherlich ist Java nicht die 1a Desktop Sprache, weil für die vielen netten Gimmiks einfach die nativen OS spezifischen Schnittstellen fehlen. Aber mal ehrlich, warum sollte eine so robuste Programmiersprache auf dem Desktop nichts taugen? Seit 15 Jahren entwickele ich Java-Desktop-Applikationen zunächst für eine kurze Zeit mit AWT, dann seit Swing 1.1 raus war, Swing-Applikationen.Ich plagte mich davor mit der Microsoft Welt und auch mit 4GL Sprachen rum. Aber kein Framework hat mir so viele Freiheiten und so eine stabile Grundlage gegeben, wie es Java mit Swing und den ganzen zusätzlichen Bibliotheken konnte.
Zwar spricht man heute bei Java vom Cobol des einundzwanzigsten Jahrhunderts, aber auch das ist völlig unterschätzt. Denn neben der Masse an Programmen, die Java (wie ehemals Cobol) trägt, gibt es doch eine Weiterentwicklung die ganz sicher keine Stagnation erfährt.
Geertjan hat in seinem Blog selbst mal wieder eine Lanze für den Desktop Einsatz von Java gebrochen und da ein Zitat von James Gosling ausgegraben, das ich hier gerne wiederhole:
If I had to say I’m jaded it’s sort of about the fact that people’s attention is so focused on the web. And it feels to me like that’s 10% of the computing landscape. If you look at what goes on in, say, biology. Huge, huge computational problems in biology. The people who do protein folding up in San Francisco, which is kind of everybody there, that stuff is really really cool. It sure doesn’t show up on anybody’s “cool” radar but, you know, all modern medicine research, it’s computers. Right, it’s computing. And none of that has any magic pixie dust coolness on it and modern materials. It’s, again, all computer simulation and all the rest of that and the computing behind that and the rest of that, most people don’t even think of it as computing because it’s something that a scientist does. Talk to most people in physics, they spend most of their time writing code, not doing physics experiments. And it’s just a cool world filled with thousands and thousands of different things and it feels like the web is only a corner of it.
Hinter dem ganzen Internet mit all seinen Online-Applikationen, Webservices, Shops, Social Media, Blogs, Twitter-Diensten, Online-Spielen usw. verbirgt sich eine Welt in der Computer auch für etwas anderes gebraucht werden. Eine Welt im Business-Bereich, eine Welt in der Forschung und Wissenschaft, eine Welt in der Produktentwicklung, eine Welt von Künstlern, Designern, Entwicklern und Autoren. Welten, wo Programme auf browserbasierten Oberflächen nur die zweite Wahl sind.
Wenn man sich das mal klar macht, kann man Goslings Aussage verstehen, wenn er vom Internet nur als eine 10% Ecke der Computerwelt spricht. Gefühlt wird jeder immer sagen, das ist mehr: Das Internet füllt mein Leben als Computer-Nerd sicher zu 90% aus. Aber die produktive Entwicklungsarbeit geschieht immer noch auf dem Desktop. Sicher, Server- und Webgestützt, aber Desktopprogramme schlagen Browser-Ajax-Anwendungen immer noch in ihrer Bedienbarkeit, Reaktion und Funktionalität.
Auch die NetBeans-Plattform als klassisches RCP Framework zeigt mit seinen ganzen existierenden Derivaten, dass der Desktop eben nicht eine verarmte Softwarelandschaft ist. Im Gegenteil, da wo massive Robustheit, gute Wartbarkeit und flexible Schnittstellen zu anderen tausenden Frameworks benötigt werden, ist Java auf dem Desktop, nach meinem Dafürhalten, die zentrale Plattform.
Was tatsächlich allein schon auf der NetBeans Plattform existiert, zeigt eindrucksvoll die Liste auf netbeans.org. Hier ein paar Screenshots:
und Hundert mehr. Diese Applikationen sind ausschließlich auf der NetBeans Plattform entstanden und stellen keine vollständige Liste dar. Dazu gibt es wieder unzählige reine Swing-Applikationen und Anwendungen, die auf dem Eclipse RCP Framework basieren.
Alles Anwendungen die stabil und robust in den Firmen dieser Welt werkeln. Vielleicht unbeachtet und nicht immer sichtbar in dem omnipräsenten Internet, aber wichtig für Entwicklung und Wirtschaft.
Aber die Entwickler stöhnen, sie vermissen den Anschluss zu den ganzen schönen Gimmicks, die Silverlight, Adobe Air und andere Frameworks bieten, um Desktop-Anwender das 110%-Feeling zu geben. Unabhängig davon, ob man das wirklich braucht, vielleicht gibt es mit JavaFX for Swing eine Lösung, die Desktop-Apps auch für den privaten Desktop wieder attraktiver macht. Vielleicht schaffen wir es SwingX und Labs-Komponenten verstärkt in das JDK aufzunehmen. Vielleicht wird es endlich eine HTML-Browser-Komponente in den Standard-Runtimes geben. Eventuell sieht man die Notwendigkeit, dass Java einfacher mit nativen Schnittstellen arbeiten kann, sei es zum OS, sei es zu Hardware-Schnittstellen.
Ich denke schon, dass es da eine Zukunft gibt. Die geballte Wucht der vorhandenen Applikationen kann sowieso nur eine Richtung vorgeben. Ich hoffe nur, dass die Macht des Faktischen auch bei Oracle erhört wird.
Beste Grüße,
Josch.
You can find an English version on NetBeans Zone.
Moin!
Bei den NetBeans Certified Trainings übernehme ich regelmäßig den Teil Nodes & Explorer Views (pdf). Dabei stelle ich regelmäßig die unterschiedlichen Views vor. Dafür habe ich ein kleines Demo-Programm geschrieben. Das Programm zeigt allerdings nicht nur die Vorzüge der Views, sondern auch die Probleme mit deren Darstellungen. Gerade das IconView glänzt nicht gerade mit schöner Optik. Ich hatte aus der Not eine Tugend gemacht und gerade für das Training eine eigene Implementation vorbereitet. Damit kann man den Teilnehmern sehr leicht zeigen, wie man eigene Views für die Nodes-API erstellen kann.
Dieses BetterIconView hat es Toni Epple angetan und er zeigte es im Dezember letzten Jahres Jaroslav Tulach. Er hatte gestern Zeit gehabt, meine paar Codezeilen zu nehmen, um damit das alte IconView zu ersetzen.
Wie man an dem Beispiel des alten IconView erkennen kann, funktioniert so einiges nicht. Fokusrahmen fehlt, Größenberechnung in der Höhe ist zu groß und die Nodes überlappen sich bei langen Texten.
Meine neue Implementation ist zwar recht einfach und nicht im jeden Detail perfekt, aber sicherlich um einiges besser, als die alte Version in der NetBeans Plattform.
Hier ein Screenshot mit drei selektierten Nodes und dem korrekten Fokusrahmen-Verhalten:
Den Quelltext kann man hier einsehen. Es ist wirklich sehr einfach ein ListView zu erweitern, um ein neues IconView zu erzeugen, deswegen werden da keine großen Überraschungen auftauchen.
Man sollte nur berücksichtigen, dass man in Views nicht mit dem Nodes aus dem Modell des Entwicklers arbeitet, sondern mit optimierten Wrappern. In einem Renderer (hier ListCellRenderer) konvertiert man das übergebene Value-Objekt in ein Node mit der Hilfsklasse Visualizer:
Node node = Visualizer.findNode(value);
Hat man diese Hürde geschafft, kann man alles realisieren, was Swing zu bieten hat. Der Kreativität sind also keine Grenzen gesetzt. Ich möchte da zum Beispiel auch auf Geertjans Blog verweisen, wo er die Visual Library als Explorer-View nutzt.
Beste Grüße,
Josch.
Moin!
Leider erreichte mich heute über die Mailingliste eine nicht sehr schöne Nachricht. Die Ruby On Rails Entwicklung wird nicht mehr vom NetBeans Core Team unterstützt.
Tinuola Awopetu von Oracle beschreibt es so:
After thorough consideration, we have taken the difficult step to discontinue support for Ruby on Rails in the NetBeans IDE.
Dabei werden zwei Hauptgründe genannt. Erstens:
Java SE 7 and Java Development Kit 7 (JDK 7) are the next major releases of the Java SE platform, which Oracle is committed to deliver in 2011. A key objective of the NetBeans IDE has always been to offer superior support for the Java platform. To maintain that objective and capitalize on the JDK 7 release themes–multi-language support, developer productivity and performance–it is necessary that our engineering resources are committed to a timely and quality release of NetBeans IDE 7.0.
Das Entwicklerteam will sich auf den Fokus JDK 7 konzentrieren, damit NetBeans 7 eine 100% Unterstützung mit dem Release bieten kann. Damit setzt Oracle (wie auch schon in der Vergangenheit angekündigt wurde), verstärkt auf die Java-Entwicklung. Ruby on Rails kann da nicht in der gewünschten Qualität einfließen.
Oracle sieht aber nicht nur einen stärkeren Fokus bei Java, sondern sieht Ruby on rails überhaupt im Abwind. Das ist der zweite Grund:
Second: Although our Ruby support has historically been well received, based on existing low usage trends we are unable to justify the continued allocation of resources to support the feature.
Ab dem 27. Januar 2011 wird Ruby on rails aus den Dev-Builds verschwinden. Entwickler aus der Community, die Ruby on Rails weiter unterstützen wollen, werden auf die NetBeans Ruby Support Seite verwiesen.
Feedback zu dieser Entscheidung wird ebenfalls gewünscht. Oracle verweist dazu auf die Mailinglisten/Foren, Twitter oder den direkten Kontakt zum NetBeans IDE Team.
Beste Grüße,
Josch.
Moin!
Nur ein kleines Appetithäppchen. Wenn man transparente JButtons benötigt, weil die dahinter liegenden JPanels Grafiken oder Farbverläufe haben, benötigt man besonders beim Windows-System Look&Feel einen Trick.
Zunächst reicht es nicht, nur setOpaque (false) zu verwenden. Es wird trotzdem ein Hintergrund als Füller gemalt. Die ui-Klasse ist so leider programmiert. Man kann aber eine transparente Hintergrundfarbe setzen:
public class TransparentButton extends JButton {
public TransparentButton () {
super();
setBackground (new Color (255, 255, 255, 0));
setOpaque (false);
}
}
Wenn man bei (halb-) transparenten Hintergrundfarben das setOpaque (false) vergisst, hat man übrigens sehr unschöne Back-Buffer Effekte. D.h. die Optimierungen im Swing-Framework verursachen dann unschöne zerhackte Grafikeffekte. In Kombination funktioniert es aber super.
Beste Grüße,
Josch.
“Der neue Besen kehrt gut, bevor er voll Staub ist.” — Freidank1 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. [...]
Moin!
Die Frage kommt immer wieder mal, deswegen wollte ich hier eine kleine Liste an Programmen erfassen, die Java-Programme in native Betriebssysteme integrieren, Installer erzeugen oder das Starten vereinfachen.
Ich habe dafür drei Gruppen. Die Launcher sind Executables, die Java-Programme wie native Programme starten lassen. Das ist üblicherweise nur unter Windows notwendig. Linux und Mac behandeln Java-Programme wie alle anderen Executables. Die nächste Gruppe wäre Installer. Diese Werkzeuge erzeugen aus Java-Projekten Installationspakete, die es dem Anwender ermöglichen per Kommandozeile oder Assistenten Java-Programme zu installieren oder später zu deinstallieren. Die letzte Gruppe behandelt Packer. Darin fasse ich Tools zusammen, die 3rd-Party-Libs in ein JAR zusammenfassen, JARs komprimieren oder Klassen so umbenennen, so dass eine Quelltextwiederherstellung sehr schwer gemacht wird (Obfuscator).
Ich würde mich freuen, wenn ein paar Anregungen und Erfahrungsberichte rein kommen würden. Für die NetBeans Platform gibt es schon integrierte Installer für Windows, Linux, MacOS und Solaris sowie eine WebStart Variante. Außerdem einen Pack200 Tool für den Installer. Da braucht man für die meisten Fälle keine externen Tools.
Hier also eine Liste, die ich schon so gesammelt habe:
Launcher
- WinRun4J
- Lizenz: CPL
- OS: Windows
- Integration: Kommandozeile, Eclipse Plugin
- Ein Executable mit native Bindings, Windows DDE-Zugriff, konfigurierbarem Icon, INI-Datei für Startparameter, Dienste-Wrapper, Systemlog-Unterstützung und Konsolenmodus.
- Es unterstützt auch das Packaging von 3rd-Party Bibliotheken.
- Soweit ich das sehen kann, hat man mindestens immer ein JAR und eine Java-Umgebung muss installiert sein.
- Launch4j
- Lizenz: BSD und MIT
- OS: Alle Java-Plattformen (Ziel bleibt aber Windows)
- Integration: Kommandozeile, Ant, Maven, GUI (Wizard)
- Launch4j erzeugt eine Windows Executable-Datei, die alle JARs, native Bibliotheken und sogar ein Embedded JRE enthalten kann.
- Es unterstützt eigene Icons für die Exe, JAR’s werden nicht beim Start extrahiert, unterstützt auch Konsolenanwendungen,
Installer
- Open Installer (Crossplatform Installer)
- Lizenz: CDDL 1.0
- OS: Alle Java-Plattformen
- Integration: Kommandozeile, NetBeans Platform Installer
- Ich erwähne den Open Installer hier, weil dieser in der NetBeans Platform integriert ist und als Installer für den Glassfish verwendet wird. Ansonsten würde ich als Entwickler die Finger davon lassen, weil das Projekt extrem schlecht dokumentiert ist. Am besten den Quelltext anschauen, wenn man eine hohes Maß an Interesse und Leidensfähigkeit hat.
- Antigen (Ant Installer Generator)
- Lizenz: k/a
- OS: Alle Java-Plattformen
- Integration: Ant, Kommandozeile, (evtl. Maven)
- Erzeugt aus Ant-Scripts einen grafischen Installer
- Alle Programmbestandteile werden in einem JAR-Installer gepackt. Eine Java Runtime muss auf dem Zielrechner existieren.
- InstallJammer
- Lizenz: GPL mit “Special Exception“, darf kommerziell genutzt werden
- OS: Multiplattform (tck/tcl), Windows 98, ME, 2000, XP, 2003, Vista, 2008, Linux, FreeBSD, Solaris, HP-UX, AIX
- Integration: Kommandozeile, GUI
- InstallJammer ist eine mächtige OpenSource Lösung die einen nativen Installer mit umfangreicher Buildumgebung anbietet. Der Installer ist nicht auf Java-Programme beschränkt. JRE wird vom Installer nur gesucht, aber nicht bei Bedarf installiert. Man kann aber das JRE wohl über Actions mitinstallieren.
- Mehrsprachigkeit, Multiplattform, Nativer Installer im OS-Look, Benutzerdefinierte Aktionen, Installer-Profile (Kommandozeile, GUI, Silent, usw), GUI-Bearbeitung der Assistenten-Panels, Themen, Deinstallationsroutinen.
- Umfangreiche Dokumentation
- Install4j
- Lizenz: Kommerziell (Windows Single ab 600€, Multiplatform Single ab 1700€, nach oben offen…)
- OS: Win32, Win64, Linux RPM, MacOS, Unix
- Integration: Ant, Kommandozeile, GUI
- Dieser kommerzielle Installer bietet einen GUI oder Kommandozeileninstaller (auch Silent), JRE-Pacakging oder Detection, GUI und Scriptsprache, Mehrsprachigkeit, Dienste-Unterstützung, eigenes Icon und sehr vieles mehr.
- Advanced Installer for Java Freeware Edition
- Lizenz: Kommerziell, Freeware Edition
- OS: Windows
- Integration: Kommandozeile, GUI
- Die Freeware Edition bietet einen MSI Installer Generator für Java Programme mit UAC Unterstützung, Registry-Zugriff, 32bit und 64bit, Scripting über XML-Konfiguration.
- JRE Bundling ist nicht in der Free Edition, Custom Icon ist nicht möglich.
- Feature Matrix
Packer
- one-jar
- Lizenz: Proprietär, OpenSource, BSD?
- OS: Alle Java-Plattformen
- Integration: Ant, Maven
- Erzeugt aus mehreren JAR-Bibliotheken eine einzelne ausführbare JAR-Datei
- Unterstützt auch native Bibliotheken, die während des Starts in das Dateisystem entpackt werden.
- ProGuard (Obfuscator)
- Lizenz: GPL
- OS: Alle Java-Plattformen
- Integration: Kommandozeile, Ant
- Verändert sprechende Bezeichner (z.B. Paket und Klassennamen) in kompilierten JARs in verkürzte oder randomisierte Namen, um die Wiederherstellung von bearbeitbaren Quelltext zu erschweren.
- Unterstützt viele Plattformen (Java 1.1 bis 6, ME, Android)
- pack200 JAR Packager
- Lizenz: Oracle License
- OS: Alle Java-Plattformen
- Integration: Kommandozeile
- Ein Tool des Java SE SDKs von Oracle, dass seit Java 5 JAR Dateien besonders effizient packen kann.
- Der Einsatz sind üblicherweise webbasierte Java-Anwendungen, damit diese im Download möglichst wenig Bandbreite verwenden
- Ant Script für die NetBeans IDE
- Lizenz: k/a
- OS: Alle Java-Plattformen
- Integration: Ant, NetBeans IDE
- Es handelt sich dabei nicht um ein Programm, sondern ein Ant-Target-Script zum Erstellen von Single-JARs. Der verlinkte Blogeintrag beschreibt, wie man in NetBeans dieses Script integrieren kann.
Moin!
Kohsuke Kawaguchi hat es gestern veröffentlicht. Der Name Hudson CI ist gestorben. Der Continious Integration Server soll in Jenkins (CI?) umbenannt werden.
Wie Kohsuke in einer Nachricht schreibt, konnte Oracle, trotz einer ein Monat langen Kommunikation, nicht davon überzeugt werden, dass der Name Hudson CI in einer “neutralen Organisation” gut aufgehoben wäre.
The central issue was that we couldn’t convince Oracle to put the trademark under a neutral party’s custody (such as Software Freedom Conservancy), to level the playing field.
Er führt aber auch aus, dass Oracle Pläne mit Hudson verfolgen will, die der aktuellen Entwicklung des CI-Servers nicht entspricht. Dazu gehöre auch eine erheblich formalisiertere Projektstruktur, Änderungen in der Architektur und das verstärkte Einbinden von 3rd-Party Abhängigkeiten.
Aside from this, Oracle wanted a broader change to the way the Hudson project operates, including a far more formal change review process, architectures, 3rd party dependencies and their licenses, and so on.
[...]
All of those still might not have been a show-stopper if we felt that there is a genuine trust between us, but in this case, we just failed to build such a relationship, even after a month long conversation.
Als Konsequenz soll das Projekt in Jenkins umbenannt werden. Dieser Name ist erneut ein “Buttler”-Name aus dem englischen Sprachraum und soll aktuell von keinem anderen Software-Projekt beansprucht sein.
So in the end, we’d like to propose the community that we regrettably abandon the name “Hudson” and rename to “Jenkins” — it’s another English-sounding butler name that doesn’t collide with any software project as far as I can tell.
Das Proposal für die Namensänderung wurde im Hudson Lab veröffentlicht:
The Proposal
First, we rename the project – the choice for a new name is Jenkins, which we think evokes the same sort of English butler feel as Hudson. We’ve already registered domains, Twitter users, etc for the new name, and have done our best to verify that there are no existing trademarks which would conflict with it. Kohsuke will be registering the trademark for Jenkins in his name, with the intent of transferring ownership of the trademark to the umbrella of the Software Freedom Conservancy once the Jenkins project has been admitted to it (which, I should add, is very much our plan, hopefully in their next round of new projects in a few months – we’ve already had preliminary contacts with SFC). We still invite Oracle to remain involved with the project, on equal terms with all other contributors, and hope they’ll take us up on this invitation.
[...]
Das Proposal umfasst aber mehr als nur eine Namensänderung. Außerdem soll die Infrastruktur des Projektes komplett von Oracle weg verschoben und ein Governance Board eingerichtet werden.
Ich würde mich freuen, wenn der CI Server auch in Zukunft in der NetBeans IDE unterstützt wird. Hudson nutze ich schon seit einiger Zeit, um zentralisierte Maven- und Ant-Builds durchzuführen.
Noch besser wäre es, Oracle würde mit seinem verbleibenden Hudson Projekt sich nicht zu weit von Jenkins entfernen. Konkurrenz belebt zwar das Geschäft, aber in diesem Fall wäre der Nutzen für die Anwender fraglich.
Beste Grüße,
Josch.
Moin!
Wenn man kleine schnelle Testumgebungen für NetBeans IDE-Plugins haben möchte, sollte man sich diese FAQ anschauen.
DevFaqNetBeansFullHack
Can I test changes to the IDE without going through the license check and so on?
Ja, tatsächlich geht das. Mit einem kleinen Parameter netbeans.full.hack auf true gesetzt, werden einiges an Dialogen, Test und Remotezugriffen (Auto Update Center, Maven Index) deaktiviert:
- Auto Update background check (to see if updates are available); you can still use AU via Tools > Plugin Manager
- prompting about still-running tasks when shutting down
- license dialog
- import of old user directory
- IDE registration dialog
- dialog suggesting that you submit usage statistics
- welcome screen displayed by default and RSS feed refreshed
- blocking dialog when some modules could not be loaded
- use of ~/NetBeansProjects/ for newly created projects (java.io.tmpdir will be used instead)
- resizing gesture submit dialog (SubmitStatus.resize)
- weekly Maven repository indexing (can be configured in Options dialog)
- long package name for default group ID in new Maven project (test used instead)
So können Plugin-Tests und automatisierte Tasks mit Aufruf der NetBeans IDE ohne störende Dialoge durchgeführt werden.
Beste Grüße,
Josch.
Moin!
Das Plugin SevenBeans bringt für die NetBeans IDE das 100% Native-Feeling auf Windows 7 Betriebssystemen.
Mit diesem Plugin erhält man zu dem NetBeans-Taskbar Icon eine
- Jumplist (spezielles Kontextmenü per rechter Maustaste),
- Iconoverlays (d.h. ein Build wird in der Taskbar als Badged Icon dargestellt),
- Den hellgrünen Fortschrittsbalken im Taskbar-Icon,
- Minivorschau der geöffneten Tabs, wenn man mit der Maus über das Taskbar-Icon fährt
- und einen Konfigurationsdialog in den Options, um alle neuen Features ein- oder auszuschalten.
SevenBeans basiert auf der Technologie von J7Goodies, ein Framework um Swing-Anwendungen mit solchen Features auszustatten.
Eine Jumplist wird so zum Kinderspiel:
DestinationList albums;
albums.addFile("C:\\Users\\Alex\\My Pictures\\Bachelor Party.alb");
albums.addFile("C:\\Users\\Alex\\My Pictures\\Family.alb");
albums.addFile("C:\\Users\\Alex\\My Pictures\\Flowers.alb");
DestinationList tasks;
tasks.addTask("/camera", "Import from camera", cameraIconPath);
tasks.addTask("/scan", "Scan a photo", scannerIconPath);
JumpList jumpList;
jumpList.appendRecentCategory();
jumpList.appendCustomCategory("Albums", albums);
jumpList.appendTasks(tasks);
jumpList.save();
Viele weitere Beispiele finden sich auf der J7Goodies Homepage.
Schön ist es, dass das Plugin für NetBeans schon fertig ist. Die aktuelle Version kann hier heruntergeladen werden.
Beste Grüße,
Josch.
Moin!
JAXenter hat wieder eine (der sehr häufigen) Quickvote-Umfragen. Aktuell geht es um das wichtigste Java-Ereignis 2010.
Klar, dass die Sun-Übernahme durch Oracle inzwischen über 60% bekommen hat, aber es wird auch gefragt, ob nicht vielleicht NetBeans 6.9 das wichtigste Ereignis gewesen ist? Hmm
Zumindest 2% erreicht NetBeans 6.9 zur Zeit, Eclipse e4-Linie nur 1%.
Ich persönlich finde neben 6.9 und Snoracle noch das Maven 3 Release als sehr wichtiges Ereignis an.
[UPDATE]
Das Ergebnis steht fest, 274 Teilnehmer (nicht sehr repräsentativ).
Nur ein kurzer Kommentar: Die NetBeans 6.9 Version bekam (ggü. den anderen wichtigen Ereignissen) gute 5%. Klar, Sun-Übernahme durch Oracle bekommt 56%. Eclipse e4 allerdings nur 0%. Die JDK7/8 Roadmap bekam 5% und ist m.E. abgestraft. Auch Oracle hat es bis jetzt nicht geschafft mehr Schwung in die Entwicklung zu bringen. Altlast durch Sun? Man könnte noch die Sprachenvielfalt für Java mit 6% dazu addieren, um das Ergebnis zu beschönigen. Aber na ja…
Beste Grüße,
Josch.
Die Zeichen stehen gut, dass in der 7er Version von NetBeans die Icons im PluginManager austauschbar werden. Jaroslav Tulach hat zwar nicht meine Version der Quelländerung verwendet, aber dafür hat er ziemlich tief in die Trickkiste gegriffen1 und das Problem geschickt umschifft. Jaroslav hat das fragliche enum CATEGORY selbst nicht verändert. Damit hat er vor [...]
Im letzten Teil der Serie wurden alle aufgeklappten Nodes eines OutlineViews ermittelt und gespeichert. In diesem Teil der Serie geht es darum diese gespeicherten Informationen zu nutzen, um den Aufbau in einem OutlineView wieder herzustellen.
In der NetBeans IDE sieht man dieses Verhalten seit 6.9 im Projekt-Explorer. Dort werden die aufgeklappten Hierarchien (und selektierten Knoten) nach dem Start so wieder aufgebaut, wie man es beim letzten Schließen verlassen hatte.
Zunächst benötige ich eine Methode, die aus der letzten Serie kodierten Nodes, wieder eine Liste von String Arrays erzeugt. Denn diese Liste braucht man für die NodeOp-Klasse aus dem NetBeans Framework.
/**
* Liest aus den Properties die ExpandedPath-Einträge.
* @param delim Trenner für den Pfad (z.B. "/")
* @param props Properties-Container, in dem die Pfade gespeichert wurden
* @param prefix Präfix für die Keys.
* @return Liste der ermittelten Path-Arrays
*/
public static List<String[]> loadPaths (String delim, Properties props, String prefix) {
List<String[]> paths = new ArrayList<String[]>();
if (prefix == null) prefix = "";
int count = Integer.parseInt(props.getProperty(prefix + "ExpandedCount", "0"));
for (int i = 0; i < count; i++) {
paths.add (props.getProperty(prefix + "ExpandedPath." + i).split(delim));
}
return paths;
}
Diese Pfade können nun mit der folgenden Methode expandiert werden:
/**
* Expandiert alle Pfade der Liste, die schon mal über getExpandedPaths ermittelt wurde.
* @param view
* @param rootNode
* @param exPaths
*/
public static void expandNodes(final OutlineView view, Node rootNode, List<String[]> exPaths) {
for (final String[] path : exPaths) {
Node found;
try {
found = NodeOp.findPath(rootNode, path);
} catch (NodeNotFoundException nnfe) {
found = nnfe.getClosestNode();
}
final Node leafNode = found;
EventQueue.invokeLater(new Runnable() {
public @Override
void run() {
TreeNode visualTreeNode[] = new TreeNode[path.length + 1];
Node n = leafNode;
for (int i = path.length; i >= 0; i--) {
if (n == null) {
return;
}
visualTreeNode[i] = Visualizer.findVisualizer(n);
n = n.getParentNode();
}
view.getOutline().expandPath(new TreePath(visualTreeNode));
}
});
}
}
In einer Schleife wird zunächst versucht mit der Hilfsmethode NodeOp.findPath den Knoten zu finden, der durch die Kaskade von String-Namen aufzufinden ist. Wird dieser Knoten nicht gefunden, gibt es eine NodeNotFoundException. Diese Exception liefert mit getClosestNode, den Node, der im Pfad als letztes gefunden wurde (wenn wirklich nichts passt, dann ist es die rootNode).
Nun muss in den EventDispatcher-Thread gewechselt werden. In dem TreeNode-Array werden nun alle TreeNodes erfasst, die den Pfad vom letzten Node bis zur root-Node gehen. Auch hier braucht man die Visualizer-Klasse, um aus dem realen Node den VisualizerNode zu erhalten. Wenn das Array zusammen ist, wird expandPath des Outlines aufgerufen. Der TreePath nimmt das Array der VisualizerNodes auf (TreePath arbeitet ja nicht mit String-Pfaden, sondern mit Objekten – und hier immer mit den VisualizerNode-Objekten).
Das sollte es auch schon gewesen sein. So kann man OutlineView-Hierarchien selektiv expandieren. In dem nächsten Teil der Serie werden die Besonderheiten bei einem BeanTreeView beschrieben. Da ist das Verfahren etwas komplizierter.
Moin!
Es ist von grundlegender Bedeutung, jedes Jahr mehr zu lernen als im Jahr davor.
Peter Ustinov
Ich wünsche allen Mitlesern einen guten Rutsch in das Neue Jahr, einen guten Start und erfolgreiches Jahr 2011.
Beste Grüße,
Josch.
Dieser Teil der Serie beschäftigt sich mit dem Problem, alle geöffneten (expandierten) Nodes eines OutlineView zu ermitteln und zu speichern.
Das OutlineView bietet glücklicherweise passende Methoden, um an grundlegende Informationen zu kommen, ohne eine Sub-Klasse erzeugen müssen. Das BeanTreeView ist da etwas komplizierter zu handhaben (dazu aber in einem späteren Teil). Zunächst ist es wichtig zu verstehen, dass Nodes in Views nicht die selben Nodes sind, die man in einem Node-Modell erzeugt. Alle View verwenden Node-Wrapper, so genannte Visualizer Nodes, um eine bessere Performance zu ermöglichen.
Wenn man direkt mit Views und den Nodes arbeitet, muss man also immer zwischen den Wrapper und dem realen Node-Objekt konvertieren. Dafür gibt es die Hilfsklasse Visualizer.
Außerdem sind die Outline- und TreeViews ganz klassische Swing-Komponenten, die mit JTree als Baum-Komponente arbeiten. Und da kommt man zwangsläufig mit TreePath in Kontakt.
Mit diesem Wissen kann man nun folgende Methode zusammenbauen:
/**
* Ermittelt alle geöffneten Pfade als String-Liste
* @param view OutlineView, auf dem gearbeitet werden soll.
* @param rootNode Die root-Node des Modells
* @return Eine Liste von String-Arrays, die die geöffneten Pfade repräsentieren.
*/
public static List<String[]> getExpandedPaths(OutlineView view, Node rootNode) {
List result<String[]> = new ArrayList<String[]>();
TreeNode rtn = Visualizer.findVisualizer(rootNode);
TreePath tp = new TreePath(rtn); // Get the root
for (TreePath ep : view.getOutline().getOutlineModel().getTreePathSupport().getExpandedDescendants(tp)) {
Node en = Visualizer.findNode(ep.getLastPathComponent());
String[] path = NodeOp.createPath(en, rootNode);
result.add(path);
}
return result;
}
Zunächst wird die VisualizerNode zur Root-Node gesucht und als TreePath kodiert. Dann holt man, über die überaus nützliche Methode getExpandedDescendants, alle TreePaths (ausgehend von der Root-Node) zu den Nodes die ausgeklappt wurden.
Zu jedem gefundene TreePath wird nun die VisualizerNode per getLastPathComponent ermittelt und sofort zur Modell-Node konvertiert.
In der Klasse NodeOp gibt es eine Hilfsmethode, die alle Node.getName()-Strings als Array von dieser Node zur Root-Node erzeugt. Dieses Array wird der Ergebnis-Liste hinzugefügt.
Das Ergebnis kann man nun in eine Properties-Collection verfrachten und diese gemeinsam mit anderen Eigenschaften des OutlineViews speichern. Dazu gab es schon einen Blog-Artikel von mir. Hier ein Beispiel einer Konvertierung:
/**
* Konvertiert die Path-Arrays in Zeichenketten mit dem Trenner aus delim und speichert
* diese Pfade in den Properties mit einem gewünschten Präfix ab.
* @param paths Liste der Path-Arrays
* @param delim Trenner für den Pfad (z.B. "/")
* @param props Properties-Container, in dem die Pfade gespeichert werden
* @param prefix Präfix für die Keys.
*/
public static void storePaths (List<String[]> paths, String delim, Properties props, String prefix) {
if (prefix == null) prefix = "";
props.put(prefix + "ExpandedCount", Integer.toString(paths.size()));
for (int i = 0; i < paths.size(); i++) {
String[] path = paths.get(i);
StringBuilder b = new StringBuilder();
for (int j = 0; j < path.length; j++) {
if ( j > 0 ) b.append(delim);
b.append(path[j]);
}
props.put(prefix + "ExpandedPath." + i, b.toString());
}
}
Im nächsten Teil der Serie werden die Pfade wieder eingelesen und alle Nodes ohne Benutzerinteraktion wieder expandiert.
Moin!
Da das Outline mit ETable auch komplett unabhängig von der NetBeans Platform eingesetzt werden kann, wurde bei der Speicherung der Settings des ETable darauf verzichtet mit NbPreferences zu arbeiten.
NbPreferences ist aber der bevorzugte Speicherplatz in Plattform, wenn es um solche Einstellungen geht. Ich habe mir für dieses “Problem” zwei kleine Hilfsmethoden gebastelt.Im Prinzip ist es nichts besonderes, aber viele wissen nicht mal, dass Outline und OutlineView überhaupt die Benutzereinstellungen speichern können.
Folgende zwei Hilfsmethoden nutze ich:
public static Properties readFromPreferences (Class clazz, String prefix) {
boolean found = false;
Properties ui = new Properties();
try {
Preferences p = NbPreferences.forModule(clazz).node(clazz.getName());
for ( String key : p.keys() ) {
if ( prefix == null || key.startsWith(prefix) ) {
ui.put(key, p.get(key, null));
found = true;
}
}
} catch (BackingStoreException ex) {
Exceptions.printStackTrace(ex);
}
return found ? ui : null;
}
public static void storeToPreferences (Class clazz, String prefix, Properties props) {
try {
Preferences p = NbPreferences.forModule(clazz).node(clazz.getName());
for (String key : props.stringPropertyNames()) {
if ( prefix == null || key.startsWith(prefix) ) {
p.put(key, props.getProperty(key));
}
}
p.flush();
} catch (BackingStoreException ex) {
Exceptions.printStackTrace(ex);
}
}
Wenn ich nun die Settings eines OutlineView speichern will, ist der Aufruf recht einfach:
Properties uiProps = new Properties();
view.writeSettings(uiProps, "contactsView");
storeToPreferences(MyTestCaseUIClass.class, "contactsView", uiProps);
Das Laden ist auch nicht komplizierter:
Properties props = readFromPreferences(MyTestCaseUIClass.class, "contactsView");
if ( props != null ) {
view.readSettings(props, "contactsView");
}
So kann man die Eigenschaften recht einfach in den NbPreferences hinterlegen. Wer TopComponents mit der Annotation @ConvertAsProperties nutzt, muss die Konvertierung natürlich nicht durchführen. Die Eigenschaften können 1:1 übernommen werden. In komplizierten UIs, die deklarativ zusammengesteckt werden und man mit JPanel-Klassen arbeitet, kann man das nur schwerlich nutzen.
Beste Grüße,
Josch.
Generell muss man verstehen, dass in der NetBeans Plattform nichts unnötigerweise in den Arbeitsspeicher geladen wird. Eine RCP Applikation hat immer mit Ressourcen zu kämpfen und vieles ist auf Optimierung ausgelegt. So auch der Aufbau der Node-Hierarchien.
Jedes Node verwaltet mit seinem Children-Container seine Kind-Elemente. Diese Container sind als Factory-Klassen aufgebaut, die nur auf Anforderung die Nodes einer tieferen Ebene erzeugen sollen. Beispielsweise dann, wenn der Anwender einen Hauptknoten in einem View per Mausklick aufklappt (Expansion). Erst dann wird der Children-Container angewiesen weitere Nodes zu erzeugen. Diese Art der On-Demand-Creation ist notwendig, weil es sehr viele Knotenhierarchien gibt, die nur in Ausschnitten von Anwender angeschaut werden. Große Teile von Hierarchien sollen gar nicht dargestellt werden. Ein sehr anschauliches Beispiel ist ein Dateisystem mit Ordnern und Dateien. Es macht gar keinen Sinn hunderttausende Dateien vorab in eine Knotenhierarchie zu laden. Der Arbeitsspeicher würde häufig nicht ausreichen und die Zeit für den Aufbau so einer Hierarchie würde den Anwender vor eine Geduldsprobe setzen.
So steht auf dem ersten Blick der Wunsch, Nodes programatisch aufzuklappen, der Optimierung der Plattform in den meisten Fällen diametral entgegen.
Aber auch weitere Überlegungen zeigen, dass Nodes, in ihrer Rolle als Präsentationsschicht, nicht einfach angesteuert werden können. Nodes sollen von Ihren Daten (bzw. dem Datenmodell) möglichst abgekoppelt sein. Nodes können sogar mehr als ein Datenobjekt beinhalten (aggregieren). Auf Modellebene sollte das präsentierende Node unbekannt sein. Ansonsten könnte man Präsentationsschichten nicht auswechseln.
Nicht als letzten Punkt in der Betrachtung macht es die Plattform einem nicht einfacher, da die Steuerung der Darstellung von Knotenhierarchien ausschließlich von Views verwaltet werden. Das führt immer zu Verwirrung bei Einsteigern, die solche Aufgaben im ExplorerMananger suchen (der für die Selektion und den Kontext zuständig ist).
Das einfachste Beispiel, die erste Hierarchieebene zu öffnen, ist dieses:
for (Node node : root.getChildren().getNodes()) {
view.expandNode(node);
}
Allerdings hat dieser simple Aufruf von Children.getNodes() seine Tücken. Es kann sein, dass in dem Array nicht alle Nodes fertig initialisiert sind. Wenn der Children-Container eine spezielle Implementation hat, in dem Nodes asynchron initialisiert werden, kann es zu Problemen kommen. Bei Children-Nodes der Root-Node fällt das so gut wie nie auf, weil meistens das View schon von Haus die erste Ebene aufklappt, aber man könnte so auf Nummer sicher gehen:
for (Node node : root.getChildren().getNodes(true)) {
view.expandNode(node);
}
Die Dokumentation von getNodes(boolean optimalResult) liefert folgende Information zu dem Parameter:
This method is usefull if you need a fully initialized array of nodes for things like MenuView, node navigation from scripts/tests and so on. But in general if you are trying to get useful data by calling this method, you are probably doing something wrong.
Leider scheint das nur eine schnelle einfache Lösung zu sein. Denn mit Children-Containern die nicht nur eine asynchrone Initialisierung der Nodes haben, sondern die Nodes an sich schon asynchron erzeugen, verhakeln sich mit dem direkten view.expandNode (node) Aufruf.
Wenn man getNodes(true) verwendet, wird von der API der Aufruf in einer asynchronen ChildFactory auf das endgültige Ergebnis gewartet. Der Thread wird damit geblockt:
@Override
public Node[] getNodes(boolean optimalResult) {
Node[] result = super.getNodes();
if (optimalResult) {
// The getNodes() call above called addNotify() and started the task
// for the first time if needed.
task.waitFinished();
result = super.getNodes();
}
return result;
}
Wenn man also recht einfach Knoten öffnen möchte, sollte man, wenn die ChildFactory verwendet wird, dass asynchron-Kennzeichen deaktivieren. Allerdings gibt es eine einfache Möglichkeit das Problem abzuwenden. Man muss nur jeden expandNode-Aufruf getrennt in den EventQueue schicken:
for (final Node n : root.getChildren().getNodes(true)) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
view.expandNode(n);
}
});
}
Bis jetzt ist diese Funktionalität relativ statisch. Man kann nun von einem Node (was hier immer root genannt wurde), alle Unterknoten öffnen. Das wird 90% aller Anwendungsfälle zutreffen. Denn wie Eingangs erwähnt, will man häufig nicht komplette Hierarchien öffnen.
Für sehr kleine strukturelle Darstellungen kann das aber trotzdem gewollt sein. Da dies nun auch nicht blockierend funktioniert, kann der Wunsch aufkommen einen ganzen Baum aufklappen zu wollen. Den obigen Code-Abschnitt brauchen wir nur als rekursive Methode implementieren:
private void recursiveExpandRunner(final OutlineView view, Node root) {
for (final Node n : root.getChildren().getNodes(true)) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
view.expandNode(n);
recursiveExpandRunner(view, n);
}
});
}
}
Aus nachvollziehbaren Gründen sollte man das nicht auf einer Node-Hierarchie anwenden, die zum Beispiel ein Dateisystem modeliert. Bei so tiefen Hierarchien (mit immensen Kosten beim Auslesen), möchte man vielleicht die Suchtiefe einschränken:
private void recursiveExpandRunner(final OutlineView view, Node root, final int currentLevel, final int maxLevel) {
if (currentLevel == maxLevel) return;
for (final Node n : root.getChildren().getNodes(true)) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
view.expandNode(n);
recursiveExpandRunner(view, n, currentLevel+1, maxLevel);
}
});
}
}
Beim ersten Aufruf sollte currentLevel == 0 sein. Der Parameter maxLevel sollte > 0 sein, oder für komplette Tiefensuche < 0 (weil dann die vorzeitige Abbruchbedingung nie erfüllt sein kann).
recursiveExpandRunner(view, root, 0, 1); // Eine Ebene
recursiveExpandRunner(view, root, 0, 2); // Zwei Ebenen
recursiveExpandRunner(view, root, 0, -1); // Alle Ebenen öffnen
Dieser Teil der Serie arbeitet komplett auf View- und Präsentationsschicht-Ebene. Es wird komplett auf die Interpretation der der Inhalte von Nodes verzichtet. D.h. es wird nicht anhand von Daten die Darstellung des Baums manipuliert, sondern völlig unabhängig davon. In einem späteren Teil wird darauf eingegangen, wie man Nodes anhand ihrer zu repäsentierenden Daten identifiziert und gezielt ansteuert.
Moin!
Die wichtigsten Views für Nodes sind das ListView, TableView, TreeTableView, BeanTreeView und das OutlineView. Wobei das OutlineView die modernste Variante ist und das BeanTreeView sowie das ListView, TableView und TreeTableView komplett ersetzen kann.
In den Trainings empfehle ich immer wieder gleich das OutlineView zu verwenden, da man sich dort durch kleinere Parameter-Umstellungen entscheiden kann, welche Ansichten das OutlineView verwenden soll. Das TableView und TreeTableView sollte man nicht mehr verwenden, weil zu viele nicht gelöste Bugs existieren.
In der Praxis wird man trotzdem immer wieder BeanTreeView oder OutlineView verwenden. In dieser Artikel-Serie möchte ich deswegen für das BeanTreeView und OutlineView ein paar Techniken beschreiben, wie man die hierarchische Darstellung von Nodes beeinflussen kann. Folgende Fragestellung kommt in den Trainings und Foren immer wieder auf:
- Wie kann man bestimmte Knotenebenen auf- und zuklappen?
- Wie ist es möglich die geöffneten und selektierten Knoten zu speichern und wiederherzustellen?
- Wie kann ich die Darstellung der Nodes und Views manipulieren?
Die Serie behandelt diese Fragen jeweils für das BeanTreeView und das OutlineView.
Viel Spaß damit, ich freue mich auf Fragen und Anregungen.
Beste Grüße,
Josch.
Moin!
In meinen RSS-Feeds ist dieses nette kleine Projekt aufgetaucht, welches die Erstellung von Swing-GUIs inklusive Binding deutlich vereinfachen will: Swing JavaBuilder.
Dieses Framework bietet auf der Basis der DSL-Sprache YAML einen Builder, der Swing GUIs erzeugt. Das Projekt hat eine hohe Entwicklungsaktivität, verfügt über ein Stable Build in Version 1.1 und ein aktuelles Dev-Build.
Wie kompakt man Swing GUIs schreiben kann, zeigt dieses kurze Beispiel in Java:
ResourceBundle bundle = ResourceBundle.getBundle("Resources");
JButton button = new JButton();
button.setName("okButton");
button.setText(bundle.getString("button.ok"));
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//execute the save method
save();
}
});
wird zu diesem Einzeiler:
Einen sehr schönen Einstieg erhält man in diesem PDF-Buch: Swing JavaBuilder: Maximum productivity with minimum code (Release 1.1)
Beste Grüße,
Josch.
Moin!
Das UML Plugin in NetBeans erfährt zur Zeit Höhen und Tiefen. Aus Qualitätsgründen und fehlenden Ressourcen wurde es schon zu Sun’s Zeiten in den Contribution Bereich verschoben. Aus diesen Sourcen lässt sich zur Zeit auch für aktuellen IDE Build ein Plugin erstellen.
Leider war die alte/neue Community zum UML Plugin nicht sonderlich erfolgreich, das Projekt voran zu treiben. Wie auch immer die Gründe der Mitglieder war, die kritische Masse für eine erfolgreiche Community war wohl noch nicht erreicht. Trotzdem ist die Verfügbarkeit eines UML-Tools sehr wichtig. Darum haben einige Mitglieder des NetBeans Dreamteams angeregt, noch mal einen Nachbrenner aufzulegen. Zeitgleich hat aber David Bastow selbst passend zu Weihnachten den Ball wieder zum Rollen gebracht:
I’ve created a diagram that represents my ideas of how the next incarnation of the UML module might fit together. It isn’t so much a software hierarchy as a description of what has been forming in my mind over the past couple of months. It is also VERY incomplete.
Die Wiki-Seiten werden aktualisiert und es werden Wünsche und Anfragen zum UML-Projekt unter folgender E-Mail Adresse angenommen: developer@uml.netbeans.org. Einstiegspunkt sollte auch die Talk Seite zu UMLRewrite sein.
Beste Grüße,
Josch.
Allen Bloglesern wünsche ich eine wunderschöne Weihnacht und ein Frohes Fest!
Mboni Chrismen! Gezur Krislinjden! Idah Saidan Wa Sanah Jadidah! Feliz Navidad! Shenoraavor Nor Dari yev Pari Gaghand! Zalig Kerstfeest en Gelukkig nieuw jaar! Boas Festas e Feliz Ano Novo! Tchestita Koleda! Feliz Navidad! Kung His Hsin Nien bing Chu Shen Tan! Glædelig Jul! Merry Christmas! Hyvaa joulua! Joyeux Noel! Kala Christouyenna! Mele Kalikimaka! Shuvo Naba Barsha! Selamat Hari Natal! Idah Saidan Wa Sanah Jadidah! Cristmas-e-shoma mobarak bashad! Nollaig Shona Dhuit Gledileg Jol! Buone Feste Natalizie! Shinnen omedeto! Sung Tan Chuk Ha! Sretan Bozic! Nathar Puthu Varuda Valthukkal! Linksmu Kaledu! Selamat Hari Natal! Sreken Bozhik! Festusu Natale! Okresmesa ombwa! Meri Kirihimete! Vrolijk Kerstfeest en een Gelukkig Nieuwjaar! Gledelig Jul! Naya Saal Mubarak Ho! Feliz Navidad y un Venturoso Año Nuevo! Wesolych Swiat Bozego Narodzenia! Feliz Natal! Sarbatori vesele! Pozdrevlyayu s prazdnikom Rozhdestva is Novim Godom! God Jul and Ett Gott Nytt År! Schöni Wienacht Hristos se rodi! Sretan Bozic! Vesele Bozicne. Screcno Novo Leto! Feliz Navidad! Sawadee Pee Mai! Prejeme Vam Vesele Vanoce a stastny Novy Rok! Noeliniz Ve Yeni Yiliniz Kutlu Olsun! Srozhdestvom Kristovym! Kellemes Karacsonyi unnepeket! Chung Mung Giang Sinh!
Beste und weihnachtliche Grüße!
Josch.
Moin!
Nach einer sehr kurzen Beta-Phase ist VirtualBox 4.0 in der finalen Version fertig. Die Downloads finden sich im Technetwork von Oracle.
Das Online-Manual beschreibt in einem Change-Log die Änderungen in 4.0.
This version is a major update. The following major new features were added:
- Reorganization of VirtualBox into a base package and Extension Packs; see the section called “Installing VirtualBox and extension packs”
- New settings/disk file layout for VM portability; see the section called “Where VirtualBox stores its files”
- Major rework of the GUI (now called “VirtualBox Manager”):
- Redesigned user interface with guest window preview (also for screenshots)
- New “scale” display mode with scaled guest display; see the section called “Resizing the machine’s window”
- Support for creating and starting
.vboxdesktop shortcuts (bug #1889)- The VM list is now sortable
- Machines can now be deleted easily without a trace including snapshots and saved states, and optionally including attached disk images (bug #5511; also,
VBoxManage unregistervm --deletecan do the same now)- Built-in creation of desktop file shortcuts to start VMs on double click (bug #2322)
- VMM: support more than 1.5/2 GB guest RAM on 32-bit hosts
- New virtual hardware:
- Intel ICH9 chipset with three PCI buses, PCI Express and Message Signaled Interrupts (MSI); see the section called “”Motherboard” tab”
- Intel HD Audio, for better support of modern guest operating systems (e.g. 64-bit Windows; bug #2785)
- Improvements to OVF support (see the section called “Importing and exporting virtual machines”):
- Open Virtualization Format Archive (OVA) support
- Significant performance improvements during export and import
- Creation of the manifest file on export is optional now
- Imported disks can have formats other than VMDK
- Resource control: added support for limiting a VM’s CPU time and IO bandwidth; see the section called “Limiting bandwidth for disk images”
- Storage: support asynchronous I/O for iSCSI, VMDK, VHD and Parallels images
- Storage: support for resizing VDI and VHD images; see the section called “VBoxManage modifyhd”.
- Guest Additions: support for multiple virtual screens in Linux and Solaris guests using X.Org server 1.3 and later
- Language bindings: uniform Java bindings for both local (COM/XPCOM) and remote (SOAP) invocation APIs
In addition, the following items were fixed and/or added:
- VMM: Enable large page support by default on 64-bit hosts (applies to nested paging only)
- VMM: fixed guru meditation when running Minix (VT-x only; bug #6557)
- VMM: fixed crash under certain circumstances (Linux hosts only, non VT-x/AMD-V mode only; bugs #4529 and #7819)
- GUI: add configuration dialog for port forwarding in NAT mode (bug #1657)
- GUI: show the guest window content on save and restore
- GUI: certain GUI warnings don’t stop the VM output anymore
- GUI: fixed black fullscreen minitoolbar on KDE4 hosts (Linux hosts only; bug #5449)
- BIOS: implemented multi-sector reading to speed up booting of certain guests (e.g. Solaris)
- Bridged networking: improved throughput by filtering out outgoing packets intended for the host before they reach the physical network (Linux hosts only; bug #7792)
- 3D support: allow use of CR_SYSTEM_GL_PATH again (bug #6864)
- 3D support: fixed various clipping/visibility issues (bugs #5659, #5794, #5848, #6018, #6187, #6570)
- 3D support: guest application stack corruption when using glGetVertexAttrib[ifd]v (bug #7395)
- 3D support: fixed OpenGL support for libMesa 7.9
- 3D support: fixed Unity/Compiz crashes on natty
- 2D Video acceleration: multimonitor support
- VRDP: fixed rare crash in multimonitor configuration
- VRDP: support for upstream audio
- Display: fixed occasional guest resize crash
- NAT: port forwarding rules can be applied at runtime
- SATA: allow to attach CD/DVD-ROM drives including passthrough (bug #7058)
- Floppy: support readonly image files, taking this as the criteria for making the medium readonly (bug #5651)
- Audio: fixed memory corruption during playback under rare circumstances
- Audio: the DirectSound backend now allows VMs to be audible when another DirectSound application is active, including another VM (bug #5578)
- EFI: support for SATA disks and CDROMs
- BIOS: reduce the stack usage of the VESA BIOS function #4F01 (Quake fix)
- OVF/OVA: fixed export of VMs with iSCSI disks
- Storage: Apple DMG image support for the virtual CD/DVD (bug #6760)
- Linux host USB support: introduced a less invasive way of accessing raw USB devices (bugs #1093, #5345, #7759)
- Linux hosts: support recent Linux kernels with CONFIG_DEBUG_SET_MODULE_RONX set
- Guest Additions: Shared Folders now can be marked as being auto-mounted on Windows, Linux and Solaris guests
- Linux Additions: Shared Folders now support symbolic links (bug #818)
- Linux Additions: combined 32-bit and 64-bit additions into one file
- Windows Additions: automatic logon on Windows Vista/Windows 7 is now able to handle renamed user accounts; added various bugfixes
Beste Grüße,
Josch.
Moin!
Erst hatte es mich irritiert, dann habe ich es erst verstanden. Seit einigen Dev-Builds kann NetBeans in der IDE die Bundle.properties Texte direkt im Editor als Code-Fold anzeigen.
Das I18N-Folding wird in den Optionen unter Miscellaneous -> Java Ext Editor eingestellt.
So sieht es im Editor aus (mit Tooltip-Hinweis auf den eingeklappten Quelltext):
Die Einstellungen (auch für weitere neue Fold-Varianten) sind zur Zeit hier zu erreichen:
Ich finde das schon ziemlich genial und macht den Quelltext übersichtlicher.
Beste Grüße,
Josch.
Moin!
Schon seit zwei Monaten (und damit bestimmt keine News) ist das Compiler Preview von Visage verfügbar. Visage bietet das als Sprache, was JavaFX abhanden gekommen ist.
Visage entspricht dem Sprachlevel von JavaFX Script in der Version 1.3.1. Dazu kommt das Schlüsselwort default für Properties. Mit default-Properties kann man sich in der DSL Syntax einiges an Tipparbeit sparen, weil der Name des default-Property weggelassen werden darf.
Beispiel:
Stage {
scene: Scene {
contents: Rectangle {
width: 100
height: 100
}
}
}
Der obige Standard-JavaFX Code reduziert sich dann in folgende Schreibweise:
Stage {
Scene {
Rectangle {
width: 100
height: 100
}
}
}
In Stage ist scene das default-Property und in Scene ist es content. Deklariert wird es folgendermaßen:
public class Stage {
public-init default var scene:Scene;
}
public class Scene {
public-init default var content:Node[];
}
Mehrere Defaults sind leider nicht möglich, weitere Infos zu den Defaults, finden sich hier.
Beste Grüße,
Josch.
Moin!
Vor einiger Zeit habe ich mit das mal auf Wiedervorlage gelegt, die GMail API for Java. Bei dieser API, gehostet bei Google Code handelt es sich um eine recht einfache API für den Zugriff auf GMail-Konten. Das Projekt von Tomas Varaneckas ist als Maven-Projekt aufgebaut und steht unter der Apache License 2.0.
Die Artifact-Koordinate für das GMail4j ist:
<groupId>com.googlecode</groupId>
<artifactId>gmail4j</artifactId>
<version>0.3</version>
Man muss aber ein spezielles Repository einbinden:
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2</url>
</repository>
<!-- other repositories -->
</repositories>
Hier ein kleines Beispiel:
final ImapGmailClient client = new ImapGmailClient(ImapGmailLabel.SENT_MAIL);
final ImapGmailConnection connection = new ImapGmailConnection();
try {
connection.setLoginCredentials(conf.getGmailCredentials());
if (conf.useProxy()) {
connection.setProxy(conf.getProxyHost(), conf.getProxyPort());
connection.setProxyCredentials(conf.getProxyCredentials());
}
log.debug("Getting unread messages");
client.setConnection(connection);
final List<GmailMessage> messages = client.getUnreadMessages();
for (GmailMessage message : messages) {
log.debug(message);
}
assertNotNull("Messages are not null", messages);
} catch (final Exception e) {
log.error("Test Failed", e);
fail("Caught exception: " + e.getMessage());
} finally {
if (connection.isConnected()) {
connection.disconnect();
}
}
Oder simpler als RSS-Feed Abfrage:
GmailClient client = new RssGmailClient();
GmailConnection connection = new HttpGmailConnection(LoginDialog.getInstance().show("Enter Gmail Login"));
client.setConnection(connection);
final List<GmailMessage> messages = client.getUnreadMessages();
for (GmailMessage message : messages) {
System.out.println(message);
}
Es gibt auch inzwischen ein NetBeans IDE Plugin, das GMail-Konten Notifications anzeigt. Das Plugin nutzt allerdings nicht die GMail-API sondern JavaMail.
Beste Grüße,
Josch.
Moin!
Microsoft selbst hat eine frei verfügbare EWS API für Java veröffentlicht, die es ermöglicht auf Microsoft Exchange Server via Exchange Web Service API (EWS) zuzugreifen. Die API soll die gleiche Funktionalität bieten, wie die schon seit Mitte November existierende EWS Managed API 1.1 für das .NET Framework.
Die Lizenz der Bibliothek steht unter Microsoft Public License (Ms-PL) und ist damit inkompatibel zur GPL – einer kommerziellen Nutzung steht aber nichts im Wege.
Der Quelltext der API steht mit dem Download des ZIP-Archivs zur Verfügung.
Ein kleines 20-seitiges RTF-Dokument “Getting Started” soll bei dem Start mit der Bibliothek helfen. Dazu gehören auch passende Klassendiagramme und Quelltextbeispiele.
Um zum Beispiel ein Wiederholungstermin anzulegen, wird dieser Quelltext vorgeschlagen:
Appointment appointment = new Appointment(service);
appointment.setSubject("Recurrence Appointment for JAVA XML TEST");
appointment.setBody(MessageBody.getMessageBodyFromText("Recurrence Test Body Msg"));
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startDate = formatter.parse("2010-05-22 12:00:00");
Date endDate = formatter.parse("2010-05-22 13:00:00");
appointment.setStart(startDate);//new Date(2010-1900,5-1,20,20,00));
appointment.setEnd(endDate); //new Date(2010-1900,5-1,20,21,00));
formatter = new SimpleDateFormat("yyyy-MM-dd");
Date recurrenceEndDate = formatter.parse("2010-07-20");
appointment.setRecurrence(new Recurrence.DailyPattern(appointment.getStart(), 3));
appointment.getRecurrence().setStartDate(appointment.getStart());
appointment.getRecurrence().setEndDate(recurrenceEndDate);
appointment.save();
Auch die Suche nach Elementen erscheint recht durchgängig:
public void findItems() {
ItemView view = new ItemView(10);
view.getOrderBy().add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
view.setPropertySet(
new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived
));
FindItemsResults findResults =
service.findItems(WellKnownFolderName.Inbox,
new SearchFilter.SearchFilterCollection(
LogicalOperator.Or,
new SearchFilter.ContainsSubstring(ItemSchema.Subject, "EWS"),
new SearchFilter.ContainsSubstring(ItemSchema.Subject, "API")
), view);
System.out.println("Total number of items found: " + findResults.getTotalCount());
for (Item item : findResults) {
System.out.println(item.getSubject());
System.out.println(item.getBody());
// Do something with the item.
}
}
Im Großen und Ganzen sieht die API durchdacht und sauber aus und steht auch damit in direkter Konkurrenz zum kommerziellen Exchange Connector von Moyosoft, den ich vor kurzem getestet hatte.
Beste Grüße,
Josch.
Moin!
Ich habe nun die zweite Abfrage aus der Idee des letzten Artikel dem CyStats Plugin hinzugefügt und das gleich mal Life auf meinem Blog getestet.
Das Ergebnis ist nun deutlich realistischer, was die Anzahl der Besuche betrifft. Nun wird nicht jeder Bounce (also einmaliger Einsprung) als Besuch gewertet. Das ist zwar eine pessimistische Darstellung von Besuchen (schließlich kommen viele Leser über News Feeds Aggregatoren und wollen gerade nur den einen Artikel lesen), aber erheblich realistischer als die bisherige Kalkulation.Ich konnte den Patch auch recht optimiert implementieren, in dem zunächst immer mit der s1-Abfrage auf “Anzahl Hits” == 2 getestet wird.
Erst wenn das wahr ist, prüfe ich auf vorhergehende Hits mit der s2-Abfrage. Damit leidet die Performance nicht allzu sehr, weil nun für die endgültige Besuchszählerberechnung zwei statt einer Abfrage benötigt werden. Üblicherweise sind die überwiegende “Anzahl an Hits” <> 2 in dem s1-Abfrage Zeitraum (und die zweite Abfrage erübrigt sich dann).
Ich habe den Patch als Unified Diff angehangen, das gegen CyStats 0.9.8 erzeugt wurde. Die Datei die gepatched wurde ist: /cystats/includes/cystats.class.php
Da die Homepage des Plugin Autors nicht mehr existiert, versuche ich ihn mal anders zu kontaktieren. Evtl. kann man das Patch (allerdings wohl dann in WordPress konfigurierbar) hinzufügen. Konfigurierbar deswegen, damit Anwender sich entscheiden können, die neue pessimistische Berechnung verwenden zu können (oder auch nicht, wenn man hohe Ergebnisse mag
).
Beste Grüße,
Josch
Moin!
Ich habe mal wieder ein spannendes Projekt gefunden. Die API svg2java ermöglicht es aus SVG Dateien Java2D Quelltext zu generieren.
Das unter New BSD License stehende Projekt von Bernd Rosstauscher ermöglicht es (100% korrekt validierbare) SVG Dateien in die Befehlsaufrufe von Graphics2D zu konvertieren und berücksichtigt dabei auch Text. Eingefügte Grafiken werden allerdings noch nicht erkannt.
Die erzeugten Klassen (Beispiel) sind nicht wirklich sehr hübsch, aber grafische Primitive als statischen Quelltext kann man kaum besser generieren.
private void paintShapeNode_0_0_0_8(Graphics2D g) {
GeneralPath shape15 = new GeneralPath();
shape15.moveTo(9.477582, 34.644264);
shape15.lineTo(55.082695, 34.644264);
shape15.curveTo(56.95121, 34.644264, 58.477207, 36.92005, 58.477207, 39.706642);
shape15.lineTo(58.477207, 56.782074);
shape15.curveTo(40.852654, 53.53046, 23.379293, 49.03524, 6.0828896, 49.45096);
shape15.lineTo(6.0828896, 39.706646);
shape15.curveTo(6.0828896, 36.92005, 7.6088877, 34.644264, 9.477403, 34.644264);
shape15.lineTo(9.477581, 34.644264);
shape15.closePath();
g.fill(shape15);
}
Man sieht aber, dass man schnell an das 64kB Limit von Java-Klassen kommt. Da hat das Projekt bestimmt noch Potenzial den Code besser aufzubereiten.
Warum sollte man nicht gleich Apache Batik nehmen, um SVG Grafiken anzuzeigen?
Nicht immer will man die komplette Batik-Suite mit in eine eigene Anwendung bringen. Der SVG-Renderer ist durch seinen Interpreter naturgemäß auch langsamer. Das Projekt will auch einen Konverter für die Android Graphics Plattform anbieten, wo man ebenfalls nicht Batik zur Verfügung hat.
Außerdem kann man mit so konvertierten SVG Grafiken erheblich einfacher Swing-Komponenten erzeugen, die skalierbar sind und wie moderne Widgets aussehen. Siehe auch mein Feature zum Harmonic Code Blog.
Beste Grüße,
Josch.
Moin!
In dem vorherigen Artikel habe ich ein paar Kritikpunkte aufgeschrieben, warum die Besucherzahl von CyStats für WordPress zu hoch berechnet wird.
Es gab zwei grundlegende Probleme:
- Neubesuche werden nicht von Wiederkehrern unterschieden
- Einmalige Aufrufe von Seiten werden als Besucher gezählt
Wenn man Punkt 1 ignoriert (weil eine Korrektur eine massive Anpassung von CyStats bedeuten würde), muss Punkt 2 auf jeden Fall angegangen werden.
Folgende Skizze habe ich für die Erklärung eines Vorschlages vorbereitet:
Für diese Berechnung will ich maximal zwei Abfragen auf die Datenbank loslassen. Diese Abfragen unterscheiden sich nur um die Betrachtung von zwei fixen Zeitabschnitten, die direkt aufeinander folgen.
Die Abfrage s1 schaut eine feste Zeit in die Vergangenheit (ts1) und zählt bis zur aktuellen Zeit (ta) die Vorkommen eines bestimmten Users. Die Abfrage s2 zählt vom Zeitabschnitt ts1 bis ts2 die Seitenklicks des selben Users.
Im Fall 1 erhalte ich den ersten Klick, berechnet durch s1 mit dem Ergebnis = 1. Die Abfrage s2 findet keine Klicks.
Im Fall 2 hat der Benutzer in der gewünschten Zeit den zweiten Klick gemacht. Damit kann man den User als Besucher werten. Die Abfrage s2 muss weiter den Wert 0 liefern.
Im Fall 3 wurden weitere Klicks gemacht (als mehr als 2 Klicks für die Abfrage s1). Diese dürfen den Besucherzähler nicht erhöhen. Die Abfrage s2 muss weiter den Wert 0 liefern.
Nun kommt der Fall 4, der einen Besuch über längere Zeiträume nicht mehrfach zählen darf. Denn zu einem älteren Zeitpunkt wurde ja zu einem delta schon ein Besuch ermittelt (siehe Pfeil neben Fall 4). Deswegen muss generell für s1>0 und s2>0 gelten, dass kein Besuch erfasst wird.
Die s1 Abfrage entspricht weiterhin der bisherigen CyStats Abfrage. Es wird nun nicht mehr bei Ergebnis = 0 der Besucherzähler hochgezählt sondern nur bei Ergebnis = 2. Außerdem muss die Vorbedingung von s2 = 0 erfüllt sein.
Kritik: Der Fall 4 könnte für einen User mehrfach auftreten und man hätte diese Tatsache nie als Besuch bewertet. Damit wäre diese Idee der Berechnung eher eine pessimistische Ermittlung von Besucherzahlen,
Es gäbe noch eine andere Möglichkeit, Serien von Klicks als Besuche zu erkennen. Man müsste alle Klicks des Besuchers ermitteln und jede Abschnittsdauer zwischen jedem Klick in einer Schleife berechnen. Dazu müsste man eine Anzahl ermitteln, wie viel Abschnitte < Delta sind. Das Ergebnis entscheidet über Klick oder Klick+Besuch. Damit würde der obige Fall 4 in Serie als Besucher zu erkennen. Aber diese Abfrage erscheint mit mit der zusätzlichen Schleife langsamer als zwei Abfragen zu festen Zeitpunkten.
Beste Grüße,
Josch
Moin!
Ich verwende CyStats als Analysetool für Hits und Besuche. Allerdings erscheint (nicht nur) mir die Anzahl der Besuche ggü. den Hits einfach zu hoch.
Die Berechnung der Besuche erfolgt so (kleiner Ausschnitt, gekürzt):
$delta=$info['timestamp']-get_option('cystats_visit_deltatime');
$q='SELECT count(stamp)
FROM '.$wpdb->prefix.CYSTATS_TABLE_STATISTICS_RAW.'
WHERE (stamp>\''.$delta.'\'
AND remote_addr=\''.($info['remote_addr']+0).'\')
ORDER BY stamp DESC LIMIT 1';
$locked=$wpdb->get_var($q);
if($locked==0){
// Es ist ein Besuch, updates/inserts
Das erscheint mir doch zu trivial. In $info[timestamp] steht der Zeitstempel des Hits. Aus get_option('cystats_visit_deltatime') wird die Zeit in Sekunden ausgelesen, die als Besuch angesehen wird.
Mit dem select wird nun rückwirkend gesucht, ob zu der IP in der “visit_deltatime” schon ein Eintrag existiert. Wurde ein Hit gefunden, treibt sich die Person weniger als “visit_deltatime” Sekunden auf der Seite rum. Der Klick wird nicht als Besucher erfasst.
Wird kein Eintrag gefunden, war diese IP nie oder nicht länger als “visit_deltatime” Sekunden auf der Homepage. Dann wird dieser Hit als neuer Besucher gewertet. Und zwar pro Tag, Woche, Monat und Jahr.
Da gibt es nun etwas Kritik. Es wird nicht unterschieden, ob es um ein Neubesuch oder um eine Rückkehr handelt. Um das zu differenzieren, müsste man Neubesuch definieren (z.B. pro Tag) und dafür zwei Abfragen durchführen (einmal checken, ob “heute” der Besucher schon da war und dann den Zeitraum prüfen).
Das andere Problem ist, dass der Zeitraum m.E. viel zu kurz im Standard gewählt wurde. 300 Sekunden gibt CyStats vor und entspricht 5 Minuten. Üblicherweise berechnen andere Statistiktools eine Wiederkehr (und so muss man das hier sehen, weil ein Neubesuch nicht unterschieden werden kann) dann, wenn mehr als eine Stunde vergangen ist (entspricht 1800 Sekunden). Aber auch das hilft wenig, wenn nicht nach Neubesuchen unterschieden wird, weil ein treuer Besucher bei vier mal “vorbeischauen” pro Tag auch vier Besuche und vier Hits generiert. D.h. im schlechtesten Fall wären Besuche gleich Hits.
Übrigens auch dann – und das ist die größte Kritik – wenn jede IP nur einmal am Tag vorbei schaut. Damit hätten wir ganz untreue Besucher und trotzdem so viele Besuche wie Hits.
Im Prinzip darf ein Besuch, gar nicht bei dem ersten Hit bewertet werden. Die Berechnung, die CyStats durchführt, verhindert nur, dass jeder Hit einer IP in einem Zeitraum zum Besuch wird. Es lässt aber völlig außer Acht, dass man einen Besuch ungern als solches definieren will, wenn es nur ein Zufallsklick gewesen sein mag (z.B. Einfall durch Suchmaschine und dann die Seite wieder verlassen, weil der Content nicht stimmt). Da hat übrigens Google Analytics größere Vorteile. Google weiß z.B., dass ein Besucher von seiner Suche sofort zurückkehrt und weiter sucht, weil der Inhalt des gefundenen nicht passt. Solche Fehlklicks werden bei GA bestimmt nicht als Besucher gezählt. Deswegen sind parallel laufende GA-Besuche immer deutlich kleiner als die CyStats-Besuchszahlen.
Da CyStats bewusst auf JavaScript verzichtet und ja nur auf der lokalen Webseite arbeitet, kann man Zufallsklicks als solche nicht erkennen. D.h. ein Hit ist zwar ein Hit und damit unqualifiziert (d.h., man darf das nicht zu hoch bewerten) aber bei einem Besuch muss man strenger rechnen, als es der einfache Algorithmus von CyStats tut. Der erste Klick von einer eindeutig nachweisbaren Quelle darf noch nicht als Besuch gewertet werden.
Dadurch wird das Erkennen von echten Besuchen aber komplizierter. Wer eine einfache Methode hat, kann ja mal eine Idee hinterlassen. Ich mache mir mal parallel ein paar Gedanken.
Beste Grüße,
Josch.
Moin!
In meinen Feature-Artikeln möchte ich gerne andere Blogs und deren Inhalte vorstellen.
Adam Bien hat eine kleine Serie (I, II, III, IV, V) gestartet, um schöne Plugable LookAndFeels (PLAFs) für Swing vorzustellen. Mit PLAFs kann man seine Swing Anwendungen recht schnell mit einem neuen Skin versehen, (fast) ohne die eigene Anwendung anpassen zu müssen. Er hat dann später die Serie erweitert, um zu zeigen was es für Erweiterungen als Swing-Komponenten gibt (Widgets): VI, VII.
Aber es geht in diesem Feature gar nicht um Adam Biens Blog
– ich habe nämlich einen Blog gefunden, der sich ausschließlich mit der Erstellung von Swing-Komponenten beschäftigt, die den besonderen Touch von Web-Widget Komponenten (wie man es von Adobe Flex oder Microsoft Silverlight und JavaFX kennt) haben.
Gerrit Grunwald, Softwareentwickler bei der Raith GmbH in Dortmund beschreibt in seinem Blog Harmonic Code den Design von Swing Komponenten in der Layer-Technik:
Was man mit dieser Technik alles erreichen kann, stelle ich mal als einen kleinen Auszug an Bildern vor:
Das besondere an dem Blog ist nicht nur die tolle Beschreibung, wie diese Komponenten mit unterschiedlichen Werkzeugen aufgebaut werden, sondern auch die Möglichkeit das alles als Webstart-Demos anzuschauen und den Sourcecode herunterladen zu können.
So wird der Blog zu einer wahren Fundgrube an Ideen, um eigene individuelle Swing-Komponenten zu erstellen.
In einer eindrucksvollen Präsentation, beschreibt Gerrit Grunwald, wie man mit passenden Tools von Designern am schnellsten zu echten skalierbaren Swing-Komponenten kommt.
Ich bin der Meinung, dass der Feed des Blogs in jeden Feedreader eines interessierten Swing-Entwicklers gehört.
Beste Grüße,
Josch.
Moin!
Wie man nicht anders erwarten durfte, ist es nun passiert. Die Apache Software Foundation hat sich heute aus dem Executive Committee des JSP zurückgezogen.
The Apache Software Foundation has resigned its seat on the Java SE/EE Executive Committee. Apache has served on the EC for the past 10 years, winning the JCP “Member of the Year” award 4 times, and recently was ratified for another term with support from 95% of the voting community. Further, the project communities of the ASF, home to Apache Tomcat, Ant, Xerces, Geronimo, Velocity and nearly a 100 mainstay java components have implemented countless JSRs and serve on and contribute to many of the JCPs technical expert groups.
Im Apache ASF Blog wird auf die (bekannten) Gründe näher eingegangen:
The recent Java SE 7 vote was the last chance for the JCP EC to demonstrate that the EC has any intent to defend the JCP as an open specification process, and demonstrate that the letter and spirit of the law matter. To sum up the issues at stake in the vote, we believe that while continuing to fail to uphold their responsibilities under the JSPA, Oracle provided the EC with a Java SE 7 specification request and license that are self-contradictory, severely restrict distribution of independent implementations of the spec, and most importantly, prohibit the distribution of independent open source implementations of the spec. Oracle has refused to answer any reasonable and responsible questions from the EC regarding these problems.
Letztendlich zieht die ASF auch alle Vertretungen in den JSRs zurück:
To that end, our representative has informed the JCP’s Program Management Office of our resignation, effective immediately. As such, the ASF is removing all official representatives from any and all JSRs. In addition, we will refuse any renewal of our JCP membership and, of course, our EC position.
Diese (schnelle) Reaktion der ASF war zu erwarten, nachdem Oracle mit seiner Pressemitteilung komplett jede Kritik unerwähnt ließ und die Annahme des JSR 336 und 337 als uneingeschränkten Erfolg feierte. In der Pressemitteilung wurde auch nicht erwähnt, dass und aus welchen Gründen einige Mitglieder (unter anderem auch Google) gegen einige JSR Elemente stimmten. Auch die Protestnoten sind unter den Tisch gefallen.
Beste Grüße,
Josch.



















