Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

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.

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.
 

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.

Thursday, November 1, 2012

Buchtipp: Effective Java von Joshua Bloch

Die Javaprogrammierung wird im IBM Umfeld immer wichtiger. Egal ob man jetzt Plugins für die Expeditor clients (Notes, Sametime) oder eigene Erweiterungen für XPages schreiben will. Immer wieder werden gute Javakenntnisse benötigt. Nun gibt es sehr viele gute Anfängerbücher wie "Java ist auch eine Insel" und ähnliche. Mit diesen Büchern kann man relativ schnell die Grundbegriffe von Java lernen.

Aber beim Java lernen wird man zwar schnell halbwegs funktionierende Programme erstellen. Doch gut erweiterbar und robust gegen Fehler werden sie in den seltensten Fällen sein, da Anfängerbücher für Java zwar meistens die Sprachkonstrukte und auch die Klassenbibliothek gut erklären, aber selten die dahinterliegenden Designentscheidungen und Gründe ausführen, warum man manche Konstrukte die zwar funktionieren vermeiden sollte und andere Konstrukte einfach Best practise sind. Um diese Best Practise zu lernen und ein besserer Java Programmierer zu werden hat mir ein Buch am meisten geholfen. "Effective Java Second Edition" von Joshua Bloch.

Der Autor dieses Buchs ist der Entwickler des meiner Meinung nach hervorragenden Collections API von Java. Er beschreibt in 78 sogenannten Items warum man etwas nicht machen sollte und wie man es besser macht. Alleine durch das Lesen des Buches habe ich viele Fehler in meinem Code gefunden, die zwar noch nicht aufgefallen sind, aber potenzielle Sicherheitslücken und ähnliches wären.

Monday, October 29, 2012

Aufruf einer 32bit JVM auf Windows 64 bit

Wenn in Windows 64 bit sowohl eine 32 bittige als auch eine 64 bittige JVM installiert sind, wird standardmäßig bei Aufruf mittels java oder javaw die 64 bittige JVM gestartet. Dies ist normalerweise kein Problem, da Javaprogramme in beiden funktionieren. Wenn man aber z.B. ein 32 bit SWT in seinem Programm verwenden möchte, dann braucht man unbedingt die 32 bit JVM. Leider funktioniert der Befehlzeilenparamter -d32 bei der Windows JVM nicht. Deshalb habe ich mir einen kleines vbs script, dass erkennt ob es in einer 64 Umgebung läuft und wenn ja sich nocheinmal als 32bit Prozess startet. Wenn es dann in einer 32 bit Umgebung läuft, kann es java aufrufen.


 
Set WShell = WScript.CreateObject("WScript.Shell")
' Falls Script in einer 64bit Umgebung ausgeführt wird, dann starte das Script nocheinmal als 32 bit script
If WShell.ExpandEnvironmentStrings("%PROCESSOR_ARCHITECTURE%")="AMD64" Then
WShell.Run WShell.ExpandEnvironmentStrings("%windir%\SysWOW64\wscript.exe SollistvergleichExportImport.vbs")
Else
WShell.run "javaw.exe  example.class"
End If

Thursday, October 25, 2012

Start 64bit vbscript von Lotus Notes

Wir verwenden unter Lotus Notes eine Inventorydatenbank, die auf jeden Client von Notes aus ein vbscript ausführt, dass diverse Infos aus der Registry ausliest und dann in Notes auf dem Client importiert. Das hat solange wunderbar funktioniert, wie wir nur 32 bit Clients gehabt haben. Auf 64 bit Windows hatte das skript, wenn es von Lotus aufgerufen wurde nur Zugriff auf die 32bit Zweige der Registry, es konnte aber keine Einträge aus den 64 bit Bereichen der Registry aufrufen.

Schuld daran ist, dass Prozesse die von einer 32 bit Applikation aus gestartet werden standardmäßig auf das windir\SysWow64 Verzeichnis umgeleitet werden, wenn Sie eigentlich auf windir\system32 zugreifen wollen. Dadurch starten Sie den vbscript Host als 32 bit Applikation und nicht als 64 bit.

