Java tutorial
/* * 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; } } }