详解JBOSS系列一(利用JNDI,EJB远程调用本地的Bean)

 

 1.前言

最近在公司里面做项目,用的是SpringMVC+EJB+JBOSS+Jpa。在整个框架中,唯一难于理解的是JBOSS的相关配置,尤其是JBOSS从5以后,在版本上做了一个很大的改变,例如加入了一些安全认证,域模式等,于是想写几篇博客来学习一下JBOSS6的有关应用。整个过程用的的JBOSS EAP6.2的版本。本篇博客就先从一个例子来讲一下,有关通过JBOSS,远程调用EJB本地Bean。所有的知识内容翻译自官网。JBOSS官网

   

 2.从一个Demo讲起

先通过一个Demo,运行起来看一下效果,后面再来详细的解释一下,整个Demo中的配置。


2.1 建立远程Bean(限于篇幅限制,只把Bean的实现放在这里,接口就不在放置了

有状态远程CounterBean

<span style="font-family:SimSun;font-size:18px;">
package com.tgb;

import javax.ejb.Remote;
import javax.ejb.Stateful;

/**
 * @author LUCKY
 */
@Stateful
@Remote(RemoteCounter.class)
public class CounterBean implements RemoteCounter {

    private int count = 0;

    @Override
    public void increment() {
        this.count++;
    }

    @Override
    public void decrement() {
        this.count--;
    }

    @Override
    public int getCount() {
        return this.count;
    }
}
</span>

无状态远程CalculatorBean

<span style="font-family:SimSun;font-size:18px;">
package com.tgb;

import javax.ejb.Remote;
import javax.ejb.Stateless;


/**
 * @author LUCKY
 */
@Stateless
@Remote(RemoteCalculator.class)
public class CalculatorBean implements RemoteCalculator {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}
</span>

2.2 部署远程服务端

把上面的远程的Bean,打成jar包,放置到jboss中,命名为ejb-service,并且启动jboss,出现下面,即为成功部署。另外JBOSS6.2的部署目录结构为JBOSS_HOME\standalone\deployments



 2.3 客户端远程调用

废话不多说了,直接上Demo吧

package main;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.tgb.RemoteCalculator;
import com.tgb.RemoteCounter;

/**
 * A sample program which acts a remote client for a EJB deployed on AS7 server. This program shows how to lookup stateful and
 * stateless beans via JNDI and then invoke on them
 */
public class RemoteEJBClient {

    public static void main(String[] args) throws Exception {
        // Invoke a stateless bean
        invokeStatelessBean();

        // Invoke a stateful bean
        invokeStatefulBean();
    }

    /**
     * Looks up a stateless bean and invokes on it
     * 
     * @throws NamingException
     */
    private static void invokeStatelessBean() throws NamingException {
        // Let's lookup the remote stateless calculator
        final RemoteCalculator statelessRemoteCalculator = lookupRemoteStatelessCalculator();
        System.out.println("Obtained a remote stateless calculator for invocation");
        // invoke on the remote calculator
        int a = 204;
        int b = 340;
        System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
        int sum = statelessRemoteCalculator.add(a, b);
        System.out.println("Remote calculator returned sum = " + sum);
        if (sum != a + b) {
            throw new RuntimeException("Remote stateless calculator returned an incorrect sum " + sum + " ,expected sum was "
                    + (a + b));
        }
        // try one more invocation, this time for subtraction
        int num1 = 3434;
        int num2 = 2332;
        System.out.println("Subtracting " + num2 + " from " + num1
                + " via the remote stateless calculator deployed on the server");
        int difference = statelessRemoteCalculator.subtract(num1, num2);
        System.out.println("Remote calculator returned difference = " + difference);
        if (difference != num1 - num2) {
            throw new RuntimeException("Remote stateless calculator returned an incorrect difference " + difference
                    + " ,expected difference was " + (num1 - num2));
        }
    }

    /**
     * Looks up a stateful bean and invokes on it
     * 
     * @throws NamingException
     */
    private static void invokeStatefulBean() throws NamingException {
        // Let's lookup the remote stateful counter
        final RemoteCounter statefulRemoteCounter = lookupRemoteStatefulCounter();
        System.out.println("Obtained a remote stateful counter for invocation");
        // invoke on the remote counter bean
        final int NUM_TIMES = 5;
        System.out.println("Counter will now be incremented " + NUM_TIMES + " times");
        for (int i = 0; i < NUM_TIMES; i++) {
            System.out.println("Incrementing counter");
            statefulRemoteCounter.increment();
            System.out.println("Count after increment is " + statefulRemoteCounter.getCount());
        }
        // now decrementing
        System.out.println("Counter will now be decremented " + NUM_TIMES + " times");
        for (int i = NUM_TIMES; i > 0; i--) {
            System.out.println("Decrementing counter");
            statefulRemoteCounter.decrement();
            System.out.println("Count after decrement is " + statefulRemoteCounter.getCount());
        }
    }

    /**
     * Looks up and returns the proxy to remote stateless calculator bean
     * 
     * @return
     * @throws NamingException
     */
    private static RemoteCalculator lookupRemoteStatelessCalculator() throws NamingException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        final Context context = new InitialContext(jndiProperties);

        // The JNDI lookup name for a stateless session bean has the syntax of:
        // ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>
        //
        // <appName> The application name is the name of the EAR that the EJB is deployed in
        // (without the .ear). If the EJB JAR is not deployed in an EAR then this is
        // blank. The app name can also be specified in the EAR's application.xml
        //
        // <moduleName> By the default the module name is the name of the EJB JAR file (without the
        // .jar suffix). The module name might be overridden in the ejb-jar.xml
        //
        // <distinctName> : AS7 allows each deployment to have an (optional) distinct name.
        // This example does not use this so leave it blank.
        //
        // <beanName> : The name of the session been to be invoked.
        //
        // <viewClassName>: The fully qualified classname of the remote interface. Must include
        // the whole package name.

        // let's do the lookup
        return (RemoteCalculator) context.lookup("ejb:/ejb-service/CalculatorBean!"
                + RemoteCalculator.class.getName());
    }

    /**
     * Looks up and returns the proxy to remote stateful counter bean
     * 
     * @return
     * @throws NamingException
     */
    private static RemoteCounter lookupRemoteStatefulCounter() throws NamingException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        final Context context = new InitialContext(jndiProperties);

        // The JNDI lookup name for a stateful session bean has the syntax of:
        // ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>?stateful
        //
        // <appName> The application name is the name of the EAR that the EJB is deployed in
        // (without the .ear). If the EJB JAR is not deployed in an EAR then this is
        // blank. The app name can also be specified in the EAR's application.xml
        //
        // <moduleName> By the default the module name is the name of the EJB JAR file (without the
        // .jar suffix). The module name might be overridden in the ejb-jar.xml
        //
        // <distinctName> : AS7 allows each deployment to have an (optional) distinct name.
        // This example does not use this so leave it blank.
        //
        // <beanName> : The name of the session been to be invoked.
        //
        // <viewClassName>: The fully qualified classname of the remote interface. Must include
        // the whole package name.

        // let's do the lookup
        return (RemoteCounter) context.lookup("ejb:/ejb-service/CounterBean!"
                + RemoteCounter.class.getName() + "?stateful");
    }
}