Es gibt aber eine einfache Lösung: Zumindest unter Windows 7. Windows stellt einen Alias bereit der sich "sysnative" nennt und auch unter 32 bit Prozessen Zugriff auf das 64 bit system32 Verzeichnis erlaubt. Man kann also ein 64 bit vbscript in einem Javaagenten einfach auf folgende Art aufrufen:

// Aufruf mit dem sysnative bewirkt, dass Script in
// 64 bit ausgeführt wird auch wenn der Hostprozess ein
// 32bitiger ist.
Process p = Runtime.getRuntime().exec(System.getenv("WINDIR") + "\\sysnative\\cscript.exe\script.vbs")
p.waitFor();
 
Genauere Infos erhält man auf MSDN

Wednesday, October 24, 2012

Probleme mit dem Zugriff auf den eigenen Host

Zur Eröffnung eines sockets für den Zugriff auf den Localhost habe ich bisher immer die Methode getLocalHost() der InetAddress Klasse verwendet.
          Socket socket=newSocket(InetAddress.getLocalHost(),5146);
          PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
          BufferedReader in = new BufferedReader(new inputStreamReader(socket.getInputStream()));
          out.println("Test");
          parameter=in.readLine();

Bisher hat das auch immer relativ gut funktioniert, bis der Code auf einem Client gelaufen ist der den Cisco VPN Client verwendet hat. Sobald ein Cryptotunnel aufgebaut wurde, wird die lokale Adresse des Clients auf eine Adresse aus dem VPN Pool umgestellt und bei Ausführung des oben genannten Codes kommt es zu keiner Verbindung mehr, sondern es kommt ein Timeout. Der Grund ist, dass getLocalHost() nicht etwa das loopback (127.0.0.1) Interface zurück gibt, sondern die IP Adresse der Netzwerkkarte. Diese wechselt nach VPN Einwahl und schon verbindet man sich mit der falschen Adresse. Eine einfache Möglichkeit das Problem zu lösen ist folgenden Code zu verwenden.
          Socket socket=new Socket(InetAddress.getByName("localhost"),5146);
          PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
          BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
          out.println("Test");
          parameter=in.readLine();
Das bietet aber noch immer die Gefahr das in der Hostsdatei localhost nicht richtig definiert ist und es dann wieder zu Problemen kommt. Nach einiger Recherche in der Dokumentation der Klasse Intnetadress habe ich dann den richtigen Weg gefunden. Die Methode getByName() darf man mit einem null Wert aufrufen und dann gibt sie automatisch und immer das loopback interface zurück.
          Socket socket=new Socket(InetAddress.getByName(null),5146);
          PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
          BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
          out.println("Test");
          parameter=in.readLine();

Monday, October 22, 2012

Internationale Sonderzeichen in Lotus Notes plugins

Wenn man in seinem Javacode internationale Sonderzeichen wie z.B. Umlaute in String Literalen verwendet, kann es passieren dass nach dem Export des Plugins die Sonderzeichen falsch angezeigt werden. Dies passiert dadurch, dass beim Kompilieren von Plugins standardmäßig nicht UTF-8 sondern ein nicht Unicode fähiger Characterset verwendet wird. Man kann das Problem einfach lösen, in dem man in die build.properties seines Plugins den Eintrag "javacDefaultEncoding.. = UTF-8" ergänzt.

Eine Beispiel "build.properties" Datei sieht dann folgendermaßen aus:

source.. = src/
output.. = bin/
javacDefaultEncoding.. = UTF-8
bin.includes = META-INF/,\
               .,\
               plugin.xml,\
               icons/


Dann sollten String Literale in diesem Plugin richtig angezeigt werden.

Tuesday, August 28, 2012

String Prüfung mit Regularexpressions in Java

