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.
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.
When mutable state leaks between clients:
The right fix depends on the intended purpose of each instance field.
@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
}
}
@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.