package controllers;
import org.apache.cassandra.cli.CliParser.newColumnFamily_return;
import models.Data;
import models.User;
import play.Logger;
import play.cache.Cache;
import play.libs.F.Promise;
import play.libs.WS;
import play.mvc.Before;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.With;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
/**
* Weather controller manages the personal page of the user displaying
* geolocalized weather data.
*
* @author Valerio Di Gregorio
*/
@With(Secure.class)
public class Weather extends Controller {
/**
* The WWO API key.
*/
private static final String WWO_API_KEY = "";
/**
* The WWO local weather REST service URL.
*/
private static final String WWO_LOCAL_WEATHER_URL = "http://api.worldweatheronline.com/free/v1/weather.ashx";
/**
* The TTL for the weather data provided by WWO. Refresh forecasts after 24h.
*/
private static final long TTL_DATA = 86400;
/**
* Minimum period between consecutive requests.
*/
private static final long MIN_PERIOD = 1000;
/**
* Render the user name in its personal page.
*/
@Before
static void setConnectedUser() {
Logger.debug("[Weather.setConnectedUser]");
if (Security.isConnected()) {
try {
User user = new User(Security.connected());
renderArgs.put("user", user.fullname);
} catch (ConnectionException e) {
error(500, "Error retrieving user information.");
}
}
}
/**
* Render the user personal page.
*/
public static void index() {
Logger.debug("[Weather.index]");
render();
}
/**
* Render the user personal page.
*
* @param ipaddress
* An IP address to simulate a different location. Client IP
* address will be used if this parameter is not present.
* @param callback
* A callback specified by the client.
*/
public static void data(String ipaddress, String callback) {
Logger.debug("[Weather.data] ipaddress = %s; callback = %s", ipaddress, callback);
if (validation.hasErrors())
error(400, "Invalid parameter.");
String sessionId = session.getId();
// Apply rate limits for consecutive requests
long now = System.currentTimeMillis();
Long lastRequest = (Long) Cache.get(sessionId + "-pending");
Cache.set(sessionId + "-pending", now);
if(lastRequest != null && (now - lastRequest < MIN_PERIOD)) {
Logger.warn("[Weather.data] Session %s request discarded (now: %d, last: %d).", sessionId, now, lastRequest);
error(429, "Wait " + MIN_PERIOD + " milliseconds.");
}
// Check for the IP address stored in the session. If not present store
// the selected IP. If no IP was selected it will use the real one. If
// an IP was stored it will be used unless the user selected a new IP.
// It's easier to read the code than the comment...
String ip = (String) Cache.get(sessionId + "-ipaddress");
if(ipaddress == null || ipaddress.length() == 0)
ipaddress = (ip != null ? ip : Http.Request.current().remoteAddress);
ip = ipaddress;
Cache.set(sessionId + "-ipaddress", ipaddress);
Logger.debug("[Weather.data] ipaddress = %s", ipaddress);
Data data = null;
try {
data = Data.get(ipaddress);
Logger.debug("[Weather.data] data = %s", data);
} catch (ConnectionException e) {
Logger.warn("[Weather.data] Error retrieving data object from DB.");
}
if(data == null) {
String url = WWO_LOCAL_WEATHER_URL + "?q=" + ipaddress
+ "&format=json"
+ "&num_of_days=5"
+ "&cc=no"
+ "&includeLocation=yes"
+ "&key=" + WWO_API_KEY;
Logger.debug("[Weather.data] url = %s", url);
Promise<WS.HttpResponse> promise = WS.url(url).getAsync();
WS.HttpResponse response = await(promise);
if (response.getStatus() / 100 != 2) {
Logger.error("[Weather.data] %d - %s",
response.getStatus(), response.getString());
error(response.getStatus(), "Unable to retrieve data.");
}
data = new Data(ipaddress, response.getString());
try {
data.store(TTL_DATA);
} catch (ConnectionException e) {
Logger.warn("[Weather.data] Error storing data object into the DB.");
}
}
// This is for jsonp requests... I can't find the way to manage them
// with play versions earlier than 2.0.3, so I assume it is not
// supported. Here is a hand-made solution.
String json = data.weather;
if(callback != null && callback.length() > 0)
json = callback + "(" + json + ")";
renderJSON(json);
}
}