A component annotated or configured as stateless in an enterprise component framework declares a mutable instance field that is not managed by the framework’s dependency injection mechanism.

Why is this an issue?

Stateless server components are pooled and reused by the application container. When a client invokes a business method, the container assigns an available instance from the pool to handle that request. After the method completes, the same instance returns to the pool and may be assigned to a completely different client on the next invocation.

If you store data in an instance variable during one client’s invocation, that data remains in memory and will be visible to the next client who receives that component instance. This creates several problems:

The container does not reset or clear instance variables between invocations. It only does so when creating a new instance or during specific lifecycle callbacks.

Safe instance fields

Container-injected resources are safe because they are either thread-safe or context-aware:

These resources are managed by the container to work correctly in a pooled environment.

In Java EE/Jakarta EE, these safe resources are typically injected using annotations: @EJB for component references, @Inject for CDI beans, @PersistenceContext for entity managers, @PersistenceUnit for entity manager factories, @Resource for JNDI resources and data sources, and @WebServiceRef for web service references.

What is the potential impact?

When mutable state leaks between clients:

How to fix it

The right fix depends on the intended purpose of each instance field.

Code examples

Noncompliant code example

@Stateless
public class OrderService {
    private int lastOrderId;      // Noncompliant
    private int maxRetries = 3;   // Noncompliant
    private int requestCount;     // Noncompliant

    public void processOrder(Order order) {
        lastOrderId = order.getId();
        requestCount++;
        // lastOrderId and requestCount may leak to the next client
    }
}

Compliant solution

@Stateless
public class OrderService {
    private static final int MAX_RETRIES = 3;  // Compliant; safe as a static final field

    @EJB
    private OrderStats orderStats;  // Compliant; shared state delegated to a helper singleton EJB

    public void processOrder(Order order) {
        int lastOrderId = order.getId();  // Compliant; moved to a method-local variable
        orderStats.incrementRequestCount();
    }
}

@Singleton
public class OrderStats {
    private int requestCount;

    @Lock(LockType.WRITE)
    public void incrementRequestCount() {
        requestCount++;
    }

    @Lock(LockType.READ)
    public int getRequestCount() {
        return requestCount;
    }
}

If a value must persist across multiple method calls for the same client, consider switching to a @Stateful session bean instead, which provides a dedicated instance per client.

Resources

Documentation