是否可以给 EJB 服务调用一个回调

Is it possible give an EJB service call a callback

是否可以制作一个接受回调的 EJB 服务并在 调用服务的客户端?用例是:上传一个大字节数组 到将解析它并将结果转换为对象的服务,并且 坚持他们。我想通知客户完成了哪些步骤。

@Local
public interface MyService {
    Status upload(byte[] content, Callable<Void> onReceived, Calable<Void> onPersisting);
}

@Stateless(name = "MyService")
public class MyServiceImpl extends MyService {
    Status upload(byte[] content, Callable<Void> onReceived, Calable<Void> onPersisting) {
        // Invoke this because all date is transfered to server.
        onReceived.call();
        // Do the parsing stuff ...
        onPersisting.call();
        // Do the persisting stuff ...
        return new Status(...); // Done or failed or such.
    }
}

在客户端上,我传入了可调用对象:

Context ctx = ...
MyService service = ctx.get(...);

ctx.upload(bytes, new Callable<void() {
    @Override
    public Void call() {
        // Do something
        return null;
    }
}, new Callable<Void>() {
    @Override
    public Void call() {
        // Do something
        return null;
    }
});

在 EJB 中可以实现类似的功能吗?

我是 JEE 世界的新手:我知道客户端会得到一些 EJB 存根 接口和呼叫由 "background magic" 转移到服务器 真正的 EJB 实现。

案例一:使用本地业务界面(或无界面视图)

是的,只要您的服务只能通过本地业务接口访问,就可以。为什么?本地业务接口只能本地客户端访问

A local client has these characteristics [LocalClients].

  • It must run in the same application as the enterprise bean it accesses.

  • It can be a web component or another enterprise bean.

  • To the local client, the location of the enterprise bean it accesses is not transparent.

总结重要特征。它 运行 在同一个应用程序中分别在同一个 JVM 中,它是一个 Web 或 EJB 组件,并且所访问的 bean 的位置对于本地客户端来说不是透明的。请查看 LocalClients 了解更多详情。

下面是一个简单的 Hello World 示例。我的示例使用无界面视图,这相当于本地业务界面。

编辑:通过 JNDI 查找扩展的示例。

/** Service class */

import javax.ejb.Stateless;

@Stateless
public class Service {

    public void upload(final Callback callback) {
        callback.call();
    }

}

/** Callback class */

public class Callback {

    public void call() {
        System.out.println(this + " called.");
    }

}

/** Trigger class */

import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Singleton;

@Singleton
public class Trigger {

    @EJB
    Service service;

    @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
    public void triggerService() {
        System.out.println("Trigger Service call");
        service.upload(new Callback());
        //or by JNDI lookup and method overriding
        try {
           Service serviceByLookup = (Service) InitialContext.doLookup("java:module/Service");
           serviceByLookup.upload(new Callback() {
               @Override
               public void call() {
                   System.out.println("Overriden: " + super.toString());
               }
           });

       } catch (final NamingException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
    }
}

也可以将 Callback class 实现为 StatelessBean 并将其注入 Service class。

/** Service class */
@Stateless
public class Service {

    @EJB
    Callback callback;

    public void upload() {
        callback.call();
    }

}

案例二:使用远程业务接口

如果您使用的是远程接口,则无法将回调对象传递给您的 EJB。要将状态信息返回给您的客户,您必须使用 JMS。

下面是一个简短的启动示例。

@Remote
public interface IService {

    void upload();

}

@Stateless
public class Service implements IService {

    @EJB
    private AsyncUploadStateSender uploadStateSender;

    @Override
    public void upload() {
        for (int i = 0; i <= 100; i += 10) {
            uploadStateSender.sendState(i);
            try {
                Thread.sleep(1000L);
            } catch (final InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


@Stateless
public class AsyncUploadStateSender {

    @Resource(lookup = "jms/myQueue")
    private Queue queue;

    @Inject
    private JMSContext jmsContext;

    @Asynchronous
    public void sendState(final int state) {
        final JMSProducer producer = jmsContext.createProducer();
        final TextMessage msg = jmsContext.createTextMessage("STATE CHANGED " + state + "%");
        producer.send(queue, msg);
    }
}

public class Client {

    public static void main(final String args[]) throws NamingException, InterruptedException, JMSException {

        final InitialContext ctx = ... // create the InitialContext;
        final IService service = (IService) ctx.lookup("<JNDI NAME OF IService>");
        final ConnectionFactory factory = (ConnectionFactory) ctx.lookup("jms/__defaultConnectionFactory");
        final Queue queue = (Queue) ctx.lookup("jms/myQueue");
        // set consumer
        final Connection connection = factory.createConnection();
        final MessageConsumer consumer = connection.createSession().createConsumer(queue);
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(final Message msg) {
                try {
                    System.out.println(((TextMessage) msg).getText());
                } catch (final JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        connection.start();
        // start upload
        service.upload();
        Thread.sleep(1000L);
    }
}

注意:您必须在应用程序服务器中创建队列 jms/myQueue 和连接工厂 jms/__defaultConnectionFactory 才能使示例正常工作。