Zur Zeit wird gefiltert nach: Java
Filter zurücksetzen
Die Java-Cloud-Collection-API
Die Java-Cloud-Collection-API

- Roman Seibold, Senior Consultant und technischer Leiter der Schulungsabteilung von aformatik
von Roman Seibold
Leider handelt es sich bei dem nachfolgenden Artikel um einen Aprilscherz. Weil der Grundgedanke dahinter aber sehr verführerisch ist, lassen wir den Text mal solange hier stehen, bis Oracle so etwas tatsächlich implementiert :-) Trotzdem wird man bei dem Cloud-Thema doch die eine Frage nicht los: "wo genau sind denn meine Daten jetzt bitteschön?"
Eine Neuigkeit für alle Interessierte: seit Java 6.0.24, der seit einigen Tagen verfügbaren neuesten Java-Version, sind für das Collection-Framework auch Cloud-Funktionen hinzugekommen und es wurden, der üblichen Sun- bzw. Oracle-Philosophie für Minor-Updates zum Trotz, neue Klassen im Paket java.util hinzugefügt.
Ausgangssituation war wohl die Klage vieler Firmen, die sich gerade im serverseitigen Bereich auf Java verlassen, das häufige Auftreten der gefürchteten java.lang.OutOfMemoryErrors. In vielen solcher Fehlersituationen hat sich bei Analysen von Heap-Dumps gezeigt, dass der Speicher hauptsächlich von Collections belegt war und dass die Daten von diesen Collections eben, da noch "hart" referenziert, bei einem Garbage Collector-Lauf nicht freigegeben werden können.
Was liegt da näher, als die Daten in die Cloud auszulagern? Damit wird der knappe Speicherplatz auf dem lokalen Server wieder verfügbar und ein OutOfMemoryError ist hoffentlich Geschichte.
Neu im API sind u.a. die Klassen CloudList und CloudSet. Hier ein kleines Beispiel:
List<HeavyObject> list =
CloudList<HeavyObject>.createSomewhere();
list.add(new HeavyObject(...));
Im obigen Beispiel kann man sehen, dass man die CloudList nicht mit new anlegt, da ansonsten deren Daten ja im lokalen Speicher verortet wären. Die createSomewhere- Methode agiert hier als Factory, die es gestattet, den günstigsten und schnellstmöglich erreichbaren Speicher zu allozieren und liefert die passende Implementierung. Danach "belastet" nur noch ein Proxy-Objekt den lokalen Speicher, der Rest der Daten liegt in der Cloud.
Damit auch bereits bestehende Collection-Klassen wie ArrayList, HashSet und co. vom neuen Feature profitieren können, wurde die Implementierung der bestehenden Klassen von Oracle einfach erweitert, bleibt dabei aber vollständig rückwärtskompatibel. Ein- und Ausschalten kann man die Erweiterung jetzt über ein System-Property use.cloud.feature, womit auch Altanwendungen ganz leicht in die Cloud gebracht werden können.
Integrieren Sie das System-Property ganz einfach in Ihre bisherige Kommandozeile, z.B.
java -Xmx512m -Duse.cloud.feature=true de.aformatik.Start
Interessant ist das Feature natürlich vor allem im Einsatz auf Application Servern, z.B. bei WebSphere oder JBoss. Hier muss man natürlich an unterschiedlichen Stellen eingreifen, um die etwas versteckte Startkonfiguration zu erreichen und damit das Property eintragen zu können. Bei WebSphere wäre das entweder über die Admin-Konsole unter server > Java and Process Management > Process Definition > Java Virtual Machine oder direkt in der Datei server.xml, dort unter
<jvmEntries ...
genericJvmArguments="-Xquickstart -Duse.cloud.feature=true">
Bei JBoss wäre dies ein Eintrag in der run.conf-Datei, dort einfach in der JAVA_OPTS-Variable die entsprechende Ergänzung machen.
So, das wars für dieses Mal. Wir wären natürlich sehr interessiert daran, ob und wie sich das Speicher- und Laufzeitverhalten Ihrer Applikationen verändert. Schreiben Sie uns doch einfach einen Kommentar und tauschen Sie mit anderen Interessierten Ihre Erfahrungen aus.
Wir freuen uns schon auf neue Erkenntnisse!
Java 6 Update 21 und Eclipse

