[doc] [toc] [previous] [next]

第13章

目次

MBeanを使ってカスタムサービスを組み込む方法
JMXコネクタの説明と使い方
Timer MBeanの使い方
JBossCXの設定

MBeanを使ってカスタムサービスを組み込む方法

Author:Scott Stark <Scott_Stark@displayscape.com>

日本語訳: 皆本房幸 <miki.htmnet@nifty.com>

イントロダクション

JBossサーバにサービスを追加する最善の方法はあなた自身のJMX MBeanを書くことです。 MBeanは、リソースとJava Management Extensions(JMX)仕様書で定義された機器インタフェース(instrumentation interface)を実装したJavaオブジェクトです。書き終えたら、jboss.jcmlを使ってJBossに追加します。jboss.jcmlはコアとなるJBoss MBeanの設定のために使います。あなたのEJBから新サービスにアクセスする最善の方法は、JNDIからアクセスできるようにすることです。

2種類のMBeanがあります。JBossから独立しているものと、JBossサービスに依存しているものです。 JBossから独立しているMBeanは、取るに足らない簡単なもので、JMX仕様書にしたがって書くことができ、jboss.confファイルへMLETタグを追加することでJBossサーバへ追加することができます。 ネーミングのようなJBossサービスに依存したMBeanを書くことは、JBossサービスパターンに従うためにあることを要求します。 そのようなMBeanを書く方法について説明する前に、MBeanサービスの設定と依存性を管理する二つのJBossサービスであるConfigurationServiceとServiceControlについて説明しましょう。

ConfigurationService MBean

JBossは、XMLに似た標準MLet設定ファイルをロードするカスタムMBeanによってMBeanサービスの設定を管理します。このカスタムMBeanはorg.jboss.configuration.ConfigurationServiceクラスで実装されます。 このConfigurationService MBeanは、JBoss起動時にjboss.confファイルのエントリに応じてJMX MLetによってロードされます。jboss.jcml設定は、次にConfigurationService MBean上でloadConfiguration()を呼び出すことによってロードされます。このloadConfigurationメソッドは次のステップを実行します。

  1. jboss.jcmlファイルを解析(parse)して、すべてのMBeanのインスタンスを作成します。

  2. オプションのjboss-auto.jcmlを解析します。

    1. もし、jboss-auto.jcmlが存在するなら、そのファイルを解析して、jboss.jcmlファイルのパースによって生成されなかったMBeanのインスタンスを作成します。

    2. jboss-auto.jcmlファイルからMBean属性の設定をおこないます。

  3. jboss.jcmlファイルからMBean属性の設定をおこないます。

ServiceControl MBean

JBossはorg.jboss.util.ServiceControlカスタムMBeanによってMBean間の依存関係を管理します。 ServiceControl MBean自身もJBoss起動時にJMX Mletによってロードされます。 それはAttributeChangeNotification、MBeanServerNotification.REGISTRATION_NOTIFICATION、それにMBeanServerNotification.UNREGISTRATION_NOTIFICATION JMXイベントを受けるためにjavax.management.NotificationListenerインタフェースを実装します。AttributeChangeNotificationは、単にJBossサーバログにログ出力されます。 REGISTRATION_NOTIFICATIONとUNREGISTRATION_NOTIFICATIONは、どのMBeanがJBossサービスMBeanであるかを決定するのに使われます。MBeanを登録する順番は、サービスを初期化して開始する順番として使われます。 ServiceControl MBeanは、init、start、stop、destroyという4つのメソッドを持ちます。

initメソッド

