Eine EJB als JMX MBean für JBoss AS erstellen

Die Java Management Extensions (JMX) sind eine sehr gute Wahl um Aufgaben zentraler Natur zu steuern, eine Anwendung von außen zu beeinflussen oder zu überwachen. So könnten zum Beispiel das Logverhalten verändert oder administrative Anwendungsfunktionen ausgeführt werden ohne den Betrieb der Anwendung zu stören.

Um JMX zu nutzen werden sogenannte MBeans (Managed Beans) erstellt welche über einen Adapter, wie zum Beispiel einen HTTP Adapter, über den Web Browser zugänglich gemacht werden. Mit Java 5 kommt auch noch die Möglichkeit auf die JConsole zu verwenden um auf MBeans zuzugreifen. Mehr zu den Grundlagen der Java Management Extension auch in der online Ausgabe Java ist auch eine Insel – Kapitel 23.

JBoss JMX MBean View

Der JBoss AS (Application Server) steuert seine Konfigurationen über einen eigenen JMX Bus und bietet dazu eine Menge an MBeans an – siehe dazu auch aus dem JBoss AS Guide – Kapitel 2 – The JBoss JMX Microkernel. Weil die JMX Unterstützung eine Kernkomponente für den JBoss ist bietet er einfache Möglichkeiten an diese Infrastruktur für die eigenen Zwecke zu verwenden. Wie man diese am einfachsten verwenden und selbst eine JMX MBean schreiben kann soll im folgenden gezeigt werden.

Dieser Artikel ist in die folgenden Abschnitten unterteilt:

Möchte man eine JMX Bean für die eigene Anwendung, welche auf dem JBoss AS (Application Server) läuft, schreiben so bietet JBoss mehrere Möglichkeiten an. Folgende drei Möglichkeiten bestehen eine MBean für die JMX Konsole zu implementieren und zu registrieren:

Obwohl ich nur auf die dritte und letzte Möglichkeit eingehen möchte, werden vorab die beiden anderen Varianten kurz erwähnt, so dass man diese einordnen kann.

JBoss Standard MBean

Für die erste Variante implementiert man einen JBoss MBean Service (auch Standard MBean genannt). Dazu erstellt man ein Interface welches sich von org.jboss.system.ServiceMBean sowie eine Klasse die dieses Interfaces implementiert. Diese MBean Service Klasse ist selbst von der Klasse org.jboss.system.ServiceMBeanSupport abgeleitet ist. Diese Klasse wird nun in der Datei jboss-service.xml als MBean konfiguriert und so dem JBoss AS bekannt gemacht.
Ein kleines “Hello World Service” Tutorial für das Erstellen eines solchen JBoss MBean Services findet sich im JBoss Wiki.

JBoss XMBean

Die zweite Variante basiert auf der eben vorgestellten, nur dass die MBean über das JBoss XMBean Framework seine Metadaten beschreibt. An dieser Stelle kommt die Möglichkeit einfache POJOs als MBeans zu verwenden ins Spiel und es ist nicht mehr notwendig das oben beschriebene Interface ServiceMBean zu implementieren oder andere Regeln in der MBean Klasse einzuhalten. Allerdings wird für diese sogenannte XMBean Deployment Descriptor in Form eines XML Dokuments benötigt. Aus diesem Grund heißt diese MBean XMBean.
In diesem Deplyoment Descriptor können sämtliche Attribute, Methoden und deren Parameter beschrieben werden. Ein weiter Vorteil war die Möglichkeit Interceptor-Methoden zu implementieren. Diese konnte man zum Beispiel für Sicherheitsüberprüfungen bei Methodenaufrufe verwenden. Eine schöne ausführliche Beschreibung der XMBean findet sich auf dem Blog von organig thoughts.

Die hier eben kurz erwähnten Wege eine JBoss MBean bzw. XMBean (beides sind JMX MBean) zu erstellen können auch noch einmal in der offiziellen JBoss Server Configuration Guide im Kapitel 3.4.3 mit dem Titel “Writing JBoss MBean Services” nachgelesen werden.

Die dritte Variante ist die in diesem Artikel eigentlich fokussierte Variante mit dem Namen JBoss Service POJOs.

Die einfachste Variante eine JBoss MBean für die JMX Konsole zu erstellen ergibt sich durch die JBoss eigene @org.jboss.annotation.ejb.Service Annotation. Diese Annotation @Service definiert im JBoss einen Service der als Singleton, also nur mit einer Instanz (Singleton), verfügbar ist. So ergibt sich der erste Schritt daraus, einen solchen Service zu erstellen.

import org.jboss.annotation.ejb.Service;

