[Howto] Maven: Wie man eine ausführbare Jar in eine Java-Webanwendung (War) via Webstart integriert.

Die Situation

Es bestehen zwei lauffähige, fertige Projekte in Maven, welche beide auch vollwertige Artefakte bilden können.

Zum einen die Webanwendung — nennen wir sie hier mal webportal — mit einem WAR-Artefakt, etwa für einen Tomcat. Ob das Artefakt als Snapshot oder Release gemacht wird, ob es nur generiert oder auch in ein Repository deployt wird, ist hierbei nicht von weiterer Bedeutung.

Zum anderen die normale Clientanwendung — nennen wir sie doch einfach userclient — mit einem JAR-Artefakt. Wichtig ist natürlich, dass hier eine startfähige Main-Klasse vorhanden ist. Dies sollte jedoch der Regelfall sein, daher nur der formale Hinweis.

Zusammengefasst, und der Auftrag an dieses Howto ist also: Wie konfiguriert und erweitert man den Buildzyklus in welchem Projekt an welcher Stelle am geschicktesten, um auf einfachem Wege die JAR-Datei userclient in die Webanwendung webportal als Java Webstart zu integrieren. Dabei ist es hilfreich, wenn man via pom.xml (und natürlich der Macht der Properties) die Versionen spezifizieren kann. Der gesamte Prozess von auswählen, signieren und ausliefern soll dabei automatisch und ohne weiteres Eingreifen des Entwicklers geschehen können. Idealerweise sollte das ganze auch optional sein — dazu gibt es dann mehr unter „Optimierungen und Verbesserungen“.

„[Howto] Maven: Wie man eine ausführbare Jar in eine Java-Webanwendung (War) via Webstart integriert.“ weiterlesen

Featurities meets Fallstrick: Die Spring Security 3.0 Konfigurationsodyssey

Mit dem Majorrelease 3.0 wurde dem Modul Spring Security eine Menge von neuen Features angeignet. Spring Security ist die Modulkomposition, welches für das Java Framework Spring quasi die gesamte Authentifizierung, Autorisierung, Legitimierung jedwegiger Art ermöglicht.

Leider wurden mit dem Release 2.x auf 3.0 eine Reihe von API-Changes vollzogen. Zugegeben, die waren auch sicher alle sinnvoll, weil Komponenten wie die Authentifizierung weiter geteilt wurden und man somit wesentlich flexibler ist, neue Anforderungen zu ermöglichen (Baukastenprinzip). Aber die Dokumentation ist – gesamtheitlich betrachtet – irgendwie immer noch mies und oft nicht aktualisiert. Oder man findet im Internet einfach nur (alte) Beispiele.

Die http-Direktive

Im Namespace von Spring Security existiert das Tagelement http, mit welchem man kurze, knappe und verständliche Konfigurationen anlegen kann. Der Vorteil liegt klar auf der Hand: Man muss nicht alle Beans, Listener und Provider anlegen, denn das geschieht automatisch. Tja, wären da nicht ein paar Einschränkungen in der Funktionsvielfalt.

Konkretes Beispiel: Remember Me

Just wurde das Minor-Release 3.0.1 veröffentlicht, und nur wenige Tage später zu erfahren, dass Remember Me kaputt sei. Egal, fahren wir erstmal weiter mit 3.0.0.

Laut Dokumentation ist es am einfachsten, wenn man die Direktive remember-me (Security Namespace) innerhalb der http-Direktive (Security Namespace) verwendet. Ohne irgendeine Angabe wird ein stinknormales, Cookie basiertes Tokenverfahren ohne (echten) privaten Schlüssel verwendet. Reicht für den ersten Einsatz erstmal auf, soll ja erstmal funktionieren.

Fehlermöglichkeit 1a: Man loggt sich ein, und es passiert nichts (kein „RememberMe“-Cookie).
Lösung: Wenn man einen eigenen Auth-Filter einsetzt, muss man diesem auch den RememberMe-Service „setten“. Außerdem muss der SecurityChainFilter (web.xml!) auch auf die login-Seite verweisen. Es dürfen auch keine Filter bei der Konfigurierung von intercepted Urls (speziell hier: login, logout) gemacht werden.

