tag:blogger.com,1999:blog-9514167219555904992024-02-19T17:03:58.031+01:00JavaBarista: Java, agil zubereitet.Gedanken und Schnipsel rund um die Java-Technologie im Allgemeinen, um Java EE im Speziellen - und auch zu den agilen Praktiken guter Softwareentwicklung.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comBlogger85125tag:blogger.com,1999:blog-951416721955590499.post-1731261736192887662020-08-13T21:57:00.002+02:002021-03-04T22:56:12.896+01:00Sei kein Held, sei eine Null! Über 10x Developer und andere Herausforderungen.<p>Im November 2018 habe ich bei den <a href="https://www.xpdays.de/">XP-Days</a> in Hamburg meine erste (und bisher einzige) "eXtreme Presentation" (manchmal auch Pecha Kucha genannt) gehalten. Dabei werden in 6'40" genau 20 Folien jeweils genau 20 Sekunden lang gezeigt und dann automatisch weitergeschaltet. Beim Aufräumen meiner Festplatte habe ich meinen Text zu den <a href="https://muchsoft.com/presentations/Sei-kein-Held-sei-eine-Null-XDDE-2018.pdf">Folien</a> wiedergefunden. Und da es meines Wissens weder Fotos noch einen Video-Mitschnitt von der <a href="https://www.xpdays.de/2018/sessions/123-sein-kein-held-sei-eine-null-ueber-10x-developer-und-andere-herausforderungen.html">Präsentation</a> gibt, halte ich die <a href="https://muchsoft.com/presentations/Sei-kein-Held-sei-eine-Null-XDDE-2018.pdf">Kurzgeschichte</a> einfach hier für die "Ewigkeit" fest :-)</p><p>[Update 04.03.2021] Bei der <a href="https://www.oop-konferenz.de/oop2021.html">OOP 2021</a> durfte ich diesen Pecha Kucha noch einmal halten. Den <a href="https://www.youtube.com/watch?v=qrCcFWMlXMA">Video</a>-Mitschnitt davon gibt es <a href="https://www.youtube.com/watch?v=qrCcFWMlXMA">hier</a>.<br /></p><p><b>[0:00, "Kleine Helden"]<br /></b>Moin zusammen!<br />Wollen wir nicht alle kleine oder große Helden sein? Zumindest ein klein bisschen?<br />Die meisten von uns wollten als Kind vermutlich Superkräfte haben. Besser sein als die anderen. Herausstechen. Bewundert werden.<br />Aber es ist doch viel schöner, einen Freund zu haben, der auch Superkräfte hat. Dann bin ich nicht so allein.<br /><br /><b>[0:20, "Berggipfel"]<br /></b>Später im Leben nehmen wir uns dann richtig große Ziele vor. Wir suchen Herausforderungen.<br />Wir wollen hoch hinaus, vielleicht ganz nach oben.<br />Und das ist auch gut so. Denn ohne das Streben nach Besserem hören wir irgendwann auf, gut zu sein.<br />Aber: Wir merken schnell, dass wir es nach oben nicht alleine schaffen.<br /><br /><b>[0:40, "Seilschaft"]<br /></b>Es mag Einzelne geben, die riskieren alles und gewinnen alles alleine. Der Großteil der Einzelnen schafft das nicht. Die gehen unter. Die stürzen ab.<br />Zusammen sichern wir uns – Profis und Neulinge – nicht nur dort, wo es steil ist, sondern auch schon vorher, weil wir die unsicheren Stellen nicht sofort als solche erkennen.<br />Und dann gelangen wir zusammen nach oben. Und stehen über der Welt.<br /><br /><b>[1:00, "ISS"]<br /></b>Am höchsten hinaus geht’s zum ultimativen Abenteuer unserer Zeit. In den Weltraum, zur <a href="https://de.wikipedia.org/wiki/Internationale_Raumstation">ISS</a> – hier noch mit Space Shuttle aus der Zeit des Aufbaus.<br />Wer mich kennt, weiß, dass Weltraumfahrt mein Hobby ist.<br />Ich bin leider kein Astronaut geworden, begeistere mich aber trotzdem auf viele Arten dafür...<br /><br /><b>[1:20, "LEGO-Apollo"]<br /></b>Hier seht Ihr beispielsweise mein Weihnachtsgeschenk vom letzten Jahr.<br />Mein Sohn hat natürlich angeboten, mitzuhelfen. Ich durfte dann geduldig beobachten und bei den schwierigen Stellen ab und zu auch mitmachen.<br />Wenn wir nicht gerade <a href="https://www.lego.com/de-de/product/lego-nasa-apollo-saturn-v-21309">LEGO</a> bauen, lese ich gerne Bücher über die Raumfahrt. Auf ein Buch wurde ich im vergangenen Jahr durch einen Vortrag von <a href="https://blog.schauderhaft.de/">Jens Schauder</a> aufmerksam.<br /><br /><b>[1:40, "Chris Hadfield"]<br /></b>Es geht um das Buch <a href="https://www.thalia.de/shop/home/artikeldetails/ID36627704.html">"An Astronaut’s Guide to Life on Earth"</a> von <a href="https://chrishadfield.ca/">Chris Hadfield</a>, einem Kanadier.<br />Er flog mir dem Space Shuttle zur Mir, zur ISS, und war später 6 Monate auf der ISS.<br />Bekannt wurde er den meisten wohl durch seine Cover-Songs aus dem All, hier z.B. „Space Oddity“ von David Bowie.<br />(Zu finden auf <a href="https://www.youtube.com/watch?v=KaOC9danxNo">YouTube</a>; und ja, er hat auch diverse populärwissenschaftliche Berichte aus dem All gesendet ;-)<br /><br /><b>[2:00, "Aim to be a Zero"]<br /></b>In dem Buch prägte er den Satz „Aim to be a Zero“.<br />Er teilt Astronauten in drei Kategorien ein:<br />Die "+1" sind ein echter Gewinn fürs Projekt und fürs Team. Das Problem dabei: Wer es darauf anlegt, eine +1 zu sein, steuert geradewegs auf eine -1 zu. Weil er als Angeber rüberkommt. Oder weil er seine Aufgabe grandios verbockt.<br /><br /><b>[2:20, "Space Shuttle Cockpit"]<br /></b>Nun sind Astronauten alle Überflieger, die Besten der Besten, die wollen alle eine +1 sein. Trotzdem müssen gerade sie sich bemühen, einfach nur ihre Arbeit gut und unaufgeregt zu machen. Einfach eine gute Null zu sein.<br />Denn der Versuch, alles über-perfekt zu machen, führt in diesem Umfeld schnell in die Katastrophe...<br /><br /><b>[2:40, "John Glenn"]<br /></b>Astronauten müssen lernen, sich zurückzunehmen, obwohl sie natürlich ALLES können.<br />Das ist schwierig und war auch nicht immer so. Die ersten Raumkapseln wurden alleine geflogen, erst danach tauchten Teams in den Raumschiffen auf.<br />Die Astronauten der ersten Generation waren Cowboys, Draufgänger.<br />Einzelkämpfer mit ausgeprägtem Ego.<br /><br /><b>[3:00, "10x Developer"]<br /></b>Das kennen wir auch in der Softwareentwicklung.<br />Wir kennen alle einen Kollegen, der unglaublich schnell abliefert. Der alle schwierigen Aufgaben bekommt oder sich nimmt. Der Liebling des Chefs.<br />Er wirkt immer beschäftigt und ist gefühlt 10x schneller als der Rest.<br />Deshalb nennen wir ihn „10x Developer“. Es gibt dabei aber ein Problem.<br /><br /><b>[3:20, "gut gemeint"]<br /></b>Wenn ich glaube, der Einzige zu sein, der solche Probleme lösen kann, dann werde ich das auch irgendwann sein. Dann werde ich zum Flaschenhals meines Teams und meiner Firma.<br />Und nach dem Abliefern der schnellen Lösung muss das Team 10x so lange aufräumen, bis die Lösung stabil betrieben und weiterentwickelt werden kann...<br /><br /><b>[3:40, "ISS-Schlaf"]<br /></b>Außerdem entsteht durch solche 10x Developer eine Heldenkultur, in der Feuerwehreinsätze belohnt und nachhaltige Lösungen vermieden werden. Das führt irgendwann dazu, dass sich ein Teil der Kollegen in Bürokratie und Prozess versteckt, während der andere Teil sich selbst ausbeutet und dabei ausbrennt. Schlafmangel ist nur ein Symptom davon.<br />Astronauten bekommen übrigens genug Schlaf, darauf achtet die Bodenkontrolle.<br /><br /><b>[4:00, "10x besser"]<br /></b>10x Developer kann man auch anders definieren, wie <a href="https://www.kateheddleston.com/blog/becoming-a-10x-developer">Kate Heddleston</a> das in ihrem Blog tut:<br />Mach die Menschen um dich herum 10x besser!<br />Du bist nicht gut, wenn Du besser bist als die anderen, sondern wenn Du den anderen hilfst, genauso gut wie Du zu werden.<br />Das nützt dem Team, dem Projekt, der Firma langfristig.<br /><br /><b>[4:20, "Außenbordeinsatz"]<br /></b>Wie passt das zu den Astronauten? Immerhin sehen wir immer solche heldenhaften Fotos...<br />Was man hier aber nicht sieht: Da draußen ist noch mindestens ein zweiter Astronaut, damit sie sich gegenseitig helfen können. Und drinnen noch ein Dritter als Unterstützung.<br />Die Astronauten MÜSSEN bei solchen Aufgaben eine unaufgeregte, gute Null sein.<br /><br /><b>[4:40, "Alexander Gerst (<a href="https://www.thalia.de/shop/home/artikeldetails/ID44264639.html">Buch</a>)"]<br /></b>Und das mit der Null ist keine Privatmeinung von Chris Hadfield. <a href="https://alexandergerst.esa.int/">Alexander Gerst</a> erwähnt das ebenfalls als mittlerweile normale Erwartungshaltung. Es geht dabei nicht um Mittelmäßigkeit, sondern um „gut genug“. Denn perfekt wird es sowieso nie. Und bei komplexen Aufgaben können Aktionismus und Heldentum katastrophale Folgen haben.<br />Warum ist das für uns in der Softwareentwicklung relevant? Viele unserer Probleme sind unglaublich komplex.<br /><br /><b>[5:00, "Cynefin"]<br /></b>Das <a href="https://en.wikipedia.org/wiki/Cynefin_framework">Cynefin-Framework</a> (Kai-ne-vin) teilt unsere Probleme in vier Kategorien ein. In der einfachen oder offensichtlichen Kategorie und vielleicht auch noch in der komplizierten Kategorie können wir Probleme vielleicht noch als Einzelkämpfer lösen.<br />Die Probleme der komplexen Kategorie aber können wir erst im Nachhinein richtig verstehen. Das ist in den meisten Fällen viel zu komplex für einen Kopf allein. Diese Probleme sollten wir mit dem ganzen Team lösen.<br /><br /><b>[5:20, "<a href="https://twitter.com/fduesterbeck/status/1001327822026272769">Star Shape</a>"]<br /></b>Dafür müssen wir uns als Entwickler anders aufstellen. Vielleicht habt Ihr vom T-Shape gehört: Zusätzlich zu meinem technischen Spezialwissen schaue ich über den technischen Tellerrand. Aber auch das reicht nicht aus. Ich muss das Problem auch fachlich verstehen. Und ich muss soziales Können haben, um mich mit anderen auszutauschen.<br />Das nennen wir <a href="http://kriscoverdale.com/2016/10/08/beyond-the-t-shaped-person-becoming-star-shaped/">Star-Shape</a>. Star-shaped developers, star-shaped testers, star-shaped product owners...<br /><br /><b>[5:40, "Mob Programming"]<br /></b>Und vielleicht benötigen wir dafür auch andere Formen der Zusammenarbeit.<br />Aufgaben einzeln lösen und das Ergebnis reviewen, wird den immer komplexeren Problemen nicht mehr gerechnet. Vielleicht ist selbst Pair Programming nicht genug, wenn nämlich das gesamte Team den Lösungs-Prozess verstehen muss.<br />Mob Programming, bei dem das gesamte Team zusammen eine Aufgabe löst, hat sich hier als unglaublich hilfreich erwiesen.<br /><br /><b>[6:00, "Sharing Culture"]<br /></b>Das alles wird letztlich auch die Kultur unserer Zusammenarbeit und die Kultur unseres Unternehmens verändern.<br />Wir brauchen keine geteilten Dokumente. Sondern geteiltes Verstehen durch gemeinsam Machen.<br />Weg von einer Kultur des Versteckens, Abkapselns – hin zu einer Kultur des Nehmens und Gebens von Wissen und Können.<br /><br /><b>[6:20, "Danke"]<br /></b>Sei kein Held. Leg es nicht darauf an, hervorzustechen.<br />Versuch lieber, eine gute Null zu sein. Mit all Deinem Können. Hilf dem Team. Lass Dir helfen.<br />Werdet zusammen besser.<br />Dann löst Ihr gemeinsam und heldenhaft die immer komplexeren Probleme unserer Zeit.<br />Sei kein Held, sei eine Null. Aim to be a Zero.<br />Vielen Dank!<br /><br /><b>[6:40, Ende]<br /></b><br /><br />Von <a href="https://www.benjaminfelis.de/">Benjamin Felis</a> gibt es übrigens ein ganz tolles <a href="https://twitter.com/thmuch/status/1060581500427608065">Graphic Recording</a> aller vier Kurzpräsentationen.<br /></p>Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-5603342679142024802017-12-11T08:54:00.000+01:002018-02-26T08:15:06.656+01:00Unendliche Streams in Java 8 und 9[This text is available in English on <a href="https://dzone.com/articles/infinite-streams-in-java-8-and-9">DZone</a>.]<br />
<br />
Mit Lambdas und Streams können wir seit Java 8 endlich etwas funktionaler programmieren. Ein wichtiges Konzept in funktionalen Sprachen sind unendliche Streams, die durch geeignete Bedingungen passend "abgebrochen" werden (die Streams sind natürlich nicht wirklich unendlich, sondern sie werden "lazy" - also erst bei Bedarf - ausgewertet). So etwas gibt es auch in Java, und das möchte ich mit dem folgenden Beispiel zeigen.<br />
<br />
Es geht um den Luhn-Algorithmus zur einfachen Berechnung einer Prüfsumme. Inspiriert dazu wurde ich durch den <a href="https://www.oio.de/m/konf/vortraege/JavaForumNord_JVM-Functional-Language-Battle_Falk%20Sippach.pdf">JVM Functional Language Battle</a> von <a href="https://twitter.com/sippsack">Falk Sippach</a>. Zu sehen gab es in dem Vortrag verschiedene Implementierungen, von klassischem, imperativem Java bis hin zu <a href="https://www.scala-lang.org/">Scala</a>, <a href="http://www.frege-lang.org/">Frege</a> und Java mit <a href="http://www.vavr.io/">Vavr</a> (vormals Javaslang).<br />
<br />
Eine kurze Zusammenfassung vom <a href="https://de.wikipedia.org/wiki/Luhn-Algorithmus">Luhn-Algorithmus</a>:<br />
<ul>
<li>Gegeben sei eine nicht negative, ganze Zahl beliebiger Länge.</li>
<li>Von rechts nach links wird jede 2. Ziffer der Zahl verdoppelt. Entsteht dabei eine zweistellige Zahl, wird die Quersumme gebildet.</li>
<li>Alle so entstandenen Ziffern (einfach und verdoppelt) werden aufsummiert.</li>
<li>Die Prüfsumme ist gültig, wenn die Gesamtsumme ohne Rest durch 10 teilbar ist.</li>
</ul>
Wie kann man das mit <b>Java-8</b>-Streams implementieren? Dazu noch ohne Variablen, Zustandsänderungen und Fallunterscheidung? Beispielsweise so: <br />
<br />
<pre>import java.util.PrimitiveIterator;
import java.util.stream.IntStream;
public class Luhn {
public static boolean isValid(String number) {
PrimitiveIterator.OfInt faktor =
<b>IntStream.iterate</b>(1, i -> 3 - i).iterator();
int summe = new StringBuilder(number).reverse()
.toString().chars()
.map(c -> c - '0')
.map(i -> i * faktor.nextInt())
.reduce(0, (a, b) -> a + b / 10 + b % 10);
return (summe % 10) == 0;
}
}
</pre>
<br />
Als Beispiel durchlaufen wir <span style="font-family: "courier new" , "courier" , monospace;">isValid()</span> mit dem String "8763".<br />
<br />
Mit <span style="font-family: "courier new" , "courier" , monospace;">IntStream.iterate()</span> erzeugen wir uns einen endlosen (aber lazy berechneten) Stream der Zahlen 1,2,1,2,1,2, ... – das ist der Faktor, mit dem jede Ziffer multipliziert wird.<br />
<br />
Dann packen wir den Zahlen-String in einen StringBuilder, weil der eine <span style="font-family: "courier new" , "courier" , monospace;">reverse()</span>-Methode bietet. Damit können wir die einzelnen Ziffern als <span style="font-family: "courier new" , "courier" , monospace;">chars()</span>-Stream vorwärts durchlaufen (und müssen nicht von hinten zählen):<br />
<br />
'3', '6', '7', '8'<br />
<br />
Jedes Ziffern-char wir nun mit dem ersten <span style="font-family: "courier new" , "courier" , monospace;">map()<span style="font-family: inherit;"></span></span> auf den Ziffern-Wert 0..9 abgebildet und mit dem zweiten <span style="font-family: "courier new" , "courier" , monospace;">map()</span> mit dem nächsten Faktor aus dem endlosen IntStream multipliziert:<br />
<br />
3, 12, 7, 16<br />
<br />
Mit <span style="font-family: "courier new" , "courier" , monospace;">reduce()</span> berechnen wir nun die Quersumme <i>aller</i> dieser Zahlen, wobei wir für jede Zahl die Zehnerstelle (div 10) und die Einerstelle (mod 10) addieren, also<br />
<br />
(0 + 3) + (1 + 2) + (0 + 7) + (1 + 6)<br />
<br />
Ergibt 20, und das ist ohne Rest durch 10 teilbar. Somit ist die Prüfsumme gültig.<br />
<br />
<br />
Mit <b>Java 9</b> können wir uns das Umwandeln in einen String (und das Umkehren der Zeichenfolge) sparen, indem wir aus der zu prüfenden Zahl einen weiteren unendlichen Stream machen, den wir mit <span style="font-family: "courier new" , "courier" , monospace;">takeWhile()</span> und einem geeigneten Lambda-Prädikat kappen:<br />
<br />
<pre>import java.util.PrimitiveIterator;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
public class Luhn9 {
public static boolean isValid(long number) {
PrimitiveIterator.OfInt faktor =
<b>IntStream.iterate</b>(1, i -> 3 - i).iterator();
long summe = <b>LongStream.iterate</b>(number, n -> n / 10)
.<b>takeWhile</b>(n -> n > 0)
.map(n -> n % 10)
.map(i -> i * faktor.nextInt())
.reduce(0, (a, b) -> a + b / 10 + b % 10);
return (summe % 10) == 0;
}
} </pre>
<br />
Der <span style="font-family: "courier new" , "courier" , monospace;">chars()</span>-Stream in der Java-8-Lösung war nach der Verarbeitung aller Zeichen zu Ende. <span style="font-family: "courier new" , "courier" , monospace;">LongStream.iterate()</span> in dieser Java-9-Variante teilt die zu prüfende Zahl dagegen endlos immer weiter durch 10, also z.B.<br />
<br />
8763, 876, 87, 8, 0, 0, 0, ...<br />
<br />
Wir müssen den Stream also nur verarbeiten, solange die Zahl größer Null ist. <span style="font-family: "courier new" , "courier" , monospace;">takeWhile()</span> macht genau das und liefert folgenden endlichen Stream:<br />
<br />
8763, 876, 87, 8<br />
<br />
Das erste <span style="font-family: "courier new" , "courier" , monospace;">map()</span> liefert einen Stream der letzten Ziffern (mod 10):<br />
<br />
3, 6, 7, 8<br />
<br />
Der Rest (Multiplizieren mit dem Faktor und Quersummen-Addition) ist dann wieder wie bei der Java-8-Lösung.<br />
<br />
Das ist vielleicht noch nicht perfekt funktional, weil wir keine
Funktionskomposition beliebiger passender Funktionen durchführen können,
sondern auf die vorhandene Stream-API angewiesen sind. Aber für
Java-Bordmittel ist das doch gar nicht so schlecht.<br />
<br />
<br />
Und wer noch nicht genug davon hat: Ein anderes schönes Beispiel für unendliche Java-Streams ist die Berechnung von <a href="https://de.wikipedia.org/wiki/Kaprekar-Zahl">Kaprekar-Zahlen</a>, was kürzlich als Challenge auf <a href="https://dev.to/peter/challenge-find-kaprekar-numbers-14m">dev.to</a> lief. Neben <span style="font-family: "courier new" , "courier" , monospace;">takeWhile()</span> kommt bei der <a href="https://dev.to/peter/challenge-find-kaprekar-numbers-14m/comments/1b32">Java-Lösung</a> noch <span style="font-family: "courier new" , "courier" , monospace;">limit()</span> zum Einsatz, um einen unendlichen Stream nicht mit einer Bedingung, sondern mit einer festen Anzahl zu begrenzen.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-86308604206817688182017-03-13T10:45:00.001+01:002017-03-13T10:45:34.750+01:00JSF-FacesContext in Unit-Tests mockenBeim Schreiben von Unit-Tests für <a href="https://de.wikipedia.org/wiki/JavaServer_Faces">JSF</a>-Beans kommt es leider ab und zu vor, dass der zu testende Code eine Fremdbibliothek verwendet, in der statisch auf<br />
<pre>FacesContext.getCurrentInstance()
</pre>
zugegriffen wird. Beim Ausführen des Tests kann dann eine NullPointerException im Code der Fremdbibliothek auftreten, die man nicht ohne Weiteres weg bekommt, denn der FacesContext hat zwar einen Getter, aber keinen Setter für das aktuelle Kontext-Objekt.<br />
<br />
Hier kommt <a href="http://showcase.omnifaces.org/">OmniFaces</a> zu Hilfe – eine nützliche Hilfsbibliothek für JSF, die man vielleicht sowieso schon ins Projekt eingebunden hat. OmniFaces bietet die Methode <code>Faces.setContext()</code> an, mit der man den aktuellen JSF-Kontext setzen kann. Normalerweise benötigt man diese Methode nicht, aber in JSF-Unit-Tests kann sie Gold wert sein. Übergibt man z.B. ein Mockito-Mock-Objekt, ist man die NullPointerException schnell und einfach los:<br />
<pre>
import org.mockito.Mock;
import org.omnifaces.util.Faces;
public class FrontendBeanTest {
@Mock
private FacesContext facesContextMock;
@Test
public testFuerJsfBean {
// Given
<b>Faces.setContext( facesContextMock );</b>
// When
...
// Then
...
}
}
</pre>Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-43041890448779684182017-03-10T20:06:00.000+01:002017-05-28T11:30:40.092+02:00Firefox 52 und Java-AppletsMit Version <a href="https://www.mozilla.org/en-US/firefox/52.0/releasenotes/">52.0</a> hat Firefox die <i>Unterstützung für Plugins eingestellt</i>, die auf die Uralte <a href="https://en.wikipedia.org/wiki/NPAPI">NPAPI</a> setzen – das betrifft auch das Java-Plugin und somit Java-Applets.<br />
<br />
Das war schon länger <a href="https://www.fxsitecompat.com/en-CA/docs/2016/plug-in-support-has-been-dropped-other-than-flash/">angekündigt</a> und ist absolut nachvollziehbar, denn die NPAPI hat diverse Sicherheitsprobleme. Zudem sind Java-Applets eine sterbende Technologie, und mit <a href="https://blogs.oracle.com/java-platform-group/entry/moving_to_a_plugin_free">Java 9</a> wird das Applet-Browser-Plugin auf den Status <i>deprecated</i> gesetzt werden.<br />
<br />
Manchmal ist man aber doch noch darauf angewiesen, dass man ein Applet im Browser aufrufen kann. Und das geht auch mit Firefox 52 noch:<br />
<ul>
<li>Zum Einen kann man <a href="https://www.mozilla.org/en-US/firefox/organizations/">Firefox 52 ESR</a> (Extended Support Release) verwenden. Darin wird der Plugin-Support bis Mai 2018 aktiviert bleiben.</li>
<li>Zum Anderen – und das ist vermutlich die schnellere Lösung – kann man die Firefox-Konfiguration anpassen. Dazu ruft man in der Adresszeile <a href="about:config">about:config</a> auf, macht einen Rechtsklick in die Liste der Optionen und wählt im erscheinenden Popup Neu/Boolean auf. Als Namen gibt man <b>plugin.load_flash_only</b> an, als Wert <b>false</b>. Nach einem Browser-Neustart können Java-Applets dann wieder genutzt werden.</li>
</ul>
Und immer dran denken: Die Nutzung sowohl dieser Informationen als auch von Plugins wie Flash und Java-Applets geschieht vollkommen auf eigene Gefahr.<br />
<ul>
</ul>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-61864709360097885422016-06-22T22:44:00.000+02:002017-02-10T21:04:03.058+01:00Pair Programming & das GehirnMitte Mai haben <a href="https://twitter.com/donmumpico">Stephan Kraus</a> und ich auf der <a href="http://www.sea-con.de/">SEACON</a> einen Vortrag über <a href="http://www.sea-con.de/seacon2016/konferenz/konferenzprogramm/vortrag/fr-43-1/title/pair-programming-entwicklers-freund-managers-feind-coaching-erfahrungen-aus-einem-grossprojek.html">Pair-Programming-Coaching im Großprojekt</a> gehalten. Wir haben dabei von unseren Erfahrungen mit der<a href="https://dev.otto.de/"> Neu- und Weiterentwicklung</a> einer <a href="http://www.otto.de/">großen E-Commerce-Plattform</a> berichtet.<br />
<br />
Das Coaching bieten wir dort als <a href="http://dev.otto.de/2013/09/23/14-friends-of-pair-programming/">Angebot an die zahlreichen Teams</a> an, um Pair Programming einfach mal ein paar Wochen während der ganz normalen Arbeit kennenzulernen (bzw. zu intensivieren), um die Vorzüge von kontinuierlichem Wissenstransfer und gegenseitiger Qualitätsverbesserung zu erleben. Immer im Wissen, dass man auch bei schon bei ziemlich <a href="https://dev.otto.de/2015/11/24/process-automation-and-continuous-delivery-at-otto-de/">guter</a> <a href="https://dev.otto.de/2016/06/08/sind-wir-wirklich-nur-testmanagerinnen/">Qualität</a> immer noch besser werden kann – weil wir als Menschen Fehler machen (aus denen wir als Team viel lernen können) und weil auch Technik nicht perfekt ist.<br />
<br />
Spannend war, dass bei der Konferenz direkt nach uns der <a href="http://www.sea-con.de/seacon2016/konferenz/konferenzprogramm/vortrag/fr-53-1/title/brain-patterns-in-der-softwareentwicklung.html">Vortrag</a> von Julia Dellnitz und Jan Gentsch über <a href="https://www.smidig.de/2016/09/brain-patterns-in-der-softwarentwicklung/">Brain Patterns in der Softwareentwicklung</a> stattfand – wo ganz nebenbei ;-) erklärt wurde, <i>warum</i> man mit Pair Programming die typischen Gehirn-Fallen, in die man sonst leicht bei der Kopfarbeit hineintappt, elegant austrickst:<br />
<ul>
<li>Unser Gehirn interpretiert unklare Dinge so, wie sie uns gerade passen. Natürliche Sprache (zwischen Fachbereich und Entwickler, aber auch zwischen den Entwicklern selbst!) ist oft nicht eindeutig... Pair Programming hilft hier, indem das, was man meint, zügig <i>als Programmcode hingeschrieben</i> und dann vom Pair verbessert bzw. vervollständigt wird. Programmierer können erfahrungsgemäß über und mit Code viel klarer diskutieren.</li>
<li>Wir können alleine nicht gleichzeitig über das "Was" und das "Wie" nachdenken. Die zwei Rollen beim Pair Programming decken aber beides ab: Der <i>Driver</i> an der Tastatur kümmert sich um die Implementierung (das "Wie"), der <i>Navigator</i> als strategischer Denker ist für das "Was" zuständig. Und weil die Rollen in Mikroiterationen ständig wechseln, kann dann jeder doch beides zur Lösung beitragen.</li>
</ul>
Die Patterns erklären außerdem, warum es wichtig ist, Pair Programming als sehr intensive Form der Zusammenarbeit zu <i>üben</i>:<br />
<ul>
<li>Sozialer Schmerz durch offenes, ehrliches Feedback wird im Gehirn wie physischer Schmerz empfunden. Das Geben und Annehmen von konstruktivem Feedback muss daher Schritt für Schritt gelernt und verbessert werden, damit es von den Team-Mitgliedern nicht als persönlicher Angriff empfunden wird.</li>
<li>Ich als der beste Programmierer der Welt muss lernen, dass ich vielleicht doch mal einen Fehler mache... und dass meine Kollegen manchmal auch echt gute Ideen haben.</li>
</ul>
Der Vortrag war eine schöne Bestätigung, dass wir mit der Förderung von Pair Programming als wichtiger <a href="https://de.wikipedia.org/wiki/Extreme_Programming#Praktiken">XP</a>-Praktik auf dem richtigen Weg sind. Die kurze, schriftliche Zusammenfassung inkl. <a href="https://www.smidig.de/2016/09/brain-patterns-in-der-softwarentwicklung/">Brain-O-Mat</a> sollte jeder Kopfarbeiter mal gelesen haben!Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-16261255614985330602016-03-19T21:29:00.000+01:002016-03-19T21:29:37.899+01:00Closures in ClojureEine Sprache, die nach Closures benannt ist (und genauso ausgesprochen wird), sollte ziemlich gut mit Closures umgehen können :-). Das Zähler-Beispiel für <a href="http://javabarista.blogspot.de/2016/03/closures-in-java.html">Java</a>, <a href="http://javabarista.blogspot.de/2016/03/closures-in-groovy.html">Groovy</a>, <a href="http://javabarista.blogspot.com/2010/01/closures-in-scala.html">Scala</a> etc. sieht in <a href="https://clojure.org/">Clojure</a> wie folgt aus:<br /><br />
<pre>
; closures.clj
(defn zaehlerMitStartwert [startwert]
(let [zaehler (atom startwert)]
(fn [] (swap! zaehler inc))))
(def next1 (zaehlerMitStartwert 10))
(def next2 (zaehlerMitStartwert 20))
(println (next1)) ;; 11
(println (next2)) ;; 21
(println (next2)) ;; 22
(println (next1)) ;; 12
</pre>
<br />
In der Funktion <code>zaehlerMitStartwert</code> wird im <code>let</code>-Block ein lokales Binding für <code>zaehler</code> angelegt. Der Wert von <code>zaehler</code> ist ein Atom mit dem übergebenen <code>startwert</code>. Ein Atom speichert in Clojure einen Zustand, der threadsicher ausgetauscht werden kann. Rückgabe von <code>zaehlerMitStartwert</code> ist eine Funktion ohne Parameter: <code>(fn [] ...)</code> - dies ist die Closure, die die freie Variable <code>zaehler</code> außerhalb der Closure-Funktion bindet (umschließt). Die Closure-Funktion verändert (<code>swap!</code>t) den <code>zaehler</code>-Wert mithilfe der <code>inc</code>-Funktion und gibt den neuen (reinge<code>swap!</code>ten) Wert zurück.
<br /><br />
Dann definieren wir zwei globale Variablen <code>next1</code> und <code>next2</code>, die mit je einer Closure-Instanz initialisiert werden. Diese Funktionen werden mit der <a href="https://de.wikipedia.org/wiki/Clojure">Clojure</a>- bzw. <a href="https://de.wikipedia.org/wiki/Lisp">Lisp</a>-typischen Syntax <code>(next1)</code> aufgerufen (statt <code>next1()</code> o.ä.).Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-8935428092802319842016-03-16T21:34:00.000+01:002016-03-16T21:34:08.581+01:00Closures in GroovyBeim <a href="http://www.javaland.eu/">JavaLand 2016</a> vor einer Woche hat <a href="https://twitter.com/mittie">Dierk König</a> einen schönen Einstieg in die elegante und kompakte Ausdrucksweise von <a href="http://www.groovy-lang.org/">Groovy</a> gegeben. Dabei hat er als Closure-Beispiel einen Zähler verwendet, den ich hier – weil er so gut zu meinen bisherigen Closure-Beispielen (u.a. für <a href="http://javabarista.blogspot.de/2016/03/closures-in-java.html">Java</a>, <a href="http://javabarista.blogspot.de/2010/02/closures-in-javascript.html">JavaScript</a> und <a href="http://javabarista.blogspot.com/2010/01/closures-in-scala.html">Scala</a>) passt – zeigen möchte (Fehler im Code gehen selbstverständlich auf mein Konto ;-):
<br />
<br />
<pre>// closures.groovy
Closure zaehlerMitStartwert(startwert) {
return {
startwert++
}
}
def next1 = zaehlerMitStartwert(10)
def next2 = zaehlerMitStartwert(20)
println next1() // 10
println next2() // 20
println next2() // 21
println next1() // 11
</pre>
<br />
Das <code>return</code> in der Funktion <code>zaehlerMitStartwert</code> gibt die Closure – den darauffolgenden Block mit geschweiften Klammern und der Zähl-Anweisung – zurück. Im Block selbst ist kein <code>return</code> notwendig, weil hier für den Compiler klar ist, dass die letzte (und hier einzige) Anweisung im Block das Ergebnis der Block-Funktion ist. Man sieht sehr schön, dass Groovy-Closures problemlos freie <i>Variablen</i> außerhalb des Closure-Blocks binden - so, wie das auch bei Scala der Fall ist (aber anders als bei Java, wo normalerweise nur final-Werte und nur mit Tricks Variablen gebunden werden können).
<br />
<br />
Wer das Groovy-Skript ausprobieren möchte, kann es einfach in die <a href="https://groovyconsole.appspot.com/">Groovy-Web-Console</a> kopieren und dort ausführen.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-10486416118656667072016-03-01T17:44:00.000+01:002016-03-01T17:44:15.151+01:00Closures in JavaJava 8 ist nun bald zwei Jahre alt – eine gute Gelegenheit, meine kleine Closures-Reihe weiterzuführen. Nach <a href="http://javabarista.blogspot.com/2010/01/closures-in-scala.html">Scala</a>, <a href="http://javabarista.blogspot.com/2009/08/closures-in-javafx-script.html">JavaFX</a> und <a href="http://javabarista.blogspot.de/2010/02/closures-in-javascript.html">JavaScript</a> sind heute also Closures in Java an der Reihe. Das Zähler-Beispiel sieht mit den Lambda-Ausdrücken aus Java 8 so aus:<br />
<pre>
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntSupplier;
public class ClosuresJava8 {
static IntSupplier <b>zaehlerMitStartwert</b>(int startwert) {
AtomicInteger n = new AtomicInteger(startwert);
<b>IntSupplier s = () -> {
return n.incrementAndGet();
};</b>
return s;
}
public static void main(String[] args) {
IntSupplier next1 = zaehlerMitStartwert(10);
IntSupplier next2 = zaehlerMitStartwert(20);
System.out.println( next1.<b>getAsInt()</b> ); // 11
System.out.println( next2.getAsInt() ); // 21
System.out.println( next2.getAsInt() ); // 22
System.out.println( next1.getAsInt() ); // 12
}
}
</pre>
Die Methode <code>zaehlerMitStartwert()</code> gibt eine <code>IntSupplier</code>-Lambda-Funktion zurück. <a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/IntSupplier.html">IntSupplier</a> ist ein sogenanntes "funktionales Interface", d.h. ein Interface mit genau einer Methode (in diesem Fall <code>getAsInt()</code>). Ein "Supplier" bezeichnet dabei eine Methode ohne Parameter, die einen Rückgabewert liefert.<br/>
<br/>
Der <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html">Lambda-Ausdruck</a> ist dabei so etwas wie eine Kurzschreibweise für ein anonymes Objekt des <code>IntSupplier</code>-Interfaces, wobei das <code>new</code>, der Interface-Name und der Methodenname weggelassen werden. Die Anweisung im Lambda-Ausdruck ("<code>return n.incrementAndGet();</code>") wird dabei zum Rumpf der unsichtbaren <code>getAsInt()</code>-Methode.<br/>
<br/>
Im Lambda-Ausdruck wird der übergebene Startwert erhöht und zurückgegeben. Allerdings müssen die gebundenen Variablen außerhalb des Lambda-Ausdrucks in Java <code>final</code> oder "effectively final" sein (d.h. sie dürfen nicht verändert werden, damit der Compiler <code>final</code> annehmen kann). Deshalb lassen wir den Lambda-Ausdruck ein <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html">AtomicInteger</a>-Objekt binden – der Inhalt des <code>AtomicInteger</code>-Objektes ist veränderbar, die Referenz darauf ist aber wie gewünscht final.<br />
<br />
Im obigen Beispiel wird die Lambda-Funktion nur zum einfacheren Verständnis in der lokalen Variablen <code>s</code> zwischengespeichert. Man kann den Lambda-Ausdruck auch direkt als Methodenergebnis zurückgeben:<br />
<pre>
static IntSupplier zaehlerMitStartwert(int startwert) {
AtomicInteger n = new AtomicInteger(startwert);
return () -> {
return n.incrementAndGet();
};
}
</pre>
Und da unser Lambda-Ausdruck nur aus einer einzigen Anweisung besteht, deren Ergebnis als Funktionsergebnis zurückgegeben wird, kann man das noch weiter kürzen und die geschweiften Klammern und das <code>return</code> im Lambda-Rumpf weglassen:<br />
<pre>
static IntSupplier zaehlerMitStartwert(int startwert) {
AtomicInteger n = new AtomicInteger(startwert);
return () -> n.incrementAndGet();
}
</pre>
Das sieht doch schon fast richtig funktional aus :-)Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-42512412223635294922015-04-30T17:42:00.000+02:002015-04-30T17:42:26.039+02:00Bean-Mapping mit MapStructAm vergangenen Wochenende habe ich auf dem <a href="http://www.mathema.de/veranstaltungen/mathema-campus">MATHEMA Campus</a> einen Vortrag über <a href="http://mapstruct.org/">MapStruct</a> gehalten. MapStruct ist ein Annotations-basierter Code-Generator für Mappings zwischen beliebigen Java-Beans und kann damit hervorragend zum Umkopieren der Daten zwischen den diversen Domänen-Modellen einer Anwendung (JPA, JAXB, REST, sonstige DTOs/POJOs ... ) genutzt werden.<br />
<br />
Der generierte Mapping-Code nutzt <i>keine Reflection</i>, sondern Getter- und Setter-Aufrufe – und erreicht damit die <a href="https://twitter.com/joao_b_reis/status/559780053979250688">Geschwindigkeit von handgeschriebenem Mapping-Code</a>. Ganz nebenbei ist der generierte Code lesbar und verständlich. Das ist wichtig, falls der Projektverantwortliche Angst hat, ein weiteres Werkzeug ins Projekt einzubinden – zur Not stellt man den generierten Code unter Versionskontrolle und pflegt ihn später von Hand weiter (wer einmal MapStruct verwendet hat, wird das aber nicht wollen ;-).<br />
<br />
MapStruct wird als Annotations-Prozessor eingebunden, d.h. der Mapping-Code wird beim Aufruf von <code>javac</code> (bzw. bei jedem Speichern z.B. in Eclipse) generiert. Das Prozessor-JAR wird dabei nur zur Compile-Zeit benötigt. Zur Laufzeit ist bei einem Komponentenmodell wie CDI gar kein JAR als Abhängigkeit nötig, ohne Komponentenmodell nur ein winziges JAR < 20 K.<br />
<br />
Folgender Code reicht, um den Mapping-Code zwischen einer Kunde-Entity und einem Kunde-DTO generieren zu lassen:<br />
<br />
<pre><b>@Mapper(componentModel = "cdi")</b>
public interface KundeMapper {
@Mapping(target = "geburtsdatum", format = "dd.MM.yyyy")
<b>KundeDTO jpaEntity2RestDto(Kunde kunde);</b>
}
</pre>
<br />
MapStruct erzeugt daraus in etwa folgenden Code:<br />
<br />
<pre>@Generated("org.mapstruct.ap.MappingProcessor")
<b>@ApplicationScoped</b>
public class KundeMapperImpl implements KundeMapper {
<b>public KundeDTO jpaEntity2RestDto(Kunde kunde)</b> {
<i>// null-Checks
// Ziel-Objekt-Erzeugung
// Getter-/Setter-Aufrufe
// ...</i>
}
}
</pre>
<br />
Der standardmäßig generierte Code ist oft schon genau das, was man benötigt. Ansonsten ist so ziemlich alles konfigurierbar: Unterschiedliche Property-Namen in Quell- und Ziel-Bean, unterschiedliche Typen, die Objekt-Fabriken zur Ziel-Objekt-Erzeugung... Zudem können vorhandene, handgeschriebene Mapper eingebunden werden. Und Objekt-Graphen werden selbstverständlich genauso gemappt wie Collections, Maps, Arrays... Für JAXB, Joda-Time und Java 8 Date&Time stehen automatische Mappings zur Verfügung.<br />
<br />
Man merkt, dass dieser Mapper mit vielen Praxis-Problemen im Hinterkopf entwickelt wurde. Und man fragt sich relativ schnell, warum man nur so lange das Bean-Mapping von Hand oder mit Reflection-Bibliotheken durchgeführt hat ;-)<br />
<br />
Mindestvoraussetzung für MapStruct ist Java 6 – aber da heute der letzte Tag der kostenfreien Java-7-Updates (und ab morgen nur noch Java 8 "aktuell") ist, sollte das kein Problem darstellen.<br />
<br />
Die Folien zu meinem Vortrag finden sich <a href="http://www.muchsoft.com/publikationen.html">hier</a>.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-65061043000453059102015-03-13T14:39:00.000+01:002015-03-13T14:39:13.291+01:00Scope-Events in CDI 1.1Wer in einer Java-Webanwendung mitbekommen möchte, wann die Anwendung fertig bereitgestellt ist (z.B. um den Deployment-Timestamp zu ermitteln und anzuzeigen), hat dazu diverse Möglichkeiten. Eine lang verfügbare Lösung ist ein ServletContextListener, seit Java EE 6 kann man auch eine <a href="http://blog.eisele.net/2010/12/seven-ways-to-get-things-started-java.html">@Singleton-@Startup-EJB</a> verwenden. Mit CDI 1.1 in Java EE 7 haben wir nun eine weitere Variante, ein <a href="http://docs.oracle.com/javaee/7/api/javax/enterprise/context/Initialized.html">@Initialized-Event</a> für den Application-Scope (d.h. für den ServletContext):<br />
<br />
<pre>import javax.inject.Named;
import javax.enterprise.event.Observes;
import javax.enterprise.context.*;
@Named
@ApplicationScoped
public class DeploymentInfoBean {
private Date deploymentTimestamp;
public Date getDeploymentTimestamp() {
return deploymentTimestamp;
}
void deploymentFinished(
<b>@Observes
@Initialized(ApplicationScoped.class)
ServletContext context</b>) {
deploymentTimestamp = new Date();
}
}
</pre>
<br />
Wer nur am Zeitpunkt interessiert ist und keine Werte aus dem ServletContext benötigt, kann sich als Parameter auch einfach ein Object übergeben lassen und beseitigt damit die Abhängigkeit zu javax.servlet.<br />
<br />
Man kann sich nicht nur über die fertige Initialisierung eines Scopes informieren lassen, sondern auch über die Beendigung eines Scopes – und so beispielsweise Anfang und Ende von HTTP-Sessions überwachen:<br />
<br />
<pre>public class SessionWatcher {
void sessionCreated(
@Observes
@Initialized(SessionScoped.class)
HttpSession session) {
...
}
void sessionDestroyed(
<b>@Observes
@Destroyed(SessionScoped.class)
HttpSession session</b>) {
...
}
}</pre>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-21048954618176768892015-03-06T18:47:00.000+01:002015-03-29T14:08:45.968+02:00Java in OS X 10.10 "Yosemite"Wie auch schon die <a href="http://javabarista.blogspot.de/2013/10/java-in-os-x-109-mavericks.html">Vorgängerversion</a> wird <a href="http://de.wikipedia.org/wiki/OS_X_10.10">OS X 10.10</a> ohne vorinstalliertes Java 6 ausgeliefert. Bei einem Update wird eine vorhandene Java-6-Installation aus <code>/System/Library/Java/JavaVirtualMachines</code> <i>gelöscht</i>. Das alles ist eigentlich kein Problem, denn Java 6 ist eh veraltet, seit Oracle vor zwei Jahren den kostenlosen Support für Version 6 eingestellt hat – und in <code>/Library/Java/JavaVirtualMachines</code> installierte Versionen von Java 7 und Java 8 bleiben bei einem Update auf OS X 10.10 erhalten.<br />
<br />
Manche Programme benötigen aber noch Java 6 zum Starten. Yosemite weist mit einem Dialog darauf hin:
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLECpmWq5yt3USg5dHx0mSCMEvbn7HZb4Gb3NqL4GaINLkz3fWoGKZLF2vDt-kUKSVFtjGvIei0UhENVXaG4AJxFNusr5EjKydlXSMoyInHyc0YUMr7KCrgqufUx0AK3mRC4S0CfTPlfg/s1600/intellij-java6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLECpmWq5yt3USg5dHx0mSCMEvbn7HZb4Gb3NqL4GaINLkz3fWoGKZLF2vDt-kUKSVFtjGvIei0UhENVXaG4AJxFNusr5EjKydlXSMoyInHyc0YUMr7KCrgqufUx0AK3mRC4S0CfTPlfg/s1600/intellij-java6.png" height="166" width="320" /></a></div>
Mit der Schaltfläche "Weitere Infos" gelangt man zur Download-Seite von <a href="https://support.apple.com/kb/DL1572?locale=en_US&viewlocale=de_DE">Java für OS X 2014-001</a>, mit dem in Mac OS X 10.7 bis OS X 10.10 Java 1.6.0_65 installiert werden kann. Dies ist dieselbe <a href="http://www.muchsoft.com/java/macosxjavaversions.html">Java-Version</a>, die Apple auch schon mit "Java für OS X 2013-005" ausgeliefert hat – nur ein geringfügig aktuellerer Build.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-42528660544068162112014-12-17T22:18:00.000+01:002014-12-17T22:18:07.513+01:00NoSQL mit JPA und Hibernate OGMNach einer längeren Beta-Phase wurde heute die erste finale Version von Hibernate OGM <a href="http://in.relation.to/Bloggers/FirstHibernateOGMReleaseAka41Final">veröffentlicht</a>! Im Gegensatz zu Hibernate ORM, mit dem Entity-Modelle auf relationale Datenbanken abgebildet werden, kümmert sich <a href="http://hibernate.org/ogm/">Hibernate OGM</a> um das Mapping von JPA-Entities auf <a href="http://de.wikipedia.org/wiki/NoSQL">NoSQL</a>-Datenbanken.<br />
<br />
Hibernate OGM erhebt dabei gar nicht erst den Anspruch, zu jedem Anwendungsfall von NoSQL-Datenbanken zu passen. Dazu gibt es zu viele unterschiedliche Ideen und Konzepte, wie nicht-relationale Daten gespeichert werden können (unstrukturiert, als Graph, als Dokument etc.). In der Praxis wird in den schemafreien NoSQL-Datenbanken aber dennoch oft mit strukturierten Daten gearbeitet, die häufig von Hand auf ein Objektmodell abgebildet werden. Der wichtige Unterschied zu den relationalen SQL-Datenbanken besteht in den flexiblen, nicht starren Schemata, d.h. Schema-Evolution (Migration) ist im laufenden Betrieb möglich und üblich.<br />
<br />
Und genau hier glänzt Hibernate OGM: Liegt der Anwendungsfall vor, ein flexibles Entity-Modell in einer NoSQL-Datenbank zu persistieren, kann man mit den bekannten JPA-Annotationen und -Konzepten programmieren. Der Einstieg ist also denkbar einfach, wobei bei Bedarf auch produktspezifische Optimierungen möglich sind, z.B. mit nativen Abfragen (JavaScript-Abfrage-Dokumente für MongoDB, Cypher-Abfragen für Neo4j etc.).<br />
<br />
OGM versucht, für jedes NoSQL-Produkt ein möglichst "natürliches" Mapping zu nutzen, u.a. damit andere Clients eine für das jeweilige Produkt gewohnte Datenbankstruktur vorfinden. Weil aber die API immer dieselbe ist (JPA), muss man sich nicht am Anfang auf ein NoSQL-Produkt festlegen, sondern kann das Mapping – und die Performance! – in diversen NoSQL-Stores ausprobieren und vergleichen.<br />
<br />
In diesem Jahr habe ich Hibernate OGM auf einigen Konferenzen vorgestellt – die Folien der Vorträge kann man <a href="http://www.muchsoft.com/publikationen.html">hier</a> anschauen bzw. runterladen. Die Folien behandeln zwar noch die Beta-Versionen, gelten aber weitestgehend auch für die erste finale Version (die übrigens nicht die Nummer 1.0, sondern 4.1 trägt).<br />
<br />
Ansonsten: Einfach mal <a href="http://hibernate.org/ogm/downloads/">ausprobieren</a>! Wer grundlegende JPA-Kenntnisse hat, wird sich schnell mit MongoDB, Neo4J & Co. zurecht finden :-)Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-84618260370389941002013-12-17T22:04:00.000+01:002013-12-17T22:04:46.762+01:00Schema-Export mit JPA 2.1Eine der Neuerungen in <a href="http://en.wikibooks.org/wiki/Java_Persistence/What_is_new_in_JPA_2.1%3F">Java Persistence 2.1</a> ist die standardisierte Möglichkeit, aus den Mapping-Informationen des Datenmodells das Datenbank-Schema generieren zu können. Seit gestern Abend steht mit <a href="http://in.relation.to/Bloggers/HibernateORM430FinalRelease">Hibernate 4.3</a> eine weitere Implementierung des aktuellen JPA-Standards zur Verfügung, mit der folgender Code zur Schema-Generierung ausgeführt werden kann:<br />
<br />
<pre>import javax.persistence.Persistence;
...
try (FileWriter out
= new FileWriter("schema-komplett.sql")) {
Map<String, Object> props = new HashMap<>();
props.put(
"javax.persistence.schema-generation.scripts.action",
"<b>create</b>");
props.put(
"javax.persistence.schema-generation.scripts.create-target",
out);
Persistence.<b>generateSchema</b>(
"name-der-persistence-unit", props);
}
</pre>
<br />
Leider scheint im Standard keine Möglichkeit vorgesehen zu sein, eine Differenz zum aktuell in der Datenbank vorhandenen Schema zu ermitteln.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-32819794884156206192013-11-29T11:07:00.000+01:002013-12-17T21:43:44.461+01:00JPA-Schema-Export mit HibernateDie Hibernate-Tools bieten mit den Klassen <code>SchemaExport</code> und <code>SchemaUpdate</code> die Möglichkeit, das Datenbank-Schema (Tabellen, Indizes, Fremdschlüssel-Constraints etc.) aus den Mapping-Informationen des Datenmodells zu generieren. Da es sich um Hibernate-spezifische API handelt, wird von den Klassen eine Hibernate-<code>Configuration</code> erwartet. Mit Hilfe der kleinen, von Hibernate nicht offiziell unterstützten (und mittlerweile auf "<a href="http://en.wikipedia.org/wiki/Deprecation">deprecated</a>" gesetzten) Klasse <a href="http://docs.jboss.org/hibernate/core/4.2/javadocs/org/hibernate/ejb/Ejb3Configuration.html">Ejb3Configuration</a> kann aber auch eine Java-Persistence-Konfigurationsdatei <code>persistence.xml</code> eingelesen werden:<br />
<br />
<pre>import org.hibernate.cfg.Configuration;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
...
Configuration cfg =
new Ejb3Configuration()
.configure("<b>name-der-persistence-unit</b>", null)
.getHibernateConfiguration();
</pre>
<br />
Mit der ermittelten Configuration kann nun das komplette Schema ...<br />
<br />
<pre>SchemaExport schemaExport = new SchemaExport(cfg);
schemaExport.setOutputFile("schema-komplett.sql");
schemaExport.create(true, false);
</pre>
<br />
... oder aber nur die Differenz zum aktuell in der Datenbank vorhandenen Schema ermittelt werden:<br />
<br />
<pre>SchemaUpdate schemaUpdate = new SchemaUpdate(cfg);
schemaUpdate.setOutputFile("schema-diff.sql");
schemaUpdate.execute(true, false);
</pre>
<br />
Das <code>true</code> beim jeweils letzten Aufruf sorgt dafür, dass das SQL-Skript in die angegebene Datei geschrieben wird. Das <code>false</code> verhindert, dass die SQL-Anweisungen in der Datenbank ausgeführt werden.<br />
<br />
Im kommenden <a href="http://hibernate.org/orm/">Hibernate</a> 4.3, das die <a href="https://jcp.org/en/jsr/detail?id=338">JPA-2.1-Spezifikation</a> implementiert, wird Ejb3Configuration nicht mehr enthalten sein. Dann muss bzw. kann endlich die mit JPA 2.1 standardisierte Schema-Generierung verwendet werden.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-81248579298398109532013-10-24T11:46:00.000+02:002013-10-25T15:16:17.449+02:00Java in OS X 10.9 "Mavericks"Das Upgrade von OS X 10.8 nach 10.9 lief komplett problemlos, so soll es sein. Allerdings war anschließend Java 6 (Apples Implementation) nicht mehr vorhanden – wer diese Version benötigt, muss <a href="http://support.apple.com/kb/DL1572">Java for OS X 2013-005</a> manuell herunterladen und installieren. Man hat dann Java 1.6.0_65 auf der Platte (im Gegensatz zu Oracle veröffentlicht Apple noch kostenfreie Updates für Java SE 6).<br />
<br />
Ein bereits installiertes JDK 7 von Oracle bleibt normalerweise erhalten. Wer zu den Nutzern gehört, bei denen auch diese Java-Installation nach dem Upgrade nicht mehr aktiv ist, installiert einfach das aktuelle <a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html">JDK 7u45</a>. Java-7-Versionen bis einschließlich 7u25 wurden/werden von Apple aus Sicherheitsgründen <a href="http://www.java.com/de/download/faq/java_mac.xml">deaktiviert</a>.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-5958230018527522382013-10-11T21:01:00.000+02:002013-10-24T10:29:46.670+02:00Java-Batch-AnwendungenMal ein bisschen Werbung in eigener Sache. Im Frühjahr wurde der neue Standard "Batch Applications for the Java Platform" (<a href="http://jcp.org/en/jsr/detail?id=352">JSR-352</a>) – oder kurz "Java Batch" – in der Version 1.0 freigegeben. Der Standard bietet einen Rahmen, um Massendaten transaktional, blockweise und im Fehlerfall mit Wiederaufsetzpunkten zu verarbeiten. Und das unabhängig davon, ob die Batch-Verarbeitung in Java SE oder in Java EE stattfinden soll.<br />
<br />
Dieser Bereich der Datenverarbeitung war lange Zeit ganz klar vom Großrechner (Host) dominiert, aber im Rahmen der allgemeinen Migrationsbestrebungen weg von COBOL (ja, das ist immer noch ein wichtiges Thema) entstehen Batch-Anwendungen mehr und mehr im Java-Umfeld.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkdKexGfRtrLuK1KKznj_ncBuxOHNwobb2AA_T8VKSSChQZ0SKEUboqZm8YcUgxMx5QnC6wHPTJZfOggo-AqJiDOe6gnKNjenwVcLzhxukZj56H-RpN7P2BQu0jDRp2WJj31G-WbODIIA/s1600/Batch-Architektur.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkdKexGfRtrLuK1KKznj_ncBuxOHNwobb2AA_T8VKSSChQZ0SKEUboqZm8YcUgxMx5QnC6wHPTJZfOggo-AqJiDOe6gnKNjenwVcLzhxukZj56H-RpN7P2BQu0jDRp2WJj31G-WbODIIA/s320/Batch-Architektur.png" width="320" /></a></div>
<br />
Nachdem ich bereits auf dem <a href="http://www.herbstcampus.de/hc13/program/sessions.html#61">Herbstcampus</a> in Nürnberg zu diesem Thema gesprochen habe, halte ich nun einen weiteren Vortrag im Rahmen der Reihe "<a href="http://www.gfu.net/semicoaktuell.html">GFU Semicolon</a>" in Köln. Nach einem Überblick über die Batch-Architektur schauen wir uns die Grundlagen der Batch-Programmierung in Java SE mit der Referenzimplementation <a href="https://java.net/projects/jbatch/">JBatch</a> an. Die Integration in Java EE 7 zeige ich am Beispiel von <a href="https://glassfish.java.net/">GlassFish 4</a>.<br />
<br />
Der Vortrag findet statt am Dienstag, 22.10.2013, von 18:00 bis 19:00 Uhr. Im Anschluss gibt es bei einem kleinen Imbiss Gelegenheit zur Diskussion. Die Teilnahme ist kostenlos, eine <a href="http://www.gfu.net/semicoaktuell.html">Anmeldung</a> ist aber erforderlich.<br />
<br />
[Update 24.10.]<br />
Die <a href="http://www.muchsoft.com/publikationen.html">Folien</a> sind nun verfügbar – und ein Foto vom Vortrag:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid9Z4nDkOBUDj3cc9b6tpiYmccTM2pU_LppJH7dsBiDmP7C7i03cRi7mxUBI_9_FCcGnLi8Atin5d4n_I4D5cVMAc9HyOxJeAFgg_7HHrV7XNtuBkcJT24hhwr64eeBMZOD4oXRyTKZL4/s1600/1401373_706678706027272_1522037429_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid9Z4nDkOBUDj3cc9b6tpiYmccTM2pU_LppJH7dsBiDmP7C7i03cRi7mxUBI_9_FCcGnLi8Atin5d4n_I4D5cVMAc9HyOxJeAFgg_7HHrV7XNtuBkcJT24hhwr64eeBMZOD4oXRyTKZL4/s320/1401373_706678706027272_1522037429_o.jpg" width="320" /></a></div>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-10344148293952475342013-04-22T17:44:00.000+02:002013-10-15T20:21:07.104+02:00REST-Web-Services mit JAX-RS portabel aktivieren... und dennoch implementationsspezifisch konfigurieren? Eins nach dem anderen. Seit <a href="http://docs.oracle.com/javaee/6/tutorial/doc/">Java EE 6</a> kann man <a href="http://de.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services">JAX-RS</a> in einem Application-Server portabel dadurch aktivieren, dass man eine Klasse von <code>javax.ws.rs.core.Application</code> ableitet und mit <code>@ApplicationPath</code> annotiert:<br />
<br />
<pre>package com.muchsoft.rest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
<b>@ApplicationPath("/rest")</b>
public class ApplicationWithRESTWebServices
<b>extends Application</b> {
}
</pre>
<br />
D.h. dass man nun REST-Web-Services anbieten kann, ohne sich um die konkrete Implementierung – beispielsweise <a href="https://jersey.java.net/">Jersey</a> als Referenz-Implementation – kümmern zu müssen. Gut.<br />
<br />
Was aber, wenn man doch gewisse produktspezifische Features nutzen möchte, wie beispielsweise ausführliches Trace-Logging bei Jersey? Dann greift man auf den Deployment-Deskriptor <code>web.xml</code> zurück und mappt dort ein Servlet mit den entsprechenden Init-Parametern. Allerdings muss das nicht eine Jersey-Klasse (mit entsprechendem <code>servlet-mapping</code>) sein, sondern es reicht, die eigene Application-Unterklasse als Servlet einzutragen (ohne <code>servlet-mapping</code>-Element):
<br />
<br />
<pre><servlet>
<servlet-name>
<b>com.muchsoft.rest.ApplicationWithRESTWebServices</b>
</servlet-name>
<init-param>
<param-name>
com.sun.jersey.api.json.POJOMappingFeature
</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>
com.sun.jersey.config.feature.Trace
</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
</pre>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-43398528512088215702013-03-21T20:31:00.000+01:002013-05-23T12:44:32.732+02:00REST-Web-Services mit BASIC-Auth und HTTPS in GlassFishNehmen wir mal einen einfachen REST-Web-Service wie den folgenden, der alle Bestellungen des Benutzers liefern soll:
<br />
<br />
<pre>@Path("/bestellungen")
public class BestellungWebService {
@Context
private SecurityContext securityContext;
@Produces({ MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML })
@GET
public Response getAlleBestellungen() {
final String username =
securityContext.getUserPrincipal().getName();
try {
List<Bestellung> bestellungen = ...;
return Response.ok( bestellungen ).build();
}
catch (Exception e) {
return Response.serverError()
.entity( e.getMessage() ).build();
}
}
}
</pre>
<br />
Mit beispielsweise <a href="http://de.wikipedia.org/wiki/Wget">Wget</a> könnte dieser Service dann wie folgt aufgerufen werden, wenn der Applikationspfad auf "rest" gemappt ist:<br />
<code><br /></code>
<code>wget http://localhost:8080/meinewebapp/rest/bestellungen</code><br />
<br />
Fast immer muss ein solcher Service abgesichert werden, damit nur bestimmte Anwender Zugriff darauf haben. Man könnte natürlich den UserPrincipal programmatisch auswerten, aber sofern die berechtigten Benutzer im Application-Server als User eingetragen sind, ist eine deklarative Absicherung einfacher - und erfordert keine Änderungen am Quelltext.<br />
<br />
Am Beispiel von <a href="https://glassfish.java.net/downloads/3.1.2.2-final.html">GlassFish 3.1.2</a> schauen wir uns das an. Wir erlauben dem GlassFish-Admin-User, den Web-Service aufzurufen. Dazu sichern wir am Ende des Deployment-Deskriptors <code>web.xml</code> alle Ressourcen unterhalb <code>/rest</code> so ab, dass nur die "admin"-Rolle darauf zugreifen darf. Mit CONFIDENTIAL erzwingen wir zudem, dass die Übertragung der Daten per https (SSL bzw. TLS) stattfindet:<br />
<br />
<pre><?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
...
<security-constraint>
<web-resource-collection>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>admin-realm</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
</web-app>
</pre>
<br />
Die BASIC-Authentication, bei der Benutzername und Passwort im Klartext (!) übertragen werden, ist hier dennoch in Ordnung, weil wir gleichzeitig mit https die verschlüsselte Datenübertragung aktivieren.
<br />
<br />
Die berechtigten Anwender müssen im "admin-realm" eingetragen sein, das ist der Realm mit allen GlassFish-Admins (der in der Standardinstallation den User "admin" enthält). Als Beispiel für einen Security-Realm ist das hier ausreichend; in der Praxis wird man aus Sicherheitsgründen eher einen separaten Realm im GlassFish konfigurieren und in <code>web.xml</code> referenzieren.<br />
<br />
Nun müssen wir dem Application-Server noch – produktspezifisch – mitteilen, welche Benutzernamen aus dem Realm auf welche Rollen gemappt werden. Dies erfolgt in der Datei <code>glassfish-web.xml</code> (früher <code>sun-web.xml</code>):<br />
<br />
<pre><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC
"-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN"
"http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
<context-root>meinewebapp</context-root>
<security-role-mapping>
<role-name>admin</role-name>
<principal-name>admin</principal-name>
</security-role-mapping>
</glassfish-web-app>
</pre>
<br />
Fertig. Der Aufruf des Web-Services erfolgt über Wget nun mit
<br />
<br />
<pre>wget https://localhost:8081/meinewebapp/rest/bestellungen
--user ... --password ... --no-check-certificate</pre>
<br />
Ein Aufruf über unverschlüsseltes http ist nicht mehr möglich. Während wir hier der Einfachheit halber auf die Prüfung des https-Zertifikats verzichten, sollte man in der Praxis besser ein geeignetes eigenes Zertifikat im Server installieren und auf Client-Seite verwenden.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-84497270750251546772013-03-14T21:33:00.000+01:002013-03-14T21:33:54.660+01:00JAXB und HashMaps, Teil 2In <a href="http://javabarista.blogspot.de/2011/12/jaxb-und-hashmaps.html">Teil 1 zu JAXB und HashMaps</a> ging es vor einiger Zeit darum, mit möglichst wenig Aufwand im Java-Code eine XML-Struktur zu verarbeiten, deren Element-Namen teilweise fest vorgegeben waren. Teil 2 zeigt nun, wie man eine XML-Struktur mit beliebigen Element-Namen aus einer (Hash)Map innerhalb einer JAXB-Entität ausgibt (bzw. dorthin einliest). Konkret soll folgendes XML-Dokument erzeugt werden:<br />
<pre><?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<meineEntitaet>
<<b>paarliste</b>>
<<b>paar</b>>
<<b>schluessel</b>>Antwort</schluessel>
<<b>wert</b>>42</wert>
</paar>
</paarliste>
</meineEntitaet>
</pre>
Dazu benötigen wir als Erstes für einen einzelnen Eintrag der Map (also für jedes Schlüssel-Wert-Paar) folgende Klasse:<br />
<br />
<pre>import javax.xml.bind.annotation.XmlElement;
class MapElement {
<b>@XmlElement(name = "schluessel")</b>
String key;
<b>@XmlElement(name = "wert")</b>
Integer value;
MapElement() {
}
MapElement(String key, Integer value) {
this.key = key;
this.value = value;
}
}
</pre>
<br />
Die Liste der Map-Einträge wird in einer eigenen Klasse verpackt, damit wir den Namen des XML-Elements festlegen können, das die einzelnen Schlüssel-Wert-Paare umschließt:<br />
<br />
<pre>class MapElements {
<b>@XmlElement(name = "paar")
List<MapElement> mapElements;</b>
MapElements() {
}
MapElements(List<MapElement> mapElements) {
this.mapElements = mapElements;
}
}
</pre>
<br />
In der JAXB-Entität taucht nun aber nicht diese MapElements-Klasse auf, sondern die ursprünglich gewünschte (Hash)Map:<br />
<br />
<pre>import java.util.*;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class MeineEntitaet {
<b>@XmlElement(name = "paarliste")
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String, Integer> map = new HashMap<>();</b>
public Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
}</pre>
<br />
Für die Umwandlung zwischen der MapElements-Klasse und der HashMap benötigen wir einen <a href="http://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html">XmlAdapter</a>:<br />
<br />
<pre>import java.util.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MapAdapter extends XmlAdapter<MapElements, Map<String, Integer>> {
@Override
public MapElements marshal(Map<String, Integer> map) {
List<MapElement> list = new ArrayList<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
list.add(new MapElement(entry.getKey(), entry.getValue()));
}
return new MapElements(list);
}
@Override
public Map<String, Integer> unmarshal(MapElements elements) {
Map<String, Integer> map = new HashMap<>();
for (MapElement mapElement : elements.mapElements) {
map.put(mapElement.key, mapElement.value);
}
return map;
}
}
</pre>
<br />
Damit können wir schließlich das XML-Dokument erzeugen:<br />
<pre>MeineEntitaet huelle = new MeineEntitaet();
huelle.getMap().put("Antwort", 42);
JAXB.marshal(huelle, System.out);
</pre>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-87794932943084283002013-03-08T07:47:00.000+01:002013-03-08T10:07:21.053+01:00Warum ist StandardCharsets final?In vielen Anwendungen und Bibliotheken findet man eine Klasse, die Konstanten für häufig benutzte Zeichensätze enthält:<br />
<br />
<pre>import java.nio.charset.Charset;</pre>
<pre> </pre>
<pre>public class ApplicationCharsets {</pre>
<pre> public static final Charset UTF_8</pre>
<pre> = Charset.forName( "UTF-8" );</pre>
<pre> public static final Charset ISO_8859_1</pre>
<pre> = Charset.forName( "ISO-8859-1" );</pre>
<pre> public static final Charset WINDOWS_1252</pre>
<pre> = Charset.forName( "WINDOWS-1252" );</pre>
<pre>} </pre>
<pre></pre>
<br />
Seit Java 7 gibt es die Klasse <a href="http://docs.oracle.com/javase/7/docs/api/java/nio/charset/StandardCharsets.html">java.nio.charset.StandardCharsets</a>, die Konstanten für die Zeichensätze enthält, die garantiert in jeder Java-Implementierung zur Verfügung stehen – US-ASCII, ISO-8859-1 (ISO Latin 1) sowie diverse Unicode-Kodierungen.<br />
<br />
Damit man im Anwendungs-Code nicht verschiedene Klassen für die Standard- und die Anwendungs-Zeichensatzkodierungen referenzieren muss, könnte man die Anwendungs-Konstanten wie folgt formulieren ...<br />
<br />
<pre>import java.nio.charset.*;</pre>
<pre> </pre>
<pre>public class ApplicationCharsets</pre>
<pre><i> extends StandardCharsets /* geht nicht :-( */</i> {</pre>
<pre> </pre>
<pre> public static final Charset WINDOWS_1252</pre>
<pre> = Charset.forName( "WINDOWS-1252" );</pre>
<pre>} </pre>
<pre></pre>
<br />
... wenn die Klasse <code>StandardCharsets</code> nicht <code>final</code> wäre. So bleibt derzeit leider nur folgender Ausweg:<br />
<br />
<pre>public class ApplicationCharsets {</pre>
<pre> public static final Charset UTF_8</pre>
<pre> = StandardCharsets.UTF_8;</pre>
<pre> public static final Charset ISO_8859_1</pre>
<pre> = StandardCharsets.ISO_8859_1;</pre>
<pre> public static final Charset WINDOWS_1252</pre>
<pre> = Charset.forName( "WINDOWS-1252" );</pre>
<pre>} </pre>
<pre></pre>
<br />
Warum also darf von <code>StandardCharsets</code> keine Unterklasse abgeleitet werden? Einfach nur aus historischen Gründen, weil ähnliche Klassen bisher auch <code>final</code> waren? Kann man das in Java 8 ändern?Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-47737592837324190352013-02-12T14:51:00.000+01:002013-02-12T15:34:06.077+01:00Wie Apple riskante Java-Versionen mit der Xprotect-Liste deaktiviertAm 1. Februar hat Oracle mit <a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html">Java 7u13</a> und <a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk6downloads-1902814.html">Java 6u39</a> zwei Updates veröffentlicht, die diverse sicherheitskritische Lücken geschlossen haben. Anwender von Mac OS X 10.6, 10.7 und 10.8 haben dies vielleicht schon einen Tag früher erahnen können, denn am 31. Januar hat Apple alle bis dahin gültigen Java-Versionen – zumindest die Applet-Nutzung – deaktiviert.<br />
<br />
Nun sind Java-Applets zwar nicht mehr der Stand der Dinge, aber diverse Anwendungen laufen nicht ohne sie (einige VPN-Clients, elektronische Steuererklärung etc.). Und wer beruflich oder privat darauf angewiesen ist und einen Tag lang nicht weiß, warum die Web-Anwendung plötzlich nicht mehr funktioniert (gestern tat sie es ja noch!), steht erst einmal auf dem Schlauch...<br />
<br />
Unabhängig von der Frage, ob es eine kluge Entscheidung war, die Java-Versionen zu deaktivieren, bevor entsprechende Updates zur Verfügung standen – wie hat Apple die Deaktivierung der Java-Applets realisiert?<br />
<br />
Dazu gibt es im System die Datei <code>/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/XProtect.meta.plist</code> mit derzeit folgendem Inhalt (XML-Prolog etc. leicht gekürzt):
<br />
<pre><dict>
<b><key>JavaWebComponentVersionMinimum</key>
<string>1.6.0_37-b06-435</string></b>
<key>LastModification</key>
<string>Fri, 08 Feb 2013 00:54:09 GMT</string>
<key>PlugInBlacklist</key>
<dict>
<key>10</key>
<dict>
<key>com.macromedia.Flash Player.plugin</key>
<dict>
<key>MinimumPlugInBundleVersion</key>
<string>11.5.502.149</string>
</dict>
<b><key>com.oracle.java.JavaAppletPlugin</key>
<dict>
<key>MinimumPlugInBundleVersion</key>
<string>1.7.11.22</string>
</dict></b>
</dict>
</dict>
<key>Version</key>
<integer>2029</integer>
</dict>
</pre>
Die angegebenen Versionsnummern beziehen sich auf die <a href="http://www.muchsoft.com/java/macosxjavaversions.html">Java Runtime Version</a>, und die waren Ende Januar noch 1.6.0_37-b06-434 bzw. 1.7.0_11-b21. Apple hat die erforderliche Version also minimal über die verfügbaren Versionen gesetzt und damit die Applet-Plugins blockiert.<br />
<br />
Wenn man auf das Arbeiten mit einer solchen, als gefährlich eingestuften Java-Version angewiesen ist und weiß, was man tut, kann man die Xprotect-Liste mit<br />
<br />
<code>sudo pico /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/XProtect.meta.plist</code><br />
<br />
bearbeiten und den Minimum-Wert auf die installierte Java-Runtime-Version setzen. Sollten die Java-Bugfix-Updates dann längere Zeit ausbleiben, könnte man noch überlegen, das Aktualisieren der XProtect-Liste auszuschalten (in den Systemeinstellungen / Sicherheit / Weitere Optionen...), damit man die manuelle Änderung nicht jeden Tag durchführen muss. Aber Achtung, diese Option bezieht sich nicht nur auf Java-Versionen, sondern auch auf Flash und eventuelle Malware-Downloads!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeFJBhyphenhyphenBtuAGNSnmp_9xYtArPOVazqA6EY0dnkNaPLUngeJ8wZPpFvFAO-myBm6p8FUAYpimSIvsS-KwjjwxFCMCZpJAgnKBSa_Wo1aIhAwT-Oyg7m_K0xh78O73nCMrDOwGKKab8LMzI/s1600/xprotect-download.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="259" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeFJBhyphenhyphenBtuAGNSnmp_9xYtArPOVazqA6EY0dnkNaPLUngeJ8wZPpFvFAO-myBm6p8FUAYpimSIvsS-KwjjwxFCMCZpJAgnKBSa_Wo1aIhAwT-Oyg7m_K0xh78O73nCMrDOwGKKab8LMzI/s320/xprotect-download.png" width="320" /></a></div>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-31640970053076440062012-11-16T08:51:00.000+01:002012-12-19T22:00:52.101+01:00OS X 10.8, Java und X11Es gibt Java-Anwendungen, die <a href="http://de.wikipedia.org/wiki/X_Window_System">X11</a> verwenden. Bei mir ist dies z.B. ein VPN-Client-Applet, was ich allerdings erst durch folgenden Hinweis erfahren habe:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEissg7sYLhFy_0VEu8pSpUxkcQppBh2gL9diHl7jpY5n6spNtkFV5EZLQVTqC-iQ-Mw7AolaR6IedF8d5UHLVBOCDJ_ZkeQg5eUeNhVJJPHORDF_YeLZs5fPZmPkYNz4qptySuDEFtZMmU/s1600/osx108-installx11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEissg7sYLhFy_0VEu8pSpUxkcQppBh2gL9diHl7jpY5n6spNtkFV5EZLQVTqC-iQ-Mw7AolaR6IedF8d5UHLVBOCDJ_ZkeQg5eUeNhVJJPHORDF_YeLZs5fPZmPkYNz4qptySuDEFtZMmU/s320/osx108-installx11.png" width="320" /></a></div>
In OS X 10.8 wird nicht nur Java nicht mehr vorinstalliert, auch X11 ist als Komponente, die "normale" Anwender nicht benötigen, nicht mehr vorhanden ...<br />
<br />
Klickt man in dem Dialog auf "Fortfahren", wird man auf eine <a href="http://support.apple.com/kb/HT5293?viewlocale=de_DE">Apple-Support</a>-Web-Seite gebracht, auf der man die Hintergründe zum <a href="http://xquartz.macosforge.org/">XQuartz</a>-Open-Source-Projekt erfährt. Dort kann man dann auch eine aktuelle Version (mind. 2.7.2, getestet habe ich 2.7.4) herunterladen, mit der OS X "Mountain Lion" und Java glücklich werden. <br />
<br />
Die Installation von X11 löst ein weiteres Problem: Das <a href="http://glassfish.java.net/downloads/3.1.2.2-final.html">GlassFish</a>-Installations-Shellskript meldet unter OS X 10.8 folgenden Fehler:<br />
<br />
<pre>much$ ./glassfish-3.1.2.2-unix.sh
This program requires DISPLAY environment variable to be set.
Please re-run after assigning an appropriate value to DISPLAY.</pre>
<br />
Nach der Installation von X11 und einem Aus- und Einloggen ist die Umgebungsvariable "DISPLAY" wieder korrekt gesetzt und das Installations-Skript funktioniert wie gewohnt.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-25842483090574767672012-10-27T09:47:00.000+02:002012-11-21T12:52:13.885+01:00Apples Java-Update 2012-006 und die Sache mit dem Browser-Applet-PluginZeitgleich haben Apple und Oracle Anfang vergangener Woche die neuesten Java-Updates veröffentlicht (<a href="http://www.muchsoft.com/java/macosxjavaversions.html">Oracle: JDK und JRE 7u9; Apple: Java 6u37</a>). Dieser Blog-Eintrag fasst zusammen, was ich bereits im <a href="https://groups.google.com/forum/?hl=de&fromgroups=#!topic/de.comp.sys.mac.misc/a-gBvEUD5GY">Usenet</a> geschrieben habe bzw. was <a href="https://blogs.oracle.com/thejavatutorials/entry/apple_s_java_mac_os">Oracle</a> als Hinweise dazu veröffentlicht hat.<br />
<br />
Bisher war es so, dass in den Mac-Web-Browsern immer Apples Java-Applet-Plugin (also Java 6) aktiv war – es sei denn, man hatte Oracles JRE 7 installiert, dann wurde genau dieses Java 7 als Applet-Plugin verwendet.<br />
<br />
Mit dem neuen Update "<a href="http://support.apple.com/kb/DL1572">Java for OS X 2012-006</a>" für Mac OS X 10.7 und 10.8 entfernt Apple das Java-6-Applet-Plugin nun explizit aus dem System. D.h. nach der Installation des Java-Updates ist zunächst einmal <i>kein</i> Applet-Plugin mehr verfügbar. Stattdessen wird auf einer Web-Seite, die ein Applet einbindet, die Meldung "Fehlendes Plug-In" angezeigt:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeZvZVFTR_4b4l1HeixmQLBYBA-as46w54A6PxdI6_jpjDtmFhCXvn86HWQkXGjcqlLL6cmF1PLO7EJ9Y96YIrSZwPLavuXFH0CSF-vS3o4P5EkKgqCCBXUeKCesjN-Y33prKNlkrRvKA/s1600/java-2012006-fehlendesplugin.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeZvZVFTR_4b4l1HeixmQLBYBA-as46w54A6PxdI6_jpjDtmFhCXvn86HWQkXGjcqlLL6cmF1PLO7EJ9Y96YIrSZwPLavuXFH0CSF-vS3o4P5EkKgqCCBXUeKCesjN-Y33prKNlkrRvKA/s1600/java-2012006-fehlendesplugin.png" /></a></div>
<br />
Klickt man auf die Meldung, erscheint ein etwas ausführlicherer Hinweis:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBtYdeQBbTCA3WmYrR6JPyCpmkwwsNnXGXpr8-KWZgVE68nreXCQfh2zF9FNlUZdLizpMiZC461ulltYCGLxDAYqw6Abp5RxghHtJJx4enOm4ysczGYnzpLFfk4xX1mAW4ZWOej4U45Pk/s1600/java-2012006-downloadalert.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBtYdeQBbTCA3WmYrR6JPyCpmkwwsNnXGXpr8-KWZgVE68nreXCQfh2zF9FNlUZdLizpMiZC461ulltYCGLxDAYqw6Abp5RxghHtJJx4enOm4ysczGYnzpLFfk4xX1mAW4ZWOej4U45Pk/s320/java-2012006-downloadalert.png" width="320" /></a></div>
Dort bringt einen der Klick auf "Weitere Infos ..." auf <a href="http://www.java.com/en/download/mac_download.jsp">Oracles Mac-JRE-Download</a>-Seite. Apple drängt die Anwender also recht eindeutig zum Upgrade auf Java 7, was angesichts von wichtigen Security-Bugfixes, die es <a href="http://www.oracle.com/technetwork/java/eol-135779.html">für Java 6 bald nicht mehr</a> geben wird, mehr als verständlich ist.<br />
<br />
Leider kommen einige wenige Java-Applets noch nicht mit Java 7 zurecht. Manche verweigern z.B. aufgrund schlechter bzw. zu restriktiver Programmierung (fest kodierte Prüfung einer bestimmten Java-Versionsnummer o.ä.) den Dienst mit der seit über einem Jahr aktuellen Java-Version... Wer auf die Arbeit mit so einem Applet angewiesen ist, kann das <a href="http://support.apple.com/kb/HT5559">alte Plugin manuell im Terminal reaktivieren</a>. Im Wesentlichen läuft das darauf hinaus, den Symlink <code>/Library/Internet Plug-Ins/JavaAppletPlugin.plugin</code> auf das noch vorhandene, aber gut versteckte <code>/System/Library/Java/Support/Deploy.bundle/Contents/Resources/JavaPlugin2_NPAPI.plugin</code> zeigen zu lassen (das ebenfalls vorhandene <code>/System/Library/Java/Support/CoreDeploy.bundle/Contents/JavaAppletPlugin.plugin</code> ist mit dem Update 2012-006 kein vollwertiges Applet-Plugin mehr, sondern zeigt nur noch den Hinweis zum fehlenden JRE7-Plugin an, s.o.).<br />
<br />
Apples Java-Update 2012-006 löscht zudem die Java-Einstellungen in den Dienstprogrammen. Das <a href="http://javabarista.blogspot.de/2012/08/oracle-jre-7-fur-mac-os-x-107-und-os-x.html">Java Control Panel</a> in den Systemeinstellungen ist damit die einzige Möglichkeit, Laufzeitparameter für Applets festzulegen. Schwerwiegender dürfte aber sein, dass Entwickler damit die bisher unter Mac OS X sehr <i>einfache</i> grafische Möglichkeit verlieren, aus allen installierten JDKs die aktive Version für die Kommandozeile auszuwählen. Stattdessen wird von den Kommandozeilentools in <code>/usr/bin</code> das JDK mit der höchsten Nummer verwendet. Was so einfach klingt, kann durchaus zu Problemen führen, wenn Vorabversionen des JDKs installiert sind:<br />
<br />
<pre>HeartOfGold:~ much$ which java
/usr/bin/java
HeartOfGold:~ much$ java -version
openjdk version "1.8.0-b50"
OpenJDK Runtime Environment (build 1.8.0-b50-20120801)
OpenJDK 64-Bit Server VM (build 24.0-b16, mixed mode)
</pre>
<br />
Glücklicherweise beachten die Tools die Umgebungsvariable <code>JAVA_HOME</code>, die man in seinen Shell-Skripten mithilfe des – <a href="http://javabarista.blogspot.de/2009/07/javahome-oder-libraryjavahome.html">schon längere Zeit verfügbaren</a> – OSX-Tools <code>/usr/libexec/java_home</code> passend setzen kann:<br />
<br />
<pre>HeartOfGold:~ much$ export JAVA_HOME=`/usr/libexec/java_home -v 1.7`
HeartOfGold:~ much$ printenv JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
HeartOfGold:~ much$ java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
</pre>
<br />
Alternativ kann man das <code>java_home</code>-Tool dazu verwenden, ein (Java-)Kommando mit einer bestimmten Java-Version auszuführen:<br />
<br />
<pre>HeartOfGold:~ much$ /usr/libexec/java_home -v 1.6 --exec java -version
java version "1.6.0_37"
Java(TM) SE Runtime Environment (build 1.6.0_37-b06-434-11M3909)
Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01-434, mixed mode)
</pre>
Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-44398227372775052932012-08-20T09:44:00.000+02:002012-11-16T17:30:29.091+01:00Oracle JRE 7 für Mac OS X 10.7 und OS X 10.8Mit dem letzte Woche veröffentlichten <a href="http://www.oracle.com/technetwork/java/javase/downloads/jre7-downloads-1637588.html">JRE 7u6</a> hat Oracle nach dem <a href="http://javabarista.blogspot.de/2012/05/oracle-jdk-7-fur-mac-os-x.html">Mac-JDK</a> im Frühjahr nun erstmals ein reines <a href="http://de.wikipedia.org/wiki/Java-Laufzeitumgebung">JRE</a> für Mac OS X ab Version 10.7.3 (d.h. auch für OS X 10.8.x) freigegeben. Endanwender können die Software nun endlich vereinfacht von <a href="http://www.java.com/de/download/mac_download.jsp">java.com</a> herunterladen und müssen sich nicht mehr durch die Entwicklerseiten klicken. Entsprechend einfach ist die Installation – das JRE wird als DMG ausgeliefert: <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL8ZTOTGGer2ROcQ9cyuB-k7fHxcoixfWJiTfjYxQ5du8f6bOzKgFv3KPoWUzzbth4qpDclWn4VDUsYd53ZvJxhnstXwq_iyHc2Hsubsmw-XE4HGZvE5hwMr9j80Y3gK8JjWn9TwsmR_4/s1600/jre7u6-dmg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL8ZTOTGGer2ROcQ9cyuB-k7fHxcoixfWJiTfjYxQ5du8f6bOzKgFv3KPoWUzzbth4qpDclWn4VDUsYd53ZvJxhnstXwq_iyHc2Hsubsmw-XE4HGZvE5hwMr9j80Y3gK8JjWn9TwsmR_4/s320/jre7u6-dmg.png" width="320" /></a></div>
Das übliche Mac OS X-Installationsprogramm führt dann durch die Installation:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9xLexH7DK4Whyphenhyphene4AEt19wAxxw2N8TNj66iD-3VlieS4e4FPuxrMlZ2ycVBa7Vg0GtzGsiBMGKC0MicQRj8bpniBG1fngnQfB0wsTP4Y1pVUzERndrZD67AO8kQB4ditjzKlRBlNZsMmg/s1600/jre7u6-installer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9xLexH7DK4Whyphenhyphene4AEt19wAxxw2N8TNj66iD-3VlieS4e4FPuxrMlZ2ycVBa7Vg0GtzGsiBMGKC0MicQRj8bpniBG1fngnQfB0wsTP4Y1pVUzERndrZD67AO8kQB4ditjzKlRBlNZsMmg/s320/jre7u6-installer.png" width="320" /></a></div>
Im Unterschied zum <a href="http://de.wikipedia.org/wiki/Java_Development_Kit">JDK</a> werden keine Entwickler-Werkzeuge und -Dokumentation mitgeliefert. Genau genommen liefert Oracle mit dem Mac-JRE im Wesentlichen <i>nur das Applet-Plugin</i> für Web-Browser aus, d.h. Anwendungen, die sich auf das <tt>java</tt>-Kommandozeilentool verlassen, laufen damit <i>nicht</i>. Mit dem Oracle-Applet-Plugin wird Java 7 auch zur Standard-Version im Browser, unabhängig davon, ob eine Apple-Java-6-Umgebung installiert ist.<br />
<br />
Weil Oracles Java nicht mehr über die Softwareaktualisierung von Mac OS X auf den neuesten Stand gebracht wird, ist es gut, dass das JRE eine Auto-Update-Funktion mitbringt. Oracle hat dafür das Rad nicht neu erfunden, sondern greift auf das bekannte <a href="http://sparkle.andymatuschak.org/">Sparkle</a>-Framework zurück.<br />
<br />
Zusätzlich installiert das JRE in den Mac-Systemeinstellungen das von anderen Betriebssystemen bekannte "Java Control Panel":<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_UGW3yqBLJXFVW_HbFCdVNFqAv9VCCBSgFiMn9zcFpYM6FPomXqN5TAiv7KZcoiMC7-rNCIymux9epjCk_3c9Thoh_P_Fu-OSeZK8fU5OsHFdE0CdQqm8vJAE8LIWAVFog85tXUfBrPE/s1600/jre7u6-sysprefs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_UGW3yqBLJXFVW_HbFCdVNFqAv9VCCBSgFiMn9zcFpYM6FPomXqN5TAiv7KZcoiMC7-rNCIymux9epjCk_3c9Thoh_P_Fu-OSeZK8fU5OsHFdE0CdQqm8vJAE8LIWAVFog85tXUfBrPE/s320/jre7u6-sysprefs.png" width="320" /></a></div>
Leider ist das Control Panel noch nicht wirklich gut in OS X integriert, denn nach dem Klick auf das Java-Symbol öffnet sich das Panel in einem separaten Fenster:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgByivzi8JdACMdvUHDscy_LrCOP1xNaU1fAr-h7bPFxb491QB9A2Ebs_pgBU_MAC7k_J4tmeqcd2OC2wpvjj6liAxLAR2rsXBhfxxpBGTdETtSjlhxrcbU4yd8L9so21Cz5sZBXSExXeU/s1600/jre7u6-prefpanel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgByivzi8JdACMdvUHDscy_LrCOP1xNaU1fAr-h7bPFxb491QB9A2Ebs_pgBU_MAC7k_J4tmeqcd2OC2wpvjj6liAxLAR2rsXBhfxxpBGTdETtSjlhxrcbU4yd8L9so21Cz5sZBXSExXeU/s320/jre7u6-prefpanel.png" width="320" /></a></div>
Immerhin findet sich dadurch jeder, der das Java Control Panel von anderen Systemen kennt, sofort bzgl. Netzwerk-Proxies, Zertifikaten etc. zurecht.<br />
<br />
Für Entwickler ist klar, dass sie sich das JDK 7 installieren (in dem u.a. das JRE enthalten ist), wahrscheinlich zusätzlich zu Apples Java-6-Implementierung. Welches Java aber sollte sich ein normaler Endanwender auf seinem Mac-System installieren? Java 7 ist seit über einem Jahr verfügbar, Java 6 wird von Oracle nur noch bis Februar 2013 öffentlich <a href="http://www.oracle.com/technetwork/java/eol-135779.html">gepflegt</a> werden (dann wird ziemlich sicher auch Apple keine Java-6-Updates mehr anbieten) – das spricht für Java 7. Weil aber viele Mac-Java-Anwendungen durch ihren Start-Code noch an Java 6 gebunden sind, muss man zur Zeit eigentlich noch zwingend <a href="http://support.apple.com/kb/DL1572">Apples Java 6</a> installieren. Also: Am besten installiert man derzeit beide Java-Versionen – und von Java 7 am besten das <a href="http://www.oracle.com/technetwork/java/javase/downloads/">JDK 7</a>, damit alle Tools mitinstalliert werden, die eventuell zum Starten einer Anwendung benötigt werden.<br />
<br />
Wenn man bereits ein JDK 7 auf dem Rechner hat und eine neue Version davon installiert, wird die alte Version – anders als beim JRE – nicht vom Rechner gelöscht, sondern steht zusätzlich zur Verfügung (Entwickler wollen ja durchaus mal unterschiedliche Java-7-Versionen im Vergleich testen). Man sollte daher nach der Installation unbedingt in den Java-Einstellungen (im Ordner "Dienstprogramme") prüfen (und ggfs. korrigieren), welches Java-7-Update aktiv ist:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj35d-ezSroVmaGgB-AJwD6XtgvEJ9C6UhW7-7_8LLXtB9FVStB2FX7K1gdR2_XqqYQbC_ebh6TPG7qzj0IIDjOl5YZc_s5dqXZk7fjqD2vKYEXCZH4MT63KOwUO6JvrSglowgk3BrW67A/s1600/jdk7-java-einstellungen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj35d-ezSroVmaGgB-AJwD6XtgvEJ9C6UhW7-7_8LLXtB9FVStB2FX7K1gdR2_XqqYQbC_ebh6TPG7qzj0IIDjOl5YZc_s5dqXZk7fjqD2vKYEXCZH4MT63KOwUO6JvrSglowgk3BrW67A/s320/jdk7-java-einstellungen.png" width="320" /></a></div>
Und ein Hinweis zum Schluss: Wenn man nur das JRE installiert hat, taucht dies in den Java-Einstellungen <i>nicht</i> auf; dort werden nur die auf dem Rechner vorhandenen JDKs aufgelistet.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.comtag:blogger.com,1999:blog-951416721955590499.post-17679127616835374562012-08-01T13:57:00.000+02:002012-11-16T09:00:25.513+01:00OS X 10.8 "Mountain Lion", Java und der GatekeeperVor einer Woche wurde (Mac) OS X 10.8 "Mountain Lion" veröffentlicht. Wie schon bei der Vorgängerversion liefert Apple keine Java-Laufzeitumgebung mehr mit dem <a href="http://de.wikipedia.org/wiki/Puma">Berglöwen/Puma</a> aus – bei einem Update von 10.7.x wird eine bestehende Java 6-Installation sogar entfernt und muss erneut installiert werden:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju1WXpd83kxUJEBjj2at4pUC0tycHLrpFQZ7dsq-M-b4Dy_E8LmQ7vdd3zhStAV1fbfPsd3UdUCN2aINYrjzRixb029GzwL48gM97wQaWvbpMHJWkhtOeexTCXfbDAQV9U4NtaMi27PQs/s1600/osx108-javaruntime.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju1WXpd83kxUJEBjj2at4pUC0tycHLrpFQZ7dsq-M-b4Dy_E8LmQ7vdd3zhStAV1fbfPsd3UdUCN2aINYrjzRixb029GzwL48gM97wQaWvbpMHJWkhtOeexTCXfbDAQV9U4NtaMi27PQs/s320/osx108-javaruntime.png" width="320" /></a></div>
Der Vergleich mit der <a href="http://javabarista.blogspot.de/2011/11/java-fur-mac-os-x-107-lion.html">Java-Installation unter 10.7</a> zeigt einen kleinen, aber wichtigen Unterschied: Es wird nun explizit eine Java SE <b>6</b>-Runtime erwähnt. D.h. selbst wenn man Java 7 installiert hat, muss man zum Ausführen eines Java Application Bundles zusätzlich das alte Java 6 herunterladen. Dies ist das derzeit aktuelle <a href="http://www.muchsoft.com/java/macosxjavaversions.html">Apple-Java-Update</a>, das ursprünglich für Mac OS X 10.7 veröffentlicht wurde:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDRfW64wX7d2nSI73LxMudXBUrJUqwG4re3oiDF5vvW4cpN44tyE1dVdVB-hT-tAYW9QLUkteWYJocEtI9RaR6I9gwiSeM4BteXACEco_0QraD0I-Oq8ElesXXQB_8qHpqTYhj4gaUlek/s1600/osx108-java-update.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="111" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDRfW64wX7d2nSI73LxMudXBUrJUqwG4re3oiDF5vvW4cpN44tyE1dVdVB-hT-tAYW9QLUkteWYJocEtI9RaR6I9gwiSeM4BteXACEco_0QraD0I-Oq8ElesXXQB_8qHpqTYhj4gaUlek/s320/osx108-java-update.png" width="320" /></a></div>
Ein Problem für Java-Anwendungen entsteht durch den <a href="https://developer.apple.com/resources/developer-id/">Gatekeeper</a>, der ab OS X 10.8 dafür sorgt, dass normalerweise nur Anwendungen aus dem <a href="http://www.apple.com/de/osx/apps/app-store.html">App Store</a> bzw. von zertifizierten Entwicklern gestartet werden können. Der <a href="http://www.snailshell.de/blog/archives/2006/01/entry_37.html">Java Application Stub</a>, mit dem die meisten Mac OS X-Java Application Bundles gestartet werden, enthält aber kein oder kein passendes Zertifikat (je nachdem, wie alt der Stub ist), was zu einer ziemlich unpassenden Fehlermeldung führt:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUSySoadCpyT-4eMU5JVB9iPUZNZejWEaFxiOwSXxPYf7oOXqhKi9emy5yx3ZtraqpUVGlCU0cZa7CAk7BKdxFooqEnP_whX9560Z-tGgA0HNIL0zi4zgQb0SZdvzGn9v3fBBtbrl_nok/s1600/osx-damaged.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUSySoadCpyT-4eMU5JVB9iPUZNZejWEaFxiOwSXxPYf7oOXqhKi9emy5yx3ZtraqpUVGlCU0cZa7CAk7BKdxFooqEnP_whX9560Z-tGgA0HNIL0zi4zgQb0SZdvzGn9v3fBBtbrl_nok/s320/osx-damaged.png" width="320" /></a></div>
Mit ein bisschen Trickserei (Austausch des Application Stubs bzw. Entfernen der Prüfsumme / des Zertifikats) kann man die Fehlermeldung zwar etwas verständlicher bekommen ...<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0lNxyT3Nkvx9kkhNdd4vLlc0kS3cTIU0hF-8mZNwEe2EIbgiS2qbGBPvntQcH8LS2aK8GqPJP6mjiHhHm8q4Cvg3IevUOPmixtwHwn4bq6m2fpHGC1uvZHw2frGg9XfNOeCd_orF8HpA/s1600/osx108-unverified.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0lNxyT3Nkvx9kkhNdd4vLlc0kS3cTIU0hF-8mZNwEe2EIbgiS2qbGBPvntQcH8LS2aK8GqPJP6mjiHhHm8q4Cvg3IevUOPmixtwHwn4bq6m2fpHGC1uvZHw2frGg9XfNOeCd_orF8HpA/s320/osx108-unverified.png" width="320" /></a></div>
... aber das ist nicht im Sinne der Erfinder / nicht ganz einfach / zu instabil für künftige OS X-Versionen. Kurzum: Tun Sie's nicht. Eine Möglichkeit für den Anwender, Java-Anwendungen trotzdem noch starten zu können, besteht darin, den Gatekeeper in den Systemeinstellungen zu deaktivieren (auf "Keine Einschränkungen" stellen):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhamtQneQ-8iq8L77dhrqnbnOUpj40C5v1iu0ZVfYZkepG_LXJNici08ZBJEYlDoXmzeNjPMOzUKViKTMfRbdTqxIglRrKBc4mesexSIoaYi_vVr07URN-zpdRTJPgruEk8VKJfIly3lOQ/s1600/osx108-sysprefs-security.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="259" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhamtQneQ-8iq8L77dhrqnbnOUpj40C5v1iu0ZVfYZkepG_LXJNici08ZBJEYlDoXmzeNjPMOzUKViKTMfRbdTqxIglRrKBc4mesexSIoaYi_vVr07URN-zpdRTJPgruEk8VKJfIly3lOQ/s320/osx108-sysprefs-security.png" width="320" /></a></div>
<i>Das</i> ist nun allerdings auch nicht im Sinne der zusätzlichen Sicherheit, die OS X 10.8 bringen soll, weil dann wirklich alle Anwendungen (auch die nativen Mac-Anwendungen) ungeprüft ausgeführt werden. Besser ist es, gezielt nur die gewünschte Java-Anwendung ohne Gatekeeper-Prüfung aufzurufen, was man über den "Öffnen"-Menüpunkt im Kontextmenü erreicht:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5C3l9zU3ptjD-NRpfH0d37X7iLS0eKSmzTt_MReYfoNfq5VWkQp_fh0BcoVxSHe_QOc0qf8niKMLLEHbXT_rZSjRJBNcFblolTRtSsW3GdrgOXqybLj6y6ce3e4naejgSZKg-pvoDHV0/s1600/osx108-contextmenu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="129" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5C3l9zU3ptjD-NRpfH0d37X7iLS0eKSmzTt_MReYfoNfq5VWkQp_fh0BcoVxSHe_QOc0qf8niKMLLEHbXT_rZSjRJBNcFblolTRtSsW3GdrgOXqybLj6y6ce3e4naejgSZKg-pvoDHV0/s320/osx108-contextmenu.png" width="320" /></a></div>
<br />
OS X verbietet das Starten nun nicht mehr pauschal, sondern fragt nach, ob man die vermeintlich unsichere Anwendung wirklich ausführen möchte:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV37BXXpzmCp36P6DvSDWFunh0sB5i5BSqsbZvVur8h1VU2yBviYvLQhFQN9x_AO4rUrcfxBxh-qZGlVS-bL9llrz8a5VhLfc-qA_h5UXaIrzyh6-X9dlX6QCPOXFHLu0qkJGXIRxHA6g/s1600/osx108-open.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV37BXXpzmCp36P6DvSDWFunh0sB5i5BSqsbZvVur8h1VU2yBviYvLQhFQN9x_AO4rUrcfxBxh-qZGlVS-bL9llrz8a5VhLfc-qA_h5UXaIrzyh6-X9dlX6QCPOXFHLu0qkJGXIRxHA6g/s320/osx108-open.png" width="320" /></a></div>
Leider klappt das – je nach verwendetem Application Stub – nicht mit allen Java-Anwendungen. <a href="http://java.decompiler.free.fr/?q=jdgui">JD-GUI</a> kann man so aufrufen, <a href="http://argouml.tigris.org/">ArgoUML</a> und <a href="http://www.tvbrowser.org/de/">TV-Browser</a> beispielsweise nicht... Für letztere muss der Gatekeeper wie oben beschrieben deaktiviert werden. Kleiner Trost: Wenn man einmal eine Java-Anwendung gestartet hat, merkt sich der OS X dies, man kann den Gatekeeper anschließend also wieder aktivieren.<br />
<br />
Am besten wäre es allerdings, wenn die Entwickler von Java-Anwendungen den <a href="https://developer.apple.com/resources/developer-id/">Gatekeeper</a> aktiv unterstützten und ihre Applikation <a href="http://developer.apple.com/library/mac/#documentation/Security/Conceptual/CodeSigningGuide/Introduction/Introduction.html">signierten</a>. Bei <a href="https://blogs.oracle.com/talkingjavadeployment/entry/java_applications_and_gatekeeper">Oracle</a> gibt es dazu kompakte Informationen. Und vor kurzem hat sich jemand die Mühe gemacht, ausführlich zu beschreiben, wie man seine <a href="http://intransitione.com/blog/take-java-to-app-store/">Java-Programme im Mac App Store veröffentlichen</a> kann.Thomas Muchhttp://www.blogger.com/profile/15594237205835321171noreply@blogger.com