xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/web/ApiUtils.java (revision cce4eb5fdc725c6561a5e047584a06646e911023)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
22  */
23 package org.opengrok.indexer.web;
24 
25 import jakarta.ws.rs.client.ClientBuilder;
26 import jakarta.ws.rs.core.HttpHeaders;
27 import jakarta.ws.rs.core.Response;
28 import org.jetbrains.annotations.NotNull;
29 import org.opengrok.indexer.configuration.RuntimeEnvironment;
30 import org.opengrok.indexer.logger.LoggerFactory;
31 
32 import java.util.concurrent.TimeUnit;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35 
36 public class ApiUtils {
37 
38     private static final Logger LOGGER = LoggerFactory.getLogger(ApiUtils.class);
39 
ApiUtils()40     private ApiUtils() {
41         // utility class
42     }
43 
44     /**
45      * Busy waits for API call to complete by repeatedly querying the status API endpoint passed
46      * in the {@code Location} header in the response parameter. The overall time is governed
47      * by the {@link RuntimeEnvironment#getApiTimeout()}, however each individual status check
48      * uses {@link RuntimeEnvironment#getConnectTimeout()} so in the worst case the total time can be
49      * {@code getApiTimeout() * getConnectTimeout()}.
50      * @param response response returned from the server upon asynchronous API request
51      * @return response from the status API call
52      * @throws InterruptedException on sleep interruption
53      * @throws IllegalArgumentException on invalid request (no {@code Location} header)
54      */
waitForAsyncApi(@otNull Response response)55     public static @NotNull Response waitForAsyncApi(@NotNull Response response)
56             throws InterruptedException, IllegalArgumentException {
57 
58         String location = response.getHeaderString(HttpHeaders.LOCATION);
59         if (location == null) {
60             throw new IllegalArgumentException(String.format("no %s header in %s", HttpHeaders.LOCATION, response));
61         }
62 
63         LOGGER.log(Level.FINER, "checking asynchronous API result on {0}", location);
64         for (int i = 0; i < RuntimeEnvironment.getInstance().getApiTimeout(); i++) {
65             response = ClientBuilder.newBuilder().
66                     connectTimeout(RuntimeEnvironment.getInstance().getConnectTimeout(), TimeUnit.SECONDS).build().
67                     target(location).request().get();
68             if (response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) {
69                 Thread.sleep(1000);
70             } else {
71                 break;
72             }
73         }
74 
75         if (response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) {
76             LOGGER.log(Level.WARNING, "API request still not completed: {0}", response);
77             return response;
78         }
79 
80         LOGGER.log(Level.FINER, "making DELETE API request to {0}", location);
81         Response deleteResponse = ClientBuilder.newBuilder().connectTimeout(3, TimeUnit.SECONDS).build().
82                 target(location).request().delete();
83         if (deleteResponse.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
84             LOGGER.log(Level.WARNING, "DELETE API call to {0} failed with HTTP error {1}",
85                     new Object[]{location, response.getStatusInfo()});
86         }
87 
88         return response;
89     }
90 }
91