Friday, November 30, 2012

Generische Collection in ein Array verwandeln.

Das Collectionsframework von Java ist vor allem seit der Einführung von Generics eine wirkliche tolle Sache. Aber leider hat man manchmal das Problem, dass man die Collection z.B. für die Übergabe an eine Funktion als Array benötigt. Dafür gibt es in der Collection Klasse die Methode toArray(). Bei nicht spezifizierten Collections funktioniert diese auch ziemlich einfach. Wenn man aber eine Collection mit einem bestimmten Typ hat, dann gibt toArray() auch ein Object Array zurück, was natürlich nicht verwendbar ist.

Ein kleines Beispiel wir haben eine List vom Typ String und möchten die Funktion test(String[]) aufrufen.

public class Test {
 public static void main(String[] args) {
  List<String> list = new ArrayList<String>();
  list.add("Test1");
  list.add("Test2");
  test(list.toArray());
 }
 private static void test(String[] test) {
  // mache irgendwas.
 }
}

Der Compiler wird folgenden Fehler ausgeben: "The method test(String[]) in the type Test is not applicable for the arguments (Object[])"

Nach längerer Suche im Internet habe ich herausgefunden, dass man ein leeres Array des Typs den die Collection hat als Parameter für toArray() übergeben muss, damit es funktioniert.

public class Test {
 public static void main(String[] args) {
  List<String> list = new ArrayList<String>();
  list.add("Test1");
  list.add("Test2");
  test(list.toArray(new String[0]));
 }
 private static void test(String[] test) {
  // mache irgendwas.
 }
}

Tuesday, November 27, 2012

Fork/Join Framework in Java 7

Auf Heise Developer wurde ein sehr interessanter Artikel über das mit Java 7 eingeführte Fork/Join Framework gepostet. Das Fork/Join Framework sieht sehr interessant aus, um komplexe Aufgaben in kleinere Tasks aufzuteilen und auf mehreren Prozessorcores gleichzeitig auszuführen. Leider wird Java 7 von Notes noch nicht unterstützt. Aber vielleicht ändert sich das ja mit Lotus Notes Social Edition.

Tuesday, November 13, 2012

Burnout in der IT Branche

Heise hat über eine Studie zum Thema "Burnout in der IT" die ich wirklich sehr interessant finde berichtet. Vor allem, dass die IT Branche eine höhere Gefährung als die Gesundheitsberufe haben soll, hätte ich so nicht erwartet.


Sunday, November 11, 2012

Problem beim Build von Plugins für Notes in PDE

Beim letzten Update meiner diversen selbst erstellten Plugins zum Notes Client kam es zu einem rätselhaften Fehler. Ich habe alle Plugins ausgiebig durchgetest, in dem ich Notes aus Eclipse aufgerufen habe. Alles lief absolut problemlos. Also in Eclipse eine Updatesite erstellt und alle Plugins bauen lassen. Der Buildprozess lief ohne ersichtlichen Fehler durch. Doch nach der Ausrollung der neuen Plugins in unsere Notesumgebung kam es plötzlich an manchen Stellen der Anwendung zu Abbruchfehlern (The method beginTask(String, int) of type DummyProgressMonitor must override a superclass method). Eine schnelle Kontrolle in Eclipse zeigte aber alles in Ordnung. beginTask überschreibt die Methode beginTask in UIProgressMonitor. Auch funktionieren die Plugins einwandfrei, wenn ich Notes aus Eclipse starte.

Eine kurze Internetrecherche zeigt, dass es im JDK 1.5 ein Problem gab, dass die @Override Anotation beim Überschreiben von Methoden aus Interfaces nicht zulässig war. Aber ich verwende doch überall das 1.6 JDK. Also, die Ausgabelogs des Build nach Error durchsucht und in den Builds an jeder Stelle an der ein Interface überschrieben wird, beim Compilieren die oben angeführte Fehlermeldung gefunden. Hm alles klar, das Plugin Development Environment verwendet für den Build für die Updatesite einen 1.5 Compiler statt eines 1.6, obwohl ich in den Standardeinstellungen von Eclipe überall 1.6 festgelegt habe.

