Welcome to Part 17 of this series of guides on how to integrate Gmail with Angular and Spring Boot.

Up to this point, you've written code to receive emails. But today you'll write code to send emails.

You're going to start on the back end, though. So no UI stuff here.

If you'd like to go straight to the source code, feel free to grab it from GitHub.

Otherwise, read on.

Where It's At

Thus far in the series, you have:

  • Created an OAuth2 client ID and secret on Google Cloud Console
  • Enabled the Gmail API via Google Cloud Console
  • Set the necessary properties in your Spring Boot application.properties file 
  • Set up a DataStore implementation with its associated factory
  • Set up a refresh listener
  • Created a utility class that handles the Google authorization code flow
  • Set up proper Gmail consent with the Google Cloud Platform
  • Created a controller that handles a request to get the Google authorization code flow URL
  • Used Postman to get the Google authorization code URL
  • Used that URL to authorize your application to access your Gmail inbox
  • Updated the controller so it handles a token request
  • Updated the utility class so it creates the credential from a token
  • Persisted that credential
  • Used Postman to retrieve the token
  • Learned why you didn't actually need to retrieve the token
  • Learned about the structure of the Message object
  • Learned about MIME
  • Created an Email type that gets sent back to the client
  • Wrote code that converts a Message object to an Email object
  • Instantiated the Gmail service
  • Used the Gmail service to retrieve just the messages you want to retrieve
  • Used the Gmail service to extracted full Message objects from very lightweight objects
  • Added a new endpoint to listen for inbox requests
  • Used that endpoint to send back the user's most recent emails
  • Learned how to disallow your application from accessing your inbox
  • Created a new component on the Angular side that shows emails from the user's inbox
  • Created a new service on the Angular side that retrieves emails from the Spring Boot microservice
  • Optimized the payload response from the microservice
  • Displayed HTML reference characters as they should be displayed
  • Got rid of whitespace that isn't whitespace
  • Designed a user-friendly, responsive inbox
  • Created a UI so people can read individual email messages
  • Learned about the security risks associated with displaying full HTML messages from the Gmail API

And if you haven't done those things, Part 1 is waiting for you.

Where It's Going

In this guide, you will:

  • Update the service class to support sending emails
  • Add a new method to the controller so that upstream applications can send emails
  • Test it all out in Postman

And then you can go back to flyfishing.

Developing With Dependencies

Now that you're sending emails, you'll need to add a couple of dependencies to your Spring Boot project.

Put this XML block in your pom.xml file:

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>javax.mail-api</artifactId>
</dependency>        
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>javax.mail</artifactId>
    <version>1.6.2</version>
</dependency>  

That first dependency enables you to instantiate a MimeMessage object. More on that in a moment.

The second dependency is required by the first dependency. Just roll with it.

A New Util

Start by creating a new utility class. Call it EmailUtil and make it look like this:

public class EmailUtil {

    public static MimeMessage convertEmailToMimeMessage(Email email) throws MessagingException {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        MimeMessage mimeMessage = new MimeMessage(session);

        mimeMessage.setFrom(new InternetAddress(email.getFrom()));
        mimeMessage.addRecipient(javax.mail.Message.RecipientType.TO,
                new InternetAddress(email.getTo()));
        mimeMessage.setSubject(email.getSubject());
        mimeMessage.setText(email.getPlainText());

        return mimeMessage;
    }
}

Yep. Just one static method.

The whole point of that method (and that class, for now) is to convert an Email object into a MimeMessage object.

You need to do that because that's the format Gmail requires. If you'd like to know more about that format, feel free to read this very ugly document.

Anyhoo, the bottom line is the Gmail emails need to be MIME-compliant. You may recall from a previous guide that Multipurpose Internet Mail Extensions (or MIME) is the standard that extends the format of email messages so they support attachments. 

So you gotta convert your POJO Email object into a MimeMessage object.

But that's only the beginning.

Serendipity in the Service

Next, update GmailService. Add this method:

    private Message createGmailMessageFromEmail(Email email) throws MessagingException, IOException {
       MimeMessage mimeMessage = EmailUtil.convertEmailToMimeMessage(email);
       
       ByteArrayOutputStream buffer = new ByteArrayOutputStream();
       mimeMessage.writeTo(buffer);
       
       byte[] bytes = buffer.toByteArray();
       String encodedEmail = Base64.getEncoder().encodeToString(bytes);
       
       Message message = new Message();
       message.setRaw(encodedEmail);
       
       return message;
    }