- Alexander Frey ist Consultant bei aformatik
von Alexander Frey
eine kleine Warnung an alle, die Eclipse zusammen mit Java 6 Update 21 (z.Zt. aktuellste Version) einsetzen wollen: es funktioniert nicht Out-Of-The-Box, es gibt aber Workarounds. Betroffen sind anscheinend alle Eclipse-Versionen von 3.3 bis 3.6 (Helios) und zwar nicht nur die IDE, sondern auch RCP-Anwendungen.
Hintergrund:
Der Eclipse-Launcher liest vor dem Java-Start unter Windows aus einer Binär-Datei die Firma der JVM aus und setzt entsprechende Parameter beim JVM-Start (passend zur VM). Bis einschließlich Update 20 war die Firma "Sun Microsystems, Inc", bei Update 21 ist es nun "Oracle" (es handelt sich hierbei nur um den Hersteller-String in einer Binär-Datei, nicht um das Property "java.vendor").
In der Folge erkennt der Launcher nicht, dass eine VM mit Hotspot gestartet wird, sondern hält die VM für JRockit. JRockit kennt leider den Parameter "-XX:MaxPermSize" nicht, weshalb der Launcher diesen Parameter nicht setzt. Bei Hotspot wird der Parameter aber dringend benötigt, da es sonst zu einem Out-Of-Memory im PermGen Space kommt.
Empfohlene Workarounds (Zitat von Eclipse.org):
1. switch back to '1.6.0_20'
2. add the following line after "-vmargs" to your eclipse.ini:-XX:MaxPermSize=256m
Der entsprechende Fehler unter bugs.sun.com deutet darauf hin, dass diese Änderung in der Binär-Datei wieder rückgängig gemacht wird. Wenn das nicht passieren sollte, bekommen alle Eclipse und RCP-Anwedungen mit dem nächsten automatischen Java-Update ein Problem (Update 21 ist kein Security-Update und wird deshalb nicht über den Java-Updater automatisch verteilt).
Mehr Infos gibt es hier:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=319514
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6969236
Refactoring Snippets