Eine oft mühsame und aufwendige Aufgabe ist die Prüfung von Strings auf Gültigkeit. Gott sei Dank bietet die String Klasse viele Methoden (z.B. contains(), startsWith() usw.) die eine solche Aufgabe vereinfachen. Die aber wohl Mächstigste ist die .matches(String regex) Methode. Diese Methode gibt den Wert "true" zurück wenn die übergebene RegularExpression mit dem String übereinstimmt.

Ein Beispiel wir wollen prüfen, ob ein String ausschließlich die Zahlen "0-9" enthält.

//Prüfe diverse String Literale ob Sie nur die Ziffern 0-9 enthalten.
System.out.println("145T".matches("[0-9]*")); //ergibt False
System.out.println("145".matches("[0-9]*"));//ergibt true

Wenn wir uns das Regex genauer anschauen gibt der Bereich in den eckigen Klammern den Bereich an, der erlaubt ist und der * sagt, dass eine beliebige Anzahl Zeichen aus der eckigen Klammer erlaut sind.


Um beispielsweise auf eine Kundennummer zu prüfen, die 5 stellig numerisch sein muss, kann man folgendermaßen vorgehen .

// Prüfen ob 5 stellig numerisch
System.out.println("1452434".matches("[0-9]{5}")); // ergibt False da zu lange
System.out.println("RALFP".matches("[0-9]{5}"));   // ergibt False da nicht numerisch
System.out.println("12345".matches("[0-9]{5}"));   // ergibt true

Natürlich kann man auch viel komplexere Gültigkeitsprüfungen mit .matches() und Regular Expression durchführen.

z.B. Prüfung auf eine gültige IP Adresse

// Prüfen eines String ob er eine gültige IP Adresse ist.
System.out.println("172.16.2.3"
    .matches("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")); // ergibt true, da es eine gültige IP Adresse ist														
System.out.println("256.256.256.256"
    .matches("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")); // ergibt false, da keine gültige IP Adresse.
Alles in allem sieht man, dass .matches und Regular Expressions wirklich eine sehr mächtige Kombination sind. Und das schöne an Regular Expressions ist, dass es sehr viele fix fertige Beispiele zur Prüfung auf diversen Beispielwebseiten gibt. Das heißt man muss sich nicht alles selber zusammenbauen, sondern kann auf erprobte Regular Expressions zurückgreifen.

zum Beispiel

http://www.regular-expressions.info/examples.html

http://www.mkyong.com/regular-expressions/10-java-regular-expression-examples-you-should-know/

http://regexlib.com/DisplayPatterns.aspx

und viele mehr lassen sich über google finden



Sunday, August 26, 2012

Zeilennummern in Stacktraces im Notesclient anzeigen

Meiner Meinung nach hat es die IBM bei der JVM Optimierung für Notes etwas übertrieben. Denn die IBM hat in den JVM.Properties eine Einstellung gesetzt, die die Anzeige von Zeilennummern in Stacktraces verhindert.Die Zeilennummern in den Stacktraces sind aber eine der wichtigsten Hilfen um Fehlern auf die Spur zu kommen. z.B. sagt ein typischer Stacktrace wie in Notes anzeigt augrund der fehlenden Nummern nicht wirklich viel aus.

java.io.IOException: Cannot run program ""C:\Program": CreateProcess error=2, Das System kann die angegebene Datei nicht finden. 
at java.lang.ProcessBuilder.start(Unknown Source) 
at java.lang.Runtime.exec(Unknown Source) 
at java.lang.Runtime.exec(Unknown Source) 
at java.lang.Runtime.exec(Unknown Source) 
at mycopmany.eclipse.teamviewercontroller.TeamViewerControllerView$5.run(Unknown Source) 
at mycopmany.eclipse.teamviewercontroller.TeamViewerControllerView$2.widgetDefaultSelected(Unknown Source) 
at mycopmany.ui.widgets.SimpleTable.onDefaultSelection(Unknown Source) 
at mycopmany.ui.widgets.SimpleTable$5.handleEvent(Unknown Source)

