/*
* Copyright (C) 2014 Stichting Akvo (Akvo Foundation)
*
* This file is part of Akvo FLOW.
*
* Akvo FLOW is free software: you can redistribute it and modify it under the terms of
* the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
* either version 3 of the License or any later version.
*
* Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License included below for more details.
*
* The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
*/
package org.waterforpeople.mapping.app.web;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.waterforpeople.mapping.app.web.dto.ImageCheckRequest;
import com.gallatinsystems.common.Constants;
import com.gallatinsystems.common.util.PropertyUtil;
import com.gallatinsystems.device.dao.DeviceFileJobQueueDAO;
import com.gallatinsystems.device.domain.DeviceFileJobQueue;
import com.gallatinsystems.framework.rest.AbstractRestApiServlet;
import com.gallatinsystems.framework.rest.RestRequest;
import com.gallatinsystems.framework.rest.RestResponse;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
public class ImageCheckServlet extends AbstractRestApiServlet {
private static final Logger log = Logger.getLogger(ImageCheckServlet.class
.getName());
private static final long serialVersionUID = 9187987692591327059L;
private static final long MAX_ATTEMPTS = 3;
private static final long DELAY = 1000 * 60 * 5; // 5min
@Override
protected RestRequest convertRequest() throws Exception {
HttpServletRequest req = getRequest();
RestRequest restRequest = new ImageCheckRequest();
restRequest.populateFromHttpRequest(req);
return restRequest;
}
@Override
protected RestResponse handleRequest(RestRequest req) throws Exception {
final ImageCheckRequest checkReq = (ImageCheckRequest) req;
if (checkReq.getFileName() == null || checkReq.getFileName().equals("")) {
log.log(Level.SEVERE, "No filename was provided, aborting check");
return new RestResponse();
}
if (checkReq.getAttempt() == null || checkReq.getAttempt().equals("")) {
log.log(Level.SEVERE,
"No attempt number was specified, aborting check");
return new RestResponse();
}
// NOTE: baseUrl contains a trailing slash
final String baseUrl = PropertyUtil.getProperty("photo_url_root");
final String imageUrl = baseUrl + checkReq.getFileName();
// MalformedURLException exception caught by method signature
final URL url = new URL(imageUrl);
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(Constants.CONNECTION_TIMEOUT);
conn.setRequestMethod("HEAD");
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
if (checkReq.getAttempt() == MAX_ATTEMPTS) {
log.log(Level.INFO, "Adding file as missing: " + checkReq);
DeviceFileJobQueueDAO jobDao = new DeviceFileJobQueueDAO();
DeviceFileJobQueue df = new DeviceFileJobQueue();
df.setFileName(checkReq.getFileName());
df.setDeviceId(checkReq.getDeviceId());
df.setQasId(checkReq.getQasId());
jobDao.save(df);
} else {
rescheduleTask(checkReq, true);
}
}
} catch (SocketTimeoutException timeout) {
// reschedule the task without delay
// Possible a hiccup in GAE side
rescheduleTask(checkReq, false);
} catch (IOException e) {
// IOException possible a http 403, reschedule the task
rescheduleTask(checkReq, true);
}
return new RestResponse();
}
@Override
protected void writeOkResponse(RestResponse resp) throws Exception {
getResponse().setStatus(200);
}
/**
* Reschedules the CheckImage task with a possible delay<br>
* delay = 3 min * attempt number
*/
private void rescheduleTask(ImageCheckRequest req, boolean delay) {
int attempt = delay ? req.getAttempt() + 1 : req.getAttempt();
log.log(Level.INFO, "Rescheduling image check: " + req);
Queue queue = QueueFactory.getQueue("background-processing");
TaskOptions to = TaskOptions.Builder
.withUrl("/app_worker/imagecheck")
.param(ImageCheckRequest.FILENAME_PARAM, req.getFileName())
.param(ImageCheckRequest.DEVICE_ID_PARAM,
String.valueOf(req.getDeviceId()))
.param(ImageCheckRequest.QAS_ID_PARAM,
String.valueOf(req.getQasId()))
.param(ImageCheckRequest.ATTEMPT_PARAM, String.valueOf(attempt))
.countdownMillis(delay ? DELAY * req.getAttempt() : 0);
queue.add(to);
}
}