- Sebastian Petzelberger Senior Consultant bei aformatik
von Sebastian Petzelberger
Ein Refactoring Snippet zeigt ein kleines Problem auf und gibt eine kleine Lösung dazu an. Die Problemstellungen kommen aus der Projektarbeit. Die Snippets sollen kleine Hilfen sein. Außerdem sollen sie generell zum Refactoring anregen, aber auch zu einer Diskussion darüber einladen.
Refactoring Snippet: Überflüssige Hierarchie
Vorstellung
In einem Projekt waren über einem Dutzend Klassen wie die folgenden zwei aufgebaut:
public class UpButton extends JButton
{
public UpButton()
{
this.setBorderPainted(false);
this.setMaximumSize(new java.awt.Dimension(20, 20));
this.setMinimumSize(new java.awt.Dimension(20, 20));
this.setPreferredSize(new java.awt.Dimension(20, 20));
this.setIcon(new javax.swing.ImageIcon(getClass().
getResource("/xxx/xxx/images/Up16.gif")));
}
}
public class TopButton extends JButton
{
public TopButton()
{
this.setBorderPainted(false);
this.setMaximumSize(new java.awt.Dimension(20, 20));
this.setMinimumSize(new java.awt.Dimension(20, 20));
this.setPreferredSize(new java.awt.Dimension(20, 20));
this.setIcon(new javax.swing.ImageIcon(getClass().
getResource("/xxx/xxx/images/Top16.gif")));
}
}
kleine Probleme
Auffällig ist:
- Viele neue Klassen
- Redundanz
Eine Unterklasse stellt neue Funktionalität bereit, die in neuen Methoden umgesetzt werden. Hier haben die Unterklassen keine neue Funktionalität.
Es kommt auch vor, dass vorhandene Funktionalität allgemein ausgeprägt in der Unterklasse speziell umgesetzt wird. Das wäre ein Überschreiben von vorhandenen Methoden der Oberklasse. Das hätte hier Anwendung finden können. Es wäre konsequent gewesen, die Methoden getMaximumSize, getPreferredSize und getIcon zu überschreiben. Der Code hätte so die Notwendigkeit von eigenen Klassen eher wieder gespiegelt, doch neue Funktionalität hätte trotzdem gefehlt. Gut, dass dies nicht so umgesetzt wurde, denn jetzt ist es leichter zu einer einfacheren Lösung zu kommen.
kleine Lösung
Die oben angeführten zwei Beispiele verwenden jeweils eine Methode zum Konstruieren. Wieso nicht stattdessen eine Methode zum Erzeugen verwenden? Mein Vorschlag, eine Dienstmethode:
public static JButton createIconButton(String resource)
{
JButton iconButton = new JButton();
iconButton.setBorderPainted(false);
iconButton.setMaximumSize(new Dimension(20, 20));
iconButton.setMinimumSize(new Dimension(20, 20));
iconButton.setPreferredSize(new Dimension(20, 20));
iconButton.setIcon(new ImageIcon(getClass()
.getResource(resource)));
return iconButton;
}
Das ist nur eine erste Refactoring Maßnahme. Mein Vorschlag selbst ist natürlich noch nicht fertig. Es sind immer noch Redundanzen vorhanden, beispielsweise "new Dimension(20, 20)". Ein weiteres Refactoring ist nicht nötig, da hier nur auf überflüssige Hierarchien hingewiesen werden soll.
Beurteilung
Wer neuartige Widgets programmiert, der benötigt (eventuell) Hierarchie. Wer aber nur zusammenbaut, der tut das besser in einer Dienst-Methode. Es ist immer die einfachere Lösung zu wählen. Eine neue Klasse ist viel mächtiger als nur eine neue Methode. Bei dem obigen Beispiel haben wir noch den Vorteil, dass der vorliegende Code nur eine Methode beinhaltet. Das Prinzip ist: Nicht vererben, sondern zusammenbauen. Es soll nicht ganz auf Hierarchien verzichtet werden, der Einsatz sollte in der Praxis nur etwas kritischer geschehen.
Es gab mal einen bekannten Swing GUI-Builder, der beliebt war aufgrund der einfachen weil visuellen Handhabung GUIs zusammenzubauen. Mein größter Kritikpunkt an diesem war, dass so viele Programmierer dann auch so entwickelt haben, wie der GUI-Builder den Swing-Code erzeugt hat. Eine noch harmlose Unsitte war, dass jede GUI-Klasse eine Unterklasse von JPanel war. Eine GUI-Klasse baut ein JPanel zusammen, ist keine neue Art von JPanel. Die riesige Methodenpalette von der Vererbungshierarchie von JPanel interessiert nur beim Zusammenbauen des JPanels, also in einer Methode. Die eigenen geschrieben Methoden der GUI-Klasse sind im Code-Assistent kaum zu finden. Das weist auch darauf hin, dass Hierarchie hier kein geeignetes Mittel ist.
In dem einen oder anderen Beispielcode von SUN sind die GUI-Klassen eine neue Art von JFrame. Eine Gui-Klasse baut ein JPanel zusammen. Dieser kann dann per Dienstmethode in einen JFrame gesetzt werden ODER in einem JDialog ODER in einem anderen JPanel Verwendung finden. Letzteres wenn nämlich eine übergeordnete GUI-Klasse diesen benutzt.
kleine Probleme
Die 80%-Schleife

