J2EE Bad Practices: Direct Use of Threads
Description
J2EE Bad Practices: Direct Use of Threads is a vulnerability that occurs when a web application directly creates and manages threads instead of using container-managed concurrency. Thread management in a web application is forbidden in some circumstances (particularly in EJBs) and is always highly error prone. Direct thread management risks interfering unpredictably with container behavior and frequently produces difficult-to-diagnose issues like deadlocks, race conditions, and synchronization errors.
Risk
Direct thread management in J2EE applications violates container contracts and creates unpredictable behavior. The container cannot manage resources properly when application code creates its own threads, potentially leading to resource exhaustion. Threads created outside the container's control may hold resources during container shutdown, causing hangs or data corruption. Race conditions and deadlocks become more likely and harder to debug. Thread lifecycle doesn't align with request lifecycle, complicating error handling and resource cleanup. In clustered environments, direct threading can cause inconsistent behavior across nodes.
Solution
Use framework-provided mechanisms for parallel execution. In EJB environments, use the EJB Timer Service, @Asynchronous methods, or ManagedExecutorService. In Spring, use @Async with proper executor configuration. Use the Java EE Concurrency Utilities (JSR 236) which provide ManagedExecutorService, ManagedScheduledExecutorService, and ManagedThreadFactory. These container-managed resources integrate properly with transaction management, security context propagation, and resource lifecycle. If legacy threading code exists, refactor to use ExecutorService with proper shutdown handling.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - The weakness negatively impacts application reliability and stability through unpredictable threading behavior. |
Example Code
Vulnerable Code
// Vulnerable: Direct thread creation in servlet
public class VulnerableServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
final String data = request.getParameter("data");
// Vulnerable: Creating thread directly
Thread worker = new Thread(new Runnable() {
public void run() {
// Background processing
processData(data);
}
});
worker.start();
response.getWriter().write("Processing started");
}
}
// Vulnerable: Using raw Thread in EJB
@Stateless
public class VulnerableEJB {
public void processAsync(final String input) {
// Vulnerable: EJBs must not create threads
new Thread(() -> {
heavyProcessing(input);
}).start();
}
public void scheduledTask() {
// Vulnerable: Custom thread for scheduling
Thread scheduler = new Thread(() -> {
while (true) {
try {
Thread.sleep(60000);
runPeriodicTask();
} catch (InterruptedException e) {
break;
}
}
});
scheduler.setDaemon(true);
scheduler.start();
}
}
// Vulnerable: Thread pool not managed by container
@WebServlet("/process")
public class VulnerablePoolServlet extends HttpServlet {
// Vulnerable: Static thread pool outside container control
private static ExecutorService executor =
Executors.newFixedThreadPool(10);
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
executor.submit(() -> processRequest(request));
}
// No proper shutdown handling!
}
Fixed Code
// Fixed: Using ManagedExecutorService (Java EE 7+)
@WebServlet("/process")
public class SecureServlet extends HttpServlet {
@Resource
private ManagedExecutorService executor;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
final String data = request.getParameter("data");
// Fixed: Container-managed executor
executor.submit(() -> processData(data));
response.getWriter().write("Processing started");
}
}
// Fixed: Using @Asynchronous in EJB
@Stateless
public class SecureEJB {
@Asynchronous
public Future<String> processAsync(String input) {
// Fixed: Container manages the thread
String result = heavyProcessing(input);
return new AsyncResult<>(result);
}
@Resource
private TimerService timerService;
@PostConstruct
public void init() {
// Fixed: Use container's timer service
timerService.createIntervalTimer(60000, 60000,
new TimerConfig("periodicTask", false));
}
@Timeout
public void runPeriodicTask(Timer timer) {
// Container manages scheduling
performScheduledWork();
}
}
// Fixed: Using ManagedThreadFactory
@WebServlet("/managed")
public class SecureManagedServlet extends HttpServlet {
@Resource
private ManagedThreadFactory threadFactory;
@Resource
private ManagedExecutorService executor;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
// Fixed: Container-managed thread factory
Runnable task = () -> processRequest(request);
executor.submit(task);
}
}
// Fixed: Spring @Async with proper configuration
@Service
public class SecureSpringService {
@Async("taskExecutor") // Uses configured executor
public CompletableFuture<String> processAsync(String input) {
String result = heavyProcessing(input);
return CompletableFuture.completedFuture(result);
}
}
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.initialize();
return executor;
}
}
// Fixed: Proper executor lifecycle in servlet context
@WebListener
public class ExecutorLifecycleListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// Initialization handled by container
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Fixed: Proper shutdown if using custom executor
ExecutorService executor =
(ExecutorService) sce.getServletContext().getAttribute("executor");
if (executor != null) {
executor.shutdown();
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}
}
CVE Examples
No specific CVEs are listed for this CWE. The vulnerability pattern appears in:
- J2EE applications using raw Thread creation
- EJBs violating threading restrictions
- Web applications with unmanaged thread pools
References
- MITRE Corporation. "CWE-383: J2EE Bad Practices: Direct Use of Threads." https://cwe.mitre.org/data/definitions/383.html
- Java EE 7 Tutorial. "Concurrency Utilities for Java EE."