ServiceControlのinitメソッドは、jboss.jcml設定ファイルがロードされた後に、JBossサーバのメインエントリによって呼び出されます。 jboss.jcmlファイルをロードする過程で、MBeanServerからの登録イベントが発行され、それによってすべてのロードされるMBeanの名前がServiceControl MBeanに登録されることになります。 initメソッドは、現在のMBean名のリストをコピーし、次に各MBeanに対してinitメソッドを続けて呼び出します。 それに成功すれば、ServiceControl MBeanは、MBeanからAttributeChangeNotificationを受けるために、MBean名に対してNotificationListenerを登録します。 初期化の順番は、jboss.jcmlファイルのMBeanエントリを登録した順番と同じです。 MBeanのinitメソッドが呼び出されると、それに先立って登録されたすべてのMBeanのinitメソッドも呼び出されます。 これはあるMBeanに対してそれが要求するMBeanやリソースが存在するかどうかを検査する機会を与えます。 そのMBeanは普通この時点では他のMBeanサービスを利用できません。ほとんどのJBoss MBeanは起動(start)されるまでは完全に機能を発揮することができないからです。

startメソッド

ServiceControlのstartメソッドは、すべてのMBeanがinitメソッドを起動された後に、JBossサーバのメインエントリによって起動されます。 startメソッドは現在のMBean名のリストをコピーして、次に続けて各MBeanに対してstartを起動します。 あるMBeanのstartメソッドが呼び出されたときは、それに先立って登録されたすべてのMBeanのstartメソッドもすでに呼び出されています。 完全に機能するために、別のMBeanサービスに通知するというのもstartメソッドの仕事のうちです。

stopメソッド

stopメソッドはorg.jboss.util.Shutdown MBeanによって管理されるJBossサーバ シャットダウンの過程で起動されます。 stopメソッドは現在のMBean名のリストをコピーし、次に続けて各MBeanに対して逆順にstopメソッドを起動します。

destroyメソッド

destroyメソッドは、stopメソッドの後にJBossサーバ シャットダウンの過程で起動されます。 destroyメソッドは現在のMBean名のリストをコピーし、次に各MBeanに対して逆順にdestroyメソッドを起動します。

JBoss MBeanサービスを書く

JBossサーバへ統合するカスタムサービスを書くことは、もしそのサービス他のJBossサービスに依存しているとしたら、org.jboss.util.Serviceインタフェースパターンを使うことが必要です。 これは、javax.management.MBeanRegistrationインタフェースのどのメソッドを使ってもJBossサービスに依存した初期化をおこなうことができないことを意味しています。 その代わり、Serviceインタフェースのinitstartでこれをおこなわなければなりません。次のどれかをおこなう必要があります。

  • あなたのMBeanに対して呼び出されるServiceメソッドのうち欲しいものをあなたのMBeanインタフェースに追加します。

  • あなたのMBeanインタフェースがorg.jboss.util.Service interfaceをextendするようにします。

  • あなたのMBeanインタフェースがorg.jboss.util.ServiceMBean interfaceをextendするようにします。 これはString getName(), int getState(), String getStateString()メソッドを追加したorg.jboss.util.Serviceのサブインタフェースです。

どのアプローチを選択するかは、JBoss固有のコードに関連させたいか否かによります。 もし関連させたくないなら、最初のアプローチを使うでしょう。 もし気にしないなら、あなたのMBeanインタフェースをorg.jboss.util.ServiceMBeanからextendして、あなたのMBean実装クラスをabstract org.jboss.util.ServiceMBeanSupportクラスからextendするのが最も単純なアプローチです。 このクラスはString getName()メソッドを除くorg.jboss.util.ServiceMBeanを実装します。 ServiceMBeanSupportは、ログ出力やJBoss状態管理を統合したinit, start, stop, destroyの実装を提供します。 各々のメソッドは、initService, startService, stopService, destroyServiceメソッドのそれぞれに対してサブクラス固有の仕事を委譲します。ServiceMBeanSupportをサブクラスを作るとき、要求されたgetNameメソッドに加えて、initService, startService, stopService, destroyServiceメソッドの一つ以上をオーバライドすることでしょう。

実例

このセクションでは、あるHashMapをJBoss JNDI名前空間(JndiName属性によって決められる場所)へバインドする単純なMBeanを使います。 そのMBeanはJNDIを使うので、それはJBossネーミングサービスMBeanに依存します。したがって、ネーミングサービスが利用可能になった時点で通知を受けられるようにJBoss MBeanサービスパターンを使わなければなりません。

