X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=models-interactions%2Fmodel-actors%2FactorServiceProvider%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fpolicy%2Fcontrolloop%2Factorserviceprovider%2Fimpl%2FHttpOperation.java;h=1e11bce4c80e0ddc7e740409f3e2cc5d6aa42b03;hb=938005505883cf7a636a8840e20e3dc8a0ad9176;hp=c3c0f6dc25aed92f7136f7ae1a644291aaebb562;hpb=ba3a17c58088a016b3ed2948b0c33502fdbc84ec;p=policy%2Fmodels.git diff --git a/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperation.java b/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperation.java index c3c0f6dc2..1e11bce4c 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperation.java +++ b/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperation.java @@ -2,7 +2,8 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +21,15 @@ package org.onap.policy.controlloop.actorserviceprovider.impl; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.Response; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.function.Function; -import javax.ws.rs.client.InvocationCallback; -import javax.ws.rs.core.Response; import lombok.Getter; import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; import org.onap.policy.common.endpoints.http.client.HttpClient; @@ -35,10 +37,12 @@ import org.onap.policy.common.endpoints.utils.NetLoggerUtil; import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType; import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.OperationResult; import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; +import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig; import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams; +import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig; import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture; -import org.onap.policy.controlloop.policy.PolicyResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,35 +56,85 @@ public abstract class HttpOperation extends OperationPartial { private static final Logger logger = LoggerFactory.getLogger(HttpOperation.class); /** - * Operator that created this operation. + * Response status. */ - protected final HttpOperator operator; + public enum Status { + SUCCESS, FAILURE, STILL_WAITING + } + + /** + * Configuration for this operation. + */ + private final HttpConfig config; /** * Response class. */ private final Class responseClass; + /** + * {@code True} to use polling, {@code false} otherwise. + */ + @Getter + private boolean usePolling; + + /** + * Number of polls issued so far, on the current operation attempt. + */ + @Getter + private int pollCount; + /** * Constructs the object. * * @param params operation parameters - * @param operator operator that created this operation + * @param config configuration for this operation * @param clazz response class + * @param propertyNames names of properties required by this operation */ - public HttpOperation(ControlLoopOperationParams params, HttpOperator operator, Class clazz) { - super(params, operator); - this.operator = operator; + protected HttpOperation(ControlLoopOperationParams params, HttpConfig config, Class clazz, + List propertyNames) { + super(params, config, propertyNames); + this.config = config; this.responseClass = clazz; } + /** + * Indicates that polling should be used. + */ + protected void setUsePolling() { + if (!(config instanceof HttpPollingConfig)) { + throw new IllegalStateException("cannot poll without polling parameters"); + } + + usePolling = true; + } + + public HttpClient getClient() { + return config.getClient(); + } + + /** + * Gets the path to be used when performing the request; this is typically appended to + * the base URL. This method simply invokes {@link #getPath()}. + * + * @return the path URI suffix + */ + public String getPath() { + return config.getPath(); + } + + public long getTimeoutMs() { + return config.getTimeoutMs(); + } + /** * If no timeout is specified, then it returns the operator's configured timeout. */ @Override protected long getTimeoutMs(Integer timeoutSec) { - return (timeoutSec == null || timeoutSec == 0 ? operator.getTimeoutMs() : super.getTimeoutMs(timeoutSec)); + return (timeoutSec == null || timeoutSec == 0 ? getTimeoutMs() : super.getTimeoutMs(timeoutSec)); } /** @@ -93,24 +147,25 @@ public abstract class HttpOperation extends OperationPartial { } /** - * Gets the path to be used when performing the request; this is typically appended to - * the base URL. This method simply invokes {@link #getPath()}. + * Makes the URL to which the HTTP request should be posted. This is primarily used + * for logging purposes. This particular method returns the base URL appended with the + * return value from {@link #getPath()}. * - * @return the path URI suffix + * @return the URL to which from which to get */ - public String makePath() { - return operator.getPath(); + public String getUrl() { + return (getClient().getBaseUrl() + getPath()); } /** - * Makes the URL to which the "get" request should be posted. This ir primarily used - * for logging purposes. This particular method returns the base URL appended with the - * return value from {@link #makePath()}. + * Resets the polling count * - * @return the URL to which from which to get + *

+ * Note: This should be invoked at the start of each operation (i.e., in + * {@link #startOperationAsync(int, OperationOutcome)}. */ - public String makeUrl() { - return (operator.getClient().getBaseUrl() + makePath()); + protected void resetPollCount() { + pollCount = 0; } /** @@ -127,7 +182,7 @@ public abstract class HttpOperation extends OperationPartial { final PipelineControllerFuture controller = new PipelineControllerFuture<>(); final CompletableFuture future = new CompletableFuture<>(); - final Executor executor = params.getExecutor(); + final var executor = params.getExecutor(); // arrange for the callback to complete "future" InvocationCallback callback = new InvocationCallback<>() { @@ -160,7 +215,7 @@ public abstract class HttpOperation extends OperationPartial { * * @param outcome outcome to be populate * @param url URL to which to request was sent - * @param response raw response to process + * @param rawResponse raw response to process * @return a future to cancel or await the outcome */ protected CompletableFuture processResponse(OperationOutcome outcome, String url, @@ -168,7 +223,7 @@ public abstract class HttpOperation extends OperationPartial { logger.info("{}.{}: response received for {}", params.getActor(), params.getOperation(), params.getRequestId()); - String strResponse = HttpClient.getBody(rawResponse, String.class); + String strResponse = rawResponse.readEntity(String.class); logMessage(EventType.IN, CommInfrastructure.REST, url, strResponse); @@ -177,7 +232,7 @@ public abstract class HttpOperation extends OperationPartial { response = responseClass.cast(strResponse); } else { try { - response = makeCoder().decode(strResponse, responseClass); + response = getCoder().decode(strResponse, responseClass); } catch (CoderException e) { logger.warn("{}.{} cannot decode response for {}", params.getActor(), params.getOperation(), params.getRequestId(), e); @@ -188,11 +243,12 @@ public abstract class HttpOperation extends OperationPartial { if (!isSuccess(rawResponse, response)) { logger.info("{}.{} request failed with http error code {} for {}", params.getActor(), params.getOperation(), rawResponse.getStatus(), params.getRequestId()); - return CompletableFuture.completedFuture(setOutcome(outcome, PolicyResult.FAILURE, response)); + return CompletableFuture.completedFuture( + setOutcome(outcome, OperationResult.FAILURE, rawResponse, response)); } logger.info("{}.{} request succeeded for {}", params.getActor(), params.getOperation(), params.getRequestId()); - setOutcome(outcome, PolicyResult.SUCCESS, response); + setOutcome(outcome, OperationResult.SUCCESS, rawResponse, response); return postProcessResponse(outcome, url, rawResponse, response); } @@ -201,10 +257,14 @@ public abstract class HttpOperation extends OperationPartial { * * @param outcome operation to be updated * @param result result of the operation - * @param response response used to populate the outcome + * @param rawResponse raw response + * @param response decoded response * @return the updated operation */ - public OperationOutcome setOutcome(OperationOutcome outcome, PolicyResult result, T response) { + public OperationOutcome setOutcome(OperationOutcome outcome, OperationResult result, Response rawResponse, + T response) { + + outcome.setResponse(response); return setOutcome(outcome, result); } @@ -221,7 +281,85 @@ public abstract class HttpOperation extends OperationPartial { protected CompletableFuture postProcessResponse(OperationOutcome outcome, String url, Response rawResponse, T response) { - return CompletableFuture.completedFuture(outcome); + if (!usePolling) { + // doesn't use polling - just return the completed future + return CompletableFuture.completedFuture(outcome); + } + + HttpPollingConfig cfg = (HttpPollingConfig) config; + + switch (detmStatus(rawResponse, response)) { + case SUCCESS -> { + logger.info("{}.{} request succeeded for {}", params.getActor(), params.getOperation(), + params.getRequestId()); + return CompletableFuture + .completedFuture(setOutcome(outcome, OperationResult.SUCCESS, rawResponse, response)); + } + case FAILURE -> { + logger.info("{}.{} request failed for {}", params.getActor(), params.getOperation(), + params.getRequestId()); + return CompletableFuture + .completedFuture(setOutcome(outcome, OperationResult.FAILURE, rawResponse, response)); + } + default -> logger.info("{}.{} request incomplete for {}", params.getActor(), params.getOperation(), + params.getRequestId()); + } + + // still incomplete + + // see if the limit for the number of polls has been reached + if (pollCount++ >= cfg.getMaxPolls()) { + logger.warn("{}: exceeded 'poll' limit {} for {}", getFullName(), cfg.getMaxPolls(), + params.getRequestId()); + setOutcome(outcome, OperationResult.FAILURE_TIMEOUT); + return CompletableFuture.completedFuture(outcome); + } + + // sleep and then poll + Function> doPoll = unused -> issuePoll(outcome); + return sleep(getPollWaitMs(), TimeUnit.MILLISECONDS).thenComposeAsync(doPoll); + } + + /** + * Polls to see if the original request is complete. This method polls using an HTTP + * "get" request whose URL is constructed by appending the extracted "poll ID" to the + * poll path from the configuration data. + * + * @param outcome outcome to be populated with the response + * @return a future that can be used to cancel the poll or await its response + */ + protected CompletableFuture issuePoll(OperationOutcome outcome) { + String path = getPollingPath(); + String url = getClient().getBaseUrl() + path; + + logger.debug("{}: 'poll' count {} for {}", getFullName(), pollCount, params.getRequestId()); + + logMessage(EventType.OUT, CommInfrastructure.REST, url, null); + + return handleResponse(outcome, url, callback -> getClient().get(callback, path, null)); + } + + /** + * Determines the status of the response. This particular method simply throws an + * exception. + * + * @param rawResponse raw response + * @param response decoded response + * @return the status of the response + */ + protected Status detmStatus(Response rawResponse, T response) { + throw new UnsupportedOperationException("cannot determine response status"); + } + + /** + * Gets the URL to use when polling. Typically, this is some unique ID appended to the + * polling path found within the configuration data. This particular method simply + * returns the polling path from the configuration data. + * + * @return the URL to use when polling + */ + protected String getPollingPath() { + return ((HttpPollingConfig) config).getPollPath(); } /** @@ -242,4 +380,12 @@ public abstract class HttpOperation extends OperationPartial { NetLoggerUtil.log(direction, infra, sink, json); return json; } + + // these may be overridden by junit tests + + protected long getPollWaitMs() { + HttpPollingConfig cfg = (HttpPollingConfig) config; + + return TimeUnit.MILLISECONDS.convert(cfg.getPollWaitSec(), TimeUnit.SECONDS); + } }