14

我们目前正在将我们的一些项目从 JBoss 4.x 移植到 JBoss 7。到目前为止,一切似乎都运行良好,除了我们通常用来提供简单管理操作的 MBean。

我已经搜索了很长一段时间,但是要么我无法找到正确的搜索词,要么我缺少一些知识来弥合 JBoss 4.x 和 JBoss 7 中的 MBean 定义之间的差距。

因此,希望有人可以提供关于我可能遗漏的内容或我必须阅读的内容的提示(可能是一些文档、示例等)

在 Jboss 4.x 中,我们的 MBean 通常看起来像这样:

@Service( objectName = "Domain:Name=SomeMBean",
  xmbean="resource:<path-to-xmbean.xml>")
class SomeMBean 
{
  @EJB
  private SomeService someService;    

  public String someOperation()
  {
     someService.doSomething();
     return "success";
  }
}

我们使用@Service注解来定义对象名称和 xmbean 描述符,JBoss 会自动注册这些 mbean。

显然,在 JBoss 7 中,@Service注释不再存在,因此需要另一种方法。

到目前为止,我设法将 MBean 手动注册到平台 mbean 服务器,但我更希望 JBoss 自动执行此操作。此外,到目前为止,我还没有设法提供方法/参数的描述(尽管这些功能更好)。

为了清楚起见,我将重复这个问题:

如何在 JBoss 7 (Java EE 6) 中定义提供以下特性的 MBean?

  • 自动部署
  • 访问 EJB
  • 可通过 JConsole 或 JMX-Console 访问(我目前正在使用 Dimitris Andreadis 的端口)
  • 提供方法/参数的描述

更新

这是我到目前为止得到的:

首先,我找到了这个投影,它使用 CDI 来包装任何带有相应注释的 bean 的注入目标,并在postConstruct()方法中进行 JMX 注册:http ://code.google.com/p/jmx-annotations/ 。此外,对找到的 MBean 进行扫描以查找类/属性/方法/参数注释,这些注释提供了注释属性的描述。

但是,postConstruct()似乎没有为 EJB 调用该方法(我假设这是为了不与 EJB 容器发生冲突)。因此,MBean 现在不应该是 EJB,而是普通的 CDI bean。

但是,因此具有 MBean 不会自动实例化的缺点。为了克服这个问题,有一个单例 bean,它在启动时循环遍历 中的所有 bean,BeanManager并为找到的每个 MBean 创建一个实例。因为 MBean 仍然有它们的注入目标,所以它的postConstruct()方法将不会被调用,并且 bean 将在 MBean 服务器中注册。

以下是启动过程的粗略概述:

  • 自定义 CDI 扩展扫描每个 CDI bean 以查找自定义 @MBean 注释
  • 对于每个符合条件的 MBean,注入目标都被包装
  • 将启动一个单例 bean,它在其 @PostConstruct 方法中将创建 MBean 的实例
  • postConstruct()调用 MBean 的注入目标的方法,从而将 MBean 注册到 MBean 服务器中

此方法的一个缺点是在执行 MBean 方法时缺少事务上下文(任何 EJB 调用都将在事务上下文中运行)。但是,如果需要,可以使用提供事务上下文的 CDI 拦截器来解决此问题。Seam 项目似乎为此提供了适当的拦截器。

我仍然不确定这是否是一种理智和稳定的方法,所以任何建设性的意见、提示等都非常受欢迎。

4

2 回答 2

3

带有@Startup 的单例 EJB?http://www.adam-bien.com/roller/abien/entry/singleton_the_simplest_possible_jmx

于 2012-07-04T17:09:18.987 回答
1

我认为更简洁的方法是使用 CDI 扩展。请看一下我们使用的解决方案:

@Documented
@Retention(value=RUNTIME)
@Target(value=TYPE)
@Inherited
public @interface MBean {
    String value() default "";
}

...

这是 CDI 扩展的工作代码:

public class ManagementExtension implements Extension {

    private static Logger log = LoggerFactory
            .getLogger(ManagementExtension.class);

    public <T> void processInjectionTarget(@Observes ProcessInjectionTarget<T> pit) {

        // check if the MBean annotation is present
        AnnotatedType<T> at = pit.getAnnotatedType();
        if (at.isAnnotationPresent(MBean.class)) {
            // it makes sense to register JMX interfaces only for singletons
            if (!at.isAnnotationPresent(Singleton.class)) {
                log.warn("Ignoring attemt to register JMX interface for a non-singleton EJB: "
                        + at.getJavaClass().getName());
                return;
            }

            try {
                // decorate the InjectionTarget
                InjectionTarget<T> delegate = pit.getInjectionTarget();
                InjectionTarget<T> wrapper = new JmxInjectionTarget<T>(delegate, getObjectName(at));

                // change the InjectionTarget with the decorated one
                pit.setInjectionTarget(wrapper);
            } catch (Exception e) {
                log.warn("Cannot get JMX object name for: " + at.getJavaClass().getName(), e);
            }

        }
    }

    private <T> ObjectName getObjectName(AnnotatedType<T> at) throws MalformedObjectNameException {

        String name = at.getAnnotation(MBean.class).value();

        if (name.isEmpty()) {
            name = at.getJavaClass().getPackage().getName() + ":type="
                    + at.getJavaClass().getSimpleName();
        }

        return new ObjectName(name);
    }

    private class JmxInjectionTarget<T> implements InjectionTarget<T> {

        private final InjectionTarget<T> d;
        private final ObjectName objectName;

        public JmxInjectionTarget(InjectionTarget<T> delegate, ObjectName objectName) {
            this.d = delegate;
            this.objectName = objectName;
        }
        @Override
        public void dispose(T instance) {
            d.dispose(instance);
        }

        @Override
        public Set<InjectionPoint> getInjectionPoints() {
            return d.getInjectionPoints();
        }

        @Override
        public T produce(CreationalContext<T> ctx) {
            return d.produce(ctx);
        }

        @Override
        public void inject(T instance, CreationalContext<T> ctx) {
            d.inject(instance, ctx);
            //the next piece of code better be done in postConstruct but...
            //got no idea why but postConstruct never gets called
            //for Singleton EJB bean
            MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
            try {
                if(mBeanServer.isRegistered(objectName))
                mBeanServer.unregisterMBean(objectName);
                mBeanServer.registerMBean(instance, objectName);
            } catch (Exception e) {
                log.warn("Cannot register "+objectName, e);
                return;
            }
            log.info("added JMX registration for: " + objectName);
        }

        @Override
        public void postConstruct(T instance) {
            d.postConstruct(instance);
        }

        @Override
        public void preDestroy(T instance) {
            d.preDestroy(instance);
        }

    }
}

然后只需通过 @Mbean 注释标记您的类,并将自动在 Mbean 服务器中注册:

@Startup 
@Singleton 
@MBean("com.company=JmxBindName")
public class SomeService

奇迹般有效 )

于 2014-03-14T19:42:07.640 回答