将 JBoss 4.x 中的端口 MBean 移植到 JBoss 7

14

我们目前正在将一些项目从JBoss 4.x迁移到JBoss 7。到目前为止,除了我们通常使用的MBeans以提供简单管理操作之外,似乎一切正常。

我已经搜索了相当长的时间,但是要么我无法想出正确的搜索术语,要么我缺少某个知识点来弥合JBoss 4.x和JBoss 7之间的差距。

因此,希望有人能够提供提示,告诉我可能缺少什么或者我需要阅读哪些文档、示例等等。

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

@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/。此外,扫描找到的MBeans会查找提供有关注释属性说明的类/属性/方法/参数注释。
然而,postConstruct()方法似乎不会对EJBs进行调用(我认为这是为了不与EJB容器冲突)。因此,MBeans现在不应该是EJBs,而是普通的CDI beans。
这个方法的缺点是MBeans不会自动实例化。为了解决这个问题,在启动时有一个单例bean循环遍历BeanManager中的所有bean,并创建每个找到的MBean的实例。因为MBeans仍然有它们的注入目标,所以它的postConstruct()方法将被调用,并且该bean将被注册到MBean服务器中。
以下是启动过程的大致概述:
-自定义CDI扩展程序扫描每个CDI bean以获取自定义@MBean注释 -对于每个符合条件的MBean,都会包装注入目标 -一个单例bean将会启动,它的@PostConstruct方法将创建MBean的实例 -MBean的注入目标的postConstruct()方法将被调用,因此MBean将被注册到MBean服务器中
此方法的一个缺点是在执行MBean方法时缺少事务上下文(任何EJB调用都将在事务上下文中运行)。但是,如果需要,可以使用CDI拦截器来提供事务上下文,Seam项目似乎有适当的拦截器。
我仍然不确定这是否是一个稳健和可靠的方法,因此欢迎任何建设性的评论、提示等。
2个回答

3

我也看到了,但我不喜欢这种方法的原因是我们必须手动将注册代码放入每个MBean中(在许多情况下我们无法使用超类)。因此,我们目前使用我上面描述的方法。尽管如此,还是谢谢你的建议 :) - Thomas
1
没有太多选择。在 JBoss 7 中,您可以通过编程方式注册或使用 jboss-service.xml。如果您想要事务,则使用 Singleton EJBs 可能是最简单的方法。您可以将注册代码从 mbeans 中移出,并创建一个 StartupRegistrationBean,在其中以类似于 CDI 扩展的方式注册所有 mbeans。 - Chase

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

工作得很好 )


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接