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 Java for OS X 2013-005 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).
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 JDK 7u45. Java-7-Versionen bis einschließlich 7u25 wurden/werden von Apple aus Sicherheitsgründen deaktiviert.
Donnerstag, 24. Oktober 2013
Freitag, 11. Oktober 2013
Java-Batch-Anwendungen
Mal ein bisschen Werbung in eigener Sache. Im Frühjahr wurde der neue Standard "Batch Applications for the Java Platform" (JSR-352) – 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.
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.
Nachdem ich bereits auf dem Herbstcampus in Nürnberg zu diesem Thema gesprochen habe, halte ich nun einen weiteren Vortrag im Rahmen der Reihe "GFU Semicolon" in Köln. Nach einem Überblick über die Batch-Architektur schauen wir uns die Grundlagen der Batch-Programmierung in Java SE mit der Referenzimplementation JBatch an. Die Integration in Java EE 7 zeige ich am Beispiel von GlassFish 4.
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 Anmeldung ist aber erforderlich.
[Update 24.10.]
Die Folien sind nun verfügbar – und ein Foto vom Vortrag:
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.
Nachdem ich bereits auf dem Herbstcampus in Nürnberg zu diesem Thema gesprochen habe, halte ich nun einen weiteren Vortrag im Rahmen der Reihe "GFU Semicolon" in Köln. Nach einem Überblick über die Batch-Architektur schauen wir uns die Grundlagen der Batch-Programmierung in Java SE mit der Referenzimplementation JBatch an. Die Integration in Java EE 7 zeige ich am Beispiel von GlassFish 4.
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 Anmeldung ist aber erforderlich.
[Update 24.10.]
Die Folien sind nun verfügbar – und ein Foto vom Vortrag:
Montag, 22. April 2013
REST-Web-Services mit JAX-RS portabel aktivieren
... und dennoch implementationsspezifisch konfigurieren? Eins nach dem anderen. Seit Java EE 6 kann man JAX-RS in einem Application-Server portabel dadurch aktivieren, dass man eine Klasse von
D.h. dass man nun REST-Web-Services anbieten kann, ohne sich um die konkrete Implementierung – beispielsweise Jersey als Referenz-Implementation – kümmern zu müssen. Gut.
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
javax.ws.rs.core.Application ableitet und mit @ApplicationPath annotiert:package com.muchsoft.rest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/rest")
public class ApplicationWithRESTWebServices
extends Application {
}
D.h. dass man nun REST-Web-Services anbieten kann, ohne sich um die konkrete Implementierung – beispielsweise Jersey als Referenz-Implementation – kümmern zu müssen. Gut.
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
web.xml zurück und mappt dort ein Servlet mit den entsprechenden Init-Parametern. Allerdings muss das nicht eine Jersey-Klasse (mit entsprechendem servlet-mapping) sein, sondern es reicht, die eigene Application-Unterklasse als Servlet einzutragen (ohne servlet-mapping-Element):
<servlet>
<servlet-name>
com.muchsoft.rest.ApplicationWithRESTWebServices
</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>
Donnerstag, 21. März 2013
REST-Web-Services mit BASIC-Auth und HTTPS in GlassFish
Nehmen wir mal einen einfachen REST-Web-Service wie den folgenden, der alle Bestellungen des Benutzers liefern soll:
Mit beispielsweise Wget könnte dieser Service dann wie folgt aufgerufen werden, wenn der Applikationspfad auf "rest" gemappt ist:
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.
Am Beispiel von GlassFish 3.1.2 schauen wir uns das an. Wir erlauben dem GlassFish-Admin-User, den Web-Service aufzurufen. Dazu sichern wir am Ende des Deployment-Deskriptors
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.
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
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
Fertig. Der Aufruf des Web-Services erfolgt über Wget nun mit
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.
@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();
}
}
}
Mit beispielsweise Wget könnte dieser Service dann wie folgt aufgerufen werden, wenn der Applikationspfad auf "rest" gemappt ist:
wget http://localhost:8080/meinewebapp/rest/bestellungenFast 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.
Am Beispiel von GlassFish 3.1.2 schauen wir uns das an. Wir erlauben dem GlassFish-Admin-User, den Web-Service aufzurufen. Dazu sichern wir am Ende des Deployment-Deskriptors
web.xml alle Ressourcen unterhalb /rest 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:<?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>
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.
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
web.xml referenzieren.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
glassfish-web.xml (früher sun-web.xml):<?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>
Fertig. Der Aufruf des Web-Services erfolgt über Wget nun mit
wget https://localhost:8081/meinewebapp/rest/bestellungen
--user ... --password ... --no-check-certificate
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.
Donnerstag, 14. März 2013
JAXB und HashMaps, Teil 2
In Teil 1 zu JAXB und HashMaps 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:
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:
In der JAXB-Entität taucht nun aber nicht diese MapElements-Klasse auf, sondern die ursprünglich gewünschte (Hash)Map:
Für die Umwandlung zwischen der MapElements-Klasse und der HashMap benötigen wir einen XmlAdapter:
Damit können wir schließlich das XML-Dokument erzeugen:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<meineEntitaet>
<paarliste>
<paar>
<schluessel>Antwort</schluessel>
<wert>42</wert>
</paar>
</paarliste>
</meineEntitaet>
Dazu benötigen wir als Erstes für einen einzelnen Eintrag der Map (also für jedes Schlüssel-Wert-Paar) folgende Klasse:import javax.xml.bind.annotation.XmlElement;
class MapElement {
@XmlElement(name = "schluessel")
String key;
@XmlElement(name = "wert")
Integer value;
MapElement() {
}
MapElement(String key, Integer value) {
this.key = key;
this.value = value;
}
}
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:
class MapElements {
@XmlElement(name = "paar")
List<MapElement> mapElements;
MapElements() {
}
MapElements(List<MapElement> mapElements) {
this.mapElements = mapElements;
}
}
In der JAXB-Entität taucht nun aber nicht diese MapElements-Klasse auf, sondern die ursprünglich gewünschte (Hash)Map:
import java.util.*;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class MeineEntitaet {
@XmlElement(name = "paarliste")
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String, Integer> map = new HashMap<>();
public Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
}
Für die Umwandlung zwischen der MapElements-Klasse und der HashMap benötigen wir einen XmlAdapter:
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;
}
}
Damit können wir schließlich das XML-Dokument erzeugen:
MeineEntitaet huelle = new MeineEntitaet();
huelle.getMap().put("Antwort", 42);
JAXB.marshal(huelle, System.out);
Freitag, 8. März 2013
Warum ist StandardCharsets final?
In vielen Anwendungen und Bibliotheken findet man eine Klasse, die Konstanten für häufig benutzte Zeichensätze enthält:
Seit Java 7 gibt es die Klasse java.nio.charset.StandardCharsets, 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.
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 ...
... wenn die Klasse
Warum also darf von
import java.nio.charset.Charset;
public class ApplicationCharsets {
public static final Charset UTF_8
= Charset.forName( "UTF-8" );
public static final Charset ISO_8859_1
= Charset.forName( "ISO-8859-1" );
public static final Charset WINDOWS_1252
= Charset.forName( "WINDOWS-1252" );
}
Seit Java 7 gibt es die Klasse java.nio.charset.StandardCharsets, 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.
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 ...
import java.nio.charset.*;
public class ApplicationCharsets
extends StandardCharsets /* geht nicht :-( */ {
public static final Charset WINDOWS_1252
= Charset.forName( "WINDOWS-1252" );
}
... wenn die Klasse
StandardCharsets nicht final wäre. So bleibt derzeit leider nur folgender Ausweg:public class ApplicationCharsets {
public static final Charset UTF_8
= StandardCharsets.UTF_8;
public static final Charset ISO_8859_1
= StandardCharsets.ISO_8859_1;
public static final Charset WINDOWS_1252
= Charset.forName( "WINDOWS-1252" );
}
Warum also darf von
StandardCharsets keine Unterklasse abgeleitet werden? Einfach nur aus historischen Gründen, weil ähnliche Klassen bisher auch final waren? Kann man das in Java 8 ändern?
Dienstag, 12. Februar 2013
Wie Apple riskante Java-Versionen mit der Xprotect-Liste deaktiviert
Am 1. Februar hat Oracle mit Java 7u13 und Java 6u39 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.
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...
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?
Dazu gibt es im System die Datei
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
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!
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...
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?
Dazu gibt es im System die Datei
/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/XProtect.meta.plist mit derzeit folgendem Inhalt (XML-Prolog etc. leicht gekürzt):
<dict>
<key>JavaWebComponentVersionMinimum</key>
<string>1.6.0_37-b06-435</string>
<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>
<key>com.oracle.java.JavaAppletPlugin</key>
<dict>
<key>MinimumPlugInBundleVersion</key>
<string>1.7.11.22</string>
</dict>
</dict>
</dict>
<key>Version</key>
<integer>2029</integer>
</dict>
Die angegebenen Versionsnummern beziehen sich auf die Java Runtime Version, 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.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
sudo pico /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/XProtect.meta.plistbearbeiten 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!
Abonnieren
Posts (Atom)