Ich weiß jetzt zwar, dass irgendwo in meiner run Methode der TeamViewerControllerView ein Fehler aufgetreten ist, aber ich habe keine Anhnung wo. Bei einer längeren Methode die oft native Programme aufruft ein Ding der Unmöglichkeit den Fehler zu finden.

Um diese ungünstige Optimierung der JVM für Notes zu deaktivieren sind zwei Schritte notwendig.

Schritt 1: In der Datei JVM.Properties in "NotesProgDir\framework\rcp\deploy den Eintrag "vmarg.Xnolinenumbers=-Xnolinenumbers" mit einem "#" auskommentieren. Dies verhindert, dass die Zeilennummern bei neuen Klassen die die JVM ausführt entfernt werden. Für Klassen die sich bereits im sogennanten Shared Class Cache der JVM befinden, hilft dieser erste Schritt nicht und man muss den Cache wie in Schritt 2 beschrieben löschen.

Schritt 2: Notes verwendet einen eigenen Shared Class Cache der im Verzeichnis "NotesDataDir\workspace\.config\org.eclipse.osgi" gespeichert wird. Um den Cache zu löschen muss man auf der Commandline in das Verzeichnis der Notes jvm "NotesProgDir\jvm\bin" wechseln und dort den Befehl

java -Xshareclasses:name=xpdplat_.jvm,controlDir="NotesDataDir\workspace\.config\org.eclipse.osgi",destroy

eigeben. NotesDataDir muss natürlich durch den richtigen Pfad ersetzt werden.

Danach bekommt man in Notes vernünftige Stacktraces

java.io.IOException: Cannot run program ""C:\Program": CreateProcess error=2, Das System kann die angegebene Datei nicht finden.
at java.lang.ProcessBuilder.start(ProcessBuilder.java:471)
at java.lang.Runtime.exec(Runtime.java:604) at java.lang.Runtime.exec(Runtime.java:442)
at java.lang.Runtime.exec(Runtime.java:339)
at mycompany.eclipse.teamviewercontroller.TeamViewerControllerView$5.run(TeamViewerControllerView.java:178)
at mycompany.eclipse.teamviewercontroller.TeamViewerControllerView$2.widgetDefaultSelected(TeamViewerControllerView.java:113) at mycompany.ui.widgets.SimpleTable.onDefaultSelection(SimpleTable.java:287)
at mycompany.ui.widgets.SimpleTable$5.handleEvent(SimpleTable.java:142)

und kann den Fehler in Zeile 178 beheben.

Tuesday, August 21, 2012

JVM Profiler im JDK

Wenn man Programmcode optimieren will, ist es wichtig dass man weiß an welchen Stellen eine Optimierung des Codes wichtig ist. Man muß sozusagen die Hotspots im Code finden an denen die meiste Zeit vertrödelt wird. Dazu verwendet man sogennante Profiler die auf der einen Seite einen genauen Einblick in den Hauptspeicher eines laufenden Javaprogramms als auch eine genaue Analyse der Lauzeit und Häufigkeit der Verwendung von Methoden erlauben.

 Man kann jetzt natürlich einen Profiler aus dem Internet herunterladen, aber zumindest seit Version 1.6 gibt es im JDK auch den sehr guten Java Visual VM im "bin" Verzeichnis. Da dieser auf jedem PC auf dem sich das JDK befindet installiert ist hat man immer schnell einen Profile bei der Hand.


Ersatz für @Explode in Java

In Notesanwendungen hat man oft die Aufgabe Text, der durch Trennzeichnen getrennt ist in eine Liste sprich Array zu verwandeln. Dazu verwendet man die Funktion @Explode.

z.B. @Explode("a,b,c") gibt eine Liste mit den Elementen a,b und c zurück.

Etwas ganz ähnliches kann man in Java mit der split Methode der String Klasse machen. Die gleiche Funktion wie oben kann in Java so codiert werden:

 z. B. "a,b,c".split(",") gibt ein Array mit den Elementen "a", "b", "c" zurück.

