Showing posts with label Tipp. Show all posts
Showing posts with label Tipp. Show all posts

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.
 }
}

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.


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.

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.

Wednesday, October 31, 2012

vbscript in eclipseplugin verpacken und aufrufen

Ein vbscript in ein Eclipse Plugin einbauen und aus diesem Plugin aufrufen, ist nicht ganz einfach. Folgende Schritte müssen beachtet werden.

1. Das vbscript muss in das Plugin entweder direkt in das Hauptverzeichnis, oder ein beliebiges Unterverzeichnis des Plugins importiert werden.



2. In den build.properties des Plugins muss das script zum export ausgewählt sein, da es sonst beim Pluginexport nicht in das Plugin eingefügt wird.


3. In dem Feature mittels dem dieses Plugin installiert wird, muss man die Option, dass das Plugin bei der Installation entpackt wird, anwählen. Sonst wird das vbscript in ein jar eingepackt und man kann es nicht ohne grössere Probleme aufrufen.


4. Wenn die Vorbereitungen passen, dann kann man das vbscript aus jeder Eclipse RCP wie z.B. Lotus Notes mit folgenden Javacode aufrufen.


URL url = FileLocator.find(Activator.getDefault().getBundle(), 
     new Path(File.sperator+"test.vbs"), null);
Process p = Runtime.getRuntime().exec(System.getenv("WINDIR") + File.seperator+"system32"+File.seperator+"cscript.exe \""
     + FileLocator.resolve(url).getPath().substring(1) + "\"");

Natürlich kann man mit der selben Vorgehendsweise auch andere ausführbare Dateien, die in einem Plugin enthalten sind aufrufen.

Ich verwende diese Technik z.B. in einem Plugin das in Lotus Notes mithilfe von externen vbscripts ein Hardware und Softwareinventory des Rechners auf dem der Notesclient installiert ist erstellt. Wie man seinen Code bei jedem start des Notesclient automatisch ausführen kann, hebe ich mir dann für einen anderen Post auf.

Tuesday, October 30, 2012

Icons zur Verwendung in eigenen Projekten

Die meisten Programmierer sind wahrscheinlich genauso wie ich schlechte Grafiker. Damit Anwendungen aber eine gute usability haben ist es  oft unumgänglich, dass man in seinen Anwendungen mit Grafiken arbeitet. Oft ist aber entweder kein Grafiker bei der Hand, oder für das Projekt zu teuer. In diesem Fall hat mir bisher sehr oft die Open Icon Library aus der Patsche geholfen. In dieser sind tausende qualitativ sehr hochwertige Icons und Symbole die man frei (natürlich empfiehlt es sich das Kleingedruckte zu lesen) benutzen darf. Falls man für seinen speziellen Anwendungsfall kein Icon findet, dann hat man sicher gute Anregungen für sein eigenes Icon. Wenn man selbst Icons erstellt hat, wäre es natürlich fantastisch, wenn man seinerseits dieses Icon in die Open Icon Library hochlädt.

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();

Friday, October 19, 2012

Selektives Auto Deployment von Widgets

Das Verpacken von Eclipse Features und Plugins in Widget ist wirklich eine praktische Sache und über den Widgetkatalog kann man diese Widgets auch gut anderen Benutzern zur Verfügung stellen. Weniger elegant sind aber die Möglichkeiten mit denen man die Installation von Widgets automatisieren kann. Standardmäßig kann man einem Widget mehrere Kategorien zuweisen. Man kann dann bei einem Benutzer eine oder mehrere dieser Kategorien in den Benutzervorgaben einstellen (oder per Desktop Policy setzen) und es werden alle Widgets dieser Kategorien bei dem Benutzer installiert und im Bedarfsfall aktualisiert.

So weit so gut, aber bei intensiverer Verwendung dieser Technik zeigen sich doch ein paar Schwachstellen:

  • Man kann zwar über Desktoppolicy festlegen welche Kategorien von Widgets bei einem Benutzer installiert werden sollen, ich kann aber nicht verhindern, dass sich ein Benutzer andere Widgetkategorien selber nachinstalliert. Das ist natürlich problematisch, wenn man Widgets hat die nicht jeder Benutzer installieren können soll.
  • Wenn man mehr als eine handvoll Widgets und Gruppen von Personen die unterschiedliche Arten von Widgets benötigen hat dann braucht man sehr viele verschiedene Desktoppolicy Dokumente und das ganze wird schnell unübersichtlich.
  • Die zur Verfügungstellung von neueren Versionen von Widgets zum Testen ist schwer oder gar nicht möglich.