JNDIMap MBean、 サービスメソッドの実装例

図 13.1. Serviceインタフェースに基づくJNDIMap MBeanインタフェースと実装は、正しく起動するために必要なServiceインタフェースメソッドと協調するJNDIMap MBeanのためのMBeanインタフェースを示します。 そのインタフェースは、すべての要求されたサービスが開始したとき通知を受けるためのServicestartメソッドとそのサービスをクリーンアップするためのstopメソッドを含みます。

図 13.1. Serviceインタフェースに基づくJNDIMap MBeanインタフェースと実装

// The JNDIMap MBean interface
import javax.naming.NamingException;

public interface JNDIMapMBean
{
    public String getJndiName();
    public void setJndiName(String jndiName) throws NamingException;
    public void start() throws Exception;
    public void stop() throws Exception;
}
// The JNDIMap MBean implementation
import java.io.InputStream;
import java.util.HashMap;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import org.jboss.naming.NonSerializableFactory;

public class JNDIMap implements JNDIMapMBean
{
    private String jndiName;
    private HashMap contextMap = new HashMap();
    private boolean started;

    public String getJndiName()
    {
       return jndiName;
    }
    public void setJndiName(String jndiName) throws NamingException
    {
        String oldName = this.jndiName;
        this.jndiName = jndiName;
        if( started )
        {
            unbind(oldName);
            try
            {
                rebind();
            }
            catch(Exception e)
            {
                NamingException ne = new NamingException("Failed to update jndiName");
                ne.setRootCause(e);
                throw ne;
            }
        }
    }

    public void start() throws Exception
    {
        started = true;
        rebind();
    }

    public void stop()
    {
        started = false;
        unbind(jndiName);
    }

    private static Context createContext(Context rootContext, Name name) throws NamingException
    {
        Context subctx = rootContext;
        for(int n = 0; n < name.size(); n ++)
        {
            String atom = name.get(n);
            try
            {
                Object obj = subctx.lookup(atom);
                subctx = (Context) obj;
            }
            catch(NamingException e)
            {	// No binding exists, create a subcontext
                subctx = subctx.createSubcontext(atom);
            }
        }

        return subctx;
    }

    private void rebind() throws NamingException
    {
        InitialContext rootCtx = new InitialContext();
        // Get the parent context into which we are to bind
        Name fullName = rootCtx.getNameParser("").parse(jndiName);
        System.out.println("fullName="+fullName);
        Name parentName = fullName;
        if( fullName.size() > 1 )
            parentName = fullName.getPrefix(fullName.size()-1);
        else
            parentName = new CompositeName();
        Context parentCtx = createContext(rootCtx, parentName);
        Name atomName = fullName.getSuffix(fullName.size()-1);
        String atom = atomName.get(0);
        NonSerializableFactory.rebind(parentCtx, atom, contextMap);
    }
    private void unbind(String jndiName)
    {
        try
        {
            Context rootCtx = (Context) new InitialContext();
            rootCtx.unbind(jndiName);
            NonSerializableFactory.unbind(jndiName);
        }
        catch(NamingException e)
        {
            e.printStackTrace();
        }
    }
}

ServiceMBeanを拡張したJNDIMap MBean

図 13.2. ServiceインタフェースとServiceMBeanSupportに基づくJNDIMap MBeanインタフェースと実装は、ServiceMBeanインタフェースをextendしたJNDIMap MBeanのためのMBeanインタフェースを示します。 その実装クラスは、次にServiceMBeanSupportクラスをextendして、すべての要求されたサービスが開始したときに通知を受けるためのstartServiceメソッドとそのサービスをクリーンアップするstopServiceメソッドをオーバライドします。 それはまたそのMBeanの説明的な名前を返すabstract getNameを実装します。

図 13.2. ServiceインタフェースとServiceMBeanSupportに基づくJNDIMap MBeanインタフェースと実装