Doch da das Trennzeichen nicht einfach nur ein Zeichen sondern eine Regular Expression sein kann ist die Funktion noch viel mächtiger. Stellen wir uns z.B. vor wir haben einen String der verschiedene Trennzeichen haben kann.

z.B. "a;b:c,d".split("[,:;]") gibt ein Array mit den Elementen "a", "b", "c", "d" zurück.

Genauso kann man mit einer anderen Regularexpression vor und nach dem Trennzeichen Whitespace entfernen.

z.B.  "a, b, c".split(",") ergibt ein Array mit den Elementen "a"," b"," c" was nicht gewünscht ist aber "a, b, c".split(""\\s*,\\s*") ergibt das korrekte Array mit den Elementen "a", "b","c".

Aufpassen beim split muss man nur, dass standardmäßig leere Elemente am Anfang nicht in das Ergebnisarray aufgenommen werden.

z.B. ",a,b".split(",") ergibt das Array "a","b" und nicht "","a", "b". Um dies zu vermeiden kann man als zweiten Paramter ein -1 angeben. ",a,b".split(",",-1) mit dem man das erwartete Ergebnis erhält.

Friday, August 10, 2012

Extension aus Dateinamen in Java extrahieren.

Oft möchte man die Extension eines Dateinamens extrahieren. Dies ist mit den Funktionen der String Klasse sehr leicht möglich.

Entweder man macht sich eine statische Methode.
public static String getExtension(String fileName) {
 int pos = fileName.lastIndexOf(".");
 return fileName.substring(pos + 1);
}
Oder man nimmt den Einzeiler
fileName.substring( fileName.lastIndexOf(".") + 1);


Die Funktion geht aber davon aus, dass es eine Extension gibt. Andernfalls müsste man noch Fehlerbehandlungscode einbauen.

Thursday, May 24, 2012

Blue J eine Umgebung zum einfachen Lernen von Java

Java ist prinzipiell eine sehr einfache Sprache die mit sehr wenigen reservierten Schlüsselwörtern auskommt. Was das Erlernen trotzdem kompliziert macht ist, die Mächtigkeit und Komplexität der von Java verwendeten Konzepte wie Objektorientiertheit, Vererbung, Ployphormismus usw. Weiters natürlich auch der Umfang der Klassenbiliothek und der Umgang mit den Tools die nötig sind um selbst einfache Programme zu schreiben. Der grösste Fehler wäre seine ersten Schritte in Java mit einer ausgewachsenen IDE wie eclipse zu starten. Bitte nicht falsch verstehen, ich arbeite sehr gerne mit eclipse oder auch netbeans aber zum Anfangen sind diese Umgebungen einfach schlecht geeignet, da man von der Vielzahl an Möglichkeiten einfach erschlagen wird.

Deshalb empfehle ich für das Erlernen von Java die Umgebung Blue J. Dies ist eine sehr einfache Entwicklungsumgebung die speziell dafür geschrieben wurde um einfache Java Klassen zu schreiben und sie sofort auszuprobieren. Man benötigt dazu keine ganzen Programmen sondern kann seine Klassen sofort nach dem Erstellen instantieren und Methoden aufrufen.

Hier ein kleines Beispiel:

Nach dem Laden von Blue J den Menüpunkt Projekt öffnen auswählen:


und das people Beispiel auswählen. Normalerweise installiert Blue J die Beispiele in das Programme Verzeichnis was natürlich ungünstig ist, wenn man keine Schreibberechtigung in dieses Programmverzeichnis hat. Darum habe ich die Beispiele bei mir auf den Desktop kopiert.

Danach muss man den Compile Button klicken und wenn alles gut geht, werden sämtlich Klassen in dem Beispiel kompiliert und man sieht folgendes Bild.


Die orangen Felder sind die einzelnen Klassen des Projekts und die Pfeile geben die Beziehungen an. Staff und Student erben die Eigenschaften von Person und Person wird in der Klasse Database verwendet. Diese Pfeile werden von Blue J auch bei eigenen Projekten automatisch erstellt und helfen am Anfang die Vererbungsbeziehungen in einem Projekt zu verstehen.

Man kann nun jederzeit von einer Klasse eine Instanz erstellen. Dazu klickt man mit der rechten Maustaste auf die entsprechende Klasse (in unserem Fall Database) und wählt einen Konstruktor (Methode die eine Instanz erzeugt) der Klasse.
Die Instanz wird, wenn der Konstruktor keine Parameter hat nach Eingabe eines Namens für die Instanz sofort erstellt und im unteren Bereich von Blue J eingezeigt..

Das gleiche machen wir jetzt für die Klasse staff. Die hat einen Konstruktor mit Parametern. Diese fragt Blue J dann automatisch ab. Wichtig ist, dass man Strings mit "" eingibt.


Den Namen der Instanz. Sie können jetzt natürlich gleich eine beliebige Anzahl von Personen anlegen.

Die Instanzen werden immer im unteren Bereich von Blue J angezeigt und Sie  können mit rechte Maustaste auf die Instanz und dem Punkt inspect jeder Zeit die Variablen einer Instanz anzeigen.




Im nächsten Schritt möchte ich noch zeigen, dass man auch die Instanzen die wir gerade erstellt haben problemlos an die Instanz people hinzufügen kann.

Mit der rechten Maustaste auf die Instanz People klicken und die Methode addPerson auswählen.

Blue J frägt dann welche Instanz man gerne anhängen möchte und ich kann dann die von mir erstelle Instanz der Klasse Staff wählen. (Achtung immer auf Großkleinschreibung achten) Man kann die Instanz natürlich auch in der Combobox auswählen. Übrigens ist mir hier ein kleiner Fehler unterlaufen, denn in Java sollten Namen von Instanzen immer mit einem Kleinbuchstaben beginnen ;-)

