Connect with us

Entwicklung & Code

Software Testing: Modelle als gemeinsame Sprache im Test


In dieser Episode spricht Richard Seidl mit Oliver Schuhmacher und Marvin Jakob über modellbasiertes Testen als Brücke zwischen Fachbereich, Entwicklung und Test. Ausgangspunkt ist ein Projekt bei Lufthansa CityLine: ein neues Crew-Management unter hohem Zeitdruck, mit klarer Abnahme.

Modelle dienen als gemeinsame Sprache, starten auf hoher Flughöhe und werden über Interviews, Verfeinerung und regelmäßige Reviews konkret. Tools generieren aus den Modellen Testfälle, machen Abdeckung sowie Impact in Jira sichtbar und halten Tests bei Änderungen aktuell.

Bei diesem Podcast dreht sich alles um Softwarequalität: Ob Testautomatisierung, Qualität in agilen Projekten, Testdaten oder Testteams – Richard Seidl und seine Gäste schauen sich Dinge an, die mehr Qualität in die Softwareentwicklung bringen.

Die aktuelle Ausgabe ist auch auf Richard Seidls Blog verfügbar: „Modelle als gemeinsame Sprache im Test – Oliver Schuhmacher, Marvin Jakob“ und steht auf YouTube bereit.


(mdo)



Source link

Entwicklung & Code

Wasm 3 bringt 64-Bit-Adressraum und Garbage Collection


Die Wasm W3C Community Group und die Wasm Working Group haben die Fertigstellung von WebAssembly (Wasm) 3 als neuen Live-Standard bekannt gegeben. Die Entwicklerinnen und Entwickler stufen Version 3 sowohl vom Umfang als auch vom Zeitaufwand her als großes Update ein. An den neuen Funktionen haben sie teilweise bis zu acht Jahre lang gearbeitet.

Zu den größten Neuerungen von WebAssembly 3 zählen unter anderem ein 64-Bit-Adressraum für Speicher und Tabellen (für Anwendungen bis 16 Exabyte), die Deklarierung mehrerer Speicherobjekte durch ein einzelnes Modul, eine native Ausnahmebehandlung, eine automatisch verwaltete Garbage Collection und die Tails Calls. Diese beenden eine aktuelle Funktion vollständig, um so die Belegung von zusätzlichem Stack-Speicherplatz zu vermeiden. Detaillierte Informationen zu den Neuerungen von Wasm 3 finden sich auf der WebAssembly-Webseite.

Mit Version 2 bekam das portable Binärformat für Stack-basierte virtuelle Maschinen vor drei Jahren das letzte große Update. Auch damals hielten viele neue Funktionen Einzug in den W3C-Standard, darunter Vektoranweisungen, Massenspeicheroperationen, mehrere Rückgabewerte und einfache Referenztypen. Wasm erlaubt es, beliebige Programmiersprachen abseits von JavaScript im Browser zu verwenden, aktuell insbesondere C/C++- und Rust-Code, aber die neuen GC-Funktionen in Wasm 3 öffnen es auch für Java oder Kotlin.


(who)



Source link

Weiterlesen

Entwicklung & Code

Core Java: Java Agents und Instrumentation API im praktischen Einsatz


Die Java Instrumentation API ist ein Teil des java.lang.instrument-Pakets und ermöglicht es, den Bytecode von Klassen zur Laufzeit zu verändern oder zu analysieren. Sie ist insbesondere für das Entwickeln von Profilern, Agenten, Monitoring-Tools oder auch dynamischen Sicherheitsmechanismen gedacht, die tief in das Verhalten einer Java-Anwendung eingreifen müssen, ohne dabei den Quellcode selbst zu verändern.


Sven Ruppert

Sven Ruppert

Seit 1996 programmiert Sven Java in Industrieprojekten und seit über 15 Jahren weltweit in Branchen wie Automobil, Raumfahrt, Versicherungen, Banken, UN und Weltbank. Seit über 10 Jahren ist er von Amerika bis nach Neuseeland als Speaker auf Konferenzen und Community Events, arbeitete als Developer Advocate für JFrog und Vaadin und schreibt regelmäßig Beiträge für IT-Zeitschriften und Technologieportale.
Neben seinem Hauptthema Core Java beschäftigt er sich mit TDD und Secure Coding Practices.

