Robert Johns | 13 Mar, 2024
Fact checked by Jim Markus

How To Create A Java Email Client App for Beginners

Want to know how to build a Java email client? In this tutorial, I’ll walk you through this fun and practical Java project step-by-step. 

Whether you’re just starting your Java development journey or are keen to learn Java, a Java email client application is a fun project for beginners to learn real-world Java skills.

In this Java email client tutorial, you'll:

  • Construct a user-friendly Java email client, focusing on readability, ease of navigation, and an intuitive layout for various email operations.
  • Design a visually appealing and functional UI using Java Swing
  • Implement essential functionalities such as email sending and receiving, dynamic user authentication, attachment handling, and real-time display of inbox updates.
  • Enhance the email interface dynamically in response to user actions.
  • Incorporate advanced functionalities such as reply and forward options and session management to maintain connectivity with the email server.

Through this tutorial, you'll not only develop a fully functional Java email client but also gain valuable insights into Java Swing for UI design, email server communication, and the application of design principles to create engaging and practical software solutions.

To make the most of this tutorial, it helps to have basic Java skills, including familiarity with basic email concepts.

Some previous experience with Java, such as working with Swing components for building user interfaces and handling events, can be beneficial. However, you don't need to be a Java expert or have prior experience with Java email APIs or email clients.

I’ve also provided the full source code for this Java project so you can follow along, experiment, and even build upon it for your own projects. 

Let’s dive in and start building!

How To Create A Java Email Client Application

Are you ready to dive into the world of Java development with a hands-on Java project

If so, you've landed in the right spot! Today, we're going to create an email client using Java. 

This project is a great starting point if you're new to Java or application development, as it provides a clear and engaging example of how Java can be used to manage emails, including sending, receiving, and organizing messages.

At the heart of our project, we'll leverage Java's capabilities for handling internet protocols like SMTP (Simple Mail Transfer Protocol) and IMAP (Internet Message Access Protocol) to interact with email servers. 

Java, with its comprehensive API for networking and email handling, makes it an ideal choice for building email applications, combining the logic and functionality we need for email exchanges.

In our email client, Java will handle the backend logic, including connecting to email servers, fetching, sending, and deleting emails, and managing user sessions. 

We won't stop at functionality, though. We'll also dive into the basics of creating a user-friendly interface using Java Swing to make our email client both powerful and pleasant to use.

Take a look at the image below to get an idea of what you’re going to build!

Build your own Java email client application

Now, you might wonder, "Is this going to be difficult to build?" Not at all! 

I've designed this Java project to be beginner-friendly, breaking it down into manageable, easy-to-follow steps. 

So, whether you're just starting your journey in Java development or you have some experience but are new to application development, this project is a fantastic way to enhance your skills.

So, let's gear up, open our IDE, and get ready to create our very own email client application. 

By the end of this tutorial, you'll not only have a functional email application to add to your portfolio, but you'll also gain a deeper understanding of Java's capabilities in handling internet protocols and developing interactive applications

Let’s get started and build something awesome!

Project Prerequisites

Before we dive into coding our Java email client application, let's review the skills you'll need to follow along with me. 

And don't worry, you don't need to be a Java expert to get started, but having a few basics under your belt will make this journey smoother and more enjoyable. 

Plus, if you're rusty in any of these areas, you can always brush up with a Java course. Remember, we're also here to help, so don’t hesitate to search hackr.io for help as you go along.

Basic Java Knowledge

You should be comfortable with Java syntax and the fundamentals of object-oriented programming, including classes, objects, methods, and inheritance.

Understanding of Java Networking and Email Protocols 

A basic grasp of networking concepts and familiarity with email protocols like SMTP and IMAP is beneficial, though we'll cover the essentials as we build our application.

Familiarity with Java Swing 

For creating the client interface, some basic knowledge of Swing will be helpful. However, this tutorial will provide the necessary guidance for beginners.

A Curious and Experimental Mind 

This might be the most crucial prerequisite! 

I really believe that when it comes to coding in Java, the most effective way to learn is through hands-on experience, making errors, and trying again. 