@Service(objectName="MyApplication:service=MyService")
public class MyServiceMBean {

}

Noch einmal: die Besonderheit an dieser Service Bean besteht darin, dass auf dem Server nur eine Instanz davon besteht. Im Gegensatz zu EJB Services ist ein MBean Service nicht gepoolt, behält seinen Status (stateful) und hat application-scope. Letztlich ist dies aber eine normale Session Bean, von der lediglich nur eine Instanz besteht. Man arbeitet somit in dem Context der Anwendung und kann auf alle konfigurierten Resourcen, wie z.b. den EntityManager oder andere EJBs, zugreifen.

Um diese Service EJB nun noch JMX fähig zu machen und sie an einem MBean Server anmelden zu können, muss nun noch ein Management Interface erstellt und der Service Bean durch die @org.jboss.annotation.ejb.Management Annotation bekannt gemacht werden.

Hier das Management Interface, welche die Getter und Setter für das Attribut “lastWrittenText” sowie die Service-Methode printOut() deklariert, die später über die JMX Konsole aufgerufen werden können.

public interface MyManagementInterface {

  public String getLastWrittenText();
  public void setLastWrittenText(String lastWrittenText);

  void printOut(String text);
}

Die erstellte Service MBean “MyServiceMBean” muss dieses Interface nun noch implementieren und zudem das gleiche Interface durch die @Management Annotation bekannt machen. Erst durch diese @Management Annotation wird der JBoss aus den beschriebenen Attributen und Methoden eine Service MBean erstellen und registrieren. Im folgenden der vollständige Source Code für den POJO Service “MyServiceMBean“:

import org.jboss.annotation.ejb.Management;
import org.jboss.annotation.ejb.Service;

@Service(objectName="MyApplication:service=MyService")
@Management(MyManagementInterface.class)
public class MyServiceMBean implements MyManagementInterface {

  private String lastWrittenText;

  public void printOut(String text) {
    System.out.println("Given text: " + text);
    lastWrittenText = text;
  }

  public String getLastWrittenText() {
    return lastWrittenText;
  }

  public void setLastWrittenText(String lastWrittenText) {
    this.lastWrittenText = lastWrittenText;
  }
}

In dem Code ist zu sehen, das bei der @Service Annotation noch ein Objektname zu finden ist. Dieser dort definierte Name erscheint nach dem Starten des JBoss und dem erfolgreichem deployen der Anwendung in der JMX Konsole (Aufzurufen durch http://localhost:8080/jmx-console/). Im folgenden Screenshot findet man die definierten Namen wieder:

Selektiert man diesen Service gelangt man in die Service View, in der man alle Attribute und Methoden des Services sehen und aufrufen kann. In diesem Beispiel soll die Methode printOut(String) aufgerufen werden:

Nach der Eingabe des Methodenparameters “test string” und dem anschließenden Klicken des Buttons “invoke” erscheint auch, wie erwartet, der übergebene Text in der Server Konsole:


23:08:51,882 INFO [Server] JBoss (MX MicroKernel) [4.2.2.GA (build: SVNTag=JBoss_4_2_2_GA date=200710221139)] Started in 21s:386ms
22:09:12,668 INFO [STDOUT] Given text: test string

Leider hat man durch die Verwendung der Annotationen keine Möglichkeit die Service Bean, Attribute, Methoden oder Paramter zu beschreiben, wie es bei der ursprünglichen XMBean Variante noch als großer Vorteil angepriesen wurde.
Die gute Nachricht ist, dass auch hier eine Möglichkeit besteht diese Beschreibungen zu liefern. Der Nachteil an der Sache ist, man muss wieder auf eine XML Beschreibung zurückgreifen. Und zwar kommt wieder der XML Deployment Descriptor der XMBeans ins Spiel. Man kann genau diesen in der @Service Annotation angeben.

@Service(objectName="MyApplication:service=MyService",
		 xmbean="resource:META-INF/myService-xmbean.xml")
public class MyServiceMBean  implements MyManagementInterface {
  ...
}

Wichtig ist, dass die @Management Annotation entfernt werden muss weil nun wieder der XMBeans Deployment Descriptor die Beschreibung der MBean übernimmt. In diesem Beispiel heißt der Deployment Descriptor myService-xmbean.xml und liegt im META-INF Ordner, direkt neben der persistence.xml Datei. Wenn die Datei nicht gefunden wird schmeißt der JBoss AS während des Startens Exceptions, so dass der Fehler schnell bemerkt wird.

Der XMBeans Deployment Descriptor myService-xmbean.xml sieht für die oben gelistete POJO Service Klasse wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mbean PUBLIC "-//JBoss//DTD JBOSS XMBEAN 1.0//EN"
  "http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_0.dtd">


  javathreads.de - MyService MBean Beschreibung

  de.webthreads.tippit.mgmt.MyServiceMBean

  
  
    Der zuletzt ausgegebene Text wird hier gehalten.
    LastWrittenText
    java.lang.String
  

  
  
    Methode um einen Text auf der Konsole auszugeben.
    printOut

      Der Text der ausgegeben werden soll.
      text
      java.lang.String
    
    void
  

In der XML Datei sind die Beschreibungen für das Attribut sowie der Methode zu sehen. Die DTD des mbean Elements sieht wie folgt aus:

Schaut man sich jetzt die JMX Konsole an sind die Beschreibungen nun auch vorhanden, wie man im folgenden Screenshot sehen kann:

Ich habe in diesem Screenshot mal die Abhängkigkeit zur XMBean hervorgehoben. Vergleicht man diese Zeile mit dem Screenshot der JMX MBean View der MBean, die mit der @Management erstellt wurde, so sieht man dort die Zeile: MBean Java Class: de.javathreads.jbosstest.mgmt.MyManagementInterface

Auch so kann man also erkennen welcher Weg gegangen wurde um eine MBean für die JMX Konsole zu erstellen.

Fazit

Insgesamt war das Erstellen einer solchen Service MBean im JBoss so einfach, dass ich es hier kurz dokumentieren musste =). Der Weg dahin hat doch etwas länger gedauert aber dadurch bin ich über einige interessante Seiten gestolpert, wie zum Beispiel das JBoss Wiki – JMX FAQ oder das wirklich gute Tutorial von JBoss Trailblazer. Nach dem lesen und googeln habe ich es ausprobiert und es hat tatsächlich (fast) auf Anhieb funktioniert.
Und jetzt während ich diesen Artikel fast zu Ende geschrieben habe entdecke ich noch eine Blitzanleitung auf dem Blog von Christian Ullenboom (Author von Java ist auch eine Insel) mit einer Schnelleinführung..