Im Zentrum dieser API steht das Konzept der Java Agents. Ein Agent ist eine spezielle Komponente, die beim Start der JVM oder auch zur Laufzeit über die Attach API geladen werden kann. Der Agent nutzt dann ein Instrumentation-Interface, das vom JVM-Launcher übergeben wird. Über dieses Interface erhält der Agent die Möglichkeit, bereits geladene Klassen zu inspizieren, neue Klassen zu transformieren oder das Verhalten der JVM zu beeinflussen – etwa durch das Einfügen von Hooks, das Messen von Aufrufzeiten oder das Injizieren von Sicherheitsprüfungen.

Die grundlegende Aufgabe besteht also darin, während des Classloading-Prozesses den Bytecode zu transformieren, ohne dabei die Semantik der Sprache Java zu verletzen. Technisch ermöglichen dies die ClassFileTransformer. Entwickler müssen sie zunächst registrieren, dann können die ClassFileTransformer bei jedem Ladevorgang einer Klasse den Bytecode manipulieren, bevor die JVM diesen interpretiert oder kompiliert.

Ein typischer Einstiegspunkt für einen statischen Agenten ist eine Klasse mit einer premain(String agentArgs, Instrumentation inst)-Methode. Bei dynamischen Agenten kommt stattdessen die agentmain(String agentArgs, Instrumentation inst)-Methode zum Einsatz. Beide Varianten erlauben es, Transformer zu registrieren und damit das Verhalten der Anwendung unterhalb der Sprachebene zu beeinflussen.

Das Verwenden der Instrumentation API erfordert ein tiefes Verständnis der JVM-Architektur und des Java-Klassenlademechanismus. Gleichzeitig eröffnet sie aber auch einzigartige Möglichkeiten zur Laufzeitanalyse und -modifikation, die mit den üblichen Mitteln in Java nicht erreichbar wären – und das in einer Art und Weise, die sich elegant in bestehende Anwendungen integrieren lässt, ohne diese neu zu kompilieren.

Ein zentraler Vorteil der Java Instrumentation API liegt in der Fähigkeit zur transparenten Bytecode-Manipulation: Das Verhalten bestehender Klassen lässt sich ändern, ohne deren Quellcode anzufassen. Das ist besonders relevant in sicherheitskritischen oder hochdynamischen Umgebungen, etwa beim Einfügen von Logging-, Tracing- oder Metrik-Code. Die API ermöglicht zudem eine Class Retransformation. Dabei lassen sich auch bereits geladene Klassen unter bestimmten Bedingungen nachträglich verändern – ein Feature, das in Verbindung mit Tools wie JRebel oder modernen Observability-Werkzeugen wie OpenTelemetry intensiv zum Einsatz kommt.




(Bild: Playful Creatives / Adobe Stock)

Am 14. Oktober findet die betterCode() Java 2025 statt. Bei der von iX und dpunkt.verlag ausgerichteten Online-Konferenz dreht sich alles um das für September geplante Java 25, das auch als LTS-Release verfügbar sein wird. Außerdem gibt es eine Keynote und ein Panel zu 30 Jahren Java und einen Vortrag zu ML für Java-Anwendungen.

Ein weiterer Vorteil ist die Integration ohne Quelltextänderungen. Ein Agent lässt sich mit dem Java-Prozess starten oder zur Laufzeit hinzufügen, ohne dass die Zielanwendung überhaupt wissen muss, dass sie instrumentiert wird. Dies ist insbesondere bei Debugging-, Monitoring- oder Sicherheitsanwendungen essenziell.

Darüber hinaus ist die Instrumentation API seit Java 5 fester Bestandteil des JDK und erfordert keine zusätzlichen Bibliotheken. In sicherheitssensiblen Umgebungen – etwa in der kritischen Infrastruktur oder im behördlichen Einsatz – kann genau dieser Umstand sowohl für den Einsatz sprechen (da keine externen Abhängigkeiten eingebracht werden müssen), als auch dagegen (da Angreifer dieselbe Schnittstelle missbrauchen könnten, um laufende Anwendungen zu manipulieren).

Die Mächtigkeit der Instrumentation API bringt zwangsläufig auch erhebliche Nachteile mit sich. An erster Stelle steht die Komplexität der Bytecode-Manipulation. Obwohl es Libraries wie ASM (Assembly Manipulator) oder Byte Buddy gibt, die den Umgang mit Bytecode erleichtern, bleibt dessen Manipulation ein Einstieg tief in den Maschinenraum der JVM und setzt ein fundiertes Verständnis der Java-Klassenstruktur, des Classloading und der Bytecode-Spezifikation voraus.

