JMX

A Unix/Linux szerverek üzemeltetése wikiből

Írta: Kocsis Imre (BME-MIT)

A Java Managements Extensions (JMX) itt megtalálható tárgyalása szándékoltan annak rendszerfelügyeleti felhasználására koncentrál, az eszköz- és alkalmazásinstrumentáció rovására. A JMX "Java felügyelete Java-ból" jellege miatt az anyag feltételez legalább alapszintű Java programozási ismereteket az olvasó részéről; mindazonáltal a szerző reményei szerint ha nehezebben is, de érthető általános objektum orientált programozási és modellezési gyakorlattal is.

Tartalomjegyzék

1 Bevezető

A Java Management Extensions (JMX) egy, a Java SE 5.0-tól kezdve a platform-implementációk által kötelezően megvalósítandó szabvány. Célja mind a Java alkalmazások és a futtató platform (távoli) menedzsmentre felkészítésének, mind pedig a menedzsment célú hozzáférés mikéntjének szabványosítása.

A JMX mind menedzsment kiszolgálói (illetve menedzselt elem), mind pedig felügyeleti alkalmazás oldalon alapvetően a Java platform feletti implementációt feltételez; főként kötelezően megvalósítandó Java interfészeket, osztályokat és azok viselkedését írja le.

Hasonlóan például a Web Based Enterprise Management nyílt távfelügyeleti szabványhoz és az arra épülő megoldásokhoz, a JMX valódi objektum-orientált felügyeleti adatmodellezést tesz lehetővé (aminek nem csak az objektumok/asszociációk/attribútumok lekérdezése része, de metódusokat is lehet hívni); azzal ellentétben azonban nem definiál előre használható/kiterjeszthető modelleket. J2EE környezetekre a JMX-re épülő JSR-77 specifikáció határozza meg a felügyeleti modellt.

2 JMX alapfogalmak és architektúra

(thumbnail)
A JMX architektúrája (a szabvány alapján)
A JMX-ben a felügyelet szempontjából érdekes erőforrásokat reprezentáló illetve kezelő Java objektumok, un. managed Bean-ek (MBean-ek) egy kifejezetten felügyeleti célú névvel regisztrációra kerülnek egy MBeanServer-ben, attribútumaikkal és hívható metódusaikkal együtt. (A regisztrálhatósághoz bizonyos, pl. osztályelnevezési konvenciókat megadó feltételeket az objektumnak teljesítenie kell.)

Az MBeanServer egy, az MBean-eket nyilvántartó objektum, melynek publikus interfésze az MBean regisztráció és törlés mellett lehetőséget biztosít többek között:

  • MBean-ek név és név-minta szerinti keresésére,
  • MBean-ek attribútum- és metódushalmazának lekérdezésére,
  • attribútumok olvasására és írására,
  • metódusok hívására,
  • az MBean-ek által létrehozott jelzésekre (Notification-ök) feliratkozásra.

Megjegyzendő, hogy a mind lokális, mind távoli (lásd később) MBeanServer-hozzáférések esetén alkalmazható interfész metóduskészlete szűkebb, mint annak a specifikusan a lokális Java virtuális gépben található MBeanServer-hez kiterjesztett gyermeke.

A Java SE 5.0-tól kezdve a Java implementációk tartalmaznak egy platform MBeanServer-t. Ez amellett, hogy képes az alkalmazások felügyeletére létrehozott Mbean-eket kezelni, a java.lang.management API által meghatározott MBeaneket implementálva magának a futtató platformnak a felügyeletét is támogatja. Egy JVM-en belül több MBeanServer objektum létezése is megengedett; a platform mellett akár más JMX implementációkkal is dolgozhatunk egyidejűleg. (Az MBeanServer-ek alapesetben egymástól és egymás MBean-jeitől teljesen függetlenek; igény esetén azonban például kaszkádolhatóak.)

Az ugyanazon JVM-en belüli MBeanServer(-ek) elérésére a felügyeletet végző Java kód a specifikáció megvalósításainak egy factory osztályát, vagy a platform implementáció factory osztályát használhatja. A távoli eléréshez azonban nyilvánvalóan szükség van valamilyen mechanizmusra, amely egy JVM MBeanServer-eit valamilyen formában elérhetővé teszi egy másik JVM objektumai számára.

A specifikáció szerint ezt a funkcionalitást látják el a konnektorok. Egy konnektor-megvalósítás részei a konnektor kliens (connector client) és a konnektor kiszolgáló (connector server). A kliens oldali megvalósítás célja az, hogy olyan, a már említett távoli interfészt implementáló lokális objektumot hozzon létre, melynek lokális metódushívásai végrehajtódnak a távoli MBeanServer-en, illetve melynek jelzéseit az átadja a helyi előfizetőknek. A specifikáció kötelezően megvalósítandóként leír egy távoli metódushívás alapú (Java RMI) konnektort, illetve egy opcionálisan megvalósítandó "JMX Messaging Protocol" alapút.