Be prepared to experiment, modify the code, and perhaps even cause a few glitches (which you'll then resolve). 

That's the essence of learning and development!

You could also consider using an AI coding assistant like GitHub Copilot to help out, but I’d recommend waiting until you’re 100% stuck, as this is where you really learn.

Step 1: Setting Up The Project

Alright! Let's kick things off by setting up our Java email client project. 

This initial step is crucial as it lays the foundation for our entire application, ensuring we have a structured and organized workspace from the get-go.

i. Install Java Development Kit (JDK)

Before anything else, ensure that you have the Java Development Kit (JDK) installed on your computer. 

The JDK is essential for developing and running Java applications. If you haven't installed it yet, visit the Oracle website or search for a JDK version compatible with your system and follow the installation instructions.

ii. Choose and Set Up Your IDE

It’s time to choose an Integrated Development Environment for developing your Java chat application. 

If you’ve read my article on the best Java IDEs, you’ll see that I favor IntelliJ IDEA, Eclipse, and NetBeans.

But I’d also encourage you to check out VSCode if you’re already familiar with that coding environment and you’d like to carry on with what you know. 

Simply head to the VSCode extension marketplace and install the ‘Extension Pack for Java’ from Microsoft, and you’ll be good to go.

iii. Create a New Java Project

Once your IDE is ready, it's time to create a new Java project:

  1. Open your IDE and select the option to create a new project.
  2. Choose a Java project from the list of project types.
  3. Name your project something descriptive, like JavaEmailClientApp.
  4. If prompted, set the JDK version to use for this project.
  5. Finish the setup process, and your IDE will generate the project structure for you.

iv. Organize Your Project Structure

Organize your project structure for better management and scalability. Here's a simple way to structure your Java chat application:

  • src: This directory will contain all your source code files.
  • com.yourname.javachatapp: Replace yourname with your or your organization's name. This will be your base package where your Java files will reside.
  • server: A package for your server-side code.
  • client: A package for your client-side code.
  • lib: If your project requires external libraries, you can place them in this directory.

v. Set Up a Version Control System (Optional but Recommended)

Consider initializing a Git repository in your project folder to manage your source code versions. 

Use the command line or your IDE's built-in Git support to create the repository. This step is highly recommended as it helps in tracking changes and collaborating with others.

vi. Verify Project Setup

To ensure everything is set up correctly, try running a simple "Hello World" Java program in your project environment. 

This test will confirm that your JDK and IDE are correctly configured:

public class HelloWorld {
  public static void main(String[] args) {
      System.out.println("Hello, Java Email Client Application!");
  }
}

vii. Ready Your Development Environment

As we move forward with building the Java email client application, keep your IDE open and familiarize yourself with its layout and features. 

You'll be spending a lot of time here, writing code, debugging, and running your application.

And there you have it! You've successfully set up your Java email client project. 

With the foundation laid down, we're ready to dive into the exciting parts of building our email client application. 

Let's proceed to Step 2, where we'll explore the basics of the email protocols required for our email client application.

Step 2: Understanding Email Protocol Basics

Now, let's delve into the essentials of email protocols before building our simple Java mail application.

Grasping these protocols is essential for the development of our email client since it relies on them to send, receive, and handle emails. 

In Java-based email communication, it is common to utilize specific application protocols tailored for email services. 

This includes SMTP for dispatching emails, along with IMAP or POP3 for the retrieval and organization of incoming emails. 

By acquainting ourselves with these protocols, we can make sure that our Java application is able to seamlessly interact with email servers in a standard way.

i. Brief Overview of Email Protocols

  • SMTP (Simple Mail Transfer Protocol): This is the protocol for email sending and it uses port 25 (or port 465 for SSL/TLS). Java applications leverage SMTP to transmit messages from a client to an email server or among servers.
  • IMAP (Internet Message Access Protocol): This protocol facilitates the retrieval of emails, allowing clients to access and manipulate messages as though they were located on the server itself. IMAP uses port 143 (or port 993 for SSL/TLS) and suits clients requiring management of various folders and access from multiple devices.
  • POP3 (Post Office Protocol version 3): A simpler email retrieval protocol than IMAP, it operates on port 110 (or port 995 for SSL/TLS), and is designed for downloading messages from the server to the client before removing them from the server.

ii. Introduction to the Java Mail API

The Java Mail API offers a platform-independent and protocol-agnostic framework for the development of mail and messaging applications. 

And while it is a part of the Java Enterprise Edition, it can also be integrated into standard Java applications if we add the JavaMail library (javax.mail) to our project.

iii. Connecting to Email Servers

  • For sending emails (SMTP): You’ll need to create a Session object that stores configuration settings, such as the SMTP server's address and authentication details, and use this to establish a Transport object for sending Message objects.
  • For receiving emails (IMAP/POP3): In a similar manner to sending with SMTP, you need to configure a Session object with the necessary settings for IMAP or POP3. You’ll then use a Store object to link to the email server and retrieve messages.

Let’s take a look at some examples of Java code to see how this works when we want to send emails with SMTP.

Example Code for Sending Emails (SMTP):

import javax.mail.*;
import javax.mail.internet.*;

import java.util.Properties;

public class EmailSender {
  public static void main(String[] args) {
      // Setup mail server properties
      Properties properties = System.getProperties();
      properties.setProperty("mail.smtp.host", "smtp.example.com");
      properties.setProperty("mail.smtp.auth", "true");

      // Authenticate
      Session session = Session.getDefaultInstance(properties,
              new Authenticator() {
                  protected PasswordAuthentication getPasswordAuthentication() {
                      return new PasswordAuthentication("username", "password");
                  }
              });

      try {
          // Create a default MimeMessage object
          MimeMessage message = new MimeMessage(session);
          message.setFrom(new InternetAddress("from@example.com"));
          message.addRecipient(Message.RecipientType.TO, new InternetAddress("to@example.com"));
          message.setSubject("This is the Subject Line!");
          message.setText("This is the email body.");

          // Send message
          Transport.send(message);
          System.out.println("Sent message successfully....");
      } catch (MessagingException mex) {
          mex.printStackTrace();
      }
  }
}

Great, we now have a basic understanding of how we’re going to set about creating our Java email client.

In the next steps, we'll build on this foundational knowledge to implement the functionalities we need for an email client, including connecting to email servers, sending emails, fetching and managing received emails, and developing a user interface.

Let’s get coding!

Step 3: Implementing Email Sending

Let's roll up our sleeves and set up the email server connection for our email client. 

This step is essential as it lays the groundwork for sending and receiving emails, acting as the gateway for our email client to interact with email servers using SMTP for sending emails and IMAP for receiving and managing them.

And rather than focusing on theory, let’s add real settings to connect to Gmail servers. Yep, this means you can use this email client to manage your own Gmail account.

That quite cool, right?!

Important note: since May 2022, Google has tightened its restrictions on less secure apps accessing Gmail. 

This means you need to follow some extra steps to use ensure your email client can play nicely with your Gmail account. 

But don’t worry: they’re easy to do, and I’ve broken them down for you:

  • Go to the Manage Your Google Account page for your Google account
  • Click on Security
  • Enable 2FA (use whichever method you prefer)
  • Search for App Passwords with the search bar and select this
  • Enter your app name as Java Email Client and click Create
  • Copy the generated password somewhere safe
  • Click on Select App and select Mail
  • Click on Select Device & select Other (custom name’, enter ‘Java Email Client’
  • Click on Generate, then save this password

You can now use this app password to access your Gmail account with no trouble! But remember to use this and not to use your standard password, otherwise it won’t work.

i. Create New Java Files for Email Operations

In your project directory, create a file named EmailSender.java.

This should be located in the src folder if you're using an IDE like Eclipse or IntelliJ IDEA or directly in your project folder if you're using a simple text editor and compiling from the command line.

If you’re choosing to organize your code into packages, the first line in your file should declare the package.

If your package is named com.emailclient, then at the top of EmailSender.java, include the line package com.emailclient;.

If you're not using packages, you can skip this step!

ii. Setting up JavaMail API

Next, we need to make sure we have the JavaMail API is setup properly for use with our project.

It’s important to note that the JavaMail API is not part of the standard Java Development Kit (JDK), so we’ll need to manually add it to our project's build path. 

Here’s how you can do this if you’re using VSCode:

  • Download the JavaMail API: Head to the JavaMail GitHub release page or find a reputable source to download the JAR file.
  • Move the Jar File: Move the downloaded JavaMail API jar file (javax.mail.jar) into your project folder. It's a good practice to create a lib folder within your project to store all external libraries, but this is optional.
  • Add Classpath Entry: You'll need to add an entry for the JavaMail API jar file to this .classpath file. The easiest way to do this is to use ctrl+alt-p to open the command palette. Enter Java Configure Classpath, then add a reference to the JAR file, as shown in the image below. Simply click Add and select the JAR file in your lib directory.

ClassPath Editor Window in VSCode

If you follow these steps, you should be good to go!

iii. Setting Up Java Activation Framework (JAF)

First things first, JAF is now known as Jakarta Activation, so we’ll refer to it by that name from here on.

But aside from this change, this process will be very familiar to you as we’re replicating the same steps we followed with the JavaMail API.

This means we need to go to the Jakarta Activation GitHub page to download the jakarta.activation.jar file.

We can then place this in our lib directory and add this to our Classpath, just as we did with the JavaMail JAR file.

Once we’ve done this, we have everything we need to use the JavaMail API, so let’s dive in.

iv. Setting Up SMTP Connection for Sending Emails via Gmail

For this tutorial, we’ll be connecting to Gmail's SMTP server to allow you to send emails, and this will require us to complete these steps:

  • Configure SMTP Properties for Gmail: Specify the necessary properties for Gmail's SMTP service, including server address (smtp.gmail.com), port (587 for TLS), and the need for authentication.
  • Create a Session with Authentication: Use these properties to establish a Session object, incorporating user authentication with your Gmail account credentials.
  • Compose and Send Emails: With an authenticated session, you can now craft Message objects for your emails and send them through Gmail using the Transport class.
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;

public class EmailSender {
  public static void sendEmail(String to, String subject, String body) {
      final String username = "youremail@gmail.com"; // Replace with your Gmail username
      final String password = "yourapppassword"; // Replace with your Gmail password

      Properties prop = new Properties();
      prop.put("mail.smtp.host", "smtp.gmail.com");
      prop.put("mail.smtp.port", "587");
      prop.put("mail.smtp.auth", "true");
      prop.put("mail.smtp.starttls.enable", "true"); // Enable TLS

      Session session = Session.getInstance(prop,
              new javax.mail.Authenticator() {
                  protected PasswordAuthentication getPasswordAuthentication() {
                      return new PasswordAuthentication(username, password);
                  }
              });

      try {
          MimeMessage message = new MimeMessage(session);
          message.setFrom(new InternetAddress(username));
          message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
          message.setSubject(subject);
          message.setText(body);

          Transport.send(message);
          System.out.println("Email sent successfully via Gmail");
      } catch (MessagingException e) {
          e.printStackTrace();
      }
  }

  public static void main(String[] args) {
      // Example email details - replace with actual recipient details
      String to = "recipient@example.com"; // recipient's email (change to real recipient)
      String subject = "Test Email from Java App";
      String body = "Hello, this is a test email sent from the Java EmailSender class.";

      sendEmail(to, subject, body);
  }
}

In this code, we've:

  • Configured SMTP properties for Gmail by adding the settings to connect to Gmail's SMTP server, ensuring communication over a secure TLS connection.
  • Used Gmail account credentials for session authentication.
  • Created an email with specified sender, recipient, subject, and body and sent it using Gmail's SMTP server.
  • Added a temporary main method to test the EmailSender can connect to the Gmail server and send a test message.

Now we have our code, let’s run it. We’ll start by compiling the EmailSender.

If you’re using an IDE, your project should automatically compile when you build it. But make sure you have set up your project correctly to include the main class.

Alternatively, open a terminal or command prompt and navigate to the directory containing your EmailSender.java file.

Compile the Java file into bytecode by running the following command:

javac -cp ".;path/to/javax.mail.jar;path/to/jakarta.activation.jar" EmailSender.java

Replace the paths with the actual paths to our JavaMail and Jakarta Activation jar files in your project. 

Note, be sure to use ‘:’ instead of ‘;’ if you're on macOS or Linux. 

This command will compile EmailSender.java into bytecode, producing an EmailSender.class file in the same directory.

After compilation, you're ready to run your EmailSender class and send an email.

If you’re using an IDE, look for a run or play button in your IDE and click this to start your application.

Also, make sure EmailSender is selected as the main class to run.

To run your application from the command line, ensure you're in the project directory where your compiled .class files are located. You can then execute the EmailSender Class by running:

java -cp ".;path/to/javax.mail.jar;path/to/jakarta.activation.jar" EmailSender

Adjust the classpath (-cp) argument as needed for your environment, ensuring the JavaMail jar is included along with your compiled classes.

After running the EmailSender class, you should see a confirmation in your terminal or console output indicating that the email was sent successfully. 

This verifies that your setup is correct and your Java email client can send emails using Gmail's SMTP server.

Congratulations on setting up and running your EmailSender class! 

You've taken a big step towards building a functional Java-based email client. 

Next, we'll focus on receiving and managing emails to build out our email client functionalities.

Step 4: Developing Email Receiving Functionality

Great, now we’ve established our ability to send emails, the next step in our Java email client journey involves developing the functionality to receive and manage emails. 

This will allow our email client not only to send but also to retrieve and display emails from a Gmail account via IMAP.

i. Create New Java File for Receiving Emails

In your project directory, it's time to create a new file named EmailReceiver.java

This should be placed in the src folder if you're using an IDE like Eclipse, IntelliJ IDEA, or VSCode or directly in your project folder for those compiling from the command line. 

If you’re organizing your code into packages, the first line in your EmailReceiver.java file should declare the package. 

If you're not using packages, you can skip this and crack on with the coding!

ii. Setting Up IMAP Connection for Receiving Emails via Gmail

To receive emails from a Gmail account, you'll need to configure your application to use IMAP with Gmail's servers. 

This involves specifying the necessary properties and creating a session to connect and interact with Gmail.

Let’s outline the steps we’ll follow:

  • Configure IMAP Properties for Gmail: Define properties such as the IMAP server address (imap.gmail.com), port (993), and enabling SSL (true) to establish a secure connection.
  • Create a Session for IMAP Connection: Use the properties to create a Session object, which will be used to connect to the IMAP server with your Gmail account credentials.
  • Access and Process Emails: With a connection established, you can now access the inbox, read emails, and perform various email management tasks.
import javax.mail.*;
import java.util.Properties;

public class EmailReceiver {
  public static void receiveEmail(String username, String password) {
      Properties properties = new Properties();
      properties.put("mail.store.protocol", "imaps");
      properties.put("mail.imaps.host", "imap.gmail.com");
      properties.put("mail.imaps.port", "993");
      properties.put("mail.imaps.ssl.enable", "true");

      try {
          Session emailSession = Session.getDefaultInstance(properties);
          Store store = emailSession.getStore("imaps");
          store.connect("imap.gmail.com", username, password);

          Folder emailFolder = store.getFolder("INBOX");
          emailFolder.open(Folder.READ_ONLY);

          Message[] messages = emailFolder.getMessages();
          System.out.println("Number of emails: " + messages.length);

          // Example: Print out subject of each email
          for (Message message : messages) {
              System.out.println("Email Subject: " + message.getSubject());
          }

          emailFolder.close(false);
          store.close();
      } catch (NoSuchProviderException e) {
          e.printStackTrace();
      } catch (MessagingException e) {
          e.printStackTrace();
      }
  }

  public static void main(String[] args) {
      // Example Gmail credentials - replace with actual credentials
      String username = "yourgmail@gmail.com";
      String password = "yourapppassword"; // Use app password for Gmail

      receiveEmail(username, password);
  }
}

In this code, we’ve:

  • Set up properties to access Gmail's IMAP server, including the host, port, and SSL encryption (
  • Created a Session object using the defined properties to form the foundation for our IMAP server connection.
  • Utilized the Session object to obtain a Store object configured for "imaps" (IMAP over SSL), then connected to the store using the Gmail account's username and the app password. 
  • Opened the "INBOX" folder from the store in read-only mode, giving us access to the inbox's contents without the ability to modify them.
  • Fetched an array of Message objects from the inbox, representing each email, and iterated through these messages, printing out the subject of each to demonstrate how to access and display email information.
  • Closed the email folder and the store connection to release resources and maintain security. It's important to close these to avoid leaks and issues with connection limits.
  • Included catch blocks for NoSuchProviderException and MessagingException to handle possible errors during email retrieval. 
  • Provided a temporary main method that calls the receiveEmail function with a sample Gmail username and an app password. 

To compile and run your EmailReceiver class, follow the same process as with EmailSender:

Use your IDE or the command line to compile the EmailReceiver.java file. Remember to include the classpath to the JavaMail and Jakarta Activation JAR files.

javac -cp ".;path/to/javax.mail.jar;path/to/jakarta.activation.jar" EmailReceiver.java

Execute the EmailReceiver class to begin fetching emails from your Gmail inbox.

java -cp ".;path/to/javax.mail.jar;path/to/jakarta.activation.jar" EmailReceiver

After running the EmailReceiver class, you should see the number of emails in your inbox and the subject of each email printed to the console.

Congratulations on successfully adding receiving and managing email functionalities to your Java email client! 

You've now covered both sending and receiving emails, making significant progress in building a comprehensive email client application.

In the next steps, we’ll build out our user interface to bring this email client to life! 

Step 5: Designing the User Interface with Swing

Our email client works great, but how about we take things a little further?

Now it’s time for our Java email client to include a graphical user interface, so how about we get to work with using Java Swing? 

The best part about this step is that it will make your application more interactive and user-friendly compared to the command-line interface. 

To begin, let’s design a simple yet functional UI for sending and receiving emails.

i. Create New Java File for the GUI

This should all be feeling quite familiar now, but here’s the process again.

Create a new file named EmailClientGUI.java in your project’s src directory or directly in your project folder for those compiling from the command line.

If you’re organizing your code into packages, the first line in your file should declare the package. 

As always, if you're not using packages, skip this and crack on with the coding!

ii. Creating the Main JFrame

Diving right in, we’ll use the JFrame as the container for our application's GUI elements. Here's a step-by-step guide to setting it up:

import javax.swing.*;
import javax.awt.BorderLayout.*;

public class EmailClientGUI extends JFrame {
  public EmailClientGUI() {
      setTitle("Java Email Client");
      setSize(600, 400);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      initUI();
      setVisible(true);
  }

  private void initUI() {
      // Initialization of UI components will be done here.
  }

  public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new EmailClientGUI());
  }
}

