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.

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.

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.

ad