Repository: camel Updated Branches: refs/heads/master 31e2df5e7 -> 426922a26
CAMEL-9273: More options to weather. Thanks to Arno Noordover for the patch. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/426922a2 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/426922a2 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/426922a2 Branch: refs/heads/master Commit: 426922a2607420ba0175da27145d520e80120291 Parents: 31e2df5 Author: Claus Ibsen <davscl...@apache.org> Authored: Fri May 6 12:05:04 2016 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Fri May 6 12:05:04 2016 +0200 ---------------------------------------------------------------------- components/camel-weather/readme.MD | 27 ++++ .../component/weather/WeatherConfiguration.java | 98 ++++++++------- .../component/weather/WeatherLanguage.java | 46 +++++++ .../camel/component/weather/WeatherQuery.java | 125 +++++++++++++++++++ .../weather/CurrentWeatherConsumerTest.java | 11 +- .../src/test/resources/log4j.properties | 2 +- 6 files changed, 262 insertions(+), 47 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/426922a2/components/camel-weather/readme.MD ---------------------------------------------------------------------- diff --git a/components/camel-weather/readme.MD b/components/camel-weather/readme.MD new file mode 100644 index 0000000..0c680f6 --- /dev/null +++ b/components/camel-weather/readme.MD @@ -0,0 +1,27 @@ +#Description of Weather API + +##Types of queries + +There are five type of queries: + +1. current weather (contexts weather, group, find and box/city); +2. forecast (5 days per 3 hours, context forecast) +3. forecast/daily (context forecast/daily); you must provide cnt parameter +4. history (context history/city and maybe also history/station) +5. station (contexts station, box/station and station/find) + +##Locations + +1. by name of the city and the country code (q={city name},{country code}) +2. by id of the city (id={city ID}); The current weather can also be +asked for a group of id's using the context group +3. by latitude and longtitude (lat={lat}&lon={lon}) +4. by box (bbox=12,32,15,37,10), you must also provide cluster=yes/no; context +must be box/city or box/station +5. find cities/station around location defined by lat/lon; context +must be find or station/find. You must provide a cnt parameter. + + +##Note +The history by station isn't documented at the OpenWeather website but +is mentioned in the issue \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/426922a2/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherConfiguration.java b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherConfiguration.java index 4dc2dba..f4da1b9 100644 --- a/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherConfiguration.java +++ b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherConfiguration.java @@ -16,19 +16,16 @@ */ package org.apache.camel.component.weather; -import java.net.URL; import java.util.Scanner; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriParam; import org.apache.camel.spi.UriParams; import org.apache.camel.spi.UriPath; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.map.ObjectMapper; +import static org.apache.camel.component.weather.WeatherLanguage.en; import static org.apache.camel.component.weather.WeatherMode.JSON; import static org.apache.camel.component.weather.WeatherUnits.METRIC; -import static org.apache.camel.util.ObjectHelper.isEmpty; import static org.apache.camel.util.ObjectHelper.notNull; @UriParams @@ -47,11 +44,19 @@ public class WeatherConfiguration { @UriParam private String lon; @UriParam + private String rightLon; + @UriParam + private String topLat; + @UriParam + private Integer zoom; + @UriParam private String period = ""; @UriParam(defaultValue = "JSON") private WeatherMode mode = JSON; @UriParam(defaultValue = "METRIC") private WeatherUnits units = METRIC; + @UriParam(defaultValue = "en") + private WeatherLanguage language = en; @UriParam private String headerName; @@ -142,6 +147,7 @@ public class WeatherConfiguration { /** * Latitude of location. You can use lat and lon options instead of location. + * For boxed queries this is the bottom latitude. */ public void setLat(String lat) { this.lat = lat; @@ -153,6 +159,7 @@ public class WeatherConfiguration { /** * Longitude of location. You can use lat and lon options instead of location. + * For boxed queries this is the left longtitude. */ public void setLon(String lon) { this.lon = lon; @@ -169,57 +176,58 @@ public class WeatherConfiguration { return appid; } + String getQuery() throws Exception { + return new WeatherQuery(this.component, this).getQuery(); + } - public String getQuery() throws Exception { - return getQuery(getLocation()); + String getQuery(String location) throws Exception { + return new WeatherQuery(this.component, this).getQuery(location); } - public String getQuery(String location) throws Exception { - String answer = "http://api.openweathermap.org/data/2.5/"; + public WeatherLanguage getLanguage() { + return language; + } - if (lat != null && lon != null) { - location = "lat=" + lat + "&lon=" + lon; - } else if (isEmpty(location) || "current".equals(location)) { - location = getCurrentGeoLocation(); - } else { - // assuming the location is a town or country - location = "q=" + location; - } - - if (isEmpty(getPeriod())) { - answer += "weather?" + location; - } else { - answer += "forecast/daily?" + location + "&cnt=" + getPeriod(); - } + /** + * Language of the response. + */ + public void setLanguage(WeatherLanguage language) { + this.language = language; + } - // append the desired measurement unit if not the default (which is metric) - if (getUnits() != METRIC) { - answer += "&units=" + getUnits().name().toLowerCase(); - } + public String getRightLon() { + return rightLon; + } - // append the desired output mode if not the default (which is json) - if (getMode() != JSON) { - answer += "&mode=" + getMode().name().toLowerCase(); - } + /** + * For boxed queries this is the right longtitude. Needs to be used + * in combination with topLat and zoom. + */ + public void setRightLon(String rightLon) { + this.rightLon = rightLon; + } - if (getAppid() != null) { - answer += "&APPID=" + getAppid(); - } - - return answer; + public String getTopLat() { + return topLat; } - private String getCurrentGeoLocation() throws Exception { - String geoLocation = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, new URL("http://freegeoip.io/json/")); - if (isEmpty(geoLocation)) { - throw new IllegalStateException("Got the unexpected value '" + geoLocation + "' for the geolocation"); - } + /** + * For boxed queries this is the top latitude. Needs to be used + * in combination with rightLon and zoom. + */ + public void setTopLat(String topLat) { + this.topLat = topLat; + } - ObjectMapper mapper = new ObjectMapper(); - JsonNode node = mapper.readValue(geoLocation, JsonNode.class); - JsonNode latitudeNode = notNull(node.get("latitude"), "latitude"); - JsonNode longitudeNode = notNull(node.get("longitude"), "longitude"); + public Integer getZoom() { + return zoom; + } - return "lat=" + latitudeNode + "&lon=" + longitudeNode; + /** + * For boxed queries this is the zoom. Needs to be used + * in combination with rightLon and topLat. + */ + public void setZoom(Integer zoom) { + this.zoom = zoom; } } http://git-wip-us.apache.org/repos/asf/camel/blob/426922a2/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherLanguage.java ---------------------------------------------------------------------- diff --git a/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherLanguage.java b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherLanguage.java new file mode 100644 index 0000000..ec4f35f --- /dev/null +++ b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherLanguage.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.apache.camel.component.weather; + +/** + * All available languages for the weather API. + */ +public enum WeatherLanguage { + en, + ru, + it, + es, + sp, + uk, + ua, + de, + pt, + ro, + pl, + fi, + nl, + fr, + bg, + sv, + se, + zh_tw, + zh, + zh_cn, + tr, + hr, + ca; +} http://git-wip-us.apache.org/repos/asf/camel/blob/426922a2/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherQuery.java ---------------------------------------------------------------------- diff --git a/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherQuery.java b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherQuery.java new file mode 100644 index 0000000..bf6edb4 --- /dev/null +++ b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherQuery.java @@ -0,0 +1,125 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.apache.camel.component.weather; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.GetMethod; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; + +import static org.apache.camel.component.weather.WeatherMode.JSON; +import static org.apache.camel.component.weather.WeatherUnits.METRIC; +import static org.apache.camel.util.ObjectHelper.isEmpty; +import static org.apache.camel.util.ObjectHelper.notNull; + +/** + * Logic for determining the query based on the provided + * configuration. + */ +public class WeatherQuery { + private final WeatherConfiguration weatherConfiguration; + private final WeatherComponent component; + + public WeatherQuery(WeatherComponent component, WeatherConfiguration weatherConfiguration) { + this.component = component; + this.weatherConfiguration = weatherConfiguration; + } + + public String getQuery() throws Exception { + return getQuery(weatherConfiguration.getLocation()); + } + + public String getQuery(String location) throws Exception { + String answer = "http://api.openweathermap.org/data/2.5/"; + + if (weatherConfiguration.getLat() != null && weatherConfiguration.getLon() != null + && weatherConfiguration.getRightLon() == null && weatherConfiguration.getTopLat() == null) { + location = "lat=" + weatherConfiguration.getLat() + "&lon=" + weatherConfiguration.getLon(); + } else if (weatherConfiguration.getLat() != null && weatherConfiguration.getLon() != null + && weatherConfiguration.getRightLon() != null && weatherConfiguration.getTopLat() != null) { + location = "bbox=" + weatherConfiguration.getLon() + "," + + weatherConfiguration.getLat() + "," + + weatherConfiguration.getRightLon() + "," + + weatherConfiguration.getTopLat() + "," + + weatherConfiguration.getZoom() + "&cluster=yes"; + } else if (isEmpty(location) || "current".equals(location)) { + location = getCurrentGeoLocation(); + } else { + // assuming the location is a town or country + location = "q=" + location; + } + + location = location + "&lang=" + weatherConfiguration.getLanguage(); + + if (weatherConfiguration.getTopLat() != null && weatherConfiguration.getRightLon() != null) { + answer += "box?" + location; + } else if (isEmpty(weatherConfiguration.getPeriod())) { + answer += "weather?" + location; + } else { + answer += "forecast/daily?" + location + "&cnt=" + weatherConfiguration.getPeriod(); + } + + // append the desired measurement unit if not the default (which is metric) + if (weatherConfiguration.getUnits() != METRIC) { + answer += "&units=" + weatherConfiguration.getUnits().name().toLowerCase(); + } + + // append the desired output mode if not the default (which is json) + if (weatherConfiguration.getMode() != JSON) { + answer += "&mode=" + weatherConfiguration.getMode().name().toLowerCase(); + } + + if (weatherConfiguration.getAppid() != null) { + answer += "&APPID=" + weatherConfiguration.getAppid(); + } + + return answer; + + } + + /** + * TODO: shouldn't this method be moved to a class of its own perhaps with an interface + * that gets injected. For testing purposes you can inject your own version when testing this + * class. + */ + + String getCurrentGeoLocation() throws Exception { + HttpClient httpClient = new HttpClient(); + GetMethod getMethod = new GetMethod("http://freegeoip.io/json/"); + try { + int statusCode = httpClient.executeMethod(getMethod); + if (statusCode != HttpStatus.SC_OK) { + throw new IllegalStateException("Got the unexpected http-status '" + getMethod.getStatusLine() + "' for the geolocation"); + } + String geoLocation = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, getMethod.getResponseBodyAsStream()); + if (isEmpty(geoLocation)) { + throw new IllegalStateException("Got the unexpected value '" + geoLocation + "' for the geolocation"); + } + + ObjectMapper mapper = new ObjectMapper(); + JsonNode node = mapper.readValue(geoLocation, JsonNode.class); + JsonNode latitudeNode = notNull(node.get("latitude"), "latitude"); + JsonNode longitudeNode = notNull(node.get("longitude"), "longitude"); + + return "lat=" + latitudeNode + "&lon=" + longitudeNode; + } finally { + getMethod.releaseConnection(); + } + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/426922a2/components/camel-weather/src/test/java/org/apache/camel/component/weather/CurrentWeatherConsumerTest.java ---------------------------------------------------------------------- diff --git a/components/camel-weather/src/test/java/org/apache/camel/component/weather/CurrentWeatherConsumerTest.java b/components/camel-weather/src/test/java/org/apache/camel/component/weather/CurrentWeatherConsumerTest.java index eceed0b..b1436ec 100644 --- a/components/camel-weather/src/test/java/org/apache/camel/component/weather/CurrentWeatherConsumerTest.java +++ b/components/camel-weather/src/test/java/org/apache/camel/component/weather/CurrentWeatherConsumerTest.java @@ -21,11 +21,20 @@ import org.apache.camel.builder.RouteBuilder; public class CurrentWeatherConsumerTest extends BaseWeatherConsumerTest { @Override + protected void checkWeatherContent(String weather) { + log.debug("The weather in {} format is {}{}", new Object[] {WeatherMode.XML, LS, weather}); + + //assertStringContains(weather, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"); + assertStringContains(weather, "<coord"); + assertStringContains(weather, "<temperature"); + } + + @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { - from("weather:foo?appid=9162755b2efa555823cfe0451d7fff38").to("mock:result"); + from("weather:foo?appid=9162755b2efa555823cfe0451d7fff38&lon=4&lat=52&rightLon=6&topLat=54").to("mock:result"); } }; } http://git-wip-us.apache.org/repos/asf/camel/blob/426922a2/components/camel-weather/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/components/camel-weather/src/test/resources/log4j.properties b/components/camel-weather/src/test/resources/log4j.properties index 33efa4c..fb0280d 100644 --- a/components/camel-weather/src/test/resources/log4j.properties +++ b/components/camel-weather/src/test/resources/log4j.properties @@ -18,7 +18,7 @@ # # The logging properties used for testing. # -log4j.rootLogger=INFO, file +log4j.rootLogger=DEBUG, file #log4j.logger.org.apache.camel=DEBUG #log4j.logger.org.apache.camel.component.weather=DEBUG