Und gerade weil das Erstellen eines solchen JMX MBean Service eigentlich so einfach ist sollten diese Erkenntnisse hier festgehalten werden.

Updates
[27.06.] Aufgrund einer Nachfrage im Kommentar wurde der Abschnitt “Verschiedene Arten der JBoss MBean Services” und der letzte Abschnitt “Management Interface mit XML Beschreiben” nachträglich in diesem Artikel eingefügt.
[21.07.] XMBean DTD Grafik und Link zur JBoss Doku hinzugefügt.
Tags: , , , , ,

Wenn du Fragen oder Anregungen zum Post hast, dann hinterlasse doch einen Kommentar oder wenn du weiterhin Artikel von Javathreads lesen möchtest, dann abonniere den RSS Feed und sehe direkt in deinem Feed Reader die nächsten Artikel.

Ähnliche Artikel, die dich interessieren könnten:
Kommentare

Das ist ja wirklich ganz einfach.
Aber wie kann ich denn meine Parameter und Methoden mit einer Description versehen, damit in der JMX-Console nicht nur (no description) bzw. der Standardtext steht?
Irgendwie kann ich dazu nichts finden…

Hi Christian,
du hast recht: man sollte eine Beschreibung vergeben können. Definiert man die MBeans via XML ist das wohl auch kein Problem. Mit Annotations scheint es aber nicht zu funktionieren, da es diese Untertützung noch nicht gibt. So eine Aussage habe ich in einem Forum gefunden:

Last I remember in a post from Bill somewhere, a user was asking the same question and the response was “this is not currently supported w/ annotations”.

(http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4072155#4072155).

Mal sehen ob ich die Tage da noch etwas finde.

Hallo,
Du schreibst, wenn man die MBeans via XML definiert ist es wohl kein Problem.
Ich hab grade Google und die JBoss Foren rauf und runter gesucht, aber leider auch dazu keine Info gefunden.
Kannst Du hier noch posten wie man ein MBean Attribut OHNE die Verwendung von Annotations mit einer Description versieht?

Hi Andreas,

ich habe jetzt den Artikel noch einmal überarbeitet und zwei weitere Abschnitte hinzugefügt. Zuerst den Abschnitt “Verschiedene Arten der JBoss MBean Services” um generell die Unterschiede kurz anzureißen und anschließen den Abschnitt “Management Interface mit XML Beschreiben” indem beschrieben wird wie man sein Service MBean doch noch mit einer Beschreibung versehen kann.

Ich hatte erst angefangen hier im Kommentar zu versuchen die Lösung zu beschreiben, fand es aber dann doch besser und praktischer den Artikel anzupassen – ist halt jetzt doch etwas umfangreicher geworden.

Hoffe aber doch dass es jetzt klarer wird =).