Dann kann man auch andere Methoden der Instanz Database ausprobieren wie z.B. listAll() die eine Liste der Personen an die Standardausgabe schickt.

Mit einem Doppelklick auf eine Klasse kann man sich auch jederzeit den Source der betreffenden Klasse anzeigen.

Wenn man selbst schon Klassen geschrieben hat, kann man diese natürlich jederzeit in Blue J importieren kompilieren und genauso interaktiv ausprobieren wie man es hier gesehen hat.

Blue J ist meiner Meinung nach eine tolle Umgebung um einfache Klassen auszuprobieren oder selber zu entwickeln. Einfach ein gutes Java Buch nehemen und die Beispiele in Blue J ausprobieren.

Es lohnt sich.

Thursday, May 3, 2012

Parameter an Hintergrundthread übergeben mit finalen Variablen

Ein oft übersehenes Feature in Java ist die Verwendung des Schlüsselwortes final bei der Definition einer Variablen. Prinzipiell weist die Verwendung von final den Java Compiler an, dass es sich bei der Variable um eine Konstante handelt, die nicht mehr geändert werden kann.

z.B.

 
final String test="Test";
test="test2";


Dieser Code erzeugt beim Compilieren einen Fehler, dass test nicht geändert werden darf.

Das ist sicher schon einmal ein Vorteil, da es Fehler vermeiden hilft, aber der ganz große Vorteil ist, dass wenn eine Variable final ist diese vom Compiler anders behandelt werden kann. Dies kann man sich beim Übergeben von Variablen an einen Hintergrundthread zu nutze machen.

Wir wollen eine Methode schreiben, die einen beliebigen Text beliebig oft an die Standardausgabe ausgibt und dies soll in einen Hintergrundthread passieren. Dies könnte man ungeführ wie unten codieren.

 
/**
* Druckt einen String mehrmals in einem Backgroundthread
* 
* @param string
* @param count
* @return
*/
public static void printHelloWorld(String string, int count) {
  
Thread thread = new Thread() {
    public void run() {
      for (int i = 0; i < count; i++) { //Compilerfehler, da count nicht sichtbar.
 System.out.println(string);  //Compilerfehler, da string nicht sichtbar.
      }
    }
  };
  thread.start();
}
Hier wird der Compiler aber sofort anmerken, dass die Variablen "string" und "count" innerhalb des Threads nicht sichtbar sind. Wenn man die Variablen aber final definiert, dann weiß der Compiler, dass die Variablen nicht mehr geändert werden können und kann diese auch im Hintergrundthread verwenden.
 