A specifikáció elismeri "protokoll adapterek" (protocol adapters) implementálásának esetleges szükségességét; ezek nem-Java kliensek számára nyújtanának hozzáférést pl. HTML vagy SNMP felett. (A szerző legjobb tudomása szerint elterjedten használt ilyen megoldás nincs.) Egy nagyon érdekes, de 2008 októberében még csak "Early Access Draft" szintű projekt a Web Services Connector; a konnektor a WS-Management szabványt használja hordozóprotokollként, így a klienseknek nem kell mindenképpen Java alkalmazásoknak lenniük (az implementáció használhatóságát demonstrálták már WinRM-mel; elméletileg nyílt implementációkkal, mint pl. a wiseman is működőképesnek kell lennie.)

3 MBean-ek metaadatai

(thumbnail)
A JMX metaadat-osztályok (a szabvány 61. oldaláról átemelve); javax.management csomag
Altalános esetben egy MBean attribútumait és metódusait az MBeanServer-től kérdezzük le. Látható, hogy a metaadat-osztályok Java osztályok egyszerű leírását adják. Kiemelendő, hogy a típus-adatok (beépített Java típusok illetve osztálynevek) egyszerűen sztringként modellezettek. Elméletileg ez lehetőséget biztosít arra, hogy a kliens ellenőrizze, hogy rendelkezik-e a megfelelő osztálydefiníciókkal; a gyakorlatban azonban a hiányzó osztálydefiníciók és a sorosíthatósági problémák elkerülésére ajánlott az MBean-ek interfészében a beépített osztályokra és típusokra, valamint azok tömbjeire szorítkozni.

A Descriptor ("leíró") interfész segítségével a metaadat-osztályok példányaihoz (kulcs,érték) pár alakú bejegyzésekkel metaadatokat fűzhetünk, ahol a kulcs egy java.lang.String, az érték pedig egy általános java.lang.Object objektum. A specifikáció megadja kulcsok egy halmazát előre definiált névvel, céllal és lehetséges értékkészlettel (lásd az interfész definícióját). Ilyenek például a defaultValue kulcs (attribútum vagy metódus-paraméter alapértelmezett értéke, az érték objektum), a severity (jelzések súlyossága, értéke 0-tól 6-ig java.lang.Integer vagy java.lang.String) vagy a units kulcs (attribútum, metódus-paraméter vagy metódus visszatérési érték mértékegysége, például "bytes" vagy "seconds"; értéke java.lang.String típusú).

Példaként tekintsük a "java.lang:type=Runtime" nevű, magát a JVM-et reprezentáló platform MBean néhány, az MbeanServer-től programozottan lekérdezett attribútum metaadatát (a teljesség kedvéért: az MBeanInfo getMBeanInfo(ObjectName name) metódus segítségével). A leírók mezői itt azt adják meg, hogy hogyan lehet a Java típusokat egy, a szabvány által megadott egyszerű típuskészletre leképezni és ezzel elősegíteni, hogy az MBean nem Java kliensekkel is együtt tudjon működni (azaz hogy "open" MBean legyen). Ezen leírók gyakorlati jelentősége igen kicsi.

"Runtime" MBean attribute: BootClassPath
Description: BootClassPath
Attribute type: java.lang.String
Readable/writable: true/false
Attribute descriptor field openType: javax.management.openmbean.SimpleType(name=java.lang.String)
Attribute descriptor field originalType: java.lang.String

"Runtime" MBean attribute: Uptime
Description: Uptime
Attribute type: long
Readable/writable: true/false
Attribute descriptor field openType: javax.management.openmbean.SimpleType(name=java.lang.Long)
Attribute descriptor field originalType: long

"Runtime" MBean attribute: ClassPath
Description: ClassPath
Attribute type: java.lang.String
Readable/writable: true/false
Attribute descriptor field openType: javax.management.openmbean.SimpleType(name=java.lang.String)
Attribute descriptor field originalType: java.lang.String

4 Az MBean-ek elnevezése

Ismétlés képpen: az MBean-ek valódi Java objektumok, melyeket (általános esetben) nem közvetlenül érünk el (pontosabban fogalmazva: nincs rájuk objektum-referenciánk), hanem egy proxy jellegű Java objektumnak (az MBeanServer-nek) mondjuk meg, hogy melyik MBean-jével milyen operációt hajtasson végre, illetve azt kérdezzük le a benne regisztrált MBean-ekkel kapcsolatban. Ennek folyománya, hogy az MBeanServer meghívásai során valahogy meg kell jelölnünk azt az MBean-t, amelyikre például egy attribútum érték (vagy éppen attribútum készlet) lekérdezés vonatkozik - ezt viszont a szokásos módon, Java objektum-referenciákkal nem tudjuk megtenni. (Ez valójában inkább előny, mint hátrány...)

Az MBean-eket egy MBeanServer-ben az ún. objektum nevük azonosítja egyértelműen. API szempontból az objektum nevet a javax.management.ObjectName osztály reprezentálja. Egy objektum név két részből áll:

  • egy domain névből,
  • és egy vagy több kulcs tulajdonság (key property) rendezetlen halmazából.