Da ich in meiner Umgebung mittlerweile von allen oben angeführten Problemen betroffen bin, habe ich eine Möglichkeit zur Lösung der Probleme gesucht die ich kurz erklären möchte.

Ich habe das Design des Widgetkatalogs dahingehend erweitert, dass ich ein Mehrfachwert Leserfeld zu der Widgetmaske hinzugefügt habe. Weiters habe ich das bereits bestehende berechnete Leser Feld in der Maske von "" auf "LocalDomainServers" geändert, damit es keine Probleme bei der Replizierung von Widgets zwischen den Servern gibt.


Nun habe ich die Kategorie aller Widgets auf eine Kategorie gesetzt die bei allen Benutzern installiert wird und kann aber über das Leserfeld ganz exakt steuern wer dieses Widget bekommen soll oder nicht. Natürlich lassen sich in dem Leserfeld auch wieder Gruppen eintragen.

2 Nachteile dieser Lösung:

  • Designänderungen von Systemdatenbanken bergen immer wieder das Risiko, dass es bei Updates zu Problemen kommt. 
  • Wenn man einen Benutzer aus dem Leserfeld entfernt, wird zwar das Widgetdokument aus seinem lokalen Widgetkatalog entfernt aber leider der Provisioningprozess nicht angestartet. Das heißt das Widget wird nicht sofort entfernt, sondern erst bei der nächsten Änderung eines Widgets das auf den Rechner des Benutzers repliziert wird. Ich löse das Problem in dem ich ein Dummywidget habe, dass ich bei einer Entfernung eines Benutzers neu speichere. Dann erkennt der Notesclient die Änderung und entfernt auch das verschwundene Widget aus dem Client.

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.

Wednesday, August 22, 2012

Automatisches Upgrade der ODS am Client

In jeder grösseren Notes/Domino Version wird die sogenannte ODS (On Disk Structure) verändert. Die ODS Version ist das Dateiformat in dem Notes/Domino die Daten der betreffenden Replik lokal speichert. Die ODS Version wird nicht repliziert. Das heißt eine Replik am Server kann eine andere ODS Version als der Client haben. Bei einem Upgrade auf eine neue Version wird die ODS Version am Server meistens mit einem copy style compact auf die aktuelle Version gehoben. Diese Änderungen wird aber dann nicht auf lokale Repliken übertragen.

Um auch die lokalen Repliken auf den neuesten Stand zu bringen, gibt es aber seit 8.5.2 den Notes.ini Parameter NSF_UpdateODS=1. Wenn dieser Parameter gesetzt ist, versucht Notes alle Datenbanken auf die neueste ODS Version zu konvertieren. Ein Teil der Datenbanken wird als Hintergrundtask konvertiert. Der Rest wird beim nächsten Starten des Client abgearbeitet. Ab Version 8 aufwärts muß man zusätzlich noch die Verwendung  der neuen ODS mit der Notes.ini Variable Create_R85_Databases=1 (oder für ODS 4.8 Create_R8_Databases=1) erlauben.

Der Parameter  NSF_UpdateODS=1 kann auch über eine Desktop Policy (Mail Tab->Alle lokalen NSF-Datenbanken auf die neueste ODS-Version aktualisieren) gesetzt werden.

 Für Details zu dieser neuen Funktion gibt es auch eine Technote der IBM
 

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.

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.

Monday, May 21, 2012

Performanceverbesserungen durch Indexonly Access beim Zugriff auf die DB/2

Sehr oft braucht man bei relationalen Zugriffen nicht den ganzen Datensatz sondern nur ein Feld. Ein typisches Beispiel ist der Kundenstamm. Meistens braucht man in Selects für die Anzeige nur den Kundennamen nicht aber den Rest der Informationen. In einer Auftragsanzeige könnte z.B. die Auftragsnummer, Kundennummer ,Kundenname, Artikelnummer angezeigt werden.

Das SQL Statement dafür wäre:

select auftraege.auftragsnummer, auftraege.kundennummer, kunden.kundenname, auftraege.artikelnummer from auftraege, kunden where auftraege.kundennummer=kunden.kundennummer

Um die Verarbeitung zu beschleunigen wurde ein Index über die Kundentabelle mit der kundennummer als Unique Key erstellt. Damit wird bei der Verarbeitung folgender Zugriffsplan verwendet:

