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