A domain név egyszerűen egy sztring, mely az MBean-ek (jelenleg) nem hierarchikus névtér alapú elválasztását teszi lehetővé. A kettőspont (':'), csillag ('*') és kérdőjel ('?') karaktereken kívül minden karakter megengedett benne. Javasolt, hogy a domain név a kiadó szervezet DNS neve alapján, a Java csomagok elnevezési konvenciójához hasonlóan legyen felépítve (pl. hu.bme.mit.inf.MyDomain) - mindazonáltal a JMX implementáció ezt a hierarchiát nem értelmezi. Minden MBeanServer-nek van egy alapértelmezett domain-je; amennyiben domain név nélkül hivatkozunk MBean-ekre (vagy keresünk, pontos illeszkedést elvárva), úgy ezt használja.

A kulcs tulajdonságok kulcs-érték párok. Mind a kulcsok, mind az értékek sztringek; egy kulcs egy adott kulcs tulajdonság halmazban nem fordulhat elő kétszer. A type tulajdonság használata általánosan elterjedt; segítségével a nevekre való mintaillesztéssel lehet egyfajta primitív példányenumerációt megvalósítani. Bizonyos különleges karakterek (mint például a kettőspont) nem megengedettek sem a nevekben, sem az értékekben. Az objektum nevek szöveges reprezentációja a következő minta alapján történik: domainNév:kulcs=érték[,kulcs=érték]*. Egy szöveges reprezentáció kanonikus név, ha a névben a kulcsok lexikografikusan rendezettek. Néhány név példa (környezet: Sun JDK 1.6 felett a platform MBeanServer-t használó Apache Tomcat 5.5, a nevek a platform MBeanServer-ből származnak):

Catalina:j2eeType=Servlet,name=default,WebModule=//localhost/host-manager,J2EEApplication=none,J2EEServer=none
Catalina:type=Cache,host=localhost,path=/tomcat-docs
Catalina:type=Cache,host=localhost,path=/servlets-examples
Catalina:type=ThreadPool,name=jk-8009
java.lang:type=Runtime
java.util.logging:type=Logging
com.sun.management:type=HotSpotDiagnostic

Látható, hogy mind a platform, mind az alkalmazás MBean-ek esetében pusztán a név alapján nem igazán dönthető el, hogy az adott MBean mit modellez és mi a szerepe. Ezen az MBean-ek attribútumainak és operációinak lekérdezése sem valószínű hogy segíteni fog; az MBean-ek által megvalósított felügyeleti modellt mindenképpen meg kell ismerni a megfelelő dokumentációkból.

Egy objektum név lehet azonban minta is, melyre illeszkedő objektum neveket keresünk. Ekkor a '*' és a '?' karakterek használhatók mint wildcard-ok (a szokásos módon; * akármilyen hosszú karaktersorozat, ? pontosan egy karakter).

Egy másik, az objektumnévhez hasonlóan alapvető fontosságú osztály az ObjectInstance (objektum példány - vitatható hogy ennek a szóösszetételnek van-e értelme). Példányai objektumnév - osztálynév összerendeléseket tartalmaznak, azaz megadják, hogy az adott néven MBean-ként regisztrált Java objektum mely Java osztály példánya.

5 A JVM felkészítése

Bár a platform MBeanServer implementációként megtalálható bármely Java SE 5 vagy 6 Java implementációban, ez még nem jelenti automatikusan azt, hogy bármely futó JVM megkötések nélkül felügyelhető lenne másik JVM-ből (adott JVM-en belül alapértelmezetten elérhetjük a JVM platform MBeanServer-ét). A platform MBeanServer beépített RMI alapú konnektorára vagy csak helyi, vagy helyi és távoli hozzáférést állíthatunk be.

A helyi hozzáférés a Java SE 5-ben jelent meg, engedélyezése a com.sun.management.jmxremote VM kapcsolóval lehetséges:

java -Dcom.sun.management.jmxremote DummyServer

A JSE 5 esetén azonban az így konfigurált VM-ben létrejövő konnektor programozottan nem volt elérhető, csak a Sun (illetve az IBM) által szállított JConsole grafikus eszköz volt képes ahhoz a fenti módon való engedélyezés után csatlakozni.

Ez a Java SE 6 kiadásával megváltozott. A nem szabványos, de a Sun által támogatott Attach API lehetővé teszi, hogy egy JVM "rácsatlakozzon" az őt futtató felhasználó egy másik JVM folyamatára. Ezzel lehetőség a nyílik a fenti kapcsoló hatására létrejött konnektor cím kiolvasására és az alapján a csatolt JVM platform MBeanServer-éhez csatlakozásra. Az Attach API viszont arra is módot ad, hogy ellenőrizzük, hogy létezik-e már a helyi konnektor cím és ha nem, akkor létrehozzuk az (egy egyszerű példa itt). Ezzel a 6-os verzióban a kapcsoló tovább él, de csak bizonyos különleges esetekben van értelme. A 6-os JDK beépített felügyeleti eszközei (a JConsole és a VisualVM; lásd később) képesek így lokális JVM-ekre csatlakozni. Megjegyzendő, hogy mivel lokális kapcsolat jön létre ugyanazon felhasználó két folyamata között, további jogosultságellenőrzések nem történnek.