Die Lösung fand sich im MANIFEST.MF file des Plugins. Dort war die Direktive "Bundle-RequiredExecutionEnvironment: JavaSE-1.5" angegeben. Diese Direktive wirkt scheinbar ausschließlich auf den Buildprozess beim Erstellen einer Updatesite. Beim normalen Build für den Test des Plugins aus Eclipse wird diese Einstellung nicht verwendet. Diese Direktive kann auch über das UI eingestellt werden und sollte natürlich für den Export in Lotus Notes auf 1.6 stehen.


Für mich haben sich aus diesem Problem zwei Lehren ergeben. Erstens immer die erstellten Logs des Buildprozess durchsehen, da Eclipse keinen Fehler bringt, wenn beim Build Fehler kommen. Und zweitens immer nach dem Erstellen der Updatesite noch einmal alles durchtesten.

Thursday, November 8, 2012

Profiling mit der IBM JVM von Lotus Notes (Teil 2)

Nachdem wir im ersten Teil die IBM Support Assistant Workbench installiert haben, wollen wir uns diesmal ansehen wie man den Healthcenteragent in die JVM von Notes installiert und welche Einstellungen man anpassen muss, damit das Profiling funktioniert.

Als erstes muss man den Healthcenteragent aus der Workbench extrahieren. Man ruft dazu das Hilfecenter auf.



In diesem wählt man in der linken Seitenleiste zuerst den Eintrag "Tool: IBM Monitoring and Diag..." dann den Punkt "Monitoring a running..." und dann "Installing the Health.."  Auf der rechten Seite wird dann eine Liste mit den Agents angezeigt. Für Lotus Notes unter Windows sollte man den "Windows x86 32 bit" auswählen.


Nach dem Anwählen des Agent erhält man ein Zip File, dass man entpacken muss. In dem Zipfile befindet sich ein Ordner "jre". Den Inhalt dieses Ordners muss man in das jvm Verzeichnis von Notes einkopieren. Unter Windows befindet sich dieses standardmäßig in "C:\Program Files (x86)\IBM\Lotus\Notes\jvm". Eventuell ist schon eine ältere Version des Healtcenteragent installiert. Diese kann problemlos überschrieben werden. Falls man mit eingeschränkten Rechten arbeitet, muss man eventuell noch das Administratorpasswort eingeben. Wichtig ist, dass der Inhalt des jre Verzeichnis aus dem zip wirklich genau in die Verzeichnisstruktur der jvm passt.

Nachdem der Healthcenteragent in der JVM installiert ist, muss man noch den Aufruf der JVM von Notes anpassen, damit der Agent bei jedem Start von Notes aktiviert wird. Dies erreicht man in dem man die Datei "C:\Program Files (x86)\IBM\Lotus\Notes\framework\rcp\deploy\jvm.properties" editiert. In diese Datei muss erstens der Eintrag "vmarg.Xhealthcenter=-Xhealthcenter:port=1999" ergänzt und die Optimierung "vmarg.Xtrace=-Xtrace:none" ausgeschaltet werden.

Die Datei sollte dann ca. so aussehen:


Die Portnummer 1999 sollte natürlich frei sein. Diese Portnummer wird verwendet, damit sich die Workbench mit der laufenden JVM verbinden kann.

Die Datei speichern und Lotus Notes neu starten.

Sobald Notes neu gestartet ist, kann man in der Workbench das Profiling starten.


IBM Monitoring and Diag... auswählen und auf starten klicken.



Einmal weiter klicken und dann in dem Verbindungsdialog die Portnummer die man in der jvm.properties verwendet hat angeben.


Den Haken bei Scan next 100 port for available connections kann man rausnehmen, wenn man die Portnummer richtig angegeben hat. Dann auf weiter klicken.

Wenn alles richtig funktioniert hat, dann sollte die Workbench die laufende Notes jvm gefunden haben und man kann den Assistent mit fertigstellen abschließen.

Nach wenigen Sekunden beginnt die Aufzeichnung des Überwachungsvorgang.



Man kann nun die verschiedenen Bereiche für die schon Daten gesammelt wurden aufrufen. z.B. die Garbagge Collection Ansicht.




Man bekommt auch mehr oder weniger sinnvolle Hinweise, dass etwas nicht richtig konfiguriert ist.

Im dritten Teil der Profiling Serie möchte ich dann zeigen, wie man einzelne Methoden seiner Klassen überwachen kann.