In this code, we've:

  • Initialized the Main JFrame to create the primary window for our application with a title, size, and default close operation.
  • Set Up a UI initialization method where all UI components will be initialized and added to the JFrame.
  • Ensured that our window will be visible to the user upon launching the application.

iii. Adding UI Components

For a functional email client, we'll need components for listing emails, reading selected emails, and composing new ones.

Let’s start with the inbox panel by adding this to the initUI method:

DefaultListModel<String> emailListModel = new DefaultListModel<>();
JList<String> emailList = new JList<>(emailListModel);
add(new JScrollPane(emailList), BorderLayout.WEST);

We’ll then add the reading panel to the initUI method:

JTextArea emailContent = new JTextArea();
emailContent.setEditable(false);
add(new JScrollPane(emailContent), BorderLayout.CENTER);

And lastly, we’ll add a compose button to the initUI method:

JButton composeButton = new JButton("Compose");
add(composeButton, BorderLayout.SOUTH);

In this initial setup, we've:

  • Utilized JList with a DefaultListModel to list email subjects, making it scrollable by adding it to a JScrollPane.
  • Added a JTextArea for displaying the content of the selected email, also within a JScrollPane to enable scrolling.
  • Added a simple button for initiating the email composition process.

Now that we have our GUI, it’s time to compile and run it!

This follows the standard Java compilation process we’ve used so far, which simply means we need to run the following commands at the terminal or run the project inside of our IDE

javac EmailClientUI.java
java EmailClientUI

Congratulations! You've now laid the groundwork for a Swing-based UI for your Java email client. 

This GUI will serve as a skeleton that you can use to build more complex functionalities, like email composition, sending, and inbox management, transforming it into a fully functional email client application.

Let’s keep up the momentum!

Step 6: Enhancing The Email Client Functionality

Now it’s time to enhance our Java email client to include the capability to send attachments with emails. 

This is a crucial feature for any comprehensive email application, allowing users to share documents, images, and other files. 

We'll also introduce a new class, AttachmentChooser, to enable file selection from the GUI and modify our EmailSender class to handle sending emails with attachments.

Let’s dive in!

i. Modifying EmailSender for Attachment Support

Firstly, we’ll need to update the EmailSender class to include a method that allows sending an email with one or more attachments. 

The method will use MimeBodyPart for the email content and attachments and Multipart to combine them.

import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.util.Properties;
import java.io.File;

