EJB Bad Practices: Use of AWT/Swing
Description
EJB Bad Practices: Use of AWT/Swing is a vulnerability where an Enterprise JavaBean violates the EJB specification by incorporating AWT (Abstract Window Toolkit) or Swing GUI functionality. The EJB specification explicitly prohibits bean providers from using "AWT functionality to attempt to output information to a display, or to input information from a keyboard." EJBs are designed to handle business logic in server-side environments where no display or keyboard interaction is available. Attempting to use GUI components in this context results in failures, as server systems typically run headless without access to display devices.
Risk
Using AWT/Swing in EJBs creates immediate operational failures and architectural violations. In headless server environments, GUI operations throw HeadlessException, causing runtime failures. Even if a server has display capability, GUI operations block threads waiting for user input that never comes, causing application hangs. The design violates the separation of concerns principle—mixing presentation logic with business logic makes code non-reusable and difficult to maintain. Applications become non-portable across different deployment environments. Container management of bean lifecycle becomes problematic when beans hold GUI resources.
Solution
Never use AWT or Swing components in EJB implementations. Follow the Model-View-Controller (MVC) pattern, keeping EJBs strictly as the model layer containing business logic. Use web tier technologies (JSP, JSF, Servlets) or dedicated client applications for presentation. If EJBs need to provide data for display, return data transfer objects (DTOs) that the presentation layer can render. For reporting or document generation, use server-side libraries that don't require a display (like Apache POI for Excel, iText for PDF). If absolutely necessary to perform graphical operations, use headless mode with BufferedImage.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - Applications fail to function correctly when GUI operations cannot be performed in server environments. |
| Availability | Scope: Availability DoS: Crash, Exit, or Restart - GUI operations in headless environments throw exceptions, potentially crashing the application. |
Example Code
Vulnerable Code
// Vulnerable: EJB using AWT/Swing components
import javax.ejb.Stateless;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@Stateless
public class VulnerableReportBean implements ReportService {
// Vulnerable: Creating AWT Frame in EJB
public void displayReport(ReportData data) {
// This will fail in headless server environment
Frame frame = new Frame("Report"); // HeadlessException!
frame.setSize(800, 600);
TextArea textArea = new TextArea();
textArea.setText(data.toString());
frame.add(textArea);
frame.setVisible(true); // No display available on server!
}
}
// Vulnerable: Stateless bean extending Component
@Stateless
public class VulnerableInputBean extends Component implements KeyListener {
// Vulnerable: Implementing KeyListener in EJB
@Override
public void keyPressed(KeyEvent e) {
// No keyboard input available on server
processKey(e.getKeyCode());
}
@Override
public void keyReleased(KeyEvent e) { }
@Override
public void keyTyped(KeyEvent e) { }
// Vulnerable: Attempting to capture keyboard input
public String getUserInput() {
// This makes no sense in server context
TextField input = new TextField(); // HeadlessException!
input.addKeyListener(this);
// Will never receive input
return input.getText();
}
}
// Vulnerable: Using Swing in EJB
@Stateless
public class VulnerableDialogBean implements DialogService {
// Vulnerable: Creating Swing dialog in EJB
public boolean confirmAction(String message) {
// This will fail on headless server
int result = JOptionPane.showConfirmDialog(
null,
message,
"Confirm",
JOptionPane.YES_NO_OPTION
); // HeadlessException in server environment!
return result == JOptionPane.YES_OPTION;
}
// Vulnerable: Creating Swing frame
public void showStatus(String status) {
JFrame frame = new JFrame("Status"); // HeadlessException!
JLabel label = new JLabel(status);
frame.add(label);
frame.pack();
frame.setVisible(true); // No display!
}
}
// Vulnerable: Using Graphics for image processing incorrectly
@Stateless
public class VulnerableImageBean implements ImageService {
// Vulnerable: Creating window for graphics
public byte[] createChart(ChartData data) {
// Vulnerable: Requires display context
Frame frame = new Frame(); // HeadlessException!
frame.addNotify();
Image image = frame.createImage(400, 300);
Graphics g = image.getGraphics();
// Draw chart
g.drawRect(0, 0, 399, 299);
// ... more drawing
g.dispose();
return convertToBytes(image);
}
}
Fixed Code
// Fixed: EJB returns data, presentation layer handles display
import javax.ejb.Stateless;
import java.util.List;
@Stateless
public class SecureReportBean implements ReportService {
@PersistenceContext
private EntityManager em;
// Fixed: Return data for presentation layer to display
public ReportDTO getReport(Long reportId) {
// Business logic only - no GUI
Report report = em.find(Report.class, reportId);
// Return DTO for presentation layer
ReportDTO dto = new ReportDTO();
dto.setTitle(report.getTitle());
dto.setData(report.getData());
dto.setGeneratedDate(new Date());
return dto;
}
// Fixed: Generate report as bytes for download
public byte[] generatePdfReport(Long reportId) {
Report report = em.find(Report.class, reportId);
// Use server-side PDF library (no GUI required)
return PdfGenerator.createReport(report);
}
}
// Fixed: Use JSP/JSF for presentation, EJB for logic
// JSF Managed Bean (presentation layer)
@Named
@ViewScoped
public class ReportController implements Serializable {
@Inject
private ReportService reportService; // EJB
private ReportDTO currentReport;
public void loadReport(Long reportId) {
// Get data from EJB
currentReport = reportService.getReport(reportId);
}
public ReportDTO getCurrentReport() {
return currentReport;
}
}
// Fixed: Separate presentation logic from business logic
// EJB handles business logic only
@Stateless
public class SecureInputBean implements InputService {
// Fixed: No keyboard input - receive data as parameters
public ProcessResult processUserInput(UserInputDTO input) {
// Validate input received from presentation layer
validateInput(input);
// Process business logic
ProcessResult result = new ProcessResult();
result.setProcessedValue(calculate(input.getValue()));
result.setTimestamp(new Date());
return result;
}
private void validateInput(UserInputDTO input) {
if (input == null || input.getValue() == null) {
throw new ValidationException("Input required");
}
}
}
// Fixed: Use headless mode for server-side image generation
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.Color;
import javax.imageio.ImageIO;
@Stateless
public class SecureImageBean implements ImageService {
// Fixed: Use BufferedImage - works in headless mode
public byte[] createChart(ChartData data) {
// Ensure headless mode (usually set at JVM startup)
// -Djava.awt.headless=true
// Create image without display
BufferedImage image = new BufferedImage(
400, 300, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
// Draw chart
g.setColor(Color.WHITE);
g.fillRect(0, 0, 400, 300);
g.setColor(Color.BLACK);
g.drawRect(0, 0, 399, 299);
// Draw data
drawChartData(g, data);
g.dispose();
// Convert to bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(image, "PNG", baos);
} catch (IOException e) {
throw new RuntimeException("Image generation failed", e);
}
return baos.toByteArray();
}
private void drawChartData(Graphics2D g, ChartData data) {
// Server-side chart rendering logic
}
}
// Fixed: Use server-side charting library
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
@Stateless
public class SecureChartBean implements ChartService {
// Fixed: Use JFreeChart (works headless)
public byte[] createBarChart(ChartDataDTO data) {
// JFreeChart works in headless mode
JFreeChart chart = ChartFactory.createBarChart(
data.getTitle(),
data.getCategoryLabel(),
data.getValueLabel(),
createDataset(data),
PlotOrientation.VERTICAL,
true, // legend
true, // tooltips
false // urls
);
// Export to PNG bytes
try {
return ChartUtilities.encodeAsPNG(
chart.createBufferedImage(600, 400));
} catch (IOException e) {
throw new RuntimeException("Chart generation failed", e);
}
}
}
// Fixed: Web service endpoint for remote clients
@Stateless
@WebService
public class SecureReportWebService {
@Inject
private ReportService reportService;
// Fixed: SOAP/REST service returns data, client renders
@WebMethod
public ReportDTO getReport(@WebParam(name="reportId") Long reportId) {
return reportService.getReport(reportId);
}
@WebMethod
public byte[] getReportPdf(@WebParam(name="reportId") Long reportId) {
return reportService.generatePdfReport(reportId);
}
}
CVE Examples
No specific CVEs are commonly attributed to this CWE, as it primarily affects application functionality rather than security vulnerabilities.
References
- MITRE Corporation. "CWE-575: EJB Bad Practices: Use of AWT Swing." https://cwe.mitre.org/data/definitions/575.html
- Oracle. "Enterprise JavaBeans Specification."
- Oracle. "Java Headless Mode Documentation."