Fehlermöglichkeit 1b: Es passiert noch immer nichts?
Lösung: Vielleicht wurde vergessen, einen Parameternamen für den Request zu setzen. Der Standardname ist ein typischer Springname, der natürlich unschön ist. Und den kann man nicht über die RememberMe-Direktive setzen, also muss man eh einen eigenen Service definieren. Bäm. Referenzierung geht dann zwar noch, aber für mehr ist die RememberMe-Direktive dann nicht mehr zu gebrauchen.

Fehlermöglichkeit 2a: Man besucht die Seite ohne Login, aber mit Cookie – und die Loginseite kommt (Log sagt kein gültiger Auth).
Lösung: Man kann der Log trauen, wenn sie zwar beim Einloggen nun einen Token ablegt (kann man zum Beispiel sehr einfach mit diesem Firefox-Addon inkl. Editor(!) verifizieren), dieses aber beim erneuten Besuchen der Seite (bzw. ohne JSPSESSION-Cookie) nicht verwendet bzw. wird nicht erkannt. Schlussendlich half u.a. das Umbenennen der Userservices-Bean in „userService“. Außerdem sollte die Loginseite keinen Filter/Access haben (s.o.) Lieber 2x prüfen!

Fehlermöglichkeit 2b: Es erscheint eine Ausnahme, dass der Key falsch sei.
Lösung: Dazu muss man wissen: Sobald man eine individualisierte RememberMe-Konfiguration nutzt, wird auch der Key nicht mehr vernünftig auf alle Komponenten (Provider, Filter, Manager) gesetzt. Beim Anlegen wird also der eigene Key verwendet, beim Auslesen der Standardkey. Yes! (s.o.)

Fehlermöglichkeit 3: Man besucht die Seite ohne Login, aber mit Cookie – aber wie in 2 nur die Loginseite.
Lösung: Im Logger/Debugger kann man nun feststellen, dass zwar das Cookie gefunden wurde, das Token gefunden und validiert wurde aber dann keine Rechte existieren – aha? Wahrscheinlich fehlt im Provider noch ein zusätzliches Setting der Komponenten. Am besten von RememberMe Service/Filter/Provider jeweils alle möglichen Properties durchgehen. Jaja, wie gesagt.. 😉

Fehlermöglichkeit 4: Das Ausloggen (beispielsweise logout.html) hat nach Aktivierung von Rememberme plötzlich keine Auswirkungen mehr.
Lösung: Zwar wird die Seite gefunden, aber es wird kein „Logout“ gemacht. Auch hier sollte man prüfen, ob ein SecurityChainFilter (web.xml) auch für die logout-Seite greift.

Fehlermöglichkeit 5: Das Besuchen der Seite wirft einen Fehler (ggf. „mit weißer Seite“), dass keine neue Session erstellt werden kann.
Lösung: Richtig, nach einem Request ist ja dann auch zu spät. Das Attribut create-session in der Direktive http sollte daher auf „ifRequeried“ gestellt sein.

Fazit:

  • SecurityChainFilter immer prüfen
  • Intercepted Urls prüfen
  • RememberMe-Direktive innerhalb der http-Direktive ist quasi abgesehen von der services-ref unbrauchbar.

Anmerkung:

