EJBs and callbacks - can you do it?

Discussions

EJB design: EJBs and callbacks - can you do it?

  1. EJBs and callbacks - can you do it? (22 messages)

    Hi,

    I want to have a client that registers itself with an ejb (probably stateful session bean) such that when other triggers are received by the ejb, it calls the client.

    Is this possible with EJB?

    Thanks in advance,
    Chris

    Threaded Messages (22)

  2. EJBs and callbacks - can you do it?[ Go to top ]

    No, there is no facility for an EJB to make a call to the client (async or otherwise).

    We are approaching this by using JMS. A client makes a request by interacting with EJB session beans. On the server, the session beans post messages on JMS queues and then return to the (syncrounous) client call. The client (if interested) can subscribe on a well-known topic to listen for async server events as their requests are processed. There is a bit more to it than that, but that is the general idea.

    -Mark
  3. Yes I agree with you.
    Using JMS is the best choice in this case.
    Yihua he
  4. Yes, this can be done using RMI or RMI-IIOP. You simply create a remote object within the client and pass the remote reference to your EJB. The EJB can then use this reference to make callbacks to the client.

    Note that with RMI-IIOP you will lose some type checking. The method that accepts the remote reference takes an Object argument that must then be narrowed for use in the EJB.

    Also, be sure that the callback object stubs are available to the EJB.
  5. Hello,

    Could you show an example in how to implement that?.

    Thanks

  6. I have thought about the problem a bit more, and it seems like a simulation of asynchronous RMI callbacks is the best solution for single use callbacks, due to the overhead of creating and destroying queues. (If you can take advantage of a single publisher - multiple subscriber JMS situation that might not be the case.)

    Although it is not specifically about EJB, there is an excellent article about implementing asynchronous RMI from an old JavaWorld: Write high performance RMI servers and Swing clients. (It also talks about how to safely do updates to Swing objects from the callbacks.)
  7. EJBs and callbacks - can you do it?[ Go to top ]

    If you want to receive event notification from the server then you need to use a message service. Create a SessionEJBProxy (hides the JNDI lookups, etc)on you client that implements the javax.jms.MessageListener interface and registers with the JMS TopicSession as a TopicSubscriber. On the server your SessionEJB registers with the JMS TopicSession as a TopicPublisher. Now the client talk with the proxy, which talks with the EJB in the normal way. When the EJB becomes aware of a client interested event it puts an EventObject into a ObjectMessage which it publishes under the appropriate topic. The Proxy receives the objectMessage, reads the Event and notifies the client of the event. Of course the EventObject will need to encapsulate session identity (concurrency issue)
     so that the proxy/TopicSubcriber knows the message is for him.

    Dave
  8. EJBs and callbacks - can you do it?[ Go to top ]

    If you want to receive event notification from the server then you need to use a message service. Create a SessionEJBProxy (hides the JNDI lookups, etc)on you client that implements the javax.jms.MessageListener interface and registers with the JMS TopicSession as a TopicSubscriber. On the server your SessionEJB registers with the JMS TopicSession as a TopicPublisher. Now the client talk with the proxy, which talks with the EJB in the normal way. When the EJB becomes aware of a client interested event it puts an EventObject into a ObjectMessage which it publishes under the appropriate topic. The Proxy receives the objectMessage, reads the Event and notifies the client of the event. Of course the EventObject will need to encapsulate session identity (concurrency issue)
     so that the proxy/TopicSubcriber knows the message is for him.

    Dave
  9. Both a remote method invocation (RMI, RMI-IIOP) and a messaging (JMS) solution are possible, but the JMS solution has the important property that the server is not blocked waiting for a client response (asynchronous callback). If there is a problem with a synchronous callback (a long running client or a network error) the server resources are pinned. If there are multiple callbacks to be made, one client might be able to indefinitely delay callback delivery to the other clients. The scope of the system that needs to be considered from a performance/scalability/reliability perspective increases significantly once synchronous callbacks are used.
  10. Hello everybody. Can I pass back an EJB Handle in a Callback? using J2EE RI.

    I'm doing a chat server for Customer support. The customer post a request into the support queue, when there is an agent that can take the request, a ChatServer (you may call it ChatRoom) is created to start chatting.
    When the ChatServer is ready I call back the Customer passing back the ChatServer EJB Handle (server.getHandle()).

    This is done this way, following the event pattern, all interfaces are remote. These are the steps.

    1. -
    Customer creates a ChatRouter (Session Statefull EJB) and add itself as a RequestListener. chatRouter.addRequestListener(this).
    When the chat server for the request is ready the Router will notify the client by calling client.OnChatRequest(ChatRequestEvent).

    2. -
    The Router creates a ChatServer and passes the chatServer.getHandle() back the customer in a new ChatRequestEvent(javax.ejb.Handle server) object.
    By calling customer.OnChatRequestReady(ChatRequestEvent anEvent).

    3. -
    The Customer get the Chat Server EJB Handle from the event, from the Handle then it gets the EJBObject and casts it to the ChatServer Interface.
    IChatServer chatServer = (IChatServer) anEvent.getServerHandle().getEJBObject();
    Then it uses ChatServer Interface to register itself as a CharListener.
    chatServer.addChatListener(this);

    Looks easy to understand. This is what I have done.

       I'm using the J2EE RI App Server and I know that I don't need to compile the Stubs and Skels for the EJBs. The deploytool communicates with the J2EE RI and this creates the Client JAR with the EJBs Stubs and Skels. Then I have to run the client including the Client jar in the classpath.

       What I need to compile with RMIC it's the Client that will be called back by the App Server. The client is implementing (IRequestListener) a remote interface so it can be called back by the Router EJB.

       I'm able to compile the client using rmic -iiop, but I have to copy the J2EE.jar to $JAVA_HOME\jre\lib\ext, so the RMIC -IIOP is creating the Stub and Skel for the client. I think??

    This problem.

       The weird thing is that when I DON'T include a member variable javax.ejb.Handle in the ChatRequestEvent to pass back the Chat Server to the client, the callback DOES work!!!. But when I DO include the variable the Chat Server in the Event, the callback DOESN'T work????.

       I think that this is very weird and I have 2 hypothesizes.

       - It's illegal to pass back references to EJB Objects that haven't been created by the Client. I don't think that this can be true. A EJB should be able to pass reference of others EJBs that it creates, like a Object Factory.

       - The J2EE RI doesn't know how to pass back reference to EJB Objects that haven't been created by the Client. I will try another App Server, but I would like this for sure.

    Any comment will be appreciated.
    Thanks in advanced.

    Regards,
    Oscar.

    This is the Code,
    <pre>
    -- Chat Request Type use as enum to select the rigth request --
    public class CSapChatRequestType extends Object
    implements java.io.Serializable, java.lang.Comparable
    {
    // Ordinal of next suit to be created
    private static int nextOrdinal = 0;
    // VALUES
    public static final CSapChatRequestType CATALOG = new CSapChatRequestType("CATALOG");

    // Register values types to solve deserialization duplicated objects
    private static final CSapChatRequestType[] TYPES =
    { CATALOG };
    // If you wants the TYPES to be available.
    // public static final List VALUES = Collections.unmodifiableList(Arrays.asList(TYPES));
    // Solves serialization duplicated objects
    private Object readResolve()
    throws ObjectStreamException
    {
    return TYPES[_ordinal]; // Canonicalize
    }

    // name
    private final String _name ;
    // Assign an ordinal to this suit
    private final int _ordinal = nextOrdinal++;

    /** Creates new CSapChatRequestType */
    CSapChatRequestType()
    {
    _name = null;
    }

    private CSapChatRequestType(String aName)
    {
    _name = aName;
    }

    public String toString()
    {
    return _name;
    }

    public int toInt()
    {
    return (int) _ordinal;
    }

    public int compareTo(Object anObject)
    {
    return _ordinal - ((CSapChatRequestType)anObject)._ordinal;
    }

    public final boolean equals(Object that)
    {
    return super.equals(that);
    }

    public final int hashCode()
    {
    return super.hashCode();
    }
    }

    -- Chat Request Event Interface --
    -- When I don't include the _chatServerHandle member variable, the server can callback the client!!!. ---
    public class CSapChatRequestEvent extends java.util.EventObject
    implements java.io.Serializable
    {
    javax.ejb.Handle _chatServerHandle = null;
    /** Creates new CSapChatEvent */
    public CSapChatRequestEvent
    (ISapChatRequestSource aChatRequestSource, javax.ejb.Handle aChatServerHandle)
    {
    super(aChatRequestSource);
    _chatServerHandle = aChatServerHandle;
    }

    javax.ejb.Handle getChatServerHandle()
    {
    return _chatServerHandle;
    }
    }
    -- Chat Request Listener Interface --
    public interface ISapChatRequestListener extends java.util.EventListener, Remote
    {
    public void onChatRequestEvent(CSapChatRequestEvent anEvent)
    throws RemoteException;

    public CSapChatRequestType getChatRequestType()
    throws java.rmi.RemoteException;
    }
    -- Chat Request Source Interface --
    public interface ISapChatRequestSource extends Remote
    {
    public void addChatRequestListener(ISapChatRequestListener l)
    throws RemoteException;

    public void removeChatRequestListener(ISapChatRequestListener l)
    throws RemoteException;
    }
    -- EJB Chat Request Server Interface --
    public interface ISapChatServer extends ISapChatSource, ISapChatListener, EJBObject
    {
    }
    -- The Client Customer Class --
    -- I generate the Stub and Skel for this using rmic -iiop --
    public class CSapChatClientCustomer extends PortableRemoteObject implements ISapChatListener, ISapChatRequestListener
    {
    static Context initial = null;
    public static void main(String[] args)
    {
    try
    {
    java.util.Properties properties = new java.util.Properties();

    properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
    properties.put(javax.naming.Context.PROVIDER_URL,"iiop://localhost:1050");
    initial = new InitialContext(properties);
    CSapChatClientCustomer customer = new CSapChatClientCustomer();
    customer.startCustomerRequest();
    }
    catch (Exception ex)
    {
    System.err.println("Caught an unexpected exception!");
    ex.printStackTrace();
    }
    }

    /**
    Constructs object and exports it on default port.
    */
    public CSapChatClientCustomer() throws java.rmi.RemoteException
    {
    super();
    }

    public void startCustomerRequest()
    {
    try
    {
    Object objref = initial.lookup("MyChatRouter");
    ISapChatRouterHome home = (ISapChatRouterHome)PortableRemoteObject.narrow(objref, ISapChatRouterHome.class);
    ISapChatRouter ejb = home.create();
    ejb.addChatRequestListener(this);
    }
    catch (Exception ex)
    {
    System.err.println("Caught an unexpected exception!");
    ex.printStackTrace();
    }
    }

    // ISapChatRequestListener
    public CSapChatRequestType getChatRequestType()
    throws java.rmi.RemoteException
    {
    return CSapChatRequestType.CATALOG;
    }

    // ISapChatRequestListener
    public void onChatRequestEvent(CSapChatRequestEvent anEvent)
    throws java.rmi.RemoteException
    {
    try
    {
    System.out.println("CSapChatClient.onChatRequestEvent():begin");
    javax.ejb.Handle ejbHandle = anEvent.getChatServerHandle();
    if (null==ejbHandle)
    {
    System.out.println("no server handle");
    System.exit(0);
    }
    ISapChatServer ejb = (ISapChatServer) ejbHandle.getEJBObject();
    System.out.println("adding myself as a client");
    ejb.addChatListener(this);
    System.out.println("fire chat event to be distributed");
    ejb.onChatEvent(new CSapChatTextEvent(this, "Hello"));
    ejb.onChatEvent(new CSapChatTextEvent(this, "How do you do?"));
    System.out.println("CSapChatClient.onChatRequestEvent():end");
    }
    catch (Exception ex)
    {
    System.err.println("Caught an unexpected exception!");
    ex.printStackTrace();
    }
    }

    public void onChatEvent(CSapChatEvent anEvent)
    throws java.rmi.RemoteException
    {
    System.out.println("CSapChatClient.onChatEvent():begin");
    if (anEvent instanceof CSapChatTextEvent)
    {
    System.out.println("local chat Client get a Message: "+((CSapChatTextEvent)anEvent).getText());
    }
    System.out.println("CSapChatClient.onChatEvent():end");
    }
    }
    </pre>
  11. To the first question, it is definitely possible to do
    and ejb callback to a client (I wrote the code for WLS 6
    and jBoss and it does work for both):

    Your client defines a remote interface:

    public IClient extends Remote {
      public void callback() throws RemoteException;
    }

    Then you write the client implementation code and
    the server that binds it to jndi.

    For your EJB:

    public interface MyEJBHome extends EJBHome {
      public MyEJB create(IClient refClient)
        throws CreateException, RemoteException;
    }

    public class MyEJBBean implements SessionBean {
      private IClient client = null;

      public void ejbCreate(IClient refClient) {
        this.client = refClient;
      }

      //...
    }

    You have to generate the client stub in make it
    available to the server's classpath. That's all.

    Thierry Janaudy
    http://www.jyperion.org/
  12. Article there:

    http://www.jyperion.org/j2ee/ejbcab/

    Thierry
  13. Hello,
    about the article mentioned (http://www.jyperion.org/j2ee/ejbcab/
    does it work on other Application Servers
    besides JBoss? I think so as it uses RMI
    to do the callback...
    thanks
    Francesco

  14. EJBs and callbacks - can you do it?[ Go to top ]

    Our company www.xadra.com specializes in technology that brings true, real-time collaboration capabilities to EJB servers. We have some unique technology that lets you communicate with clients of virtually any type using bi-directional messaging between the client and the EJB server.

    You are not restricted to the use of EJB capable clients. We provide support for Flash, Shockwave, Java, and any XML-capable client.

    If there are additional questions on this topic, we will gladly participate.
  15. EJBs and callbacks - can you do it?[ Go to top ]

    I think a JMS solution is appropriate , as long as you are in the java world.
    In our current project, we need to perform callbacks to a c++ client.
    Yes, it's also possible with jms and corba callbacks.
    But it would be nicer, if we had a C++ api for the mom, which is not available (in out case from bea).
    That's bad in heterogeneuos environments (like in the most real life scenarios).
    Any ideas about that ?

    Best Regards wpl
  16. EJBs and callbacks - can you do it?[ Go to top ]

    Wolfgang,

    >>Any ideas about that ?<Yes. Change MoM... I know of one MoM product which certainly is C++ compatible, don't know if it can work with Bea: it's called Tempest Messenger from Tempest Software (www. tempestsoft.com).

    Have a look also at Talarian Sockets. Might also be a solution (I don't know the product, but it seems to be good stuff).

    Regards

    Bernard
  17. many JMS vendors will also use http 1.1 to traverse both sides of the dmz, such as sonicmq.
  18. EJBs and callbacks - can you do it?[ Go to top ]

    We have a BOF at J1 that would answer some of the questions related to that topic.

    http://servlet.java.sun.com/javaone/conf/bofs/2482/54192-sf2001.jsp

    It would go over enabling technologies for aynchronous and // using J2EE and propose an candidate architecture.

    Alexis
  19. We have developed an application using RMI Callback (with SUN's RMI implementation). But when the client (which is an applet) is behind the firewall (for incoming traffic only port 80 is available) it does not work. In RMI Callback client has to export its reference to the RMIServer. And that reference consists of the IP (where the applet is running) and a dynamic port. Although client can communicate with RMIServer (as there is no restrictions for outgoing traffic in firewall), but RMIServer can't communicate. Even you you can specuify a PORT number while exporting the reference from client, but you need to change the firewall configuration which may be always against the company policy.

    If anybody has come up with some solution to this please inform asap. We are using Weblogic6.0 as application server.
    How can I develop the RMIServer using weblogic app server?

    So whenever anyone is referring to RMICallback technolgy he/she should note the point that this technology is going to work in inranet , not in INTERNET. Even the provider of the technolocy should specify that clearly.
  20. It depends on your environment.
    My client is an Applet and my J2EE container is WebLogic. I use an RMI callback with a multi threaded client as described in the weblogic/examples/rmi/stock directory.

    The other alternative is PUB/SUB using JMS or Tibco RV.

    Here is a simple decision matrix:
    * For two applications communicating with no firewall between I would consider using JMS/Tibco

    * For two applications communicating where one is an Applet and the other is a WebLogic server I would consider using RMI Callbacks, as long as no firewall exist between the Applet and the J2EE container.

    * If an Applet need to communicate with a J2EE container over a firewall use Pushlets.


  21. Message Driven Bean will do the job.
  22. I used Jboss (Jbossmq).
  23. Hello!

    I have somewhat different question, than callbacks.

    We want to implement messaging, using WL6.1 and JMS on app server side, and MQSeries on EIS side. We did not make any projects with JMS before. We also want to retain our old code, that uses sockets and synchronous calls.

    Documentation for JMS 1.0 states, that "J2EE platform supports a new kind of enterprise bean, the message-driven bean, which allows J2EE applications to process JMS messages asynchronously. Session beans and entity beans allow you to send messages and to receive them synchronously, but not asynchronously."

    We want to receive messages asynchronously, but our current application design is synchronous, based on sockets, and does not support asynchronous message receiving. We want to "fool" the client of a session bean, to make it think, that calls to EIS are synchronous. We want to return response to client from the same business method, that was called to send the request. In this case, MDB are not suitable for us, because it is not clear, what should be done with received message (yes, yes, callbacks...). That is why we tried to implement message listener in the session bean, and luckily, the test program works! Basically, it is the example program from SUN, but modified for EJB session bean.

     * In ejbCreate() method the bean subscribes to the topic
     * business method call the remote system, effectively
       putting message in synchronous queue, and gets acknowledge.
     * after getting acknowledge, business method is going
       in sleep()
     * onMessage() callback method receives message from JMS,
       and if this message is destined to this particular bean
       instance, it puts response in shared bean field and wakes
       up main bean thread, using Thread.interrupt()
     * main bean thread awakes in business method, reads data
       from shared field and returns it to client.

    In this case single session bean instance has two threads, one is "main" thread, and another is created by WL server for onMessage() callback method. We used two thread control methods: sleep() and interrupt(). Chapter 18.2 of EJB 1.1 Spec says, that "The enterprise bean must not attempt to manage threads. The enterprise bean must not attempt to start, stop, suspend, or resume a thread; or to change a thread's priority or name. The enterprise bean must not attempt to manage thread groups." We believe, that it should be pretty safe to use sleep(), because no object monitors are lost.

    What do you guys think about how "legal" is this approach? This functionality is perfect for us, but because it is somewhat against specifications and official recommendations, we are not sure, that this functionality would not be considered by BEA as a bug, and would not be "corrected".

    It is also interesting, does this approach on other J2EE app servers.

    The code for session bean looks somewhat like this:

    === cut here ===

    public class SyncBean implements SessionBean, MessageListener {
      private Thread mf; // Main bean thread
      private String mesValue; // Value of a message

      ...

      //
      // This method is written in single-thread manner.
      // It is called by client, and returns as if it is synchronous.
      //

      public String businessMethod {
        ...
        try {
          // Save current thread in shared variable
          mf = Thread.currentThread();

          // Sleep until responce is received
          Thread.currentThread().sleep();
        } catch (InterruptedException e) { mf=null; }
        return mesValue;
      }

      ...

      //
      // Message handler runs in the other thread
      //

      public void onMessage(Message inMessage) {
        TextMessage msg = null;

        try {
            ...
            mesValue = msg.getText(); // Do not need to synchronize

            synchronized(this) {
              if(mf!=null) {
                mf.interrupt();
              }
            }
        } catch (JMSException e) { ... }
        } catch (Throwable te) { ... }
      }
    }

    === cut here ===

    Thanks!