Egy "különleges eset" amire a fenti kitétel utal például a Tomcat 5.5 szigorúan lokális monitorozása. Ha a JVM-et a fenti kapcsoló nélkül indítjuk (pl. a teljes mértékben alapértelmezett konfigurációval), akkor a JConsole, illetve a VisualVM az Attach API segítségével aktiválni tudja a platform MBean-eket, de nem a Tomcat MBean-jeit, melyek a kiszolgáló-funkciók felügyeletéhez szükségesek. Ha azonban a fenti kapcsolót megadjuk, akkor induláskor a Tomcat létrehozatja a platformmal a platform MbeanServer objektumot és a saját MBean-eit regisztrálja benne (más kérdés, hogy ez így elég bugosan működik - pl. a servlet-ek nem jelennek meg...). Programozottan lehet hogy lehetőség lenne az Attach API-val a platform MBeanServer létrehozatása után a Tomcat MBean-ek létrehozására és regisztrációjára is, a szerző azonban ezt a lehetőséget tárgyaló forrást nem talált.

A távoli hozzáférést (és mellékhatásként egyben a helyit is) a com.sun.management.jmxremote.port kapcsolóval engedélyezhetjük, megadva a portot amelyen az RMI konnektor kiszolgáló a kapcsolatokat várja. További kapcsolókkal rendelkezhetünk a kapcsolat titkosításáról és az autentikáció módjáról is:

java -Dcom.sun.management.jmxremote.port=9004 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
DummyServer

FIGYELEM: a fenti példát a külvilágtól megfelelően leválasztott tesztkörnyezeteket leszámítva ne használjuk, hiszen ez esetben se titkosítás nincs, se (legalább jelszó alapú) autentikáció. Az adatbiztonságot érintő lehetőségekkel és beállításokkal kapcsolatban lásd a megfelelő Sun oldalt.

6 Programozott távoli hozzáférés: egy példaprogram

A Java-ban megvalósított programozott távoli hozzáférés szemléltetésére bemutatunk egy egyszerű példaprogramot, mely egy, a helyi gépen futó, távoli hozzáférésre felkonfigurált JVM platform MBeanServer-éhez csatlakozik. A kód kimérete (a Java nem éppen feszes szintaxisának és programozási stílusának köszönhetően) megtévesztő; a kapcsolódás és az operációk egy igen egyszerű logikát követnek. A használt osztályokat és metódusokat itt részletesen nem tárgyaljuk; az érdeklődő olvasó azokat megtalálja a javax.management csomag dokumentációjában.

Első lépésként egy speciális URL objektumban megadjuk a JMX szolgáltatás (pontosabban az RMI konnektor kiszolgáló) címét, létrehozunk egy konnektor-objektumot erre a címre, és megpróbálunk tőle elkérni egy (már elindított) kapcsolat objektumot, mely az MBeanServer már korábban említett, távoli hozzáférésre szűkített interfészét implementálja:

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.Set;

import javax.management.*;

public class MyClient {

	public static void main(String[] args){
	
		
		JMXServiceURL address = null;
		JMXConnector connector = null;
		MBeanServerConnection connection = null;
		try {
			address = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi");
		} catch (MalformedURLException e) {
			e.printStackTrace(); System.exit(1);
		}
		
		try {
			//Note: for real life cases it's not that simple; we would have to
			//specify a host of additional information in a further java.util.Map
			//argument.
			connector = JMXConnectorFactory.connect(address);
		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}
	
		try {
			connection = connector.getMBeanServerConnection();
		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}

Ezek után "körbenézünk": lekérdezzük és kiírjuk a domain-eket, az alapértelmezett domain-t, az MBean-ek számát és nevét:

		try {
			String[] domains = connection.getDomains();
			System.out.println("Domains:");
			System.out.println("-----------------------");
			for(int i = 0; i < domains.length; i++){
				System.out.println(domains[i]);
			}
			System.out.println();
			
			System.out.println("Default domain: " + connection.getDefaultDomain());
			System.out.println("MBean count: " + connection.getMBeanCount() + "\n");
			
			System.out.println("MBean names:");
			System.out.println("-----------------------");
			Set<ObjectName> names = connection.queryNames(null, null);
			Iterator<ObjectName> si = names.iterator();
			while(si.hasNext()){
				ObjectName name = si.next();
				System.out.println(name);
			}

A már említett Tomcat 5.5 környezetre a kód konzol-kimenetének eleje a következő (a Tomcat MBean-jeinek nagy száma miatt a lista erősen rövidített):

Domains:
-----------------------
JMImplementation
Users
com.sun.management
Catalina
java.lang
java.util.logging

Default domain: DefaultDomain
MBean count: 212

MBean names:
-----------------------
Catalina:j2eeType=WebModule,name=//localhost/host-manager,J2EEApplication=none,J2EEServer=none
Catalina:type=ServerClassLoader,name=shared
Catalina:j2eeType=Servlet,name=default,WebModule=//localhost/webdav,J2EEApplication=none,J2EEServer=none
...
java.lang:type=Memory
...
java.lang:type=Compilation
...
java.lang:type=Runtime
...

Tegyük fel, hogy már tudjuk hogy a java.lang:type=Memory MBean-nek milyen attribútamai vannak és mi valójában a folyamat által aktuálisan használt heap memória mennyiségére vagyunk kíváncsiak:


			try {
				javax.management.openmbean.CompositeDataSupport mUsage = 
					(javax.management.openmbean.CompositeDataSupport)
					connection.getAttribute(new ObjectName("java.lang:type=Memory"), 
							"HeapMemoryUsage");
				Long heap = (Long)mUsage.get("used");
				System.out.println(heap);
			} catch (Exception e) {
				e.printStackTrace(); System.exit(1);
			}

Eredményként megkapjuk, hogy jelenleg megközelítőleg 5MB heap memória van használatban:

5090544

A programot azzal fejezzük be hogy lezárjuk a kapcsolatot:

		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}
		
		try {
			connector.close();
		} catch (IOException e) {
			e.printStackTrace(); System.exit(1);
		}
	}	
}