Natürlich kann man sich das Problem mit den SecurityChains vom Hals schaffen, indem man stupide ein /* filtert. Das hat jedoch zur Auswirkung, das Spring Security auch jeden verdammten Request anguckt; bei zusätzlichen (statischen) Inhalten wie Javascript, Stylesheets, Bildern, Flash u.ä. ist das ein Overhead, der unnötig ist.

Java-Swing-Komponenten als Bild / Snapshot

Für unsere Anwendung (ähm, halt Diplomarbeit ftw!) wollte ich eine Export-als-Bild-Funktion für die Anzeige des aktuellen Graphen einbauen. Dabei handelt es sich um eine JComponent, die über das JUNG-Framework dargestellt wird.

Während jetzt Apple-Benutzer leichtfertig über dieses Feature lachen können, ist dies für Hauptzielgruppe der Anwendung (also Windowsbenutzer) eine sinnvolle Funktion. Es hat sicherlich wenig Sinn, umständlich einen Komplett-Screenshot anfertigen zu müssen.. viel einfacher kann das Programm das ja selbst machen.
Es gibt eine Reihe von Suchtreffern in Google, die durchaus lesenswert sind.

Die Grundidee ist, dass man sich selbst ein Graphics-Objekt erstellt und dann die JComponent darauf arbeiten lässt. Im Falle von Swing ist dies idR vom Framework vorgegeben (nämlich für den Bildschirm), hier aber wollen wir ja ein eigenes Bild haben. Die Höhe und Breite ist in den meisten Fällen von der Komponente zu erfragen.
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

Anschließend brauchen wir das zugehörige Graphics-Objekt, denn damit arbeiten die Paint-Methoden. Clue Nummer 1: Man könnte in Versuchung kommen, und einfach getGraphics() zu nehmen – ist ja auch naheliegend. Tatsächlich erstellt das aber auch nur ein Graphics-Objekt, während createGraphics() ein Graphics2D-Objekt erstellt. Und in den meisten Fällen braucht man das auch.
Graphics2D g = bi.createGraphics();

Anschließend muss man sich entscheiden, welche Zeichenroutine man anstoßen will. Es gibt prinzipiell folgende Möglichkeiten: paint(Graphics), paintComponent(Graphics) und paintAll(Graphics). Sofern paintComponent() ausreicht, diese nutzen. Im Falle des JUNG-Graphen mit verschiedenen Subelementen und eigenem Renderer funktioniert jedoch nur paintAll(Graphics) auf allen System zuverlässig. Während paint(Graphics) unter MacOS und Linux noch funktioniert, produziert es unter Windows unschöne Ergebnisse.

Anschließend kann man mit dem statischen Aufruf ImageIO.write(bi, "png", file); das gesamte Bild als — hier PNG — speichern. Achtung: Dies ist eine JavaSE6-Klasse, in den Vorgängern benötigt man dann den Umweg über FileStream & Co.

[1] jung.sourceforge.net
[2] java.sun.com

Code-Coverage, Eclipse, jUnit

eclemma-1

Joa, es ist wieder Zeit für einen Artikel in der Java/DA-Reihe.

Als erstes und sehr einfach zu testendes Kriterium gilt beim Testen die Code Coverage. Eigentlich sehr simpel, denn es quantifiziert nur die Code-Coverage, also von wie vielen Zeilen Code (Instructions) tatsächlich wie viele (bzw. welche) auch ausgeführt werden. Da man dies natürlich gerne automatisiert machen möchte, sind Tests wie mit jUnit ideal. Aber, auch normale Anwendungen sind mitzutracen und den Codecoverage ist zu ermitteln.

eclemma-2

Für Eclipse gibt es dazu ein prima Plugin namens EclEmma. Und auch schnell über die Update-URL im Manager installiert. EclEmma dient dabei als eine Art Wrapper für verschiedene Launchtypen – also beispielsweise als normale Applikation oder einen jUnit-Test bzw eine ganze Suite (Plugins). Das Ergebnis wird nach Beendigung des Programms/Tests innerhalb von Eclipse in einer Baumstruktur dargestellt; sehr schön ist auch die grafische Hervorhebung direkt im Sourcecode. Alternativ kann man sich das Ergebnis auch als HTML exportieren lassen.

Yaml, Yuml, ja was denn?

Für große Datenbanklayouts braucht man irgendwann Diagramme; die werden entweder mittels „ER-Syntax“ oder im „modernen“ UML entwickelt. Bei kleineren Projekten oder Veranschaulichungen kann so ein UML-Editor aber schnell zum exponentiellen Overhead werden, weil es sich einfach weder lohnt noch in der „Projektlage“ jeder damit genug auskennt. In diese Richtung stoßt yuml.me, die mit Hilfe einer wirklich einfachen Syntax wunderschöne UML-Diagramme zaubern. Die sind zwar ggf. nicht 100% UML2-tauglich (bzw. haben nicht alle Funktionen des Standards), aber das Ergebnis ist durchaus sehenswert und für die meisten kleinen Dinge mehr als ausreichend.

Auf Seite der Konfiguration ist es natürlich blöd, dauernd mit diesen URLs zu hantieren. Aber, es gibt für alles eine Lösung: MMan nehme YAML. Sehr hübscher Syntax, fast ohne Overhead und damit viel Platz für das eigentlich Wesentliche: Informationen und Daten. Das Doctine Project, ein PHP Object Relational Mapper, nutzt zur Definition von Datenbankschemata auch eine YAML-Struktur (Beispiel).

Da ich weder Lust auf PHP coden noch das Aufsetzen eines Java-Servers hatte, habe ich eine kleine, feine Java-Anwendung geschrieben. Sie konvertiert ein DB-Schema in YAML in ein Bild (zeigt dieses an) und lässt auf Wunsch dieses Speichern.

Downloads (benötigt Java 1.6):

  1. Ausführbare Jar kAml.jar
  2. Dmg Imagefile alsAnwendung für Mac OS X

Starten einer JAR unter Mac OSX – Bad version number?

Wir hatten dieses Thema bereits indirekt durch den ApplicationBundler im Beitrag vorletzter Woche besprochen.. aber hier nochmal zum eigentlichen Problem und dessen Lösung.

Der Fehler verursacht die aktuell ausgewählte Java-Version (und das ist bei den meisten Leuten der Standard Java 1.5.0). Wenn jedoch die Jar mit Java 1.6 kompiliert wurde, kann der 1.5-Loader damit nichts anfangen bzw. schützt aus naheliegenden Gründen vor der Ausführung. Zwar ist auf Mac OS X System der Version 10.5 auch Java 1.6 installiert, dieses ist jedoch kein Standard.

Es gibt dafür nun drei Möglichkeiten:

  1. Der Benutzer startet die JAR nicht mit Doppelklick bzw. dem Kommando „java -jar JarFile.jar“ sondern mit der direkten Angabe im Frameworkverzeichnis.. diese Lösung ist inakzeptabel.
  2. Der Benutzer stellt seine Default-JVM auf 1.6 um.. das machen die Entwickler eh, aber der Benutzer wird auch damit überfordert sein. Auch keine akzeptable Lösung.
  3. Man erstellt eine Mac-Application mit entsprechenden Application-Bootloader und einer Art Manifest, wo eingestellt wird, welche Java-Version verwendet werden muss. Damit findet sich Java 1.6 sogar dann, wenn man als Default 1.4 eingestellt hat. Außerdem hat es den positiven Nebeneffekt, dass die „technische“ Jar vor dem Benutzer durch ein Anwendungsymbol kaschiert wird.

Non-Java exception raised, not handled (Error creating CGSWindow)

Update: It seems that MacWidgets causes this problem. In the applications main frame I added the line

MacUtils.makeWindowLeopardStyle(getRootPane());

If I disable (comment out) this line there will no exceptions. What a..

The structure of components: One Mainframe with BorderLayout (Toolbar, Sidebar and MainContent). The MainContent contains eventually the graph, the corresponding satellite component is in the sidebar.

Original post:

I had already posted about it last week. But now it seems that is was not a reasonable solution.

I have a java swing application which uses standard java swing elements and the jung2 graph framework visualizing graphs. The application is developed under java 1.6 and mac os x 10.5.6.

The actual problem is that since yesterday the AWT-Eventthread crashed, for example like this:

Thu May 7 15:04:28 X.localdomain java[2002] : CGSResolveShmemReference : reference offset (65232) exceeds bounds (32768) on shmem obj 0x257e
Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Non-Java exception raised, not handled! (Original problem: Error (1000) creating CGSWindow)
at apple.awt.CRenderer.doRect(Native Method)
at apple.awt.OSXSurfaceData.doRect(OSXSurfaceData.java:1247)
at apple.awt.CRenderer.fillRect(CRenderer.java:157)
at apple.awt.CRenderer.fillRect(CRenderer.java:145)
at sun.java2d.pipe.ValidatePipe.fillRect(ValidatePipe.java:58)
at sun.java2d.SunGraphics2D.fillRect(SunGraphics2D.java:2501)
at edu.uci.ics.jung.visualization.BasicVisualizationServer.renderGraph(BasicVisualizationServer.java:341)
at edu.uci.ics.jung.visualization.BasicVisualizationServer.paintComponent(BasicVisualizationServer.java:321)
at javax.swing.JComponent.paint(JComponent.java:1027)
at javax.swing.JComponent.paintChildren(JComponent.java:864)
at javax.swing.JComponent.paint(JComponent.java:1036)
at javax.swing.JComponent._paintImmediately(JComponent.java:5096)
at javax.swing.JComponent.paintImmediately(JComponent.java:4880)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:749)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:679)
at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:659)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:128)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:300)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:210)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:200)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:195)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:187)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

This happens in the event thread, so the application is „still“ useable. Well, of course, the graph painting is a mess.

After googling, I found some solutions:

  1. Execute the file access rights (permissions)-fix via disk utilities & reboot the maschine. Yes, this will fix the problem for some starts, but then.. what a dejavu.
  2. Assure that the paint-process is not outside of the awt-thread.. yes it is. See trace. (Link developer.apple.com)
  3. Exclude the SWT jar.. well, I do not use this and I have not include it. (Link publicobject.com)
  4. Use not the apple ui manager.. well, i replace the manager for the components of jung, i disabled my own selfwritten graph vertex renderer.. no changes. No less exceptions, nothing.

Perhaps this could be an issue by jung, but..

  • why only on this mac – but no error/exception on others systems like windows and linux? Also I have a 2 years older macbookpro.. no problem there.
  • why, or some time, is there no problem after fixing the file access rights (permissions) and rebooting the machine?

Currently, I have no solution.. the exeptions raises while painting the graph (which uses many paint()/Graphs objects ofc) moving/zooming it. I was able to catch some of the strange exceptions overriding the default visualizerviewer (by jung) and put in a small try-catch-wrapper of the corresponding method (here renderGraph). But other exceptions raised within javax.swing.Component and so on…

And.. how strange: the line

Thu May 7 15:04:28 X.localdomain java[2002] <Warning>: CGSResolveShmemReference : reference offset (65232) exceeds bounds (32768) on shmem obj 0x257e

is not an output by the java exception but from the failed native method. The numbers: 32768 (=2^15) and 65232 (2^16 -4). 64bit-problem?

I’m very appreciate if someone has an idea or solution.

Mac OS X: Non-Java exception raised.

Böses Erwachen nach einem Reboot: Auf einmal funktionierte Swing nicht mehr richtig, Components mit eigener paint()-Methode funktionierten gar nicht oder verursachten die abenteuerlichsten Zeichnungen. Schätzungsweise nach dem Installieren des Developerpacks habe ich irgendwann letzte Woche (> 9 Tage) nicht den Mac nicht neu gestartet.. dann wäre mir das früher aufgefallen.

Der genaue Wortlaut der Meldung: exception: java.lang.RuntimeException: Non-Java exception raised, not handled! (Original problem: Error (10000) creating CGSWindow)

Nach ein bisschen Recherche mit dieser Meldung stieß ich dann auf diesen Thread, in welchem auch die Lösung parat steht: Zugriffsrechte reparieren lassen und rebooten. Warum man rebooten muss (man muss es, habe es ausprobiert), entzieht sich meiner Kenntnisse; vielleicht werden manche Ressourcen nur einmal geöffnet?

TestSuites in Eclipse mit jUnit 4 (Nachtrag)

Das Problem: Wenn man in einem Projekt eine Reihe von Testcases gemacht hat, dann sind diese meist (natürlich) auf verschiedene Packages verteilt. Außerdem gibt es ja reine Komponententests als auch Usecasetests (soweit möglich), oder das Testen zweier oder mehrere Komponenten im Verbund. Schlussendlich also eine große Anzahl von „TestCase“-Klassen, die jeweils mehrere Tests haben. Ideal wäre also, wenn man diese ganzen Testcases zusammenpacken könnte.. und dann auch ausführen könnte.

jUnit bietet (in Eclipse) dafür die Option TestSuite an. Eine TestSuite ist ja auch genau das, was wir brauchen. Leider zeigt der Wizard keine unserer TestCases an.. warum? Nach einigem Recherchieren (viele Suchergebnisse zeigen indirekt noch auf jUNit 3.8) ist die Frage eigentlich simpel gelöst.. wenn man denn weiß, wie 😉

Als erstes müssen alle TestCase-Klassen (d.h. hier die Klasse MyTests, die @Test-Methoden enthält) eine neue Methode erhalten:

public static junit.framework.Test suite() {
return new JUnit4TestAdapter(MyTests.class);
}

Damit erkennt der Wizard für eine TestSuite nun auch jene Klassen.

Falls man es lieber manuell machen möchte (Beispiel): suite.addTest(MyTests.suite());f

Es ist übrigens kein Problem, TestSuites zu verschachteln – dafür einfach das Prozedere „wiederholen“ bzw. suite.addTest(MyOtherSuite.suite()) schreiben. Tipp: Es gibt die äußerst hilfreiche Methode suite.setName(string) – damit man sich in der jUnit-Baumansicht nicht verwirrt.

Nachtrag:

Wesentlich eleganter ist natürlich der Annotation-Weg. Die „SuperTestSuite“ braucht „nur“ so auszusehen:

@RunWith(Suite.class)
@SuiteClasses( { MySuite1.class, MySuite2.class, MySuite3.class })

public class AllTests {
}