Java platvormi siluri arhitektuur

Java platvormi siluri arhitektuur (inglise keeles Java Platform Debugger Architecture, lühidalt JDPA) on mitmetasandiline siluri arhitektuur, mis aitab arendajatel luua silumisrakendusi, mida saab teisaldatavalt kasutada eri platvormidel ning mitmesuguste virtuaalmasinate ja Java arenduskomplektidega.

Tasemed

Java platvormi siluri arhitektuur koosneb kolmest tasandist: Java virtuaalmasina tööriista liides, Java silumise ülekande protokoll ja Java siluriliides.

Java virtuaalmasina tööriista liides

Java virtuaalmasina tööriista liides (Java Virtual Machine Tool Interface, edaspidi JVM TI) defineerib silutavad teenused, mida virtuaalmasin pakub. See sisaldab nii infopäringuid, tegevusi (näiteks kuhu asetada katkestuspunkt) ja teateid (näiteks kood jõudis katkestuspunktini).

Java silumise ülekande protokoll

Java silumise ülekande protokoll (Java Debug Wire Protocol, edaspidi JDWP) defineerib siluri ja silutavate protsesside vahelise kommunikatsiooni.

Java siluriliides

Java siluriliides (Java Debug Interface, edaspidi JDI) defineerib kõrgetasemelise Java keele liidese, mida tööriista arendajad saavad kasutada kaugsiluri rakenduste kirjutamiseks.

Siluri arhitektuuri kasutamine

Siluri arendaja võib siluri arhitektuuri kasutada ükskõik mis tasemel. Kuna JDI on kõige kõrgemal tasemel ja kõige lihtsam kasutada, siis kasutatakse seda enim siluri loomiseks. Selle peamiseks eeliseks on teisaldatavus - seda saab kasutada eri platvormide ja virtuaalmasinate peal ilma koodi muutmata.
Kui arendajal on vaja rohkem funktsionaalsust, siis on võimalik ka kirjutada rakendus, mis on ehitatud protokolli JDWP või liidese JVM TI peale. Näiteks siluriliidese arendamine mõnele teisele keelele on võimalik ainult JDWP või JVM TI abil.
Kõige levinumat kasutust siluri arhitektuurile pakuvad integreeritud arenduskeskkonnad, kus on tihti juba olemas graafiline ja lihtsasti arusaadav kasutajaliides programmi silumiseks. Sellisteks keskkondadeks on näiteks Eclipse, IntelliJ IDEA, NetBeans ja JDeveloper.

Näide JDI kasutamisest

Allolev Java programm loob liidest JDI kasutades ühenduse virtuaalmasinaga samal arvutil pordil 5005. Seejärel saadab programm virtuaalmasinale üle JDWP käsu lisada klassi 'näide.SilutavKlass' reale 10 katkestuspunkti ning pärast seda küsib virtuaalmasinalt kas katkestuspunkti on läbitud.
public static void main(String[] args) throws Exception {
  // Leia JDI siluri ühendaja
  Connector ühendaja = Bootstrap.virtualMachineManager().allConnectors().stream()
      .filter(c -> c.name().equals("com.sun.jdi.SocketAttach"))
      .findFirst().get();

  // Määra silutava virtuaalmasina aadressiks localhost:5005
  Map<String, Connector.Argument> argumendid = ühendaja.defaultArguments();
  argumendid.get("hostname").setValue("localhost");
  argumendid.get("port").setValue("5005");

  // Ühenda virtuaalmasinaga
  VirtualMachine silutavVirtuaalMasin = ((AttachingConnector) ühendaja).attach(argumendid);

  // Küsi silutavalt virtuaalmasinalt viidet klassile 'näide.SilutavKlass'
  ReferenceType klassiViide = silutavVirtuaalMasin.classesByName("näide.SilutavKlass").get(0);

  // Leia katkestuspunkti asukoht real 10 klassis 'näide.SilutavKlass'
  Location katkestuspunktiAsukoht = klassiViide.allLineLocations().stream()
      .filter(loc -> loc.lineNumber() == 10)
      .findFirst().get();

  // Lisa katkestuspunkt
  BreakpointRequest breakPointRequest = silutavVirtuaalMasin.eventRequestManager().createBreakpointRequest(katkestuspunktiAsukoht);
  breakPointRequest.enable();

  // Prindi välja katkestuspunktis peatumised
  while (!Thread.interrupted()) {
    EventSet teated = silutavVirtuaalMasin.eventQueue().remove();
    
    teated.stream()
        .filter(event -> Objects.equals(event.request(), breakPointRequest))
        .forEach(teade -> System.out.println("Virtuaalmasin peatus katkestuspunktis " + ((BreakpointRequest) teade).location()));

    // Juhul kui leiti mõni teade, siis pärast selle käsitlemist palume virtuaalmasinal oma tööd jätkata
    if (!teated.isEmpty()) {
      silutavVirtuaalMasin.resume();
    }
  }}