Friday, December 28, 2012

Notes 9 als Anwender; erster Blick Hui, zweiter Blick leider Pfui.

Endlich über die Weihnachtsfeiertage bin ich auch mal dazu gekommen mir die Beta von Notes 9 Social Edition im Detail anzusehen. Vorweg schicken möchte ich, dass ich Notes seit über 12 Jahren sowohl als Anwender als auch als Administrator und auch als Entwickler verwendet habe und auch nach wie vor verwende. Ich stehe Notes prinzipiell sehr positiv gegenüber und war auch immer ein Verfechter des Standardclients auch wenn die ersten Versionen des Standardclients sehr buggy waren. Ich war aber speziell ab der 8.5.1 der Meinung, dass Notes auf einen sehr guten Weg war. Die neuen Features wurden immer stabiler und auch die Performance wurde langsam besser. Als ich dann gehört habe, dass bei Notes 8.5.4 bzw. jetzt Notes 9 nocheinmal Verbesserungen am UI gemacht würden, war ich begeistert.

Für den Test habe ich eine komplett neue Umgebung installiert, ohne irgendeine Übernahme von Altdaten, damit sollten die besten Voraussetzungen geschaffen sein, dass Notes 9 sein volles Potential ausspielen kann.

Der Startschirm von Notes sieht auch wirklich sehr gut aus. Die reduzierte Farbwahl und die Fonts sehen wirklich sehr stimmig aus.



Auch die Mailansicht sieht auf den ersten Blick sehr ansprechend aus und erzeugt ein starkes ich will das so bald wie möglich auf meinem Arbeitsrechner haben .



Aber all diese Dinge haben auch schon in 8.5.3 schon ziemlich gut ausgesehen und leider hat die IBM es versäumt das wichtigste zu machen. Die Dinge in Notes die bisher schon total veraltet und vom Design inkosistent waren auf das Design von 8.5.3 oder 9 zu heben. In diesem Bereich versagt Notes 9 völlig. Eher ist es so, dass neue Inkosistenzen dazu gekommen sind.

Und bei diesen Dingen im alten Design handelt es sich nicht etwa um wirklich selten verwendete Features. Hier ein paar Beispiele:



Der Öffnen Dialog eine sehr of verwendete Funktion sieht immer noch weitgehend wie in Notes 7 aus. Die Fensterdekorationen und auch der Hinweis für die Vergrösserung des Fensters sind für Windows 7 vollkommen untypisch. Die Icons passen überhaupt nicht zum Rest des Clients. Die Knöpfe sehen ziemlich nach Windows 98 aus.

