This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 64f10b7c556d CAMEL-26131: camel-weather - the geolocation of a query
is not kept on the configuration, so the CamelWeatherLocation header is queried
(#28767)
64f10b7c556d is described below
commit 64f10b7c556d51c274d380bd65720264389b8377
Author: Vignesh Nayak Manel <[email protected]>
AuthorDate: Sun Nov 1 19:03:06 2026 +0530
CAMEL-26131: camel-weather - the geolocation of a query is not kept on the
configuration, so the CamelWeatherLocation header is queried (#28767)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../camel/component/weather/WeatherProducer.java | 2 +-
.../camel/component/weather/WeatherQuery.java | 34 +++---
.../weather/WeatherProducerLocationHeaderTest.java | 123 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 8 ++
4 files changed, 149 insertions(+), 18 deletions(-)
diff --git
a/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherProducer.java
b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherProducer.java
index fb18d562aeef..8c6d7ce9293f 100644
---
a/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherProducer.java
+++
b/components/camel-weather/src/main/java/org/apache/camel/component/weather/WeatherProducer.java
@@ -61,7 +61,7 @@ public class WeatherProducer extends DefaultProducer {
if (HttpStatus.SC_OK != response.getCode()) {
throw new IllegalStateException(
"Got the invalid http status value '" +
response.getCode()
- + "' as the result
of the query '" + query + "'");
+ + "' as the result
of the query '" + uri + "'");
}
String weather =
EntityUtils.toString(response.getEntity(), "UTF-8");
LOG.debug("Got back the Weather information {}",
weather);
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
index 4550af8c6776..d467f79da55f 100644
---
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
@@ -37,13 +37,17 @@ public class WeatherQuery {
public String getQuery(String location) throws Exception {
String answer = "http://api.openweathermap.org/data/2.5/";
- if (weatherConfiguration.getLat() != null &&
weatherConfiguration.getLon() != null
+ // the lat and lon of the current geolocation are for this query only:
the configuration is shared by all the
+ // queries of the endpoint, and a later query can be for another
location
+ String lat = weatherConfiguration.getLat();
+ String lon = weatherConfiguration.getLon();
+ if (lat != null && lon != null
&& weatherConfiguration.getRightLon() == null &&
weatherConfiguration.getTopLat() == null) {
- location = createLatLonQueryString();
- } else if (weatherConfiguration.getLat() != null &&
weatherConfiguration.getLon() != null
+ location = createLatLonQueryString(lat, lon);
+ } else if (lat != null && lon != null
&& weatherConfiguration.getRightLon() != null &&
weatherConfiguration.getTopLat() != null) {
- location = "bbox=" + weatherConfiguration.getLon() + ","
- + weatherConfiguration.getLat() + ","
+ location = "bbox=" + lon + ","
+ + lat + ","
+ weatherConfiguration.getRightLon() + ","
+ weatherConfiguration.getTopLat() + ","
+ weatherConfiguration.getZoom() + "&cluster=yes";
@@ -53,9 +57,9 @@ public class WeatherQuery {
location = "id=" + weatherConfiguration.getIds();
} else if (isEmpty(location) || "current".equals(location)) {
GeoLocation geoLocation = getCurrentGeoLocation();
- weatherConfiguration.setLat(geoLocation.getLatitude());
- weatherConfiguration.setLon(geoLocation.getLongitude());
- location = createLatLonQueryString();
+ lat = geoLocation.getLatitude();
+ lon = geoLocation.getLongitude();
+ location = createLatLonQueryString(lat, lon);
} else {
// assuming the location is a town or country
location = "q=" + location;
@@ -63,7 +67,7 @@ public class WeatherQuery {
location = location + "&lang=" + weatherConfiguration.getLanguage();
- String context = createContext();
+ String context = createContext(lat, lon);
answer += context + location;
if (!isEmpty(weatherConfiguration.getPeriod())) {
@@ -89,7 +93,7 @@ public class WeatherQuery {
return answer;
}
- private String createContext() {
+ private String createContext(String lat, String lon) {
String answer;
if (isBoxedQuery()) {
if (weatherConfiguration.getWeatherApi() == WeatherApi.Station) {
@@ -97,7 +101,7 @@ public class WeatherQuery {
} else {
answer = "box/city?";
}
- } else if (isGeoLocation() && weatherConfiguration.getCnt() != null) {
+ } else if (lat != null && lon != null && weatherConfiguration.getCnt()
!= null) {
if (weatherConfiguration.getWeatherApi() == WeatherApi.Station) {
answer = "station/find?";
} else {
@@ -131,12 +135,8 @@ public class WeatherQuery {
return answer;
}
- private boolean isGeoLocation() {
- return weatherConfiguration.getLat() != null &&
weatherConfiguration.getLon() != null;
- }
-
- private String createLatLonQueryString() {
- return "lat=" + weatherConfiguration.getLat() + "&lon=" +
weatherConfiguration.getLon();
+ private static String createLatLonQueryString(String lat, String lon) {
+ return "lat=" + lat + "&lon=" + lon;
}
private boolean isBoxedQuery() {
diff --git
a/components/camel-weather/src/test/java/org/apache/camel/component/weather/WeatherProducerLocationHeaderTest.java
b/components/camel-weather/src/test/java/org/apache/camel/component/weather/WeatherProducerLocationHeaderTest.java
new file mode 100644
index 000000000000..bc1a1d2c6d1e
--- /dev/null
+++
b/components/camel-weather/src/test/java/org/apache/camel/component/weather/WeatherProducerLocationHeaderTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.component.weather.geolocation.GeoLocation;
+import org.apache.camel.component.weather.geolocation.GeoLocationProvider;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * The geolocation of a query for the current location is not kept on the
shared configuration of the endpoint, so a
+ * later CamelWeatherLocation header is the location that is queried.
+ */
+class WeatherProducerLocationHeaderTest extends CamelTestSupport {
+
+ private static final String CURRENT = "lat=2&lon=1";
+
+ private final List<String> requested = new ArrayList<>();
+ private int status = HttpStatus.SC_OK;
+
+ @BindToRegistry("geo")
+ private final GeoLocationProvider geoLocationProvider = () -> new
GeoLocation("1", "2");
+
+ @BindToRegistry("http")
+ private final CloseableHttpClient httpClient = createHttpClient();
+
+ @Test
+ void headerLocationIsQueriedOnAnEndpointWithoutLocation() {
+ String uri =
"weather:foo?appid=x&geoLocationProvider=#geo&httpClient=#http";
+
+ template.requestBodyAndHeader(uri, null,
WeatherConstants.WEATHER_LOCATION, "Paris");
+ template.requestBody(uri, (Object) null);
+
+ assertEquals(List.of("q=Paris", CURRENT), requestedLocations());
+ assertNoLatLonOnTheConfiguration(uri);
+ }
+
+ @Test
+ void currentLocationIsNotUsedByTheNextQuery() {
+ String uri =
"weather:foo?appid=x&location=Madrid&geoLocationProvider=#geo&httpClient=#http";
+
+ template.requestBodyAndHeader(uri, null,
WeatherConstants.WEATHER_LOCATION, "current");
+ template.requestBodyAndHeader(uri, null,
WeatherConstants.WEATHER_LOCATION, "Paris");
+ template.requestBody(uri, (Object) null);
+
+ assertEquals(List.of(CURRENT, "q=Paris", "q=Madrid"),
requestedLocations());
+ assertNoLatLonOnTheConfiguration(uri);
+ }
+
+ @Test
+ void errorNamesTheUriThatWasCalled() {
+ String uri =
"weather:foo?appid=x&location=Madrid&geoLocationProvider=#geo&httpClient=#http";
+ status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
+
+ CamelExecutionException e = assertThrows(CamelExecutionException.class,
+ () -> template.requestBodyAndHeader(uri, null,
WeatherConstants.WEATHER_LOCATION, "Paris"));
+
+ IllegalStateException cause =
assertInstanceOf(IllegalStateException.class, e.getCause());
+ assertTrue(cause.getMessage().contains("?q=Paris&"),
cause.getMessage());
+ }
+
+ private List<String> requestedLocations() {
+ // the location part of each requested URI, such as q=Paris or
lat=2&lon=1
+ return requested.stream().map(u -> u.substring(u.indexOf('?') + 1,
u.indexOf("&lang="))).toList();
+ }
+
+ private void assertNoLatLonOnTheConfiguration(String uri) {
+ WeatherConfiguration configuration = context.getEndpoint(uri,
WeatherEndpoint.class).getConfiguration();
+ assertNull(configuration.getLat());
+ assertNull(configuration.getLon());
+ }
+
+ private CloseableHttpClient createHttpClient() {
+ try {
+ CloseableHttpClient client = mock(CloseableHttpClient.class);
+ when(client.execute(any(HttpGet.class),
any(HttpClientResponseHandler.class))).thenAnswer(invocation -> {
+ HttpGet request = invocation.getArgument(0);
+ requested.add(request.getRequestUri());
+ BasicClassicHttpResponse response = new
BasicClassicHttpResponse(status);
+ response.setEntity(new StringEntity("{}"));
+ HttpClientResponseHandler<?> handler =
invocation.getArgument(1);
+ return handler.handleResponse(response);
+ });
+ return client;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 674fffc04fb1..55384f7e25f2 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -587,6 +587,14 @@ If you replaced these dependencies to use another HTTP
client implementation, na
`software.amazon.awssdk.http.service.impl` (synchronous clients) or
`software.amazon.awssdk.http.async.service.impl`
(asynchronous clients): Camel then leaves the choice of the HTTP client to the
AWS SDK.
+=== camel-weather - the CamelWeatherLocation header is the location that is
queried
+
+The latitude and longitude of the current geolocation (a location of
`current`, or no location) are no longer written
+onto the configuration of the endpoint. Before, they were, so every later
query on that endpoint, including one with a
+`CamelWeatherLocation` header such as `Paris`, requested the weather of the
current geolocation. An endpoint without a
+`location` already did the geolocation when its producer was created, so its
`CamelWeatherLocation` header was never
+used. The `lat` and `lon` options of an endpoint are still used instead of the
location.
+
=== camel-gson, camel-jsonb - the unmarshal type header needs
allowUnmarshallType
The Gson and JSON-B data formats now use the `CamelGsonUnmarshalType` and
`CamelJsonbUnmarshallType` headers to choose