pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>gmail-sender</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Gmail API -->
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-gmail</artifactId>
<version>v1-rev20230810-2.0.0</version>
</dependency>
<!-- Google Auth Library -->
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.11.0</version>
</dependency>
<!-- Google Auth API Client -->
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-appengine</artifactId>
<version>1.11.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JSON Processing (Jackson) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
GmailConfig.java
package com.example.gmail.config;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
@Configuration
public class GmailConfig {
private static final String APPLICATION_NAME = "Gmail API Spring Boot";
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
private static final String TOKENS_DIRECTORY_PATH = "tokens";
private static final List<String> SCOPES =
Collections.singletonList(GmailScopes.GMAIL_SEND);
@Bean
public Gmail gmailService() throws GeneralSecurityException, IOException {
NetHttpTransport httpTransport = new NetHttpTransport();
return new Gmail.Builder(
httpTransport,
JSON_FACTORY,
getCredentials(httpTransport))
.setApplicationName(APPLICATION_NAME)
.build();
}
private Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT)
throws IOException {
InputStream in = GmailConfig.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new IOException("credentials.json not found in resources!");
}
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(
new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver =
new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver)
.authorize("user");
}
}
EmailRequest.java & EmailResponse.java
// EmailRequest.java
package com.example.gmail.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EmailRequest {
private String to;
private String subject;
private String body;
private boolean isHtml;
}
// EmailResponse.java
package com.example.gmail.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EmailResponse {
private boolean success;
private String message;
private String messageId;
private String error;
}
GmailService.java
package com.example.gmail.service;
import com.example.gmail.dto.EmailRequest;
import com.example.gmail.dto.EmailResponse;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.Message;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.Properties;
@Slf4j
@Service
@RequiredArgsConstructor
public class GmailService {
private final Gmail gmail;
@Value("${gmail.sender.email}")
private String senderEmail;
public EmailResponse sendEmail(EmailRequest request) {
try {
MimeMessage mimeMessage = createMimeMessage(request);
String encodedMessage = encodeMessage(mimeMessage);
Message message = new Message();
message.setRaw(encodedMessage);
Message sentMessage = gmail.users()
.messages()
.send("me", message)
.execute();
log.info("Email sent successfully. Message ID: {}", sentMessage.getId());
return EmailResponse.builder()
.success(true)
.message("Email sent successfully")
.messageId(sentMessage.getId())
.build();
} catch (Exception e) {
log.error("Failed to send email", e);
return EmailResponse.builder()
.success(false)
.message("Failed to send email")
.error(e.getMessage())
.build();
}
}
private MimeMessage createMimeMessage(EmailRequest request)
throws Exception {
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.setFrom(new InternetAddress(senderEmail));
mimeMessage.addRecipient(
javax.mail.Message.RecipientType.TO,
new InternetAddress(request.getTo()));
mimeMessage.setSubject(request.getSubject());
if (request.isHtml()) {
mimeMessage.setContent(
request.getBody(),
"text/html; charset=utf-8");
} else {
mimeMessage.setText(request.getBody());
}
return mimeMessage;
}
private String encodeMessage(MimeMessage message)
throws Exception {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
message.writeTo(buffer);
byte[] bytes = buffer.toByteArray();
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
}
}
EmailController.java
package com.example.gmail.controller;
import com.example.gmail.dto.EmailRequest;
import com.example.gmail.dto.EmailResponse;
import com.example.gmail.service.GmailService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/email")
@RequiredArgsConstructor
public class EmailController {
private final GmailService gmailService;
@PostMapping("/send")
public ResponseEntity<EmailResponse> sendEmail(
@RequestBody EmailRequest emailRequest) {
if (!isValidEmail(emailRequest.getTo())) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(EmailResponse.builder()
.success(false)
.message("Invalid recipient email address")
.build());
}
EmailResponse response = gmailService.sendEmail(emailRequest);
return ResponseEntity
.status(response.isSuccess() ?
HttpStatus.OK : HttpStatus.INTERNAL_SERVER_ERROR)
.body(response);
}
private boolean isValidEmail(String email) {
String emailPattern =
"^[A-Za-z0-9+_.-]+@(.+)$";
return email != null &&
email.matches(emailPattern);
}
}
application.properties
# Server Configuration
server.port=8080
server.servlet.context-path=/
# Application Name
spring.application.name=Gmail Sender API
# Gmail Configuration
gmail.sender.email=your-email@gmail.com
# Logging
logging.level.root=INFO
logging.level.com.example.gmail=DEBUG