// The JNDIMap MBean interface
import javax.naming.NamingException;

public interface JNDIMapMBean extends org.jboss.util.ServiceMBean
{
    public String getJndiName();
    public void setJndiName(String jndiName) throws NamingException;
}
// The JNDIMap MBean implementation
import java.io.InputStream;
import java.util.HashMap;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import org.jboss.naming.NonSerializableFactory;

public class JNDIMap extends org.jboss.util.ServiceMBeanSupport implements JNDIMapMBean
{
    private String jndiName;
    private HashMap contextMap = new HashMap();

    public String getJndiName()
    {
       return jndiName;
    }
    public void setJndiName(String jndiName) throws NamingException
    {
        String oldName = this.jndiName;
        this.jndiName = jndiName;
        if( super.getState() == STARTED )
        {
            unbind(oldName);
            try
            {
                rebind();
            }
            catch(Exception e)
            {
                NamingException ne = new NamingException("Failed to update jndiName");
                ne.setRootCause(e);
                throw ne;
            }
        }
    }

    public String getName()
    {
        return "JNDIMap(" + jndiName + ")";
    }

    public void startService() throws Exception
    {
        rebind();
    }

    public void stopService()
    {
        unbind(jndiName);
    }

    private static Context createContext(Context rootContext, Name name) throws NamingException
    {
        Context subctx = rootContext;
        for(int n = 0; n < name.size(); n ++)
        {
            String atom = name.get(n);
            try
            {
                Object obj = subctx.lookup(atom);
                subctx = (Context) obj;
            }
            catch(NamingException e)
            {	// No binding exists, create a subcontext
                subctx = subctx.createSubcontext(atom);
            }
        }

        return subctx;
    }

    private void rebind() throws NamingException
    {
        InitialContext rootCtx = new InitialContext();
        // Get the parent context into which we are to bind
        Name fullName = rootCtx.getNameParser("").parse(jndiName);
        log.debug("fullName="+fullName);
        Name parentName = fullName;
        if( fullName.size() > 1 )
            parentName = fullName.getPrefix(fullName.size()-1);
        else
            parentName = new CompositeName();
        Context parentCtx = createContext(rootCtx, parentName);
        Name atomName = fullName.getSuffix(fullName.size()-1);
        String atom = atomName.get(0);
        NonSerializableFactory.rebind(parentCtx, atom, contextMap);
    }
    private void unbind(String jndiName)
    {
        try
        {
            Context rootCtx = (Context) new InitialContext();
            rootCtx.unbind(jndiName);
            NonSerializableFactory.unbind(jndiName);
        }
        catch(NamingException e)
        {
            log.exception(e);
        }
    }
}

サンプルjboss.jcml mbeanエントリ

JNDIMap MBeanのためのサンプルとなるjboss.jcml mbeanエントリを図 13.3. JNDIMap MBeanのためのサンプルjboss.jcmlエントリに示します。 これはあるHasyMapオブジェクトを"inmemory/maps/MapTest" JNDI名にバインドします。

図 13.3. JNDIMap MBeanのためのサンプルjboss.jcmlエントリ

<server>
...
  <mbean code="org.jboss.naming.NamingService" name="DefaultDomain:service=Naming">
    <attribute name="Port">1099</attribute>
  </mbean>

  <!-- Add the JNDIMap entry after the NamingService since the NamingService must
	be running in order for the JNDIMap bean to start.
  -->
  <mbean code="JNDIMap" name="DefaultDomain:service=JNDIMap,jndiName=inmemory/maps/MapTest">
    <attribute name="JndiName">inmemory/maps/MapTest</attribute>
  </mbean>
...
</server>
				
				
// Sample lookup code
InitialContext ctx = new InitialContext();
HashMap map = (HashMap) ctx.lookup("inmemory/maps/MapTest");
				
[doc] [toc] [previous] [next]

Copyright © 2000 2001 JBoss Organization
Copyright © 2001 2002 日本語訳: Neverbird Project