That method takes the MimeMessage object you just created and converts it to a Message object.

Wait. Why is that necessary? Didn't we just convert the POJO into a MimeMessage object?

Well, yeah. But that just means that raw content of the email is MIME-compliant. The Gmail Message object can include lots of metadata to go along with that message.

So you need to convert the message from Email into MimeMessage and then into Message.

That method doesn't do anything overly complicated, by the way. It starts by creating a MimeMessage object from the Email POJO.

Then it takes the MimeMessage object and converts it to a byte array. After that, it incodes the byte array in Base64 format and sets the raw content of a newly instantiated Message object with that encoded result.

Finally, it returns the new Message object.

Next, create a sendEmail() method in the same service:

    public Email sendEmail(Email email, Credential credential) throws IOException, GeneralSecurityException, MessagingException {
        Gmail service = new Gmail.Builder(GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory(), credential)
                .setApplicationName(GmailConstants.GMAIL_APPLICATION_NAME)
                .build(); 
         
        Message message = createGmailMessageFromEmail(email);
        
        Message sentMessage = service
                                .users()
                                .messages()
                                .send(GmailConstants.CURRENT_USER, message)
                                .execute();
        
        Email sentEmail = getEmail(sentMessage, true);
        return sentEmail;
    }

Here's where the "sending" actually happens.

The sendEmail() method takes two parameters: the Email POJO and the Credential object.

By now, you should be familiar with the process of instantiating Gmail. If not, feel free to revisit the guide on getting only the emails you want.

Next, the code invokes the createGmailMessageFromEmail() method that you just saw. That will return a Message object.

Then, the code uses the Gmail service to actually send the email. If everything goes well, the API will return a new Message object that includes the email's ID.

The code converts that Message object into an Email POJO and returns it.

Controller Panel

Next, add a new method to GmailController.

    @PostMapping("/messages")
    public ResponseEntity<?> sendEmailMessage(@RequestBody Email email) {
        LOG.debug("Sending message " + email);

        try {
            User currentUser = authUtil.getCurrentUser();            
            Credential credential = googleOauthService.getCredential(currentUser.getId());
            
            Email sentEmail = gmailService.sendEmail(email, credential);
            
            return ResponseEntity.ok(sentEmail);
        } catch (IOException ie) {
            LOG.error("Problem retrieving email!", ie);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ie.getMessage());
        } catch (GeneralSecurityException ge) {
            LOG.error("Security issue!", ge);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ge.getMessage());
        } catch (MessagingException me) {
            LOG.error("Messaging issue!", me);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(me.getMessage());            
        }
    }

The @PostMapping annotation maps this method to the /email/messages endpoint when the request is a POST

(If you're wondering where the /email comes from, it's in the @RequestMapping annotation towards the top of the class but not pictured here).

The method accepts an object of type Email in the request body. That means it will look something like this:

{
    "plainText" : "Check out what's going on in the watercooler room.",
    "subject" : "Jack's back",
    "from" : "boris@darling2.com",
    "to" : "natasha@darling2.com"
}

That JSON object will get converted to an Email POJO thanks to the magic of the Spring framework.

Next, the controller follows the usual pattern of grabbing the user's credential from the data store and sending that with the Email object to the sendEmail() method you just saw.

Then it returns that lightweight Email object it gets back from the service. That's assuming everything worked.

And that's it. 

Testing Time

Fire up Postman. Then, fire up the Email API (the thing you've been working on) as well as the User API

Head over to Postman and use the /authenticate endpoint to log in. Take a look at Part 5 of this series if you need a refresher on how to do that.

Create a new request that points to /email/messages. Make it a POST. Click on the Body tab just under the URL bar and add a body similar to what you saw above.

The whole thing should look like this:

 

You'll probably want to use a legit "to" address. Preferably one of your own email addresses so you can make sure that the Gmail API sent the message.

Next, grab that Bearer token you got back when you authenticated. Paste it into the Token field in the Authorization tab. If you have no idea what I'm talking about, Part 5 is your friend.

Now click that big blue Send button.

If everything went according to plan, you should get a response back with a populated "id" property. That means it worked.

Next, check your inbox. Make sure the message arrived.

If it did, you're in great shape.

Wrapping It Up

Congratulations! You've sent your first email.

In the next guide, you'll kick it up a notch. 

Until then, have fun!

Photo by John-Mark Smith from Pexels