Skip to content

Commit 6978144

Browse files
Merge pull request #162 from mailjet/async-operations
Async operations
2 parents ae5981b + 7dc9d5b commit 6978144

6 files changed

Lines changed: 303 additions & 54 deletions

File tree

src/main/java/com/mailjet/client/MailjetClient.java

Lines changed: 157 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.io.IOException;
2020
import java.io.UnsupportedEncodingException;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.concurrent.CompletableFuture;
2123

2224
import com.mailjet.client.errors.MailjetClientCommunicationException;
2325
import com.mailjet.client.errors.MailjetUnauthorizedException;
@@ -80,48 +82,90 @@ public MailjetClient(ClientOptions clientOptions) {
8082
* performs GET request.
8183
* @param request request to be sent to Mailjet server
8284
* @return MailjetResponse with parameters of the response
83-
* @throws com.mailjet.client.errors.MailjetException in case of unsuccess response status code
85+
* @throws com.mailjet.client.errors.MailjetException in case of unsuccessfull response status code
8486
*/
8587
public MailjetResponse get(MailjetRequest request) throws MailjetException {
86-
try {
87-
88-
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
89-
.get()
90-
.build();
91-
92-
try (final Response okHttpResponse = _client.newCall(okHttpRequest).execute()) {
93-
return parseResponse(request, okHttpResponse);
94-
}
9588

89+
try (final Response okHttpResponse = getGetCall(request).execute()) {
90+
return parseResponse(request, okHttpResponse);
9691
} catch (IOException ex) {
9792
throw new MailjetClientCommunicationException("Connection Exception", ex);
9893
}
9994
}
10095

96+
/**
97+
* async version of {@link #get(MailjetRequest)} method
98+
* performs GET request.
99+
* @param request request to be sent to Mailjet server
100+
* @return CompletableFuture with MailjetResponse response, or exception thrown during the call/response parsing
101+
* CompletableFuture contains com.mailjet.client.errors.MailjetException in case of unsuccessfull call
102+
*/
103+
public CompletableFuture<MailjetResponse> getAsync(MailjetRequest request) {
104+
final CompletableFuture<MailjetResponse> resultFuture = new CompletableFuture<>();
105+
106+
try {
107+
getGetCall(request).enqueue(getAsyncCallback(request, resultFuture));
108+
} catch (MailjetException e) {
109+
resultFuture.completeExceptionally(e);
110+
}
111+
112+
return resultFuture;
113+
}
114+
115+
private Call getGetCall(MailjetRequest request) throws MailjetException {
116+
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
117+
.get()
118+
.build();
119+
120+
return _client.newCall(okHttpRequest);
121+
}
101122

102123
/**
103124
* performs POST request.
104125
* @param request request to be sent to Mailjet server
105126
* @return MailjetResponse with parameters of the response
106-
* @throws com.mailjet.client.errors.MailjetException in case of unsuccess response status code
127+
* @throws com.mailjet.client.errors.MailjetException in case of unsuccessfull response status code
107128
*/
108129
public MailjetResponse post(MailjetRequest request) throws MailjetException {
109130

131+
try (final Response okHttpResponse = getPostCall(request).execute()) {
132+
return parseResponse(request, okHttpResponse);
133+
} catch (IOException ex) {
134+
throw new MailjetClientCommunicationException("Connection Exception", ex);
135+
}
136+
}
137+
138+
/**
139+
* async version of {@link #post(MailjetRequest)} method
140+
* performs POST request.
141+
* @param request request to be sent to Mailjet server
142+
* @return CompletableFuture with MailjetResponse response, or exception thrown during the call/response parsing
143+
* CompletableFuture contains com.mailjet.client.errors.MailjetException in case of unsuccessfull call
144+
*/
145+
public CompletableFuture<MailjetResponse> postAsync(MailjetRequest request) {
146+
final CompletableFuture<MailjetResponse> resultFuture = new CompletableFuture<>();
147+
110148
try {
111-
final RequestBody requestBody = RequestBody.create(
112-
MediaType.parse(request.getContentType()), request.getBody().getBytes("UTF8"));
149+
getPostCall(request).enqueue(getAsyncCallback(request, resultFuture));
150+
} catch (MailjetException e) {
151+
resultFuture.completeExceptionally(e);
152+
}
113153

114-
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
115-
.post(requestBody)
116-
.build();
154+
return resultFuture;
155+
}
117156

118-
try (final Response okHttpResponse = _client.newCall(okHttpRequest).execute()) {
119-
return parseResponse(request, okHttpResponse);
120-
}
157+
private Call getPostCall(MailjetRequest request) throws MailjetException {
121158

122-
} catch (IOException ex) {
123-
throw new MailjetClientCommunicationException("Connection Exception", ex);
124-
}
159+
final byte[] bodyContent = request.getBody().getBytes(StandardCharsets.UTF_8);
160+
161+
final RequestBody requestBody = RequestBody.create(
162+
MediaType.parse(request.getContentType()), bodyContent);
163+
164+
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
165+
.post(requestBody)
166+
.build();
167+
168+
return _client.newCall(okHttpRequest);
125169
}
126170

127171
/**
@@ -131,49 +175,112 @@ public MailjetResponse post(MailjetRequest request) throws MailjetException {
131175
* @throws com.mailjet.client.errors.MailjetException in case of unsuccess response status code
132176
*/
133177
public MailjetResponse put(MailjetRequest request) throws MailjetException {
134-
try {
135-
final RequestBody requestBody = RequestBody.create(
136-
MediaType.parse(request.getContentType()), request.getBody().getBytes("UTF8"));
137-
138-
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
139-
.put(requestBody)
140-
.build();
141-
142-
try (final Response okHttpResponse = _client.newCall(okHttpRequest).execute()) {
143-
return parseResponse(request, okHttpResponse);
144-
}
145178

179+
try (final Response okHttpResponse = getPutCall(request).execute()) {
180+
return parseResponse(request, okHttpResponse);
146181
} catch (IOException ex) {
147182
throw new MailjetClientCommunicationException("Connection Exception", ex);
148183
}
149184
}
150185

186+
/**
187+
* async version of {@link #put(MailjetRequest)} method
188+
* performs PUT request.
189+
* CompletableFuture contains com.mailjet.client.errors.MailjetException in case of unsuccessfull call
190+
* @param request request to be sent to Mailjet server
191+
* @return CompletableFuture with MailjetResponse response, or exception thrown during the call/response parsing
192+
*/
193+
public CompletableFuture<MailjetResponse> putAsync(MailjetRequest request) {
194+
final CompletableFuture<MailjetResponse> resultFuture = new CompletableFuture<>();
195+
196+
try {
197+
getPutCall(request).enqueue(getAsyncCallback(request, resultFuture));
198+
} catch (MailjetException e) {
199+
resultFuture.completeExceptionally(e);
200+
}
201+
202+
return resultFuture;
203+
}
204+
205+
private Call getPutCall(MailjetRequest request) throws MailjetException {
206+
207+
final byte[] bodyContent = request.getBody().getBytes(StandardCharsets.UTF_8);
208+
209+
final RequestBody requestBody = RequestBody.create(
210+
MediaType.parse(request.getContentType()), bodyContent);
211+
212+
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
213+
.put(requestBody)
214+
.build();
215+
216+
return _client.newCall(okHttpRequest);
217+
}
218+
151219
/**
152220
* performs DELETE request.
153221
* @param request request to be sent to Mailjet server
154222
* @return MailjetResponse with parameters of the response
155223
* @throws com.mailjet.client.errors.MailjetException in case of unsuccess response status code
156224
*/
157225
public MailjetResponse delete(MailjetRequest request) throws MailjetException {
226+
227+
try (final Response okHttpResponse = getDeleteCall(request).execute()) {
228+
return parseResponse(request, okHttpResponse);
229+
} catch (IOException ex) {
230+
throw new MailjetClientCommunicationException("Connection Exception", ex);
231+
}
232+
}
233+
234+
/**
235+
* async version of {@link #delete(MailjetRequest)} method
236+
* performs DELETE request.
237+
* CompletableFuture contains com.mailjet.client.errors.MailjetException in case of unsuccessfull call
238+
* @param request request to be sent to Mailjet server
239+
* @return CompletableFuture with MailjetResponse with response
240+
*/
241+
public CompletableFuture<MailjetResponse> deleteAsync(MailjetRequest request) {
242+
243+
final CompletableFuture<MailjetResponse> resultFuture = new CompletableFuture<>();
244+
158245
try {
246+
getDeleteCall(request).enqueue(getAsyncCallback(request, resultFuture));
247+
} catch (MailjetException e) {
248+
resultFuture.completeExceptionally(e);
249+
}
159250

160-
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
161-
.delete()
162-
.build();
251+
return resultFuture;
252+
}
163253

164-
try (final Response okHttpResponse = _client.newCall(okHttpRequest).execute()) {
165-
return parseResponse(request, okHttpResponse);
254+
private Call getDeleteCall(MailjetRequest request) throws MailjetException {
255+
final Request okHttpRequest = getPreconfiguredRequestBuilder(request)
256+
.delete()
257+
.build();
258+
259+
return _client.newCall(okHttpRequest);
260+
}
261+
262+
private Callback getAsyncCallback(MailjetRequest request, CompletableFuture<MailjetResponse> completableFuture){
263+
return new Callback() {
264+
@Override
265+
public void onFailure(Call call, IOException e) {
266+
completableFuture.completeExceptionally(new MailjetClientCommunicationException("Connection Exception", e));
166267
}
167268

168-
} catch (IOException ex) {
169-
throw new MailjetClientCommunicationException("Connection Exception", ex);
170-
}
269+
@Override
270+
public void onResponse(Call call, Response response) throws IOException {
271+
try {
272+
completableFuture.complete(parseResponse(request, response));
273+
} catch (MailjetException e) {
274+
completableFuture.completeExceptionally(e);
275+
}
276+
}
277+
};
171278
}
172279

173280
private MailjetResponse parseResponse(MailjetRequest request, Response okHttpResponse) throws IOException, MailjetException {
174281

175282
final int responseStatusCode = okHttpResponse.code();
176-
final String responseBody = okHttpResponse.body().string();
283+
final String responseBody = okHttpResponse.body() != null ? okHttpResponse.body().string() : null;
177284

178285
MailjetResponseUtil.validateMailjetResponse(request, responseStatusCode, responseBody);
179286

@@ -189,14 +296,19 @@ private static OkHttpClient createDefaultOkHttpClient(){
189296
.build();
190297
}
191298

192-
private Request.Builder getPreconfiguredRequestBuilder(MailjetRequest request) throws MailjetUnauthorizedException, UnsupportedEncodingException {
299+
private Request.Builder getPreconfiguredRequestBuilder(MailjetRequest request) throws MailjetException {
193300

194-
final String url = request.buildUrl(this._options.getBaseUrl());
301+
final String url;
302+
try {
303+
url = request.buildUrl(this._options.getBaseUrl());
304+
} catch (UnsupportedEncodingException e) {
305+
throw new MailjetClientCommunicationException("Connection Exception", e);
306+
}
195307

196308
final Request.Builder builder = new Request
197309
.Builder()
198310
.addHeader("Accept", "application/json")
199-
.addHeader("User-Agent", this.userAgent)
311+
.addHeader("User-Agent", userAgent)
200312
.url(url);
201313

202314
switch (request.getAuthenticationType()){

src/main/java/com/mailjet/client/transactional/Attachment.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import lombok.Builder;
44
import lombok.Data;
55

6+
import java.io.ByteArrayOutputStream;
67
import java.io.File;
78
import java.io.IOException;
9+
import java.io.InputStream;
810
import java.nio.file.Files;
911
import java.nio.file.Path;
1012
import java.util.Base64;
@@ -55,4 +57,32 @@ public static Attachment fromFile(String pathToFile) throws IOException {
5557
.filename(path.getFileName().toString())
5658
.build();
5759
}
60+
61+
/**
62+
* Creates an attachment from the input stream
63+
* @param inputStream input stream that will be read as byte array
64+
* @param attachmentFileName the name that will be given to the attachment in the email
65+
* @param contentType mime type of the attachment https://www.iana.org/assignments/media-types/media-types.xhtml
66+
* @return constructed Attachment object
67+
* @throws IOException if something wrong with reading from input stream
68+
*/
69+
public static Attachment fromInputStream(InputStream inputStream, String attachmentFileName, String contentType) throws IOException {
70+
71+
// replace this with .readAllBytes when migrating to Java 9
72+
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
73+
int nRead;
74+
byte[] data = new byte[4000];
75+
76+
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
77+
buffer.write(data, 0, nRead);
78+
}
79+
80+
final String base64Content = Base64.getEncoder().encodeToString(buffer.toByteArray());
81+
82+
return new AttachmentBuilder()
83+
.base64Content(base64Content)
84+
.contentType(contentType)
85+
.filename(attachmentFileName)
86+
.build();
87+
}
5888
}

src/main/java/com/mailjet/client/transactional/SendEmailsRequest.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
import com.mailjet.client.resource.Emailv31;
1111
import com.mailjet.client.transactional.response.SendEmailsResponse;
1212
import lombok.Builder;
13-
import lombok.Builder.Default;
1413
import lombok.Singular;
1514

1615
import java.util.List;
16+
import java.util.concurrent.CompletableFuture;
1717

1818
@Builder
1919
public class SendEmailsRequest {
@@ -55,14 +55,40 @@ public class SendEmailsRequest {
5555
*/
5656
public SendEmailsResponse sendWith(MailjetClient mailjetClient) throws MailjetException {
5757

58-
MailjetRequest request = new MailjetRequest(Emailv31.resource);
59-
request.setBody(gson.toJson(this));
58+
final MailjetResponse response = mailjetClient.post(getMailjetRequest());
6059

61-
MailjetResponse response = mailjetClient.post(request);
60+
return convertResponse(response);
61+
}
6262

63-
String responseContent = response.getRawResponseContent();
63+
/**
64+
* Represents a method to send multiple transactional emails asynchronously
65+
* Note: Mailjet send API v3.1 will be used
66+
* Note: Max 50 emails per batch allowed
67+
* @param mailjetClient the Mailjet client that will be used to send messages
68+
* @return A response future with sent messages information, or error information
69+
* The returned future will contain MailjetException in case of communication error in HTTP stack,
70+
* like, TLS connection couldn't be established to the Mailjet server
71+
* Or the Server returned 5xx error,
72+
* Or the Server returned the generic error response
73+
*/
74+
public CompletableFuture<SendEmailsResponse> sendAsyncWith(MailjetClient mailjetClient) {
6475

65-
SendEmailsResponse typedResponse = gson.fromJson(responseContent, SendEmailsResponse.class);
76+
final CompletableFuture<SendEmailsResponse> responseCompletableFuture = new CompletableFuture<>();
77+
78+
try {
79+
CompletableFuture<MailjetResponse> future = mailjetClient.postAsync(getMailjetRequest());
80+
responseCompletableFuture.complete(convertResponse(future.get()));
81+
} catch (Exception e) {
82+
responseCompletableFuture.completeExceptionally(e);
83+
}
84+
85+
return responseCompletableFuture;
86+
}
87+
88+
private SendEmailsResponse convertResponse(MailjetResponse response) throws MailjetClientRequestException {
89+
final String responseContent = response.getRawResponseContent();
90+
91+
final SendEmailsResponse typedResponse = gson.fromJson(responseContent, SendEmailsResponse.class);
6692

6793
// in some cases, Mailjet server returns generic error w/o parsing the real passed messages
6894
if (typedResponse.getMessages() == null && response.getStatus() != MailjetResponseUtil.CREATED_STATUS){
@@ -71,4 +97,10 @@ public SendEmailsResponse sendWith(MailjetClient mailjetClient) throws MailjetEx
7197

7298
return typedResponse;
7399
}
100+
101+
private MailjetRequest getMailjetRequest() {
102+
final MailjetRequest request = new MailjetRequest(Emailv31.resource);
103+
request.setBody(gson.toJson(this));
104+
return request;
105+
}
74106
}

0 commit comments

Comments
 (0)