Grüße
Markus

Hallo Markus,

der Titel Deines Artikels ist “Eine EJB als JMX MBean für JBoss AS erstellen”, daher hab ich gedacht, dass hier eine EJB zu einer MBean instrumentalisiert wird. Weisst Du, ob das überhaupt möglich und sinnvoll ist? Oder ist es besser über eine MBean auf eine EJB zuzugreifen und diese so zu überwachen?

Viele Grüße Sarah

Hi Sarah,

laut der JBoss Dokumentation reden sie bei der @Service Annotation von einer EJB 3.0 Erweiterung und nennen diese auch EJB: @Service EJB. Kannst du hier nachlesen: http://docs.jboss.org/ejb3/app-server/reference/build/reference/en/html/jboss_extensions.html.

Du kannst an diese Beans wie gewohnt mit @Local und @Remote als EJB den Clients verfügbar machen. Allerdings wird bei einer mit @Service annotierten Bean nur eine Instanz, also Singleton, vorgehalten. Das ist auch die Besonderheit diese @Service EJB.
Die zweite Besonderheit ist, dass man diese @Service EJB mit der @Management Annotation sehr schnell und einfach zu einer MBean, aufrufbar über die JMX Konsole, machen kann.

Sobald du deine normale Session EJB zu einer Service umwandelst wird sie halt vom Server anders behandelt. Ich würde wohl eher eine eigene @Service MBean für verschiedene Service/Admin Funktionen erstellen die dann explizit auf Session Beans zugreift bzw. diese überwacht.

Grüße
Markus

Danke

Hallo Markus,

danke (mal wieder) für die ausführlichen und hilfreichen Informationen.

Nur wie komme ich von einer anderen EJB (z.B. einer Stateless Session Bean) an die Instanz der MBean per Dependency Injection?

@Resource MyManagementInterface mbean;
oder
@Resource MyServiceMBean mbean;
oder
@EJB MyManagementInterface mbean;
oder
@EJB MyServiceMBean mbean;

Irgendwie will mir das nicht gelingen, weisst du Rat?

viele Grüße,
Simon

Hi Simon,
Deine mit @Service annotierte Klasse kann ohne weiters auch ein @Remote oder @Locale Interface implementieren und ist somit im Context als normale EJB registriert.

Du müsstest dann deine Service EJB mit der @EJB Annotation injecten können.

@Local
public interface MyServiceMBeanLocal {
  void doSomething();
}

@Service
public class MyServiceMBean implements MyServiceMBeanLocal {
  public void soSomething(){
    // implement me
  }
}

Und in deiner Sateless Session Bean sollte man diese Service EJB einfach injecten können:

@Stateless
public class OtherSessionBean implements OtherSessionBeanLocal {
  @EJB
  private MyServiceMBeanLocal myServiceMBean;
}

Hab den Code jetzt nicht getestet, sollte (hoffentlich) so funktionieren =).
Hier gibt es auch noch eine ganz kurze dafür aber sehr offizielle Erläuterung zu den JBoss eigenen EJB 3.0 Erweiterungen: http://docs.jboss.org/ejb3/app-server/reference/build/reference/en/html/jboss_extensions.html

Grüße
Markus

Hallo Simon, hallo Markus,

zum Issue @EJB Dependency Injection:
Ich habe genau das selbe Problem wie du Simon, mir gelingt allerdings das von Markus im letzten Posting beschriebene nur in JBoss 4.2.3, nicht jedoch in JBoss 5.0.0.GA.

Der JBoss 5.0.0 zeigt mir die mit @Service annotierte Klasse in der JMX Console weder an, noch kann ich dieses @Service, welches auch ein @Local Interface implementiert, per Dependency Injection in einer anderen SessionBean per @EJB Annotation nutzen. JBoss 5 schreit hier schon beim Deployment:

jboss.j2ee:ear=myear-1.0-SNAPSHOT.ear,jar=myjar-1.0-SNAPSHOT.jar,name=UserServiceBean,service=EJB3
-> {Describ
ed:** UNRESOLVED Demands ‘Class:MyServiceMBeanLocal’ **}

Im 4.2.3er funktionert der gleiche Code aber!
–> Kann es sein, dass ich hier beim JBoss 5 noch irgendwas konfigurieren muss, oder ist das @Service Singleton in JBoss 5 nicht mehr verfügbar, oder handelt es sich um einen Bug im 5er? Ich weiß nicht weiter..

