1 package com.guinetik.examples;
2
3 import com.guinetik.rr.RocketRest;
4 import com.guinetik.rr.RocketRestConfig;
5 import com.guinetik.rr.RocketRestOptions;
6 import com.guinetik.rr.result.ApiError;
7 import com.guinetik.rr.result.Result;
8
9 import java.io.UnsupportedEncodingException;
10 import java.net.URLEncoder;
11 import java.nio.charset.StandardCharsets;
12 import java.util.Arrays;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Scanner;
17 import java.util.concurrent.CompletableFuture;
18 import java.util.concurrent.CountDownLatch;
19 import java.util.stream.Collectors;
20 import java.util.function.Function;
21 import com.fasterxml.jackson.annotation.JsonProperty;
22
23
24
25
26 public class WeatherExample implements Example {
27
28 private static final String WTTR_API_URL = "https://wttr.in";
29 private static final Scanner scanner = new Scanner(System.in);
30 private RocketRest client;
31
32 @Override
33 public String getName() {
34 return "Weather API (using async)";
35 }
36
37 @Override
38 public void run() {
39 System.out.println("==============================");
40 System.out.println("Weather API Example (async)");
41 System.out.println("==============================");
42 System.out.println("This example demonstrates using RocketRest's async API");
43 System.out.println("to fetch weather data from wttr.in service.\n");
44
45 try {
46
47 initializeClient();
48
49
50 mainMenu();
51
52 } catch (Exception e) {
53 System.out.println("❌ Error: " + e.getMessage());
54 e.printStackTrace();
55 } finally {
56 if (client != null) {
57 client.shutdown();
58 }
59 }
60 }
61
62
63
64
65 private void initializeClient() {
66 RocketRestConfig config = RocketRestConfig.builder(WTTR_API_URL)
67 .defaultOptions(options -> {
68 options.set(RocketRestOptions.LOGGING_ENABLED, true);
69 options.set(RocketRestOptions.TIMING_ENABLED, true);
70 options.set(RocketRestOptions.LOG_REQUEST_BODY, true);
71 options.set(RocketRestOptions.LOG_RESPONSE_BODY, true);
72 })
73 .build();
74
75 client = new RocketRest(WTTR_API_URL, config);
76 System.out.println("RocketRest client initialized for wttr.in API\n");
77 }
78
79
80
81
82 private void mainMenu() {
83 boolean exit = false;
84
85 while (!exit) {
86 System.out.println("\n--- Weather API Menu ---");
87 System.out.println("1. Get Current Weather by Location");
88 System.out.println("2. Get Weather Forecast by Location");
89 System.out.println("3. Compare Weather of Multiple Locations");
90 System.out.println("4. Exit");
91 System.out.print("\nEnter your choice (1-4): ");
92
93 String choice = scanner.nextLine().trim();
94
95 switch (choice) {
96 case "1":
97 getCurrentWeather();
98 break;
99 case "2":
100 getWeatherForecast();
101 break;
102 case "3":
103 compareMultipleLocations();
104 break;
105 case "4":
106 exit = true;
107 System.out.println("Exiting Weather API Example. Goodbye!");
108 break;
109 default:
110 System.out.println("Invalid choice. Please try again.");
111 }
112 }
113 }
114
115
116
117
118 private String encodeLocation(String location) {
119 try {
120 return URLEncoder.encode(location, StandardCharsets.UTF_8.toString())
121 .replace("+", "%20");
122 } catch (UnsupportedEncodingException e) {
123 System.out.println("Warning: Error encoding location. Using raw value.");
124 return location;
125 }
126 }
127
128
129
130
131 private void getCurrentWeather() {
132 System.out.println("\n=== Current Weather ===");
133 System.out.print("Enter location (city name or coordinates): ");
134 String location = scanner.nextLine().trim();
135
136 if (location.isEmpty()) {
137 System.out.println("Location cannot be empty. Returning to menu.");
138 return;
139 }
140
141
142 String encodedLocation = encodeLocation(location);
143
144 System.out.println("\nFetching current weather for " + location + "...");
145
146
147 Map<String, String> params = new HashMap<>();
148 params.put("format", "j1");
149
150
151 Function<WeatherResponse, CompletableFuture<WeatherResponse>> enrichWeatherData =
152 response -> {
153
154
155
156
157 System.out.println("Processing weather data...");
158
159
160 return CompletableFuture.completedFuture(response);
161 };
162
163
164 client.async()
165 .get("/" + encodedLocation, WeatherResponse.class, params)
166
167 .thenCompose(enrichWeatherData)
168
169 .thenAccept(response -> {
170 System.out.println("Weather data received successfully!");
171 displayCurrentWeather(response);
172 })
173
174 .exceptionally(ex -> {
175 System.out.println("❌ Error fetching weather: " + ex.getMessage());
176 return null;
177 })
178
179 .join();
180 }
181
182
183
184
185 private void getWeatherForecast() {
186 System.out.println("\n=== Weather Forecast ===");
187 System.out.print("Enter location (city name or coordinates): ");
188 String location = scanner.nextLine().trim();
189
190 if (location.isEmpty()) {
191 System.out.println("Location cannot be empty. Returning to menu.");
192 return;
193 }
194
195
196 String encodedLocation = encodeLocation(location);
197
198 System.out.println("\nFetching weather forecast for " + location + "...");
199
200
201 Map<String, String> params = new HashMap<>();
202 params.put("format", "j1");
203
204
205 client.async()
206 .get("/" + encodedLocation, WeatherResponse.class, params)
207
208 .thenApply(response -> {
209
210 return response;
211 })
212
213 .thenAccept(this::displayForecast)
214
215 .exceptionally(ex -> {
216 System.out.println("❌ Error fetching forecast: " + ex.getMessage());
217 return null;
218 })
219
220 .join();
221 }
222
223
224
225
226 private void compareMultipleLocations() {
227 System.out.println("\n=== Compare Multiple Locations ===");
228 System.out.println("Enter locations (separated by commas):");
229 String input = scanner.nextLine().trim();
230
231 if (input.isEmpty()) {
232 System.out.println("No locations provided. Returning to menu.");
233 return;
234 }
235
236
237 List<String> locations = Arrays.stream(input.split(","))
238 .map(String::trim)
239 .filter(s -> !s.isEmpty())
240 .collect(Collectors.toList());
241
242 System.out.println("\nFetching weather data for " + locations.size() + " locations...");
243
244
245 Map<String, String> params = new HashMap<>();
246 params.put("format", "j1");
247
248
249 List<CompletableFuture<Pair<String, WeatherResponse>>> futures = locations.stream()
250 .map(location -> {
251
252 String encodedLocation = encodeLocation(location);
253
254
255 return client.async()
256 .get("/" + encodedLocation, WeatherResponse.class, params)
257 .thenApply(response -> new Pair<>(location, response))
258 .exceptionally(ex -> {
259 System.out.println("❌ Error for " + location + ": " + ex.getMessage());
260 return new Pair<>(location, null);
261 });
262 })
263 .collect(Collectors.toList());
264
265
266 CompletableFuture<List<Pair<String, WeatherResponse>>> allFutures =
267 CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
268 .thenApply(v -> futures.stream()
269 .map(CompletableFuture::join)
270 .collect(Collectors.toList()));
271
272
273 allFutures.thenAccept(results -> {
274 System.out.println("\n=== Weather Comparison ===");
275
276 results.forEach(pair -> {
277 String location = pair.getFirst();
278 WeatherResponse response = pair.getSecond();
279
280 System.out.println("\nLocation: " + location);
281 if (response != null) {
282 displayWeatherSummary(response);
283 } else {
284 System.out.println("❌ No data available");
285 }
286 });
287 }).join();
288 }
289
290
291
292
293 private void displayCurrentWeather(WeatherResponse response) {
294 if (response == null || response.getCurrent_condition() == null || response.getCurrent_condition().isEmpty()) {
295 System.out.println("No weather data available.");
296 return;
297 }
298
299 CurrentCondition current = response.getCurrent_condition().get(0);
300 NearestArea area = response.getNearest_area() != null && !response.getNearest_area().isEmpty()
301 ? response.getNearest_area().get(0) : null;
302
303
304 System.out.println("\nDEBUG - Current condition raw data: " + current);
305
306 System.out.println("\n=== Current Weather ===");
307
308
309 if (area != null) {
310 String areaName = area.getAreaName() != null && !area.getAreaName().isEmpty()
311 ? area.getAreaName().get(0).getValue() : "Unknown";
312 String country = area.getCountry() != null && !area.getCountry().isEmpty()
313 ? area.getCountry().get(0).getValue() : "";
314 String region = area.getRegion() != null && !area.getRegion().isEmpty()
315 ? area.getRegion().get(0).getValue() : "";
316
317 System.out.println("Location: " + areaName +
318 (region.isEmpty() ? "" : ", " + region) +
319 (country.isEmpty() ? "" : ", " + country));
320 }
321
322
323 System.out.println("Observation Time: " + current.getLocalObsDateTime());
324
325
326 String weatherDesc = current.getWeatherDesc() != null && !current.getWeatherDesc().isEmpty()
327 ? current.getWeatherDesc().get(0).getValue() : "Unknown";
328 System.out.println("Conditions: " + weatherDesc);
329
330
331 System.out.println("Temperature: " + current.getTemp_C() + "°C / " + current.getTemp_F() + "°F");
332 System.out.println("Feels Like: " + current.getFeelsLikeC() + "°C / " + current.getFeelsLikeF() + "°F");
333
334
335 System.out.println("Humidity: " + current.getHumidity() + "%");
336 System.out.println("Wind: " + current.getWindspeedKmph() + " km/h (" + current.getWinddir16Point() + ")");
337 System.out.println("Pressure: " + current.getPressure() + " hPa");
338 System.out.println("Cloud Cover: " + current.getCloudcover() + "%");
339 System.out.println("Visibility: " + current.getVisibility() + " km");
340 System.out.println("UV Index: " + current.getUvIndex());
341 }
342
343
344
345
346 private void displayForecast(WeatherResponse response) {
347 if (response == null || response.getWeather() == null || response.getWeather().isEmpty()) {
348 System.out.println("No forecast data available.");
349 return;
350 }
351
352 Weather today = response.getWeather().get(0);
353 NearestArea area = response.getNearest_area() != null && !response.getNearest_area().isEmpty()
354 ? response.getNearest_area().get(0) : null;
355
356 System.out.println("\n=== Weather Forecast ===");
357
358
359 if (area != null) {
360 String areaName = area.getAreaName() != null && !area.getAreaName().isEmpty()
361 ? area.getAreaName().get(0).getValue() : "Unknown";
362 String country = area.getCountry() != null && !area.getCountry().isEmpty()
363 ? area.getCountry().get(0).getValue() : "";
364
365 System.out.println("Location: " + areaName + (country.isEmpty() ? "" : ", " + country));
366 }
367
368
369 System.out.println("\nForecast for " + today.getDate() + ":");
370 System.out.println("Temperature Range: " + today.getMintempC() + "°C to " + today.getMaxtempC() + "°C");
371 System.out.println("Average Temperature: " + today.getAvgtempC() + "°C");
372 System.out.println("Sunshine Hours: " + today.getSunHour());
373
374
375 if (today.getAstronomy() != null && !today.getAstronomy().isEmpty()) {
376 Astronomy astronomy = today.getAstronomy().get(0);
377 System.out.println("\nAstronomy:");
378 System.out.println("Sunrise: " + astronomy.getSunrise());
379 System.out.println("Sunset: " + astronomy.getSunset());
380 System.out.println("Moonrise: " + astronomy.getMoonrise());
381 System.out.println("Moonset: " + astronomy.getMoonset());
382 System.out.println("Moon Phase: " + astronomy.getMoon_phase());
383 System.out.println("Moon Illumination: " + astronomy.getMoon_illumination() + "%");
384 }
385 }
386
387
388
389
390 private void displayWeatherSummary(WeatherResponse response) {
391 if (response == null) {
392 System.out.println("No data available.");
393 return;
394 }
395
396 if (response.getCurrent_condition() != null && !response.getCurrent_condition().isEmpty()) {
397 CurrentCondition current = response.getCurrent_condition().get(0);
398 String weatherDesc = current.getWeatherDesc() != null && !current.getWeatherDesc().isEmpty()
399 ? current.getWeatherDesc().get(0).getValue() : "Unknown";
400
401 System.out.println("Temperature: " + current.getTemp_C() + "°C / " + current.getTemp_F() + "°F");
402 System.out.println("Conditions: " + weatherDesc);
403 System.out.println("Humidity: " + current.getHumidity() + "%");
404 System.out.println("Wind: " + current.getWindspeedKmph() + " km/h (" + current.getWinddir16Point() + ")");
405 } else {
406 System.out.println("No current conditions available.");
407 }
408 }
409
410
411
412
413
414
415
416
417 public static class WeatherResponse {
418 private List<CurrentCondition> current_condition;
419 private List<NearestArea> nearest_area;
420 private List<Weather> weather;
421
422 public List<CurrentCondition> getCurrent_condition() {
423 return current_condition;
424 }
425
426 public void setCurrent_condition(List<CurrentCondition> current_condition) {
427 this.current_condition = current_condition;
428 }
429
430 public List<NearestArea> getNearest_area() {
431 return nearest_area;
432 }
433
434 public void setNearest_area(List<NearestArea> nearest_area) {
435 this.nearest_area = nearest_area;
436 }
437
438 public List<Weather> getWeather() {
439 return weather;
440 }
441
442 public void setWeather(List<Weather> weather) {
443 this.weather = weather;
444 }
445 }
446
447
448
449
450 public static class CurrentCondition {
451 @JsonProperty("FeelsLikeC")
452 private String FeelsLikeC;
453 @JsonProperty("FeelsLikeF")
454 private String FeelsLikeF;
455 private String cloudcover;
456 private String humidity;
457 private String localObsDateTime;
458 private String observation_time;
459 private String precipInches;
460 private String precipMM;
461 private String pressure;
462 private String pressureInches;
463 private String temp_C;
464 private String temp_F;
465 private String uvIndex;
466 private String visibility;
467 private String visibilityMiles;
468 private String weatherCode;
469 private List<WeatherDesc> weatherDesc;
470 private String winddir16Point;
471 private String winddirDegree;
472 private String windspeedKmph;
473 private String windspeedMiles;
474
475 public String getFeelsLikeC() {
476 return FeelsLikeC;
477 }
478
479 public void setFeelsLikeC(String feelsLikeC) {
480 this.FeelsLikeC = feelsLikeC;
481 }
482
483 public String getFeelsLikeF() {
484 return FeelsLikeF;
485 }
486
487 public void setFeelsLikeF(String feelsLikeF) {
488 this.FeelsLikeF = feelsLikeF;
489 }
490
491 public String getCloudcover() {
492 return cloudcover;
493 }
494
495 public void setCloudcover(String cloudcover) {
496 this.cloudcover = cloudcover;
497 }
498
499 public String getHumidity() {
500 return humidity;
501 }
502
503 public void setHumidity(String humidity) {
504 this.humidity = humidity;
505 }
506
507 public String getLocalObsDateTime() {
508 return localObsDateTime;
509 }
510
511 public void setLocalObsDateTime(String localObsDateTime) {
512 this.localObsDateTime = localObsDateTime;
513 }
514
515 public String getObservation_time() {
516 return observation_time;
517 }
518
519 public void setObservation_time(String observation_time) {
520 this.observation_time = observation_time;
521 }
522
523 public String getPrecipInches() {
524 return precipInches;
525 }
526
527 public void setPrecipInches(String precipInches) {
528 this.precipInches = precipInches;
529 }
530
531 public String getPrecipMM() {
532 return precipMM;
533 }
534
535 public void setPrecipMM(String precipMM) {
536 this.precipMM = precipMM;
537 }
538
539 public String getPressure() {
540 return pressure;
541 }
542
543 public void setPressure(String pressure) {
544 this.pressure = pressure;
545 }
546
547 public String getPressureInches() {
548 return pressureInches;
549 }
550
551 public void setPressureInches(String pressureInches) {
552 this.pressureInches = pressureInches;
553 }
554
555 public String getTemp_C() {
556 return temp_C;
557 }
558
559 public void setTemp_C(String temp_C) {
560 this.temp_C = temp_C;
561 }
562
563 public String getTemp_F() {
564 return temp_F;
565 }
566
567 public void setTemp_F(String temp_F) {
568 this.temp_F = temp_F;
569 }
570
571 public String getUvIndex() {
572 return uvIndex;
573 }
574
575 public void setUvIndex(String uvIndex) {
576 this.uvIndex = uvIndex;
577 }
578
579 public String getVisibility() {
580 return visibility;
581 }
582
583 public void setVisibility(String visibility) {
584 this.visibility = visibility;
585 }
586
587 public String getVisibilityMiles() {
588 return visibilityMiles;
589 }
590
591 public void setVisibilityMiles(String visibilityMiles) {
592 this.visibilityMiles = visibilityMiles;
593 }
594
595 public String getWeatherCode() {
596 return weatherCode;
597 }
598
599 public void setWeatherCode(String weatherCode) {
600 this.weatherCode = weatherCode;
601 }
602
603 public List<WeatherDesc> getWeatherDesc() {
604 return weatherDesc;
605 }
606
607 public void setWeatherDesc(List<WeatherDesc> weatherDesc) {
608 this.weatherDesc = weatherDesc;
609 }
610
611 public String getWinddir16Point() {
612 return winddir16Point;
613 }
614
615 public void setWinddir16Point(String winddir16Point) {
616 this.winddir16Point = winddir16Point;
617 }
618
619 public String getWinddirDegree() {
620 return winddirDegree;
621 }
622
623 public void setWinddirDegree(String winddirDegree) {
624 this.winddirDegree = winddirDegree;
625 }
626
627 public String getWindspeedKmph() {
628 return windspeedKmph;
629 }
630
631 public void setWindspeedKmph(String windspeedKmph) {
632 this.windspeedKmph = windspeedKmph;
633 }
634
635 public String getWindspeedMiles() {
636 return windspeedMiles;
637 }
638
639 public void setWindspeedMiles(String windspeedMiles) {
640 this.windspeedMiles = windspeedMiles;
641 }
642
643
644 @Override
645 public String toString() {
646 return "CurrentCondition{" +
647 "feelsLikeC='" + FeelsLikeC + '\'' +
648 ", feelsLikeF='" + FeelsLikeF + '\'' +
649 ", temp_C='" + temp_C + '\'' +
650 ", temp_F='" + temp_F + '\'' +
651 ", weatherDesc=" + (weatherDesc != null ? weatherDesc.size() : "null") +
652 '}';
653 }
654 }
655
656
657
658
659 public static class NearestArea {
660 private List<Value> areaName;
661 private List<Value> country;
662 private String latitude;
663 private String longitude;
664 private String population;
665 private List<Value> region;
666
667 public List<Value> getAreaName() {
668 return areaName;
669 }
670
671 public void setAreaName(List<Value> areaName) {
672 this.areaName = areaName;
673 }
674
675 public List<Value> getCountry() {
676 return country;
677 }
678
679 public void setCountry(List<Value> country) {
680 this.country = country;
681 }
682
683 public String getLatitude() {
684 return latitude;
685 }
686
687 public void setLatitude(String latitude) {
688 this.latitude = latitude;
689 }
690
691 public String getLongitude() {
692 return longitude;
693 }
694
695 public void setLongitude(String longitude) {
696 this.longitude = longitude;
697 }
698
699 public String getPopulation() {
700 return population;
701 }
702
703 public void setPopulation(String population) {
704 this.population = population;
705 }
706
707 public List<Value> getRegion() {
708 return region;
709 }
710
711 public void setRegion(List<Value> region) {
712 this.region = region;
713 }
714 }
715
716
717
718
719 public static class Weather {
720 private List<Astronomy> astronomy;
721 private String avgtempC;
722 private String avgtempF;
723 private String date;
724 private String maxtempC;
725 private String maxtempF;
726 private String mintempC;
727 private String mintempF;
728 private String sunHour;
729 private String totalSnow_cm;
730 private String uvIndex;
731
732 public List<Astronomy> getAstronomy() {
733 return astronomy;
734 }
735
736 public void setAstronomy(List<Astronomy> astronomy) {
737 this.astronomy = astronomy;
738 }
739
740 public String getAvgtempC() {
741 return avgtempC;
742 }
743
744 public void setAvgtempC(String avgtempC) {
745 this.avgtempC = avgtempC;
746 }
747
748 public String getAvgtempF() {
749 return avgtempF;
750 }
751
752 public void setAvgtempF(String avgtempF) {
753 this.avgtempF = avgtempF;
754 }
755
756 public String getDate() {
757 return date;
758 }
759
760 public void setDate(String date) {
761 this.date = date;
762 }
763
764 public String getMaxtempC() {
765 return maxtempC;
766 }
767
768 public void setMaxtempC(String maxtempC) {
769 this.maxtempC = maxtempC;
770 }
771
772 public String getMaxtempF() {
773 return maxtempF;
774 }
775
776 public void setMaxtempF(String maxtempF) {
777 this.maxtempF = maxtempF;
778 }
779
780 public String getMintempC() {
781 return mintempC;
782 }
783
784 public void setMintempC(String mintempC) {
785 this.mintempC = mintempC;
786 }
787
788 public String getMintempF() {
789 return mintempF;
790 }
791
792 public void setMintempF(String mintempF) {
793 this.mintempF = mintempF;
794 }
795
796 public String getSunHour() {
797 return sunHour;
798 }
799
800 public void setSunHour(String sunHour) {
801 this.sunHour = sunHour;
802 }
803
804 public String getTotalSnow_cm() {
805 return totalSnow_cm;
806 }
807
808 public void setTotalSnow_cm(String totalSnow_cm) {
809 this.totalSnow_cm = totalSnow_cm;
810 }
811
812 public String getUvIndex() {
813 return uvIndex;
814 }
815
816 public void setUvIndex(String uvIndex) {
817 this.uvIndex = uvIndex;
818 }
819 }
820
821
822
823
824 public static class Astronomy {
825 private String moon_illumination;
826 private String moon_phase;
827 private String moonrise;
828 private String moonset;
829 private String sunrise;
830 private String sunset;
831
832 public String getMoon_illumination() {
833 return moon_illumination;
834 }
835
836 public void setMoon_illumination(String moon_illumination) {
837 this.moon_illumination = moon_illumination;
838 }
839
840 public String getMoon_phase() {
841 return moon_phase;
842 }
843
844 public void setMoon_phase(String moon_phase) {
845 this.moon_phase = moon_phase;
846 }
847
848 public String getMoonrise() {
849 return moonrise;
850 }
851
852 public void setMoonrise(String moonrise) {
853 this.moonrise = moonrise;
854 }
855
856 public String getMoonset() {
857 return moonset;
858 }
859
860 public void setMoonset(String moonset) {
861 this.moonset = moonset;
862 }
863
864 public String getSunrise() {
865 return sunrise;
866 }
867
868 public void setSunrise(String sunrise) {
869 this.sunrise = sunrise;
870 }
871
872 public String getSunset() {
873 return sunset;
874 }
875
876 public void setSunset(String sunset) {
877 this.sunset = sunset;
878 }
879 }
880
881
882
883
884 public static class WeatherDesc {
885 private String value;
886
887 public String getValue() {
888 return value;
889 }
890
891 public void setValue(String value) {
892 this.value = value;
893 }
894 }
895
896
897
898
899 public static class Value {
900 private String value;
901
902 public String getValue() {
903 return value;
904 }
905
906 public void setValue(String value) {
907 this.value = value;
908 }
909 }
910
911
912
913
914 private static class Pair<F, S> {
915 private final F first;
916 private final S second;
917
918 public Pair(F first, S second) {
919 this.first = first;
920 this.second = second;
921 }
922
923 public F getFirst() {
924 return first;
925 }
926
927 public S getSecond() {
928 return second;
929 }
930 }
931 }