- Roman Seibold, Senior Consultant und technischer Leiter der Schulungsabteilung von aformatik
von Roman Seibold
Natürlich ist sie schon ein wenig älter, die "neue" for-Schleife, oder auch "enhanced-for-loop", sogar schon über fünf Jahre, aber sie lohnt dennoch einen genaueren Blick. Für alle, die sie noch nicht kennen sollten: seit Java 5 kann man mit einer vereinfachten Syntax über Arrays oder Collections iterieren. Beispiel:
String[] array = { "enhanced", "for", "loop" };
for (String string : array)
{
System.out.println(string);
}
Eingebürgert hat sich auch ihr zweiter Name "for-each-loop", da die Lesart des obigen Konstrukts wie folgt lautet: "for each String string in array do..."
Hier stellt sich dem einen oder anderen schon die erste Frage: warum heisst das Schlüsselwort dann nicht "foreach" und warum muss ich einen Doppelpunkt machen, wenn man hier "in" liest? Diese Frage haben die Macher der Spezifikation (der JSR 201, der im übrigen auch Autoboxing, Enums, Varargs und static Imports enthält) gleich mit der Spezifikation beantwortet: "Die Kosten, neue Schlüsselwörter in der Sprache zu verankern, sind enorm. Außerdem entsteht eine Inkompatibilität zu existierendem Sourcecode, der die neuen Schlüsselwörter als Identifier benützt. Ganz konkret würde beispielsweise das Schlüsselwort 'in' stark in Konflikt mit 'System.in' geraten."
Ähnliches hat man ja auch erlebt, nachdem das Schlüsselwort 'enum' in Java 5 eingeführt wurde, da hat so manche Schleife, die mit einer Enumeration gearbeitet hatte, nicht mehr compiliert. Vermutlich wollte man uns mehr solcher Dinge ersparen.
Eine zweite Frage kommt einem in den Sinn: schön, dass ich nun über Arrays iterieren kann, aber warum kann ich dabei keine Änderungen vornehmen? Zur Illustration:
String[] array = { "enhanced", "for", "loop" };
for (String string : array)
{
string = "changed";
}
Das compiliert zwar, erfüllt aber nicht den beabsichtigten Zweck überall im Array den String "changed" zu hinterlegen. Für Schreibzugriffe auf Collections und Arrays muss man nach wie vor mit einer klassischen for- oder while-Schleife arbeiten. Auch hier hat die Spezifikation schon wieder eine gute Begründung parat: "Die Expertengruppe hat über solche Fälle nachgedacht, dann aber für eine einfache und saubere Erweiterung gestimmt, die eine Sache macht, und die gut. Das Design folgt der 80-20-Regel (d.h. es löst 80% der Fälle mit 20% Aufwand). Wenn ein Problem in die restlichen 20% fällt, kann man es immer noch mit einem expliziten Iterator oder einem Index lösen, genau so wie man es in der Vergangenheit getan hat."
Jetzt, nach über 5 Jahren, kann man zurückblickend sagen, dass diese Entscheidung gut war. In vielen, vielen Fällen möchte man nur über Werte iterieren und mit diesen Werten dann eine andere Funktionalität aufrufen. Und das sind gefühlt schon diese 80%.
Und noch eine Frage mag sich stellen: wo ist denn eigentlich bei Collections mein Iterator hin? Das Beispiel von oben, in Collections übersetzt:
String[] array = { "enhanced", "for", "loop" };
List<string> list = Arrays.asList(array);
for (String string : list)
{
System.out.println(string);
}
Hier taucht überhaupt kein Iterator auf. Die Lösung ist, dass in Wirklichkeit die enhanced-for-loop gar nicht mit Collections arbeitet, sondern nur mit Arrays und Instanzen von java.lang.Iterable. Iterable wiederum ist ein Interface, dass nur eine einzige Methode enthält, nämlich iterator(). In Java 5 wurde also die Eigenschaft, einen Iterator produzieren zu können aus dem Collection-Framework herausgelöst. Direkt mit einem Iterator arbeitet die neue for-Schleife auch überhaupt nicht, d.h.
Iterator<string> iterator = list.iterator();
for (String string : iterator)
{
System.out.println(string);
}
funktioniert nicht. Warum denn das nicht? Hier gibt es ebenfalls eine Erklärung, die sich in der Spezifikation findet. "Es gibt zwei Gründe, warum die foreach- Schleife nicht mit einem Iterator funktioniert: (1) Das Konstrukt würde nicht mehr wirklich viel syntaktische Verbesserung bringen und (2) die Ausführung der Schleife hätte den Seiteneffekt, dass sie den Iterator verbraucht."
Außerdem ist der Iterator ja genau das Artefakt in einer solchen Schleife, an dem man am wenigsten interessiert ist. Genaugenommen braucht man ihn ja nur für den Zweck, die Collection besuchen zu können, wie man die oft bemühte Indexvariable i häufig nur braucht, um auf ein Array in einer Schleife zugreifen zu können. Mit dem Iterator oder mit i selbst möchte man (in 80% der Fälle) eigentlich gar nichts machen, sie sind also nur unnötige technische Artefakte. Man vergleiche selbst:
// 1
for (String string : array)
{
System.out.println(string);
}
// 2
for (int i = 0; i < array.length; i++)
{
System.out.println(array[i]);
}
Im zweiten Beispiel muss man einiges tun. Man braucht eine Hilfsvariable i vom Typ int. Man muß wissen, dass Arrays ab 0 zählen. Man muss die Obergrenze definieren. Man darf nicht vergessen, i in jedem Schleifenschritt zu erhöhen. Und man muss den Elementzugriff mit dem Indexoperator [] durchführen. Klar, kalter Kaffee für erfahrene Java-Leute. Aber zugegeben, der Code mit der Hilfsvariablen ist mehr und auch etwas schwieriger zu lesen. Und vom Code-Verständnis bis zur Wartung ist nur noch ein kleiner Schritt.
Wer übrigens viele von diesen 80%-Schleifen in seinem Code hat und diesen vereinfachen möchte, dem kann ich nur Eclipse und sein "Clean Up" Wizard ans Herz legen, zu finden im Source-Menü. Dort gibt es die Option "Convert for loops to enhanced". Auf das obige Beispiel 2 angewendet erzeugt Eclipse bei mir folgenden Output:
for (String element : array)
{
System.out.println(element);
}
Nett. Und wer nun selbst noch Nachforschungen betreiben möchte, findet die Spezifikation unter http://jcp.org/aboutJava/communityprocess/final/jsr201/index.html .
Weitere Erkenntnisse, Meinungen und Erfahrungen aus Projekten sind hier jederzeit gerne als Kommentare gesehen.
Bis dahin grüßt Sie (zu 100%)
Roman Seibold
Nur mal kurz...

