org.jasig.portlet.weather.dao.worldwide.WorldWeatherOnlineDaoImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.portlet.weather.dao.worldwide.WorldWeatherOnlineDaoImpl.java

Source

/**
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a
 * copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.jasig.portlet.weather.dao.worldwide;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.IOUtils;
import org.jasig.portlet.weather.QuietUrlCodec;
import org.jasig.portlet.weather.TemperatureUnit;
import org.jasig.portlet.weather.dao.Constants;
import org.jasig.portlet.weather.dao.IWeatherDao;
import org.jasig.portlet.weather.dao.worldwide.xml.LocationResult;
import org.jasig.portlet.weather.dao.worldwide.xml.SearchData;
import org.jasig.portlet.weather.dao.worldwide.xml.WeatherData;
import org.jasig.portlet.weather.dao.worldwide.xml.WeatherForecast;
import org.jasig.portlet.weather.domain.Current;
import org.jasig.portlet.weather.domain.Forecast;
import org.jasig.portlet.weather.domain.Location;
import org.jasig.portlet.weather.domain.Weather;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataRetrievalFailureException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/* ****
Note that requests to World Weather Online's web services go to free.worldweatheronline.com,
but links to their pages go to their www.worldweatheronline.com site.
**** */

public class WorldWeatherOnlineDaoImpl implements IWeatherDao, DisposableBean, InitializingBean {

    private static final String DATE_PATTERN = "yyyy-MM-dd";
    private static final String DAY_PATTERN = "EEE";

    private static final String FIND_URL = "http://free.worldweatheronline.com/feed/search.ashx?key=@KEY@&query=@QUERY@&num_of_results=3&format=xml";
    private static final String WEATHER_URL = "http://free.worldweatheronline.com/feed/weather.ashx?key=@KEY@&num_of_days=3&format=xml&q=@LOCATION@";
    private String key = null;

    private Properties imageMapping;

    //Multi-threaded connection manager for exclusive access
    private final MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();

    //Define the HttpClient here and pass it so we only define one instance
    private final HttpClient httpClient = new HttpClient(connectionManager);

    //Default timeout of 5 seconds
    private int connectionTimeout = 5000;

    //Default timeout of 5 seconds
    private int readTimeout = 5000;