Übrigens falls man sein Programm statt mit der IBM JVM auch mit der Oracle JVM laufen lassen kann, gibt es einen viel einfacheren und meiner Meinung nach auch besseren Profiler.

P.S. Wenn man nicht gerade die Workbench verwendet, sollte man in der jvm.properties den Healthcenter Eintrag wieder auskommentieren, damit man keinen unnötigen Overhead produziert.


Wednesday, November 7, 2012

Profiling mit der IBM JVM von Lotus Notes (Teil 1)

Für die JVM von Oracle gibt es einen gratis Profiler der sehr gut funktioniert und auch sehr einfach einzusetzen ist. Leider kann dieser Profiler nicht mit der IBM JVM die z.B. in Lotus Notes genutzt wird, verwendet werden. Auch die IBM bietet einen Profiler an, der aber leider ziemlich aufwendig zu installieren ist.

Der erste Schritt ist, dass man falls man die IBM Support Assistance Workbench noch nicht hat, diese unter http://www-01.ibm.com/software/support/isa/ herunterlädt.

Der Inhalt der heruntergeladenen Datei muß nun in einen temporären Ordner entpackt werden.
Der temporäre Ordner sollte auf der gleichen Platte wie das Installationsziel sein, da sonst der Installer während der Installation abstürzt.

Im entpackten Ordner muß man dann die Datei "setupwin32.exe aufrufen.

Die Eula abnicken und solange weiter klicken, bis das Programm fertig installiert ist.

Danach sollte man wenn man mit einem Benutzer arbeitet, der auf das Programmverzeichnis keine Schreibrechte hat für die Workbench ausnahmsweise Schreibrechte vergeben. Der Updater der beim Starten der Anwendung läuft ist nicht schlau genug, dass er über die UAC höhere Rechte anfordert.


Jetzt kann man die Support Assistant Workbench starten. Den Anfangsassistenten kann man falls man nicht einen Proxy einstellen muß beherzt duchklicken. Es dauert dann etwas bis die Workbench sich aus der Updatesite im Internet die richtigen Updates gezogen hat.



Dann die Updates die er anbietet installieren, man braucht jedoch keine Produkt Addons installieren. Diese Seite einfach abbrechen.

Nach einiger Zeit sollte man dann die Meldung bekommen, dass die Workbench erfolgreich upgedated wurde.

Nach dem Fertigstellung muss man dann noch einmal  neustarten.

Jetzt kann man die JVM Healthcentertools in die Support Workbench installieren. Dazu muss man im Menü "Aktualisieren->Neue suchen..." den Punkte "Tool-Add-ons" aufrufen.


Nach einiger Nachdenkpause der Workbench kann man dann in das kleine Suchfeld den Begriff "health" eingeben und dann das Healthcenter auswählen.

Danach wieder die Lizenzbedienungen abnicken und wenige Minuten später sollten auch die Addons installiert sein. Jetzt will die Workbench natürlich wieder einmal neu starten.

Nach dem Neustart ist die Workbench mit dem JVM Profiler soweit startklar.

Die Installation des Healthcenteragent in Lotus Notes und welche Einstellungen in Lotus Notes angepasst werden müssen damit der agent funktioniert, beschreibe ich dann in Teil 2.
 

Tuesday, November 6, 2012

Content Assist in Domino Designer oder Eclipse anpassen.

Aufgrund der historisch gewachsenen API der Notes.jar können die Syntaxvervollständigungsvorschläge teilweise etwas verwirrend sein. Es gibt die selben Typennamen in bis zu 4 Paketen. Es ist auf den ersten Blick nicht leicht zu erkennen welchen Namen man jetzt importieren soll.


Der Richtige in dem Fall wäre der Erste.

Eclipse bzw. DDE bieten aber eine komfortable Einstellungsmöglichkeit mit der man die Vorschläge für die falschen Paketen ausschließen kann.

Unter DDE muss man den Menüpunkt "Datei->Vorgaben" und unter Eclipse "Windows->Preferences" aufrufen. Dort kann man dann  unter "Java->Darstellung->Typfilter" die Pakete hinzufügen, für die man keine Syntaxvervollständigung mehr haben will.


Und schon klappt es auch mit der verbesserten Syntaxvervollständigung.


