11 Replies Latest reply on Jan 7, 2008 5:12 AM by stfkbf

    Re-obtaining a reference to an existing stateful EJB3 instan

    fschoning

      Hi,

      I have a proprietary communications protocol where clients come in over TCP sockets. I then create a stateful EJB3 per client. If my socket connection fails I would like to reattach the client back to the existing stateful EJB3 instance using a sessionid of sorts.

      Does anyone know how I can re-obtain the reference to the existing stateful EJB3? The system eventually needs to be clustered and hence the client might reconnect to a socket on one server and the stateful EJB instance is on another server.

      Thanks for the help.

        • 1. Re: Re-obtaining a reference to an existing stateful EJB3 in
          tzwoenn

          How about storing the reference of the stateful bean in the JNDI context?

          e.g.

          IntitialContext ctx = new InitialConext();
          ctx.bind("yourSubCtxHere/" + obj.getSessionID(), obj);


          BUT: Dont forget to remove this binding in the @Remove method of the stateful bean. Otherwise you will flood your JNDI context and maybe get invalid references...

          • 2. Re: Re-obtaining a reference to an existing stateful EJB3 in
            fschoning

            Thanks for the suggestion. Can JNDI efficiently handle thousands of session objects like mine? I was thinking along the lines of a database and storing the serialized handle to the ejb.

            I also don't know if one can just store the reference to the ejb and get access to the ejb instance from another clustered server. I saw some ejb2.1 sample code where one can get a hold of a handle to the ejb from its home interface - getHandle(). This handle is apparently clusterable and serializable. Can one get a similar type of handle from a ejb3?

            • 3. Re: Re-obtaining a reference to an existing stateful EJB3 in
              tzwoenn

              Well, storing the serializable SfSB references into the database would be the best solution. There is just some problems:

              1. obtained EJB3 SfSB references are not serializable at all (if I am wrong, I would be pleased to be corrected)

              2. EJB3 SfSB are not allowed to use Handle.getEJBObject() This will result in an RuntimeException with a note, that the functionality is not implemented. Maybe one of the developers will read this and tell us if the functionality will be implemented sometime or if it is forbidden for EJB3 beans to retrieve this kind of handle.


              Back to JNDI :)

              I was not able to do any performance test with the JBoss JNDI context, but this should not be a bottleneck, cause you only do some bindings and unbindings in a single JVM.
              I am not an expert in clustering so i cant tell you, if this solution will work in a clustering scenario. The JNDI context acts as a directory service and should be accessible from all cluster nodes, either by synchronize their context or by electing one of the nodes to share its context with the others.


              PS: Due to my diploma thesis I am going to deal with the same problem: storing EJB3 SfSB references somewhere, so i can retrieve them later. In my case clustering is not a required feature, so i could easily use some kind of static Hashmap for the references. But I would be more pleased, if there was another solution to this problem.

              • 4. Re: Re-obtaining a reference to an existing stateful EJB3 in
                fschoning

                1. Does JNDI not serialize the references? I will try this out when I have a moment.

                2. I have found that ejb2.1's can do this. From the home interface one can call getHandle(). This handle is serializable and can be used from different VM's. Do a search for getHandle() in these forums.

                I have also found that there is a compatibility layer for ejb3's to behave like ejb2.1's. In this case a home interface must be defined and the getHandle() function is available. I don't know if it throws unimplemented exceptions but there is test code that uses it. I don't have the test class names handy, but do a search for getHandle() in the cvs code in the ejb3 directory. I wouldn't want to use this though as I might as well use ejb2.1's then.

                • 5. Re: Re-obtaining a reference to an existing stateful EJB3 in
                  tzwoenn

                  1. Due to the API, for binding there is no need objects to be serializable.

                  2. EJB 2.1 can and will do it. I have used this kind of solution before.

                  For EJB3 i had wrote a small bean to test the functionality. Indeed you can get a serializable handle by using:

                  sesionContext.getEJBObject().getHandle();


                  But when it's time to deserialize the handle, the recent JBoss release throws the mentioned RuntimeException.

                  If you are able to find a solution to this problem that still uses EJB3, i would be happy to hear about it :) At the moment i am stucked with entity mapping due to heavy cross references.

                  • 6. Re: Re-obtaining a reference to an existing stateful EJB3 in
                    iterrell

                    I'm currently having trouble reobtaining a reference to a sfsb, myself. Unfortunately, I've tried every method listed in this thread. :)

                    My initial (perhaps naive) approach was to stick a reference to the bean in the HttpSession, but after approximately 2-5 minutes method invocations via that reference go to a new bean.

                    I tried putting it in JNDI with something like the following:

                    Putting in:

                    IntitialContext ctx = new InitialConext();
                    SFSB bean = (SFSB)ctx.lookup(SFSB.class.getName());
                    ctx.bind(httpSession.getId(), bean);
                    


                    Retrieving:
                    IntitialContext ctx = new InitialConext();
                    SFSB bean = (SFSB)ctx.lookup(httpSession.getId());
                    


                    This approach threw no exceptions, but invoking methods against these references always went to different beans.

                    I've also tried putting the reference in a static HashMap<SessionID,Bean>, but this approach has the same trouble as putting it directly in the session (i.e. after 2-5 minutes method invocations go to a new bean).

                    I even tried using sessionContext.getEJBObject().getHandle(); and storing *that* in the session; unfortunately it was always null.

                    I'm using EJB3 on JBoss 4.0.3SP1.

                    Does anyone have any suggestions?

                    • 7. Re: Re-obtaining a reference to an existing stateful EJB3 in
                      fschoning

                      I have used the following successfully but have not tested it extensively or over a number of session calls. I have also not tested this across different VM's.

                      ChatMsgHandler ejbhandler = (ChatMsgHandler)ctx.lookup(ChatMsgHandler.class.getName());
                      

                      To serialize the ejb3 proxy object:
                      ByteArrayOutputStream baos = new ByteArrayOutputStream();
                      ObjectOutputStream oos = new ObjectOutputStream(baos);
                      oos.writeObject(ejbhandler);
                      

                      To deserialize the ejb proxy object:
                      ByteArrayInputStream file = new ByteArrayInputStream(baos.toByteArray());
                      ObjectInputStream input = new ObjectInputStream(file);
                      ChatMsgHandler ejbhandler2 = (ChatMsgHandler)input.readObject();
                      


                      • 8. Re: Re-obtaining a reference to an existing stateful EJB3 in
                        tzwoenn

                        Does this piece of code really work? Are you able to call business methods at the session bean after deserialization?

                        This would mean all ejb3 proxys to be serializable, which would make me very happy... :]

                        • 9. Re: Re-obtaining a reference to an existing stateful EJB3 in
                          fschoning

                          It appears to work fine for me :-) and I successfully call business methods after deserialization. I have not tested it over a long session containing numerous calls and also not across different VM's for clustering.

                          Let me know how it works for you?

                          • 10. Re: Re-obtaining a reference to an existing stateful EJB3 in
                            tzwoenn

                            Well, i have done some tests and as far as i can see you can manually serialize ejb3 proxys using objectstreams. But when it came to automatical serialization (done by the container), it wont work, even if set the business interfaces serializable by hand, by extending java.io.Serializable.

                            But... i also had some problems using the containers object serialization mechanism, when trying to persist some obviously serializable objects, getting some uninterpretable ClassCastExeceptions. Doing it manually (again using objectstreams) in @preupdate respectively @postloaded annotated methods work fine however. Compared to persisting some "simple" objects like Hashmaps the container is doing a rather good job.

                            • 11. Re: Re-obtaining a reference to an existing stateful EJB3 in
                              stfkbf

                              Serializing and deserializing (and transforming the reference to something that can be transmitted via a web service) works for me also (serializing and deserializing the remote interface). When deserializing, the remote interface points to the original bean.

                              However if the serialized object is returned through a web service and then reused in a later web service call (by passing the serialized object to the service), the serialized object points to a new bean instance instead of the original one.

                              Is there an obvious explanation for this that i have missed?