【问题标题】:Is it possible give an EJB service call a callback是否可以给 EJB 服务调用一个回调
【发布时间】:2016-09-08 18:13:13
【问题描述】:

是否可以创建一个 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 存根 接口和调用通过“后台魔法”转移到服务器 真正的 EJB 实现。

【问题讨论】:

  • 这可能对你有帮助,stackoverflow.com/questions/2407159/…
  • MyService 是本地还是远程业务接口?如果是远程业务接口,那么不,你不能这样做。如果它是本地业务接口,那么是的,这应该可以正常工作。
  • 我必须先了解差异才能回答这个问题......
  • 据我了解代码库:所有业务服务接口都是@Local,但它们是通过应用程序客户端容器从远程客户端调用的。哪个是第三方模块。
  • 您还有问题吗?

标签: java jakarta-ee ejb


【解决方案1】:

案例 1:使用本地业务接口(或无接口视图)

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

本地客户端具有这些特征[LocalClients]

  • 它必须在与其访问的企业 bean 相同的应用程序中运行。

  • 它可以是一个 Web 组件或另一个企业 bean。

  • 对于本地客户端,它访问的企业 bean 的位置是不透明的。

总结重要特征。它分别在同一个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 类实现为StatelessBean 并将其注入Service 类。

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

    @EJB
    Callback callback;

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

}

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

如果您使用的是远程接口,则无法将回调对象传递给您的 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 才能使示例正常运行。

【讨论】:

  • 我知道这样的事情是可能的。我想将回调作为参数提供给服务方法。
  • @Weltraumschaf,当服务具有本地业务接口时,它应该像我的第一个示例中所示那样工作。
  • 我是否理解正确,如果通过应用程序客户端容器调用服务是不可能的?
  • @Weltraumschaf,没错,但您也不能通过应用程序客户端容器调用本地业务接口。
  • 嗯,这就是我想做的。如何解决长期运行的 EJB 服务更新客户端进度的问题?就像我可以在 HTTP 环境中使用 websockets 一样:在那里我可以保持与客户端的连接处于打开状态,并将数据连续发送回客户端。
猜你喜欢
  • 1970-01-01
  • 2020-12-15
  • 2015-02-13
  • 1970-01-01
  • 1970-01-01
  • 2011-05-06
  • 2019-01-22
  • 1970-01-01
  • 2018-05-19
相关资源
最近更新 更多