/**
* Druckt einen String mehrmals in einem Backgroundthread
* 
* @param string
* @param count
* @return
*/
public static void printHelloWorld(final String string, final int count) {
  
Thread thread = new Thread() {
    public void run() {
      for (int i = 0; i < count; i++) { //Jetzt ist count sichtbar, da eine finale Variable
 System.out.println(string); //Jetzt ist string sichtbar, da eine finale Variable
      }
    }
  };
  thread.start();
}

Tuesday, April 24, 2012

"Recycle" ein Versuch einer Erklärung 2. Teil

Im Teil 1 haben wir geklärt, warum man Notes Objekte überhaupt recyceln muß und wie man die Session am Ende eines Programms ordentlich beendet. Bei ganz kleinen Programmen würde das auch toll funktionieren, aber sobald Programme mehr Daten verarbeiten und seien Sie sich sicher, auch wenn die Datenmenge derzeit noch überschaubar ist, irgendwann wird sie so groß, dass die verfügbaren handles oder der Speicherplatz ausgehen.

Deshalb gilt folgender Merksatz: Jedes Notesobjekt soll so rasch als möglich wieder recycelt werden. Spätestens jedoch bevor es derefenziert wird.

Ein Beispiel:
if(flag) 
 Document doc=view.getFirstDocument(); 
 System.out.println(doc.getItemValueString("Test"); 
} 

Die Variable doc ist nur innerhalb des ifs gültig und muss deshalb noch innerhalb des if's recycelt werden.

