/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.weather.internal.provider;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Calendar;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.weather.internal.common.LocationConfig;
import org.openhab.binding.weather.internal.common.ProviderConfig;
import org.openhab.binding.weather.internal.common.WeatherConfig;
import org.openhab.binding.weather.internal.common.WeatherContext;
import org.openhab.binding.weather.internal.model.Forecast;
import org.openhab.binding.weather.internal.model.ProviderName;
import org.openhab.binding.weather.internal.model.Weather;
import org.openhab.binding.weather.internal.parser.WeatherParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Common base class for all weather providers. Retrieves, parses and returns
* weather data.
*
* @author Gerhard Riegler
* @since 1.6.0
*/
public abstract class AbstractWeatherProvider implements WeatherProvider {
private static final Logger logger = LoggerFactory.getLogger(AbstractWeatherProvider.class);
private static HttpClient httpClient = null;
private WeatherConfig config = WeatherContext.getInstance().getConfig();
private WeatherParser parser;
static {
httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
HttpClientParams params = httpClient.getParams();
params.setConnectionManagerTimeout(15000);
params.setSoTimeout(30000);
params.setContentCharset("UTF-8");
params.setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
params.setVersion(HttpVersion.HTTP_1_0);
}
public AbstractWeatherProvider(WeatherParser parser) {
this.parser = parser;
}
/**
* {@inheritDoc}
*/
@Override
public Weather getWeather(LocationConfig locationConfig) throws Exception {
Weather weather = new Weather(getProviderName());
executeRequest(weather, prepareUrl(getWeatherUrl(), locationConfig), locationConfig);
String forecastUrl = getForecastUrl();
if (forecastUrl != null && !weather.hasError()) {
executeRequest(weather, prepareUrl(forecastUrl, locationConfig), locationConfig);
}
if (logger.isDebugEnabled()) {
logger.debug("{}[{}]: {}", getProviderName(), locationConfig.getLocationId(), weather.toString());
for (Weather fc : weather.getForecast()) {
logger.debug("{}[{}]: {}", getProviderName(), locationConfig.getLocationId(), fc.toString());
}
}
return weather;
}
/**
* Prepares the provider URL by setting the config.
*/
private String prepareUrl(String url, LocationConfig locationConfig) {
ProviderConfig providerConfig = config.getProviderConfig(getProviderName());
if (providerConfig != null) {
url = StringUtils.replace(url, "[API_KEY]", providerConfig.getApiKey());
url = StringUtils.replace(url, "[API_KEY_2]", providerConfig.getApiKey2());
}
if (locationConfig.getLatitude() != null) {
url = StringUtils.replace(url, "[LATITUDE]", locationConfig.getLatitude().toString());
}
if (locationConfig.getLongitude() != null) {
url = StringUtils.replace(url, "[LONGITUDE]", locationConfig.getLongitude().toString());
}
if (locationConfig.getMeasurementUnits() != null) {
url = StringUtils.replace(url, "[UNITS]", locationConfig.getMeasurementUnits());
}
url = StringUtils.replace(url, "[LANGUAGE]", locationConfig.getLanguage());
url = StringUtils.replace(url, "[WOEID]", locationConfig.getWoeid());
return url;
}
/**
* Executes the http request and parses the returned stream.
*/
private void executeRequest(Weather weather, String url, LocationConfig locationConfig) throws Exception {
GetMethod get = null;
try {
logger.trace("{}[{}]: request : {}", getProviderName(), locationConfig.getLocationId(), url);
get = new GetMethod(url);
httpClient.executeMethod(get);
InputStream is = null;
if (logger.isTraceEnabled()) {
String response = get.getResponseBodyAsString(100000);
response = StringUtils.remove(response, "\n");
response = StringUtils.trim(response);
logger.trace("{}[{}]: response: {}", getProviderName(), locationConfig.getLocationId(), response);
is = new ByteArrayInputStream(response.getBytes(get.getResponseCharSet()));
} else {
is = get.getResponseBodyAsStream();
}
if (get.getStatusCode() == HttpStatus.SC_OK) {
parser.parseInto(is, weather);
}
// special handling because of bad OpenWeatherMap json structure
if (weather.getProvider() == ProviderName.OPENWEATHERMAP && weather.getResponseCode() != null
&& weather.getResponseCode() == 200) {
weather.setError(null);
}
if (!weather.hasError() && get.getStatusCode() != HttpStatus.SC_OK) {
weather.setError(get.getStatusLine().toString());
}
if (weather.hasError()) {
logger.error("{}[{}]: Can't retreive weather data: {}", getProviderName(),
locationConfig.getLocationId(), weather.getError());
} else {
setLastUpdate(weather);
}
} catch (Exception ex) {
logger.error(getProviderName() + ": " + ex.getMessage());
weather.setError(ex.getClass().getSimpleName() + ": " + ex.getMessage());
throw ex;
} finally {
if (get != null) {
get.releaseConnection();
}
}
}
/**
* Sets the current timestamp in every weather object.
*/
private void setLastUpdate(Weather weather) {
Calendar cal = Calendar.getInstance();
weather.getCondition().setLastUpdate(cal);
for (Forecast forecast : weather.getForecast()) {
forecast.getCondition().setLastUpdate(cal);
}
}
/**
* Returns the provider weather url.
*/
protected abstract String getWeatherUrl();
/**
* Returns the provider forecast url, some providers needs a second http
* request.
*/
protected String getForecastUrl() {
return null;
}
}