7 A platform MBean-ek

Ha egy Java alkalmazás a távfelügyeletére nem is regisztrál MBean-eket, a JMX akkor is hasznos lehet: a platform MBean-ek segítségével fontos információkhoz juthatunk a JVM-mel kapcsolatban, illetve befolyásolhatjuk annak működését.

  • Osztálybetöltés (java.lang:type=ClassLoading): betöltött/kiürített osztályok számának lekérdezése
  • Fordítás (java.lang:type=Compilation): a "Just In Time" (JIT) fordító nevének és a fordítással eltöltött időnek a lekérdezése
  • Memória (java.lang:type=Memory): összes rendelkezésre álló, lefoglalt és aktuálisan használt heap és nem heap memória mennyiségének lekérdezése, szemétgyűjtés (garbage collection) indítása.
  • Szálkezelés (java.lang:type=type=Threading): szálak számának, CPU használatának és egyéb jellemzőinek lekérdezése.
  • Futtatórendszer (java.lang:type=Runtime): a virtuális gép tulajdonságainak, mint például gyártó, a megvalósított Java specifikáció vagy a classpath lekérdezése.
  • Operációs rendszer (java.lang:type=OperatingSystem): operációs rendszer nevének, verziójának, az architektúrának és a processzorok számának a lekérdezése.
  • Szemétgyűjtők (java.lang:type=GarbageCollector,name=szemétgyűjtő neve): statisztikai adatok a szemétgyűjtőkről.
  • Memóriakezelők (java.lang:type=MemoryManager,name=memóriakezelő neve): a memóriakezelők alapadatai.
  • Memőriaterületek (a heap és nem heap memória részei, java.lang:type=MemoryPool,name=terület neve): különböző lekérdező- és kezelő-metódusok.

Ezen MBean-ek egy része a Java platformot csak kevéssé ismerő felhasználók számára is értelmes jelentéssel bír - például ha a heap memória majdnem elfogyott és szinte folyamatosan fut a szemétgyűjtés, akkor "gond van"; más részük felügyeleti szempontból értelmes használatához (mint például a memóriaterületek) a Java platform behatóbb ismerete szükséges.

8 Nem programozott hozzáférés: JConsole

(thumbnail)
JConsole: áttekintő nézet
(thumbnail)
JConsole: MBean nézet

A JConsole egy grafikus alkalmazás, mely része a Java SE 6 JDK-knak. Segítségével mind távoli, mind helyi hozzáféréssel csatlakozhatunk egy platform MBeanServer-hez, grafikusan tallózhatjuk az MBean-eket, meghívhatjuk operációikat és áttekintő képet kapunk a platform állapotáról - egy sor Java kód írása nélkül. A JConsole-t erőforrásigénye miatt éles rendszereken ajánlott mindig más gépen futtatni, mint ahol a felügyelt alkalmazás fut. Indításához a JDK bin könyvtárában a jconsole binárist kell futtatni.

9 Nem programozott hozzáférés: Java VisualVM

(thumbnail)
VisualVM: MBean nézet
(thumbnail)
VisualVM: heap dump megtekintése

A Java VisualVM egy, a NetBeans platformra épülő "minden az egyben hibaelhárító" eszköz, mely a Sun JDK 6-ban szintén helyet kap (JDK bin könyvtár, jvisualvm bináris). Tartalmazza a JConsole funkcionalitását - ezen kívül azonban képes többek között "on-the-fly" kódinstrumentációval CPU és memóriahasználat profilozásra, heap és szál dump-ok generálására és tartalmuk megjelenítésére. Egyben kiterjeszthető platform is; képességei plugin-ekkel bővíthetők.

10 Szkriptelt hozzáférés

(thumbnail)
JConsole: script shell

Bár a szakmai köztudat összemossa a Java nyelvet és a Java platformot, műszaki értelemben véve a Java platform ugyanúgy nem kötött a Java nyelvhez, mint a .NET platform a C#-hoz: a JVM-re elméletileg más nyelvek is fordíthatóak. Továbbá Java-ban implementálhatóak más nyelvek interpreterei, mely megoldás az előzőhöz hasonlóan hozzáférést biztosíthat az interpretált nyelv számára Java osztályokhoz.

