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.

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

Java-Applikation als Mac-Anwendung deployen (Ant)

Wie bereits berichtet, ist es von Vorteil, der Buildprozess mit einem Ant-Script zu automatisieren. Um eine Mac-Application zu erstellen, muss man nur wissen, wie eine solche Application aufgebaut ist.. der Rest ist ein Klacks.

Eine Mac-Application ist ein Verzeichnis mit der Erweiterung .app. Die Struktur einer Application (wir nennen sie im Folgenden Application) sieht wie folgt aus:

  • Application.app
    • Contents
      • Info.plist — Properties-Datei für die Anwendung und Java
      • MacOS — Verzeichnis
        • JavaApplicationStub — Bootloader für Javaprogramme
      • PkgInfo — Datei mit Entwicklerinfo
      • Resources — Verzeichnis mit dem eigentlichen Javaprogramm
        • Java
          • Die Jars und so.

Den Bootloader befindet sich auf einem Mac in dem Ordner /System/Library/Frameworks/JavaVM.framework/Resources/MacOS/JavaApplicationStub.

Prinzipiell kann man sich diese Struktur komplett selber erstellen; es ist aber zu Empfehlen, den initialen Prozess durch das Programm /Developer/Applications/Utilities/Jar Bundler zu machen. Mit Hilfe dieses Programmes lässt sich für eine bereits fertige JAR die Application erstellen.

Anschließend kopiert man das erstellte Resultat – quasi als Vorlage – in ein Verzeichnis für die Builds. In meinem Fall in dem build_data Verzeichnis. Ein Ant-Task erstellt darauf hin im Verzeichnis deploy eine Application. Die Vorlage ist idealerweise auch im SVN und auch später noch veränderbar – beispielsweise durch das Ändern des Applicationicon, -titel oder anderen Einstellungen.

Wichtig 1: Bei Verwendung von FatJar muss man als Package für die Main den Bootloader von FatJar (com.simontuffs.onejar.Boot) verwenden. Dies erkennt das Programm Jar Bundler aber auch automatisch.

Wichtig 2: Beim Kopieren der Dateien kann es passieren, dass die Rechte an der JavaApplicationStub verloren gehen. Diese muss als ausführbar markiert sein. Ein entsprechender Ant-Befehl stellt das sicher.

<target name="makeMac" depends="makeNormal">
<copy todir="deploy/Application.app">
<fileset dir="build_data/Application.app"/>
</copy>
<!-- chmod the application stub file executable -->
<chmod file="deploy/Application.app/Contents/MacOS/JavaApplicationStub" perm="755"/>
<copy file="deploy/Application.jar" tofile="deploy/Application.app/Contents/Resources/Java/Application.jar" />
</target>

Automatischer Deploy von Java-Applikationen oder: Ant Builds mit externen Jars und SVN-Informationen

Der Eclipse-verwöhnte Programmierer braucht sich eigentlich nicht mit Begriffen wie Deploy oder Build herumschlagen — zumindestens nicht so lange man in der Entwicklung ist. Ein Knopfdruck auf „Run“ und das Programm läuft. Sowohl alle lokalen Resourcen als auch alle externen Jars werden gefunden, eingebunden und können einwandfrei genutzt werden.

Möchte man die Applikation nun fertigstellen und anderen zur Verfügung stellen – oder früher im Rahmen von Enduser-Tests – so kommt es bei großen Projekten unweigerlich zu Problemen.

Im Folgenden beschäftige ich mit den Problemen:

  1. Wie exportiere ich ein Projekt mit vielen externen Jars einfach und sicher in ein lauffähiges Jar?
  2. Wie stelle ich sicher, das sowohl in der Entwicklungsumgebung als auch in der Jar alle Bilder o.ä. Ressourcen wiedergefunden werden?
  3. Wie nutze ich einen eigenen Buildprozess und
  4. Wie kann ich im Buildprozess Zusatzinformationen wie die aktuelle SVN-Revision oder das Build-Datum verwerten?

„Automatischer Deploy von Java-Applikationen oder: Ant Builds mit externen Jars und SVN-Informationen“ weiterlesen

Java: Nativer Dateidialog unter MacOS X

Definitiv in der Kategorie: „Was ich schon immer mal wissen wollte..“

Wenn man mit Java Swing ein JFileChooser verwendet, so sieht der unter MacOS X alles andere als schön aus – und nativ ist er sowieso nicht. Was ich nicht wusste: Apple hat das AWT-Pendant FileDialog nativ implementiert (oder nachgebaut?). Gewusst wie – darauf gekommen bin ich auch nur, weil ich einfach mal gesucht habe.

Edit: Wie es aussieht, sieht der FileDialog auch unter Windows besser als sein Swing-Pendant aus. Wie sieht das mit den anderen System aus?

Statische Code-Analysetools für Java

Um die Softwarequalität zu verbessern, werde ich mir in den nächsten Java-gestützen Projekten einmal diese beiden Tools angucken. Im Vergleich schneidet PMD etwas besser als FindBugs ab, wir werden es sehen.

Ernüchterned ist es ja schon irgendwie, wenn man sein Fehler- und Warnungsfreie Projekt (Eclipse Compiler läßt grüßen) damit checkt und vor Hunderten Fehlern steht..

XML/DTD Validator Version 1.0

Aus Zweckmäßigkeit habe ich mir einen kleinen XML/DTD-Generator und Validator gebaut.

  • Laden einer XML-Datei als URL
  • optionales Laden einer DTD-Datei als URL
  • alternativ: aus geladener XML-Datei eine DTD generieren lassen
  • entweder aus der geladenen DTD oder aus der DTD im XML-Header das XML-Dokument validieren

Da das Tool nur die URLs zu den Dateien speichert, kann man auch auch während der Laufzeit die Dateien bearbeiten und erneut validieren lassen.
Das Tool ist komplett über GUI steuerbar, es sind keine Konsoleneingaben erforderlich (und afaik auch nicht möglich).

Für den Part des DTD-Generators (den ich anpassen musste, um ihn von der GUI aus zu steuern und Fehlerreports in die GUI zu leiten): xmlBlueprint: DTD Generator

Zum Download des XML/DTD Validator Version 1.0