From 9e4e4474ee079176b26ed0af7105a4b23540d585 Mon Sep 17 00:00:00 2001 From: "Henry.Sun" Date: Fri, 12 Jul 2019 15:49:30 +0000 Subject: add status filter in restclient and restrequestor Issue-ID: POLICY-1742 Change-Id: Ib4984b18c0353cddb12c13c352277728be691bf5 Signed-off-by: Henry.Sun --- .../carrier/restclient/ApexRestClientConsumer.java | 52 +++++-- .../RestClientCarrierTechnologyParameters.java | 173 ++++++++++++--------- 2 files changed, 134 insertions(+), 91 deletions(-) (limited to 'plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java') diff --git a/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/ApexRestClientConsumer.java b/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/ApexRestClientConsumer.java index 49ca1e309..aaad52954 100644 --- a/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/ApexRestClientConsumer.java +++ b/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/ApexRestClientConsumer.java @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2019 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +25,13 @@ import java.util.EnumMap; import java.util.Map; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.core.Response; +import org.apache.commons.lang3.StringUtils; import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; import org.onap.policy.apex.service.engine.event.ApexEventConsumer; @@ -52,6 +56,9 @@ public class ApexRestClientConsumer implements ApexEventConsumer, Runnable { // The amount of time to wait in milliseconds between checks that the consumer thread has stopped private static final long REST_CLIENT_WAIT_SLEEP_TIME = 50; + // The Key for property + private static final String HTTP_CODE_STATUS = "HTTP_CODE_STATUS"; + // The REST parameters read from the parameter service private RestClientCarrierTechnologyParameters restConsumerProperties; @@ -67,25 +74,30 @@ public class ApexRestClientConsumer implements ApexEventConsumer, Runnable { // The peer references for this event handler private Map peerReferenceMap = new EnumMap<>(EventHandlerPeeredMode.class); + // The pattern for filtering status code + private Pattern httpCodeFilterPattern = null; + // The consumer thread and stopping flag private Thread consumerThread; private boolean stopOrderedFlag = false; @Override public void init(final String consumerName, final EventHandlerParameters consumerParameters, - final ApexEventReceiver incomingEventReceiver) throws ApexEventException { + final ApexEventReceiver incomingEventReceiver) throws ApexEventException { this.eventReceiver = incomingEventReceiver; this.name = consumerName; // Check and get the REST Properties if (!(consumerParameters.getCarrierTechnologyParameters() instanceof RestClientCarrierTechnologyParameters)) { - final String errorMessage = "specified consumer properties are not applicable to REST client consumer (" - + this.name + ")"; + final String errorMessage = + "specified consumer properties are not applicable to REST client consumer (" + this.name + ")"; LOGGER.warn(errorMessage); throw new ApexEventException(errorMessage); } - restConsumerProperties = (RestClientCarrierTechnologyParameters) consumerParameters - .getCarrierTechnologyParameters(); + restConsumerProperties = + (RestClientCarrierTechnologyParameters) consumerParameters.getCarrierTechnologyParameters(); + + this.httpCodeFilterPattern = Pattern.compile(restConsumerProperties.getHttpCodeFilter()); // Check if the HTTP method has been set if (restConsumerProperties.getHttpMethod() == null) { @@ -94,8 +106,8 @@ public class ApexRestClientConsumer implements ApexEventConsumer, Runnable { if (!RestClientCarrierTechnologyParameters.HttpMethod.GET.equals(restConsumerProperties.getHttpMethod())) { final String errorMessage = "specified HTTP method of \"" + restConsumerProperties.getHttpMethod() - + "\" is invalid, only HTTP method \"GET\" " - + "is supported for event reception on REST client consumer (" + this.name + ")"; + + "\" is invalid, only HTTP method \"GET\" " + + "is supported for event reception on REST client consumer (" + this.name + ")"; LOGGER.warn(errorMessage); throw new ApexEventException(errorMessage); } @@ -187,13 +199,17 @@ public class ApexRestClientConsumer implements ApexEventConsumer, Runnable { public void run() { try { final Response response = client.target(restConsumerProperties.getUrl()).request("application/json") - .headers(restConsumerProperties.getHttpHeadersAsMultivaluedMap()).get(); + .headers(restConsumerProperties.getHttpHeadersAsMultivaluedMap()).get(); - // Check that the event request worked - if (!Response.Status.Family.familyOf(response.getStatus()).equals(Response.Status.Family.SUCCESSFUL)) { + // Match the return code + Matcher isPass = httpCodeFilterPattern.matcher(String.valueOf(response.getStatus())); + + // Check that status code + if (!isPass.matches()) { final String errorMessage = "reception of event from URL \"" + restConsumerProperties.getUrl() - + "\" failed with status code " + response.getStatus() + " and message \"" - + response.readEntity(String.class) + "\""; + + "\" failed with status code " + response.getStatus() + " and message \"" + + response.readEntity(String.class) + "\""; + LOGGER.warn(errorMessage); throw new ApexEventRuntimeException(errorMessage); } @@ -201,14 +217,18 @@ public class ApexRestClientConsumer implements ApexEventConsumer, Runnable { final String eventJsonString = response.readEntity(String.class); // Check there is content - if (eventJsonString == null || eventJsonString.trim().length() == 0) { - final String errorMessage = "received an empty event from URL \"" + restConsumerProperties.getUrl() - + "\""; + if (StringUtils.isBlank(eventJsonString)) { + final String errorMessage = + "received an empty event from URL \"" + restConsumerProperties.getUrl() + "\""; throw new ApexEventRuntimeException(errorMessage); } + // build a key and value property in excutionProperties + Properties executionProperties = new Properties(); + executionProperties.put(HTTP_CODE_STATUS, response.getStatus()); + // Send the event into Apex - eventReceiver.receiveEvent(new Properties(), eventJsonString); + eventReceiver.receiveEvent(executionProperties, eventJsonString); } catch (final Exception e) { LOGGER.warn("error receiving events on thread {}", consumerThread.getName(), e); } diff --git a/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/RestClientCarrierTechnologyParameters.java b/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/RestClientCarrierTechnologyParameters.java index ca0084982..8d783c5cd 100644 --- a/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/RestClientCarrierTechnologyParameters.java +++ b/plugins/plugins-event/plugins-event-carrier/plugins-event-carrier-restclient/src/main/java/org/onap/policy/apex/plugins/event/carrier/restclient/RestClientCarrierTechnologyParameters.java @@ -26,13 +26,22 @@ import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; + +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; import org.onap.policy.common.parameters.GroupValidationResult; import org.onap.policy.common.parameters.ValidationStatus; import org.onap.policy.common.utils.validation.ParameterValidationUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +// @formatter:off /** * Apex parameters for REST as an event carrier technology with Apex as a REST client. * @@ -42,11 +51,21 @@ import org.onap.policy.common.utils.validation.ParameterValidationUtils; * parameter is mandatory. *
  • httpMethod: The HTTP method to use when sending events over REST, legal values are POST (default) and PUT. When * receiving events, the REST client plugin always uses the HTTP GET method. + *
  • httpHeaders, the HTTP headers to send on REST requests, optional parameter, defaults to none. + *
  • httpCodeFilter: a regular expression filter for returned HTTP codes, if the returned HTTP code passes this + * filter, then the request is assumed to have succeeded by the plugin, optional, defaults to allowing 2xx codes + * through, that is a regular expression of "[2][0-9][0-9]" * * * @author Joss Armstrong (joss.armstrong@ericsson.com) */ +//@formatter:on +@Setter +@Getter public class RestClientCarrierTechnologyParameters extends CarrierTechnologyParameters { + // Get a reference to the logger + private static final Logger LOGGER = LoggerFactory.getLogger(RestClientCarrierTechnologyParameters.class); + /** The supported HTTP methods. */ public enum HttpMethod { GET, PUT, POST, DELETE @@ -61,15 +80,22 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara /** The consumer plugin class for the REST carrier technology. */ public static final String RESTCLIENT_EVENT_CONSUMER_PLUGIN_CLASS = ApexRestClientConsumer.class.getName(); + /** The default HTTP code filter, allows 2xx HTTP codes through. */ + public static final String DEFAULT_HTTP_CODE_FILTER = "[2][0-9][0-9]"; + // Commonly occurring strings private static final String HTTP_HEADERS = "httpHeaders"; + private static final String HTTP_CODE_FILTER = "httpCodeFilter"; + + // Regular expression patterns for finding and checking keys in URLs + private static final Pattern patternProperKey = Pattern.compile("(?<=\\{)[^}]*(?=\\})"); + private static final Pattern patternErrorKey = + Pattern.compile("(\\{[^\\{}]*.?\\{)|(\\{[^\\{}]*$)|(\\}[^\\{}]*.?\\})|(^[^\\{}]*.?\\})|\\{\\s*\\}"); private String url = null; private HttpMethod httpMethod = null; private String[][] httpHeaders = null; - private static final Pattern patternProperKey = Pattern.compile("(?<=\\{)[^}]*(?=\\})"); - private static final Pattern patternErrorKey = Pattern.compile( - "(\\{[^\\{}]*.?\\{)|(\\{[^\\{}]*$)|(\\}[^\\{}]*.?\\})|(^[^\\{}]*.?\\})|\\{\\s*\\}"); + private String httpCodeFilter = DEFAULT_HTTP_CODE_FILTER; /** * Constructor to create a REST carrier technology parameters instance and register the instance with the parameter @@ -82,43 +108,6 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara this.setLabel(RESTCLIENT_CARRIER_TECHNOLOGY_LABEL); this.setEventProducerPluginClass(RESTCLIENT_EVENT_PRODUCER_PLUGIN_CLASS); this.setEventConsumerPluginClass(RESTCLIENT_EVENT_CONSUMER_PLUGIN_CLASS); - - } - - /** - * Gets the URL for the REST request. - * - * @return the URL - */ - public String getUrl() { - return url; - } - - /** - * Sets the URL for the REST request. - * - * @param incomingUrl the URL - */ - public void setUrl(final String incomingUrl) { - this.url = incomingUrl; - } - - /** - * Gets the HTTP method to use for the REST request. - * - * @return the HTTP method - */ - public HttpMethod getHttpMethod() { - return httpMethod; - } - - /** - * Sets the HTTP method to use for the REST request. - * - * @param httpMethod the HTTP method - */ - public void setHttpMethod(final HttpMethod httpMethod) { - this.httpMethod = httpMethod; } /** @@ -130,15 +119,6 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara return httpHeaders != null && httpHeaders.length > 0; } - /** - * Gets the http headers for the REST request. - * - * @return the headers - */ - public String[][] getHttpHeaders() { - return httpHeaders; - } - /** * Gets the http headers for the REST request as a multivalued map. * @@ -174,7 +154,7 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara * @return set of the tags */ public Set getKeysFromUrl() { - Matcher matcher = patternProperKey.matcher(this.url); + Matcher matcher = patternProperKey.matcher(getUrl()); Set key = new HashSet<>(); while (matcher.find()) { key.add(matcher.group()); @@ -183,34 +163,54 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara } /** - * Validate tags in url. + * {@inheritDoc}. + */ + @Override + public GroupValidationResult validate() { + GroupValidationResult result = super.validate(); + + result = validateUrl(result); + + result = validateHttpHeaders(result); + + return validateHttpCodeFilter(result); + } + + // @formatter:off + /** + * Validate the URL. + * + *

    Checks: * http://www.blah.com/{par1/somethingelse (Missing end tag) use {[^\\{}]*$ * http://www.blah.com/{par1/{some}thingelse (Nested tag) use {[^}]*{ * http://www.blah.com/{par1}/some}thingelse (Missing start tag1) use }[^{}]*.} * http://www.blah.com/par1}/somethingelse (Missing start tag2) use }[^{}]*} * http://www.blah.com/{}/somethingelse (Empty tag) use {[\s]*} - * - * @return if url is legal + * @param result the result of the validation */ - public boolean validateTagInUrl() { - // Check url tag syntax error - Matcher matcher = patternErrorKey.matcher(this.url); - return (!matcher.find()); - } - - /** - * {@inheritDoc}. - */ - @Override - public GroupValidationResult validate() { - final GroupValidationResult result = super.validate(); - + // @formatter:on + private GroupValidationResult validateUrl(final GroupValidationResult result) { // Check if the URL has been set for event output if (getUrl() == null) { + result.setResult("url", ValidationStatus.INVALID, "no URL has been set for event sending on REST client"); + return result; + } + + Matcher matcher = patternErrorKey.matcher(getUrl()); + if (matcher.find()) { result.setResult("url", ValidationStatus.INVALID, - "no URL has been set for event sending on REST client"); + "no proper URL has been set for event sending on REST client"); } + return result; + } + + /** + * Validate the HTTP headers. + * + * @param result the result of the validation + */ + private GroupValidationResult validateHttpHeaders(final GroupValidationResult result) { if (httpHeaders == null) { return result; } @@ -220,21 +220,44 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara result.setResult(HTTP_HEADERS, ValidationStatus.INVALID, "HTTP header array entry is null"); } else if (httpHeader.length != 2) { result.setResult(HTTP_HEADERS, ValidationStatus.INVALID, - "HTTP header array entries must have one key and one value: " - + Arrays.deepToString(httpHeader)); + "HTTP header array entries must have one key and one value: " + + Arrays.deepToString(httpHeader)); } else if (!ParameterValidationUtils.validateStringParameter(httpHeader[0])) { result.setResult(HTTP_HEADERS, ValidationStatus.INVALID, - "HTTP header key is null or blank: " + Arrays.deepToString(httpHeader)); + "HTTP header key is null or blank: " + Arrays.deepToString(httpHeader)); } else if (!ParameterValidationUtils.validateStringParameter(httpHeader[1])) { result.setResult(HTTP_HEADERS, ValidationStatus.INVALID, - "HTTP header value is null or blank: " + Arrays.deepToString(httpHeader)); + "HTTP header value is null or blank: " + Arrays.deepToString(httpHeader)); } } - if (!validateTagInUrl()) { - result.setResult("url", ValidationStatus.INVALID, - "no proper URL has been set for event sending on REST client"); + return result; + } + + /** + * Validate the HTTP code filter. + * + * @param result the result of the validation + */ + public GroupValidationResult validateHttpCodeFilter(final GroupValidationResult result) { + if (httpCodeFilter == null) { + httpCodeFilter = DEFAULT_HTTP_CODE_FILTER; + + } else if (StringUtils.isBlank(httpCodeFilter)) { + result.setResult(HTTP_CODE_FILTER, ValidationStatus.INVALID, + "HTTP code filter must be specified as a three digit regular expression"); + } else { + try { + Pattern.compile(httpCodeFilter); + } catch (PatternSyntaxException pse) { + String message = + "Invalid HTTP code filter, the filter must be specified as a three digit regular expression: " + + pse.getMessage(); + result.setResult(HTTP_CODE_FILTER, ValidationStatus.INVALID, message); + LOGGER.debug(message, pse); + } } + return result; } @@ -244,6 +267,6 @@ public class RestClientCarrierTechnologyParameters extends CarrierTechnologyPara @Override public String toString() { return "RestClientCarrierTechnologyParameters [url=" + url + ", httpMethod=" + httpMethod + ", httpHeaders=" - + Arrays.deepToString(httpHeaders) + "]"; + + Arrays.deepToString(httpHeaders) + ", httpCodeFilter=" + httpCodeFilter + "]"; } } -- cgit 1.2.3-korg