Több nyelv is létezik, mely ezen két mintát kihasználva egyszerűbb programozást, illetve szkriptelést valósít meg a Java platformon. A JMX szempontjából ezek jelentősége abban áll, hogy a JMX-et elérő kód nagyban egyszerűsödik.

A JavaScript némiképp különleges eset. A JConsole-hoz létezik és a 6-os JDK-val együtt települ egy plugin, mely egy alapértelmezetten JavaScript-et támogató "script shell" fület ad hozzá a JConsole GUI-hoz. Aktiváláshához a jconsole alkalmazást a következőképp kell indítani:

<JDK6>/bin/jconsole -pluginpath <JDK6>/demo/scripting/jconsole-plugin/jconsole-plugin.jar

A JConsole shell támogatás a JVisualVM-be is telepíthető az eszköz pluginkezelője segítségével. A script shell továbbá átkonfigurálható JavaScript helyett pl. Groovy futtatására is.

11 JMX a J2EE környezetek felügyeletében

A JMX nem ad meg szabványos felügyeleti modelleket, így az egyes alkalmazások tervezőin múlik, hogy hogyan modellezik az alkalmazás felügyeletét MBean-ekkel. A teljes tervezői szabadság megengedése az általános alkalmazások szintjén nyilvánvalóan indokolt; a J2EE esetén azonban, ahol a különböző megvalósítások olyan jól meghatározott, a felügyelet szempontjából is természetes módon különálló objektumokként modellezhető fogalmakkal operálnak, mint az Enterprise JavaBean-ek vagy a servlet-ek, már értelmes a felügyeletet szabványosítani.

Ezt a célt szolgálja a JSR-77: "Java 2 Platform, Enterprise Edition Management Specification". A szabvány a megadott felügyeleti modellhez valójában nem csak a JMX feletti hozzáférést követeli meg, az EJB-jellegű hozzáféréssel mi azonban itt nem foglalkozunk.

A WebSphere, JBoss, Weblogic, GlassFish és Oracle J2EE alkalmazás-szerverek aktuális verziói különböző mértékű hiányosságokkal és eltérésekkel, de mind támogatják a JSR-77-et. Alkalmazásszerver-környezetekben azonban jellemző, hogy a JMX támogatás jóval a JSE platform-támogatás előtt jelent meg, így a szabvány RMI konnektor mellett saját, nem szabványos konnektorokat is (vagy inkább...) támogatnak - természetszerűleg saját konnektor-kliens könyvtárakkal. (Megjegyzés: egy felületes keresés alapján annak a támogatása, hogy az alkalmazásszerverek a platform MBeanServer-t használják illetve oda is reflektálják a saját implementációjukat egyelőre minden esetben problematikus.)

(thumbnail)
A J2EEManagedObject és "gyerekei" (a szabvány 20. oldaláról átemelve)
A JSR-77 megadja, hogy egy J2EE környezet bizonyos objektumait milyen (nem kimondva, de lényegében a JMX MBean értelemben véve) menedzselt objektumokként kell reprezentálni. A megadott objektum-szignatúrák egy szignatúra-kiterjesztési hierarchiát alkotnak, melynek gyökerében a J2EEManagedObject elnevezésű szignatúra-típus található. Ez a következő attribútumok meglétét írja elő:
  • objectName: objektumnév
  • stateManageable: boolean
  • statisticsProvider: boolean
  • eventProvider: boolean

Az objektumnév egy JMX objektum név, azzal a megkötéssel hogy a következő kulcs tulajdonságoknak mindenképpen szerepelnie kell benne:

  • j2eeType: értéke a J2EEManagedObject típus-hierarchiában a megfelelő típus neve
  • name: az objektum neve (tetszőleges)
  • <szülő-j2eeType-ok>: kulcsként olyan típusnevek, melyek meglétét a szabvány előírja az adott típusra, értékeikként pedig a típusok "példányai". Egyfajta egyszerű tartalmazási/csoportosítási reláció leírás; például egy J2EEApplication-höz meg kell adni a J2EEServer-t is, amin fut:
MitInfJ2EEDomain:j2eeType=J2EEApplication,name=LabPortal,J2EEServer=SauronServer

A boolean flag-ek további képességek meglétét vagy meg nem létét jelzik. Amennyiben értékük "true", úgy az objektumnak további, a szabvány által meghatározott követelményeknek kell megfelelnie. A "stateManageable" objektumok akutális állapotának kiolvashatónak kell lennie (értékek: STOPPED, STARTING, RUNNING, STOPPING, FAILED) és egy start() és stop() metódussal módot kell adnia az indítás és leállítás megkísérlésére. A "statisticsProvider" objektumoknak hozzáférést kell biztosítaniuk a típusuknak megfelelő teljesítményleíró-interfészekhez.

