2장. 확장(Extensions)

차례

소개
Standard MBean descriptions
standard MBean에 대한 MBeanServer Invocation을 위한 Proxy
내부 logging 전송

소개

Simone Bordet

(번역)전 준식(locus@nextel.co.kr)

고친 과정
고침 $Revision: 1.1 $$Date: 2002/04/10 14:11:51 $

MX4J는 JMX 스펙에 몇가지 확장을 제공한다. 그래도, MBean의 호환성은 보장된다. 이는 사용자 MBean 또한 같은 방식으로 모든 JMX Spec 구현 서버에서 동작할 수 있다는 것을 보장한다. 그러나, MX4J 구현으로 사용될 때, 몇가지 부가적인 이점이 제공된다.

Standard MBean descriptions

MBean이 standard MBean으로서 코딩될 때, JMX 에이전트는 이에 대한 메타데이터 정보의 생성을 해야할 책임이 있다. 반대로, dynamic MBean으로 코딩될 때, 프로그래머는 public MBeanInfo getMBeanInfo(); method of the DynamicMBean 인터페이스를 통해서 JMX 에이전트에 노출할 메타데이터 정보를 만들어야할 책임이 있다.

비록 JMX 에이전트는 standard MBean에 대한 어트리뷰트, operation, 생성자, notification에 대한 정보를 추출할 수 있지만, 어트리뷰트 설명, operation 설명, 파라미터 이름과 설명 등등의 사용자 정보를 추출할 수 없다.

이러한 정보는 파라미터와 연계된 설명을 읽기만 해도 operation 파라미터가 무엇을 의미하는지를 즉시 이해할 수 있어, 관리용 어플리케이션의 사용자에게는 매우 중요하다. 어트리뷰트에 있어서도 마찬가지이다.

MX4J는 standard MBean의 속성, Operation, 생성자, notification에 대한 파라미터 이름과 설명을 customize할 수 있는 기능을 제공한다. 이런 customization은 다음 몇가지 어휘적인 패턴을 따르는 클래스를 구현함으로서 가능하다. standard MBean과 JMX 에이전트에 대한 관리 인터페이스를 표현하는 Java 인터페이스에서도 매우 유사하게 일어난다.

MBean 프로그래머는 MBean 클래스의 FQN(Full Qualified Name)과 같은 이름을 가지며, "MBeanDescrition"으로 끝나는 클래스를 작성하며, mx4j.MBeanDescription interface 나 mx4j.MBeanDescriptionAdapter를 확장하는(extends) 클래스를 구현하여야 한다.

예를 들어 my.package.MyService라는 MBean 클래스를 가지고 있다면, Java 인터페이스 my.package.MyServiceMBean에서 정의된 관리 인터페이스를 가지게 될 것이고, mx4j.MBeanDescription인터페이스를 구현하며, MBean에 대한 설명과, 파라미터이름을 명시하는 my.package.MyServiceMBeanDescription라는 이름의 클래스를 추가할 수 있다.

예 2.1. standard MBean에 대한 설명을 명시하기

		
public interface MyServiceMBean
{
	public void start();
	public void setStatus(int status);
}

public class MyService implements MyServiceMBean
{
	public MyService(String type) {...}
	public void start() {...}
	public void stop() {...}
	public void setStatus(int status) {...}
}

public class MyServiceMBeanDescription extends MBeanDescriptionAdapter
{
	public String getConstructorDescription(Constructor ctor)
	{
		// Only one constructor
		return "Creates a new instance of my personal service";
	}

	public String getConstructorParameterName(Constructor ctor, int index)
	{
		// Constructor has only one parameter
		return "type";
	}

	public String getConstructorParameterDescription(Constructor ctor, int index)
	{
		// Constructor has only one parameter
		return "The type of the service. Valid values are 'VOLATILE' or 'PERMANENT'.";
	}

	public String getAttributeDescription(String attribute)
	{
		// There is only one attribue, 'Status'
		return "The status of the service. Can be set to ON=1, OFF=0";
	}

	public String getOperationDescription(Method operation)
	{
		String name = operation.getName();
		if (name.equals("start"))
		{
			return "Starts the service. After the service is started its status is ON";
		}
		else if (name.equals("stop"))
		{
			return "Stops the service. After the service is stopped its status is OFF";
		}
		else
		{
			return super.getOperationDescription(operation);
		}
	}
}
		
		

standard MBean에 대한 MBeanServer Invocation을 위한 Proxy

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 인터페이스에 속하는 메소드만 호출하여야 한다.