if(flag){
  Document doc=view.getFirstDocument();
  System.out.println(doc.getItemValueString("Test");
  doc.recycle();
}

Es gibt beim Recycle jedoch eine Erleichterung, die einem einige Arbeit spart. Das recyceln von übergeordneten Objekten recycelt alle abgeleiteten Objekte mit.

Auf unser Beispiel übertragen:

View view=cdb.getView("Test");
if(flag){
  Document doc=view.getFirstDocument();
  System.out.println(doc.getItemValueString("Test");
  // doc.recycle(); kann man sich sparen, da das Dokument beim Recyclen der View mitrecycelt wird

}
else{
  Document doc=view.getLastDocument();
  System.out.println(doc.getItemValueString("Test");
   // doc.recycle(); kann man sich sparen, da das Dokument beim Recyclen der View mitrecycelt wird
}
view.recycle();

Man sollte dieses implizite Recycle aber nur dann verwenden, wenn man wie in unseren Beispiel gewährleisten kann, dass nur sehr wenige Objekte erzeugt werden und diese auch nicht mit der Datenmenge mehr werden.

Fatal wäre folgender Code:
View view=cdb.getView("Test");
Document doc=view.getFirstDocument();
while(doc!=null){
  //Mach was mit doc
  doc=view.getNextDocument();
}
view.recycle();

Dieser Code wird spätestens, bei ein paar Hundert Dokumenten in der View crashen.

Man muß also doc bei jedem Schleifendurchlauf recyceln. Das ist jedoch nicht ganz einfach, da doc ja nach getNextDocument nicht mehr da ist und vorher darf ich es nicht recyclen, da sonst getNextDocument() nicht mehr funktioniert, wenn das aktuelle Dokument recycelt wird. Folgendes Pattern hat sich bewährt:

View view=cdb.getView("Test");
Document doc=view.getFirstDocument();
while(doc!=null){
  //Mach was mit doc
  Document tempdoc=doc;
  doc=view.getNextDocument();
  tempdoc.recycle();
}
view.recycle();

 Aufpassen muß man natürlich auch darauf, dass man nicht Objekte implizit bereinigt die man noch gerne verwenden möchte:

View view=cdb.getView("Test");
Document doc=view.getFirstDocument();
view.recycle(); //Hier wird doc implizit mitrecycelt. Man darf doc nicht mehr verwenden.
System.out.println(doc.getItemValueString("form")); //Hier wird das Programm crashen.

Ich hoffe etwas Licht in die Sache gebracht zu haben. Würde mich auch über Kommentare zu spezifischen Problemen freuen, die ich dann im 3. Teil behandeln werde.

Monday, April 16, 2012

Den Öffnen Knopf im Lotus Notes Client erweitern

Die Dokumentation zu Expeditor ist ja eher unübersichtlich, daher hier eine kleine Anleitung wie man ein bestehendes Plugin erweitern muß, damit man im Launcher (Öffnen Knopf) eigene Einträge platzieren kann.

Die Voraussetzungen für diese Beschreibung ist, dass in Eclipse bereits die Targetplattform für Notes eingerichtet ist und sich Notes aus Eclipse heraus starten lässt. Entweder mittels Expeditor Toolkit oder der Beschreibung von Mikkel Heisterberg. 

Dann legen wir mit dem entsprechenden Assistenen in Eclipse ein neues Pluginprojekt an.

In diesem neuen Plugin müssen wir folgende zusätzliche Abhängigkeiten hinzufügen:
 
Diese sind notwendig, damit man das UI von Lotus Notes erweitern kann.

Danach kann man in seinem Plugin den Erweiterungspunkt "com.ibm.rcp.ui.launcherSet"erweitern.



Dann sollte man einen LaunchItemType anlegen. Ein LaunchItemType kann für mehrere LaunchItems zuständig sein. Die Klasse MyLaunchHandler müssen wir später noch selbst anlegen.


Jetzt können wir die von uns gewünschten LaunchItems anlegen. Zuerst "Test1"

Natürlich kann man wenn gewünscht auch noch Icons angeben.

und dann als Übung einfach noch ein zweites mit "Test2" anlegen.


Dann muss man die Klasse MyLaunchHandler erstellen. Normalerweise kann man das bequem über den Link "class*:" beim LaunchItemType machen. Aber die IBM hat obwohl Lotus Expeditor noch ziemlich neu ist schon ein ziemliches Chaos angerichtet und deshalb gibt es die Basisklasse LaunchItemContribution bereits in zwei verschiedenen Paketen. In der Extensionpointbeschreibung ist blöderweise die falsche angegeben. Aber kein Problem einfach über den Eclipse Assistenten eine neue Klasse erstellen.


Wichtig ist, dass der Name übereinstimmt mit dem Klassennamen den wir im Type angegeben haben. In Echt wird man das ganze natürlich in ein vernünftiges Paket packen. Weiters ist wichtig, dass man als Superclass die LauncherContributionItem Klasse aus com.ibm.rcp.ui.launcher und nicht die aus jfacex nimmt.

In die Neuerstellte Klasse muss dann nur noch eine Launchmethode eingefügt werden.

public void launch(int arg0) {
        super.launch(arg0);
        MessageBox msg = new           MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
        if (getId().equals("org.startmenuitem2")) {
            msg.setMessage("Es wurde Test2 ausgewählt");
        }
        if (getId().equals("org.startmenuitem1")) {
            msg.setMessage("Es wurde Test1 ausgewählt");
        }
        msg.open();
    }


Diese wird aufgerufen jedes Mal wenn ein Launchitem des von uns erstellten Typs aufgerufen wird. Anhand von getId() kann man dann abfragen welches item konkret der Benutzer aufgerufen hat. Wir zeigen dann mal die entsprechende Messagebox an. In meinen Plugins wird meist ein Assistent aufgerufen, der von Benutzern einige Eingaben erfordert und dann z.B. eine neuen Reiter mit einem Eclipse UI im Notes öffnet.

ad