Ein zweites Risiko betrifft die Stabilität und Vorhersehbarkeit: Eingriffe in den Bytecode können zu subtilen Fehlern führen, etwa wenn Methodensignaturen verändert, Sicherheitsprüfungen umgangen oder Synchronisationsblöcke manipuliert werden. Solche Fehler sind schwer zu testen und treten gegebenenfalls nur unter bestimmten Laufzeitbedingungen auf – was den Debugging-Aufwand drastisch erhöht.

Auch die Kompatibilität über verschiedene JVM-Versionen hinweg ist ein Problem. Nicht jede JVM verhält sich identisch beim Classloading oder bei der Transformation. Besonders mit GraalVM oder in AOT-Umgebungen kommt es vor, dass bestimmte Features der Instrumentation API nicht oder nur eingeschränkt funktionieren. Das betrifft insbesondere dynamisches Nachladen von Klassen oder den Zugriff auf Low-level JVM-Interna.

Zu guter Letzt ist auch der Performance-Overhead nicht zu vernachlässigen. Zwar ist das Registrieren eines Agent meist sehr effizient, aber wenn Transformer komplexe Operationen bei jedem Classload ausführen oder Metriken injizieren, kann dies zu messbaren Verzögerungen beim Start oder zur Laufzeit führen – insbesondere in I/O-intensiven Serveranwendungen.

Die Java Instrumentation API ist daher ein zweischneidiges Schwert: Sie bietet eine einzigartige Möglichkeit, tief in das Verhalten einer Java-Anwendung einzugreifen – allerdings zum Preis erhöhter Komplexität, potenzieller Instabilität und erschwerter Wartbarkeit. Ihr Einsatz sollte daher gut begründet und gezielt erfolgen – stets begleitet von einer Test- und Logging-Infrastruktur. In sicherheitskritischen oder hochdynamischen Kontexten, in denen keine Änderung des Anwendungscodes möglich ist, bleibt sie jedoch ein unverzichtbares Werkzeug.

Die Performanceimplikationen der Java Instrumentation API sind vielschichtig und hängen stark vom jeweiligen Anwendungsfall, der Komplexität der durchgeführten Transformationen und der Art der Zielanwendung ab. Grundsätzlich kann man drei Phasen unterscheiden, in denen die Instrumentation API die Performance beeinflusst: beim Classloading, zur Laufzeit der instrumentierten Klassen und im Zusammenhang mit speicher- oder sicherheitsrelevanten Operationen.

Der signifikanteste unmittelbare Einfluss entsteht beim Laden einer Klasse durch den Classloader. Sobald ein ClassFileTransformer registriert ist, wird er bei jedem Ladevorgang einer neuen Klasse aufgerufen. In diesem Moment übergibt die JVM den Original-Bytecode der Klasse an den Transformer, der ihn modifiziert oder unverändert zurückgeben kann. Die Dauer dieser Transformation wirkt sich direkt auf die Startzeit der Anwendung oder auf das dynamische Nachladen von Klassen aus.

Komplexe Transformationen, insbesondere solche, die mit ASM oder ähnlichen Bytecode-Libraries durchgeführt werden, erzeugen häufig einen messbaren Overhead – insbesondere bei Framework-basierten Anwendungen mit tausenden Klassen. Wenn zusätzlich Logging, Tracing oder Code-Injektionen in jede Methode erfolgen, steigt die Ladedauer teils exponentiell an, da jeder Methodenblock individuell bearbeitet werden muss.

Je nach den am Bytecode vorgenommenen Veränderungen, kann sich auch zur Laufzeit ein zusätzlicher Overhead ergeben. Das gilt insbesondere dann, wenn der Transformer zusätzlichen Kontrollfluss einfügt – etwa Logging-Anweisungen, Zeitmessung, zusätzliche Validierungen oder sicherheitsrelevante Checks. Solche Einfügungen können beispielsweise die Ausführung jeder Methode um mehrere Nanosekunden bis zu Millisekunden verlängern, was in hochfrequent aufgerufenen Methoden (z. B. in I/O-Schleifen oder Geschäftslogik) zu massiven Performanceverlusten führen kann.

Ein typisches Beispiel ist das Method Entry/Exit Tracing, bei dem vor und nach jeder Methode Logging-Code injiziert wird. Obwohl dieser Logging-Code selbst nicht aktiv schreibt, sondern nur passive Marker setzt, entsteht dennoch eine Speicher- und CPU-Last, die sich unter hoher Last summieren kann.

Darüber hinaus kann die Instrumentierung auch JIT-Optimierungen der JVM beeinträchtigen, insbesondere wenn unvorhersehbare Kontrollflüsse eingefügt oder Methoden künstlich aufgebläht werden. Dies führt gegebenenfalls dazu, dass bestimmte Hotspots nicht mehr in nativ optimierten Code überführt werden – mit entsprechenden Folgen für die Ausführungszeit.

