Enterprise Java SDK with Maven/Gradle support, Spring Boot integration, and Android compatibility.
<dependency>
<groupId>com.sparkmailr</groupId>
<artifactId>sparkmailr-java-sdk</artifactId>
<version>1.0.0</version>
</dependency>
implementation 'com.sparkmailr:sparkmailr-java-sdk:1.0.0'
import com.sparkmailr.SparkMailrClient;
import com.sparkmailr.model.SendEmailRequest;
import com.sparkmailr.model.EmailResponse;
import com.sparkmailr.exceptions.SparkMailrException;
import java.util.Arrays;
public class EmailExample {
public static void main(String[] args) {
// Initialize client
SparkMailrClient client = new SparkMailrClient.Builder()
.apiKey("your-api-key")
.build();
try {
// Send email
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("user@example.com"))
.from("noreply@yourapp.com")
.subject("Welcome to SparkMailr!")
.html("<h1>Hello World</h1>")
.text("Hello World")
.build();
EmailResponse response = client.emails().send(request);
System.out.println("Email sent: " + response.getId());
} catch (SparkMailrException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
import com.sparkmailr.SparkMailrClient;
import com.sparkmailr.SparkMailrConfig;
import java.time.Duration;
// Full configuration using Builder pattern
SparkMailrClient client = new SparkMailrClient.Builder()
.apiKey("your-api-key")
.baseUrl("https://api.sparkmailr.com") // Optional
.timeout(Duration.ofSeconds(30)) // Request timeout
.maxRetries(3) // Max retry attempts
.retryDelay(Duration.ofSeconds(1)) // Delay between retries
.debug(false) // Enable debug logging
.build();
// Configuration from properties
SparkMailrConfig config = SparkMailrConfig.builder()
.apiKey(System.getProperty("sparkmailr.api.key"))
.baseUrl(System.getProperty("sparkmailr.base.url"))
.timeout(Duration.ofSeconds(
Integer.parseInt(System.getProperty("sparkmailr.timeout", "30"))
))
.build();
SparkMailrClient client = new SparkMailrClient(config);
# application.yml
sparkmailr:
api-key: ${SPARKMAILR_API_KEY}
base-url: https://api.sparkmailr.com
timeout: 30000
max-retries: 3
debug: ${spring.profiles.active:} == 'dev'
@Configuration
@ConfigurationProperties(prefix = "sparkmailr")
@Data
public class SparkMailrProperties {
private String apiKey;
private String baseUrl = "https://api.sparkmailr.com";
private int timeout = 30000;
private int maxRetries = 3;
private boolean debug = false;
}
@Configuration
@EnableConfigurationProperties(SparkMailrProperties.class)
public class SparkMailrConfig {
@Bean
public SparkMailrClient sparkMailrClient(SparkMailrProperties properties) {
return new SparkMailrClient.Builder()
.apiKey(properties.getApiKey())
.baseUrl(properties.getBaseUrl())
.timeout(Duration.ofMillis(properties.getTimeout()))
.maxRetries(properties.getMaxRetries())
.debug(properties.isDebug())
.build();
}
}
@Service
@Slf4j
public class EmailService {
private final SparkMailrClient sparkMailrClient;
public EmailService(SparkMailrClient sparkMailrClient) {
this.sparkMailrClient = sparkMailrClient;
}
public String sendWelcomeEmail(String userEmail, String userName) {
try {
Map<String, Object> templateData = Map.of(
"name", userName,
"activationLink", "https://app.example.com/activate",
"supportEmail", "support@yourapp.com"
);
SendEmailRequest request = new SendEmailRequest.Builder()
.to(List.of(userEmail))
.from("noreply@yourapp.com")
.templateId("welcome-template")
.templateData(templateData)
.build();
EmailResponse response = sparkMailrClient.emails().send(request);
log.info("Welcome email sent to {}: {}", userEmail, response.getId());
return response.getId();
} catch (SparkMailrException e) {
log.error("Failed to send welcome email to {}: {}", userEmail, e.getMessage());
throw new EmailServiceException("Failed to send welcome email", e);
}
}
@Async
public CompletableFuture<String> sendWelcomeEmailAsync(String userEmail, String userName) {
return CompletableFuture.supplyAsync(() -> sendWelcomeEmail(userEmail, userName));
}
}
@RestController
@RequestMapping("/api/emails")
@Validated
public class EmailController {
private final EmailService emailService;
public EmailController(EmailService emailService) {
this.emailService = emailService;
}
@PostMapping("/send-welcome")
public ResponseEntity<Map<String, String>> sendWelcomeEmail(
@Valid @RequestBody SendWelcomeRequest request) {
try {
String emailId = emailService.sendWelcomeEmail(
request.getEmail(),
request.getName()
);
return ResponseEntity.ok(Map.of(
"success", "true",
"emailId", emailId
));
} catch (EmailServiceException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of(
"success", "false",
"error", e.getMessage()
));
}
}
@PostMapping("/send-bulk")
public ResponseEntity<Map<String, Object>> sendBulkEmails(
@Valid @RequestBody BulkEmailRequest request) {
List<BulkEmailRecipient> recipients = request.getEmails().stream()
.map(email -> new BulkEmailRecipient.Builder()
.to(email.getTo())
.templateData(email.getTemplateData())
.build())
.collect(Collectors.toList());
com.sparkmailr.model.BulkEmailRequest bulkRequest =
new com.sparkmailr.model.BulkEmailRequest.Builder()
.from(request.getFrom())
.templateId(request.getTemplateId())
.recipients(recipients)
.build();
BulkEmailResponse response = sparkMailrClient.emails().sendBulk(bulkRequest);
return ResponseEntity.ok(Map.of(
"success", true,
"batchId", response.getBatchId(),
"count", recipients.size()
));
}
}
// Send email with template and complex data
Map<String, Object> templateData = new HashMap<>();
templateData.put("user", Map.of(
"firstName", "John",
"lastName", "Doe",
"plan", "premium"
));
templateData.put("features", Arrays.asList("Feature 1", "Feature 2", "Feature 3"));
templateData.put("activationLink", "https://app.example.com/activate/123");
templateData.put("expirationDate", "2025-02-15");
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("user@example.com"))
.from("noreply@yourapp.com")
.templateId("welcome-template")
.templateData(templateData)
.build();
EmailResponse response = client.emails().send(request);
System.out.println("Template email sent: " + response.getId());
// Prepare bulk recipients
List<BulkEmailRecipient> recipients = Arrays.asList(
new BulkEmailRecipient.Builder()
.to("user1@example.com")
.templateData(Map.of("name", "User 1", "code", "ABC123"))
.build(),
new BulkEmailRecipient.Builder()
.to("user2@example.com")
.templateData(Map.of("name", "User 2", "code", "DEF456"))
.build(),
new BulkEmailRecipient.Builder()
.to("user3@example.com")
.templateData(Map.of("name", "User 3", "code", "GHI789"))
.build()
);
BulkEmailRequest bulkRequest = new BulkEmailRequest.Builder()
.from("newsletter@yourapp.com")
.templateId("newsletter-template")
.recipients(recipients)
.build();
BulkEmailResponse response = client.emails().sendBulk(bulkRequest);
System.out.println("Bulk batch: " + response.getBatchId());
// Get email status
String emailId = "em_1234567890";
EmailStatus status = client.emails().getStatus(emailId);
System.out.println("Status: " + status.getStatus());
System.out.println("Delivered: " + status.getDeliveredAt());
System.out.println("Opened: " + status.getOpenedAt());
// Get analytics
AnalyticsRequest analyticsRequest = new AnalyticsRequest.Builder()
.startDate(LocalDate.of(2025, 1, 1))
.endDate(LocalDate.of(2025, 1, 31))
.build();
EmailAnalytics analytics = client.analytics().getEmailStats(analyticsRequest);
System.out.println("Total sent: " + analytics.getSent());
System.out.println("Delivery rate: " + analytics.getDeliveryRate() + "%");
System.out.println("Open rate: " + analytics.getOpenRate() + "%");
System.out.println("Click rate: " + analytics.getClickRate() + "%");
import com.sparkmailr.exceptions.*;
try {
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("invalid-email"))
.from("noreply@yourapp.com")
.subject("Test Email")
.build();
EmailResponse response = client.emails().send(request);
} catch (AuthenticationException e) {
System.err.println("Authentication failed - check API key: " + e.getMessage());
} catch (RateLimitException e) {
System.err.println("Rate limited - retry after: " + e.getRetryAfter() + " seconds");
// Implement exponential backoff
try {
Thread.sleep(e.getRetryAfter() * 1000);
// Retry logic here
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
} catch (ValidationException e) {
System.err.println("Validation failed:");
e.getValidationErrors().forEach((field, errors) ->
System.err.println(" " + field + ": " + String.join(", ", errors))
);
} catch (NetworkException e) {
System.err.println("Network error: " + e.getMessage());
} catch (SparkMailrException e) {
System.err.println("SparkMailr error: " + e.getMessage());
} catch (Exception e) {
System.err.println("Unexpected error: " + e.getMessage());
}
// build.gradle (Module: app)
dependencies {
implementation 'com.sparkmailr:sparkmailr-java-sdk:1.0.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
// EmailService.java
public class EmailService {
private static final String TAG = "EmailService";
private SparkMailrClient client;
public EmailService(String apiKey) {
this.client = new SparkMailrClient.Builder()
.apiKey(apiKey)
.timeout(Duration.ofSeconds(30))
.build();
}
public void sendContactEmail(String name, String email, String message,
EmailCallback callback) {
// Use AsyncTask or modern alternatives like ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler mainHandler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
try {
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("support@yourapp.com"))
.from("noreply@yourapp.com")
.subject("Contact Form from " + name)
.html("<h2>Contact Form</h2>" +
"<p><strong>Name:</strong> " + name + "</p>" +
"<p><strong>Email:</strong> " + email + "</p>" +
"<p><strong>Message:</strong></p>" +
"<p>" + message + "</p>")
.build();
EmailResponse response = client.emails().send(request);
mainHandler.post(() -> callback.onSuccess(response.getId()));
} catch (SparkMailrException e) {
Log.e(TAG, "Failed to send email", e);
mainHandler.post(() -> callback.onError(e.getMessage()));
}
});
}
public interface EmailCallback {
void onSuccess(String emailId);
void onError(String error);
}
}
// Usage in Activity
public class ContactActivity extends AppCompatActivity {
private EmailService emailService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact);
emailService = new EmailService(getString(R.string.sparkmailr_api_key));
Button sendButton = findViewById(R.id.send_button);
sendButton.setOnClickListener(v -> sendContactForm());
}
private void sendContactForm() {
EditText nameField = findViewById(R.id.name_field);
EditText emailField = findViewById(R.id.email_field);
EditText messageField = findViewById(R.id.message_field);
emailService.sendContactEmail(
nameField.getText().toString(),
emailField.getText().toString(),
messageField.getText().toString(),
new EmailService.EmailCallback() {
@Override
public void onSuccess(String emailId) {
Toast.makeText(ContactActivity.this,
"Message sent successfully!", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(String error) {
Toast.makeText(ContactActivity.this,
"Failed to send message: " + error, Toast.LENGTH_LONG).show();
}
}
);
}
}
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
public class SparkMailrClientTest {
@Mock
private SparkMailrClient mockClient;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testSendEmailSuccess() throws SparkMailrException {
// Arrange
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("test@example.com"))
.from("noreply@test.com")
.subject("Test Email")
.html("<p>Test content</p>")
.build();
EmailResponse expectedResponse = new EmailResponse("em_123456", "queued");
when(mockClient.emails().send(request)).thenReturn(expectedResponse);
// Act
EmailResponse response = mockClient.emails().send(request);
// Assert
assertEquals("em_123456", response.getId());
assertEquals("queued", response.getStatus());
verify(mockClient.emails()).send(request);
}
@Test
void testRateLimitException() throws SparkMailrException {
// Arrange
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("test@example.com"))
.from("noreply@test.com")
.subject("Test")
.build();
RateLimitException exception = new RateLimitException("Rate limit exceeded", 60);
when(mockClient.emails().send(request)).thenThrow(exception);
// Act & Assert
RateLimitException thrown = assertThrows(RateLimitException.class, () -> {
mockClient.emails().send(request);
});
assertEquals(60, thrown.getRetryAfter());
assertEquals("Rate limit exceeded", thrown.getMessage());
}
}
// Integration test with WireMock
@ExtendWith(WireMockExtension.class)
public class SparkMailrIntegrationTest {
@Test
void testSendEmailIntegration(WireMockRuntimeInfo wmRuntimeInfo) {
// Setup WireMock
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/emails/send"))
.willReturn(WireMock.aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\":\"em_123456\",\"status\":\"queued\"}")));
// Create client with WireMock URL
SparkMailrClient client = new SparkMailrClient.Builder()
.apiKey("test-key")
.baseUrl(wmRuntimeInfo.getHttpBaseUrl())
.build();
// Send email
SendEmailRequest request = new SendEmailRequest.Builder()
.to(Arrays.asList("test@example.com"))
.from("noreply@test.com")
.subject("Integration Test")
.html("<p>Test content</p>")
.build();
EmailResponse response = client.emails().send(request);
// Verify
assertEquals("em_123456", response.getId());
assertEquals("queued", response.getStatus());
}
}