Die Auftragsdatei wird mittels Tablescan komplett durchgelesen. Für jeden Auftrag wird ein Index Zugriff durchgeführt und mit den Auftragen zusammengejoint. Dann muss aber noch für jeden Kunden ein extra Zugriff auf den Satz in der Kunden Tabelle erfolgen, um den Namen zu lesen. In unserer Beispieldatenbank sind weil wir 14.941 Aufträge haben dafür genau so viele Zugriffe notwendig.

Wenn diese Abfrage in der Anwendung sehr oft verwendet wird, entsteht natürlich ein relativ großer Overhead. Dies kann man durch einen besseren Index, der die Verwendung von Index only Access erlaubt verbessert werden.

Wir ergänzen daher unseren Index und fügen hinter der kundennummer noch das Feld kundenname an.

Wenn wir nun die selbe SQL Anweisung wie oben ausführen, bekommen wir einen schnelleren Zugriffsplan:


  
Die aufwendige Verarbeitung des Tableprobes ist komplett weggefallen. In den Erläuterungen zu dem Zugriffsplan steht auch, dass die Indexonly Methode für den Zugriff verwendet wurde.

Bei meinen Tests konnte ich eine 25% Performanceverbesserung des Query mit dem verbesserten Index gegenüber der ursprünglichen Variante feststellen.

Der Indexonly Zugriff beschleunigt nicht nur Joins sondern auch normale Zugriffe.

Das SQL:

select kundenname from kunden where kundennummer=4711

wird wenn der um kundenname erweiterte Index vorhanden ist mit einem optimierten Zugriffsplan ausgeführt.

statt dem schlechteren Zugriffsplan wenn nur die kundenNummer im Index vorhanden ist.
Die Tests wurden auf einen i/5 V6R1 durchgeführt. Sollten sich aber auf anderen Systemen ebenfalls nachvollziehen lassen. Einfach einmal ausprobieren.

Sunday, May 20, 2012

Adresse mit Hilfe der Formelsprache auf Google Maps Karte zeigen.

Ich bin ja nicht gerade ein Fan der Formelsprache von Lotus Notes, aber manche Dinge lassen sich damit sehr elegant und einfach lösen. z.B. wenn man die Anforderung hat eine Adresse mittels Google maps nachzuschlagen, dann kann man folgende einfache Formel verwenden.:

@URLOpen("http://www.google.com/maps?f=q&hl=de&q="

+ @URLEncode("Platform" ; Strasse)

+ "+"
+ @URLEncode("Platform" ; Ort)+
"+"+
@URLEncode("Platform" ; Land))


Wen das aktuelle Dokument die Felder Strasse, Ort und Land (falls im Dokument nur das Länderkürzel steht muss hier noch mit einem kleinen DBLookup nachgeholfen werden) wird bei der Durchführung der Formel z.B. in einer Aktion im Defaultbrowser eine Googlemaps Karte mit einem Pin auf der gewünschten Adresse aufgemacht. Die Urlencode Formel ist wichtig, damit Sonderzeichen oder Umlaute richtig in die URL übersetzt werden. Natürlich kann man auf diese Art beliebige Urls zusammenbauen. Ich verwende das z.B. in einer Literaturdatenbank zum Anzeigen des Buches auf Amazon.


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();
}

Wednesday, April 25, 2012

Bilder von iPhone als Anhang im Lotus Notes Client

Seit einem Notes Update (8.5.3) hatten wir das Problem, dass Bilder die per mail von iPhones gesendet wurden nicht mehr als Dateianhang angezeigt wurden, sondern inline angezeigt wurden. Dies ist auch vom Notesclient soweit korrekt, da das iPhone die Bilder wirklich als inline schickt. Im Notes sind aber inline Bilder eher unpraktisch, da man diese nicht öffnen oder speichern kann, ausser über den Umweg der Zwischenablage.


Man kann das Anzeigen von Bildern als Inline Image in Notes aber einfach über die Einstellung


verhindern. Das bewirkt auch soweit ich bis jetzt gesehen habe keine anderen Seiteneffekte, da Bilder in HTML e-mails nach wie vor inline angezeigt werden. Der Haken im UI setzt die Notes.ini Variable ShowMIMEImagesAsAttachments=1. Man kann diese Variable natürlich auch über eine Desktop Policy auf allen Clients ausrollen.

Diese kleine Änderung kann die Userakzeptanz des Notes Client stark verbessern.
ad