Példaként tekintsük a web modulokat és a servlet-eket! A servlet-ek általában viszonlyag egyszerű Java programok, melyek egy un. servlet konténerben kerülnek elhelyezésre. A konténer a beérkező, jellemzően HTTP kéréseket Java objektummá alakítva mint bemenetekkel meghívja a servlet-kódot és a visszatéréskor kapott Java objektumból választ konstruál. A web modulok egy vagy több szervletet, JSP oldalakat, statikus tartalmakat és egy telepítési leíró XML állományt tartalmazó JAR állományok, ".war" ("Web Archive") kiterjesztéssel.

A web modulokat a J2EEManagedObject -> J2EEDeployedObject -> J2EEModule -> WebModule típus modellezi. Az "öröklési útra" aggregált interfésze:

  • deploymentDescriptor: string, a teljes XML kell legyen
  • server: objektum név
  • javaVMs: objektum név tömb
  • servlets: objetum név tömb

Teljesítményleíró interfész nem tartozik hozzá.

A servlet típus közvetlen gyermeke a J2EEManagedObject-nek, további attribútumok nem tartoznak hozzá; teljesítmény-interfésze a minimum/maximum/átlagos végrehajtási idő és a hívások számának lekérdezhetőségét írja elő.

12 Tomcat 5.5 felügyelete JMX-szel

Az Apache Tomcat egy nyílt forráskódú, ingyenes servlet és JSP kiszolgáló. A JMX feletti felügyelhetőségéhez képes használni a platform MBeanServer-t a szabványos RMI konnektor kiszolgáló engedélyezésével. A itt leírt példákban ehhez csatlakozunk a JConsole (vagy a JVisualVM) segítségével; a hozzáférés a JavaScript shell-en keresztül történik. A valódi programozott hozzáféréshez módosítás nélkül felhasználható a korábban bemutatott kód (eltekintve a biztonságos hozzáféréshez szükséges paraméterektől); megjegyzendő, hogy valójában a script shell a programozott hozzáférésnél bemutatott, a JConsole által "előre elkészített" MBeanServerConnection Java objektumot használja, melyhez egy globális változó lekérdezésével, vagy egy egyszerű JavaScript függvényhívással férhetünk hozzá:

js>plugin.context.MBeanServerConnection
javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection@605b1e
js>mbeanConnection
sun.org.mozilla.javascript.internal.InterpretedFunction@6480d6
js>mbeanConnection()
javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection@605b1e

A beépített és a help() függvényhívás segítségével kiíratható JavaScript függvények megtalálhatóak a <JDK6>\demo\scripting\jconsole-plugin\src\resources állományban. (A függvényeken jól meg lehet figyelni hogy milyen egyszerűen képes a JavaScript Java objektumokhoz hozzáférni és JavaScript változóknak Java objektumokat értékül adni.) Valójában az itt shell-ben megvalósított lekérdezéseket és operációkat a programozott hozzáférés mellett akár a JConsole/JVisualVM GUI-ból is elvégezhetnénk; mindazonáltal didaktikai szempontból a parancssoros megközelítés talán a legszerencsésebb.

Tudjuk, hogy a Tomcat egy servlet/JSP konténer, s mint ilyen a teljes J2EE specifikáció egy részét implementálja. Kérdezzük le a JSR-77-nek megfelelő nevű menedzselt objektumokat! Először készítsünk egy beépített JavaScript üggvény segítségével egy megfelelő Java objektum név objektumot, és tároljuk el egy JavaScript változóban:

js>j2eenames=objectName("Catalina:j2eeType=*,*")
Catalina:j2eeType=*,*

Ezután kérdezzük le az MBean neveket, melyek erre a mintára illeszkednek és a java.lang.Object és a java.lang.Class megfelelő metódushívásaival derítsük ki, hogy milyen típusú Java objektumot kaptunk vissza:

js>names=queryNames(j2eenames, null)
[...]
js>names.getClass().getName()
java.util.HashSet

A java.util.HashSet interfészt átnézve készítsünk egy iterátort a halmaz tartalmára és ennek segítségével írassuk ki az egyes elemeket:

js>iterator=names.iterator()
java.util.HashMap$KeyIterator@d14a1a
js>while(iterator.hasNext()){ name=iterator.next(); echo(name);}
Catalina:j2eeType=WebModule,name=//localhost/,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.jsp2.tagfiles.panel_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.include.foo_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.forward.one_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=default,WebModule=//localhost/,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=WebModule,name=//localhost/host-manager,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=default,WebModule=//localhost/webdav,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=servletToJsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=HelloWorldExample,WebModule=//localhost/servlets-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.error.err_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.source_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.jsp2.simpletag.repeat_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=org.apache.jsp.sessions.carts_jsp,WebModule=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=Servlet,name=SessionExample,WebModule=//localhost/servlets-examples,J2EEApplication=none,J2EEServer=none 
Catalina:j2eeType=WebModule,name=//localhost/servlets-examples,J2EEApplication=none,J2EEServer=none 
[...]

Mint látható, a JSR-77 MBean-ek servlet-ek és web modulok. Kérdezzük le az egyik servlet attribútumait és operációit, majd kérdezzük le, hogy a servlet a web modulján belül milyen URL posztfixekre van leképezve! (A mesterséges sortöréseket a shell nem fogadja el, azok az olvashatóság miatt szerepelnek.)

