Sending emails programmatically is a common requirement for many desktop applications, from newsletter tools to automated notification systems. While Java provides the robust javax.mail API (now Jakarta Mail) for handling email protocols, combining it with a Swing GUI creates a user-friendly desktop utility.
This article guides you through building a fully-featured SMTP email client with a graphical user interface in Java, supporting attachments, HTML content, and secure authentication.
Architecture and Technologies
Our application will be built on two main pillars:
- Frontend (GUI): Java Swing for the user interface.
- Backend (Email): Jakarta Mail (formerly JavaMail) for SMTP communication.
Project Setup: Dependencies
You need to add the Jakarta Mail dependency to your project.
Maven:
<dependencies> <dependency> <groupId>com.sun.mail</groupId> <artifactId>jakarta.mail</artifactId> <version>2.0.1</version> </dependency> <!-- Also ensure you have the Jakarta Activation API --> <dependency> <groupId>com.sun.activation</groupId> <artifactId>jakarta.activation</artifactId> <version>2.0.1</version> </dependency> </dependencies>
Gradle:
dependencies {
implementation 'com.sun.mail:jakarta.mail:2.0.1'
implementation 'com.sun.activation:jakarta.activation:2.0.1'
}
Building the GUI: A Step-by-Step Approach
We'll create a form with fields for all essential email components and a console for feedback.
Main Application Frame
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
public class EmailSenderGUI extends JFrame {
// Form components
private JTextField smtpHostField, smtpPortField, usernameField;
private JPasswordField passwordField;
private JCheckBox authCheckBox, tlsCheckBox;
private JTextField toField, ccField, bccField, subjectField;
private JTextArea messageArea;
private JButton attachButton, sendButton, clearButton;
private JList<File> attachmentList;
private DefaultListModel<File> attachmentListModel;
private JTextArea consoleArea;
public EmailSenderGUI() {
initializeGUI();
}
private void initializeGUI() {
setTitle("Java SMTP Email Sender");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout(10, 10));
// Create main panels
JPanel serverPanel = createServerPanel();
JPanel recipientPanel = createRecipientPanel();
JPanel messagePanel = createMessagePanel();
JPanel attachmentPanel = createAttachmentPanel();
JPanel buttonPanel = createButtonPanel();
JPanel consolePanel = createConsolePanel();
// Arrange panels in the frame
JPanel northPanel = new JPanel(new GridLayout(3, 1, 5, 5));
northPanel.add(serverPanel);
northPanel.add(recipientPanel);
northPanel.add(attachmentPanel);
add(northPanel, BorderLayout.NORTH);
add(messagePanel, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
add(consolePanel, BorderLayout.SOUTH);
// Adjust layout
pack();
setSize(800, 700);
setLocationRelativeTo(null); // Center the window
}
Server Configuration Panel
private JPanel createServerPanel() {
JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5));
panel.setBorder(BorderFactory.createTitledBorder("SMTP Server Configuration"));
// Initialize components
smtpHostField = new JTextField("smtp.gmail.com");
smtpPortField = new JTextField("587");
usernameField = new JTextField();
passwordField = new JPasswordField();
authCheckBox = new JCheckBox("Require Authentication", true);
tlsCheckBox = new JCheckBox("Use STARTTLS", true);
// Add components to panel
panel.add(new JLabel("SMTP Host:"));
panel.add(smtpHostField);
panel.add(new JLabel("SMTP Port:"));
panel.add(smtpPortField);
panel.add(new JLabel("Username:"));
panel.add(usernameField);
panel.add(new JLabel("Password:"));
panel.add(passwordField);
panel.add(authCheckBox);
panel.add(tlsCheckBox);
return panel;
}
Recipient Information Panel
private JPanel createRecipientPanel() {
JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Recipients"));
toField = new JTextField();
ccField = new JTextField();
bccField = new JTextField();
subjectField = new JTextField();
panel.add(new JLabel("To (comma-separated):"));
panel.add(toField);
panel.add(new JLabel("CC (comma-separated):"));
panel.add(ccField);
panel.add(new JLabel("BCC (comma-separated):"));
panel.add(bccField);
panel.add(new JLabel("Subject:"));
panel.add(subjectField);
return panel;
}
Message Composition Panel
private JPanel createMessagePanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createTitledBorder("Message"));
messageArea = new JTextArea(15, 50);
messageArea.setLineWrap(true);
messageArea.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(messageArea);
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
Attachment Management Panel
private JPanel createAttachmentPanel() {
JPanel panel = new JPanel(new BorderLayout(5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Attachments"));
attachmentListModel = new DefaultListModel<>();
attachmentList = new JList<>(attachmentListModel);
JScrollPane listScrollPane = new JScrollPane(attachmentList);
attachButton = new JButton("Add Attachment");
JButton removeButton = new JButton("Remove Selected");
attachButton.addActionListener(this::addAttachment);
removeButton.addActionListener(this::removeAttachment);
JPanel buttonPanel = new JPanel();
buttonPanel.add(attachButton);
buttonPanel.add(removeButton);
panel.add(listScrollPane, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
return panel;
}
private void addAttachment(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(true);
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
File[] files = fileChooser.getSelectedFiles();
for (File file : files) {
if (!attachmentListModel.contains(file)) {
attachmentListModel.addElement(file);
}
}
}
}
private void removeAttachment(ActionEvent e) {
int[] selectedIndices = attachmentList.getSelectedIndices();
for (int i = selectedIndices.length - 1; i >= 0; i--) {
attachmentListModel.remove(selectedIndices[i]);
}
}
Action Buttons and Console
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
sendButton = new JButton("Send Email");
clearButton = new JButton("Clear All");
sendButton.addActionListener(this::sendEmail);
clearButton.addActionListener(this::clearAll);
panel.add(sendButton);
panel.add(clearButton);
return panel;
}
private JPanel createConsolePanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createTitledBorder("Console Output"));
consoleArea = new JTextArea(8, 60);
consoleArea.setEditable(false);
consoleArea.setBackground(Color.BLACK);
consoleArea.setForeground(Color.GREEN);
consoleArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
JScrollPane scrollPane = new JScrollPane(consoleArea);
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
private void clearAll(ActionEvent e) {
toField.setText("");
ccField.setText("");
bccField.setText("");
subjectField.setText("");
messageArea.setText("");
attachmentListModel.clear();
consoleArea.setText("");
}
private void log(String message) {
consoleArea.append(message + "\n");
consoleArea.setCaretPosition(consoleArea.getDocument().getLength()); // Auto-scroll
}
The Email Sending Engine
This is the core functionality that uses Jakarta Mail to send emails.
private void sendEmail(ActionEvent e) {
// Run in a separate thread to keep GUI responsive
SwingWorker<Boolean, String> worker = new SwingWorker<>() {
@Override
protected Boolean doInBackground() throws Exception {
try {
publish("Starting email sending process...");
// Validate required fields
if (toField.getText().trim().isEmpty()) {
publish("ERROR: No recipients specified.");
return false;
}
// Get SMTP properties
java.util.Properties props = new java.util.Properties();
props.put("mail.smtp.auth", String.valueOf(authCheckBox.isSelected()));
props.put("mail.smtp.starttls.enable", String.valueOf(tlsCheckBox.isSelected()));
props.put("mail.smtp.host", smtpHostField.getText());
props.put("mail.smtp.port", smtpPortField.getText());
// For debugging
props.put("mail.debug", "true");
// Get session
jakarta.mail.Session session;
if (authCheckBox.isSelected()) {
session = jakarta.mail.Session.getInstance(props,
new jakarta.mail.Authenticator() {
@Override
protected jakarta.mail.PasswordAuthentication getPasswordAuthentication() {
return new jakarta.mail.PasswordAuthentication(
usernameField.getText(),
new String(passwordField.getPassword())
);
}
});
} else {
session = jakarta.mail.Session.getInstance(props);
}
// Create message
jakarta.mail.Message message = new jakarta.mail.internet.MimeMessage(session);
message.setFrom(new jakarta.mail.internet.InternetAddress(usernameField.getText()));
// Set recipients
message.setRecipients(jakarta.mail.Message.RecipientType.TO,
jakarta.mail.internet.InternetAddress.parse(toField.getText()));
if (!ccField.getText().trim().isEmpty()) {
message.setRecipients(jakarta.mail.Message.RecipientType.CC,
jakarta.mail.internet.InternetAddress.parse(ccField.getText()));
}
if (!bccField.getText().trim().isEmpty()) {
message.setRecipients(jakarta.mail.Message.RecipientType.BCC,
jakarta.mail.internet.InternetAddress.parse(bccField.getText()));
}
message.setSubject(subjectField.getText());
// Create multipart message for text and attachments
jakarta.mail.Multipart multipart = new jakarta.mail.internet.MimeMultipart();
// Create text part
jakarta.mail.BodyPart messageBodyPart = new jakarta.mail.internet.MimeBodyPart();
String messageText = messageArea.getText();
// Check if message contains HTML
if (messageText.trim().startsWith("<html>")) {
messageBodyPart.setContent(messageText, "text/html; charset=utf-8");
} else {
messageBodyPart.setText(messageText);
}
multipart.addBodyPart(messageBodyPart);
// Add attachments
for (int i = 0; i < attachmentListModel.size(); i++) {
File file = attachmentListModel.getElementAt(i);
publish("Attaching file: " + file.getName());
messageBodyPart = new jakarta.mail.internet.MimeBodyPart();
messageBodyPart.setDataHandler(
new jakarta.activation.DataHandler(
new jakarta.activation.FileDataSource(file)
)
);
messageBodyPart.setFileName(file.getName());
multipart.addBodyPart(messageBodyPart);
}
// Set the complete message parts
message.setContent(multipart);
// Send message
publish("Sending email...");
jakarta.mail.Transport.send(message);
publish("SUCCESS: Email sent successfully!");
return true;
} catch (Exception ex) {
publish("ERROR: " + ex.getMessage());
ex.printStackTrace();
return false;
}
}
@Override
protected void process(java.util.List<String> chunks) {
for (String message : chunks) {
log(message);
}
}
@Override
protected void done() {
sendButton.setEnabled(true);
try {
Boolean success = get();
if (success) {
JOptionPane.showMessageDialog(EmailSenderGUI.this,
"Email sent successfully!", "Success",
JOptionPane.INFORMATION_MESSAGE);
}
} catch (Exception ex) {
log("ERROR in worker thread: " + ex.getMessage());
}
}
};
sendButton.setEnabled(false);
worker.execute();
}
Main Method
public static void main(String[] args) {
// Set system look and feel
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeel());
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(() -> {
new EmailSenderGUI().setVisible(true);
});
}
}
Configuration for Popular Email Providers
Gmail:
- SMTP Host:
smtp.gmail.com - Port:
587(TLS) or465(SSL) - Requires "App Password" if 2-factor authentication is enabled
Outlook/Office 365:
- SMTP Host:
smtp.office365.com - Port:
587 - Requires authentication
Yahoo Mail:
- SMTP Host:
smtp.mail.yahoo.com - Port:
587 - Requires authentication
Security Considerations
- Password Storage: The current implementation stores passwords in memory only during the session. For production applications, consider using secure credential storage.
- SSL/TLS: Always use TLS (STARTTLS) for secure communication.
- Input Validation: Validate all user inputs to prevent injection attacks.
- File Security: Validate attachment types and sizes to prevent security risks.
Conclusion
This SMTP Email Sender GUI provides a solid foundation for a desktop email utility. You can extend it with features like:
- Email templates
- Contact list integration
- Rich text editing for HTML emails
- Batch sending capabilities
- SSL certificate handling
- Logging and sent items history
The combination of Swing for the interface and Jakarta Mail for the email functionality creates a powerful, self-contained application that demonstrates several important Java concepts including GUI development, network communication, and multi-threading with SwingWorker.