Danke,
Jonas

Erstmal danke Markus, dein Code funktionierte so wie gegeben. Es stellte sich heraus, dass ich ein anderen Fehler gemacht hatte: ich hatte von einer MBean auf ein eine andere EJB zugreifen wollen. Das scheiterte mit einer nichtssagenden Null-Pointer-Exception, doch ich hab dann nach langem Suchen gefunden, woran es lag: es werden beim Deployment erst die @Service-MBeans geladen und dann die EJBs (@Stateless, @Stateful aber, wie es scheint auch die @Local-Interfaces von den MBeans). Mit einem @Depends ging es dann problemlos.

@Jonas: Hier ist auch JBoss 4.2.3 im Einsatz, kann da keine Aussagen zu 5.0.0 machen.

Nützlich bei solchen Problemen finde ich aber immer die MBean jboss:service=JNDIView, die hat eine Methode list(), die kann man über die JMX-Console aufrufen und sehen was er beim Deployment wirklich geladen hat und ob er dazu auch Local/Remote-Interfaces erstellt hat.

Hallo Simon, danke für deine Antwort. Ich kenne die JMX-Console nun zu Genüge ;)
Kannst du mal versuchen, ob du ein @Service Singleton incl @Local Interface per Dependency Injection (@EJB) in einer anderen EJB in JBoss 5 _deployen_ bekommst?! Bei mir funktioniert das Deployment nicht mal, wegen oben beschriebenen Fehler.

Markus, weißt du, wo da das Problem sein kann?

Danke,
Jonas

Nach 2 Tagen Frust möchte ich euch die Auflösung meines Problems, ein @Service Singleton in JBoss 5 zu verwenden, nicht vorenthalten:

@Service funktioniert…
im JBoss 4 (auch) mit folgendem Import
org.jboss.annotation.ejb.Service
im JBoss 5 aber NUR mit
org.jboss.ejb3.annotation.Service

Warum sind in den JBoss Libraries 2 Versionen enthalten frage ich mich?!
Ich hoffe, jemand anders liest das hier, bevor er sich ärgert :-)

Jonas

Hi Jonas,

vielen Dank, dass du die Lösung auch noch hier präsentierst! Hätte erst nächste Woche geschafft mal mit dem JBoss 5 und der @Service Annotation rumzuspielen..

[Edit] Jonas, habe mir erlaubt deine korrigierte Version in deinen usprünglichen Kommentar einzubauen. Nochmal vielen Dank für die Lösung!

Gruß
Markus

Sorry, copy+paste Fehler.. Hier die richtige Variante:

@Service funktioniert…
im JBoss 4 (auch) mit folgendem Import
org.jboss.annotation.ejb.Service
im JBoss 5 aber NUR mit
org.jboss.ejb3.annotation.Service

Mann, der JBoss kostet oft Nerven ;-)

HTH,
Jonas

Hallo Markus,

danke für Deinen tollen Artikel. Alles klappt so wie Du es beschrieben hast. Zwei Sachen sind mir bei der Umsetzung noch aufgefallen:

Gibt es auch die Möglichkeit Attribute mit einem default Wert zu versehen (nicht im java-File)?
Es gibt die Möglichkeit in der …-xmBean.xml
in der Attribute Beschreibung ein Tag anzugeben.
(s.u.) leider hat es keine Wirkung.

Test Text.
Text
java.lang.String

Auch vermisse ich die Kontrolle des ‘Lifecycles’ (create, start, etc). Diese Funktionen werden nur aufgerufen, wenn ich mit @Management arbeite, aber dann fehlen mir wieder die ausführlichen Beschreibungen in der JMX-Console.

Gibt es hierzu eine Lösung?

Danke!

Grüße
Stefan

Hallo Markus,

besten Dank wiedermal für deine informativen Artikel.

Ich wollte heute eine ServiceBean mit Kommentaren versehen, habe dazu eine XML-Datei mit den passenden Kommentaren unter META-INF/mbean angelegt und diese Datei als xmbean in der Service-Annotation angegeben … und mich dann gewundert, warum die Kommentare in der JMX-Konsole nicht sichtbar wurden.

Per Google habe ich mal wieder einen Artikel von Markus gefunden, man kennt den Autor ja ;-)

Und hier habe ich sofort die Antwort gefunden: Die @Management-Annotation muss dafür wieder weg, aha.

… und siehe da, es funktionierte auf Anhieb.

Danke und viele Grüße aus Frankfurt.

Freut mich sehr zu hören! =)

Hinterlasse einen Kommentar