com.sun.syndication.feed.weather.WeatherGateway.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.syndication.feed.weather.WeatherGateway.java

Source

/*
 * Copyright 2004 Sun Microsystems, Inc.
 *
 * Licensed 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 com.sun.syndication.feed.weather;

import com.sun.syndication.io.*;
import com.sun.syndication.io.impl.*;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.logging.*;
import org.jdom.*;
import org.jdom.input.*;

import java.io.*;
import java.util.*;

/**
 * Gateway to the weather.com XML Data Feed.  Performs its own caching,
 * in compliance with the mandatory refresh rates in the weather.com
 * implementation guide.
 * <p>
 * Gateway, an object that encapsulates access to an external system or
 * resource [Fowler, P of EAA].
 * <p>
 * @author Enrique Rodriguez
 */
public class WeatherGateway {
    protected static Log LOG = LogFactory.getLog(WeatherGateway.class);
    private static final String GET_CURRENT_CONDITIONS = "cc";
    private static final String GET_FORECAST = "dayf";

    private static final String PRODUCT_CODE = "prod";
    private static final String XOAP_CODE = "xoap";
    private static final String PARTNER_ID = "par";
    private static final String LICENSE_KEY = "key";
    private static final String UNIT = "unit"; // s or m

    // Mandatory refresh rates per the Weather.com Implementation Guide
    private static final long LOCATION_TTL = 1000 * 60 * 60 * 12;
    private static final long CURRENT_CONDITIONS_TTL = 1000 * 60 * 30;
    private static final long FORECAST_TTL = 1000 * 60 * 60 * 2;

    private String weatherServer;
    private String partnerID;
    private String licenseKey;

    private NameValuePair userid;
    private NameValuePair password;
    private NameValuePair prod;

    private boolean defaultMetricSystem = false;

    private Map weatherCache = new HashMap();

    public WeatherGateway(WeatherConfigurationI config) {
        licenseKey = config.getLicenseKey();
        partnerID = config.getPartnerID();
        weatherServer = config.getWeatherServer();
        defaultMetricSystem = config.isMetric();

        userid = new NameValuePair(PARTNER_ID, partnerID);
        password = new NameValuePair(LICENSE_KEY, licenseKey);
        prod = new NameValuePair(PRODUCT_CODE, XOAP_CODE);
    }

    public WeatherChannel getLocation(String locationID) throws IOException {
        WeatherChannel channel = doQuery(locationID, LOCATION_TTL, new NameValuePair[] { userid, password, prod });

        return channel;
    }

    public WeatherChannel getCurrentConditions(String locationID) throws IOException {
        return getCurrentConditions(locationID, defaultMetricSystem);
    }

    public WeatherChannel getCurrentConditions(String locationID, boolean metricSystem) throws IOException {
        NameValuePair currentConditions = new NameValuePair(GET_CURRENT_CONDITIONS, "*");
        NameValuePair useMetricSystem = new NameValuePair(UNIT, getUnitString(metricSystem));

        WeatherChannel channel = doQuery(locationID, CURRENT_CONDITIONS_TTL,
                new NameValuePair[] { userid, password, prod, currentConditions, useMetricSystem });

        return channel;
    }

    public WeatherChannel getForecast(String locationID, int forecastLength) throws IOException {
        return getForecast(locationID, forecastLength, defaultMetricSystem);
    }

    public WeatherChannel getForecast(String locationID, int forecastLength, boolean metricSystem)
            throws IOException {
        NameValuePair forecast = new NameValuePair(GET_FORECAST, String.valueOf(forecastLength));
        NameValuePair useMetricSystem = new NameValuePair(UNIT, getUnitString(metricSystem));

        WeatherChannel channel = doQuery(locationID, FORECAST_TTL,
                new NameValuePair[] { userid, password, prod, forecast, useMetricSystem });

        return channel;
    }

    public void setDefaultMetricSystem(boolean defaultMetricSystem) {
        this.defaultMetricSystem = defaultMetricSystem;
    }

    protected WeatherChannel doQuery(String locationID, long allowedAge, NameValuePair[] query) throws IOException {
        HttpClient client = new HttpClient();

        HttpMethod method = new GetMethod(weatherServer + locationID);
        method.setQueryString(query);

        URI uri = method.getURI();
        LOG.debug("Performing query with URI:  " + uri);

        WeatherChannel channel = new WeatherChannel();

        if (weatherCache.containsKey(uri)) {
            WeatherCacheEntry entry = (WeatherCacheEntry) weatherCache.get(uri);

            long lastUpdated = entry.getLastUpdated().getTimeInMillis();
            LOG.debug("Cache entry last updated:  " + lastUpdated);
            long now = new GregorianCalendar().getTimeInMillis();
            LOG.debug("Cache entry current time:  " + now);
            long diffMillis = now - lastUpdated;
            LOG.debug("Cache entry time difference is:  " + diffMillis);

            if (diffMillis < allowedAge) {
                channel = entry.getChannel();
                LOG.debug("Returning channel from cache.");
                return channel;
            }
        }

        int statusCode = -1;

        for (int attempt = 0; statusCode == -1 && attempt < 3; attempt++) {
            try {
                statusCode = client.executeMethod(method);
            } catch (HttpRecoverableException e) {
                LOG.warn("Retrying after recoverable exception:  " + e.getMessage());
            } catch (IOException e) {
                LOG.error("Failed to download file:  " + e.getMessage());
            }
        }

        if (statusCode == -1) {
            LOG.error("Failed to recover from exception.");
            throw new IOException();
        }

        channel = buildWeather(method);
        WeatherCacheEntry entry = new WeatherCacheEntry(channel);
        weatherCache.put(uri, entry);
        LOG.debug("Returning channel from network.");
        return channel;
    }

    protected WeatherChannel buildWeather(HttpMethod method) {
        WeatherChannel weather = null;
        try {
            // If Weather20Parser were registered with Rome, we could call WireFeedInput instead.
            SAXBuilder saxBuilder = new SAXBuilder();
            Document document = saxBuilder.build(method.getResponseBodyAsStream());
            method.releaseConnection();
            WireFeedParser parser = new Weather20Parser();
            weather = (WeatherChannel) parser.parse(document, false);
        } catch (Exception e) {
            LOG.error("Failed to parse XML document for weather:  " + e.getMessage());
        }
        return weather;
    }

    private String getUnitString(boolean metricSystem) {
        String unit = "s";
        if (metricSystem)
            unit = "m";
        else
            unit = "s";
        return unit;
    }

    private class WeatherCacheEntry implements Serializable {
        private Calendar lastUpdated;
        private WeatherChannel channel;

        public WeatherCacheEntry(WeatherChannel channel) {
            Calendar now = new GregorianCalendar();
            this.lastUpdated = now;
            this.channel = channel;
        }

        public WeatherChannel getChannel() {
            return channel;
        }

        public Calendar getLastUpdated() {
            return lastUpdated;
        }
    }
}