내부 logging 전송

MX4J는 로그 우선순위를 조정할 수 있게 하며, MX4J의 내부 로그를 Log4J와 같은 로깅 시스템으로 redirect할 수 있는 기능을 가지는 매우 유연한 로깅 시스템을 가지고 있다.

MX4J 로깅 시스템은 여섯개의 로깅 순위가 있다. 낮은 우선 순위부터 높은 순위로 적는다.

  • trace

  • debug

  • info

  • warn

  • error

  • fatal

기본 설정 레벨은 warn이고, "mx4j.log.priority" 시스템 속성의 값을 위의 값들 중에 하나로 세팅하여 설정을 변경할 수 있다.

예를 들어, JVM의 명령행에서 MX4J 로그를 debug 레벨로 변경하려면 :

java -Dmx4j.log.priority=debug MyMainClass

fatal 레벨은 MX4J에서는 사용되지 않는다.

MX4J 기본 로깅은 System.out을 통해 콘솔로 출력된다. 그러나 MX4J 로깅 API를 사용하거나, broadcater MBean(아래에)을 사용하여, 다른 로깅 시스템으로 redirect할 수 있다.

예를 들어, MX4J의 로깅을 Log4J로깅 시스템으로 redirect하고자 한다면, 아래 코드가 해야 할 일이다.

예 2.4. Log4J로 로깅 Redirection

		
import org.apache.log4j.PropertyConfigurator;
import mx4j.log.*;

public class Main
{
	public static void main(String[] args) throws Exception
	{
		// Configure Log4J
		PropertyConfigurator.configureAndWatch("log4j.properties");
		// Or use the XML version below
		// DOMConfigurator.configureAndWatch("log4j.xml");

		// Redirect MX4J logging to Log4J
		Log.redirectTo(new Log4JLogger());

		// Normal code here
		MBeanServer server = MBeanServerFactory.createMBeanServer();
		...

		// Reset redirection, log in the normal way (to console)
		Log.redirectTo(null);
	}
}
		
		

위의 예제는 또 logging redirection을 어떻게 콘솔에 로그를 표시하는 표준으로 초기화하는지도 보여준다.

새로운 Logger의 프로토타입은 위의 예제에 있는 Log4JLogger를 코드에서나, 시스템 프로퍼티 "mx4j.log.prototype"에 Logger subclass의 FQN(Full Qulified Name)을 설정하면 가능하다. 예를 들면 :

java -Dmx4j.log.prototype=mx4j.log.Log4JLogger MyMainClass

등록된 리스너들에게 notification을 전송하는, MX4J Broadcaster MBean으로 로그를 redirect하는 것이 가능하다. MBeanServer 메카니즘은 이런 notification을 전송할 수 있고, 모든 리스너는 그 자신이 관심없는 것을 제외시키는 필터를 가지고 등록할 수 있다. 아래 예제는 logging 시스템을 broadcaster MBean으로 재전송하는데 필요한 코드를 보여준다.

예 2.5. Broadcaster MBean으로 Logging redirection

		
import mx4j.log.*;

public class Main
{
	public static void main(String[] args) throws Exception
	{
		MBeanServer server = MBeanServerFactory.createMBeanServer();

		// Register the broadcaster logger mbean
		ObjectName name = new ObjectName("Logger:type=broadcaster");
		server.createMBean("mx4j.log.LoggerBroadcaster", name, null);

		// The filter: only errors are logged
		NotificationFilter filter = new NotificationFilter()
		{
			public boolean isNotificationEnabled(Notification notification)
			{
				if (notification.getType().equals("mx4j.logger.error")) {return true;}
				return false;
			}
		};

		// The listener: logs on System.err instead of System.out
		NotificationListener listener = new NotificationListener()
		{
			public void handleNotification(Notification notification, Object handback)
			{
				System.err.println("[MX4J ERROR]: " + notification);
			}
		};

		// Register the listener along with the filter
		server.addNotificationListener(name, listener, filter, null);

		// Starts the redirector
		LoggerBroadcasterMBean redirector = (LoggerBroadcasterMBean)MBeanProxy.create(LoggerBroadcasterMBean.class, server, name);
		redirector.start();

		...

		// Stops the redirector
		redirector.stop();
	}
}
		
		

위의 예제는 또 logging redirection을 어떻게 콘솔에 로그를 표시하는 표준 로깅으로 초기화하는지도 보여준다.