public class EmailSender {
  public static void sendEmailWithAttachment(String to, String subject, String body, File[] attachments) {
      final String username = "yourgmail@gmail.com"; // Replace with your Gmail username
      final String password = "yourapppassword"; // Use your Gmail app password

      Properties prop = new Properties();
      prop.put("mail.smtp.host", "smtp.gmail.com");
      prop.put("mail.smtp.port", "587");
      prop.put("mail.smtp.auth", "true");
      prop.put("mail.smtp.starttls.enable", "true"); // Enable TLS

      Session session = Session.getInstance(prop, new javax.mail.Authenticator() {
          protected PasswordAuthentication getPasswordAuthentication() {
              return new PasswordAuthentication(username, password);
          }
      });

      try {
          Message message = new MimeMessage(session);
          message.setFrom(new InternetAddress(username));
          message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
          message.setSubject(subject);

          Multipart multipart = new MimeMultipart();

          MimeBodyPart textPart = new MimeBodyPart();
          textPart.setText(body);
          multipart.addBodyPart(textPart);

          // Add each attachment
          for (File file : attachments) {
              MimeBodyPart attachmentPart = new MimeBodyPart();
              attachmentPart.attachFile(file);
              multipart.addBodyPart(attachmentPart);
          }

          message.setContent(multipart);
          Transport.send(message);
          System.out.println("Email sent successfully with attachments.");
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}

In this code, we've made the following changes:

  • Added functionality to attach one or more files to the email. Each file selected for attachment is added as a separate MimeBodyPart, and all parts (text and attachments) are combined into a Multipart object.

ii. Creating the AttachmentChooser Class

Now we can implement the AttachmentChooser class to open a file chooser dialog where users can select files to attach to their email.

Of course, as always, we need to create a new Java file named AttachmentChooser.java inside our src directory, and do the necessary if we’re using packages.

import javax.swing.*;
import java.io.File;

public class AttachmentChooser {
  public static File[] chooseAttachments() {
      JFileChooser fileChooser = new JFileChooser();
      fileChooser.setMultiSelectionEnabled(true);
      int option = fileChooser.showOpenDialog(null);
      if (option == JFileChooser.APPROVE_OPTION) {
          return fileChooser.getSelectedFiles();
      }
      return new File[] {}; // Return an empty array if no selection
  }
}

In this code, we've:

  • Leveraged JFileChooser to create a dialog that allows users to navigate their filesystem and select one or more files to attach to an email.
  • Configured the file chooser to allow multi-selection, enhancing user experience by enabling the attachment of several files in one go.
  • The method chooseAttachments returns an array of File objects representing the user's selected files, ready to be attached to the email. If no files are selected, an empty array is returned, ensuring the method's return type remains consistent.

With EmailSender and AttachmentChooser ready, the next stage is to integrate the attachment functionality into your email client's GUI. 

This will involve adding a button or other control to trigger the attachment selection process and storing the selected files to be included when the email is sent.

Let’s get to work on this right now!

Step 7: Linking the GUI with Email Functionalities

In this step, we're going to weave together all the pieces—sending emails, handling attachments, and receiving emails—directly within our Java email client's GUI. 

We’ll also add the ability to login to your email account from the GUI, rather than hardcoding the credentials into our EmailSender and EmailReceiver classes.

This idea now is to ensure our email client is not just functional but also intuitive and user-friendly.

Let’s summarize the changes and additions we’ll need to make:

  • EmailClientGUI.java: This will now include functionality for user login, composing emails with attachments, and displaying received emails.
  • EmailSender.java: This will supports dynamic user authentication for sending emails.
  • EmailReceiver.java: Similar to the sender, this will be adjusted to use login credentials.
  • AttachmentChooser.java: This will be invoked from the GUI to select files to be attached.

i. Enhancing EmailReceiver for Dynamic Authentication

Firstly, you are going to create a new EmailSessionManager.java file for a class of the same name.

Why? Well, when it comes to managing an IMAP connection with the Gmail server, we need to think about how long we want to maintain an open connection so that we can access our email content and its various properties like subject, sender, etc.

When we tested this out with our first version of the EmailReceiver, it worked fine because we accessed these properties before closing the connection.

However, now we have a GUI, we may want to maintain this connection with Gmail until we choose to close the email client application.

Otherwise, we won’t be able to interact with our emails, which is no fun!

So, let’s kick things off by creating our EmailSessionManager:

import javax.mail.Message;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import java.util.Properties;

public class EmailSessionManager {
  private Session emailSession;
  private Store store;
  private Folder emailFolder;
  private static EmailSessionManager instance;
 
  // Add static fields to store username and password
  private static String currentUsername = "";
  private static String currentPassword = "";

  private EmailSessionManager(String username, String password) throws MessagingException {
      Properties properties = new Properties();
      properties.put("mail.store.protocol", "imaps");
      properties.put("mail.imaps.host", "imap.gmail.com");
      properties.put("mail.imaps.port", "993");
      properties.put("mail.imaps.ssl.enable", "true");
      this.emailSession = Session.getInstance(properties, null);
      this.store = emailSession.getStore("imaps");
      this.store.connect(username, password);
     
      // Store the credentials upon successful connection
      currentUsername = username;
      currentPassword = password;
  }

  public static EmailSessionManager getInstance(String username, String password) throws MessagingException {
      if (instance == null) {
          instance = new EmailSessionManager(username, password);
      }
      return instance;
  }
 
  public static EmailSessionManager getInstance() throws IllegalStateException {
      if (instance == null) {
          throw new IllegalStateException("EmailSessionManager is not initialized. Please login first.");
      }
      return instance;
  }
 
  // Method to retrieve the current username
  public static String getUsername() {
      return currentUsername;
  }
 
  // Method to retrieve the current password
  public static String getPassword() {
      return currentPassword;
  }

  public Message[] receiveEmail() throws MessagingException {
      if (emailFolder == null || !emailFolder.isOpen()) {
          emailFolder = store.getFolder("INBOX");
          emailFolder.open(Folder.READ_ONLY);
      }
      return emailFolder.getMessages();
  }

  public void close() throws MessagingException {
      if (emailFolder != null) {
          emailFolder.close(false);
          emailFolder = null;
      }
      if (store != null) {
          store.close();
          store = null;
      }
      instance = null;
      // Clear the credentials upon closing the session
      currentUsername = "";
      currentPassword = "";
  }
}

In this code, we've created a singleton EmailSessionManager class to manage email sessions for connecting to, fetching emails from, and disconnecting from an IMAP email server. 

But let’s dive a little deeper, as we’ve also:

  • Implemented a singleton pattern to ensure that only one instance of this class is created and used throughout the application. 
  • Made the constructor private to enforce a singleton pattern. This initializes the email session by setting up connection properties (such as protocol, host, port, and SSL enablement) and connects to the email server using the provided username and password.
  • Created a static getInstance method to provide a global access point to the singleton instance of EmailSessionManager 
  • Overloaded the getInstance method with a parameterless version for accessing the instance after a login has occurred
  • Added getter methods to return the username and password used to log the user in. I’d also encourage you to explore ways to secure this information wit encryption.
  • Created a receiveEmail method for fetching emails from the inbox.
  • Created a close method to properly close the emailFolder and store.

By encapsulating email session management in this singleton class, your app can maintain a clean and efficient connection lifecycle, improve resource management, and simplifying the process of accessing and displaying emails. 

Plus, this aligns with best practices for working with external resources, like email servers, by centralizing connection management and reducing redundancy in the codebase.

Now, let’s make our adjustments to the EmailReceiver:

import javax.mail.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class EmailReceiver {
  // Static fields for storing email credentials
  private static String username = "";
  private static String password = "";

  // Method to set credentials
  public static void setCredentials(String user, String pass) {
      username = user;
      password = pass;
  }

  public static Message[] receiveEmail() throws MessagingException {
      Properties properties = new Properties();
      properties.put("mail.store.protocol", "imaps");
      properties.put("mail.imaps.host", "imap.gmail.com");
      properties.put("mail.imaps.port", "993");
      properties.put("mail.imaps.ssl.enable", "true");
     
      List<Message> messagesList = new ArrayList<>();
 
      Session emailSession = Session.getInstance(properties);
      Store store = emailSession.getStore("imaps");
      store.connect("imap.gmail.com", username, password);
 
      Folder emailFolder = store.getFolder("INBOX");
      emailFolder.open(Folder.READ_ONLY);
 
      Message[] messages = emailFolder.getMessages();
      for (Message message : messages) {
          messagesList.add(message);
      }
 
      emailFolder.close(false);
      store.close();
     
      return messagesList.toArray(new Message[0]);
}

In this modification, we've:

  • Implemented static fields for storing the user's email credentials.
  • Added a setCredentials method to dynamically set these credentials so that they can be called with the user's input from the GUI after login.
  • Adjusted the receiveEmail method to use the stored credentials instead of passing them as parameters.
  • We’ve also removed our console printing mechanism in favor of returning an array of messages that can be displayed in the GUI.

Great! We’ll need to circle back to this later as we’ll need to initialize our EmailSessionManager instance after the user logs in.

ii. Enhancing EmailSender for Dynamic Authentication

Now, let’s update the EmailSender to accept usernames and passwords dynamically, and this will take advantage of the getter methods we defined for EmailSessionManger singleton.

public class EmailSender {
  public static void sendEmailWithAttachment(String to, String subject, String body, File[] attachments) {
      try {
          String username = EmailSessionManager.getUsername();
          String password = EmailSessionManager.getPassword();
          Properties prop = new Properties();
          prop.put("mail.smtp.host", "smtp.gmail.com");
          prop.put("mail.smtp.port", "587");
          prop.put("mail.smtp.auth", "true");
          prop.put("mail.smtp.starttls.enable", "true"); // Enable TLS

          Session session = Session.getInstance(prop, new javax.mail.Authenticator() {
              protected PasswordAuthentication getPasswordAuthentication() {
                  return new PasswordAuthentication(username, password);
              }
          });

          try {
              Message message = new MimeMessage(session);
              message.setFrom(new InternetAddress(username));
              message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
              message.setSubject(subject);
              Multipart multipart = new MimeMultipart();
              MimeBodyPart textPart = new MimeBodyPart();
              textPart.setText(body);
              multipart.addBodyPart(textPart);

              for (File file : attachments) {
                  MimeBodyPart attachmentPart = new MimeBodyPart();
                  attachmentPart.attachFile(file);
                  multipart.addBodyPart(attachmentPart);
              }

              message.setContent(multipart);
              Transport.send(message);
              System.out.println("Email sent successfully with attachments.");
          } catch (Exception e) {
              e.printStackTrace();
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}

In this modification, we've:

  • Retrieved credentials from EmailSessionManager rather than passing them directly to the sendEmailWithAttachment method or managing them within EmailSender
  • Used these credentials in the sendEmailWithAttachment method for SMTP authentication, removing the need to pass credentials as parameters for each send.

iii. Developing the Login Interface in EmailClientUI

Now, let’s implement a simple login interface that collects your Gmail username and app password, storing them for use in email operations.

We’ll add this code to our EmailClientGUI class:

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.GridLayout;

public class EmailClientGUI extends JFrame {
  private JTextField usernameField = new JTextField(20);
  private JPasswordField passwordField = new JPasswordField(20);

  public EmailClientGUI() {
      setTitle("Java Email Client");
      setSize(800, 600);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      initUI();
      setVisible(true);
  }

  private void initUI() {
      // Existing code...   
      SwingUtilities.invokeLater(this::showLoginDialog);
  }

  private void showLoginDialog() {
  JPanel panel = new JPanel(new GridLayout(0, 1));
  panel.add(new JLabel("Email:"));
  panel.add(usernameField);
  panel.add(new JLabel("App Password:"));
  panel.add(passwordField);

  int result = JOptionPane.showConfirmDialog(null, panel, "Login",
          JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
  if (result == JOptionPane.OK_OPTION) {
      String username = usernameField.getText();
      String password = new String(passwordField.getPassword());
      try {
          // Initialize EmailSessionManager here
          EmailSessionManager.getInstance(username, password);
          refreshInbox(); // Refresh inbox to load emails
      } catch (MessagingException e) {
          JOptionPane.showMessageDialog(this, "Failed to initialize email session: " + e.getMessage(), "Login Error", JOptionPane.ERROR_MESSAGE);
      }
  } else {
      System.out.println("Login cancelled.");
  }
  }

  public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new EmailClientGUI());
  }
}

In this code, we've:

  • Implemented a login dialog that prompts the user to provide their email credentials.
  • Handled credentials with EmailSessionManager to ensure all subsequent email operations use the authenticated session, enhancing security and simplifying credential management.
  • Ensured the login dialog is invoked immediately after the UI initializes, making the login process the first interaction the user has with the application.

iv. Developing the Logout Functionality

Now, as I mentioned earlier, we’ve defined our EmailSessionManager to keep our connection open with the Gmail server, but we do need to ensure this is closed down when we’ve finished.

So, let’s add a window listener to monitor when we’re closing the application:

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
// Other imports as needed

public class EmailClientGUI extends JFrame {
  // Class members and constructor as before

  public EmailClientGUI() {
      setTitle("Java Email Client");
      setSize(800, 600);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      initUI();
      setVisible(true);

      // Add window listener to handle application close
      addWindowListener(new WindowAdapter() {
          @Override
          public void windowClosing(WindowEvent e) {
              try {
                  if (EmailSessionManager.getInstance() != null) {
                      EmailSessionManager.getInstance().close(); // Close the email session
                  }
              } catch (MessagingException ex) {
                  ex.printStackTrace();
              }
          }
      });
  }

  // The rest of your EmailClientGUI implementation
}

We haven’t written a lot of code here, but here’s what’s happening:

  • We’ve added a window listener to the JFrame to monitor for the window closing event, which signals the user's intent to exit the application. 
  • We’ve used the window listener's windowClosing method to call the EmailSessionManager class’ getInstance().close(). This ensures when we exit the application, the email session is properly closed. 

Awesome! Now we have the login and logout functionality taken care of, let’s dive in to the rest!

v. Fetching & Displaying Emails in the GUI

Now, let’s bring our client to life by fetching and displaying our emails in the client GUI. 

To do this, we’ll add a RefreshInbox method that ties in with the EmailReceiver class and it’s receiveEmails method.

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.mail.*;
import javax.mail.internet.InternetAddress;

public class EmailClientGUI extends JFrame {
  private JTextField usernameField = new JTextField();
  private JPasswordField passwordField = new JPasswordField();
  private DefaultListModel<String> emailListModel; // Model for the email list
  private JList<String> emailList; // Component to display emails

  public EmailClientGUI() {
      setTitle("Java Email Client");
      setSize(800, 600);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      initUI();
      setVisible(true);
  }

  private void initUI() {
      JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
      splitPane.setResizeWeight(0.5);
      splitPane.setOneTouchExpandable(true);

      emailList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      emailList.addListSelectionListener(this::emailListSelectionChanged);
      JScrollPane listScrollPane = new JScrollPane(emailList);

      emailContent.setEditable(false);
      JScrollPane contentScrollPane = new JScrollPane(emailContent);

      splitPane.setLeftComponent(listScrollPane);
      splitPane.setRightComponent(contentScrollPane);

      getContentPane().add(splitPane, BorderLayout.CENTER);

      bottomPanel.add(composeButton);
      bottomPanel.add(refreshInboxButton);
      add(bottomPanel, BorderLayout.SOUTH);
      // Prompt the user to log in when the application starts
      SwingUtilities.invokeLater(this::showLoginDialog);
  }

  private void refreshInbox() {
      try {
          messages = EmailSessionManager.getInstance().receiveEmail();
          emailListModel.clear();
          for (Message message : messages) {
              emailListModel.addElement(message.getSubject() + " - From: " + InternetAddress.toString(message.getFrom()));
          }
      } catch (MessagingException e) {
          JOptionPane.showMessageDialog(this, "Failed to fetch emails: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
      }
  }

  private void showLoginDialog() {
      // Existing code...
  }

  public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new EmailClientGUI());
  }
}

In this code, we've:

  • Switched over to a JSplitPane so that users have the option to resize the email list and email reader panes within our client.
  • Enabled the setOneTouchExpandable feature of JSplitPane, allowing users to easily collapse or expand either component with a single click.
  • Added a "Refresh Inbox" button alongside the "Compose" button, placing both in a new bottomPanel to organize the UI. 
  • Defined a refreshInbox method to use the EmailSessionManager and its getInstance and receiveEmail methods to fetch emails based on the credentials previously provided. 
  • The subjects of fetched emails, along with the sender's address, are listed in the JList component.

vi. Reading Email Content with GUI

So, we’ve fetched our emails and are showing them in our inbox panel, but naturally, we’d also like to read these emails!

Let’s tackle that now by adding a ListSelectionListener to the JList that displays the email subjects. 

When a user selects an email, your application will fetch the complete email content and display it in a text area:

// Imports

public class EmailClientGUI extends JFrame {
  // Existing code...
  private JTextArea emailContent = new JTextArea();
  private Message[] messages; // Array to hold fetched messages

  public EmailClientGUI() {
      // Existing code...
      }

  private void initUI() {
      // Existing code...
  }

  private void refreshInbox() {
      // Existing code...
  }

  private void emailListSelectionChanged(ListSelectionEvent e) {
  if (!e.getValueIsAdjusting() && emailList.getSelectedIndex() != -1) {
      try {
          Message selectedMessage = messages[emailList.getSelectedIndex()];
          emailContent.setText(""); // Clear previous content
          emailContent.append("Subject: " + selectedMessage.getSubject() + "\n\n");
          emailContent.append("From: " + InternetAddress.toString(selectedMessage.getFrom()) + "\n\n");
          // Use the new method to get and display the email body
          emailContent.append(getTextFromMessage(selectedMessage));
      } catch (MessagingException | IOException ex) {
          emailContent.setText("Error reading email content: " + ex.getMessage());
      }
  } 
  }

  private String getTextFromMessage(Message message) throws MessagingException, IOException {
  if (message.isMimeType("text/plain")) {
      return (String) message.getContent();
  } else if (message.isMimeType("multipart/*")) {
      MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
      for (int i = 0; i < mimeMultipart.getCount(); i++) {
          BodyPart bodyPart = mimeMultipart.getBodyPart(i);
          if (bodyPart.isMimeType("text/plain")) {
              return (String) bodyPart.getContent();
          }
      }
  }
  return "No readable content found."; // Fallback text
  }
 
  public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new EmailClientGUI());
  }
}

In this code, we've:

  • Added an event listener for the email list selection changes. When a user selects an email from the list, the application fetches and displays the email's subject, sender information, and body content in a dedicated reading area.
  • Developed getTextFromMessage to extract and present the email body as plain text. This handles both simple text emails and more complex multipart messages.
  • Parsed MimeMultipart objects to find and display only the "text/plain" parts to prevent the display of non-readable content or object references.
  • Added a fallback for emails that don't contain a straightforward "text/plain" part or are otherwise unreadable.

vii. Composing & Sending Emails with the GUI

We’re making seriously great progress, so let’s now allow users to compose and send emails with optional attachments within the EmailClientGUI.

We’ll create a new UI component to allow users to input the recipient's email address, subject, and body of the email and also allow them to select files to attach by doing the following:

  • Enhance the existing "Compose" button to open a dialog or another form where users can write their email.
  • Create a dialog with text fields for the recipient, subject, and body, plus a button to add attachments.
  • Use JFileChooser to let users pick files to attach.
  • Gather the information from the dialog, including any attachments, and use EmailSender to dispatch the email.

Let’s take a look at the code we need to make this all happen, starting with our new imports.

import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

Be sure to add these to the top of your EmailClientGUI file. We can then add an action listener to our initUI method.

private void initUI() {
  // Existing setup...

  JButton composeButton = new JButton("Compose");
  composeButton.addActionListener(e -> showComposeDialog());
  // Add to bottomPanel or wherever it's placed in your layout
}

By adding an action listener to our “Compose” button, we can launch showComposeDialog once it’s clicked.

Let’s now design our composition window and define the showComposeDialog method:

private void showComposeDialog() {
  JDialog composeDialog = new JDialog(this, "Compose Email", true);
  composeDialog.setLayout(new BorderLayout(5, 5));

  Box fieldsPanel = Box.createVerticalBox();
  JTextField toField = new JTextField();
  JTextField subjectField = new JTextField();
  JTextArea bodyArea = new JTextArea(10, 20);
  bodyArea.setLineWrap(true);
  bodyArea.setWrapStyleWord(true);

  fieldsPanel.add(new JLabel("To:"));
  fieldsPanel.add(toField);
  fieldsPanel.add(new JLabel("Subject:"));
  fieldsPanel.add(subjectField);

  JPanel bottomPanel = new JPanel();
  JButton attachButton = new JButton("Attach Files");
  JButton sendButton = new JButton("Send");
  JLabel attachedFilesLabel = new JLabel("No files attached");

  List<File> attachedFiles = new ArrayList<>();
  attachButton.addActionListener(e -> {
      File[] files = AttachmentChooser.chooseAttachments();
      attachedFiles.addAll(Arrays.asList(files));
      attachedFilesLabel.setText(attachedFiles.size() + " files attached");
  });

  sendButton.addActionListener(e -> {
      String to = toField.getText();
      String subject = subjectField.getText();
      String body = bodyArea.getText();
      File[] attachments = attachedFiles.toArray(new File[0]);
      EmailSender.sendEmailWithAttachment(to, subject, body, attachments);
      composeDialog.dispose();
  });

  bottomPanel.add(attachButton);
  bottomPanel.add(sendButton);

  composeDialog.add(fieldsPanel, BorderLayout.NORTH);
  composeDialog.add(new JScrollPane(bodyArea), BorderLayout.CENTER); // Ensure body area is scrollable
  composeDialog.add(bottomPanel, BorderLayout.SOUTH);

  composeDialog.pack(); // Adjust dialog size to fit content
  composeDialog.setLocationRelativeTo(this); // Center dialog relative to the main window
  composeDialog.setVisible(true);
}

In this code, we've:

  • Introduced UI components for entering the recipient, subject, and body of the email. A simple label indicates the number of attached files.
  • Provided a button that uses our AttachmentChooser class to select files for attachment.
  • Added a send button that collects the input data, including attachments, and calls EmailSender.sendEmailWithAttachment to send the email. After sending, the dialog is closed.

This step finalizes the functional development of our Java email client, making it a powerful tool for email communication directly from a user-friendly interface.

To compile and run your EmailClientGUI, follow the same process we’ve used in each of our previous steps:

Use your IDE or the command line to compile the EmailClientGUI.java file, then execute the EmailClientGUI class to begin fetching emails from your Gmail inbox.

After running the EmailClientGUI class, you should see the login window, followed by the main GUI with a list of your emails and the reader window.

Nice work on getting this far! Before we wrap things up and move on to testing, how would you feel about exploring some more advanced styling and functionality?

Let’s take a look at the next step!

Step 8: Advanced Functionality & Styling [Optional]

In this section,  let’s add some common features in all email clients, namely the option to reply to an email or forward an email to someone else.

We’ll also add some styling flourishes to make our finished email client look really professional.

i. Adding Reply and Forward Buttons

First, let's add "Reply" and "Forward" buttons to the email reading area. These will trigger the compose dialog with pre-filled fields depending on the action.

// Inside EmailClientGUI's initUI method or wherever you're setting up the UI
JButton replyButton = new JButton("Reply");
JButton forwardButton = new JButton("Forward");

replyButton.addActionListener(e -> prepareEmailAction("Reply"));
forwardButton.addActionListener(e -> prepareEmailAction("Forward"));

JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
actionPanel.add(replyButton);
actionPanel.add(forwardButton);

add(actionPanel, BorderLayout.NORTH); // Add the panel to the top or as appropriate

In this step, we introduced the "Reply" and "Forward" buttons to the email client, and we attached action listeners to trigger a prepareEmailAction method.

This will handle the logic for preparing the email composition dialog with appropriate information depending on whether the user is replying to or forwarding an email.

We’ll also need to ensure we add this to our imports:

import java.awt.FlowLayout;

Next, we need to create the prepareEmailAction method to prepare emails based on the type of action the user has opted for:

private void prepareEmailAction(String actionType) {
  if (emailList.getSelectedIndex() == -1) {
      JOptionPane.showMessageDialog(this, "No email selected.", "Error", JOptionPane.ERROR_MESSAGE);
      return;
  }
  try {
      Message selectedMessage = messages[emailList.getSelectedIndex()];
      String to = actionType.equals("Reply") ? InternetAddress.toString(selectedMessage.getFrom()) : "";
      String subjectPrefix = actionType.equals("Reply") ? "Re: " : "Fwd: ";
      String subject = subjectPrefix + selectedMessage.getSubject();
      String body = getTextFromMessage(selectedMessage);

      showComposeDialog(to, subject, body);
  } catch (MessagingException | IOException ex) {
      JOptionPane.showMessageDialog(this, "Error preparing email action.", "Error", JOptionPane.ERROR_MESSAGE);
  }
}

In this method, we've streamlined the process of replying to or forwarding emails directly from the user interface. 

By determining the type of email action, we dynamically set the recipient's address, subject, and body text in preparation for composing an email.

But this also means we need to make some adjustments to the showComposeDialog method to accept these new parameters:

private void showComposeDialog(String to, String subject, String body) {
  JDialog composeDialog = new JDialog(this, "Compose Email", true);
  composeDialog.setLayout(new BorderLayout(5, 5));

  Box fieldsPanel = Box.createVerticalBox();
  JTextField toField = new JTextField(to);
  JTextField subjectField = new JTextField(subject);
  JTextArea bodyArea = new JTextArea(10, 20);
  bodyArea.setText(body);
  bodyArea.setLineWrap(true);
  bodyArea.setWrapStyleWord(true);

  fieldsPanel.add(new JLabel("To:"));
  fieldsPanel.add(toField);
  fieldsPanel.add(new JLabel("Subject:"));
  fieldsPanel.add(subjectField);

  JPanel bottomPanel = new JPanel();
  JButton attachButton = new JButton("Attach Files");
  JButton sendButton = new JButton("Send");
  JLabel attachedFilesLabel = new JLabel("No files attached");

  List<File> attachedFiles = new ArrayList<>();
  attachButton.addActionListener(e -> {
      File[] files = AttachmentChooser.chooseAttachments();
      attachedFiles.addAll(Arrays.asList(files));
      attachedFilesLabel.setText(attachedFiles.size() + " files attached");
  });

  sendButton.addActionListener(e -> {
      EmailSender.sendEmailWithAttachment(toField.getText(), subjectField.getText(), bodyArea.getText(), attachedFiles.toArray(new File[0]));
      composeDialog.dispose();
  });

  bottomPanel.add(attachButton);
  bottomPanel.add(sendButton);

  composeDialog.add(fieldsPanel, BorderLayout.NORTH);
  composeDialog.add(new JScrollPane(bodyArea), BorderLayout.CENTER); // Ensure body area is scrollable
  composeDialog.add(bottomPanel, BorderLayout.SOUTH);

  composeDialog.pack(); // Adjust dialog size to fit content
  composeDialog.setLocationRelativeTo(this); // Center dialog relative to the main window
  composeDialog.setVisible(true);
}

In this modification, we've adapted the showComposeDialog method to accept parameters for "to", "subject", and "body" to pre-fill the compose email dialog. 

This enables seamless integration of reply and forward functionalities by automatically populating the relevant fields based on the user's action and the actual message they received. 

Now, you should have noticed that we also need to make a small adjustment to our action listener for the “Compose” button, as this does not currently pass any arguments.

To keep things simple, we can simply pass three blank strings.

private void initUI() {
  // existing code...
  composeButton.addActionListener(e -> showComposeDialog("", "", ""));
  // rest of code...
}

ii. Styling Enhancements

Great, now, let's add some final styling touches to make your email client visually appealing.

We’ll start with the color scheme and the fonts for our various UI components:

private static final Color BACKGROUND_COLOR = new Color(230, 240, 250);
private static final Color ACTION_PANEL_COLOR = new Color(200, 220, 240);
private static final Color BUTTON_COLOR = new Color(180, 220, 240);
private static final Font BUTTON_FONT = new Font("SansSerif", Font.BOLD, 12);
private static final Font EMAIL_LIST_FONT = new Font("SansSerif", Font.PLAIN, 14);
private static final Font EMAIL_CONTENT_FONT = new Font("SansSerif", Font.PLAIN, 14);

private void initUI() {
  JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  splitPane.setResizeWeight(0.5);
  splitPane.setOneTouchExpandable(true);

  emailList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  emailList.addListSelectionListener(this::emailListSelectionChanged);
  emailList.setFont(EMAIL_LIST_FONT);
  JScrollPane listScrollPane = new JScrollPane(emailList);
  listScrollPane.setBackground(BACKGROUND_COLOR);

  emailContent.setEditable(false);
  emailContent.setFont(EMAIL_CONTENT_FONT);
  JScrollPane contentScrollPane = new JScrollPane(emailContent);
  contentScrollPane.setBackground(BACKGROUND_COLOR);

  splitPane.setLeftComponent(listScrollPane);
  splitPane.setRightComponent(contentScrollPane);

  getContentPane().setBackground(BACKGROUND_COLOR);
  getContentPane().add(splitPane, BorderLayout.CENTER);

  JButton replyButton = new JButton("Reply");
  JButton forwardButton = new JButton("Forward");
  replyButton.setFont(BUTTON_FONT);
  forwardButton.setFont(BUTTON_FONT);
  replyButton.setBackground(BUTTON_COLOR);
  forwardButton.setBackground(BUTTON_COLOR);

  replyButton.addActionListener(e -> prepareEmailAction("Reply"));
  forwardButton.addActionListener(e -> prepareEmailAction("Forward"));

  JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
  actionPanel.setBackground(ACTION_PANEL_COLOR);
  actionPanel.add(replyButton);
  actionPanel.add(forwardButton);

  add(actionPanel, BorderLayout.NORTH);

  JPanel bottomPanel = new JPanel(new GridLayout(1, 2));
  bottomPanel.setBackground(ACTION_PANEL_COLOR);
  JButton composeButton = new JButton("Compose");
  JButton refreshInboxButton = new JButton("Refresh Inbox");
  composeButton.setBackground(BUTTON_COLOR);
  refreshInboxButton.setBackground(BUTTON_COLOR);
  composeButton.setFont(BUTTON_FONT);
  refreshInboxButton.setFont(BUTTON_FONT);

  composeButton.addActionListener(e -> showComposeDialog("", "", ""));
  refreshInboxButton.addActionListener(e -> refreshInbox());
  bottomPanel.add(composeButton);
  bottomPanel.add(refreshInboxButton);
  add(bottomPanel, BorderLayout.SOUTH);

  SwingUtilities.invokeLater(this::showLoginDialog);
}

In this code, we've:

  • Defined color constants and font settings to create a cohesive color scheme and ensure readability and visual appeal.
  • Applied these colors and fonts to various UI components, including the email list, content area, buttons, and panels, to achieve a consistent and stylish appearance throughout the application.
  • Set the background color of the JScrollPane components to match the overall theme.
  • Utilized FlowLayout for the action panel to ensure buttons are aligned nicely and provide a cleaner look at the top of the application.

Note that we also need to ensure we add these new imports:

import java.awt.Color;
import java.awt.Font;

Now, let’s also add the same styling to our email composition and login dialog windows:

private void showLoginDialog() {
  JPanel panel = new JPanel(new GridLayout(0, 1));
  panel.setBackground(BACKGROUND_COLOR);
 
  JLabel emailLabel = new JLabel("Email:");
  emailLabel.setFont(BUTTON_FONT);
  panel.add(emailLabel);
  usernameField.setFont(EMAIL_LIST_FONT);
  panel.add(usernameField);
 
  JLabel passwordLabel = new JLabel("App Password:");
  passwordLabel.setFont(BUTTON_FONT);
  panel.add(passwordLabel);
  passwordField.setFont(EMAIL_LIST_FONT);
  panel.add(passwordField);

  int result = JOptionPane.showConfirmDialog(null, panel, "Login",
          JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
  if (result == JOptionPane.OK_OPTION) {
      String username = usernameField.getText();
      String password = new String(passwordField.getPassword());
      try {
          EmailSessionManager.getInstance(username, password);
          refreshInbox();
      } catch (MessagingException e) {
          JOptionPane.showMessageDialog(this, "Failed to initialize email session: " + e.getMessage(),
                  "Login Error", JOptionPane.ERROR_MESSAGE);
      }
  } else {
      System.out.println("Login cancelled.");
  }
}
private void showComposeDialog(String to, String subject, String body) {
  JDialog composeDialog = new JDialog(this, "Compose Email", true);
  composeDialog.setLayout(new BorderLayout(5, 5));
  composeDialog.getContentPane().setBackground(BACKGROUND_COLOR);

  Box fieldsPanel = Box.createVerticalBox();
  fieldsPanel.setBackground(BACKGROUND_COLOR);
 
  JTextField toField = new JTextField(to);
  toField.setFont(EMAIL_CONTENT_FONT);
  JTextField subjectField = new JTextField(subject);
  subjectField.setFont(EMAIL_CONTENT_FONT);
  JTextArea bodyArea = new JTextArea(10, 20);
  bodyArea.setText(body);
  bodyArea.setFont(EMAIL_CONTENT_FONT);
  bodyArea.setLineWrap(true);
  bodyArea.setWrapStyleWord(true);

  JLabel toLabel = new JLabel("To:");
  toLabel.setFont(BUTTON_FONT);
  fieldsPanel.add(toLabel);
  fieldsPanel.add(toField);
  JLabel subjectLabel = new JLabel("Subject:");
  subjectLabel.setFont(BUTTON_FONT);
  fieldsPanel.add(subjectLabel);
  fieldsPanel.add(subjectField);

  JPanel bottomPanel = new JPanel();
  bottomPanel.setBackground(ACTION_PANEL_COLOR);
 
  JButton attachButton = new JButton("Attach Files");
  attachButton.setFont(BUTTON_FONT);
  attachButton.setBackground(BUTTON_COLOR);
 
  JButton sendButton = new JButton("Send");
  sendButton.setFont(BUTTON_FONT);
  sendButton.setBackground(BUTTON_COLOR);
 
  JLabel attachedFilesLabel = new JLabel("No files attached");
  attachedFilesLabel.setFont(BUTTON_FONT);

  List<File> attachedFiles = new ArrayList<>();
  attachButton.addActionListener(e -> {
      File[] files = AttachmentChooser.chooseAttachments();
      attachedFiles.addAll(Arrays.asList(files));
      attachedFilesLabel.setText(attachedFiles.size() + " files attached");
  });

  sendButton.addActionListener(e -> {
      EmailSender.sendEmailWithAttachment(toField.getText(), subjectField.getText(), bodyArea.getText(),
              attachedFiles.toArray(new File[0]));
      composeDialog.dispose();
  });

  bottomPanel.add(attachButton);
  bottomPanel.add(sendButton);

  composeDialog.add(fieldsPanel, BorderLayout.NORTH);
  composeDialog.add(new JScrollPane(bodyArea), BorderLayout.CENTER); // Ensure body area is scrollable
  composeDialog.add(bottomPanel, BorderLayout.SOUTH);

  composeDialog.pack(); // Adjust dialog size to fit content
  composeDialog.setLocationRelativeTo(this); // Center dialog relative to the main window
  composeDialog.setVisible(true);
}​

In this code, we've:

  • Applied a background color to the panels of each dialog for a consistent look with the rest of the application.
  • Set custom fonts for labels, buttons, text fields, and text areas to improve readability and aesthetics.
  • Used setBackground and setFont methods to apply these styles to various components within the dialogs.
  • Ensured that the "Attach Files" and "Send" buttons in the compose dialog also followed the application's color scheme and font styling.
  • Adjusted layout and spacing to make the dialogs not only functional but also attractive, providing a better user experience.

Great work on tackling this extra step! This is really only the tip of the iceberg, as you can get as creative as you like with the styling and UX design.

There is also a lot more functionality we could dive into, including marking emails as read, deleting emails, organizing emails, offering user customizability for the UI, and much more.

I would definitely encourage you to explore all of these features to really test yourself and take your Java email client to the next level!

Plus, we’ve focused on connecting with Gmail, but perhaps you can experiment with various email providers and servers. You could even add the option for a user to choose their provider.

Step 9: Testing the Application

Fantastic work on developing your Java email client application! 

Now, it's time to apply the finishing touches and rigorously test the application to ensure its functionality is flawless and the user experience is intuitive and engaging. 

For our email client, testing could involve several key areas: functionality, usability, and interface design. 

Let’s take a look at how you could approach each of these:

i. Functionality Testing

  • Email Sending and Receiving: Confirm that emails are correctly sent and received through the application. Include tests for attachments and ensure that they are properly attached and accessible on the receiver's end.
  • Login/Logout: Test the login mechanism with valid and invalid credentials. Verify that logout or application exit properly ends the session without leaving connections open.
  • Error Handling: Introduce scenarios such as incorrect login credentials, network failures, and sending emails without a recipient to ensure the application handles these gracefully.
  • Attachment Functionality: Specifically test the attachment functionality to ensure files are correctly attached, sent, and received.

ii. Usability Testing

  • User Interface and Experience: Seek feedback on the application's ease of use, including the intuitiveness of sending emails, attaching files, and navigating received emails.
  • Performance: Assess the application's performance, particularly its responsiveness when sending and receiving emails, including those with large attachments.
  • Accessibility: Ensure the application is accessible, with clear fonts, appropriate color contrasts, and support for keyboard navigation where possible.

iii. Interface Design Testing

  • Consistency and Responsiveness: Check the application's appearance across different operating systems, screen sizes, and resolutions to ensure consistent styling and responsive design.
  • Aesthetic Appeal: Evaluate the application's visual appeal, including the use of colors, fonts, and layout, to ensure it provides a pleasant user experience.

When it comes to conducting these tests, much of the functionality and usability testing will be manual due to the interactive nature of an email client application.

I’d also highly encourage you to get a small group of real users to use the application and provide feedback on usability and any bugs they encounter.

And remember that testing should be an iterative process. After fixing issues, retest to ensure no new problems have been introduced and that the original issue has been resolved.

Now that you’ve reached this stage, you other steps I’d encourage you to consider include

Documentation and Sharing:

  • Consider drafting a README file if you plan to showcase your project on platforms like GitHub, detailing the project setup and any noteworthy features.
  • Share your project within developer communities or on social media to engage with a broader audience and gather additional feedback.

Reflect and Plan Next Steps:

  • Reflect on the knowledge gained through this project and contemplate how these learnings can be applied to future endeavors.
  • Consider expanding your chat application with more complex features, like sound effects, user profiles, user avatars, user status messages, and more.

Great job on building and refining your Java email client! Take a moment to appreciate your hard work!

Whether you're building this for fun, as a learning experience, or as a portfolio piece, you've developed valuable skills in programming, problem-solving, and user interface design.

Be proud of your work, share it with others, and consider what project you'll take on next!

Step 10: Final Touches and Packaging

After thorough testing and refinements, it's time to apply the final touches to your Java email client and prepare it for distribution. 

This step is all about ensuring your application is polished, user-friendly, and ready for others to use.

i. Final Review and Refinement

  • Code Cleanup: Go through your code to remove any unused variables, methods, or imports. Ensure your code is well-commented and follows a consistent coding style for easy maintenance and readability.
  • UI Consistency Check: Review the GUI to ensure all elements align with the chosen design theme. Test the application on different screen sizes and resolutions to guarantee a consistent user experience.
  • Optimization: Optimize performance where possible, focusing on reducing latency and improving message delivery times. Ensure the application uses resources efficiently, minimizing its footprint on the user's system.
  • Security Review: Conduct a final security review to ensure there are no vulnerabilities, such as unprotected sensitive data or susceptibility to injection attacks.

ii. Documentation

  • User Guide: Create a user guide that includes installation instructions, features overview, and usage tips. Include troubleshooting steps for common issues users might encounter.
  • Developer Documentation: Document your code to help future developers understand and contribute to the project. Consider using tools like Javadoc to generate professional-looking documentation.

iii. Packaging and Distribution

  • Packaging the Application: For desktop apps, package your Java application as an executable JAR file. Use tools like Launch4j or JPackage to bundle your app into native installers for Windows, macOS, and Linux. Ensure all libraries are included in the package, and test the installation process on different OS.
  • Distribution Platforms: Choose appropriate platforms to distribute your application, such as GitHub for open-source projects, or personal websites for broader distribution.
  • Web Application: Consider using Java Web Start (for older versions of Java) or creating applets if your application needs to run within a web browser, though modern security restrictions make desktop distribution more viable for most cases.

Next Steps & Further Learning

Congratulations on successfully building your own Java email client application!

This is a significant achievement, but your learning journey doesn't stop here. There are many ways to further your skills in Java development. Let's explore some ideas:

Learn More About Java

  • Java Enhancements: Explore more advanced Java concepts and apply them to your app. This could be displaying html email, marking emails as read, adding cc and bcc, organizing mail into folders, setting up auto-replies, deleting emails, or sound effects. How about letting users customize the UI? And how about trying to work with various email providers and servers outside of Gmail?
  • Explore Frameworks: Experiment with Java frameworks to see how they can be used to build more dynamic and complex Java applications.

Join Online Communities and Collaborate

  • Engage in Forums: Participate in Java development forums and communities. Share your email client, get feedback, and learn from others.
  • Contribute to Open Source: Consider making your email client open-source and collaborate with others to improve it.

Keep Up with Trends and Best Practices

Stay updated with the latest trends in Java development. Subscribe to blogs like hackr.io, watch webinars, and join online courses.

Document and Share Your Learning Journey

  • Blog About Your Project: Write about your development process, challenges you faced, and how you overcame them. Share your blog with the developer community.
  • Share Your Code: Publish your code on platforms like GitHub. This not only showcases your work but also allows others to learn from your project.

Challenge Yourself Regularly

Take part in coding challenges or hackathons to sharpen your skills and learn new techniques.

And if you're hungry for more Java projects, check back regularly as we’re constantly adding new step-by-step tutorials.

For example, have you taken a look at my tutorials on how to build a Java chat app, or how to create your own Java chess game?

Java Email Client Application Full Source Code

Email Sender:

import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
import java.io.File;

public class EmailSender {
  public static void sendEmailWithAttachment(String to, String subject, String body, File[] attachments) {
      try {
          String username = EmailSessionManager.getUsername();
          String password = EmailSessionManager.getPassword();

          Properties prop = new Properties();
          prop.put("mail.smtp.host", "smtp.gmail.com");
          prop.put("mail.smtp.port", "587");
          prop.put("mail.smtp.auth", "true");
          prop.put("mail.smtp.starttls.enable", "true");

          Session session = Session.getInstance(prop, new javax.mail.Authenticator() {
              protected PasswordAuthentication getPasswordAuthentication() {
                  return new PasswordAuthentication(username, password);
              }
          });

          try {
              Message message = new MimeMessage(session);
              message.setFrom(new InternetAddress(username));
              message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
              message.setSubject(subject);

              Multipart multipart = new MimeMultipart();

              MimeBodyPart textPart = new MimeBodyPart();
              textPart.setText(body);
              multipart.addBodyPart(textPart);

              for (File file : attachments) {
                  MimeBodyPart attachmentPart = new MimeBodyPart();
                  attachmentPart.attachFile(file);
                  multipart.addBodyPart(attachmentPart);
              }

              message.setContent(multipart);
              Transport.send(message);
              System.out.println("Email sent successfully with attachments.");
          } catch (Exception e) {
              e.printStackTrace();
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}

Email Receiver:

import javax.mail.Message;
import javax.mail.MessagingException;

public class EmailReceiver {

  public static Message[] receiveEmail() throws MessagingException {
      EmailSessionManager manager = EmailSessionManager.getInstance();
      Message[] messages = manager.receiveEmail();
      return messages;
  }
}

Email Session Manager:

import javax.mail.Message;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import java.util.Properties;

public class EmailSessionManager {
  private Session emailSession;
  private Store store;
  private Folder emailFolder;
  private static EmailSessionManager instance;

  private static String currentUsername = "";
  private static String currentPassword = "";

  private EmailSessionManager(String username, String password) throws MessagingException {
      Properties properties = new Properties();
      properties.put("mail.store.protocol", "imaps");
      properties.put("mail.imaps.host", "imap.gmail.com");
      properties.put("mail.imaps.port", "993");
      properties.put("mail.imaps.ssl.enable", "true");
      this.emailSession = Session.getInstance(properties, null);
      this.store = emailSession.getStore("imaps");
      this.store.connect(username, password);

      currentUsername = username;
      currentPassword = password;
  }

  public static EmailSessionManager getInstance(String username, String password) throws MessagingException {
      if (instance == null) {
          instance = new EmailSessionManager(username, password);
      }
      return instance;
  }

  public static EmailSessionManager getInstance() throws IllegalStateException {
      if (instance == null) {
          throw new IllegalStateException("EmailSessionManager is not initialized. Please login first.");
      }
      return instance;
  }

  public static String getUsername() {
      return currentUsername;
  }

  public static String getPassword() {
      return currentPassword;
  }

  public Message[] receiveEmail() throws MessagingException {
      if (emailFolder == null || !emailFolder.isOpen()) {
          emailFolder = store.getFolder("INBOX");
          emailFolder.open(Folder.READ_ONLY);
      }
      return emailFolder.getMessages();
  }

  public void close() throws MessagingException {
      if (emailFolder != null) {
          emailFolder.close(false);
          emailFolder = null;
      }
      if (store != null) {
          store.close();
          store = null;
      }
      instance = null;
      currentUsername = "";
      currentPassword = "";
  }
}

Attachment Chooser:

import javax.swing.*;
import java.io.File;

public class AttachmentChooser {
  public static File[] chooseAttachments() {
      JFileChooser fileChooser = new JFileChooser();
      fileChooser.setMultiSelectionEnabled(true);
      int option = fileChooser.showOpenDialog(null);
      if (option == JFileChooser.APPROVE_OPTION) {
          return fileChooser.getSelectedFiles();
      }
      return new File[] {};
  }
}

Email Client GUI:

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.io.IOException;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMultipart;
import java.awt.Color;
import java.awt.Font;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class EmailClientGUI extends JFrame {
  private JTextField usernameField = new JTextField();
  private JPasswordField passwordField = new JPasswordField();
  private DefaultListModel<String> emailListModel = new DefaultListModel<>();
  private JList<String> emailList = new JList<>(emailListModel);
  private JTextArea emailContent = new JTextArea();
  private Message[] messages;

  public EmailClientGUI() {
      setTitle("Java Email Client");
      setSize(800, 600);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      initUI();
      setVisible(true);

      addWindowListener(new WindowAdapter() {
          @Override
          public void windowClosing(WindowEvent e) {
              try {
                  if (EmailSessionManager.getInstance() != null) {
                      EmailSessionManager.getInstance().close(); // Close the email session
                  }
              } catch (MessagingException ex) {
                  ex.printStackTrace();
              }
          }
      });
  }

  private static final Color BACKGROUND_COLOR = new Color(230, 240, 250);
  private static final Color ACTION_PANEL_COLOR = new Color(200, 220, 240);
  private static final Color BUTTON_COLOR = new Color(180, 220, 240);
  private static final Font BUTTON_FONT = new Font("SansSerif", Font.BOLD, 12);
  private static final Font EMAIL_LIST_FONT = new Font("SansSerif", Font.PLAIN, 14);
  private static final Font EMAIL_CONTENT_FONT = new Font("SansSerif", Font.PLAIN, 14);

  private void initUI() {
      JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
      splitPane.setResizeWeight(0.5);
      splitPane.setOneTouchExpandable(true);

      emailList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      emailList.addListSelectionListener(this::emailListSelectionChanged);
      emailList.setFont(EMAIL_LIST_FONT);
      JScrollPane listScrollPane = new JScrollPane(emailList);
      listScrollPane.setBackground(BACKGROUND_COLOR);

      emailContent.setEditable(false);
      emailContent.setFont(EMAIL_CONTENT_FONT);
      JScrollPane contentScrollPane = new JScrollPane(emailContent);
      contentScrollPane.setBackground(BACKGROUND_COLOR);

      splitPane.setLeftComponent(listScrollPane);
      splitPane.setRightComponent(contentScrollPane);

      getContentPane().setBackground(BACKGROUND_COLOR);
      getContentPane().add(splitPane, BorderLayout.CENTER);

      JButton replyButton = new JButton("Reply");
      JButton forwardButton = new JButton("Forward");
      replyButton.setFont(BUTTON_FONT);
      forwardButton.setFont(BUTTON_FONT);
      replyButton.setBackground(BUTTON_COLOR);
      forwardButton.setBackground(BUTTON_COLOR);

      replyButton.addActionListener(e -> prepareEmailAction("Reply"));
      forwardButton.addActionListener(e -> prepareEmailAction("Forward"));

      JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
      actionPanel.setBackground(ACTION_PANEL_COLOR);
      actionPanel.add(replyButton);
      actionPanel.add(forwardButton);

      add(actionPanel, BorderLayout.NORTH);

      JPanel bottomPanel = new JPanel(new GridLayout(1, 2));
      bottomPanel.setBackground(ACTION_PANEL_COLOR);
      JButton composeButton = new JButton("Compose");
      JButton refreshInboxButton = new JButton("Refresh Inbox");
      composeButton.setBackground(BUTTON_COLOR);
      refreshInboxButton.setBackground(BUTTON_COLOR);
      composeButton.setFont(BUTTON_FONT);
      refreshInboxButton.setFont(BUTTON_FONT);

      composeButton.addActionListener(e -> showComposeDialog("", "", ""));
      refreshInboxButton.addActionListener(e -> refreshInbox());
      bottomPanel.add(composeButton);
      bottomPanel.add(refreshInboxButton);
      add(bottomPanel, BorderLayout.SOUTH);

      SwingUtilities.invokeLater(this::showLoginDialog);
  }

  private void showLoginDialog() {
      JPanel panel = new JPanel(new GridLayout(0, 1));
      panel.setBackground(BACKGROUND_COLOR);

      JLabel emailLabel = new JLabel("Email:");
      emailLabel.setFont(BUTTON_FONT);
      panel.add(emailLabel);
      usernameField.setFont(EMAIL_LIST_FONT);
      panel.add(usernameField);

      JLabel passwordLabel = new JLabel("App Password:");
      passwordLabel.setFont(BUTTON_FONT);
      panel.add(passwordLabel);
      passwordField.setFont(EMAIL_LIST_FONT);
      panel.add(passwordField);

      int result = JOptionPane.showConfirmDialog(null, panel, "Login",
              JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
      if (result == JOptionPane.OK_OPTION) {
          String username = usernameField.getText();
          String password = new String(passwordField.getPassword());
          try {
              EmailSessionManager.getInstance(username, password);
              refreshInbox();
          } catch (MessagingException e) {
              JOptionPane.showMessageDialog(this, "Failed to initialize email session: " + e.getMessage(),
                      "Login Error", JOptionPane.ERROR_MESSAGE);
          }
      } else {
          System.out.println("Login cancelled.");
      }
  }

  private void refreshInbox() {
      try {
          messages = EmailSessionManager.getInstance().receiveEmail();
          emailListModel.clear();
          for (Message message : messages) {
              emailListModel
                      .addElement(message.getSubject() + " - From: " + InternetAddress.toString(message.getFrom()));
          }
      } catch (MessagingException e) {
          JOptionPane.showMessageDialog(this, "Failed to fetch emails: " + e.getMessage(), "Error",
                  JOptionPane.ERROR_MESSAGE);
      }
  }

  private void emailListSelectionChanged(ListSelectionEvent e) {
      if (!e.getValueIsAdjusting() && emailList.getSelectedIndex() != -1) {
          try {
              Message selectedMessage = messages[emailList.getSelectedIndex()];
              emailContent.setText("");
              emailContent.append("Subject: " + selectedMessage.getSubject() + "\n\n");
              emailContent.append("From: " + InternetAddress.toString(selectedMessage.getFrom()) + "\n\n");
              emailContent.append(getTextFromMessage(selectedMessage));
          } catch (MessagingException | IOException ex) {
              emailContent.setText("Error reading email content: " + ex.getMessage());
          }
      }
  }

  private String getTextFromMessage(Message message) throws MessagingException, IOException {
      if (message.isMimeType("text/plain")) {
          return (String) message.getContent();
      } else if (message.isMimeType("multipart/*")) {
          MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
          for (int i = 0; i < mimeMultipart.getCount(); i++) {
              BodyPart bodyPart = mimeMultipart.getBodyPart(i);
              if (bodyPart.isMimeType("text/plain")) {
                  return (String) bodyPart.getContent();
              }
          }
      }
      return "No readable content found.";
  }
 
  private void prepareEmailAction(String actionType) {
      if (emailList.getSelectedIndex() == -1) {
          JOptionPane.showMessageDialog(this, "No email selected.", "Error", JOptionPane.ERROR_MESSAGE);
          return;
      }
      try {
          Message selectedMessage = messages[emailList.getSelectedIndex()];
          String to = actionType.equals("Reply") ? InternetAddress.toString(selectedMessage.getFrom()) : "";
          String subjectPrefix = actionType.equals("Reply") ? "Re: " : "Fwd: ";
          String subject = subjectPrefix + selectedMessage.getSubject();
          String body = getTextFromMessage(selectedMessage);

          showComposeDialog(to, subject, body);
      } catch (MessagingException | IOException ex) {
          JOptionPane.showMessageDialog(this, "Error preparing email action.", "Error", JOptionPane.ERROR_MESSAGE);
      }
  }

  private void showComposeDialog(String to, String subject, String body) {
      JDialog composeDialog = new JDialog(this, "Compose Email", true);
      composeDialog.setLayout(new BorderLayout(5, 5));
      composeDialog.getContentPane().setBackground(BACKGROUND_COLOR);

      Box fieldsPanel = Box.createVerticalBox();
      fieldsPanel.setBackground(BACKGROUND_COLOR);

      JTextField toField = new JTextField(to);
      toField.setFont(EMAIL_CONTENT_FONT);
      JTextField subjectField = new JTextField(subject);
      subjectField.setFont(EMAIL_CONTENT_FONT);
      JTextArea bodyArea = new JTextArea(10, 20);
      bodyArea.setText(body);
      bodyArea.setFont(EMAIL_CONTENT_FONT);
      bodyArea.setLineWrap(true);
      bodyArea.setWrapStyleWord(true);

      JLabel toLabel = new JLabel("To:");
      toLabel.setFont(BUTTON_FONT);
      fieldsPanel.add(toLabel);
      fieldsPanel.add(toField);
      JLabel subjectLabel = new JLabel("Subject:");
      subjectLabel.setFont(BUTTON_FONT);
      fieldsPanel.add(subjectLabel);
      fieldsPanel.add(subjectField);

      JPanel bottomPanel = new JPanel();
      bottomPanel.setBackground(ACTION_PANEL_COLOR);

      JButton attachButton = new JButton("Attach Files");
      attachButton.setFont(BUTTON_FONT);
      attachButton.setBackground(BUTTON_COLOR);

      JButton sendButton = new JButton("Send");
      sendButton.setFont(BUTTON_FONT);
      sendButton.setBackground(BUTTON_COLOR);

      JLabel attachedFilesLabel = new JLabel("No files attached");
      attachedFilesLabel.setFont(BUTTON_FONT);

      List<File> attachedFiles = new ArrayList<>();
      attachButton.addActionListener(e -> {
          File[] files = AttachmentChooser.chooseAttachments();
          attachedFiles.addAll(Arrays.asList(files));
          attachedFilesLabel.setText(attachedFiles.size() + " files attached");
      });

      sendButton.addActionListener(e -> {
          EmailSender.sendEmailWithAttachment(toField.getText(), subjectField.getText(), bodyArea.getText(),
                  attachedFiles.toArray(new File[0]));
          composeDialog.dispose();
      });

      bottomPanel.add(attachButton);
      bottomPanel.add(sendButton);

      composeDialog.add(fieldsPanel, BorderLayout.NORTH);
      composeDialog.add(new JScrollPane(bodyArea), BorderLayout.CENTER);
      composeDialog.add(bottomPanel, BorderLayout.SOUTH);

      composeDialog.pack();
      composeDialog.setLocationRelativeTo(this);
      composeDialog.setVisible(true);
  }

  public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new EmailClientGUI());
  }
}

Wrapping Up

Building a Java chat application is a great way to enhance your Java development skills and delve into creating interactive Java applications.

By creating this interactive Java email client application, you've navigated through a range of challenges, including devising a user-friendly interface, handling email protocols, and dynamically updating the user interface based on email interactions.

In this tutorial, you’ve learned how to:

  • Utilize Java Swing to design and implement a visually appealing and functional email client GUI.
  • Write Java code for user authentication, sending emails with attachments, and fetching emails from a server in real time.
  • Dynamically update Swing components to display incoming emails and ensure a responsive user experience.
  • Capture user inputs through event listeners to compose new emails, attach files, and manage the login process.
  • Integrate additional functionalities such as replying to and forwarding emails, thereby enhancing the email management capabilities.

You now possess the essential tools and knowledge needed to further develop and expand this Java email client application. 

Potential enhancements could include advanced email sorting and filtering, encryption for email security, or even integration with multiple email providers. 

Your journey into the world of Java doesn't end here. With these new skills, you're well-equipped to experiment with more complex Java projects, explore other aspects of Java, and continue building a range of Java applications.

Have fun and happy coding!

Want to sharpen up your Java development skills in 2024? Check out:

Udemy's Top Rated Course: Java 17 Masterclass: Start Coding in 2024

By Robert Johns

Technical Editor for Hackr.io | 15+ Years in Python, Java, SQL, C++, C#, JavaScript, Ruby, PHP, .NET, MATLAB, HTML & CSS, and more... 10+ Years in Networking, Cloud, APIs, Linux | 5+ Years in Data Science | 2x PhDs in Structural & Blast Engineering

View all post by the author

Learn More

Please login to leave comments