javaweb-servicesjakarta-eewildflystateful-session-bean

Mapping WebService-Client-Access to @Stateful


if I understand it right a @Stateful bean saves the state. If the client does a request again it comes back to the same instance. So it's possible to save class-attributes, what's not possible in @Stateless. In an other thread here someone wrote "it's like a classical java instance, every injection gets it's own instance of this bean".

But I don't understand how the mapping of the request to the @Stateful bean works - what is to do that it works? This question goes out for two cases:

  1. I call @Stateful by a webservice by the client software. Is it an ID I have to send with it? But what is the ID and how do the container knows that this is the identify-attribut and routes it to the right @Stateful bean?
  2. I call @Stateful out of an @Stateless bean. As example if the client first calls a @Stateless bean and is redirect to his @Stateful bean.

This question is not for the technical process of the container / server-software, it's for the specific doing at the development. Thank you for your support.

Greetings


Solution

  • That's unfortunately not the way web services work. The stateful bean is only stateful for the stateless bean. And not for a client. And that's very dangerous for several reasons:

    -The stateless bean saves state of a call in its stateful reference. But the next call of the stateless bean can be happen in another context/by another client.

    -The stateful bean can be destroyed by the container while the stateless is still alive/in the pool.

    You can use stateful beans with remote-calls or in web applications but not in the context of webservices.

    A webservice is per definition without any application state. The Java EE-Servlet listens for the requests and call one stateless bean implementation from a pool of instances.

    If you really want to implement stateful web services, you must do it on your own. The following example will work in a Java EE 6-container:

    ///  Client depended values(your statefull bean)
    public class SessionValues {
       private final List<String> values = new ArrayList<String>(); 
    
       public void addValue(String s){
            values.add(s);
        }
    
        public List<String> loadValues(){
            return Collections.unmodifiableList(values);
        }
    }
    

    You can store the sessions in a singleton(your own pool)

     @Singleton
     @Startup
     public class StatefullSingleton {
       private final Map<String, SessionValues> sessions = new Hashtable<String, SessionValues>();
    
        @Lock(LockType.READ)
        public void addValue(String sessionId, String value) {
          if (!sessions.containsKey(sessionId))
              sessions.put(sessionId, new SessionValues());
          SessionValues p = sessions.get(sessionId);
          p.addValue(value);
      }
    
      @Lock(LockType.READ)
      public List<String> loadValues(String sessionId) {
        if (sessions.containsKey(sessionId))
            return sessions.get(sessionId).loadValues();
        else
            return new ArrayList<String>();
      }
    }
    

    and inject the singleton in the stateless webservice beans(the pool, the singleton and the calls of the singleton are managed by the Java EE-Container):

    @Stateless
    @WebService
    public class WebserviceBean {
    
    @Inject
    private StatefullSingleton ejb;
    
    public void addvalue(String sessionId, String value) {
        ejb.addValue(sessionId, value);
    }
    
    public List<String> loadValues(String sessionId) {
        return ejb.loadValues(sessionId);
    }
    

    }

    The example above is only a pattern. You must be very carefully with the session-id and the multithreading if you want to implement it in production.

    Edit: remove the unnecessary @localBean