    //Default retry of 5 times
    private int timesToRetry = 5;

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public void setTimesToRetry(int timesToRetry) {
        this.timesToRetry = timesToRetry;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public void setImageMapping(Resource imageMapping) {
        this.imageMapping = new Properties();
        InputStream is = null;
        try {
            is = imageMapping.getInputStream();
            this.imageMapping.load(is);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load image mapping", e);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /* (non-Javadoc)
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        final HttpConnectionManager httpConnectionManager = httpClient.getHttpConnectionManager();
        final HttpConnectionManagerParams params = httpConnectionManager.getParams();
        params.setConnectionTimeout(connectionTimeout);
        params.setSoTimeout(readTimeout);

        params.setParameter(HttpMethodParams.RETRY_HANDLER, new HttpMethodRetryHandler() {
            public boolean retryMethod(final HttpMethod method, final IOException exception, int executionCount) {
                if (executionCount >= timesToRetry) {
                    // Do not retry if over max retry count
                    return false;
                }
                if (exception instanceof NoHttpResponseException) {
                    // Retry if the server dropped connection on us
                    return true;
                }
                if (exception instanceof SocketException) {
                    // Retry if the server reset connection on us
                    return true;
                }
                if (exception instanceof SocketTimeoutException) {
                    // Retry if the read timed out
                    return true;
                }
                if (!method.isRequestSent()) {
                    // Retry if the request has not been sent fully or
                    // if it's OK to retry methods that have been sent
                    return true;
                }
                // otherwise do not retry
                return false;
            }
        });
    }

    /* (non-Javadoc)
     * @see org.springframework.beans.factory.DisposableBean#destroy()
     */
    public void destroy() throws Exception {
        this.connectionManager.shutdown();
    }

    /*
     * (non-Javadoc)
     * @see org.jasig.portlet.weather.dao.IWeatherDao#find(java.lang.String)
     */
    public Collection<Location> find(String location) {
        final String url = FIND_URL.replace("@KEY@", key).replace("@QUERY@",
                QuietUrlCodec.encode(location, Constants.URL_ENCODING));

        HttpMethod getMethod = new GetMethod(url);
        InputStream inputStream = null;
        try {
            // Execute the method.
            int statusCode = httpClient.executeMethod(getMethod);
            if (statusCode != HttpStatus.SC_OK) {
                final String statusText = getMethod.getStatusText();
                throw new DataRetrievalFailureException(
                        "get of '" + url + "' failed with status '" + statusCode + "' due to '" + statusText + "'");
            }

            // Read the response body
            inputStream = getMethod.getResponseBodyAsStream();

            List<Location> locations = deserializeSearchResults(inputStream);

            return locations;

        } catch (HttpException e) {
            throw new RuntimeException(
                    "http protocol exception while getting data from weather service from: " + url, e);
        } catch (IOException e) {
            throw new RuntimeException("IO exception while getting data from weather service from: " + url, e);
        } catch (JAXBException e) {
            throw new RuntimeException("Parsing exception while getting data from weather service from: " + url, e);
        } finally {
            //try to close the inputstream
            IOUtils.closeQuietly(inputStream);
            //release the connection
            getMethod.releaseConnection();
        }
    }

    /* (non-Javadoc)
     * @see org.jasig.portlet.weather.dao.IWeatherDao#getWeather(java.lang.String, org.jasig.portlet.weather.TemperatureUnit)
     */
    public Weather getWeather(String locationCode, TemperatureUnit unit) {
        final String worldwideweatherUrl = WEATHER_URL.replace("@KEY@", this.key).replace("@LOCATION@",
                QuietUrlCodec.encode(locationCode, Constants.URL_ENCODING));

        Weather weather = (Weather) this.getAndDeserialize(worldwideweatherUrl, unit);
        String[] locationParts = locationCode.split(", ");
        Location location = new Location();
        location.setCity(locationParts[0]);
        if (locationParts.length > 1) {
            location.setStateOrCountry(locationParts[1]);
        }
        weather.setLocation(location);
        weather.setMoreInformationLink("http://www.worldweatheronline.com/weather.aspx?q="
                + QuietUrlCodec.encode(locationCode, Constants.URL_ENCODING));

        return weather;
    }

    public String getWeatherProviderName() {
        return "World Weather Online";
    }

    public String getWeatherProviderLink() {
        return "http://www.worldweatheronline.com/";
    }

    protected Object getAndDeserialize(String url, TemperatureUnit unit) {
        HttpMethod getMethod = new GetMethod(url);
        InputStream inputStream = null;
        try {
            // Execute the method.
            int statusCode = httpClient.executeMethod(getMethod);
            if (statusCode != HttpStatus.SC_OK) {
                final String statusText = getMethod.getStatusText();
                throw new DataRetrievalFailureException(
                        "get of '" + url + "' failed with status '" + statusCode + "' due to '" + statusText + "'");
            }

            // Read the response body
            inputStream = getMethod.getResponseBodyAsStream();

            Weather weather = deserializeWeatherResult(inputStream, unit);

            return weather;

        } catch (HttpException e) {
            throw new RuntimeException(
                    "http protocol exception while getting data from weather service from: " + url, e);
        } catch (IOException e) {
            throw new RuntimeException("IO exception while getting data from weather service from: " + url, e);
        } catch (JAXBException e) {
            throw new RuntimeException("Parsing exception while getting data from weather service from: " + url, e);
        } catch (ParseException e) {
            throw new RuntimeException("Parsing exception while getting data from weather service from: " + url, e);
        } finally {
            //try to close the inputstream
            IOUtils.closeQuietly(inputStream);
            //release the connection
            getMethod.releaseConnection();
        }
    }

    protected Weather deserializeWeatherResult(InputStream inputStream, TemperatureUnit unit)
            throws JAXBException, ParseException {
        JAXBContext jaxbContext = JAXBContext.newInstance(WeatherData.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        WeatherData data = (WeatherData) unmarshaller.unmarshal(inputStream);

        Weather weather = new Weather();

        Current current = new Current();
        current.setCondition(data.getCondition().getDescription());
        current.setHumidity(data.getCondition().getHumidity());
        current.setPressure(data.getCondition().getPressure());
        current.setWindDirection(data.getCondition().getWindDir());

        if (data.getCondition().getWeatherIconUrl() != null
                && data.getCondition().getWeatherIconUrl().contains("night")) {
            current.setImgName(imageMapping.getProperty("image.night." + data.getCondition().getWeatherCode()));
        } else {
            current.setImgName(imageMapping.getProperty("image.day." + data.getCondition().getWeatherCode()));
        }

        switch (unit) {
        case C:
            current.setTemperature(data.getCondition().getTempC());
            current.setWindSpeed(data.getCondition().getSpeedKmph());
            weather.setTemperatureUnit("C");
            weather.setWindUnit("kmph");
            weather.setPressureUnit("mb");
            break;
        default:
            current.setTemperature(data.getCondition().getTempF());
            current.setWindSpeed(data.getCondition().getSpeedMiles());
            weather.setTemperatureUnit("F");
            weather.setWindUnit("mi");
            weather.setPressureUnit("mb");
        }
        weather.setCurrentWeather(current);

        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN);
        SimpleDateFormat dayFormat = new SimpleDateFormat(DAY_PATTERN);

        List<Forecast> forecasts = new ArrayList<Forecast>();
        for (WeatherForecast f : data.getForecasts()) {
            Forecast forecast = new Forecast();
            forecast.setCondition(f.getDescription());

            if (f.getIconUrl() != null && f.getIconUrl().contains("night")) {
                forecast.setImgName(imageMapping.getProperty("image.night." + f.getWeatherCode()));
            } else {
                forecast.setImgName(imageMapping.getProperty("image.day." + f.getWeatherCode()));
            }

            Date date = dateFormat.parse(f.getDate());
            forecast.setDay(dayFormat.format(date));

            switch (unit) {
            case C:
                forecast.setHighTemperature(f.getTempMaxC());
                forecast.setLowTemperature(f.getTempMinC());
                break;
            default:
                forecast.setHighTemperature(f.getTempMaxF());
                forecast.setLowTemperature(f.getTempMinF());
            }
            forecasts.add(forecast);
        }
        weather.setForecast(forecasts);

        return weather;
    }

    protected List<Location> deserializeSearchResults(InputStream inputStream) throws JAXBException {

        JAXBContext jaxbContext = JAXBContext.newInstance(SearchData.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        SearchData data = (SearchData) unmarshaller.unmarshal(inputStream);

        List<Location> locations = new ArrayList<Location>();

        for (LocationResult l : data.getResults()) {
            Location location = new Location();
            location.setCity(l.getCity());
            location.setLatitude(l.getLatitude());
            location.setLongitude(l.getLongitude());
            location.setStateOrCountry(l.getRegion() != null ? l.getRegion() : l.getCountry());

            StringBuffer code = new StringBuffer();
            code.append(l.getCity());
            if (l.getRegion() != null && !l.getRegion().contains(", ")) {
                code.append(", ").append(l.getRegion());
            }
            if (l.getCountry() != null) {
                code.append(", ").append(l.getCountry());
            }
            location.setLocationCode(code.toString());
            locations.add(location);
        }

        return locations;
    }

}