JMX(Java Management Extensions)는 J2SE 5.0 이후로 계속 JDK에 포함되었습니다. JDK 6에서 썬은 새롭고 진보된 JMX 샘플을 JDK sample 디렉토리에 추가했습니다. 새로운 샘플($JDK_HOME/sample/jmx/jmx-scandir)은 JMX best practices를 바탕으로 하며, JMX를 이용하여 관리 인터페이스를 설계할 때의 몇 가지 보편적인 JMX 패턴과 피해야 할 몇 가지 함정에 대해서도 검토합니다.
특히 jmx-scandir 샘플은 MBeanRegistration 인터페이스를 구현함으로써 해결될 수 있는 여러 가지 용례를 보여줍니다.
본 테크팁은 그러한 용례에 초점을 둘 것입니다 : MBeanRegistration 인터페이스를 사용한 MBean(Management Bean) 라이프 사이클 관리. JMX와 MBean의 개념에 익숙하지 않은 독자는 더 나아가기 전에 자바 튜토리얼에서 JMX trail을 살펴보십시오.
MBeanRegistration 인터페이스
MBeanRegistration 인터페이스는 콜백 인터페이스입니다. 그것은 MBean으로 구현될 수 있으며, 이 때 MBeanServer로 등록 또는 등록 해제할 때 특정한 액션을 수행해야 할 필요가 있습니다. MBean이 MBeanRegistration을 구현할 때, 그 인터페이스에 정의된 메소드는 MBean이 MBeanServer로부터 등록 및 등록 해제하기 전과 후에 MBeanServer에 의해 호출됩니다.
MBeanRegistration 인터페이스를 구현하는 MBean은 자신이 등록될 MBeanServer로의 레퍼런스를 획득하며, 등록되기 전에 자신의 ObjectName을 획득할-그리고 아마도 변경할- 수 있게 되며, 자기 자신의 등록 또는 등록 해제를 수락 또는 거부할 수도 있을 것입니다.
다음 코드에서는 Rack MBean이 MBeanRegistration 인터페이스를 구현합니다.
------------------------------------------------------------------
package jmxtechtip;
public interface RackMBean {
/... defines methods exposed for management .../
}
------------------------------------------------------------------
package jmxtechtip;
public class Rack implements RackMBean, MBeanRegistration {
/... implements RackMBean methods .../
/... implements MBeanRegistration methods .../
}
------------------------------------------------------------------
MBean 인터페이스는 MBeanRegistration을 확장(extend)하지 않습니다. 오직 실제의 MBean 클래스가 그것을 구현(implement)합니다. 실제로, MBeanRegistration 메소드는 등록 시에 오직 MBeanServer 에 의해서만 호출되는 것으로 설계되었으므로 관리 인터페이스를 통해 MBeanRegistration에 정의된 메소드를 노출하는 것은 위반에 해당합니다.
이 테크팁에서는 종속 MBean들을 등록 및 등록 해제하기 위한 MBeanRegistration의 메소드를 구현하는 방법을 제시합니다.
랙과 카드의 용례
하드웨어의 일부를 노출할 JMX 애플리케이션을 설계하고 있다고 가정해 봅시다. 장비에는 랙이 하나 있습니다. -- RackMBean으로 나타냅니다. 랙 자체는 카드를 꽂을 수 있는 다수의 슬롯을 가지고 있습니다. 각 카드가 꽂히게 되면 CardMBean 또는 CardMBean의 서브클래스로 표시됩니다.
이 예에서 RackMBean이 CardMBean 인스턴스를 생성하고 등록하는 것에 대해 완전히 책임이 있다고 가정합니다. 클래스 Card는 Card에 특정한 구현을 제공하도록 확장될 수 있습니다. 클래스 Rack은 자신의 createCard(...) 메소드를 override하기 위해 확장(extend)될 수 있습니다.
다음은 사용자가 이루고자 하는 목표입니다.
* Rack 클래스에서 RackMBean이 등록될 때, 어떤 슬롯이 이미 카드를 갖고 있는지를 알아내고, 그들 각각에 대해 CardMBean을 생성 및 등록합니다.
* Rack 클래스에서 RackMBean이 등록 해제될 때, 모든 그에 대응하는 Card 인스턴스들도 등록 해제합니다.
* Card 클래스에서 CardMBean은 오직 Rack에 의해서만 등록될 수 있음을 확실히 하고, CardMBean은 예를 들어 CardMBean에 대해 직접적으로 MBeanServer.unregisterMBean(...)을 호출하는 클라이언트 JMX 애플리케이션에 의해서가 아니라 오직 Rack에 의해서만 등록 해제될 수 있음을 분명히 하십시오.
참고: 여기서의 의도는 랙 또는 카드를 모델링하는 방법을 보여주는 것이 아닙니다. 저자는 오직 예를 들기 위해서 랙과 카드를 선택했습니다. 왜냐하면 그들의 관계가 이해하기 쉽기 때문입니다.
MBeanRegistration 인터페이스 구현
다음 코드는 Rack MBean이 MBeanRegistration 인터페이스를 구현하는 방법을 보여줍니다.
/**
* A Rack contains Cards.
*/
public class Rack implements RackMBean, MBeanRegistration {
// MBeans must be multi-thread safe.
// 'rackName' and 'server' will be set at registration time, so you
// must therefore take care of synchronization issues.
// Use 'volatile' to make sure that you always use an accurate value.
//
// Name of this rack, extracted from the ObjectName.
private volatile String rackName;
// The MBeanServer in which this MBean is registered
private volatile MBeanServer server;
// An array of slots that may be occupied (and contain a card) or may be
// unoccupied. slots[i]==null means that slot 'i' is unoccupied.
private final Card[] slots;
/** * Creates a new rack.
* @param slotCount Total number of slots. **/
public Rack(int slotCount) {
if (slotCount < 0)
throw new IllegalArgumentException(Integer.toString(slotCount));
this.slots = new Card[slotCount];
...
}
// --------------------------------------------------------------------
// *Implementation of the MBeanRegistration Interface*
// --------------------------------------------------------------------
/**
* Allows the MBean to perform any operations it needs before
* being registered in the MBean server.
* If any exception is raised, the MBean will not be registered
* in the MBean server.
*
* RackMBean uses this method to check the validity of the supplied
* ObjectName, and in particular that it corresponds to a valid rack.
*
* It also uses this method to obtain a reference to the MBeanServer in
* which it is registered.
*
* @param server -- The MBean server in which the MBean will be registered.
* @param name -- The object name of the MBean. In this implementation, the
* supplied name must not be null.
*
* @return -- The name under which the MBean is to be registered. If null,
* the registration will fail.
*
* @throws Exception -- This exception will be caught by the MBean
* server and rethrown as an MBeanRegistrationException.
*/
*public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {*
// A null ObjectName is not allowed. Let MBeanServer throw
// the exception.
if (name == null) return null;
// Get rackname.
rackName = name.getKeyProperty(RACK_KEY);
if (rackName == null)
throw new MalformedObjectNameException("missing rack name: " + name);
if (!isValidRack(rackName))
throw new IllegalArgumentException(rackName + ": not a valid rack");
this.server = server;
return name;
}
/**
* Allows the MBean to perform any operations needed after having
* been registered in the MBean server or after the registration
* has failed.
*
* If the registration is successful, the Rack MBean will
* register all its related CardMBeans.
*
* @param registrationDone -- Indicates whether or not the MBean
* has been successfully registered in the MBean server.
* The value false means that the registration has failed.
*/
*public void postRegister(Boolean registrationDone) {*
if (!registrationDone) return;
for (int i = 0; i < slots.length; i++) {
final String cardType = discoverCardType(i);
if (cardType != null) {
try {
final Card card = createCard(i, cardType);
final ObjectName cardName = createCardName(rackName, i);
// Avoid calling an MBeanServer operation
// from within a synchronized block.
card.registerSelf(server, cardName);
synchronized (slots) {
slots[i] = card;
}
} catch (Exception x) {
LOG.warning("Couldn't create CardMBean for " +
cardType + "[" + i + "]");
}
}
}
}
/**
* Allows the MBean to perform any operations it needs before being
* unregistered by the MBean server
*
* The RackMBean uses this method to unregister all its related
* CardMBeans. If for some reason, unregistration of one of these
* MBeans fails, the RackMBean will no be unregistered either.
*
* @throws Exception -- This exception will be caught by the MBean
* server and rethrown as an MBeanRegistrationException.
*/
*public void preDeregister() throws Exception {*
for (int i=0; i<SLOTS.LENGTH; class="" implementation="" i++) card; if (card="=" null) continue; Avoid calling MBeanServer operation from within block. card.unregisterSelf(); synchronized (slots) slots[i]="null;" Allows perform any operations needed having unregistered implementation, simply variable. *public void postDeregister() {* In case, will garbage collected after it?s been unregistered. Here make assumption object might reused application code created it, you thus reset internal variables value they had before its registration MBean server. choice and not general rule. server *End MBeanRegistration* Discovers what kind If no plugged in, returns null. discoverCardType(int Check provided rackName="null;" corresponds that can modeled class. boolean isValidRack(String rackName) ... Create instance cardType given slot. Can be overridden by create subclasses of Card. protected Card createCard(int i, cardType) new Card(i, cardType); Static stuff ------------------------------------------------------------ Creates an the card="slots[i];" contained ?rackName? * at slot ?slot?. createCardName(String rackName, int slot) throws MalformedObjectNameException { domain="Card.class.getPackage().getName();" type="Card.class.getSimpleName();" ObjectName ObjectName(domain ?:type=" + type + " ,? ,slot=" + slot);
return name;
}
/**
* Returns the rack name embedded in the given ObjectName.
**/
public static String getRackName(ObjectName name) {
final String rackName = name.getKeyProperty(RACK_KEY);
if (rackName == null)
throw new IllegalArgumentException(" missing name: ?=" + rackName + " + name); return rackName; A logger for this private final Logger LOG="Logger.getLogger(Rack.class.getName());" This key is used in ObjectNames to indicate a rack name="new" ** public static String RACK_KEY ; } < pre>
preRegister <#preRegisterRack>에서 Rack MBean은 그것이 등록된 MBeanServer로의 레퍼런스와 자기 자신의 ObjectName에 대한 레퍼런스를 획득합니다. 그것은 제공된 ObjectName이 유효한지 체크할 수 있으며 -- 이 예에서는 그것이 rack 키를 포함하고 있으면 유효한 것으로 간주함 -- 그것은 실제로 자신이 모델링하기로 요청 받은 랙을 모델링할 수있다는 것을 체크할 수 있습니다. 여기서 이것은 isValidRack(...) 메소드에 의해 행해지게 되어 있으며, 이 메소드의 구현은 표시되지 않습니다. 이러한 조건이 맞지 않으면 Rack MBean은 exception을 발생시킬 것이며 따라서 등록이 거부될 것입니다.
postRegister <#postRegisterRack>에서 Rack MBean은 등록 성공 여부를 확인합니다. 실패였으면 여기서 중단합니다. 그렇지 않으면 그것은 어느 슬롯이 이미 카드가 꽂혀있는지를 알아내고 그것들 각각에 대해 CardMBean을 생성 및 등록합니다. 그러나 일반적인 MBeanServer.registerMBean(...)을 사용하는 대신, 생성된 Card 인스턴스에 대한 package-protected 메소드를 호출합니다. 이 예에서는 Card.registerSelf() <#registerSelf>입니다.
이 메소드는 로직과 함께 생성되었기 때문에 CardMBean은 그것의 registerSelf(...) 메소드를 호출하여 MBeanServer 에 단지 등록만 될 수 있습니다.
postRegister는 exception을 발생시키지 않도록 되어 있다는 점에 유의하십시오. 따라서 모든 Card MBean 등록을 try { } catch { } 블럭으로 보호했습니다. 애플리케이션 로직에 따르면 Card MBean 등록은 이 시점에서 실패하지 않을 것이므로 이렇게 해도 괜찮습니다. 만약 그렇다면, 단순히 경고 메시지만 기록할 것입니다. Had you wanted the Card MBean이 등록 실패일 때마다 Rack MBean 등록도 실패하도록 해야한다면, 대신 preRegister <#preRegisterRack>에서 Card 등록 로직을 구현했을 것입니다. 그렇게 되면 그 로직은 더 복잡해졌을 것입니다. 왜냐면 그들 중 하나가 등록에 실패한 경우에 이미 등록된 Card MBeans의 등록을 rollback 하는 코드를 구현해야 하기 때문입니다.
preDeregister <#preDeregisterRack>에서, Rack MBean은 그것이 전에 등록했던 모든 Card MBean들을 등록 해제합니다. 다시 여기서, 일반적인 MBeanServer.unregisterMBean(...)을 사용하는 대신, 등록된 Card 인스턴스에 대한 package-protected 메소드를 호출합니다. 이 예에서는 Card.unregisterSelf() <#unregisterSelf>입니다.
이 메소드는 로직과 함께 생성되었기 때문에 Card MBean은 그것의 unregisterSelf(...) 메소드를 호출하여 MBeanServer로부터 오직 등록 해제만 될 수 있습니다. 단순히 MBeanServer.unregisterMBean(...)을 호출하면 실패일 것입니다. 이는 Card MBean의 생성자만이 그것을 등록 및 등록 해제할 수 있다는 것을 확실히 합니다. JMX 클라이언트가 Card MBean 상에서 MBeanServer.unregisterMBean(...)을 직접적으로 호출하려고 하면 이는 실패합니다.
이제 어떻게 Card MBean이 MBeanRegistration 인터페이스를 사용하는지를 살펴봅시다. 다음 코드는 어떻게 Card MBean이 구현되었는지를 보여줍니다.
/**
* A Card is plugged in a Rack slot.
*/
public class Card implements CardMBean, MBeanRegistration {
// This MBean will accept to be registered or unregistered
// only if 'isRegistrationAllowed' is 'true'.
private volatile boolean isRegistrationAllowed = false;
private volatile ObjectName name;
private volatile MBeanServer server;
public Card(int occupiedSlot, String cardType) { ... }
// This method sets 'isRegistrationAllowed' to 'true', calls
// server.registerMBean(this, name), and finally sets
// 'isRegistrationAllowed' back to false.
//
*void registerSelf(MBeanServer server, ObjectName name) throws JMException {*
*isRegistrationAllowed = true;*
try {
this.server = server;
this.name = name;
this.name = server.registerMBean(this, name).getObjectName();
} finally {
*isRegistrationAllowed = false;*
}
}
// This method sets 'isRegistrationAllowed' to 'true',
// calls server.unregisterMBean(name), and finally sets
// 'isRegistrationAllowed' back to false.
//
*void unregisterSelf() throws JMException {*
*isRegistrationAllowed = true;*
try {
if (server != null && server.isRegistered(name))
server.unregisterMBean(name);
this.server = null;
this.name = null;
} finally {
*isRegistrationAllowed = false;*
}
}
/**
* Allows the MBean to perform any operations it needs before being
* registered in the MBean server. *
* *In this example, the CardMBean will refuse to be registered if
* 'isRegistrationAllowed' is false -- which means that Card MBeans
* can only be registered by {@link #registerSelf registerSelf()}.*
*
* {@code registerSelf()} is a package method which is only called by
* {@link Rack}.
*/
*public final ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {*
if (*!isRegistrationAllowed*)
throw new IllegalStateException("Illegal registration attempt");
assert server == this.server && name == this.name;
return name;
}
/**
* Allows the MBean to perform any operations needed after having
* been registered in the MBean server or after the registration has
* failed.
* *The CardMBean does not need to do anything here.*
* @param registrationDone -- Indicates whether or not the MBean
* has been successfully registered in the MBean server. The value
* 'false' means that the registration has failed.
*/
*public void postRegister(Boolean registrationDone) {}*
/**
* Allows the MBean to perform any operations it needs before
* being unregistered by the MBean server.
* *The CardMBean will refuse to let itself be unregistered if
* 'isRegistrationAllowed' is false, which means that Card MBeans
* can only be unregistered by {@link #unregisterSelf unregisterSelf()}.*
*
* {@code unregisterSelf()} is a package method which is only called by
* {@link Rack}.
*
* @throws Exception -- This exception will be caught by the MBean
* server and rethrown as an MBeanRegistrationException.
*/
*public final void preDeregister() throws Exception {*
if (*!isRegistrationAllowed*)
throw new IllegalStateException("Illegal unregistration attempt"); }
/**
* Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server.
* *The CardMBean does not need to do anything here.*
*/
*public void postDeregister() { }*
}
preRegister <#preRegisterCard>, preDeregister <#preDeregisterCard>, registerSelf <#registerSelf> 및 unregisterSelf <#unregisterSelf>에서의 로직은 Card MBean이 그것을 생성한 Rack MBean이 아닌 누군가에 의해 등록 또는 등록 해제 되는 것을 방지하는 것입니다. registerSelf 및 unregisterSelf 메소드는 package-protected 상태를 유지하므로 Rack MBean 만이 이를 호출할 수 있습니다. preRegister 및 preDeregister 메소드는 final로 선언되었기 때문에 서브클래스들이 해당 로직을 변경할 수 없습니다.
참고: Card의 등록 또는 등록 해제 시 서브클래스에서 특정 추가 액션을 수행해야 하는 경우, Martin Fowler's note에서 설명하는 대로 목적에 맞는 새로운 protected hook을 사용할 수 있습니다.
그러나 단순한 volatile boolean isRegistrationAllowed 플래그를 사용하게 되면 쓰레드 경합(thread contention)에 대한 약간의 여지가 여전히 남게 됩니다. 실제로 Rack MBean이 unregisterSelf <#unregisterSelf>를 호출할 때, 다른 쓰레드가 살짝 들어와서 isRegistrationAllowed 플래그는 설정된 직후이지만 unregisterSelf가 완료되기 전에 MBeanServer.unregisterMBean을 호출할 수도 있습니다.
따라서 더 나은 솔루션으로는 isRegistrationAllowed 플래그를 ThreadLocal로 저장하는 방법이 있습니다.
다음 코드에서는 Card MBean 구현에서 변경이 필요한 사항을 보여줍니다.
// initialization of isRegistrationAllowed flag:
// private volatile boolean isRegistrationAllowed = false;
// => replaced by:
private final static ThreadLocal isRegistrationAllowed = new ThreadLocal() {
@Override protected Boolean initialValue() {return false;}
};
// in registerSelf / unregisterSelf
....
// isRegistrationAllowed = true;
// => replaced by:
*isRegistrationAllowed.set(true);*
try {
...
} finally {
// isRegistrationAllowed = false;
// => replaced by:
*isRegistrationAllowed.set(false);*
}
....
// in preRegister / preDeregister
...
// if (!isRegistrationAllowed)
// => replaced by: if (*!isRegistrationAllowed.get()*)
throw new IllegalStateException(...);
...
결론
이 테크팁은 MBean이 종속 MBean을 등록 및 등록 해제하기 위해 MBeanRegistration 인터페이스를 어떻게 이용하는지를 소개했습니다. 또한 생성자만이 이를 등록 및 등록 해제할 수 있도록 하는 방법을 통해 종속 MBean를 보호할 수 있다는 것도 살펴 보았습니다.
JDK 6용 고급 JMX 샘플($JDK_HOME/sample/jmx/jmx-scandir)에서는 MBeanRegistration 인터페이스 사용에 대해 많은 다른 가능성들을 제시합니다.
저자 정보
Daniel Fuchs는 프랑스 Grenoble 소재 썬마이크로시스템즈의 Java DMK, JMX, Java SE 팀에서 일하고 있습니다.
* 이 아티클의 영어 원본은
http://blogs.sun.com/corejavatechtips/e ··· rface_to에서 보실 수 있습니다.
"Java SE" 카테고리의 다른 글
- STRINGTOKENIZER에서 SCANNER까지 (댓글 2개 / 트랙백 1개) 2005/04/26
- 정적 인스턴스 초기화 블록 사용하기 (댓글 2개 / 트랙백 0개) 2004/04/13
- QUEUE와 DELAYED 프로세싱 (댓글 3개 / 트랙백 0개) 2004/11/04
- Callable을 사용하여 Runnable로부터 결과 반환 (댓글 0개 / 트랙백 0개) 2008/02/20
- 가비지 콜렉션 (댓글 3개 / 트랙백 0개) 2004/06/30
- 스윙에서의 멀티 쓰레딩 (댓글 2개 / 트랙백 0개) 2003/12/12
- SWING COMPONENTS의 저장과 재구성 (댓글 5개 / 트랙백 0개) 2003/08/05
- 스플래시 스크린과 MUSTANG (댓글 1개 / 트랙백 2개) 2005/12/20
- WSIT에서의 지원 토큰과 발급된 토큰 위임 (댓글 20개 / 트랙백 2개) 2007/09/03
- Singleton 패턴에 대한 재고찰 (댓글 4개 / 트랙백 1개) 2006/04/21
TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/484
-
MBeanRegistration 인터페이스를 사용한 MBean 의라이프 사이클 관리
Tracked from Kyeseon Development Blog 1.0 삭제Rack과 그 안의 Card의 예를 통해 MBean의 라이프 사이클을 이해하기 쉽도록 한 좋은 예제라고 생각한다.또한 지금 구상하고 있는 솔류션에 MBean을 어떻게 구성하고 MBean의 라이프 사이클을 어떻게 관리할 것인가를 고민하고 있었는데 좋은 아이디어와 정보를 제공해준 글이라고 생각된다. 실 세계의 Rack과 Card를 Modeling을 통해 어떻게 추상화하는지에 대해서도 살짝 엿볼 수 있었다. URL: http://sdnkorea.com/b..
2008/01/24 13:46
댓글을 달아 주세요