5555import software .amazon .awssdk .services .cloudwatch .model .SingleMetricAnomalyDetector ;
5656import software .amazon .awssdk .services .cloudwatch .model .StandardUnit ;
5757import software .amazon .awssdk .services .cloudwatch .model .Statistic ;
58+ import software .amazon .awssdk .services .cloudwatch .paginators .DescribeAlarmHistoryPublisher ;
5859import software .amazon .awssdk .services .cloudwatch .paginators .ListDashboardsPublisher ;
5960import software .amazon .awssdk .services .cloudwatch .paginators .ListMetricsPublisher ;
6061
@@ -82,6 +83,24 @@ public class CloudWatchActions {
8283
8384 private static final Logger logger = LoggerFactory .getLogger (CloudWatchActions .class );
8485
86+ /**
87+ * Retrieves an asynchronous CloudWatch client instance.
88+ *
89+ * <p>
90+ * This method ensures that the CloudWatch client is initialized with the following configurations:
91+ * <ul>
92+ * <li>Maximum concurrency: 100</li>
93+ * <li>Connection timeout: 60 seconds</li>
94+ * <li>Read timeout: 60 seconds</li>
95+ * <li>Write timeout: 60 seconds</li>
96+ * <li>API call timeout: 2 minutes</li>
97+ * <li>API call attempt timeout: 90 seconds</li>
98+ * <li>Retry strategy: STANDARD</li>
99+ * </ul>
100+ * </p>
101+ *
102+ * @return the asynchronous CloudWatch client instance
103+ */
85104 private static CloudWatchAsyncClient getAsyncClient () {
86105 if (cloudWatchAsyncClient == null ) {
87106 SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient .builder ()
@@ -204,7 +223,7 @@ public CompletableFuture<DeleteDashboardsResponse> deleteDashboardAsync(String d
204223 * @param fileName the name of the file to save the metric image to
205224 * @return a {@link CompletableFuture} that completes when the image has been saved to the file
206225 */
207- public CompletableFuture <Void > getAndOpenMetricImageAsync (String fileName ) {
226+ public CompletableFuture <Void > downloadAndSaveMetricImageAsync (String fileName ) {
208227 logger .info ("Getting Image data for custom metric." );
209228 String myJSON = """
210229 {
@@ -370,6 +389,7 @@ public CompletableFuture<Void> getAlarmHistoryAsync(String fileName, String date
370389 }
371390 });
372391
392+ // Use the alarm name to describe alarm history with a paginator.
373393 return readFileFuture .thenCompose (alarmName -> {
374394 try {
375395 Instant start = Instant .parse (date );
@@ -381,26 +401,33 @@ public CompletableFuture<Void> getAlarmHistoryAsync(String fileName, String date
381401 .historyItemType (HistoryItemType .ACTION )
382402 .build ();
383403
384- return getAsyncClient ().describeAlarmHistory (historyRequest ).thenAccept (response -> {
385- List <AlarmHistoryItem > historyItems = response .alarmHistoryItems ();
386- if (historyItems .isEmpty ()) {
387- logger .info ("No alarm history data found for {} " , alarmName );
388- } else {
389- for (AlarmHistoryItem item : historyItems ) {
390- logger .info ("History summary: {} " , item .historySummary ());
391- logger .info ("Timestamp: {}" , item .timestamp ());
404+ // Use the paginator to paginate through alarm history pages.
405+ DescribeAlarmHistoryPublisher historyPublisher = getAsyncClient ().describeAlarmHistoryPaginator (historyRequest );
406+ CompletableFuture <Void > future = historyPublisher
407+ .subscribe (response -> response .alarmHistoryItems ().forEach (item -> {
408+ logger .info ("History summary: {}" , item .historySummary ());
409+ logger .info ("Timestamp: {}" , item .timestamp ());
410+ }))
411+ .whenComplete ((result , exception ) -> {
412+ if (exception != null ) {
413+ logger .error ("Error occurred while getting alarm history: " + exception .getMessage (), exception );
414+ } else {
415+ logger .info ("Successfully retrieved all alarm history." );
392416 }
393- }
394- });
417+ });
418+
419+ // Return the future to the calling code for further handling
420+ return future ;
395421 } catch (Exception e ) {
396422 throw new RuntimeException ("Failed to process alarm history" , e );
397423 }
398424 }).whenComplete ((result , exception ) -> {
399425 if (exception != null ) {
400- throw new RuntimeException ("Error getting alarm history" , exception );
426+ throw new RuntimeException ("Error completing alarm history processing " , exception );
401427 }
402428 });
403429 }
430+
404431 // snippet-end:[cloudwatch.java2.scenario.get.alarm.history.main]
405432
406433 // snippet-start:[cloudwatch.java2.scenario.check.met.alarm.main]
@@ -664,7 +691,6 @@ public CompletableFuture<Void> describeAlarmsAsync() {
664691 // snippet-end:[cloudwatch.java2.describe_alarms.main]
665692
666693 // snippet-start:[cloudwatch.java2.scenario.create.alarm.main]
667-
668694 /**
669695 * Creates an alarm based on the configuration provided in a JSON file.
670696 *
@@ -673,7 +699,6 @@ public CompletableFuture<Void> describeAlarmsAsync() {
673699 * @throws RuntimeException if an exception occurs while reading the JSON file or creating the alarm
674700 */
675701 public CompletableFuture <String > createAlarmAsync (String fileName ) {
676- // Handle potential exceptions from reading the file synchronously
677702 com .fasterxml .jackson .databind .JsonNode rootNode ;
678703 try {
679704 JsonParser parser = new JsonFactory ().createParser (new File (fileName ));
@@ -716,7 +741,6 @@ public CompletableFuture<String> createAlarmAsync(String fileName) {
716741 logger .info ("Failed to create alarm: {}" , ex .getMessage ());
717742 throw new RuntimeException ("Failed to create alarm" , ex );
718743 } else {
719- // If successful, log and return the alarm name
720744 logger .info ("{} was successfully created!" , alarmName );
721745 return alarmName ;
722746 }
@@ -725,7 +749,6 @@ public CompletableFuture<String> createAlarmAsync(String fileName) {
725749 // snippet-end:[cloudwatch.java2.scenario.create.alarm.main]
726750
727751 // snippet-start:[cloudwatch.java2.scenario.add.metric.dashboard.main]
728-
729752 /**
730753 * Adds a metric to a dashboard asynchronously.
731754 *
@@ -761,6 +784,12 @@ public CompletableFuture<PutDashboardResponse> addMetricToDashboardAsync(String
761784 // snippet-end:[cloudwatch.java2.scenario.add.metric.dashboard.main]
762785
763786 // snippet-start:[cloudwatch.java2.scenario.create.metric.main]
787+ /**
788+ * Creates a new custom metric.
789+ *
790+ * @param dataPoint the data point to be added to the custom metric
791+ * @return a {@link CompletableFuture} representing the asynchronous operation of adding the custom metric
792+ */
764793 public CompletableFuture <PutMetricDataResponse > createNewCustomMetricAsync (Double dataPoint ) {
765794 Dimension dimension = Dimension .builder ()
766795 .name ("UNIQUE_PAGES" )
@@ -797,7 +826,6 @@ public CompletableFuture<PutMetricDataResponse> createNewCustomMetricAsync(Doubl
797826 // snippet-end:[cloudwatch.java2.scenario.create.metric.main]
798827
799828 // snippet-start:[cloudwatch.java2.scenario.list.dashboard.main]
800-
801829 /**
802830 * Lists the available dashboards.
803831 *
@@ -822,15 +850,15 @@ public CompletableFuture<Void> listDashboardsAsync() {
822850 // snippet-start:[cloudwatch.java2.scenario.create.dashboard.main]
823851
824852 /**
825- * Creates a new dashboard asynchronously with the specified name and metrics from the given file.
853+ * Creates a new dashboard with the specified name and metrics from the given file.
826854 *
827855 * @param dashboardName the name of the dashboard to be created
828856 * @param fileName the name of the file containing the dashboard body
829857 * @return a {@link CompletableFuture} representing the asynchronous operation of creating the dashboard
830858 * @throws IOException if there is an error reading the dashboard body from the file
831859 */
832860 public CompletableFuture <PutDashboardResponse > createDashboardWithMetricsAsync (String dashboardName , String fileName ) throws IOException {
833- String dashboardBody = readFileAsString (fileName ); // Assume this method already throws IOException
861+ String dashboardBody = readFileAsString (fileName );
834862 PutDashboardRequest dashboardRequest = PutDashboardRequest .builder ()
835863 .dashboardName (dashboardName )
836864 .dashboardBody (dashboardBody )
@@ -910,7 +938,6 @@ public CompletableFuture<GetMetricStatisticsResponse> getMetricStatisticsAsync(S
910938
911939
912940 // snippet-start:[cloudwatch.java2.scenario.display.metrics.main]
913-
914941 /**
915942 * Retrieves and displays metric statistics for the specified parameters.
916943 *
@@ -961,7 +988,6 @@ public CompletableFuture<GetMetricStatisticsResponse> getAndDisplayMetricStatist
961988 // snippet-end:[cloudwatch.java2.scenario.display.metrics.main]
962989
963990 // snippet-start:[cloudwatch.java2.list_metrics.main]
964-
965991 /**
966992 * Retrieves a list of metric names for the specified namespace.
967993 *
@@ -993,7 +1019,6 @@ public CompletableFuture<ArrayList<String>> listMetsAsync(String namespace) {
9931019 // snippet-end:[cloudwatch.java2.list_metrics.main]
9941020
9951021 // snippet-start:[cloudwatch.java2.scenario.list.namespaces.main]
996-
9971022 /**
9981023 * Lists the available namespaces for the current AWS account.
9991024 *
0 commit comments