Wird bei der Instrumentierung zusätzlicher Objektzustand eingeführt – beispielsweise durch statische Caches, Trace-Informationen oder assoziierte Metadaten –, kann das zu einem Anstieg der Heap-Nutzung führen. Kritisch wird die Situation, wenn Weak- oder Soft-References nicht korrekt verwaltet oder kontinuierlich neue Klassen mit leicht unterschiedlichen Strukturen erzeugt werden, was schlimmstenfalls zu einem OutOfMemoryError beim Klassenspeicher führt.

Die Java Instrumentation API lässt sich mit minimalem Einfluss auf die Performance nutzen, sofern man sie effizient und zielgerichtet einsetzt. Das setzt allerdings voraus, dass man die registrierten Transformer so schlank wie möglich hält, unnötige Transformationen vermeidet und Klassen gezielt filtert. Ferner ist stets zu bedenken, dass selbst kleine Änderungen im Bytecode weitreichende Auswirkungen auf die Optimierungsstrategien der JVM und das Speicherverhalten haben können. Daher sind ein sorgfältiges Benchmarking und gezieltes Monitoring unerlässlich, wenn man die API in Produktionssystemen einsetzen möchte.

Da Instrumentation API Klassen zur Laufzeit manipulieren, Sicherheitsprüfungen entfernen oder neuen Bytecode injizieren kann, eröffnet sie eine Angriffsfläche, die weit über das hinausgeht, was typischer Anwendungscode leisten darf. Sie stellt damit ein potenzielles Einfallstor für Privilege Escalation, Code Injection und Persistenzmechanismen dar, wie sie bei Advanced Persistent Threats (APT) oder bösartigen Agenten zu beobachten sind.

Es ist wichtig festzuhalten, dass die Instrumentation API bewusst an der JVM-Sandbox vorbei operieren kann. Ist ein Agent einmal aktiv – sei es beim Start über das -javaagent-Argument oder dynamisch über die Attach API – dann hat er im Kontext der laufenden JVM nahezu vollständige Kontrolle über alle geladenen Klassen. Der Agent kann Sicherheitsprüfungen entfernen, Stacktraces verändern, Kontrollfluss umleiten oder sensible Informationen aus dem Speicher extrahieren. Auch die Transformation von Klassen aus sicherheitsrelevanten Packages wie java.lang.reflect oder javax.crypto ist möglich, sofern keine SecurityManager-Restriktionen aktiv sind (die allerdings seit Java 17 deprecated und seit Java 21 vollständig entfallen sind).

Besonders kritisch in diesem Zusammenhang ist die Attach API, da sie es erlaubt, beliebige Java-Prozesse auf dem lokalen System zur Laufzeit mit einem Agenten zu versehen – solange derselbe Benutzerkontext vorliegt. Das bedeutet: Ein Angreifer, der sich Zugriff auf einen Shell-Account verschafft, könnte jeden beliebigen Java-Prozess dieses Benutzers kompromittieren, ohne dass dieser Prozess dafür vorbereitet sein müsste. Dieses Szenario entspricht einer Form von Runtime Privilege Escalation auf Prozessebene.



Source link

Weiterlesen

Entwicklung & Code

Deno vs. Oracle: Deno sammelt 200.000 US-Dollar im Kampf um die Marke JavaScript


Im Kampf gegen den Markennamen JavaScript – im Besitz von Oracle – sammelt der Hersteller der JavaScript-Runtime Deno nun Spenden in einer Online-Kampagne ein. Der in den USA geführte Markenrechtsstreit ist in die „Discovery“-Phase getreten, in der Deno nun Beweise zusammentragen muss, dass JavaScript Public Domain geworden ist, also in der Öffentlichkeit als generischer Begriff für die Sprache gebraucht wird und nicht als Marke von Oracle.

In der GoFundMe-Kampagne versucht Deno nun, 200.000 US-Dollar einzusammeln. Das Geld benötigt die Firma für Umfragen, Expertisen und Aussagen, die fundiert belegen sollen, dass die Öffentlichkeit JavaScript nicht mit Oracle in Verbindung bringt. Außerdem will die Firma damit Prozesskosten decken und das, was am Schluss übrig bleibt, an OpenJS spenden: „Das Geld wird nicht an Deno gehen“, verspricht die Firma im Blog.

Deno hatte im November 2024 den Löschantrag beim US-Markenamt eingereicht, Oracle hatte dem im August 2025 widersprochen.


(who)



Source link

Weiterlesen

Beliebt