`
daodan988
  • 浏览: 31563 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

jboss4.2.2GA+Cluster+HASingleton

阅读更多
Here is an outline of the steps I took in order to use HASingletons in a clustered JBoss application. I hope it will be helpful to others who try to use HASingletons for the first time. The below is based on the approach explained by Ivelin Ivanov in the ONJava article found here: http://www.onjava.com/pub/a/onjava/2003/08/20/jboss_clustering.html. Additional information is provided in order to explain the steps necessary for accessing the HASingleton's operations from other classes.

Bear in mind that a JBoss HASingleton is not a true Singleton per se in that you won't get a reference to it and call methods on it like you would with a normal Singleton. Instead it is a JMX service (MBean) with managed operations that you invoke somewhat indirectly. Once I finally understood this the whole approach made a whole lot more sense to me.

The below is working for me using JBoss 3.2.6.


First you create the HASingleton MBean and configure it to be controlled by a HASingletonController.


1. Create an MBean which has lifecycle methods for use by JBoss for initialization and shutdown of the service. Name them whatever you like, I used the names startSingleton() and stopSingleton() as in Ivanov's example. Other methods which return values shoud return Objects instead of primitives, due to the mechanism used for invoking them, as we'll see later. Below is an simple example HASingleton MBean interface and class:


| // -------- ExampleMBean interface --------------------------------------------------
| package com.example.mbean;
|  
| import org.jboss.system.ServiceMBean;
|  
| public interface ExampleMBean
|   extends ServiceMBean
| {
|   //===================================================================================
|   // Managed MBean operations which control the lifecycle of the HASingleton service
|   //===================================================================================
|  
|   /**
|    * Starts the service.
|    */
|   public void startSingleton ();
|  
|     
|   /**
|    * Stops the service.
|    */
|   public void stopSingleton ();
|
|  
|   //================================================================================
|   // Managed MBean operations
|   //================================================================================
|
|   /**
|    * Determine whether or not a user ID is present in the collection.
|    *
|    * @param userId the user ID to check
|    * @return whether or not the specified user ID is present in the collection
|    */
|   public Boolean isUserIdPresent (String userId);
|     
|     
|   /**
|    * Add a user ID to the collection.
|    *
|    * @param userId the user ID to add to the collection
|    */
|   public void addUserId (String userId);  
|  
|  
|   /**
|    * Remove a user ID from the collection.
|    *
|    * @param userId the user ID to remove from the collection
|    */
|   public void removeUserId (String userId);
| }
|  
|  
|  
| // -------- Example MBean class ------------------------------------------------------
|  
| package com.example.mbean;
|     
| import java.util.Vector;
| import org.jboss.system.ServiceMBeanSupport;
|
|
| public class Example
|   extends ServiceMBeanSupport
|   implements ExampleMBean
| {
|   // collection of user IDs
|   private Vector m_userIds;
|  
|   //==================================================================================
|   // Managed MBean operations which control the lifecycle of the HASingleton service
|   //================================================================================== 
|  
|   /**
|    * Starts the service.
|    */
|   public void startSingleton ()
|   {}
|
|   /**
|    * Stops the service.
|    */
|   public void stopSingleton ()
|   {}
|  
|  
|   /**
|    * Constructor.
|    */
|   public Example ()
|   {
|      // create the collection of user IDs
|      m_userIds = new Vector();
|   }
|  
|  
|   //================================================
|   // Managed MBean operations
|   //================================================
|  
|   /**
|    * Determine whether or not a user ID is present in the collection.
|    *
|    * @param userId the user ID to check
|    * @return whether or not the specified user ID is present in the collection
|    */
|   public Boolean isUserIdPresent (String userId)
|   {
|      return new Boolean(m_userIds.contains(userId));
|   }
|  
|  
|   /**
|    * Add a user ID to the collection.
|    *
|    * @param userId the user ID to add to the collection
|    */
|   public void addUserId (String userId)
|   {
|      // add the user ID if not already present
|      if (! m_userIds.contains(userId))
|      {
|         m_userIds.add(userId);
|      }
|   }
|  
|     
|   /**
|    * Remove a user ID from the collection.
|    *
|    * @param userId the user ID to remove from the collection
|    */
|   public void removeUserId (String userId)
|   {
|      // remove the user ID if it's present
|      if (m_userIds.contains(userId))
|      {
|         m_userIds.remove(userId);
|      }
|   }
| }
|


2. In the SAR which contains the HASingleton MBean declare the MBean and an HASingletonController to control it. This is done in the META-INF/jboss-service.xml file of the SAR. For example the below is a jboss-service.xml for the above HASingleton MBean:


| <?xml version="1.0" encoding="UTF-8"?>
|
| <server>
|
|   <!-- MBean to hold all user messages received from the queue -->
|   <mbean code="com.example.mbean.Example"
|        name="example.mbean:service=Example">
|   </mbean>
|
|   <!-- HASingletonController to run the above MBean as a HASingleton -->
|   <mbean code="org.jboss.ha.singleton.HASingletonController"
|        name="jboss.hasingleton:service=ExampleSingletonController">
|      <depends>jboss:service=DefaultPartition</depends>
|      <depends>example.mbean:service=Example</depends>
|      <attribute name="TargetName">example.mbean:service=Example</attribute>
|      <attribute name="TargetStartMethod">startSingleton</attribute>
|      <attribute name="TargetStopMethod">stopSingleton</attribute>
|   </mbean>
|  
| </server>
|

3. Put the SAR with the HASingleton MBean classes into the JBOSS/server/all/deploy-hasingleton directory on every node of your cluster.



To be able to access the HASingleton you need to use a RMIAdaptor which also runs as a HASingleton. This HASingleton RMIAdaptor will locate your HASingletons and enable you to invoke their operations. Essentially it's running on the same node/JVM by virtue of it being an HASingleton itself, so it can "see" the other HASingletons. This is in contrast to the normal approach of using an MBeanServer, which may or may not be running on the same node/JVM as the HASingletons and hence may not be able to see them since they may not be registered.
 
 
1. Copy the jmx-invoker-adaptor-server.sar directory from JBOSS/server/all/deploy into JBOSS/server/all/deploydeploy-hasingleton.


2. Modify JBOSS/server/all/deploy-hasingleton/jmx-invoker-adaptor-server.sar/META-INF/jboss-service.xml:

  a) rename the JRMPProxyFactory MBean as SingletonInvoker
  b) change the JndiName for the JRMPProxyFactory MBean to jmx/invoker/SingletonRMIAdaptor
  c) add a HASingletonController for the SingletonInvoker
  d) remove the NamingAlias MBean for /jmx/rmi/RMIAdaptor

  Below is the jboss-service.xml I am using:


| <?xml version="1.0" encoding="UTF-8"?>
|
| <!-- $Id: jboss-service.xml,v 1.1.2.10 2004/09/09 04:19:30 osdchicago Exp $ -->
| <server>
|
|   <!-- The JRMP invoker proxy configuration for the InvokerAdaptorService -->
|   <mbean code="org.jboss.invocation.jrmp.server.JRMPProxyFactory"
|        name="jboss.jmx:type=adaptor,name=SingletonInvoker,protocol=jrmp,service=proxyFactory">
|      <!-- Use the standard JRMPInvoker from conf/jboss-service.xxml -->
|      <depends optional-attribute-name="InvokerName">jboss:service=invoker,type=jrmp</depends>
|      <!-- The target MBean is the InvokerAdaptorService configured below -->
|      <depends optional-attribute-name="TargetName">
|         jboss.jmx:type=adaptor,name=SingletonInvoker
|      </depends>
|      <!-- Where to bind the RMIAdaptor proxy -->
|      <attribute name="JndiName">jmx/invoker/SingletonRMIAdaptor</attribute>
|      <!-- The RMI compabitle MBeanServer interface -->
|      <attribute name="ExportedInterfaces">org.jboss.jmx.adaptor.rmi.RMIAdaptor,
|         org.jboss.jmx.adaptor.rmi.RMIAdaptorExt
|      </attribute>
|      <attribute name="ClientInterceptors">
|         <interceptors>
|           <interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
|           <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
|           <interceptor>
|              org.jboss.jmx.connector.invoker.client.InvokerAdaptorClientInterceptor
|           </interceptor>
|           <interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
|         </interceptors>
|      </attribute>
|   </mbean>
|
|
|   <!-- This is the service that handles the RMIAdaptor invocations by routing
|       them to the MBeanServer the service is deployed under. -->
|   <mbean code="org.jboss.jmx.connector.invoker.InvokerAdaptorService"
|        name="jboss.jmx:type=adaptor,name=SingletonInvoker"
|        xmbean-dd="">
|      <xmbean>
|         <description>The JMX Detached Invoker Service</description>
|         <class>org.jboss.jmx.connector.invoker.InvokerAdaptorService</class>
|
|         <!-- Attributes -->
|         <attribute access="read-only"
|                getMethod="getName">
|           <description>The class name of the MBean</description>
|           <name>Name</name>
|           <type>java.lang.String</type>
|         </attribute>
|         <attribute access="read-only"
|                getMethod="getState">
|           <description>The status of the MBean</description>
|           <name>State</name>
|           <type>int</type>
|         </attribute>
|         <attribute access="read-only"
|                getMethod="getStateString">
|           <description>The status of the MBean in text form</description>
|           <name>StateString</name>
|           <type>java.lang.String</type>
|         </attribute>
|         <attribute access="read-write"
|                getMethod="getExportedInterfaces"
|                setMethod="setExportedInterfaces">
|           <description>The interfaces the invoker proxy supports</description>
|           <name>ExportedInterfaces</name>
|           <type>[Ljava.lang.Class;</type>
|         </attribute>
|         <attribute access="read-only"
|                getMethod="getMethodMap">
|           <description>Map(Long hash, Method) of the proxy interface methods</description>
|           <name>MethodMap</name>
|           <type>java.util.Map</type>
|         </attribute>
|         <!-- Operations -->
|         <operation>
|           <description>The start lifecycle operation</description>
|           <name>start</name>
|         </operation>
|         <operation>
|           <description>The stop lifecycle operation</description>
|           <name>stop</name>
|         </operation>
|         <operation>
|           <description>The detyped lifecycle operation (for internal use only)</description>
|           <name>jbossInternalLifecycle</name>
|           <parameter>
|              <description>The lifecycle operation</description>
|              <name>method</name>
|              <type>java.lang.String</type>
|           </parameter>
|           <return-type>void</return-type>
|         </operation>
|
|         <operation>
|           <description>The detached invoker entry point</description>
|           <name>invoke</name>
|           <parameter>
|              <description>The method invocation context</description>
|              <name>invocation</name>
|              <type>org.jboss.invocation.Invocation</type>
|           </parameter>
|           <return-type>java.lang.Object</return-type>
|           <!-- Uncomment to require authenticated users . Also an AuthorizationInterceptor
|               is provided which whill help in authorizing users to make JMX calls at the
|               MBean operations level. You will need to write a class that overrides a method
|               with the signature
|               "public Boolean authorize( Principal caller, Collection roles,String objectname
| ,String opname)"
|               is needed to be defined in the attribute 'authorizingClass'
|           <descriptors>
|              <interceptors>
|                 <interceptor code="org.jboss.jmx.connector.invoker.AuthenticationInterceptor"
|                         securityDomain="java:/jaas/jmx-console"/>
|                 <interceptor code="org.jboss.jmx.connector.invoker.AuthorizationInterceptor"
|                         authorizingClass="CustomAuthorizationClass"
|                         securityDomain="java:/jaas/jmx-console"/>
|              </interceptors>
|           </descriptors>
|           -->
|         </operation>
|      </xmbean>
|      <attribute name="ExportedInterfaces">
|         org.jboss.jmx.adaptor.rmi.RMIAdaptor,
|         org.jboss.jmx.adaptor.rmi.RMIAdaptorExt
|      </attribute>
|   </mbean>
|
|
|   <!-- HASingletonController to run the InvokerAdaptorService MBean as a HASingleton -->
|   <mbean code="org.jboss.ha.singleton.HASingletonController"
|        name="jboss.hasingleton:service=RMIAdaptorSingletonController">
|      <depends>jboss:service=DefaultPartition</depends>
|      <depends>jboss.jmx:type=adaptor,name=SingletonInvoker</depends>
|      <attribute name="TargetName">jboss.jmx:type=adaptor,name=SingletonInvoker</attribute>
|      <attribute name="TargetStartMethod">start</attribute>
|      <attribute name="TargetStopMethod">stop</attribute>
|   </mbean>
|
| </server>
|

Now to access the HASingleton in client code (EJBS, Servlets, other MBeans, etc.) I use the following approach:

  1. Lookup the HASingleton RMIApdaptor
  2. Make sure that the HASingleton I want to use is registered
  3. Invoke the operation and cast the returned object, if any, to the expected class (RMIAdaptor.invoke() returns an object, which is why all of our MBean operations must return Objects and not primitives)

For example:

| String userId = "elvis";
| try
| {
|   // create a HashTable of environment properties for the InitialContext
|   //
|   // NOTE -- These are hard-coded since the using jndi.properties with a URL containing
|   //      the HA-JNDI port causes the NamingService to fail on start up; hopefully this
|   //      issue can be resolved and these properties can again be set in jndi.properties.
|   Hashtable jndiProperties = new Hashtable();
|   jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY,
|                "org.jnp.interfaces.NamingContextFactory");
|   // use the port specified in the HANamingService entry (currently set in cluster-service.xml)
|   jndiProperties.put(Context.PROVIDER_URL, "localhost:11100");
|   jndiProperties.put("java.naming.factory.url.pkgs",
|                "org.jboss.naming:org.jnp.interfaces");
|
|   // get the InitialContext and lookup the SingletonRMIAdaptor
|   Context context = new InitialContext(jndiProperties);
|   RMIAdaptor rmiAdaptor = (RMIAdaptor) context.lookup("jmx/invoker/SingletonRMIAdaptor");
|
|   // if we have an RMIAdaptor and the Example MBean is registered with it then we can invoke operations
|   if ((rmiAdaptor != null) && (rmiAdaptor.isRegistered(new ObjectName("example.mbean:service=Example"))))
|   {
|      // add the message to the list for this user ID
|      Boolean isUserIdPresent = rmiAdaptor.invoke(new ObjectName("example.mbean:service=Example"),
|                                   "isUserIdPresent",
|                                   new Object[]{userId},
|                                   new String[]{"java.lang.String"});
|                                  
|      // show our result                            
|      if (isUserIdPresent.booleanValue())
|      {
|         System.out.println("User ID " + userId + " is in the collection");
|      }
|      else
|      {
|         System.out.println("User ID " + userId + " is not in the collection");
|      }
|   }
|   else
|   {
|      // log the error
|      m_logger.error("Unable to find an RMIAdaptor with a registered Example HASingleton MBean");
|  
|      // throw an exception
|      throw new Exception("Unable to find an RMIAdaptor with a registered Example HASingleton MBean");
|   }
| }
| catch (MBeanException e)
| {
|   // log the error
|   m_logger.error("Unable to invoke the Example.isUserIdPresent() method", e);
|  
|   // throw an exception
|   throw new Exception("Unable to invoke the Example.isUserIdPresent() method", e);
| }
| catch (ReflectionException e)
| {
|   // log the error
|   m_logger.error("Unable to invoke the Example.isUserIdPresent() method", e);
|  
|   // throw an exception
|   throw new Exception("Unable to invoke the Example.isUserIdPresent() method", e);
| }
| catch (InstanceNotFoundException e)
| {
|   // log the error
|   m_logger.error("Unable to invoke the Example.isUserIdPresent() method", e);
|  
|   // throw an exception
|   throw new Exception("Unable to invoke the Example.isUserIdPresent() method", e);
| }
| catch (MalformedObjectNameException e)
| {
|   // log the error
|   m_logger.error("Unable to invoke the Example.isUserIdPresent() method", e);
|  
|   // throw an exception
|   throw new Exception("Unable to invoke the Example.isUserIdPresent() method", e);
| }
| catch (NamingException e)
| {
|   // log the error
|   m_logger.error("Unable to invoke the Example.isUserIdPresent() method", e);
|  
|   // throw an exception
|   throw new Exception("Unable to invoke the Example.isUserIdPresent() method", e);
| }
| catch (RemoteException e)
| {
|   // log the error
|   m_logger.error("Unable to invoke the Example.isUserIdPresent() method", e);
|  
|   // throw an exception
|   throw new Exception("Unable to invoke the Example.isUserIdPresent() method", e);
| }
|


The above approach works well for me except in the case when the master node of the cluster goes down and the HASingletons are restarted on the new master node of the cluster. The state of the HASingletons is lost since they are started fresh. For example in the above example if the master node running the HASingleton goes down then the collection of user IDs will be lost. For this reason I would not recommend using HASingletons if you need to maintain state, I have learned this lesson the hard way. My understanding at this point is that a stateful Singleton is not a good idea and instead an Entity Bean should be used. If anyone can suggest a solution to this problem, other than rewriting everything using Entity Beans (which, I fear, might in fact be the only solution) then I'd certainly appreciate it. You can post suggestions in the following thread:http://www.jboss.org/index.html?module=bb&op=viewtopic&t=55787

I hope the above is helpful.

分享到:
评论

相关推荐

    JBOSS 4.2.2GA 使用指南

    **JBOSS 4.2.2GA 使用指南** JBOSS是一款开源的企业级Java应用服务器,基于Java 2企业版(J2EE)规范。JBOSS 4.2.2GA是其一个特定的稳定版本,发布于2007年,提供了一个全面的平台来部署和管理各种Java应用程序和...

    jboss4.2.2GA中文文档

    **JBoss 4.2.2 GA 中文文档详解** JBoss 4.2.2 GA 是一个基于Java的企业级应用服务器,它提供了全面的中间件服务,支持Java EE(Enterprise Edition)规范,包括EJB(Enterprise JavaBeans)、JMS(Java Message ...

    jboss4.2.2.GA

    jboss4.2.2.GA java开发的好帮手

    EJB3.0+JBOSS4.2.2GA集群

    **EJB 3.0 和 JBoss 4.2.2 GA 集群详解** EJB(Enterprise JavaBeans)3.0 是Java EE(Java Platform, Enterprise Edition)中的一个核心组件,它提供了用于构建可扩展、分布式和事务处理的企业级应用程序的框架。...

    Jboss4.2.2+Spring2.5.6+hibernate+JTA事务的实现

    "Jboss4.2.2+Spring2.5.6+Hibernate+JTA事务的实现"就是一个典型的例子,它涉及到四个关键的技术栈,即JBoss Application Server 4.2.2、Spring 2.5.6、Hibernate ORM以及Java Transaction API(JTA)。这些技术的...

    jboss-4.2.2.GA

    JBoss 4.2.2.GA是该平台的一个稳定版本,发布于2007年,包含了对Java EE 5的支持。 **一、JBoss 4.2.2.GA核心特性** 1. **Java EE 5兼容性**:JBoss 4.2.2.GA支持Java Platform, Enterprise Edition 5(Java EE 5...

    JBoss4.2.3GA + EJB3.0 + JAAS

    【JBoss4.2.3GA + EJB3.0 + JAAS】是企业级Java应用服务器、EJB(Enterprise JavaBeans)版本和安全性框架JAAS(Java Authentication and Authorization Service)的一个经典组合。这个组合在Java开发领域具有重要的...

    MacOS下jdk1.6+Eclipse4.4.2+JBoss4.2.2+SVN开发环境搭建

    - 从JBoss官方网站或者镜像站点下载JBoss AS 4.2.2 GA的zip文件。 - 解压缩文件,将解压后的目录移动到你希望存放的位置,如`/Users/your_username/Documents/JBoss`. - 在终端中,通过cd命令进入JBoss目录,启动...

    Jboss4.2.2.GA英文文档

    **JBoss 4.2.2 GA英文文档详解** JBoss AS(Application Server)是Red Hat公司开发的一个开源Java EE应用服务器,版本4.2.2 GA是它的一个重要里程碑。这个版本提供了全面的支持,包括对Java Enterprise Edition 5...

    jboss-4.2.2.GA.part5

    共5个压缩包,全部下载才能够解压 jboss jboss4.2 jboss4.2.2 jboss-4.2.2 jboss-4.2.2.GA

    jboss-4.2.2.GA.part2.

    共5个压缩包,全部下载才能够解压 jboss jboss4.2 jboss4.2.2 jboss-4.2.2 jboss-4.2.2.GA............

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part3

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part3

    jboss-4.2.2.GA端口路径

    在本文中,我们将深入探讨如何在JBoss 4.2.2.GA版本中修改默认的端口设置,这是一个常见的需求,尤其是在端口被其他服务占用时。 ### JBoss 4.2.2.GA端口路径与配置 JBoss 4.2.2.GA版本在运行时,默认监听在8080...

    经典JAVA EE企业应用实战 基于WEBLOGIC JBOSS的JSF+EJB 3+JPA整合开发.part2

    经典JAVA EE企业应用实战 基于WEBLOGIC JBOSS的JSF+EJB 3+JPA整合开发.part2

    jboss-4.2.2.GA.part2

    jboss 4.2.2.GA part2,办公区上不了官网,只能先传到这里了。和我一样杯具的码农可以下了使用。

    经典JAVA EE企业应用实战基于WEBLOGIC JBOSS的JSF+EJB 3+JPA整合开发——源码.part1

    经典JAVA EE企业应用实战基于WEBLOGIC JBOSS的JSF+EJB 3+JPA整合开发——源码.part1 其他部分详见我的上传列表,全部分卷下载完成才能解压。 本书介绍了Java EE规范的三大主要规范JSF、EJB 3和JPA,其中JSF是Sun...

Global site tag (gtag.js) - Google Analytics