Übrigens für Entwickler die viel mit SWT und jface arbeiten, kann man mit der selben Funktion auch awt und swing unterdrücken, da es auch hier viele Überschneidungen der Typnamen gibt.

Sunday, November 4, 2012

Java ist langsam, oder doch nicht?

Oft hört man den Vorwurf, dass Java langsam ist und nur C oder C++ Programme eine ordentliche Performance haben. Dies hat vielleicht für die ersten Versionen von Java gestimmt, aber seit Einführung von JIT Compilern stimmt dieser Vorwurf sicher nicht mehr. Warum sich Javaprogramme trotzdem oft ziemlich träge anfühlen, liegt weniger an der Geschwindigkeit der jvm sondern viel eher an der Unwissenheit der Programmierer die Javaprogramme erstellen. Ich möchte das heute an einem einfachen Beispiel zeigen.

In dem Beispiel erzeugen wir eine große SWT Tabelle wie es in vielen Anwendungen der Fall ist. Denken wir z.B. an eine Ansicht über den Kundenstamm. So eine Tabelle kann locker mal 100000 Einträge haben. Ich habe das ganze erst einmal so geschrieben, wie SWT Tabellen in den meisten Handbüchern erklärt werden. Zuerst wird ein Fenster erstellt. In dem Fenster die Tabelle und dann befülle ich über eine Schleife die Tabelle mit 100000 Zeilen. Das dauert auf meinem Rechner mit Quadcore CPU und reichlich Speicher unerträglich lange 15 Sekunden und zusätzlich zeigt mir der Systemmonitor einen Speicherverbrauch von unvorstellbaren 514 Megabyte für so ein simples Programm an.

Hier mal der Code wie es oft gemacht wird, man es aber bei größeren Tabellen auf keinen Fall machen sollte:
public class TestNormalTable {
 public static void main(String[] args) {
  Display display=new Display();
  Shell shell=new Shell(display);
  shell.setSize(600, 1024);
  shell.setLayout(new FillLayout());
  Table table=new Table(shell, SWT.BORDER);
  table.setLinesVisible(true);
  table.setHeaderVisible(true);
  //Spalten erstellen.
  {
  TableColumn spalte=new TableColumn(table,SWT.NONE);
  spalte.setText("Spalte 1");
  spalte=new TableColumn(table,SWT.NONE);
  spalte.setText("Spalte 2");
  }
  //Zeilen befüllen
  for(int i=0;i<100000;i++){
   TableItem zeile=new TableItem(table, SWT.NONE);
   zeile.setText(0,"Beispieltext zeile ");
   zeile.setText(1,new Integer(i).toString() );
  }
  //Spaltenbreiten optimieren.
  for(TableColumn spalte:table.getColumns()){
  spalte.pack(); 
  }
  shell.open();
  while(!shell.isDisposed()){
   if(!display.readAndDispatch()) display.sleep();
  }
  display.dispose();
 }
}

Screenshot des erstellen Fensters.



Bei oberflächlicher Betrachtung könnte man annehmen, dass Java einfach nicht schneller eine so simple Schleife mit 100.000 Wiederholungen durchlaufen kann. Wenn man das Programm aber durch einen Profiler schickt, dann sieht man sehr schnell, dass die verbrauchte Zeit und auch der verbrauchte Speicher gar nicht im Javacode steckt, sondern dass die meiste Zeit in nativen Code des Fenstertoolkit verschwendet wird. SWT zeichnet seine Komponenten zu denen auch die Tabelle gehört ja nicht selbst, sondern verwendet das native Fenstertoolkit der Plattform auf der das Programm läuft. In meinem Fall GTK+. Die Frage ist jetzt wie kann man vermeiden, dass bei einer so großen Tabelle alle nativen Tableitems erstellt werden müssen. Die Antwort ist ziemlich einfach und auch sehr effektiv. Man muss eine sogenannte Virtuelle SWT Tabelle verwenden.

Bei der virtuellen Tabelle wird die Tabelle mit dem style 'SWT.VIRTUAL' erstellt werden. Damit signalisiert man SWT, dass man eine Tabelle haben will, die dynamisch je nach Bedarf befüllt wird.

Hier mal der Code wie man eine hochperformante große Tabelle erstellt:
public class TestVirtualTable {

