Skip to content

Commit 5857c5f

Browse files
committed
Added handling of request timeouts
1 parent 5d6ef6d commit 5857c5f

File tree

1 file changed

+132
-11
lines changed

1 file changed

+132
-11
lines changed

src/main/java/de/bitzeche/video/transcoding/zencoder/ZencoderClient.java

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Map;
2222

2323
import javax.ws.rs.core.MediaType;
24+
import javax.xml.parsers.DocumentBuilder;
25+
import javax.xml.parsers.DocumentBuilderFactory;
2426
import javax.xml.parsers.ParserConfigurationException;
2527
import javax.xml.transform.OutputKeys;
2628
import javax.xml.transform.Transformer;
@@ -62,6 +64,9 @@ public class ZencoderClient implements IZencoderClient {
6264
private final String zencoderAPIKey;
6365
private final ZencoderAPIVersion zencoderAPIVersion;
6466
private XPath xPath;
67+
68+
private final int MAX_CONNECTION_ATTEMPTS = 5;
69+
private int currentConnectionAttempt = 0;
6570

6671
public ZencoderClient(String zencoderApiKey) {
6772
this(zencoderApiKey, ZencoderAPIVersion.API_V1);
@@ -77,6 +82,11 @@ public ZencoderClient(String zencoderApiKey, ZencoderAPIVersion apiVersion) {
7782

7883
httpClient = ApacheHttpClient.create();
7984
httpClient.setFollowRedirects(true);
85+
86+
// set a 20 second timeout on the client
87+
httpClient.setConnectTimeout(20000);
88+
httpClient.setReadTimeout(20000);
89+
8090
xPath = XPathFactory.newInstance().newXPath();
8191
zencoderAPIBaseUrl = zencoderAPIVersion.getBaseUrl();
8292
}
@@ -138,35 +148,77 @@ private void completeJobInfo(ZencoderJob job, Document response) {
138148
LOGGER.error("XPath threw Exception", e);
139149
}
140150
}
151+
152+
private void resetConnectionCount() {
153+
// reset to 0 for use in tracking connections next time
154+
currentConnectionAttempt = 0;
155+
}
156+
157+
private Document createDocumentForException(String message) {
158+
try {
159+
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
160+
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
161+
Document errorDocument = documentBuilder.newDocument();
162+
Element root = errorDocument.createElemet("error");
163+
errorDocument.appendChild(root);
164+
Node input = errorDocument.createElement("reason");
165+
input.setTextContent(message);
166+
root.appendChild(input);
167+
return errorDocument;
168+
} catch (ParserConfigurationException e) {
169+
LOGGER.error("Exception creating document for exception '" + message + "'", e);
170+
return null;
171+
}
172+
}
141173

142174
@Override
143175
public Document createJob(ZencoderJob job)
144176
throws ZencoderErrorResponseException {
177+
if(currentConnectionAttempt > MAX_CONNECTION_ATTEMPTS) {
178+
resetConnectionCount();
179+
String message = "Reached maximum number of connection attempts for Zencoder, aborting creation of job";
180+
Document errorDocument = createDocumentForException(message);
181+
throw new ZencoderErrorResponseException(errorDocument);
182+
}
145183
Document data;
146184
try {
147185
data = job.createXML();
148186
if (data == null) {
149-
LOGGER.error("Got no XML from Job");
187+
String message = "Got no XML from Job";
188+
LOGGER.error(message);
189+
resetConnectionCount();
190+
Document errorDocument = createDocumentForException(message);
191+
throw new ZencoderErrorResponseException(errorDocument);
150192
}
151193
Element apikey = data.createElement("api_key");
152194
apikey.setTextContent(zencoderAPIKey);
153195
data.getDocumentElement().appendChild(apikey);
154196
Document response = sendPostRequest(zencoderAPIBaseUrl
155197
+ "jobs?format=xml", data);
198+
// a null response means the call did not get through
199+
// we should try again, since the job has not been started
200+
if(response == null) {
201+
currentConnectionAttempt++;
202+
// maybe delay this call by a few seconds?
203+
return createJob(job);
204+
}
156205
String id = (String) xPath.evaluate("/api-response/job/id",
157206
response, XPathConstants.STRING);
158207
if (StringUtils.isNotEmpty(id)) {
159208
job.setJobId(Integer.parseInt(id));
209+
resetConnectionCount();
160210
return response;
161211
}
162212
completeJobInfo(job, response);
163213
LOGGER.error("Error when sending request to Zencoder: ", response);
214+
resetConnectionCount();
164215
throw new ZencoderErrorResponseException(response);
165216
} catch (ParserConfigurationException e) {
166217
LOGGER.error("Parser threw Exception", e);
167218
} catch (XPathExpressionException e) {
168219
LOGGER.error("XPath threw Exception", e);
169220
}
221+
resetConnectionCount();
170222
return null;
171223
}
172224

@@ -205,23 +257,47 @@ public Document getJobProgress(ZencoderJob job) {
205257
}
206258

207259
public Document getJobProgress(int id) {
260+
if(currentConnectionAttempt > MAX_CONNECTION_ATTEMPTS) {
261+
resetConnectionCount();
262+
LOGGER.error("Reached maximum number of attempts for getting job progress. Aborting and returning null");
263+
return null;
264+
}
208265
if (zencoderAPIVersion != ZencoderAPIVersion.API_V2) {
209266
LOGGER.warn("jobProgress is only available for API v2. Returning null.");
210267
return null;
211268
}
212269
String url = zencoderAPIBaseUrl + "jobs/" + id
213270
+ "/progress.xml?api_key=" + zencoderAPIKey;
214-
return sendGetRequest(url);
271+
Document result = sendGetRequest(url);
272+
if(result == null) {
273+
currentConnectionAttempt++;
274+
// delay this call by a few seconds?
275+
return getJobProgress(id);
276+
}
277+
resetConnectionCount();
278+
return result;
215279
}
216280

217281
public Document getJobDetails(ZencoderJob job) {
218282
return getJobDetails(job.getJobId());
219283
}
220284

221285
public Document getJobDetails(int id) {
286+
if(currentConnectionAttempt > MAX_CONNECTION_ATTEMPTS) {
287+
resetConnectionCount();
288+
LOGGER.error("Reached maximum number of attempts for getting job details. Aborting and returning null");
289+
return null;
290+
}
222291
String url = zencoderAPIBaseUrl + "jobs/" + id + ".xml?api_key="
223292
+ zencoderAPIKey;
224-
return sendGetRequest(url);
293+
Document result = sendGetRequest(url);
294+
if(result == null) {
295+
currentConnectionAttempt++;
296+
// delay this call by a few seconds?
297+
return getJobDetails(id);
298+
}
299+
resetConnectionCount();
300+
return result;
225301
}
226302

227303
public boolean resubmitJob(ZencoderJob job) {
@@ -233,10 +309,20 @@ public boolean resubmitJob(ZencoderJob job) {
233309
}
234310

235311
public boolean resubmitJob(int id) {
312+
if(currentConnectionAttempt > MAX_CONNECTION_ATTEMPTS) {
313+
resetConnectionCount();
314+
LOGGER.error("Reached maximum number of attempts to resubmit job, aborting");
315+
return false;
316+
}
236317
String url = zencoderAPIBaseUrl + "jobs/" + id + "/resubmit?api_key="
237318
+ zencoderAPIKey;
238319
ClientResponse response = sendPutRequest(url);
320+
if(response == null) {
321+
currentConnectionAtttempt++;
322+
return resubmitJob(id);
323+
}
239324
int responseStatus = response.getStatus();
325+
resetConnectionCount();
240326
if (responseStatus == 200 || responseStatus == 204) {
241327
return true;
242328
} else if (responseStatus == 409) {
@@ -255,10 +341,20 @@ public boolean cancelJob(ZencoderJob job) {
255341
}
256342

257343
public boolean cancelJob(int id) {
344+
if(currentConnectionAttempt > MAX_CONNECTION_ATTEMPTS) {
345+
resetConnectionCount();
346+
LOGGER.error("Reached maximum number of attempts to cancel job, aborting");
347+
return false;
348+
}
258349
String url = zencoderAPIBaseUrl + "jobs/" + id
259350
+ "/cancel.json?api_key=" + zencoderAPIKey;
260351
ClientResponse res = sendPutRequest(url);
352+
if(res == null) {
353+
currentConnectionAttempt++;
354+
return cancelJob(id);
355+
}
261356
int responseStatus = res.getStatus();
357+
resetConnectionCount();
262358
if (responseStatus == 200 || responseStatus == 204) {
263359
return true;
264360
} else if (responseStatus == 409) {
@@ -292,20 +388,38 @@ public boolean deleteJob(int id) {
292388

293389
protected Document sendGetRequest(String url) {
294390
LOGGER.debug("calling: {}", url);
295-
WebResource webResource = httpClient.resource(url);
296-
Document response = webResource.get(Document.class);
391+
try {
392+
WebResource webResource = httpClient.resource(url);
393+
Document response = webResource.get(Document.class);
297394

298-
logXmlDocumentToDebug("Got response", response);
299-
return response;
395+
logXmlDocumentToDebug("Got response", response);
396+
return response;
397+
} catch (Exception e) {
398+
if(e instanceof SocketTimeoutException) {
399+
LOGGER.warn("Connection to Zencoder timed out");
400+
} else {
401+
LOGGER.warn(e.getMessage());
402+
}
403+
}
404+
return null;
300405
}
301406

302407
protected ClientResponse sendPutRequest(String url) {
303408
LOGGER.debug("calling: {}", url);
304-
WebResource webResource = httpClient.resource(url);
305-
ClientResponse response = webResource.put(ClientResponse.class);
409+
try {
410+
WebResource webResource = httpClient.resource(url);
411+
ClientResponse response = webResource.put(ClientResponse.class);
306412

307-
LOGGER.debug("Got response: {}", response);
308-
return response;
413+
LOGGER.debug("Got response: {}", response);
414+
return response;
415+
} catch (Exception e) {
416+
if(e instanceof SocketTimeoutException) {
417+
LOGGER.warn("Connection to Zencoder timed out");
418+
} else {
419+
LOGGER.warn(e.getMessage());
420+
}
421+
}
422+
return null;
309423

310424
}
311425

@@ -331,7 +445,14 @@ protected Document sendPostRequest(String url, Document xml) {
331445
}
332446
LOGGER.error("couldn't submit job: {}", errormessage);
333447
return errorXml;
448+
} catch (Exception e) {
449+
if(e instanceof SocketTimeoutException) {
450+
LOGGER.warn("Connection to Zencoder timed out");
451+
} else {
452+
LOGGER.warn(e.getMessage());
453+
}
334454
}
455+
return null;
335456

336457
}
337458

0 commit comments

Comments
 (0)