Übrigens im Designer wurde der selbe Dialog gegen einen neuen ausgetauscht, der nicht nur eine Windows 7 Fensterdekoration sondern auch die richtigen Knöpfe und auch ganz andere Icons für die Anwendungen und Ordner verwendet. Man muss aber sagen, dass auch diese Icons noch im 8.x Gewand sind und von der Farbe nicht zu Notes 9 passen :-( Wie viel Aufwand wäre es wohl gewesen diesen Dialog ohne Workingset Rubrik auch im Client zu verwenden.


Super konsistent sind auch die Beschreibungen ("On My Computer" vs. "local").


Fast noch schlimmer, der Passwortdialog von Lotus Notes der ebenfalls schon lange nicht mehr neu designed wurde.



Dieser Dialog ist die erste Visitenkarte die jeder Anwender bei der Verwendung von Notes bekommt. Leider vermittelt dieser Dialog und vor allem auch der Schlüsselring ein für das Jahr 2012 unerträgliches Windows 98 Feeling.

Alle Dialogboxen in Notes verwenden total unterschiedliche UI Widgetsets.Hier mal eine kurze Zusammenstellung was die UI Designer der IBM so an verschiedenen Buttonstilen zu bieten haben.

 
 


Es kann doch nicht sein, dass ein und die selbe Awendung vier verschiedene Stile für normale Schaltflächen in Dialogen (In normalen Notesmasken wird natürlich wieder eine andere Optik benützt) verwenden muss. Bevor man das bereits gute Design der Notes PIM CA's wieder komplett überarbeitet wäre es doch mal an der Zeit gewesen, eventuell alte Dialoge an das aktuelle Design anzupassen. Übrigens wenn man für die Schaltflächen die Widgets vom Operating System verwenden würde, wie in den Eclipseteilen der Anwendung, dann hätte man automatisch immer einheitliche Schaltflächen und diese würden auch immer automatisch nativ aussehen.

Das selbe gilt natürlich für alle anderen UI Elemente genauso. z.B. für die Checkboxen.

Einmal richtig, wie es unter Windows 7 aussehen soll.

Dann wieder im Stil von Windows 98 und das nicht etwa an einer ganz anderen Stelle sondern im selben Einstellungsdialog von Notes 9!

Ebenfalls unverständlich die schon unter Notes 8 wie ein Fremdkörper wirkende Follow Up Ansicht, die bei uns in der Firma sehr gerne verwendet wird, wurde nicht aktualisiert. Wie viel Aufwand wäre das wohl gewesen (hochgerechnet mit testen und übersetzen vielleicht 1 Mannwoche).


So passen halt weder die Schriftarten, noch die Aktionsleiste noch die Spaltenköpfe noch das generelle Design der Ansicht zum neuen bzw. auch nicht zum 8.x.x Inbox design.

Das in der Maildatenbank für die Follow Up Funktion unterschiedliche Symbole für die Darstellung der selben Priorität verwendet werden, ist da nur noch die Spitze des Eisbergs.

z.B. im Dialog zur Erstellung eines Follow Ups
 z.B. in der Inbox View


Sollte doch normal eine Bildressource sein und dann überall gleich aussehen oder nicht?

Auch uralte Bugs bei der Fontauswahl wie z.B. SPR KSOR/USEJ3 im Standardclient sind in Notes 9 noch immer nicht gefixt.

Man könnte diesen Rant jetzt ewig fortsetzen, aber das erspare ich mir. Als Resumee bleibt Notes 9 sieht auf den ersten Blick fantastisch aus, aber leider hat die IBM wieder nur die Komponenten die bereits unter Notes 8 Standard sehr gut ausgesehen haben verbessert. Die anderen Komponenten sehen nach wie vor wie in Notes 7 aus und werden berechtigterweise von Endbenutzern sehr häufig kritisiert.

 P.S. Ich bin nach wie vor der Meinung, dass IBM Notes eine gute Software ist, aber leider wird nie dieser letzte Aufwand betrieben um die ganzen Ecken und Kanten abzuschleifen die derzeit verhindern, dass Notes ein von Anwendern gliebtes Programm wird.


Für weitere Infos zur neuen Version schauen Sie auch auf meine Übersicht über alle Postings zum Thema Notes/Domino 9

Thursday, December 27, 2012

Hochperformante und sichere SQL Zugriffe in Java

Beim Zugriff auf SQL Datenbanken wird immer gerne der Fehler gemacht, dass wenn ein SQL Statement mehrmals mit verschiedenen Parametern ausgeführt werden soll nicht die dafür vorgesehenen PreparedStatements sondern die SQL Befehle mit String Konkatinierung  zusammengebaut und mit execute() ausgeführt werden. Sogar in Java Lehrbüchern findet man immer wieder Beispiele wie das folgende, dass man aber in der Praxis auf keinen Fall so verwenden sollte.

public class StatementJDBC {
 private static Statement stmt;
 /**
  * @param args
  */
 public static void main(String[] args) {
  //Verbinde mit Datenbank und initalisiere Anweisung
  AS400JDBCDataSource dataSource = new AS400JDBCDataSource("localhost", "user", "password");
  try {
   Connection con=dataSource.getConnection();
   stmt=con.createStatement();
  //Gib verschiedene Namen aus der Adressdatei mit verschiedenen Adressnummern aus.
   Date start=new Date();
   System.out.println(getName("40"));
   System.out.println(getName("41"));
   System.out.println(getName("42"));
   System.out.println(getName("43"));
   System.out.println(new Date().getTime()-start.getTime());
  } catch (SQLException e) {
   e.printStackTrace();
  }
 }
 /**
  * Liest mittels SQL den Namen eines Adressatzes.
  * 
  * Hier  wird jedes mal ein SQLStatement mit String Konkatinierung zusammengebaut 
     * und ausgeführt. 
     *
     * Dies soll man in der Praxis nicht machen.
  * @param adressNummer
  * @return Name
  */
 private static String getName(String adressNummer) throws SQLException {
  ResultSet rs=stmt.executeQuery("select * from adressen where adnr="+adressNummer);
  if(rs.next()) return rs.getString("adnam1");
  return "";
 }

}

Es gibt zwei Gründe warum man seinen SQL Code nicht jedes mal als String zusammenbauen und dann mit execute() ausführen soll.

Bei wiederholter Ausführung des selben SQL Code ist die Performance viel besser, wenn der SQL Code nicht jedesmal geparst, geprüft und in einen Zugriffsplan übersetzt werden muß. Der Vorgang ein SQL Kommando in einen Zugriffsplan zu übersetzen ist je nach verwendeter Datenbank und Komplexität der Anweisung extrem aufwendig. Der oben angeführte Codeblock in dem die Zeit gemessen wird, läuft wenn man Preparedstatements verwendet, ca. 3 mal schneller als mit dem oben angeführten Code.

Was aber fast noch wichtiger ist, wenn ich SQL Befehle mit String Konkatinierung zusammenbaue, dann wird der Code anfällig für SQL Injections. Das heißt, es kann in der Variable mit ein paar Tricks beliebiger SQL Code eingeschleust werden. Dies ist eine der häufigsten Einfallstore für Hacks auf Webseiten. Also selbst wenn ein Statement nur einmal verwendet wird, sollte man auf jeden Fall um SQL Injections zu vermeiden Preparedstatements verwenden.

Noch dazu ist die Verwendung von Preparedstatements eigentlich extrem einfach. Man ändert den Typ von stmt auf PreparedStatement, und erstellt das Objekt mit prepareStatement(). Für jeden Wert der in der Anweisung variabel sein soll fügt man ein Fragezeichen ein. Der sqlCode aus obigen Beispiel heißt dann "select * from adressen where adnr=?". Bei jeder Verwendung der SQL Anweisung muss dann mit der passenden set Methode der Platzhalter im Statement mit dem richtigen Wert befüllt werden. Am leichtesten zu verstehen ist es wenn man sich folgendes Beispiel anschaut.
public class PreparedStatementJDBC {
 private static PreparedStatement stmt;
 /**
  * @param args
  */
 public static void main(String[] args) {
  //Verbinde mit Datenbank und initalisiere Anweisung
  AS400JDBCDataSource dataSource = new AS400JDBCDataSource("localhost", "user", "password");
  try {
   Connection con=dataSource.getConnection();
   stmt=con.prepareStatement("select * from adressen where adnr=?");
  //Gib verschiedene Namen aus der Adressdatei mit verschiedenen Adressnummern aus.
   Date start=new Date();
   System.out.println(getName("40"));
   System.out.println(getName("41"));
   System.out.println(getName("42"));
   System.out.println(getName("43"));
   System.out.println(new Date().getTime()-start.getTime());
  } catch (SQLException e) {
   e.printStackTrace();
  }
 }
 /**
  * Liest mittels SQL den Namen eines Adressatzes.
  * @param adressNummer
  * @return Name
  */
 private static String getName(String adressNummer) throws SQLException {
  //Parameter im vorbereiteten Statement setzen.
  stmt.setInt(1, new Integer(adressNummer).intValue());
  ResultSet rs=stmt.executeQuery();
  if(rs.next()) return rs.getString("adnam1");
  return "";
 }

}

Die Preparedstatements können natürlich nicht nur für "Selects", sondern genauso gut auch für "Inserts" und "Updates" verwendet werden. 

Wer JDBC Zugriffe in seinen xPages, Plugins oder Agenten verwendet, sollte prüfen ob er auch wirklich überall schon PreparedStatements verwendet. Der Performancegewinn und vor allem die zusätzliche Sicherheit vor SQL Injections sollte den geringen Änderungsaufwand auf jeden Fall lohnen.

Monday, December 24, 2012

Frohe Weihnachten

Ich möchte allen Lesern meines Blogs ein besinnliches und frohes Weihnachtsfest wünschen. Wir sollten diesen Tag nützen, um uns im Kreise der Familie vom Weihnachtsstreß zu erholen und darüber nachzudenken was die wirklich wichtigen Dinge in unserm Leben sind. Das Christus vor über 2000 Jahren nicht in eine reiche Königsfamilie sondern in eine arme Handwerkerfamilie geboren wurde soll uns zum Nachdenken anregen, dass das Anhäufen von irdischen Gütern nicht der alleinige Zweck unseres Daseins sein kann.

Quelle Openclipart Library

Friday, December 14, 2012

"equal" vs "==" in Java

Ein typischer Anfängerfehler in Java ist, zwei Objekte in einer if Klausel mit "==" statt mit .equals() zu vergleichen. Dies ist aber in den meisten Fällen grundfalsch. Das tückische dabei ist, dass der Fehler nicht immer sofort auffällt, da unter manchen Umständen der Vergleich mit "==" auch funktioniert.

Um zu verstehen warum die beiden Vergleiche unterschiedlich sind, muss man zuerst wissen, wie Java eigentlich Objekte verwaltet. Wenn mit new ein neues Objekt erzeugt wird und einer Variable zugewiesen wird, wird nicht das Objekt in der Variable gespeichert, sondern das Objekt wird am Heap (Hauptspeicher) erzeugt und ein Zeiger auf die Stelle im Heap an der das Objekt gespeichert ist, wird der Variable zugewiesen. Man kann sich sozusagen die Variable als einen primitiven Datentyp vorstellen, der eine Referenz auf den Heap speichert. Mehrere Variablen können die selbe Referenz auf das selbe Objekt im Heap zugewiesen haben. Wenn nun mit "==" verglichen wird, wird das Objekt im Heap gar nicht angeschaut, sondern es wird nur verglichen, ob die Variable die gleiche Referenz enthält. Wenn ja wird true zurückgeliefert und wenn nein wird false zurückgeliefert. Ganz anders verhält es sich beim equals(). Hier müssen die Objekte nicht einmal vom gleichen Typ sein, sondern es kommt auf die Implementierung der "equals" Methode an, ob das Ergebnis true oder false ist. Im Normalfall wird man immer mit equals() vergleichen, da ich im Normalfall wissen will, ob es sich um ein logisch gleiches Objekt handelt und nicht um das selbe Objekt. Wenn alles so klar ist, warum funktioniert dann folgender Code:
String test1="Test";
String test2="Test";
System.out.println(test1==test2); //Bitte auf keinen Fall so machen
System.out.println(test1.equals(test2); //So vergleicht man richtig.
Im Normalfall würde man erwarten, da man zwei String Literale hat, dass der erste Vergleich false ergeben würde. Doch die JVM verwendet für Stringliterale einen Cache und so wird der zweite Literal wegoptimiert und beide Variablen verweisen auf das selbe Objekt im String Cache. Nur sollte man sich auf keinen Fall auf das verlassen, da eine andere JVM Implementierung auf diesen Cache eventuell verzichtet, oder der Cache bereits voll ist und dann hat man plötzlich zwei unterschiedliche Objekte und der erste Vergleich ergibt false.

Thursday, December 6, 2012

System.out Ausgaben in eine Datei umleiten

Manchmal hat man Java Programme, die statt einem Loggingframework alle Statusausgaben mit System.out.println() machen. Wenn nun so ein Programm während der Ausführung abstürzt hat man natürlich nicht mehr viel von den Statusmeldungen. Wenn ich so ein Javaprogramm habe, mache ich einfach eine kleine Wrapperklasse darum, mit der die Standardausgabe in eine Datei umgeleitet wird.

Ein kleines Beispiel:

public class LogginWrapper {
   /**
    * @param args
    */
    public static void main(String[] args) {
       // Leite die Ausgabe von Standard Out und ERR in Log Dateien um
 try {
   System.setOut(new PrintStream(new FileOutputStream(System.getenv("TEMP") 
  + File.separator + "$$log" + new Date().getTime() + ".out")));
   System.setErr(new PrintStream(new FileOutputStream(System.getenv("TEMP") 
  + File.separator + "$$log" + new Date().getTime() + ".err")));
 } catch (FileNotFoundException e) {
   e.printStackTrace();
        }  
      // Aufruf des Programms ohne Loggingframework.
  Test.main(args);
   }
}
Dann kann man einfach statt der Programmklasse die Wrapperklasse aufrufen und alle Ausgaben an Out und ERR werden in das Tempverzeichnis geschrieben. Im Fehlerfall hat man dann Anhaltspunkte was schiefgegangen ist.

Tuesday, December 4, 2012

Fehlerquelle beim Dividieren von BigDecimals in Java

Ich verwende bei Berechnungen sehr gerne die BigDecimal Klasse von Java. Da mit dieser beliebig große Zahlen und auch in beliebig großer Genauigekeit gerechnet werden können. Beim Dividieren bekommt man aber bei Berechnungen die zu keinem eindeutigen Ergebnis (z.B. 10/3) kommen die Exception "Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result." 

Deshalb sollte man bei Divisionen mit BigDecimal immer die Anzahl der gewünschten Dezimalen und den Rundungsmodus wie in der letzten Berechnung des kleinen Beispiels angeben.
 
public static void main(String[] args) {
 // Berechnung ergibt das endliche Ergebnis 0.5
 System.out.println(new BigDecimal(1).divide(new BigDecimal(2)));
 // Hier tritt eine ArithmeticException auf.
 System.out.println(new BigDecimal(10).divide(new BigDecimal(3)));
 // Berechnung ergibt 3.33 und wirft keine Exception, da wir die Anzahl
 // der Stellen beschränkt haben. Das Ergebnis ist aber natürlich nicht
 // 100% korrekt, aber in der Praxis bei für die Anwendung genügend
 // signifikante Stellen ausreichend.
 System.out.println(new BigDecimal(10).divide(new BigDecimal(3), 2, BigDecimal.ROUND_HALF_UP));
} 

Einen Notesagenten aus einem cmd script starten.

Im atnotes Forum wurde die Frage gestellt, wie man einen Notes agent aus einem cmd Befehlszeilenscript aufrufen kann. Über Befehlszeile oder über eine URL ist das relativ schwierig, da aber Lotus Notes sein API über COM anbietet ist es ein leichtes dass in einem vbscript zu implementieren. Dieses vbscript kann dann aus dem cmd script aufgerufen werden.

Dim s
Dim db
Dim agent
Set s=CreateObject("Lotus.NotesSession")
Call s.Initialize
Set db=s.GetDatabase("servername","db.nsf")
Set agent=db.GetAgent("agent")
Call agent.Run
servername, db.nsf und agent müssen natürlich angepasst werden.

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

ad