2.4 配置jboss-ejb-client.properties配置文件,放置到src下面

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false

remote.connections=default

remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false





 3 Demo详解

上面的Demo来自于官方网站,并且每一行都有详细的注释,下面的就带大家来分析一下,整个Demo中的关键点。

3.1 JNDI命名规则

For stateless beans

ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>

For stateful beans

ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>?stateful


首先ejb:命名空间,表明了,这是一个有关于EJB的远程调用的实例,其余的部分的解释如下


app-name:这是一个有关于.ear的前缀名称,如果我们部署在jboss中的是.ear结尾的文件的话,那么这个app-name就代表的是除去前缀的名称,例如如果我部署的是ejb-server.ear的话,那么app-name就代表的是ejb-server,如果没有的话,则为空表示,需要注意的是,前面没有斜线


module-name:这是有关于.jar包的前缀,正如我们这个例子中,我们把服务端最后打成了一个ejb-server.jar包,那么前缀就是ejb-server,这个模块的名称也可以为空,也可以重写


distinct-name:这是一个可选的名称,可以为空


bean-name:这是我们需要远程调用的bean的名称,记住这里是实现的bean的名称,不是接口的名称


viewClassName:这是我们所要调用bean的接口的全名,包括包的名称例如一个远程调用的bean的接口是RemoteCalculator,则这里必须是包名+类名,即为com.tgb.RemoteCalculator。再次需要注意的是,如果是有状态的bean的话,需要加上?stateful。下面再看我们上面例子的话,有状态的bean的全称为

ejb:/ejb-service/CounterBean!com.tgb.RemoteCounter?stateful

因为我们没有打ear包,所以名称可以省略。



3.2 jboss-ejb-client.properties详解

接下来我们来分析一下,有关JNDI properties的配置。

 jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

上面的Context.URL_PKG_PREFIXES是为了我们让JDNI API知道,我们目前带调用的是EJB命名空间的bean




 

你可能感兴趣的:(详解JBOSS系列一(利用JNDI,EJB远程调用本地的Bean))