js>exampleservlet=objectName("Catalina:j2eeType=Servlet,name=HelloWorldExample, \
WebModule=//localhost/servlets-examples,J2EEApplication=none,J2EEServer=none")
[...]
js>exampleinfo=mbeanInfo(exampleservlet)
[...]
js>exampleattribs=exampleinfo.getAttributes()
[...]
js>for(var i = 0; i < exampleattribs.length; i++) {echo(exampleattribs[i]);}
ModelMBeanAttributeInfo: modelerType ;\
Description: Type of the modeled resource. Can be set only once ;\
 Types: java.lang.String ;\
 isReadable: true ; isWritable: false ;\
 Descriptor: descriptorType=attribute,\
 displayName=modelerType,\
 getMethod=getModelerType,\
 name=modelerType 
ModelMBeanAttributeInfo: engineName ; [...] 
ModelMBeanAttributeInfo: eventProvider ; [...]
ModelMBeanAttributeInfo: objectName ; [...]
ModelMBeanAttributeInfo: stateManageable ; [...]
ModelMBeanAttributeInfo: statisticsProvider ; [...]
ModelMBeanAttributeInfo: processingTime ; Description: Total execution time of the servlet's service method ; [...] 
ModelMBeanAttributeInfo: minTime ; Description: Minimum processing time of a request ; [...]
ModelMBeanAttributeInfo: requestCount ; Description: Number of requests processed by this wrapper ; [...]
ModelMBeanAttributeInfo: errorCount ; Description: Error count ; [...]
ModelMBeanAttributeInfo: loadTime ; Description: Load time ; [...]
ModelMBeanAttributeInfo: classLoadTime ; Description: Class loading time ; [...]
js>exampleops=exampleinfo.getOperations()
[...]
js>for(var i = 0; i < exampleops.length; i++){echo(exampleops[i]);}
ModelMBeanOperationInfo: findMappings ; Description: Return the mappings associated with this wrapper ; [...]
ModelMBeanOperationInfo: findMappingObject ; Description: Return an object which may be utilized for mapping to this component ; [...] 
js>examplemapping=mbeanConnection().invoke(exampleservlet, "findMappings", null, null)
[Ljava.lang.String;@11d44ee
js>for(var i= 0; i < examplemapping.length; i++){echo(examplemapping[i]);}
/servlet/HelloWorldExample

Tehát böngészőből ez a servlet a http://<hosztnév>:8080/servlets-examples/servlet/HelloWorldExample címen érhető el (ha a 8080-as portra van egy Tomcat HTTP konnektor konfigurálva). A servlet ugyan a JSR-77 értelemben véve nem deklarálja hogy teljesítményadatokat nyújtana, de az attribútumok között láttunk olyanokat, melyek pont ezt teszik. Például a requestCount:

js>mbean(exampleservlet).requestCount
3

Vagy a loadTime:

js>mbean(exampleservlet).loadTime
16

Mi ezeknek a nem szabványos attribútumoknak a pontos jelentése (és a második esetben mértékegysége)? Ehhez általában (hacsak az adott eszköznek nincs használható, a felügyeleti modellt leíró dokumentációja) az MBean-ként reprezentált objektumok valódi megvalósító osztályainak a dokumentációjához kell fordulnunk. Ez az MBean modelerType attribútumából kiolvasható, a vizsgált esetre org.apache.catalina.core.StandardWrapper. Ennek a dokumentációja viszont erősen foghíjas, így a loadTime-ról a forráskódból derül csak ki, hogy milliszekundumban mérve az az idő, ami alatt egy új kiszolgáló szál létrejön mely képes kérések végrehajtására (főleg a konténer környezet kialakítását, a megfelelő osztály dinamikus betöltését és a servlet inicializáló metódusának végrehajtását tartalmazza).

A servlet-et tartalmazó web modul (Catalina:j2eeType=WebModule,name=//localhost/servlets-examples,J2EEApplication=none,J2EEServer=none) részben beállítja be a szabvány által definiált attribútumokat és ezek mellett még rendelkezik másokkal is. Metódusaival a web modult inicializálni lehet, elindítani, leállítani és megsemmisíteni.

A Tomcat a servlet és web modul MBean-eken kívül még számos más típusú MBean-t is kiajánl; egy alapértelmezett beállításokkal indított Tomcat 5.5 esetében összesen 215 MBean van beregisztrálva:

js>mbeanConnection().getMBeanCount()
215

A nem szabványos MBean-ek által megvalósított felügyeleti modell azonban a Tomcat architektúráját adja vissza, így tárgyalásukhoz először azt is be kellene mutatni részleteiben.


13 Kimaradtak, pedig fontos lenne

Koncepcionálisan:

  • "agent service" MBean-ek (timer, monitor, dynamic class loader...)

F/OSS technológiák:

  • a Spring keretrendszer és a JMX (más néven deklaratív JMX)
  • MC4J és MX4J
Személyes eszközök