 /**
  * @param args
  */
 public static void main(String[] args) {
  final String[] spalte1=new String[100000];
  final int[] spalte2=new int[100000];
  Display display = new Display();
  Shell shell = new Shell(display);
  shell.setSize(600, 1024);
  shell.setLayout(new FillLayout());
  final Table table = new Table(shell, SWT.BORDER | SWT.VIRTUAL);
  table.setLinesVisible(true);
  table.setHeaderVisible(true);
  //Arraytabelle befüllen.
  for(int i=0;i<100000;i++){
   spalte1[i]="Beispieltext zeile";
   spalte2[i]=i;
  }
  // Spalten erstellen.
  {
   TableColumn spalte = new TableColumn(table, SWT.NONE);
   spalte.setText("Spalte 1");
   spalte = new TableColumn(table, SWT.NONE);
   spalte.setText("Spalte 2");
  }
  // Grösse der Tabelle festlegen
  table.setItemCount(100000);
  // Callback Listener zum Auslesen der Daten hinzufügen.
  // Immer wenn eine neue Zeile am Bildschirm angezeigt wird, wird dieser
  // Listener aufgerufen um Daten anzufordern.
  // Es werden also nur die Zeilen erstellt, die der Benutzer wirklich
  // anschaut.
  table.addListener(SWT.SetData, new Listener() {
   @Override
   public void handleEvent(Event event) {
    TableItem zeile = (TableItem) event.item;
    // Ermittelen des Zeilenindex. Im echten Leben würde man mit
    // diesem Index z.B. auf ein Array zugreifen.
    int index = table.indexOf(zeile);
    zeile.setText(0, spalte1[index]);
    zeile.setText(1, new Integer(spalte2[index]).toString());
   }
  });
  // Spaltenbreiten optimieren.
  // Da die virtuelle Tabelle erst nach dem Anzeigen befüllt wird, müssen
  // wir die Spaltenbreitenoptimierung in die Eventwarteschlange hinten
  // anfügen.
  // Damit wird die Spaltenbreitenoptimierung nach dem Anzeigen der ersten
  // Sätze ausgeführt und sollte schon gute Ergebnisse bringen.
  Display.getDefault().asyncExec(new Runnable() {
   @Override
   public void run() {
    for (TableColumn spalte : table.getColumns()) {
     spalte.pack();
    }
   }
  });
  shell.open();
  while (!shell.isDisposed()) {
   if (!display.readAndDispatch())
    display.sleep();
  }
  display.dispose();
 }
}

Diese Version wird auf meinen Rechner praktisch verzögerungsfrei angezeigt. Zusätzlich belegt sie nur etwas über 34 Megabyte Hauptspeicher. Bei völlig gleichem Ergebnis.

Hier nocheinmal im Detail die wichtigen Punkte:
  
  • Zeile 13 Die Tabelle mit dem style 'SWT.VIRTUAL' erstellen.
  • Zeile 29 mit setItemCount der Tabelle mitteilen wie viele Items die Tabelle hat. 
  • Zeile 35-45 einen Listener der der Tabelle hinzufügen, der jedes mal aufgerufen wird, wenn die Tabelle neue Items darstellen will. 
  • Zeile 52-59 Da die Tabelle vor dem anzeigen noch keine Items hat, funktioniert der pack zum Festlegen der Spaltenbreite nicht, wenn man es wie im Code ohne Virtual macht. Ich setze daher den Code in ein Runnable dass ich mit asyncExec in die Eventqueue stelle. Dieser Code, wird dann nach dem Anzeigen der Tabelle ausgeführt. Dann hat die Tabelle schon die Items der ersten Seite und die Spaltenbreitenoptmierung wird dann anhand dieser Daten gemacht.

Wie man an diesen Beispiel sieht, können oft schon kleine Änderungen an einem Programm den Unterschied machen ob das Programm ein quälend langsamer Ressourcenfresser oder hochperformant und effizient ist. Ich verwende die virtuellen Tabellen übrigens z.B. in einem Programm zur Anzeige der Änderungen im Journal unserer DB2 durchgeführt werden. Wenn man da die Änderungen eines ganzen Tages aufruft, dann kommen schnell mal 2 Millionen Änderungen zusammen. Auch diese riesen Tabellen werden Dank SWT.VIRTUAL ohne Verzögerungen angezeigt.

ad