Integration von Java in C++ Anwendungen Matthias Müller – 19.11.2015 DOAG Jahreskonferenz 2015 Nürnberg Die Datenbank-Spezialisten. Zahlen und Fakten Robotron Datenbank-Software GmbH Gründungsjahr 1990 Geschäftsform GmbH (9 Gesellschafter) Mitarbeiterzahl 365 (Stand 10/2015) Stammkapital 2,4 Mio. EUR Umsatz 2014 31,2 Mio. EUR Umsatz 2015 32,8 Mio. EUR Hauptsitz Oracle-Partner ISO 9001 zertifiziert Die Datenbank-Spezialisten. Schulungs- und Kongresszentrum Das Leistungsangebot von Robotron methodische und technologische Kompetenzen umfangreiches Fachwissen in den branchentypischen Prozessen Die Datenbank-Spezialisten. Agenda Warum Java in C++? Java Native Interface – Konzepte und Grundlagen Integration von Java in C++ an einer Beispielanwendung Probleme und Risiken Best Practices Fazit Die Datenbank-Spezialisten. Warum? Die Datenbank-Spezialisten. Warum Java in C++-Anwendungen? Erweiterung bestehender Anwendungen um neue Funktionalitäten „weiche“ Migration von C++-Anwendungen nach Java Integration mit neueren Java-Anwendungen Kostenersparnisse Umgehung von Entwicklungsengpässen Die Datenbank-Spezialisten. Konzepte und Grundlagen Die Datenbank-Spezialisten. Java Native Interface - Einordnung Standardisierte und herstellerunabhängige Schnittstelle der JVM Verbindet C/C++ und Java zwei Richtungen Die Nutzung von JNI erfolgt über eine dynamische Bibliothek Plattformunabhängig und Binärkompatibel Überwindet die Mängel anderer Ansätze (Netscape, Microsoft) Die Datenbank-Spezialisten. Java Native Interface – Zentrale Konzepte Invocation und Interface API Interface Pointer Datentypen und Objekt-Referenzen Objekt-Zugriff Fehlerbehandlung Die Datenbank-Spezialisten. Java Native Interface – Das API Java Invocation API – Einbindung und Verwaltung einer JVM in nativen Code Java Native Interface API – Verwaltung und Zugriff auf Objekte der JVM Die Datenbank-Spezialisten. Java Native Interface – Interface Pointer Die Datenbank-Spezialisten. Java Native Interface – Datentypen Einfache Datentypen – Direkte Abbildung auf ihre korrespondierenden C/C++ Datentypen – call by value-Semantik Komplexe Datentypen und Klassen – Verwendung von Referenzen Die Datenbank-Spezialisten. Java Native Interface – Referenzen Globale Referenzen – Threadübergreifend gültig Schwache globale Referenzen (weak global references) – Werden vom GC verwaltet Lokale Referenzen – Nur im erzeugenden Thread gültig – Konvertierung in globale Referenzen Die Datenbank-Spezialisten. Java Native Interface – Objektzugriff Erfolgt indirekt über eine Menge an Zugriffsfunktionen – Ermittlung des Java-Class Objekts der Klasse – Beschaffung der Methoden-Id – Aufruf der Methode Ausnahme sind Arrays Die Datenbank-Spezialisten. Java Native Interface – Fehlerbehandlung JNI führt in vielen Fällen keine Fehlerprüfungen aus Ausnahmen führen nicht direkt zu einer Unterbrechung Eine Prüfung auf Ausnahmen erfolgt über – Den Rückgabewert einer JNI-Funktion – Dedizierte JNI-Funktionen (ExceptionOccured() oder ExceptionCheck()) Ausnahmen müssen explizit entfernt werden (ExceptionClear()) Bei pending exceptions ist die Ausführung der meisten JNI-Funktionen nicht sicher Die Datenbank-Spezialisten. Integration in C++ Die Datenbank-Spezialisten. Integration in C++ am Beispiel Generierung eines PDF-Formulars mittels XSL-FO – Überführung eines XML-Formulars in eine PDF-Datei – Java-Klassen implementieren den Transformationsprozess – Ausführung der Transformation durch Apache FOP – Signatur: int XML2PDF::transform(String xmlFile) throw Exception() Voraussetzungen – Java SE Development Kit – C++-Compiler Java 8 HotSpot VM Die Datenbank-Spezialisten. Beteiligte Komponenten jvm.dll C++ Quellen CC .exe jni.h jvm.lib Die Datenbank-Spezialisten. Java Binaries Einbindung der Java-VM // JNI-Repräsentation der JVM jni::JavaVM * pJavaVM; // JNI-Struktur für Java-Zugriff in VM für den "Main Thread" jni::JNIEnv * pJNIEnv; // VM Optionen festlegen jni::JavaVMInitArgs vm_args; jni::JavaVMOption options[nMaxVMOptions]; options[0].optionString = const_cast<char *>(CLASS_PATH); vm_args.nOptions = 1; ... vm_args.options = options; // VM Laden jni::jint res = jni::JNI_CreateJavaVM(&pJavaVM, (void**)&pJNIEnv, &vm_args); Die Datenbank-Spezialisten. Erzeugung des Formular-Generators // Klassen-Objekt laden jni::jclass clsXPdf = pJNIEnv->FindClass("jni/demo/XML2PDF"); if (clsXPdf == 0) throw std::exception("JNI Fehler"); // Konstruktor beschaffen ... jni::jmethodID jmiXPdf = pJNIEnv->GetMethodID(clsXPdf, "<init>", "()V"); if (jmiXPdf == 0) throw std::exception("JNI Fehler"); // ... und Instanz von XML2PDF erzeugen jni::jobject jXPdf = pJNIEnv->NewObject(clsXPdf, jmiXPdf); if (jXPdf == 0) throw std::exception("JNI Fehler"); Die Datenbank-Spezialisten. Aufruf der Transformationsmethode // JNI Methoden-ID für Transformationsmethode beschaffen jni::jmethodID jmiTransform = pJNIEnv->GetMethodID(clsXPdf, "transform", "(Ljava/lang/String;)V"); if (jmiTransform == 0) throw std::exception("JNI Fehler"); // Parameter-String erzeugen jni::jstring jstrParam = pJNIEnv->NewStringUTF(XML_FILE); if (jstrParam == 0) throw std::exception("JNI Fehler"); // Transformation starten pJNIEnv->CallVoidMethod(jXPdf, jmiTransform, jstrParam); // Test auf pending Exceptions if (pJNIEnv->ExceptionCheck() == JNI_TRUE) throw std::exception("JNI Fehler"); Die Datenbank-Spezialisten. Fehlerbehandlung catch (const std::exception & e) { jni::jthrowable jexc = pJNIEnv->ExceptionOccurred(); if (jexc) { // Stacktrace auf stderr pJNIEnv->ExceptionDescribe(); // Alle pending Exceptions aufräumen pJNIEnv->ExceptionClear(); std::cerr << e.what() << ": " << What(pJNIEnv, jexc) << std::endl; // Lokale Referenz zu Exception freigeben pJNIEnv->DeleteLocalRef(jexc); } } Die Datenbank-Spezialisten. Ressourcenfreigabe // JNI Objektreferenzen freigeben if (clsXPdf != NULL) pJNIEnv->DeleteLocalRef(clsXPdf); if (jXPdf != NULL) pJNIEnv->DeleteLocalRef(jXPdf); if (jstrParam != NULL) pJNIEnv->DeleteLocalRef(jstrParam); Die Datenbank-Spezialisten. Probleme und Risiken Die Datenbank-Spezialisten. Speicherbedarf der JVM Zuteilung ausreichenden Speichers bei Start der VM Keine dynamische Reallokation möglich Die Java VM kann in einem Prozess nicht neu gestartet werden Konsequenz ist ein Neustart der Anwendung, um die Speicherzuteilung zu ändern Die Datenbank-Spezialisten. Fehlerbehandlung JNI prüft nicht auf Korrektheit der Argumente Unbehandelte (pending) Ausnahmen führen zu subtilen Fehlern im Programmablauf Fehler können zum Absturz der VM führen und damit einen Neustart der Anwendung notwendig machen Fehleranalyse ist teilweise aufwendiger Die Datenbank-Spezialisten. Internationalisierung Native C/C++ Anwendungen sind oft an den Zeichensatz des Hosts gebunden Java verwendet intern mit UTF-16 Unicode für die Zeichenkodierung JNI bietet String-Zugriffsfunktionen für UTF-16 und modifiziertem UTF-8 – UTF-16 Strings sind nicht NULL-Terminiert – Natives UTF-8 kann nicht as is an Java übergeben werden Für andere Kodierungen/Zeichensätze müssen eigene Funktionen bereitgestellt werden Die Datenbank-Spezialisten. Eingeschränkte Compileranalyse Verletzung der Sichtbarkeitsdefinitionen einer Klasse – Der Aufruf einer Methode mit eingeschränkter Sichtbarkeit führt zu einem Laufzeitfehler (IllegalAccessException) Statische Typsicherheit kann nicht gewährleistet werden – Verletzungen führen zu Laufzeitfehlern JNI verwendet variable Parameterlisten in Methodenaufrufen – Inkorrekte Behandlung der Argumentlisten führt teilweise zu schwer analysierbaren LaufzeitProblemen Die Datenbank-Spezialisten. Weitere Stolperfallen Verletzung der Thread-Korrektheit – JNI Environment zum Aufruf von JNI-Funktionen wird im falschen Thread verwendet Fehlerhafte Ressourcenfreigabe – Referenzen werden nicht wieder freigegeben – Benutzung ungültiger Referenzen Performance-Probleme bei komplexen Datentypen (Arrays) in der Schnittstelle Die Datenbank-Spezialisten. Best Practices Die Datenbank-Spezialisten. Korrektheit und Robustheit I Fehlerprüfungen sind langweilig – aber WICHTIG Kapselung der JNI-Strukturen und Funktionen – Verwendung von Wrapper-Klassen – Minimale Schnittstelle zu JNI-Funktionen – Zentralisierung der Fehlerbehandlung – Ressourcenfreigabe durch Dekonstruktor Die Datenbank-Spezialisten. Korrektheit und Robustheit II Erstellung von Peer-Klassen für Java-Objekte – Transparente Integration in C++-Code – Codereduzierung – Wartbarkeit Mapping der Java-Ausnahmen in C++-Ausnahmen Vernünftige Limitierung des Heap Space der Java VM Die Datenbank-Spezialisten. Performance Caching von häufig benutzten JNI-Strukturen – Attribut- und Methoden-Id – Objekt-Referenzen – JNI Environment JNIEnv des Threads Sorgfältiges Design der Schnittstelle C++/Java – Vermeidung iterativer Zugriffe auf Java-Arrays – Häufige Kontextwechsel zwischen C++ und Java vermeiden Rückgabewerte testen anstatt auf Ausnahmen Die Datenbank-Spezialisten. Fehleranalyse Verwendung der Java-Option –Xcheck:jni Schreiben von Log-Dateien in Java-Komponenten Generierung von Debug-Information durch javac Die Option –verbose:jni protokolliert die JNI-Aufrufe Die Datenbank-Spezialisten. Fazit Die Datenbank-Spezialisten. Fazit Zuverlässige, plattformunabhängige und portable Schnittstelle Mit einem kleinen Satz an Basisklassen gestaltet sich auch die Programmierung einfach JNI-basierte Lösungen im Projekt auf über 3000 PC bisher ohne Probleme Java Versionswechsel der Clients ohne neues Rollout der Anwendung Die Datenbank-Spezialisten. Matthias Müller Senior Systemberater Robotron Datenbank-Software GmbH Stuttgarter Str. 29 | D-01189 Dresden Tel. +49 (0) 351-25859 - 0 [email protected] www.robotron.de Fragen? Die Datenbank-Spezialisten.