MX4J는 MBean 개발 및 사용을 쉽게 하기 위해 몇가지 유틸리티들을 제공한다.
이런 유틸리티 클래스들은 MX4J가 아닌 다른 JMX 구현들과 함께 사용하려면
압축을 풀고 다시 jar로 묶으면 된다.
(그렇지 않으면 기본적으로 MX4J와 함께 배포된다.)
Invovation이 매우 장황하고(verbose), 호출하려는 MBean에 대해 직접적으로 관계되지 않은 예외상활 처리를
다루어야 하기 때문에 가끔 MBeanServer.invoke
메소드를 호출하는 것이
매우 지루한 일이 될 수 있다.
MBean에 대한 persistence의 MX4J를 예를 들어서 살펴보자 :
예 2.2. 전형적인 MBeanServer.invoke 호출 방법
public interface PersisterMBean { public void store(Object data) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException; ... } public abstract class Persister implements PersisterMBean {} public class MBeanPersister extends Persister { private MBeanServer m_server; private ObjectName m_name; public MBeanPersister(MBeanServer server, ObjectName name) { m_server = server; m_name = name; } public void store(Object data) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException { try { m_server.invoke(m_name, "store", new Object[] {data}, new String[] {"java.lang.Object"}); } catch (ReflectionException x) { throw new MBeanException(x.getTargetException()); } } }
위에서 보았듯이 store의 구현은 MBeanServer.invoke
를
호출한다. 이는 error-prone이며, store 메소드의 호출과 관계없는 exception을 catch하거나
다시 throw 하여야 한다.
그러나, J2SE 1.3에서 소개된 dynamic proxy API 덕택에, mx4j.util.StandardMBeanProxy클래스를 사용하여 위에서 언급한 모든 문제들을 피할 수 있게 되었다. 아래의 예제를 살펴보고, 이 유틸리티 클래스가 얼마나 유용한지 store 구현을 비교해 보자.
예 2.3. Proxy MBeanServer invocation
public class MBeanPersister extends Persister { private MBeanServer m_server; private ObjectName m_name; private PersisterMBean m_proxy; public MBeanPersister(MBeanServer server, ObjectName name) { m_server = server; m_name = name; m_proxy = (PersisterMBean)StandardMBeanProxy.create(PersisterMBean.class, server, name); } public void store(Object data) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException { m_proxy.store(data); } }
이미 보았듯이, Invocation은 이제 가능한 에러들이 컴파일러에 의해 잡힐수 있는 type-safe하게 되었고, 많은 에러 처리를 할 필요도 없어졌다.
StandardMBeanProxy.create
와 반환된 객체를 같은 인터페이스로 cast하기 위한
관리 MBean 인터페이스를 명시해야 하기 때문에, StandardMBeanProxy 클래스는 standard MBean에서 동작한다.
java.lang.Object으로 부터 상속받은 메소드를 호출해서는 안된고, 관리 MBean 인터페이스에
속하는 메소드만 호출하여야 한다.
javax.management.DynamicMBean 인터페이스를 구현하는 MBean을 작성하는 것은 덩치크고 지루한 작업이 될 것이다.
MX4J는 dynamic MBean을 구현하는 기본 클래스로
mx4j.AbstractDynamicMBean 클래스를 제공한다.
이 클래스는 dynamic MBean을 구현할 때 필요한 것들의 지루한 작업들의 대부분을 감당하고 있어서,
개발자는 MBean을 생성할 때 반드시 필요한 정보들을 제공하기 위한 몇가지 메쏘드만 재정의(override)하면 된다.
개발자가 AbstractDynamicMBean 클래스에서 구현해야 할 몇가지 메쏘드들은 2가지로 분류된다. 하나는 DynamicMBean 인터페이스와 AbstractDynamicMBean에서 추가된 메쏘드들이다.
AbstractDynamicMBean는 이미 DynamicMBean 인터페이스의
모든 메쏘드를 구현하였기 때문에 개발자는 이 부분을 다시 재정의할 필요는 없다.
2번째 분류에 속하는 메쏘드들은 MBean 메타 정보들을 제공하기 위해 개발자가 재정의해야 한며
다음과 같다.
createMBeanAttributeInfo
, MBean이 관리가 필요한 속성을 가졌을 때createMBeanOperationInfo
, MBean이 관리가 필요한 메쏘드(operation)를 가졌을 때createMBeanNotificationInfo
, MBean이 관리가 필요한 알림(notification)을 가졌을 때createMBeanConstructorInfo
, MBean이 관리가 필요한 생성자를 가졌을 때getMBeanDescription
3번째 분류에 속하는 메쏘드들은 AbstractDynamicMBean를 상속받은 클래스에 속한 메쏘드들이며 MBean 자신의 기능을 구현하는 메쏘드들이다. (아래의 예제를 확인해보시오.)
예 2.4. AbstractDynamicMBean 클래스 상속하기
public class SimpleDynamic extends AbstractDynamicMBean { /* Method of the second group that is overridden */ protected MBeanAttributeInfo[] createMBeanAttributeInfo() { return new MBeanAttributeInfo[] { new MBeanAttributeInfo("Name", String.class.getName(), "The name", true, true, false) }; } /* Method of the second group that is overridden */ protected String getMBeanDescription() { return "A simple DynamicMBean"; } /* Method of the third group that implements the MBean functionality */ public String getName() { ... } /* Method of the third group that implements the MBean functionality */ public void setName(String name) { ... } }
위에서 봤듯이, DynamicMBean 인터페이스에서 필요한 메쏘드들은 구현하지 않았다. 2번째 분류의 몇가지 메쏘드들만 재정의하는 것으로도 충분하며, 3번째 분류와 관련된 메쏘드들도 제공한다.
일반적으로 개발자들은 AbstractDynamicMBean 클래스를 상속받으면 된다.
만약 MBean이 DynamicMBean를 구현하고
AbstractDynamicMBean를 대신할만한 정도의 클래스를 상속받았다면,
setResource
메쏘드를 호출하는데 주의할 필요가 있다.
(아래의 예제를 보시오.)
예 2.5. AbstractDynamicMBean의 상속받은 클래스로 대신(delegating)하기
public class ComposedDynamic extends MyBaseClass implements DynamicMBean { /* Create an AbstractDynamicMBean subclass */ private AbstractDynamicMBean delegate = new AbstractDynamicMBean() { protected MBeanAttributeInfo[] createMBeanAttributeInfo() { return new MBeanAttributeInfo[] { new MBeanAttributeInfo("Status", int.class.getName(), "The status", true, true, false), new MBeanAttributeInfo("Enabled", boolean.class.getName(), "The enable status", true, false, true) }; } protected MBeanOperationInfo[] createMBeanOperationInfo() { return new MBeanOperationInfo[] { new MBeanOperationInfo("enable", "Enables this MBean", new MBeanParameterInfo[0], Void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("disable", "Disables this MBean", new MBeanParameterInfo[0], Void.class.getName(), MBeanOperationInfo.ACTION) }; } }; private int status; private boolean enabled; public ComposedDynamicMBean() { // Set the actual resource delegate.setResource(this); } /* Implement the methods of DynamicMBean interface to delegate to the AbstractDynamicMBean subclass */ public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { return delegate.getAttribute(attribute); } // Do the same with all other methods of DynamicMBean interface ... /* Methods of the third group that implements MBean functionality */ public void setStatus(int status) { this.status = status; } public int getStatus() { return status; } public boolean isEnabled() { return this.enabled; } public void enable() { this.enabled = true; } public void disable() { this.enabled = false; } }
AbstractDynamicMBean 클래스는 간접검사 관리(non-invasive management)에 주로 사용될 수 있다. 이 관리방법은 여러분이 관리 인터페이스를 구현하여 변경되지 않기를 원하는 어떤 컴퍼넌트를 이미 갖고 있을 때 AbstractDynamicMBean를 상속 받은 클래스에 대해서만 변경할 수 있고 적합한 메타 정보를 제공할 수도 있게 한다.
예 2.6. AbstractDynamicMBean 클래스 상속받기
public class NonInvasiveDynamic extends AbstractDynamicMBean { /* 2번째 분류의 메쏘드들을 재정의 */ protected MBeanOperationInfo[] createMBeanOperationInfo() { return new MBeanOperationInfo[] { new MBeanOperationInfo("start", "Starts this MBean", new MBeanParameterInfo[0], Void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("stop", "Stops this MBean", new MBeanParameterInfo[0], Void.class.getName(), MBeanOperationInfo.ACTION) }; } protected String getMBeanDescription() { return "A non invasive DynamicMBean that manages resource"; } /* 관리할 리소스들을 가져오는 생성자 */ public NonInvasiveDynamic(ExternalService service) { // 이 MBean이 나타내는 실제의 리소스들을 설정한다. setresource(service); } /* Old main, before JMX public static void main(String[] args) throws Exception { // 서비스를 생성한다. ExternalService service = new ExternalService(); // 서비스를 시작한다. service.start(); } */ public static void main(String[] args) throws Exception { // 서비스를 생성한다. ExternalService service = new ExternalService(); MBeanServer server = MBeanServerFactory.createMBeanServer(); NonInvasiveDynamic mbean = new NonInvasiveDynamic(service); ObjectName name = new ObjectName("domain:key=value"); server.registerMBean(mbean, name); // 여기에서 JMX를 통해서 서비스를 시작한다. // Few lines more, but now the service is manageable ! server.invoke(name, "start", null, null); } }
이 예제는 JMX를 기존의 아키텍쳐에 끼워넣는 것(plug)이 얼마나 간단한고, 어떻게 가능한지, 또 기존의 서비스 코드를 수정하지 않고도 간단한 몇줄의 코드를 통해 관리가능한(또는 JSR 160에서 명시한 원격에서 관리가능한) 서비스로 만들어 줄 수 있는지를 보여준다.