- Roman Seibold, Senior Consultant und technischer Leiter der Schulungsabteilung von aformatik
...einen JAX-WS WebService mit JBoss 5.1 erstellen.
von Roman Seibold
Vorgeschichte: der WebService
"Nur mal kurz" geht in der IT gar nicht. Musste ich neulich wieder feststellen. Eigentlich ging es hauptsächlich überhaupt nicht um einen WebService, aber ich brauchte einen, um etwas anderes tun zu können. Ist ja jetzt ganz einfach, mit Java EE 5 und JAX-WS. Dachte ich. Der WebService, als POJO nicht als EJB, ist mit JAX-WS tatsächlich schnell geschrieben. Hier, auch was den Rückgabetyp betrifft, sehr vereinfacht dargestellt:
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class WebService
{
@WebMethod
public String getContent()
{
return "Juhu, tut!";
}
}
Das ist schon alles. Mit @WebService erkläre ich die Klasse zum Web-Service, mit @WebMethod wird klar, was als Operation für den Service im WSDL verfügbar wird.
Deployment
Jetzt beginnt der proprietäre Teil. Ein Java-EE-compliant Application- Server ist nur verpflichtet, einen Web-Access-Point für JAX-WS zur Verfügung zu stellen. Wie ist nicht gesagt. Das macht jeder Server anders, z.B. indem er einen automatisch aktivierten WebService- Router zur Verfügung stellt, wenn JAX-WS-Services erkannt werden. JBoss verfährt so nicht. Es gibt verschiedene Möglichkeiten, mit WebServices umzugehen. Mein POJO-WebService wird nur von außen erreichbar, wenn ich ihn als Servlet (!) definiere. Das sieht im Deployment-Deskriptor so aus:
<servlet> <servlet-name>JaxWSServlet</servlet-name> <servlet-class>
com.aformatik.biblio.service.BiblioAccessService
</servlet-class></servlet><servlet-mapping> <servlet-name>JaxWSServlet</servlet-name> <url-pattern>/BiblioAccess</url-pattern></servlet-mapping>
Das war schon die erste, nicht ganz intuitive Hürde. Weiterhin ist mein Projekt folgendermaßen - eigentlich recht simpel - strukturiert:
WebServiceEAR.ear
+-- WebServiceWeb.war
Das heißt, dass der WebService in einem Web-Modul definiert ist, das einem EAR-Modul zugeordnet ist. Das ganze schnell auf einen JBoss AS 5.1 deployed. Fehlerfrei. Gut.
Nur noch kurz...
Jetzt "nur noch kurz" den Client dazu... Geht eigentlich auch einfach. Man importiert das vom Server gemäß JAX-WS generierte WSDL-File und lässt sich die entsprechenden Client-Klassen erstellen und arbeitet damit. Die Frage ist nur: wo ist denn das WSDL-File? Angeblich gibt es bei JBoss eine URL unter der die deployten Services erreichbar sind. Bei mir sollte dies http://localhost:8080/jbossws/services sein. Tatsächlich, da ist auch was. Wenn man aber auf den Link zum Service klickt, dann kommt "Not Found". Hmmm...
Nach längerer Zeit wurde ich im "data"-Verzeichnis des Servers fündig, da liegt das generierte WSDL ebenfalls. Na gut, dann nimmt man halt das. Auf dieser Basis den Client generiert - und nichts tat. Not found. Ich habe also ein WSDL für einen Service, der angeblich verfügbar ist, aber nicht auf Zugriff reagiert. Das stimmte mich miß. Hier schloß sich nun eine längere Zeit für Recherche und Tests an. Offenbar war ich einem Problem begegnet, das noch niemand so hatte. Es ließ sich jedenfalls nicht auf die Schnelle im Internet etwas finden. Macht keiner WebServices, oder nicht mit JBoss, oder (noch) nicht mit JAX-WS?
Die Lösung
Auf einer einzigen, etwas versteckten JBoss-JIRA-Bug-Tracking Webseite, die eigentlich einen Bug im Zusammenhang mit JBoss ESB thematisierte, wurde ich fündig: die JBoss-native WebService-Implementierung hat einen Bug in der ausgelieferten Version 3.1.2. Der Bug kommt aber nur zur Geltung, wenn eine WAR-Datei, die einen Web-Service definiert, in einem EAR deployt wird. War bei mir ja der Fall. Lösung: die neuere Implementierung der WebServices 3.2.0 verwenden. Leider, leider ist die neueste Server-Version, die man von JBoss herunterladen kann, die 5.1 GA. Punkt. Kein Service-Release, kein sonstwas. Ist aber vom Mai, jetzt haben wir November. Haben die kein Release-Management? Na gut, dann basteln wir uns eben einen gepimpten Server. In einem Blog fand ich, wie man eine andere WebService-Implementierung ("Metro") aktualisiert. Ich wollte aber die JBoss-native. In diesem Beitrag ist die Download-Adresse der Metro-Implementierung angegeben, wenn man aber versucht, einfach mal ins Verzeichnis zu schauen, um nach den anderen Implementierungen zu suchen, dann klappt das aus Berechtigungsgründen nicht. Man muss den Dateinamen schon wissen! Jetzt hilft mal wieder nur "inspiriertes Raten" und es klappte mit dem URL http://jboss.org/file-access/default/members/jbossws/downloads/jbossws-native-3.2.0.GA.zip. War jetzt auch nicht so schwer. Danach die Steps ausgeführt, die der Kollege mit dem unaussprechlichen Namen "Prakashbabu" vorschlägt, und - voila - JBoss bootet noch (!) und zeigt im Log:
JBoss Web Services - Native Server
3.2.0.GA
Und, oh Wunder, jetzt tut sofort alles, wie beschrieben. Unter http://localhost:8080/jbossws/services/WebService?wsdl ist mein WSDL verfügbar, der Eclipse-Testclient und ein generierter Client-Code können sofort auf den Service zugreifen. Super! War ja auch ganz einfach. Naja, JAX-WS schon, nur der konkrete Teil mit Produkten und Versionen, der war mal wieder unschön.
Komisch aber schon, dass dieser Fehler kein größeres Echo hervorgerufen hat und dass die seit Mai verfügbare Download-Version von JBoss AS 5.1 immer noch diesen Bug beinhalten darf und dass es keine aktualisierte gibt. Das lässt schon den Verdacht etwas keimen, dass JAX-WS-WebServices noch nicht so verbreitet sind. Oder dass niemand die native Implementierung verwendet. Oder...







