actor: APPC
recipe: Restart
target:
- type: VM
+ targetType: VM
retry: 3
timeout: 1200
success: final_success
"policies": [
{
"operational.modifyconfig": {
- "type": "onap.policies.controlloop.Operational",
- "version": "1.0.0",
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
"metadata": {
"policy-id": "operational.modifyconfig"
},
"properties": {
- "controlLoop": {
- "version": "2.0.0",
- "controlLoopName": "ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a",
- "trigger_policy": "unique-policy-id-1-modifyConfig",
- "timeout": 1200,
- "abatement": false
- },
- "policies": [
+ "id": "ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a",
+ "timeout": 1200,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-modifyConfig",
+ "operations": [
{
"id": "unique-policy-id-1-modifyConfig",
- "name": "modify packet gen config",
- "description": null,
- "actor": "APPC",
- "recipe": "ModifyConfig",
- "target": {
- "resourceID": "Eace933104d443b496b8.nodes.heat.vpg",
- "type": "VNF"
+ "description": "Modify the packet generator",
+ "operation": {
+ "actor": "APPC",
+ "operation": "ModifyConfig",
+ "target": {
+ "targetType": "VNF",
+ "entityId": {
+ "resourceID": "bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38"
+ }
+ }
},
- "retry": 0,
"timeout": 300,
+ "retries": 0,
"success": "final_success",
"failure": "final_failure",
"failure_timeout": "final_failure_timeout",
"failure_exception": "final_failure_exception",
"failure_guard": "final_failure_guard"
}
- ]
+ ],
+ "controllerName": "usecases"
}
}
}
onap.policies.controlloop.Operational:
derived_from: tosca.policies.Root
version: 1.0.0
- description: Operational Policy for Control Loops
\ No newline at end of file
+ description: Operational Policy for Control Loops Supporting Legacy YAML Policy Definition.
\ No newline at end of file
onap.policies.controlloop.operational.Common:
derived_from: tosca.policies.Root
version: 1.0.0
- description: Operational Policy for Control Loop execution
+ description: |
+ Operational Policy for Control Loop execution. Originated in Frankfurt to support TOSCA Compliant
+ Policy Types. This does NOT support the legacy Policy YAML policy type.
properties:
id:
type: string
entry_schema:
type: onap.datatype.controlloop.Operation
- onap.policies.controlloop.operational.common.Drools:
- derived_from: onap.policies.controlloop.operational.Common
- type_version: 1.0.0
- version: 1.0.0
- description: Operational policies for Drools PDP
- properties:
- controllerName:
- type: string
- description: Drools controller properties
- required: false
-
data_types:
- # TBD if this is needed
- onap.datatype.controlloop.operation.Failure:
- derived_from: tosca.datatypes.Root
- description: Captures information of an operational failure performed for control loop
- properties:
- messages:
- type: string
- description: error message
- required: true
- category:
- type: string
- description: |
- The category the error occurred in. Whether this is a general error from the actor, or the operation
- timed out, retries were exhausted in trying to execute the operation, a guard policy prevented the
- operation from occuring, or an exception in the system caused the failure.
- constraints:
- - valid_values: [error, timeout, retries, guard, exception]
-
onap.datatype.controlloop.Target:
derived_from: tosca.datatypes.Root
description: Definition for a entity in A&AI to perform a control loop operation on
Map of values that identify the resource. If none are provided, it is assumed that the
entity that generated the ONSET event will be the target.
required: false
+ entry_schema:
+ type: string
onap.datatype.controlloop.Actor:
derived_from: tosca.datatypes.Root
description: The operation the actor is performing.
required: true
target:
- type: string
+ type: onap.datatype.controlloop.Target
description: The resource the operation should be performed on.
required: true
metadata:
policy_types:
onap.policies.controlloop.operational.common.Apex:
derived_from: onap.policies.controlloop.operational.Common
+ type_version: 1.0.0
version: 1.0.0
description: Operational policies for Apex PDP
properties:
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+policy_types:
+ onap.policies.controlloop.operational.common.Drools:
+ derived_from: onap.policies.controlloop.operational.Common
+ type_version: 1.0.0
+ version: 1.0.0
+ description: Operational policies for Drools PDP
+ properties:
+ controllerName:
+ type: string
+ description: Drools controller properties
+ required: false
+
policy_types:
onap.policies.Monitoring:
derived_from: tosca.policies.Root
- description: a base policy type for all policies that governs monitoring provisioning
+ version: 1.0.0
+ description: a base policy type for all policies that govern monitoring provisioning
onap.policies.monitoring.cdap.tca.hi.lo.app:
derived_from: onap.policies.Monitoring
version: 1.0.0
policy_types:
onap.policies.Monitoring:
derived_from: tosca.policies.Root
- description: a base policy type for all policies that govern monitoring provision
+ description: a base policy type for all policies that govern monitoring provisioning
version: 1.0.0
onap.policies.monitoring.dcaegen2.collectors.datafile.datafile-app-server:
- derived_from: policy.nodes.Root
+ derived_from: onap.policies.Monitoring
version: 1.0.0
properties:
buscontroller_feed_publishing_endpoint:
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (C) 2018 Huawei Intellectual Property. All rights reserved.
+ Modifications Copyright (C) 2019-2020 Nordix Foundation.
+ Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+ <artifactId>model-actors</artifactId>
+ <version>2.2.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>actor.aai</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+ <artifactId>actorServiceProvider</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+ <artifactId>aai</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+ <artifactId>events</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.common</groupId>
+ <artifactId>policy-endpoints</artifactId>
+ <version>${policy.common.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+ <artifactId>actor.test</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions</groupId>
+ <artifactId>simulators</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpActor;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+
+/**
+ * A&AI Actor.
+ */
+public class AaiActorServiceProvider extends HttpActor {
+ public static final String NAME = AaiConstants.ACTOR_NAME;
+
+ /**
+ * Constructs the object.
+ */
+ public AaiActorServiceProvider() {
+ super(NAME);
+
+ addOperator(HttpOperator.makeOperator(NAME, AaiCustomQueryOperation.NAME,
+ AaiCustomQueryOperation::new));
+
+ // add all "get" operators
+ for (String operation : AaiGetOperation.OPERATIONS) {
+ addOperator(HttpOperator.makeOperator(NAME, operation, AaiGetOperation::new));
+ }
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A&AI Custom Query. Stores the {@link AaiCqResponse} in the context. In addition, if the
+ * context does not contain the "tenant" data for the vserver, then it will request that,
+ * as well.
+ */
+public class AaiCustomQueryOperation extends HttpOperation<String> {
+ private static final Logger logger = LoggerFactory.getLogger(AaiCustomQueryOperation.class);
+
+ public static final String NAME = "CustomQuery";
+
+ public static final String RESOURCE_LINK = "resource-link";
+ public static final String RESULT_DATA = "result-data";
+
+ private static final String PREFIX = "/aai/v16";
+
+ /**
+ * Constructs the object.
+ *
+ * @param params operation parameters
+ * @param operator operator that created this operation
+ */
+ public AaiCustomQueryOperation(ControlLoopOperationParams params, HttpOperator operator) {
+ super(params, operator, String.class);
+ }
+
+ /**
+ * Queries the vserver, if necessary.
+ */
+ @Override
+ protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+ String vserver = params.getTargetEntity();
+
+ ControlLoopOperationParams tenantParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
+ .operation(AaiGetOperation.TENANT).payload(null).retry(null).timeoutSec(null).build();
+
+ return params.getContext().obtain(AaiGetOperation.getTenantKey(vserver), tenantParams);
+ }
+
+ @Override
+ protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
+
+ Map<String, String> request = makeRequest();
+
+ Entity<Map<String, String>> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
+
+ Map<String, Object> headers = makeHeaders();
+
+ headers.put("Accept", MediaType.APPLICATION_JSON);
+ String url = makeUrl();
+
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+
+ // @formatter:off
+ return handleResponse(outcome, url,
+ callback -> operator.getClient().put(callback, makePath(), entity, headers));
+ // @formatter:on
+ }
+
+ /**
+ * Constructs the custom query using the previously retrieved tenant data.
+ */
+ private Map<String, String> makeRequest() {
+ String vserver = params.getTargetEntity();
+ StandardCoderObject tenant = params.getContext().getProperty(AaiGetOperation.getTenantKey(vserver));
+
+ String resourceLink = tenant.getString(RESULT_DATA, 0, RESOURCE_LINK);
+ if (resourceLink == null) {
+ throw new IllegalArgumentException("cannot perform custom query - no resource-link");
+ }
+
+ resourceLink = resourceLink.replace(PREFIX, "");
+
+ return Map.of("start", resourceLink, "query", "query/closed-loop");
+ }
+
+ @Override
+ protected Map<String, Object> makeHeaders() {
+ return AaiUtil.makeHeaders(params);
+ }
+
+ /**
+ * Injects the response into the context.
+ */
+ @Override
+ protected void postProcessResponse(OperationOutcome outcome, String url, Response rawResponse, String response) {
+
+ logger.info("{}: caching response for {}", getFullName(), params.getRequestId());
+ params.getContext().setProperty(AaiCqResponse.CONTEXT_KEY, new AaiCqResponse(response));
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Superclass of A&AI operators that use "get" to perform their request and store their
+ * response within the context as a {@link StandardCoderObject}. The property name under
+ * which they are stored is ${actor}.${operation}.${targetEntity}.
+ */
+public class AaiGetOperation extends HttpOperation<StandardCoderObject> {
+ private static final Logger logger = LoggerFactory.getLogger(AaiGetOperation.class);
+
+ public static final int DEFAULT_RETRY = 3;
+
+ // operation names
+ public static final String TENANT = "Tenant";
+
+ // property prefixes
+ private static final String TENANT_KEY_PREFIX = AaiConstants.CONTEXT_PREFIX + TENANT + ".";
+
+ /**
+ * Operation names supported by this operator.
+ */
+ public static final Set<String> OPERATIONS = Set.of(TENANT);
+
+
+ /**
+ * Responses that are retrieved from A&AI are placed in the operation context under
+ * the name "${propertyPrefix}.${targetEntity}".
+ */
+ private final String propertyPrefix;
+
+ /**
+ * Constructs the object.
+ *
+ * @param params operation parameters
+ * @param operator operator that created this operation
+ */
+ public AaiGetOperation(ControlLoopOperationParams params, HttpOperator operator) {
+ super(params, operator, StandardCoderObject.class);
+ this.propertyPrefix = operator.getFullName() + ".";
+ }
+
+ /**
+ * Gets the "context key" for the tenant query response associated with the given
+ * target entity.
+ *
+ * @param targetEntity target entity
+ * @return the "context key" for the response associated with the given target
+ */
+ public static String getTenantKey(String targetEntity) {
+ return (TENANT_KEY_PREFIX + targetEntity);
+ }
+
+ @Override
+ protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
+
+ Map<String, Object> headers = makeHeaders();
+
+ headers.put("Accept", MediaType.APPLICATION_JSON);
+ String url = makeUrl();
+
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
+
+ // @formatter:off
+ return handleResponse(outcome, url,
+ callback -> operator.getClient().get(callback, makePath(), headers));
+ // @formatter:on
+ }
+
+ @Override
+ protected Map<String, Object> makeHeaders() {
+ return AaiUtil.makeHeaders(params);
+ }
+
+ @Override
+ public String makePath() {
+ return (operator.getPath() + "/" + params.getTargetEntity());
+ }
+
+ /**
+ * Injects the response into the context.
+ */
+ @Override
+ protected void postProcessResponse(OperationOutcome outcome, String url, Response rawResponse,
+ StandardCoderObject response) {
+ String entity = params.getTargetEntity();
+
+ logger.info("{}: caching response of {} for {}", getFullName(), entity, params.getRequestId());
+
+ params.getContext().setProperty(propertyPrefix + entity, response);
+ }
+
+ /**
+ * Provides a default retry value, if none specified.
+ */
+ @Override
+ protected int getRetry(Integer retry) {
+ return (retry == null ? DEFAULT_RETRY : retry);
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+
+/**
+ * Utilities used by A&AI classes.
+ */
+public class AaiUtil {
+
+ private AaiUtil() {
+ // do nothing
+ }
+
+ /**
+ * Makes standard request headers for A&AI requests.
+ *
+ * @param params operation parameters
+ * @return new request headers
+ */
+ public static Map<String, Object> makeHeaders(ControlLoopOperationParams params) {
+ Map<String, Object> headers = new HashMap<>();
+
+ headers.put("X-FromAppId", "POLICY");
+ headers.put("X-TransactionId", params.getRequestId().toString());
+
+ return headers;
+ }
+}
--- /dev/null
+org.onap.policy.controlloop.actor.aai.AaiActorServiceProvider
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class AaiActorServiceProviderTest {
+
+ @Test
+ public void testAaiActorServiceProvider() {
+ final AaiActorServiceProvider prov = new AaiActorServiceProvider();
+
+ // verify that it has the operators we expect
+ List<String> expected = new LinkedList<>();
+ expected.add(AaiCustomQueryOperation.NAME);
+ expected.addAll(AaiGetOperation.OPERATIONS);
+
+ Collections.sort(expected);
+
+ var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
+
+ assertEquals(expected.toString(), actual.toString());
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.policy.PolicyResult;
+
+public class AaiCustomQueryOperationTest extends BasicAaiOperator<Map<String, String>> {
+ private static final StandardCoder coder = new StandardCoder();
+
+ private static final String MY_LINK = "my-link";
+
+ @Mock
+ private Actor tenantActor;
+
+ private AaiCustomQueryOperation oper;
+
+ public AaiCustomQueryOperationTest() {
+ super(AaiConstants.ACTOR_NAME, AaiCustomQueryOperation.NAME);
+ }
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MyTenantOperator tenantOperator = new MyTenantOperator();
+
+ when(service.getActor(AaiConstants.ACTOR_NAME)).thenReturn(tenantActor);
+ when(tenantActor.getOperator(AaiGetOperation.TENANT)).thenReturn(tenantOperator);
+
+ oper = new AaiCustomQueryOperation(params, operator);
+ }
+
+ @Test
+ public void testAaiCustomQueryOperation() {
+ assertEquals(AaiConstants.ACTOR_NAME, oper.getActorName());
+ assertEquals(AaiCustomQueryOperation.NAME, oper.getName());
+ }
+
+ @Test
+ public void testStartOperationAsync_testStartPreprocessorAsync_testMakeRequest_testPostProcess() throws Exception {
+ // need two responses
+ when(rawResponse.readEntity(String.class)).thenReturn(makeTenantReply()).thenReturn(makeCqReply());
+ when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+ when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+
+ CompletableFuture<OperationOutcome> future2 = oper.start();
+
+ assertEquals(PolicyResult.SUCCESS, getResult(future2));
+
+ // tenant response should have been cached within the context
+ assertNotNull(context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY)));
+
+ // custom query response should have been cached within the context
+ AaiCqResponse cqData = context.getProperty(AaiCqResponse.CONTEXT_KEY);
+ assertNotNull(cqData);
+ }
+
+ /**
+ * Tests when preprocessor step is not needed.
+ */
+ @Test
+ public void testStartOperationAsync_testStartPreprocessorAsyncNotNeeded() throws Exception {
+ // pre-load the tenant data
+ final StandardCoderObject data = preloadTenantData();
+
+ // only need one response
+ when(rawResponse.readEntity(String.class)).thenReturn(makeCqReply());
+ when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+
+ CompletableFuture<OperationOutcome> future2 = oper.start();
+
+ assertEquals(PolicyResult.SUCCESS, getResult(future2));
+
+ // should not have replaced tenant response
+ assertSame(data, context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY)));
+
+ // custom query response should have been cached within the context
+ AaiCqResponse cqData = context.getProperty(AaiCqResponse.CONTEXT_KEY);
+ assertNotNull(cqData);
+ }
+
+ @Test
+ public void testMakeHeaders() {
+ verifyHeaders(oper.makeHeaders());
+ }
+
+ @Test
+ public void testMakeRequestNoResourceLink() throws Exception {
+ // pre-load EMPTY tenant data
+ preloadTenantData(new StandardCoderObject());
+
+ when(rawResponse.readEntity(String.class)).thenReturn(makeCqReply());
+ when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+
+ CompletableFuture<OperationOutcome> future2 = oper.start();
+
+ assertEquals(PolicyResult.FAILURE_EXCEPTION, getResult(future2));
+ }
+
+ private String makeTenantReply() throws Exception {
+ Map<String, String> links = Map.of(AaiCustomQueryOperation.RESOURCE_LINK, MY_LINK);
+ List<Map<String, String>> data = Arrays.asList(links);
+
+ Map<String, Object> reply = Map.of(AaiCustomQueryOperation.RESULT_DATA, data);
+ return coder.encode(reply);
+ }
+
+ private String makeCqReply() {
+ return "{}";
+ }
+
+ private StandardCoderObject preloadTenantData() throws Exception {
+ StandardCoderObject data = coder.decode(makeTenantReply(), StandardCoderObject.class);
+ preloadTenantData(data);
+ return data;
+ }
+
+ private void preloadTenantData(StandardCoderObject data) {
+ context.setProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY), data);
+ }
+
+ private PolicyResult getResult(CompletableFuture<OperationOutcome> future2)
+ throws InterruptedException, ExecutionException, TimeoutException {
+
+ executor.runAll(100);
+ assertTrue(future2.isDone());
+
+ return future2.get().getResult();
+ }
+
+ protected class MyTenantOperator extends HttpOperator {
+ public MyTenantOperator() {
+ super(AaiConstants.ACTOR_NAME, AaiGetOperation.TENANT);
+
+ HttpParams http = HttpParams.builder().clientName(MY_CLIENT).path(PATH).timeoutSec(1).build();
+
+ configure(Util.translateToMap(AaiGetOperation.TENANT, http));
+ start();
+ }
+
+ @Override
+ public Operation buildOperation(ControlLoopOperationParams params) {
+ return new AaiGetOperation(params, this);
+ }
+
+ @Override
+ protected HttpClientFactory getClientFactory() {
+ return factory;
+ }
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.policy.PolicyResult;
+
+public class AaiGetOperatorTest extends BasicAaiOperator<Void> {
+
+ private static final String INPUT_FIELD = "input";
+ private static final String TEXT = "my-text";
+
+ private AaiGetOperation oper;
+
+ public AaiGetOperatorTest() {
+ super(AaiConstants.ACTOR_NAME, AaiGetOperation.TENANT);
+ }
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ oper = new AaiGetOperation(params, operator);
+ }
+
+ @Test
+ public void testGetRetry() {
+ // use default if null retry
+ assertEquals(AaiGetOperation.DEFAULT_RETRY, oper.getRetry(null));
+
+ // otherwise, use specified value
+ assertEquals(0, oper.getRetry(0));
+ assertEquals(10, oper.getRetry(10));
+ }
+
+ @Test
+ public void testStartOperationAsync_testStartQueryAsync_testPostProcessResponse() throws Exception {
+
+ // return a map in the reply
+ Map<String, String> reply = Map.of(INPUT_FIELD, TEXT);
+ when(rawResponse.readEntity(String.class)).thenReturn(new StandardCoder().encode(reply));
+
+ when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+
+ CompletableFuture<OperationOutcome> future2 = oper.startOperationAsync(1, outcome);
+ assertFalse(future2.isDone());
+
+ executor.runAll(100);
+ assertTrue(future2.isDone());
+
+ assertEquals(PolicyResult.SUCCESS, future2.get().getResult());
+
+ // data should have been cached within the context
+ StandardCoderObject data = context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY));
+ assertNotNull(data);
+ assertEquals(TEXT, data.getString(INPUT_FIELD));
+ }
+
+ /**
+ * Tests startOperationAsync() when there's a failure.
+ */
+ @Test
+ public void testStartOperationAsyncFailure() throws Exception {
+
+ when(rawResponse.getStatus()).thenReturn(500);
+ when(rawResponse.readEntity(String.class)).thenReturn("");
+
+ when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+
+ CompletableFuture<OperationOutcome> future2 = oper.startOperationAsync(1, outcome);
+ assertFalse(future2.isDone());
+
+ executor.runAll(100);
+ assertTrue(future2.isDone());
+
+ assertEquals(PolicyResult.FAILURE, future2.get().getResult());
+
+ // data should NOT have been cached within the context
+ assertNull(context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY)));
+ }
+
+ @Test
+ public void testMakeHeaders() {
+ verifyHeaders(oper.makeHeaders());
+ }
+
+ @Test
+ public void testMakePath() {
+ assertEquals(PATH + "/" + TARGET_ENTITY, oper.makePath());
+ }
+
+ @Test
+ public void testAaiGetOperator() {
+ assertEquals(AaiConstants.ACTOR_NAME, oper.getActorName());
+ assertEquals(AaiGetOperation.TENANT, oper.getName());
+ }
+
+ @Test
+ public void testGetTenantKey() {
+ assertEquals("AAI.Tenant." + TARGET_ENTITY, AaiGetOperation.getTenantKey(TARGET_ENTITY));
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import java.util.Map;
+import org.junit.Test;
+
+public class AaiUtilTest extends BasicAaiOperator<Void> {
+
+ @Test
+ public void testMakeHeaders() {
+ makeContext();
+
+ Map<String, Object> headers = AaiUtil.makeHeaders(params);
+
+ verifyHeaders(headers);
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+import org.onap.policy.controlloop.actor.test.BasicHttpOperation;
+
+/**
+ * Superclass for various operator tests.
+ */
+public abstract class BasicAaiOperator<Q> extends BasicHttpOperation<Q> {
+
+ /**
+ * Constructs the object using a default actor and operation name.
+ */
+ public BasicAaiOperator() {
+ super();
+ }
+
+ /**
+ * Constructs the object.
+ *
+ * @param actor actor name
+ * @param operation operation name
+ */
+ public BasicAaiOperator(String actor, String operation) {
+ super(actor, operation);
+ }
+
+ protected void verifyHeaders(Map<String, Object> headers) {
+ assertEquals("POLICY", headers.get("X-FromAppId").toString());
+ assertEquals(params.getRequestId().toString(), headers.get("X-TransactionId"));
+ }
+}
============LICENSE_END=========================================================
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
- <artifactId>model-actors</artifactId>
- <version>2.2.1-SNAPSHOT</version>
- </parent>
+ <parent>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+ <artifactId>model-actors</artifactId>
+ <version>2.2.1-SNAPSHOT</version>
+ </parent>
- <artifactId>actor.appc</artifactId>
+ <artifactId>actor.appc</artifactId>
- <dependencies>
- <dependency>
- <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
- <artifactId>actorServiceProvider</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
- <artifactId>appc</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
- <artifactId>events</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.onap.policy.models.policy-models-interactions</groupId>
- <artifactId>simulators</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.onap.policy.common</groupId>
- <artifactId>policy-endpoints</artifactId>
- <version>${policy.common.version}</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+ <artifactId>actorServiceProvider</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+ <artifactId>appc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+ <artifactId>aai</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+ <artifactId>events</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+ <artifactId>actor.aai</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions</groupId>
+ <artifactId>simulators</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.common</groupId>
+ <artifactId>policy-endpoints</artifactId>
+ <version>${policy.common.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
</project>
import org.onap.policy.common.utils.coder.StandardCoder;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
-import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
+import org.onap.policy.controlloop.actorserviceprovider.impl.BidirectionalTopicActor;
import org.onap.policy.controlloop.policy.Policy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class AppcActorServiceProvider extends ActorImpl {
+public class AppcActorServiceProvider extends BidirectionalTopicActor {
private static final String NAME = "APPC";
private static final Logger logger = LoggerFactory.getLogger(AppcActorServiceProvider.class);
+ // TODO old code: remove lines down to **HERE**
+
private static final StandardCoder coder = new StandardCoder();
// Strings for targets
private static final String RECIPE_MODIFY = "ModifyConfig";
private static final ImmutableList<String> recipes =
- ImmutableList.of(RECIPE_RESTART, RECIPE_REBUILD, RECIPE_MIGRATE, RECIPE_MODIFY);
+ ImmutableList.of(RECIPE_RESTART, RECIPE_REBUILD, RECIPE_MIGRATE, RECIPE_MODIFY);
private static final ImmutableMap<String, List<String>> targets = new ImmutableMap.Builder<String, List<String>>()
- .put(RECIPE_RESTART, ImmutableList.of(TARGET_VM)).put(RECIPE_REBUILD, ImmutableList.of(TARGET_VM))
- .put(RECIPE_MIGRATE, ImmutableList.of(TARGET_VM)).put(RECIPE_MODIFY, ImmutableList.of(TARGET_VNF)).build();
+ .put(RECIPE_RESTART, ImmutableList.of(TARGET_VM)).put(RECIPE_REBUILD, ImmutableList.of(TARGET_VM))
+ .put(RECIPE_MIGRATE, ImmutableList.of(TARGET_VM)).put(RECIPE_MODIFY, ImmutableList.of(TARGET_VNF))
+ .build();
private static final ImmutableMap<String, List<String>> payloads = new ImmutableMap.Builder<String, List<String>>()
- .put(RECIPE_MODIFY, ImmutableList.of("generic-vnf.vnf-id")).build();
+ .put(RECIPE_MODIFY, ImmutableList.of("generic-vnf.vnf-id")).build();
+
+ // **HERE**
+ /**
+ * Constructs the object.
+ */
public AppcActorServiceProvider() {
super(NAME);
}
+
+ // TODO old code: remove lines down to **HERE**
+
@Override
public String actor() {
return NAME;
}
/**
- * Constructs an APPC request conforming to the legacy API. The legacy API will be deprecated in
- * future releases as all legacy functionality is moved into the LCM API.
+ * Constructs an APPC request conforming to the legacy API. The legacy API will be
+ * deprecated in future releases as all legacy functionality is moved into the LCM
+ * API.
*
* @param onset the event that is reporting the alert for policy to perform an action
- * @param operation the control loop operation specifying the actor, operation, target, etc.
- * @param policy the policy the was specified from the yaml generated by CLAMP or through the
- * Policy GUI/API
+ * @param operation the control loop operation specifying the actor, operation,
+ * target, etc.
+ * @param policy the policy the was specified from the yaml generated by CLAMP or
+ * through the Policy GUI/API
* @return an APPC request conforming to the legacy API
*/
public static Request constructRequest(VirtualControlLoopEvent onset, ControlLoopOperation operation, Policy policy,
- String targetVnf) {
+ String targetVnf) {
/*
* Construct an APPC request
*/
}
}
+ // **HERE**
}
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
super(params, operator, SdncResponse.class);
}
+ /**
+ * Starts the GUARD.
+ */
+ @Override
+ protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+ return startGuardAsync();
+ }
+
@Override
protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
headers.put("Accept", MediaType.APPLICATION_JSON);
String url = makeUrl();
- logRestRequest(url, request);
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
// @formatter:off
return handleResponse(outcome, url,
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction;
import org.onap.policy.common.utils.coder.CoderException;
verify(client).post(callbackCaptor.capture(), any(), requestCaptor.capture(), any());
callbackCaptor.getValue().completed(rawResponse);
- assertEquals(PolicyResult.SUCCESS, future2.get(5, TimeUnit.SECONDS).getResult());
+ executor.runAll(100);
+ assertTrue(future2.isDone());
+
+ assertEquals(PolicyResult.SUCCESS, future2.get().getResult());
return requestCaptor.getValue().getEntity();
}
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.onap.policy.common</groupId>
+ <artifactId>utils-test</artifactId>
+ <version>${policy.common.version}</version>
+ </dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import org.onap.policy.common.endpoints.http.client.HttpClient;
import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.utils.time.PseudoExecutor;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
import org.onap.policy.controlloop.actorserviceprovider.ActorService;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
protected VirtualControlLoopEvent event;
protected ControlLoopEventContext context;
protected OperationOutcome outcome;
+ protected PseudoExecutor executor;
/**
* Constructs the object using a default actor and operation name.
future = new CompletableFuture<>();
when(client.getBaseUrl()).thenReturn(BASE_URI);
+ executor = new PseudoExecutor();
+
makeContext();
outcome = params.makeOutcome();
/**
* Reinitializes {@link #enrichment}, {@link #event}, {@link #context}, and
* {@link #params}.
+ * <p/>
+ * Note: {@link #params} is configured to use {@link #executor}.
*/
protected void makeContext() {
enrichment = new TreeMap<>(makeEnrichment());
context = new ControlLoopEventContext(event);
- params = ControlLoopOperationParams.builder().context(context).actorService(service).actor(actorName)
- .operation(operationName).targetEntity(TARGET_ENTITY).build();
+ params = ControlLoopOperationParams.builder().executor(executor).context(context).actorService(service)
+ .actor(actorName).operation(operationName).targetEntity(TARGET_ENTITY).build();
}
/**
protected Map<String, String> makeEnrichment() {
return new TreeMap<>();
}
+
+ /**
+ * Provides a response to an asynchronous HttpClient call.
+ *
+ * @param response response to be provided to the call
+ * @return a function that provides the response to the call
+ */
+ protected Answer<CompletableFuture<Response>> provideResponse(Response response) {
+ return args -> {
+ InvocationCallback<Response> cb = args.getArgument(0);
+ cb.completed(response);
+ return CompletableFuture.completedFuture(response);
+ };
+ }
}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Response;
import org.junit.Before;
import org.junit.Test;
}
@Test
- public void testSetUp() {
+ public void testSetUp() throws Exception {
assertNotNull(oper.client);
assertSame(oper.client, oper.factory.get(BasicHttpOperation.MY_CLIENT));
assertEquals(200, oper.rawResponse.getStatus());
assertEquals(BasicHttpOperation.BASE_URI, oper.client.getBaseUrl());
assertNotNull(oper.context);
assertNotNull(oper.outcome);
+ assertNotNull(oper.executor);
assertTrue(oper.operator.isAlive());
}
assertSame(oper.context, oper.params.getContext());
assertSame(oper.service, oper.params.getActorService());
+ assertSame(oper.executor, oper.params.getExecutor());
assertEquals(ACTOR, oper.params.getActor());
assertEquals(OPERATION, oper.params.getOperation());
assertEquals(BasicHttpOperation.TARGET_ENTITY, oper.params.getTargetEntity());
assertTrue(oper.makeEnrichment().isEmpty());
}
+ @Test
+ public void testProvideResponse() throws Exception {
+ InvocationCallback<Response> cb = new InvocationCallback<>() {
+ @Override
+ public void completed(Response response) {
+ // do nothing
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ // do nothing
+ }
+ };
+
+
+ when(oper.client.get(any(), any(), any())).thenAnswer(oper.provideResponse(oper.rawResponse));
+
+ assertSame(oper.rawResponse, oper.client.get(cb, null, null).get());
+ }
}
* {@link #start()} to start all of the actors. When finished using the actor service,
* invoke {@link #stop()} or {@link #shutdown()}.
*/
-public class ActorService extends StartConfigPartial<Map<String, Object>> {
+public class ActorService extends StartConfigPartial<Map<String, Map<String, Object>>> {
private static final Logger logger = LoggerFactory.getLogger(ActorService.class);
private final Map<String, Actor> name2actor;
}
@Override
- protected void doConfigure(Map<String, Object> parameters) {
+ protected void doConfigure(Map<String, Map<String, Object>> parameters) {
logger.info("configuring actors");
BeanValidationResult valres = new BeanValidationResult("ActorService", parameters);
for (Actor actor : name2actor.values()) {
String actorName = actor.getName();
- Map<String, Object> subparams = Util.translateToMap(actorName, parameters.get(actorName));
+ Map<String, Object> subparams = parameters.get(actorName);
if (subparams != null) {
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.impl;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onap.policy.common.endpoints.event.comm.client.BidirectionalTopicClientException;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicActorParams;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicHandler;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicManager;
+
+/**
+ * Actor that uses a bidirectional topic. The actor's parameters must be a
+ * {@link BidirectionalTopicActorParams}.
+ */
+public class BidirectionalTopicActor extends ActorImpl implements BidirectionalTopicManager {
+
+ /**
+ * Maps a pair of sink and source topic names to their bidirectional topic.
+ */
+ private final Map<Pair<String, String>, BidirectionalTopicHandler> params2topic = new ConcurrentHashMap<>();
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param name actor's name
+ */
+ public BidirectionalTopicActor(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void doStart() {
+ params2topic.values().forEach(BidirectionalTopicHandler::start);
+ super.doStart();
+ }
+
+ @Override
+ protected void doStop() {
+ params2topic.values().forEach(BidirectionalTopicHandler::stop);
+ super.doStop();
+ }
+
+ @Override
+ protected void doShutdown() {
+ params2topic.values().forEach(BidirectionalTopicHandler::shutdown);
+ params2topic.clear();
+ super.doShutdown();
+ }
+
+ @Override
+ public BidirectionalTopicHandler getTopicHandler(String sinkTopic, String sourceTopic) {
+ Pair<String, String> key = Pair.of(sinkTopic, sourceTopic);
+
+ return params2topic.computeIfAbsent(key, pair -> {
+ try {
+ return makeTopicHandler(sinkTopic, sourceTopic);
+ } catch (BidirectionalTopicClientException e) {
+ throw new IllegalArgumentException(e);
+ }
+ });
+ }
+
+ /**
+ * Translates the parameters to a {@link BidirectionalTopicActorParams} and then
+ * creates a function that will extract operator-specific parameters.
+ */
+ @Override
+ protected Function<String, Map<String, Object>> makeOperatorParameters(Map<String, Object> actorParameters) {
+ String actorName = getName();
+
+ // @formatter:off
+ return Util.translate(actorName, actorParameters, BidirectionalTopicActorParams.class)
+ .doValidation(actorName)
+ .makeOperationParameters(actorName);
+ // @formatter:on
+ }
+
+ // may be overridden by junit tests
+
+ protected BidirectionalTopicHandler makeTopicHandler(String sinkTopic, String sourceTopic)
+ throws BidirectionalTopicClientException {
+
+ return new BidirectionalTopicHandler(sinkTopic, sourceTopic);
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.impl;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+import lombok.Getter;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicHandler;
+import org.onap.policy.controlloop.actorserviceprovider.topic.Forwarder;
+import org.onap.policy.controlloop.policy.PolicyResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Operation that uses a bidirectional topic.
+ *
+ * @param <S> response type
+ */
+@Getter
+public abstract class BidirectionalTopicOperation<Q, S> extends OperationPartial {
+ private static final Logger logger = LoggerFactory.getLogger(BidirectionalTopicOperation.class);
+
+ /**
+ * Response status.
+ */
+ public enum Status {
+ SUCCESS, FAILURE, STILL_WAITING
+ }
+
+ // fields extracted from the operator
+
+ private final BidirectionalTopicHandler topicHandler;
+ private final Forwarder forwarder;
+ private final BidirectionalTopicParams topicParams;
+ private final long timeoutMs;
+
+ /**
+ * Response class.
+ */
+ private final Class<S> responseClass;
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param params operation parameters
+ * @param operator operator that created this operation
+ * @param clazz response class
+ */
+ public BidirectionalTopicOperation(ControlLoopOperationParams params, BidirectionalTopicOperator operator,
+ Class<S> clazz) {
+ super(params, operator);
+ this.topicHandler = operator.getTopicHandler();
+ this.forwarder = operator.getForwarder();
+ this.topicParams = operator.getParams();
+ this.responseClass = clazz;
+ this.timeoutMs = TimeUnit.MILLISECONDS.convert(topicParams.getTimeoutSec(), TimeUnit.SECONDS);
+ }
+
+ /**
+ * If no timeout is specified, then it returns the default timeout.
+ */
+ @Override
+ protected long getTimeoutMs(Integer timeoutSec) {
+ // TODO move this method to the superclass
+ return (timeoutSec == null || timeoutSec == 0 ? this.timeoutMs : super.getTimeoutMs(timeoutSec));
+ }
+
+ /**
+ * Publishes the request and arranges to receive the response.
+ */
+ @Override
+ protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
+
+ final Q request = makeRequest(attempt);
+ final List<String> expectedKeyValues = getExpectedKeyValues(attempt, request);
+
+ final PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
+ final Executor executor = params.getExecutor();
+
+ // register a listener BEFORE publishing
+
+ BiConsumer<String, StandardCoderObject> listener = (rawResponse, scoResponse) -> {
+ OperationOutcome latestOutcome = processResponse(outcome, rawResponse, scoResponse);
+ if (latestOutcome != null) {
+ // final response - complete the controller
+ controller.completeAsync(() -> latestOutcome, executor);
+ }
+ };
+
+ forwarder.register(expectedKeyValues, listener);
+
+ // ensure listener is unregistered if the controller is canceled
+ controller.add(() -> forwarder.unregister(expectedKeyValues, listener));
+
+ // publish the request
+ try {
+ publishRequest(request);
+ } catch (RuntimeException e) {
+ logger.warn("{}: failed to publish request for {}", getFullName(), params.getRequestId());
+ forwarder.unregister(expectedKeyValues, listener);
+ throw e;
+ }
+
+ return controller;
+ }
+
+ /**
+ * Makes the request.
+ *
+ * @param attempt operation attempt
+ * @return a new request
+ */
+ protected abstract Q makeRequest(int attempt);
+
+ /**
+ * Gets values, expected in the response, that should match the selector keys.
+ *
+ * @param attempt operation attempt
+ * @param request request to be published
+ * @return a list of the values to be matched by the selector keys
+ */
+ protected abstract List<String> getExpectedKeyValues(int attempt, Q request);
+
+ /**
+ * Publishes the request. Encodes the request, if it is not already a String.
+ *
+ * @param request request to be published
+ */
+ protected void publishRequest(Q request) {
+ String json;
+ try {
+ if (request instanceof String) {
+ json = request.toString();
+ } else {
+ json = makeCoder().encode(request);
+ }
+ } catch (CoderException e) {
+ throw new IllegalArgumentException("cannot encode request", e);
+ }
+
+ if (!topicHandler.send(json)) {
+ throw new IllegalStateException("nothing published");
+ }
+
+ logMessage(EventType.OUT, topicHandler.getSinkTopicCommInfrastructure(), topicHandler.getSinkTopic(), request);
+ }
+
+ /**
+ * Processes a response.
+ *
+ * @param infra communication infrastructure on which the response was received
+ * @param outcome outcome to be populated
+ * @param response raw response to process
+ * @param scoResponse response, as a {@link StandardCoderObject}
+ * @return the outcome, or {@code null} if still waiting for completion
+ */
+ protected OperationOutcome processResponse(OperationOutcome outcome, String rawResponse,
+ StandardCoderObject scoResponse) {
+
+ logger.info("{}.{}: response received for {}", params.getActor(), params.getOperation(), params.getRequestId());
+
+ logMessage(EventType.IN, topicHandler.getSourceTopicCommInfrastructure(), topicHandler.getSourceTopic(),
+ rawResponse);
+
+ // decode the response
+ S response;
+ if (responseClass == String.class) {
+ response = responseClass.cast(rawResponse);
+
+ } else if (responseClass == StandardCoderObject.class) {
+ response = responseClass.cast(scoResponse);
+
+ } else {
+ try {
+ response = makeCoder().decode(rawResponse, responseClass);
+ } catch (CoderException e) {
+ logger.warn("{}.{} cannot decode response for {}", params.getActor(), params.getOperation(),
+ params.getRequestId());
+ throw new IllegalArgumentException("cannot decode response", e);
+ }
+ }
+
+ // check its status
+ switch (detmStatus(rawResponse, response)) {
+ case SUCCESS:
+ logger.info("{}.{} request succeeded for {}", params.getActor(), params.getOperation(),
+ params.getRequestId());
+ setOutcome(outcome, PolicyResult.SUCCESS);
+ postProcessResponse(outcome, rawResponse, response);
+ return outcome;
+
+ case FAILURE:
+ logger.info("{}.{} request failed for {}", params.getActor(), params.getOperation(),
+ params.getRequestId());
+ return setOutcome(outcome, PolicyResult.FAILURE);
+
+ case STILL_WAITING:
+ default:
+ logger.info("{}.{} request incomplete for {}", params.getActor(), params.getOperation(),
+ params.getRequestId());
+ return null;
+ }
+ }
+
+ /**
+ * Processes a successful response.
+ *
+ * @param outcome outcome to be populated
+ * @param rawResponse raw response
+ * @param response decoded response
+ */
+ protected void postProcessResponse(OperationOutcome outcome, String rawResponse, S response) {
+ // do nothing
+ }
+
+ /**
+ * Determines the status of the response.
+ *
+ * @param rawResponse raw response
+ * @param response decoded response
+ * @return the status of the response
+ */
+ protected abstract Status detmStatus(String rawResponse, S response);
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import lombok.Getter;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicHandler;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicManager;
+import org.onap.policy.controlloop.actorserviceprovider.topic.Forwarder;
+import org.onap.policy.controlloop.actorserviceprovider.topic.SelectorKey;
+
+/**
+ * Operator that uses a bidirectional topic. Topic operators may share a
+ * {@link BidirectionalTopicHandler}.
+ */
+public abstract class BidirectionalTopicOperator extends OperatorPartial {
+
+ /**
+ * Manager from which to get the topic handlers.
+ */
+ private final BidirectionalTopicManager topicManager;
+
+ /**
+ * Keys used to extract the fields used to select responses for this operator.
+ */
+ private final List<SelectorKey> selectorKeys;
+
+ /*
+ * The remaining fields are initialized when configure() is invoked, thus they may
+ * change.
+ */
+
+ /**
+ * Current parameters. While {@link params} may change, the values contained within it
+ * will not, thus operations may copy it.
+ */
+ @Getter
+ private BidirectionalTopicParams params;
+
+ /**
+ * Topic handler associated with the parameters.
+ */
+ @Getter
+ private BidirectionalTopicHandler topicHandler;
+
+ /**
+ * Forwarder associated with the parameters.
+ */
+ @Getter
+ private Forwarder forwarder;
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param actorName name of the actor with which this operator is associated
+ * @param name operation name
+ * @param topicManager manager from which to get the topic handler
+ * @param selectorKeys keys used to extract the fields used to select responses for
+ * this operator
+ */
+ public BidirectionalTopicOperator(String actorName, String name, BidirectionalTopicManager topicManager,
+ List<SelectorKey> selectorKeys) {
+ super(actorName, name);
+ this.topicManager = topicManager;
+ this.selectorKeys = selectorKeys;
+ }
+
+ @Override
+ protected void doConfigure(Map<String, Object> parameters) {
+ params = Util.translate(getFullName(), parameters, BidirectionalTopicParams.class);
+ ValidationResult result = params.validate(getFullName());
+ if (!result.isValid()) {
+ throw new ParameterValidationRuntimeException("invalid parameters", result);
+ }
+
+ topicHandler = topicManager.getTopicHandler(params.getSinkTopic(), params.getSourceTopic());
+ forwarder = topicHandler.addForwarder(selectorKeys);
+ }
+
+ /**
+ * Makes an operator that will construct operations.
+ *
+ * @param <Q> request type
+ * @param <S> response type
+ * @param actorName actor name
+ * @param operation operation name
+ * @param topicManager manager from which to get the topic handler
+ * @param operationMaker function to make an operation
+ * @param keys keys used to extract the fields used to select responses for this
+ * operator
+ * @return a new operator
+ */
+ // @formatter:off
+ public static <Q, S> BidirectionalTopicOperator makeOperator(String actorName, String operation,
+ BidirectionalTopicManager topicManager,
+ BiFunction<ControlLoopOperationParams, BidirectionalTopicOperator,
+ BidirectionalTopicOperation<Q, S>> operationMaker,
+ SelectorKey... keys) {
+ // @formatter:off
+
+ return makeOperator(actorName, operation, topicManager, Arrays.asList(keys), operationMaker);
+ }
+
+ /**
+ * Makes an operator that will construct operations.
+ *
+ * @param <Q> request type
+ * @param <S> response type
+ * @param actorName actor name
+ * @param operation operation name
+ * @param topicManager manager from which to get the topic handler
+ * @param keys keys used to extract the fields used to select responses for
+ * this operator
+ * @param operationMaker function to make an operation
+ * @return a new operator
+ */
+ // @formatter:off
+ public static <Q,S> BidirectionalTopicOperator makeOperator(String actorName, String operation,
+ BidirectionalTopicManager topicManager,
+ List<SelectorKey> keys,
+ BiFunction<ControlLoopOperationParams, BidirectionalTopicOperator,
+ BidirectionalTopicOperation<Q,S>> operationMaker) {
+ // @formatter:on
+
+ return new BidirectionalTopicOperator(actorName, operation, topicManager, keys) {
+ @Override
+ public synchronized Operation buildOperation(ControlLoopOperationParams params) {
+ return operationMaker.apply(params, this);
+ }
+ };
+ }
+}
import org.onap.policy.common.endpoints.http.client.HttpClient;
import org.onap.policy.common.endpoints.utils.NetLoggerUtil;
import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
-import org.onap.policy.common.utils.coder.Coder;
import org.onap.policy.common.utils.coder.CoderException;
-import org.onap.policy.common.utils.coder.StandardCoder;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
@Getter
public abstract class HttpOperation<T> extends OperationPartial {
private static final Logger logger = LoggerFactory.getLogger(HttpOperation.class);
- private static final Coder coder = new StandardCoder();
/**
* Operator that created this operation.
String strResponse = HttpClient.getBody(rawResponse, String.class);
- logRestResponse(url, strResponse);
+ logMessage(EventType.IN, CommInfrastructure.REST, url, strResponse);
T response;
if (responseClass == String.class) {
try {
response = makeCoder().decode(strResponse, responseClass);
} catch (CoderException e) {
- logger.warn("{}.{} cannot decode response with http error code {} for {}", params.getActor(),
- params.getOperation(), rawResponse.getStatus(), params.getRequestId(), e);
- return setOutcome(outcome, PolicyResult.FAILURE_EXCEPTION);
+ logger.warn("{}.{} cannot decode response for {}", params.getActor(), params.getOperation(),
+ params.getRequestId(), e);
+ throw new IllegalArgumentException("cannot decode response");
}
}
return (rawResponse.getStatus() == 200);
}
- /**
- * Logs a REST request. If the request is not of type, String, then it attempts to
- * pretty-print it into JSON before logging.
- *
- * @param url request URL
- * @param request request to be logged
- */
- public <Q> void logRestRequest(String url, Q request) {
- String json;
- try {
- if (request == null) {
- json = null;
- } else if (request instanceof String) {
- json = request.toString();
- } else {
- json = makeCoder().encode(request, true);
- }
-
- } catch (CoderException e) {
- logger.warn("cannot pretty-print request", e);
- json = request.toString();
- }
-
- NetLoggerUtil.log(EventType.OUT, CommInfrastructure.REST, url, json);
- logger.info("[OUT|{}|{}|]{}{}", CommInfrastructure.REST, url, NetLoggerUtil.SYSTEM_LS, json);
- }
-
- /**
- * Logs a REST response. If the response is not of type, String, then it attempts to
- * pretty-print it into JSON before logging.
- *
- * @param url request URL
- * @param response response to be logged
- */
- public <S> void logRestResponse(String url, S response) {
- String json;
- try {
- if (response == null) {
- json = null;
- } else if (response instanceof String) {
- json = response.toString();
- } else {
- json = makeCoder().encode(response, true);
- }
-
- } catch (CoderException e) {
- logger.warn("cannot pretty-print response", e);
- json = response.toString();
- }
-
- NetLoggerUtil.log(EventType.IN, CommInfrastructure.REST, url, json);
- logger.info("[IN|{}|{}|]{}{}", CommInfrastructure.REST, url, NetLoggerUtil.SYSTEM_LS, json);
- }
-
- // these may be overridden by junit tests
-
- protected Coder makeCoder() {
- return coder;
+ @Override
+ public <Q> String logMessage(EventType direction, CommInfrastructure infra, String sink, Q request) {
+ String json = super.logMessage(direction, infra, sink, request);
+ NetLoggerUtil.log(direction, infra, sink, json);
+ return json;
}
}
package org.onap.policy.controlloop.actorserviceprovider.impl;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.actorserviceprovider.CallbackManager;
import org.onap.policy.controlloop.actorserviceprovider.Operation;
* be done to cancel that particular operation.
*/
public abstract class OperationPartial implements Operation {
-
private static final Logger logger = LoggerFactory.getLogger(OperationPartial.class);
+ private static final Coder coder = new StandardCoder();
public static final long DEFAULT_RETRY_WAIT_MS = 1000L;
// values extracted from the operator
* Similar to {@link CompletableFuture#anyOf(CompletableFuture...)}, but it cancels
* any outstanding futures when one completes.
*
- * @param futures futures for which to wait
- * @return a future to cancel or await an outcome. If this future is canceled, then
- * all of the futures will be canceled
+ * @param futureMakers function to make a future. If the function returns
+ * {@code null}, then no future is created for that function. On the other
+ * hand, if the function throws an exception, then the previously created
+ * functions are canceled and the exception is re-thrown
+ * @return a future to cancel or await an outcome, or {@code null} if no futures were
+ * created. If this future is canceled, then all of the futures will be
+ * canceled
*/
- protected CompletableFuture<OperationOutcome> anyOf(List<CompletableFuture<OperationOutcome>> futures) {
-
- // convert list to an array
- @SuppressWarnings("rawtypes")
- CompletableFuture[] arrFutures = futures.toArray(new CompletableFuture[futures.size()]);
+ protected CompletableFuture<OperationOutcome> anyOf(
+ @SuppressWarnings("unchecked") Supplier<CompletableFuture<OperationOutcome>>... futureMakers) {
- @SuppressWarnings("unchecked")
- CompletableFuture<OperationOutcome> result = anyOf(arrFutures);
- return result;
+ return anyOf(Arrays.asList(futureMakers));
}
/**
- * Same as {@link CompletableFuture#anyOf(CompletableFuture...)}, but it cancels any
- * outstanding futures when one completes.
+ * Similar to {@link CompletableFuture#anyOf(CompletableFuture...)}, but it cancels
+ * any outstanding futures when one completes.
*
- * @param futures futures for which to wait
- * @return a future to cancel or await an outcome. If this future is canceled, then
- * all of the futures will be canceled
+ * @param futureMakers function to make a future. If the function returns
+ * {@code null}, then no future is created for that function. On the other
+ * hand, if the function throws an exception, then the previously created
+ * functions are canceled and the exception is re-thrown
+ * @return a future to cancel or await an outcome, or {@code null} if no futures were
+ * created. If this future is canceled, then all of the futures will be
+ * canceled. Similarly, when this future completes, any incomplete futures
+ * will be canceled
*/
protected CompletableFuture<OperationOutcome> anyOf(
- @SuppressWarnings("unchecked") CompletableFuture<OperationOutcome>... futures) {
+ List<Supplier<CompletableFuture<OperationOutcome>>> futureMakers) {
+
+ PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
+
+ CompletableFuture<OperationOutcome>[] futures =
+ attachFutures(controller, futureMakers, UnaryOperator.identity());
+
+ if (futures.length == 0) {
+ // no futures were started
+ return null;
+ }
if (futures.length == 1) {
return futures[0];
}
- final Executor executor = params.getExecutor();
- final PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- attachFutures(controller, futures);
-
- // @formatter:off
- CompletableFuture.anyOf(futures)
- .thenApply(object -> (OperationOutcome) object)
- .whenCompleteAsync(controller.delayedComplete(), executor);
- // @formatter:on
+ CompletableFuture.anyOf(futures).thenApply(outcome -> (OperationOutcome) outcome)
+ .whenCompleteAsync(controller.delayedComplete(), params.getExecutor());
return controller;
}
/**
- * Similar to {@link CompletableFuture#allOf(CompletableFuture...)}, but it cancels
- * the futures if returned future is canceled. The future returns the "worst" outcome,
- * based on priority (see {@link #detmPriority(OperationOutcome)}).
+ * Similar to {@link CompletableFuture#allOf(CompletableFuture...)}.
*
- * @param futures futures for which to wait
- * @return a future to cancel or await an outcome. If this future is canceled, then
- * all of the futures will be canceled
+ * @param futureMakers function to make a future. If the function returns
+ * {@code null}, then no future is created for that function. On the other
+ * hand, if the function throws an exception, then the previously created
+ * functions are canceled and the exception is re-thrown
+ * @return a future to cancel or await an outcome, or {@code null} if no futures were
+ * created. If this future is canceled, then all of the futures will be
+ * canceled
*/
- protected CompletableFuture<OperationOutcome> allOf(List<CompletableFuture<OperationOutcome>> futures) {
-
- // convert list to an array
- @SuppressWarnings("rawtypes")
- CompletableFuture[] arrFutures = futures.toArray(new CompletableFuture[futures.size()]);
+ protected CompletableFuture<OperationOutcome> allOf(
+ @SuppressWarnings("unchecked") Supplier<CompletableFuture<OperationOutcome>>... futureMakers) {
- @SuppressWarnings("unchecked")
- CompletableFuture<OperationOutcome> result = allOf(arrFutures);
- return result;
+ return allOf(Arrays.asList(futureMakers));
}
/**
- * Same as {@link CompletableFuture#allOf(CompletableFuture...)}, but it cancels the
- * futures if returned future is canceled. The future returns the "worst" outcome,
- * based on priority (see {@link #detmPriority(OperationOutcome)}).
+ * Similar to {@link CompletableFuture#allOf(CompletableFuture...)}.
*
- * @param futures futures for which to wait
- * @return a future to cancel or await an outcome. If this future is canceled, then
- * all of the futures will be canceled
+ * @param futureMakers function to make a future. If the function returns
+ * {@code null}, then no future is created for that function. On the other
+ * hand, if the function throws an exception, then the previously created
+ * functions are canceled and the exception is re-thrown
+ * @return a future to cancel or await an outcome, or {@code null} if no futures were
+ * created. If this future is canceled, then all of the futures will be
+ * canceled. Similarly, when this future completes, any incomplete futures
+ * will be canceled
*/
protected CompletableFuture<OperationOutcome> allOf(
- @SuppressWarnings("unchecked") CompletableFuture<OperationOutcome>... futures) {
-
- if (futures.length == 1) {
- return futures[0];
- }
+ List<Supplier<CompletableFuture<OperationOutcome>>> futureMakers) {
+ PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
- final PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- attachFutures(controller, futures);
+ Queue<OperationOutcome> outcomes = new LinkedList<>();
- OperationOutcome[] outcomes = new OperationOutcome[futures.length];
+ CompletableFuture<OperationOutcome>[] futures =
+ attachFutures(controller, futureMakers, future -> future.thenApply(outcome -> {
+ synchronized (outcomes) {
+ outcomes.add(outcome);
+ }
+ return outcome;
+ }));
- @SuppressWarnings("rawtypes")
- CompletableFuture[] futures2 = new CompletableFuture[futures.length];
+ if (futures.length == 0) {
+ // no futures were started
+ return null;
+ }
- // record the outcomes of each future when it completes
- for (int count = 0; count < futures2.length; ++count) {
- final int count2 = count;
- futures2[count] = futures[count].whenComplete((outcome2, thrown) -> outcomes[count2] = outcome2);
+ if (futures.length == 1) {
+ return futures[0];
}
// @formatter:off
- CompletableFuture.allOf(futures2)
+ CompletableFuture.allOf(futures)
.thenApply(unused -> combineOutcomes(outcomes))
.whenCompleteAsync(controller.delayedComplete(), params.getExecutor());
// @formatter:on
}
/**
- * Attaches the given futures to the controller.
+ * Invokes the functions to create the futures and attaches them to the controller.
*
* @param controller master controller for all of the futures
- * @param futures futures to be attached to the controller
- */
- private void attachFutures(PipelineControllerFuture<OperationOutcome> controller,
- @SuppressWarnings("unchecked") CompletableFuture<OperationOutcome>... futures) {
+ * @param futureMakers futures to be attached to the controller
+ * @param adorn function that "adorns" the future, possible adding onto its pipeline.
+ * Returns the adorned future
+ * @return an array of futures, possibly zero-length. If the array is of size one,
+ * then that one item should be returned instead of the controller
+ */
+ private CompletableFuture<OperationOutcome>[] attachFutures(PipelineControllerFuture<OperationOutcome> controller,
+ List<Supplier<CompletableFuture<OperationOutcome>>> futureMakers,
+ UnaryOperator<CompletableFuture<OperationOutcome>> adorn) {
+
+ if (futureMakers.isEmpty()) {
+ @SuppressWarnings("unchecked")
+ CompletableFuture<OperationOutcome>[] result = new CompletableFuture[0];
+ return result;
+ }
- if (futures.length == 0) {
- throw new IllegalArgumentException("empty list of futures");
+ // the last, unadorned future that is created
+ CompletableFuture<OperationOutcome> lastFuture = null;
+
+ List<CompletableFuture<OperationOutcome>> futures = new ArrayList<>(futureMakers.size());
+
+ // make each future
+ for (var maker : futureMakers) {
+ try {
+ CompletableFuture<OperationOutcome> future = maker.get();
+ if (future == null) {
+ continue;
+ }
+
+ // propagate "stop" to the future
+ controller.add(future);
+
+ futures.add(adorn.apply(future));
+
+ lastFuture = future;
+
+ } catch (RuntimeException e) {
+ logger.warn("{}: exception creating 'future' for {}", getFullName(), params.getRequestId());
+ controller.cancel(false);
+ throw e;
+ }
}
- // attach each task
- for (CompletableFuture<OperationOutcome> future : futures) {
- controller.add(future);
+ @SuppressWarnings("unchecked")
+ CompletableFuture<OperationOutcome>[] result = new CompletableFuture[futures.size()];
+
+ if (result.length == 1) {
+ // special case - return the unadorned future
+ result[0] = lastFuture;
+ return result;
}
+
+ return futures.toArray(result);
}
/**
* @param outcomes outcomes to be examined
* @return the combined outcome
*/
- private OperationOutcome combineOutcomes(OperationOutcome[] outcomes) {
+ private OperationOutcome combineOutcomes(Queue<OperationOutcome> outcomes) {
// identify the outcome with the highest priority
- OperationOutcome outcome = outcomes[0];
+ OperationOutcome outcome = outcomes.remove();
int priority = detmPriority(outcome);
- // start with "1", as we've already dealt with "0"
- for (int count = 1; count < outcomes.length; ++count) {
- OperationOutcome outcome2 = outcomes[count];
+ for (OperationOutcome outcome2 : outcomes) {
int priority2 = detmPriority(outcome2);
if (priority2 > priority) {
}
/**
- * Performs a task, after verifying that the controller is still running. Also checks
- * that the previous outcome was successful, if specified.
+ * Performs a sequence of tasks, stopping if a task fails. A given task's future is
+ * not created until the previous task completes. The pipeline returns the outcome of
+ * the last task executed.
*
- * @param controller overall pipeline controller
- * @param checkSuccess {@code true} to check the previous outcome, {@code false}
- * otherwise
- * @param outcome outcome of the previous task
- * @param task task to be performed
- * @return the task, if everything checks out. Otherwise, it returns an incomplete
- * future and completes the controller instead
+ * @param futureMakers functions to make the futures
+ * @return a future to cancel the sequence or await the outcome
*/
- // @formatter:off
- protected CompletableFuture<OperationOutcome> doTask(
- PipelineControllerFuture<OperationOutcome> controller,
- boolean checkSuccess, OperationOutcome outcome,
- CompletableFuture<OperationOutcome> task) {
- // @formatter:on
+ protected CompletableFuture<OperationOutcome> sequence(
+ @SuppressWarnings("unchecked") Supplier<CompletableFuture<OperationOutcome>>... futureMakers) {
- if (checkSuccess && !isSuccess(outcome)) {
- /*
- * must complete before canceling so that cancel() doesn't cause controller to
- * complete
- */
- controller.complete(outcome);
- task.cancel(false);
- return new CompletableFuture<>();
+ return sequence(Arrays.asList(futureMakers));
+ }
+
+ /**
+ * Performs a sequence of tasks, stopping if a task fails. A given task's future is
+ * not created until the previous task completes. The pipeline returns the outcome of
+ * the last task executed.
+ *
+ * @param futureMakers functions to make the futures
+ * @return a future to cancel the sequence or await the outcome, or {@code null} if
+ * there were no tasks to perform
+ */
+ protected CompletableFuture<OperationOutcome> sequence(
+ List<Supplier<CompletableFuture<OperationOutcome>>> futureMakers) {
+
+ Queue<Supplier<CompletableFuture<OperationOutcome>>> queue = new ArrayDeque<>(futureMakers);
+
+ CompletableFuture<OperationOutcome> nextTask = getNextTask(queue);
+ if (nextTask == null) {
+ // no tasks
+ return null;
+ }
+
+ if (queue.isEmpty()) {
+ // only one task - just return it rather than wrapping it in a controller
+ return nextTask;
}
- return controller.wrap(task);
+ /*
+ * multiple tasks - need a controller to stop whichever task is currently
+ * executing
+ */
+ final PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
+ final Executor executor = params.getExecutor();
+
+ // @formatter:off
+ controller.wrap(nextTask)
+ .thenComposeAsync(nextTaskOnSuccess(controller, queue), executor)
+ .whenCompleteAsync(controller.delayedComplete(), executor);
+ // @formatter:on
+
+ return controller;
}
/**
- * Performs a task, after verifying that the controller is still running. Also checks
- * that the previous outcome was successful, if specified.
+ * Executes the next task in the queue, if the previous outcome was successful.
*
- * @param controller overall pipeline controller
- * @param checkSuccess {@code true} to check the previous outcome, {@code false}
- * otherwise
- * @param task function to start the task to be performed
- * @return a function to perform the task. If everything checks out, then it returns
- * the task. Otherwise, it returns an incomplete future and completes the
- * controller instead
+ * @param controller pipeline controller
+ * @param taskQueue queue of tasks to be performed
+ * @return a future to execute the remaining tasks, or the current outcome, if it's a
+ * failure, or if there are no more tasks
*/
- // @formatter:off
- protected Function<OperationOutcome, CompletableFuture<OperationOutcome>> doTask(
+ private Function<OperationOutcome, CompletableFuture<OperationOutcome>> nextTaskOnSuccess(
PipelineControllerFuture<OperationOutcome> controller,
- boolean checkSuccess,
- Function<OperationOutcome, CompletableFuture<OperationOutcome>> task) {
- // @formatter:on
+ Queue<Supplier<CompletableFuture<OperationOutcome>>> taskQueue) {
return outcome -> {
-
- if (!controller.isRunning()) {
- return new CompletableFuture<>();
+ if (!isSuccess(outcome)) {
+ // return the failure
+ return CompletableFuture.completedFuture(outcome);
}
- if (checkSuccess && !isSuccess(outcome)) {
- controller.complete(outcome);
- return new CompletableFuture<>();
+ CompletableFuture<OperationOutcome> nextTask = getNextTask(taskQueue);
+ if (nextTask == null) {
+ // no tasks - just return the success
+ return CompletableFuture.completedFuture(outcome);
}
- return controller.wrap(task.apply(outcome));
+ // @formatter:off
+ return controller
+ .wrap(nextTask)
+ .thenComposeAsync(nextTaskOnSuccess(controller, taskQueue), params.getExecutor());
+ // @formatter:on
};
}
+ /**
+ * Gets the next task from the queue, skipping those that are {@code null}.
+ *
+ * @param taskQueue task queue
+ * @return the next task, or {@code null} if the queue is now empty
+ */
+ private CompletableFuture<OperationOutcome> getNextTask(
+ Queue<Supplier<CompletableFuture<OperationOutcome>>> taskQueue) {
+
+ Supplier<CompletableFuture<OperationOutcome>> maker;
+
+ while ((maker = taskQueue.poll()) != null) {
+ CompletableFuture<OperationOutcome> future = maker.get();
+ if (future != null) {
+ return future;
+ }
+ }
+
+ return null;
+ }
+
/**
* Sets the start time of the operation and invokes the callback to indicate that the
* operation has started. Does nothing if the pipeline has been stopped.
return (thrown instanceof TimeoutException);
}
+ /**
+ * Logs a response. If the response is not of type, String, then it attempts to
+ * pretty-print it into JSON before logging.
+ *
+ * @param direction IN or OUT
+ * @param infra communication infrastructure on which it was published
+ * @param source source name (e.g., the URL or Topic name)
+ * @param response response to be logged
+ * @return the JSON text that was logged
+ */
+ public <T> String logMessage(EventType direction, CommInfrastructure infra, String source, T response) {
+ String json;
+ try {
+ if (response == null) {
+ json = null;
+ } else if (response instanceof String) {
+ json = response.toString();
+ } else {
+ json = makeCoder().encode(response, true);
+ }
+
+ } catch (CoderException e) {
+ String type = (direction == EventType.IN ? "response" : "request");
+ logger.warn("cannot pretty-print {}", type, e);
+ json = response.toString();
+ }
+
+ logger.info("[{}|{}|{}|]{}{}", direction, infra, source, NetLoggerUtil.SYSTEM_LS, json);
+
+ return json;
+ }
+
// these may be overridden by subclasses or junit tests
/**
protected long getTimeoutMs(Integer timeoutSec) {
return (timeoutSec == null ? 0 : TimeUnit.MILLISECONDS.convert(timeoutSec, TimeUnit.SECONDS));
}
+
+ // these may be overridden by junit tests
+
+ protected Coder makeCoder() {
+ return coder;
+ }
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.parameters;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.common.parameters.annotations.Min;
+
+/**
+ * Parameters used by Actors whose Operators use bidirectional topic.
+ */
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = true)
+public class BidirectionalTopicActorParams extends CommonActorParams {
+
+ /*
+ * Optional, default values that are used if missing from the operation-specific
+ * parameters.
+ */
+
+ /**
+ * Sink topic name to which requests should be published.
+ */
+ private String sinkTopic;
+
+ /**
+ * Source topic name, from which to read responses.
+ */
+ private String sourceTopic;
+
+ /**
+ * Amount of time, in seconds, to wait for the HTTP request to complete. The default
+ * is 90 seconds.
+ */
+ @Min(1)
+ private int timeoutSec = 90;
+}
import org.onap.policy.common.parameters.annotations.NotNull;
/**
- * Parameters used by Operators that connect to a server via DMaaP.
+ * Parameters used by Operators that use a bidirectional topic.
*/
@NotNull
@NotBlank
@Data
@Builder(toBuilder = true)
-public class TopicParams {
+public class BidirectionalTopicParams {
/**
- * Name of the target topic end point to which requests should be published.
+ * Sink topic name to which requests should be published.
*/
- private String target;
+ private String sinkTopic;
/**
- * Source topic end point, from which to read responses.
+ * Source topic name, from which to read responses.
*/
- private String source;
+ private String sourceTopic;
/**
- * Amount of time, in seconds to wait for the response, where zero indicates that it
- * should wait forever. The default is zero.
+ * Amount of time, in seconds to wait for the response.
+ * <p/>
+ * Note: this should NOT have a default value, as it receives its default value from
+ * {@link BidirectionalTopicActorParams}.
*/
- @Min(0)
- @Builder.Default
- private long timeoutSec = 0;
+ @Min(1)
+ private int timeoutSec;
+
/**
- * Validates both the publisher and the subscriber parameters.
+ * Validates the parameters.
*
* @param resultName name of the result
*
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.parameters;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Function;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.common.parameters.BeanValidator;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.common.parameters.annotations.NotNull;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+
+/**
+ * Superclass for Actor parameters that have default values in "this" object, and
+ * operation-specific values in {@link #operation}.
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CommonActorParams {
+
+ /**
+ * Maps the operation name to its parameters.
+ */
+ @NotNull
+ protected Map<String, Map<String, Object>> operation;
+
+
+ /**
+ * Extracts a specific operation's parameters from "this".
+ *
+ * @param name name of the item containing "this"
+ * @return a function to extract an operation's parameters from "this". Note: the
+ * returned function is not thread-safe
+ */
+ public Function<String, Map<String, Object>> makeOperationParameters(String name) {
+
+ Map<String, Object> defaultParams = Util.translateToMap(name, this);
+ defaultParams.remove("operation");
+
+ return operationName -> {
+ Map<String, Object> specificParams = operation.get(operationName);
+ if (specificParams == null) {
+ return null;
+ }
+
+ // start with a copy of defaults and overlay with specific
+ Map<String, Object> subparams = new TreeMap<>(defaultParams);
+ subparams.putAll(specificParams);
+
+ return Util.translateToMap(name + "." + operationName, subparams);
+ };
+ }
+
+ /**
+ * Validates the parameters.
+ *
+ * @param name name of the object containing these parameters
+ * @return "this"
+ * @throws IllegalArgumentException if the parameters are invalid
+ */
+ public CommonActorParams doValidation(String name) {
+ ValidationResult result = validate(name);
+ if (!result.isValid()) {
+ throw new ParameterValidationRuntimeException("invalid parameters", result);
+ }
+
+ return this;
+ }
+
+ /**
+ * Validates the parameters.
+ *
+ * @param resultName name of the result
+ *
+ * @return the validation result
+ */
+ public ValidationResult validate(String resultName) {
+ return new BeanValidator().validateTop(resultName, this);
+ }
+}
package org.onap.policy.controlloop.actorserviceprovider.parameters;
-import java.util.Map;
-import java.util.function.Function;
-import lombok.Data;
-import org.onap.policy.common.parameters.BeanValidationResult;
-import org.onap.policy.common.parameters.BeanValidator;
-import org.onap.policy.common.parameters.ValidationResult;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
import org.onap.policy.common.parameters.annotations.Min;
-import org.onap.policy.common.parameters.annotations.NotBlank;
-import org.onap.policy.common.parameters.annotations.NotNull;
-import org.onap.policy.controlloop.actorserviceprovider.Util;
/**
- * Parameters used by Actors that connect to a server via HTTP. This contains the
- * parameters that are common to all of the operations. Only the path changes for each
- * operation, thus it includes a mapping from operation name to path.
+ * Parameters used by Actors that connect to a server via HTTP.
*/
-@Data
-@NotNull
-@NotBlank
-public class HttpActorParams {
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = true)
+public class HttpActorParams extends CommonActorParams {
+
+ /*
+ * Optional, default values that are used if missing from the operation-specific
+ * parameters.
+ */
/**
* Name of the HttpClient, as found in the HttpClientFactory.
private String clientName;
/**
- * Amount of time, in seconds to wait for the HTTP request to complete, where zero
- * indicates that it should wait forever. The default is zero.
- */
- @Min(0)
- private int timeoutSec = 0;
-
- /**
- * Maps the operation name to its URI path.
- */
- private Map<String, String> path;
-
- /**
- * Extracts a specific operation's parameters from "this".
- *
- * @param name name of the item containing "this"
- * @return a function to extract an operation's parameters from "this". Note: the
- * returned function is not thread-safe
- */
- public Function<String, Map<String, Object>> makeOperationParameters(String name) {
- HttpParams subparams = HttpParams.builder().clientName(getClientName()).timeoutSec(getTimeoutSec()).build();
-
- return operation -> {
- String subpath = path.get(operation);
- if (subpath == null) {
- return null;
- }
-
- subparams.setPath(subpath);
- return Util.translateToMap(name + "." + operation, subparams);
- };
- }
-
- /**
- * Validates the parameters.
- *
- * @param name name of the object containing these parameters
- * @return "this"
- * @throws IllegalArgumentException if the parameters are invalid
+ * Amount of time, in seconds, to wait for the HTTP request to complete. The default
+ * is 90 seconds.
*/
- public HttpActorParams doValidation(String name) {
- ValidationResult result = validate(name);
- if (!result.isValid()) {
- throw new ParameterValidationRuntimeException("invalid parameters", result);
- }
-
- return this;
- }
-
- /**
- * Validates the parameters.
- *
- * @param resultName name of the result
- *
- * @return the validation result
- */
- public ValidationResult validate(String resultName) {
- BeanValidationResult result = new BeanValidator().validateTop(resultName, this);
-
- result.validateMap("path", path, (result2, entry) -> result2.validateNotNull(entry.getKey(), entry.getValue()));
-
- return result;
- }
+ @Min(1)
+ private int timeoutSec = 90;
}
private String path;
/**
- * Amount of time, in seconds to wait for the HTTP request to complete, where zero
- * indicates that it should wait forever. The default is zero.
+ * Amount of time, in seconds, to wait for the HTTP request to complete.
+ * <p/>
+ * Note: this should NOT have a default value, as it receives its default value from
+ * {@link HttpActorParams}.
*/
- @Min(0)
- @Builder.Default
- private int timeoutSec = 0;
+ @Min(1)
+ private int timeoutSec;
/**
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import java.util.List;
+import org.onap.policy.common.endpoints.event.comm.client.BidirectionalTopicClient;
+import org.onap.policy.common.endpoints.event.comm.client.BidirectionalTopicClientException;
+
+/**
+ * Handler for a bidirectional topic, supporting both publishing and forwarding of
+ * incoming messages.
+ */
+public class BidirectionalTopicHandler extends BidirectionalTopicClient {
+
+ /**
+ * Listener that will be attached to the topic to receive responses.
+ */
+ private final TopicListenerImpl listener = new TopicListenerImpl();
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param sinkTopic sink topic name
+ * @param sourceTopic source topic name
+ * @throws BidirectionalTopicClientException if an error occurs
+ */
+ public BidirectionalTopicHandler(String sinkTopic, String sourceTopic) throws BidirectionalTopicClientException {
+ super(sinkTopic, sourceTopic);
+ }
+
+ /**
+ * Starts listening on the source topic(s).
+ */
+ public void start() {
+ getSource().register(listener);
+ }
+
+ /**
+ * Stops listening on the source topic(s).
+ */
+ public void stop() {
+ getSource().unregister(listener);
+ }
+
+ /**
+ * Stops listening on the source topic(s) and clears all of the forwarders.
+ */
+ public void shutdown() {
+ stop();
+ listener.shutdown();
+ }
+
+ public Forwarder addForwarder(SelectorKey... keys) {
+ return listener.addForwarder(keys);
+ }
+
+ public Forwarder addForwarder(List<SelectorKey> keys) {
+ return listener.addForwarder(keys);
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+/**
+ * Manages bidirectional topics.
+ */
+@FunctionalInterface
+public interface BidirectionalTopicManager {
+
+ /**
+ * Gets the topic handler for the given parameters, creating one if it does not exist.
+ *
+ * @param sinkTopic sink topic name
+ * @param sourceTopic source topic name
+ * @return the topic handler associated with the given sink and source topic names
+ */
+ BidirectionalTopicHandler getTopicHandler(String sinkTopic, String sourceTopic);
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Forwarder that selectively forwards message to listeners based on the content of the
+ * message. Each forwarder is associated with a single set of selector keys. Listeners are
+ * then registered with that forwarder for a particular set of values for the given keys.
+ */
+public class Forwarder {
+ private static final Logger logger = LoggerFactory.getLogger(Forwarder.class);
+
+ /**
+ * Maps a set of field values to one or more listeners.
+ */
+ // @formatter:off
+ private final Map<List<String>, Map<BiConsumer<String, StandardCoderObject>, String>>
+ values2listeners = new ConcurrentHashMap<>();
+ // @formatter:on
+
+ /**
+ * Keys used to extract the field values from the {@link StandardCoderObject}.
+ */
+ private final List<SelectorKey> keys;
+
+ /**
+ * Constructs the object.
+ *
+ * @param keys keys used to extract the field's value from the
+ * {@link StandardCoderObject}
+ */
+ public Forwarder(List<SelectorKey> keys) {
+ this.keys = keys;
+ }
+
+ /**
+ * Registers a listener for messages containing the given field values.
+ *
+ * @param values field values of interest, in one-to-one correspondence with the keys
+ * @param listener listener to register
+ */
+ public void register(List<String> values, BiConsumer<String, StandardCoderObject> listener) {
+ if (keys.size() != values.size()) {
+ throw new IllegalArgumentException("key/value mismatch");
+ }
+
+ values2listeners.compute(values, (key, listeners) -> {
+ Map<BiConsumer<String, StandardCoderObject>, String> map = listeners;
+ if (map == null) {
+ map = new ConcurrentHashMap<>();
+ }
+
+ map.put(listener, "");
+ return map;
+ });
+ }
+
+ /**
+ * Unregisters a listener for messages containing the given field values.
+ *
+ * @param values field values of interest, in one-to-one correspondence with the keys
+ * @param listener listener to unregister
+ */
+ public void unregister(List<String> values, BiConsumer<String, StandardCoderObject> listener) {
+ values2listeners.computeIfPresent(values, (key, listeners) -> {
+ listeners.remove(listener);
+ return (listeners.isEmpty() ? null : listeners);
+ });
+ }
+
+ /**
+ * Processes a message, forwarding it to the appropriate listeners, if any.
+ *
+ * @param textMessage original text message that was received
+ * @param scoMessage decoded text message
+ */
+ public void onMessage(String textMessage, StandardCoderObject scoMessage) {
+ // extract the key values from the message
+ List<String> values = new ArrayList<>(keys.size());
+ for (SelectorKey key : keys) {
+ String value = key.extractField(scoMessage);
+ if (value == null) {
+ /*
+ * No value for this field, so this message is not relevant to this
+ * forwarder.
+ */
+ return;
+ }
+
+ values.add(value);
+ }
+
+ // get the listeners for this set of values
+ Map<BiConsumer<String, StandardCoderObject>, String> listeners = values2listeners.get(values);
+ if (listeners == null) {
+ // no listeners for this particular list of values
+ return;
+ }
+
+
+ // forward the message to each listener
+ for (BiConsumer<String, StandardCoderObject> listener : listeners.keySet()) {
+ try {
+ listener.accept(textMessage, scoMessage);
+ } catch (RuntimeException e) {
+ logger.warn("exception thrown by listener {}", Util.ident(listener), e);
+ }
+ }
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import lombok.EqualsAndHashCode;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+
+/**
+ * Selector key, which contains a hierarchical list of Strings and Integers that are used
+ * to extract the content of a field, typically from a {@link StandardCoderObject}.
+ */
+@EqualsAndHashCode
+public class SelectorKey {
+
+ /**
+ * Names and indices used to extract the field's value.
+ */
+ private final Object[] fieldIdentifiers;
+
+ /**
+ * Constructs the object.
+ *
+ * @param fieldIdentifiers names and indices used to extract the field's value
+ */
+ public SelectorKey(Object... fieldIdentifiers) {
+ this.fieldIdentifiers = fieldIdentifiers;
+ }
+
+ /**
+ * Extracts the given field from an object.
+ *
+ * @param object object from which to extract the field
+ * @return the extracted value, or {@code null} if the object does not contain the
+ * field
+ */
+ public String extractField(StandardCoderObject object) {
+ return object.getString(fieldIdentifiers);
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.event.comm.TopicListener;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A topic listener. When a message arrives on a topic, it is forwarded to listeners based
+ * on the content of fields found within the message. However, depending on the message
+ * type, the relevant fields might be found in different places within the message's
+ * object hierarchy. For each different list of keys, this class maintains a
+ * {@link Forwarder}, which is used to forward the message to all relevant listeners.
+ * <p/>
+ * Once a selector has been added, it is not removed until {@link #shutdown()} is invoked.
+ * As selectors are typically only added by Operators, and not by individual Operations,
+ * this should not pose a problem.
+ */
+public class TopicListenerImpl implements TopicListener {
+ private static final Logger logger = LoggerFactory.getLogger(TopicListenerImpl.class);
+ private static StandardCoder coder = new StandardCoder();
+
+ /**
+ * Maps selector to a forwarder.
+ */
+ private final Map<List<SelectorKey>, Forwarder> selector2forwarder = new ConcurrentHashMap<>();
+
+
+ /**
+ * Removes all forwarders.
+ */
+ public void shutdown() {
+ selector2forwarder.clear();
+ }
+
+ /**
+ * Adds a forwarder, if it doesn't already exist.
+ *
+ * @param keys the selector keys
+ * @return the forwarder associated with the given selector keys
+ */
+ public Forwarder addForwarder(SelectorKey... keys) {
+ return addForwarder(Arrays.asList(keys));
+ }
+
+ /**
+ * Adds a forwarder, if it doesn't already exist.
+ *
+ * @param keys the selector keys
+ * @return the forwarder associated with the given selector keys
+ */
+ public Forwarder addForwarder(List<SelectorKey> keys) {
+ return selector2forwarder.computeIfAbsent(keys, key -> new Forwarder(keys));
+ }
+
+ /**
+ * Decodes the message and then forwards it to each forwarder for processing.
+ */
+ @Override
+ public void onTopicEvent(CommInfrastructure infra, String topic, String message) {
+ StandardCoderObject object;
+ try {
+ object = coder.decode(message, StandardCoderObject.class);
+ } catch (CoderException e) {
+ logger.warn("cannot decode message", e);
+ return;
+ }
+
+ /*
+ * We don't know which selector is appropriate for the message, so we just let
+ * them all take a crack at it.
+ */
+ for (Forwarder forwarder : selector2forwarder.values()) {
+ forwarder.onMessage(message, object);
+ }
+ }
+}
private Map<String, Object> sub2;
private Map<String, Object> sub3;
private Map<String, Object> sub4;
- private Map<String, Object> params;
+ private Map<String, Map<String, Object>> params;
private ActorService service;
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.impl;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.function.Function;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.client.BidirectionalTopicClientException;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicActorParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicHandler;
+
+public class BidirectionalTopicActorTest {
+
+ private static final String ACTOR = "my-actor";
+ private static final String UNKNOWN = "unknown";
+ private static final String MY_SINK = "my-sink";
+ private static final String MY_SOURCE1 = "my-source-A";
+ private static final String MY_SOURCE2 = "my-source-B";
+ private static final int TIMEOUT = 10;
+
+ @Mock
+ private BidirectionalTopicHandler handler1;
+ @Mock
+ private BidirectionalTopicHandler handler2;
+
+ private BidirectionalTopicActor actor;
+
+
+ /**
+ * Configures the endpoints.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ Properties props = new Properties();
+ props.setProperty("noop.sink.topics", MY_SINK);
+ props.setProperty("noop.source.topics", MY_SOURCE1 + "," + MY_SOURCE2);
+
+ // clear all topics and then configure one sink and two sources
+ TopicEndpointManager.getManager().shutdown();
+ TopicEndpointManager.getManager().addTopicSinks(props);
+ TopicEndpointManager.getManager().addTopicSources(props);
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() {
+ // clear all topics after the tests
+ TopicEndpointManager.getManager().shutdown();
+ }
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ actor = new MyActor();
+ actor.configure(Util.translateToMap(ACTOR, makeParams()));
+ }
+
+ @Test
+ public void testDoStart() throws BidirectionalTopicClientException {
+ // allocate some handlers
+ actor.getTopicHandler(MY_SINK, MY_SOURCE1);
+ actor.getTopicHandler(MY_SINK, MY_SOURCE2);
+
+ // start it
+ actor.start();
+
+ verify(handler1).start();
+ verify(handler2).start();
+
+ verify(handler1, never()).stop();
+ verify(handler2, never()).stop();
+
+ verify(handler1, never()).shutdown();
+ verify(handler2, never()).shutdown();
+ }
+
+ @Test
+ public void testDoStop() throws BidirectionalTopicClientException {
+ // allocate some handlers
+ actor.getTopicHandler(MY_SINK, MY_SOURCE1);
+ actor.getTopicHandler(MY_SINK, MY_SOURCE2);
+
+ // start it
+ actor.start();
+
+ // stop it
+ actor.stop();
+
+ verify(handler1).stop();
+ verify(handler2).stop();
+
+ verify(handler1, never()).shutdown();
+ verify(handler2, never()).shutdown();
+ }
+
+ @Test
+ public void testDoShutdown() {
+ // allocate some handlers
+ actor.getTopicHandler(MY_SINK, MY_SOURCE1);
+ actor.getTopicHandler(MY_SINK, MY_SOURCE2);
+
+ // start it
+ actor.start();
+
+ // stop it
+ actor.shutdown();
+
+ verify(handler1).shutdown();
+ verify(handler2).shutdown();
+
+ verify(handler1, never()).stop();
+ verify(handler2, never()).stop();
+ }
+
+ @Test
+ public void testMakeOperatorParameters() {
+ BidirectionalTopicActorParams params = makeParams();
+
+ final BidirectionalTopicActor prov = new BidirectionalTopicActor(ACTOR);
+ Function<String, Map<String, Object>> maker =
+ prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params));
+
+ assertNull(maker.apply(UNKNOWN));
+
+ // use a TreeMap to ensure the properties are sorted
+ assertEquals("{sinkTopic=my-sink, sourceTopic=my-source-A, timeoutSec=10}",
+ new TreeMap<>(maker.apply("operA")).toString());
+
+ assertEquals("{sinkTopic=my-sink, sourceTopic=topicB, timeoutSec=10}",
+ new TreeMap<>(maker.apply("operB")).toString());
+
+ // with invalid actor parameters
+ params.setOperation(null);
+ assertThatThrownBy(() -> prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params)))
+ .isInstanceOf(ParameterValidationRuntimeException.class);
+ }
+
+ @Test
+ public void testBidirectionalTopicActor() {
+ assertEquals(ACTOR, actor.getName());
+ assertEquals(ACTOR, actor.getFullName());
+ }
+
+ @Test
+ public void testGetTopicHandler() {
+ assertSame(handler1, actor.getTopicHandler(MY_SINK, MY_SOURCE1));
+ assertSame(handler2, actor.getTopicHandler(MY_SINK, MY_SOURCE2));
+
+ assertThatIllegalArgumentException().isThrownBy(() -> actor.getTopicHandler(UNKNOWN, MY_SOURCE1));
+ }
+
+ @Test
+ public void testMakeTopicHandler() {
+ // use a real actor
+ actor = new BidirectionalTopicActor(ACTOR);
+
+ handler1 = actor.getTopicHandler(MY_SINK, MY_SOURCE1);
+ handler2 = actor.getTopicHandler(MY_SINK, MY_SOURCE2);
+
+ assertNotNull(handler1);
+ assertNotNull(handler2);
+ assertNotSame(handler1, handler2);
+ }
+
+
+ private BidirectionalTopicActorParams makeParams() {
+ BidirectionalTopicActorParams params = new BidirectionalTopicActorParams();
+ params.setSinkTopic(MY_SINK);
+ params.setSourceTopic(MY_SOURCE1);
+ params.setTimeoutSec(TIMEOUT);
+
+ // @formatter:off
+ params.setOperation(Map.of(
+ "operA", Map.of(),
+ "operB", Map.of("sourceTopic", "topicB")));
+ // @formatter:on
+ return params;
+ }
+
+ private class MyActor extends BidirectionalTopicActor {
+
+ public MyActor() {
+ super(ACTOR);
+ }
+
+ @Override
+ protected BidirectionalTopicHandler makeTopicHandler(String sinkTopic, String sourceTopic)
+ throws BidirectionalTopicClientException {
+
+ if (MY_SINK.equals(sinkTopic)) {
+ if (MY_SOURCE1.equals(sourceTopic)) {
+ return handler1;
+ } else if (MY_SOURCE2.equals(sourceTopic)) {
+ return handler2;
+ }
+ }
+
+ throw new BidirectionalTopicClientException("no topic " + sinkTopic + "/" + sourceTopic);
+ }
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.impl;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiConsumer;
+import lombok.Getter;
+import lombok.Setter;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.common.utils.time.PseudoExecutor;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicHandler;
+import org.onap.policy.controlloop.actorserviceprovider.topic.Forwarder;
+import org.onap.policy.controlloop.policy.PolicyResult;
+
+public class BidirectionalTopicOperationTest {
+ private static final CommInfrastructure SINK_INFRA = CommInfrastructure.NOOP;
+ private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
+ private static final String ACTOR = "my-actor";
+ private static final String OPERATION = "my-operation";
+ private static final String REQ_ID = "my-request-id";
+ private static final String MY_SINK = "my-sink";
+ private static final String MY_SOURCE = "my-source";
+ private static final String TEXT = "some text";
+ private static final int TIMEOUT_SEC = 10;
+ private static final long TIMEOUT_MS = 1000 * TIMEOUT_SEC;
+ private static final int MAX_REQUESTS = 100;
+
+ private static final StandardCoder coder = new StandardCoder();
+
+ @Mock
+ private BidirectionalTopicOperator operator;
+ @Mock
+ private BidirectionalTopicHandler handler;
+ @Mock
+ private Forwarder forwarder;
+
+ @Captor
+ private ArgumentCaptor<BiConsumer<String, StandardCoderObject>> listenerCaptor;
+
+ private ControlLoopOperationParams params;
+ private BidirectionalTopicParams topicParams;
+ private OperationOutcome outcome;
+ private StandardCoderObject stdResponse;
+ private String responseText;
+ private PseudoExecutor executor;
+ private int ntimes;
+ private BidirectionalTopicOperation<MyRequest, MyResponse> oper;
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() throws CoderException {
+ MockitoAnnotations.initMocks(this);
+
+ topicParams = BidirectionalTopicParams.builder().sourceTopic(MY_SOURCE).sinkTopic(MY_SINK)
+ .timeoutSec(TIMEOUT_SEC).build();
+
+ when(operator.getActorName()).thenReturn(ACTOR);
+ when(operator.getName()).thenReturn(OPERATION);
+ when(operator.getTopicHandler()).thenReturn(handler);
+ when(operator.getForwarder()).thenReturn(forwarder);
+ when(operator.getParams()).thenReturn(topicParams);
+ when(operator.isAlive()).thenReturn(true);
+
+ when(handler.send(any())).thenReturn(true);
+ when(handler.getSinkTopicCommInfrastructure()).thenReturn(SINK_INFRA);
+
+ executor = new PseudoExecutor();
+
+ params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).executor(executor).build();
+ outcome = params.makeOutcome();
+
+ responseText = coder.encode(new MyResponse());
+ stdResponse = coder.decode(responseText, StandardCoderObject.class);
+
+ ntimes = 1;
+
+ oper = new MyOperation();
+ }
+
+ @Test
+ public void testConstructor_testGetTopicHandler_testGetForwarder_testGetTopicParams() {
+ assertEquals(ACTOR, oper.getActorName());
+ assertEquals(OPERATION, oper.getName());
+ assertSame(handler, oper.getTopicHandler());
+ assertSame(forwarder, oper.getForwarder());
+ assertSame(topicParams, oper.getTopicParams());
+ assertEquals(TIMEOUT_MS, oper.getTimeoutMs());
+ assertSame(MyResponse.class, oper.getResponseClass());
+ }
+
+ @Test
+ public void testStartOperationAsync() throws Exception {
+
+ // tell it to expect three responses
+ ntimes = 3;
+
+ CompletableFuture<OperationOutcome> future = oper.startOperationAsync(1, outcome);
+ assertFalse(future.isDone());
+
+ verify(forwarder).register(eq(Arrays.asList(REQ_ID)), listenerCaptor.capture());
+
+ verify(forwarder, never()).unregister(any(), any());
+
+ verify(handler).send(any());
+
+ // provide first response
+ listenerCaptor.getValue().accept(responseText, stdResponse);
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertFalse(future.isDone());
+
+ // provide second response
+ listenerCaptor.getValue().accept(responseText, stdResponse);
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertFalse(future.isDone());
+
+ // provide final response
+ listenerCaptor.getValue().accept(responseText, stdResponse);
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertTrue(future.isDone());
+
+ assertSame(outcome, future.get());
+ assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+
+ verify(forwarder).unregister(eq(Arrays.asList(REQ_ID)), eq(listenerCaptor.getValue()));
+ }
+
+ /**
+ * Tests startOperationAsync() when the publisher throws an exception.
+ */
+ @Test
+ public void testStartOperationAsyncException() throws Exception {
+ // indicate that nothing was published
+ when(handler.send(any())).thenReturn(false);
+
+ assertThatIllegalStateException().isThrownBy(() -> oper.startOperationAsync(1, outcome));
+
+ verify(forwarder).register(eq(Arrays.asList(REQ_ID)), listenerCaptor.capture());
+
+ // must still unregister
+ verify(forwarder).unregister(eq(Arrays.asList(REQ_ID)), eq(listenerCaptor.getValue()));
+ }
+
+ @Test
+ public void testGetTimeoutMsInteger() {
+ // use default
+ assertEquals(TIMEOUT_MS, oper.getTimeoutMs(null));
+ assertEquals(TIMEOUT_MS, oper.getTimeoutMs(0));
+
+ // use provided value
+ assertEquals(5000, oper.getTimeoutMs(5));
+ }
+
+ @Test
+ public void testPublishRequest() {
+ assertThatCode(() -> oper.publishRequest(new MyRequest())).doesNotThrowAnyException();
+ }
+
+ /**
+ * Tests publishRequest() when nothing is published.
+ */
+ @Test
+ public void testPublishRequestUnpublished() {
+ when(handler.send(any())).thenReturn(false);
+ assertThatIllegalStateException().isThrownBy(() -> oper.publishRequest(new MyRequest()));
+ }
+
+ /**
+ * Tests publishRequest() when the request type is a String.
+ */
+ @Test
+ public void testPublishRequestString() {
+ MyStringOperation oper2 = new MyStringOperation();
+ assertThatCode(() -> oper2.publishRequest(TEXT)).doesNotThrowAnyException();
+ }
+
+ /**
+ * Tests publishRequest() when the coder throws an exception.
+ */
+ @Test
+ public void testPublishRequestException() {
+ setOperCoderException();
+ assertThatIllegalArgumentException().isThrownBy(() -> oper.publishRequest(new MyRequest()));
+ }
+
+ /**
+ * Tests processResponse() when it's a success and the response type is a String.
+ */
+ @Test
+ public void testProcessResponseSuccessString() {
+ MyStringOperation oper2 = new MyStringOperation();
+
+ assertSame(outcome, oper2.processResponse(outcome, TEXT, null));
+ assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+ }
+
+ /**
+ * Tests processResponse() when it's a success and the response type is a
+ * StandardCoderObject.
+ */
+ @Test
+ public void testProcessResponseSuccessSco() {
+ MyScoOperation oper2 = new MyScoOperation();
+
+ assertSame(outcome, oper2.processResponse(outcome, responseText, stdResponse));
+ assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+ }
+
+ /**
+ * Tests processResponse() when it's a failure.
+ */
+ @Test
+ public void testProcessResponseFailure() throws CoderException {
+ // indicate error in the response
+ MyResponse resp = new MyResponse();
+ resp.setOutput("error");
+
+ responseText = coder.encode(resp);
+ stdResponse = coder.decode(responseText, StandardCoderObject.class);
+
+ assertSame(outcome, oper.processResponse(outcome, responseText, stdResponse));
+ assertEquals(PolicyResult.FAILURE, outcome.getResult());
+ }
+
+ /**
+ * Tests processResponse() when the decoder succeeds.
+ */
+ @Test
+ public void testProcessResponseDecodeOk() throws CoderException {
+ assertSame(outcome, oper.processResponse(outcome, responseText, stdResponse));
+ assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+ }
+
+ /**
+ * Tests processResponse() when the decoder throws an exception.
+ */
+ @Test
+ public void testProcessResponseDecodeExcept() throws CoderException {
+ // @formatter:off
+ assertThatIllegalArgumentException().isThrownBy(
+ () -> oper.processResponse(outcome, "{invalid json", stdResponse));
+ // @formatter:on
+ }
+
+ @Test
+ public void testPostProcessResponse() {
+ assertThatCode(() -> oper.postProcessResponse(outcome, null, null)).doesNotThrowAnyException();
+ }
+
+ @Test
+ public void testMakeCoder() {
+ assertNotNull(oper.makeCoder());
+ }
+
+ /**
+ * Creates a new {@link #oper} whose coder will throw an exception.
+ */
+ private void setOperCoderException() {
+ oper = new MyOperation() {
+ @Override
+ protected Coder makeCoder() {
+ return new StandardCoder() {
+ @Override
+ public String encode(Object object, boolean pretty) throws CoderException {
+ throw new CoderException(EXPECTED_EXCEPTION);
+ }
+ };
+ }
+ };
+ }
+
+ @Getter
+ @Setter
+ public static class MyRequest {
+ private String theRequestId = REQ_ID;
+ private String input;
+ }
+
+ @Getter
+ @Setter
+ public static class MyResponse {
+ private String requestId = REQ_ID;
+ private String output;
+ }
+
+
+ private class MyStringOperation extends BidirectionalTopicOperation<String, String> {
+ public MyStringOperation() {
+ super(BidirectionalTopicOperationTest.this.params, operator, String.class);
+ }
+
+ @Override
+ protected String makeRequest(int attempt) {
+ return TEXT;
+ }
+
+ @Override
+ protected List<String> getExpectedKeyValues(int attempt, String request) {
+ return Arrays.asList(REQ_ID);
+ }
+
+ @Override
+ protected Status detmStatus(String rawResponse, String response) {
+ return (response != null ? Status.SUCCESS : Status.FAILURE);
+ }
+ }
+
+
+ private class MyScoOperation extends BidirectionalTopicOperation<MyRequest, StandardCoderObject> {
+ public MyScoOperation() {
+ super(BidirectionalTopicOperationTest.this.params, operator, StandardCoderObject.class);
+ }
+
+ @Override
+ protected MyRequest makeRequest(int attempt) {
+ return new MyRequest();
+ }
+
+ @Override
+ protected List<String> getExpectedKeyValues(int attempt, MyRequest request) {
+ return Arrays.asList(REQ_ID);
+ }
+
+ @Override
+ protected Status detmStatus(String rawResponse, StandardCoderObject response) {
+ return (response.getString("output") == null ? Status.SUCCESS : Status.FAILURE);
+ }
+ }
+
+
+ private class MyOperation extends BidirectionalTopicOperation<MyRequest, MyResponse> {
+ public MyOperation() {
+ super(BidirectionalTopicOperationTest.this.params, operator, MyResponse.class);
+ }
+
+ @Override
+ protected MyRequest makeRequest(int attempt) {
+ return new MyRequest();
+ }
+
+ @Override
+ protected List<String> getExpectedKeyValues(int attempt, MyRequest request) {
+ return Arrays.asList(REQ_ID);
+ }
+
+ @Override
+ protected Status detmStatus(String rawResponse, MyResponse response) {
+ if (--ntimes <= 0) {
+ return (response.getOutput() == null ? Status.SUCCESS : Status.FAILURE);
+ }
+
+ return Status.STILL_WAITING;
+ }
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.impl;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiFunction;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicHandler;
+import org.onap.policy.controlloop.actorserviceprovider.topic.BidirectionalTopicManager;
+import org.onap.policy.controlloop.actorserviceprovider.topic.Forwarder;
+import org.onap.policy.controlloop.actorserviceprovider.topic.SelectorKey;
+
+public class BidirectionalTopicOperatorTest {
+ private static final String ACTOR = "my-actor";
+ private static final String OPERATION = "my-operation";
+ private static final String MY_SOURCE = "my-source";
+ private static final String MY_SINK = "my-target";
+ private static final int TIMEOUT_SEC = 10;
+
+ @Mock
+ private BidirectionalTopicManager mgr;
+ @Mock
+ private BidirectionalTopicHandler handler;
+ @Mock
+ private Forwarder forwarder;
+ @Mock
+ private BidirectionalTopicOperation<String, Integer> operation;
+
+ private List<SelectorKey> keys;
+ private BidirectionalTopicParams params;
+ private MyOperator oper;
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ keys = List.of(new SelectorKey(""));
+
+ when(mgr.getTopicHandler(MY_SINK, MY_SOURCE)).thenReturn(handler);
+ when(handler.addForwarder(keys)).thenReturn(forwarder);
+
+ oper = new MyOperator(keys);
+
+ params = BidirectionalTopicParams.builder().sourceTopic(MY_SOURCE).sinkTopic(MY_SINK).timeoutSec(TIMEOUT_SEC)
+ .build();
+ oper.configure(Util.translateToMap(OPERATION, params));
+ oper.start();
+ }
+
+ @Test
+ public void testConstructor_testGetParams_testGetTopicHandler_testGetForwarder() {
+ assertEquals(ACTOR, oper.getActorName());
+ assertEquals(OPERATION, oper.getName());
+ assertEquals(params, oper.getParams());
+ assertSame(handler, oper.getTopicHandler());
+ assertSame(forwarder, oper.getForwarder());
+ }
+
+ @Test
+ public void testDoConfigure() {
+ oper.stop();
+
+ // invalid parameters
+ params.setSourceTopic(null);
+ assertThatThrownBy(() -> oper.configure(Util.translateToMap(OPERATION, params)))
+ .isInstanceOf(ParameterValidationRuntimeException.class);
+ }
+
+ @Test
+ public void testMakeOperator() {
+ AtomicReference<ControlLoopOperationParams> paramsRef = new AtomicReference<>();
+ AtomicReference<BidirectionalTopicOperator> operRef = new AtomicReference<>();
+
+ // @formatter:off
+ BiFunction<ControlLoopOperationParams, BidirectionalTopicOperator,
+ BidirectionalTopicOperation<String, Integer>> maker =
+ (params, operator) -> {
+ paramsRef.set(params);
+ operRef.set(operator);
+ return operation;
+ };
+ // @formatter:on
+
+ BidirectionalTopicOperator oper2 =
+ BidirectionalTopicOperator.makeOperator(ACTOR, OPERATION, mgr, maker, new SelectorKey(""));
+
+ assertEquals(ACTOR, oper2.getActorName());
+ assertEquals(OPERATION, oper2.getName());
+
+ ControlLoopOperationParams params2 = ControlLoopOperationParams.builder().build();
+
+ assertSame(operation, oper2.buildOperation(params2));
+ assertSame(params2, paramsRef.get());
+ assertSame(oper2, operRef.get());
+ }
+
+
+ private class MyOperator extends BidirectionalTopicOperator {
+ public MyOperator(List<SelectorKey> selectorKeys) {
+ super(ACTOR, OPERATION, mgr, selectorKeys);
+ }
+
+ @Override
+ public Operation buildOperation(ControlLoopOperationParams params) {
+ return null;
+ }
+ }
+}
HttpActorParams params = new HttpActorParams();
params.setClientName(CLIENT);
params.setTimeoutSec(TIMEOUT);
- params.setPath(Map.of("operA", "urlA", "operB", "urlB"));
+
+ // @formatter:off
+ params.setOperation(Map.of(
+ "operA", Map.of("path", "urlA"),
+ "operB", Map.of("path", "urlB")));
+ // @formatter:on
final HttpActor prov = new HttpActor(ACTOR);
Function<String, Map<String, Object>> maker =
new TreeMap<>(maker.apply("operB")).toString());
// with invalid actor parameters
- params.setClientName(null);
+ params.setOperation(null);
assertThatThrownBy(() -> prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params)))
.isInstanceOf(ParameterValidationRuntimeException.class);
}
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import ch.qos.logback.classic.Logger;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams.TopicParamsBuilder;
import org.onap.policy.common.endpoints.http.client.HttpClient;
import org.onap.policy.common.endpoints.http.server.HttpServletServer;
import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
import org.onap.policy.common.gson.GsonMessageBodyHandler;
-import org.onap.policy.common.utils.coder.Coder;
import org.onap.policy.common.utils.coder.CoderException;
-import org.onap.policy.common.utils.coder.StandardCoder;
import org.onap.policy.common.utils.network.NetworkUtil;
-import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
import org.onap.policy.controlloop.actorserviceprovider.Operation;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
import org.onap.policy.controlloop.policy.PolicyResult;
-import org.slf4j.LoggerFactory;
public class HttpOperationTest {
private static final String HTTP_CLIENT = "my-client";
private static final String HTTP_NO_SERVER = "my-http-no-server-client";
private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json";
- private static final String MY_REQUEST = "my-request";
private static final String BASE_URI = "oper";
private static final String PATH = "/my-path";
private static final String TEXT = "my-text";
private static final UUID REQ_ID = UUID.randomUUID();
- /**
- * Used to attach an appender to the class' logger.
- */
- private static final Logger logger = (Logger) LoggerFactory.getLogger(HttpOperation.class);
- private static final ExtractAppender appender = new ExtractAppender();
-
/**
* {@code True} if the server should reject the request, {@code false} otherwise.
*/
HttpClientFactoryInstance.getClientFactory()
.build(builder.clientName(HTTP_NO_SERVER).port(NetworkUtil.allocPort()).build());
-
- /**
- * Attach appender to the logger.
- */
- appender.setContext(logger.getLoggerContext());
- appender.start();
-
- logger.addAppender(appender);
}
/**
*/
@AfterClass
public static void tearDownAfterClass() {
- appender.stop();
-
HttpClientFactoryInstance.getClientFactory().destroy();
HttpServletServerFactoryInstance.getServerFactory().destroy();
}
public void setUp() {
MockitoAnnotations.initMocks(this);
- appender.clearExtractions();
-
rejectRequest = false;
nget = 0;
npost = 0;
@Test
public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() {
- // no default yet
- assertEquals(0L, oper.getTimeoutMs(null));
- assertEquals(0L, oper.getTimeoutMs(0));
+ // use value from operator
+ assertEquals(1000L, oper.getTimeoutMs(null));
+ assertEquals(1000L, oper.getTimeoutMs(0));
// should use given value
assertEquals(20 * 1000L, oper.getTimeoutMs(20));
public void testProcessResponseDecodeExcept() throws CoderException {
MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
- assertSame(outcome, oper2.processResponse(outcome, PATH, response));
- assertEquals(PolicyResult.FAILURE_EXCEPTION, outcome.getResult());
+ assertThatIllegalArgumentException().isThrownBy(() -> oper2.processResponse(outcome, PATH, response));
}
@Test
assertEquals(PolicyResult.SUCCESS, outcome.getResult());
}
- @Test
- public void testLogRestRequest() throws CoderException {
- // log structured data
- appender.clearExtractions();
- oper.logRestRequest(PATH, new MyRequest());
- List<String> output = appender.getExtracted();
- assertEquals(1, output.size());
-
- assertThat(output.get(0)).contains(PATH).contains("{\n \"input\": \"some input\"\n}");
-
- // log a plain string
- appender.clearExtractions();
- oper.logRestRequest(PATH, MY_REQUEST);
- output = appender.getExtracted();
- assertEquals(1, output.size());
-
- assertThat(output.get(0)).contains(PATH).contains(MY_REQUEST);
-
- // log a null request
- appender.clearExtractions();
- oper.logRestRequest(PATH, null);
- output = appender.getExtracted();
- assertEquals(1, output.size());
-
- // exception from coder
- oper = new MyGetOperation<>(String.class) {
- @Override
- protected Coder makeCoder() {
- return new StandardCoder() {
- @Override
- public String encode(Object object, boolean pretty) throws CoderException {
- throw new CoderException(EXPECTED_EXCEPTION);
- }
- };
- }
- };
-
- appender.clearExtractions();
- oper.logRestRequest(PATH, new MyRequest());
- output = appender.getExtracted();
- assertEquals(2, output.size());
- assertThat(output.get(0)).contains("cannot pretty-print request");
- assertThat(output.get(1)).contains(PATH);
- }
-
- @Test
- public void testLogRestResponse() throws CoderException {
- // log structured data
- appender.clearExtractions();
- oper.logRestResponse(PATH, new MyResponse());
- List<String> output = appender.getExtracted();
- assertEquals(1, output.size());
-
- assertThat(output.get(0)).contains(PATH).contains("{\n \"output\": \"some output\"\n}");
-
- // log a plain string
- appender.clearExtractions();
- oper.logRestResponse(PATH, MY_REQUEST);
- output = appender.getExtracted();
- assertEquals(1, output.size());
-
- // log a null response
- appender.clearExtractions();
- oper.logRestResponse(PATH, null);
- output = appender.getExtracted();
- assertEquals(1, output.size());
-
- assertThat(output.get(0)).contains(PATH).contains("null");
-
- // exception from coder
- oper = new MyGetOperation<>(String.class) {
- @Override
- protected Coder makeCoder() {
- return new StandardCoder() {
- @Override
- public String encode(Object object, boolean pretty) throws CoderException {
- throw new CoderException(EXPECTED_EXCEPTION);
- }
- };
- }
- };
-
- appender.clearExtractions();
- oper.logRestResponse(PATH, new MyResponse());
- output = appender.getExtracted();
- assertEquals(2, output.size());
- assertThat(output.get(0)).contains("cannot pretty-print response");
- assertThat(output.get(1)).contains(PATH);
- }
-
@Test
public void testMakeDecoder() {
assertNotNull(oper.makeCoder());
private void initOper(HttpOperator operator, String clientName) {
operator.stop();
- HttpParams params = HttpParams.builder().clientName(clientName).path(PATH).build();
+ HttpParams params = HttpParams.builder().clientName(clientName).path(PATH).timeoutSec(1).build();
Map<String, Object> mapParams = Util.translateToMap(OPERATION, params);
operator.configure(mapParams);
operator.start();
headers.put("Accept", MediaType.APPLICATION_JSON);
String url = makeUrl();
- logRestRequest(url, null);
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
// @formatter:off
return handleResponse(outcome, url,
headers.put("Accept", MediaType.APPLICATION_JSON);
String url = makeUrl();
- logRestRequest(url, request);
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
// @formatter:off
return handleResponse(outcome, url,
headers.put("Accept", MediaType.APPLICATION_JSON);
String url = makeUrl();
- logRestRequest(url, request);
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
// @formatter:off
return handleResponse(outcome, url,
headers.put("Accept", MediaType.APPLICATION_JSON);
String url = makeUrl();
- logRestRequest(url, null);
+ logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
// @formatter:off
return handleResponse(outcome, url,
package org.onap.policy.controlloop.actorserviceprovider.impl;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import ch.qos.logback.classic.Logger;
import java.time.Instant;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.Setter;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
+import org.onap.policy.common.utils.coder.Coder;
import org.onap.policy.common.utils.coder.CoderException;
import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
+import org.onap.policy.common.utils.time.PseudoExecutor;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
import org.onap.policy.controlloop.actorserviceprovider.Operation;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
-import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture;
import org.onap.policy.controlloop.policy.PolicyResult;
+import org.slf4j.LoggerFactory;
public class OperationPartialTest {
- private static final int MAX_PARALLEL_REQUESTS = 10;
+ private static final CommInfrastructure SINK_INFRA = CommInfrastructure.NOOP;
+ private static final CommInfrastructure SOURCE_INFRA = CommInfrastructure.UEB;
+ private static final int MAX_REQUESTS = 100;
+ private static final int MAX_PARALLEL = 10;
private static final String EXPECTED_EXCEPTION = "expected exception";
private static final String ACTOR = "my-actor";
private static final String OPERATION = "my-operation";
- private static final String TARGET = "my-target";
+ private static final String MY_SINK = "my-sink";
+ private static final String MY_SOURCE = "my-source";
+ private static final String TEXT = "my-text";
private static final int TIMEOUT = 1000;
private static final UUID REQ_ID = UUID.randomUUID();
private static final List<PolicyResult> FAILURE_RESULTS = Arrays.asList(PolicyResult.values()).stream()
.filter(result -> result != PolicyResult.SUCCESS).collect(Collectors.toList());
+ /**
+ * Used to attach an appender to the class' logger.
+ */
+ private static final Logger logger = (Logger) LoggerFactory.getLogger(OperationPartial.class);
+ private static final ExtractAppender appender = new ExtractAppender();
+
private VirtualControlLoopEvent event;
private ControlLoopEventContext context;
- private MyExec executor;
+ private PseudoExecutor executor;
private ControlLoopOperationParams params;
private MyOper oper;
private OperatorPartial operator;
+ /**
+ * Attaches the appender to the logger.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ /**
+ * Attach appender to the logger.
+ */
+ appender.setContext(logger.getLoggerContext());
+ appender.start();
+
+ logger.addAppender(appender);
+ }
+
+ /**
+ * Stops the appender.
+ */
+ @AfterClass
+ public static void tearDownAfterClass() {
+ appender.stop();
+ }
+
/**
* Initializes the fields, including {@link #oper}.
*/
event.setRequestId(REQ_ID);
context = new ControlLoopEventContext(event);
- executor = new MyExec();
+ executor = new PseudoExecutor();
params = ControlLoopOperationParams.builder().completeCallback(this::completer).context(context)
.executor(executor).actor(ACTOR).operation(OPERATION).timeoutSec(TIMEOUT)
- .startCallback(this::starter).targetEntity(TARGET).build();
+ .startCallback(this::starter).targetEntity(MY_SINK).build();
operator = new OperatorPartial(ACTOR, OPERATION) {
@Override
*/
@Test
public void testStartMultiple() {
- for (int count = 0; count < MAX_PARALLEL_REQUESTS; ++count) {
+ for (int count = 0; count < MAX_PARALLEL; ++count) {
oper.start();
}
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS * MAX_PARALLEL));
assertNotNull(opstart);
assertNotNull(opend);
assertEquals(PolicyResult.SUCCESS, opend.getResult());
- assertEquals(MAX_PARALLEL_REQUESTS, numStart);
- assertEquals(MAX_PARALLEL_REQUESTS, oper.getCount());
- assertEquals(MAX_PARALLEL_REQUESTS, numEnd);
+ assertEquals(MAX_PARALLEL, numStart);
+ assertEquals(MAX_PARALLEL, oper.getCount());
+ assertEquals(MAX_PARALLEL, numEnd);
}
/**
oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
oper.start().cancel(false);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertNull(opstart);
assertNull(opend);
@Test
public void testStartOperationAsync() {
oper.start();
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertEquals(1, oper.getCount());
}
outcome.setResult(PolicyResult.FAILURE);
// incorrect actor
- outcome.setActor(TARGET);
+ outcome.setActor(MY_SINK);
assertFalse(oper.isActorFailed(outcome));
outcome.setActor(null);
assertFalse(oper.isActorFailed(outcome));
outcome.setActor(ACTOR);
// incorrect operation
- outcome.setOperation(TARGET);
+ outcome.setOperation(MY_SINK);
assertFalse(oper.isActorFailed(outcome));
outcome.setOperation(null);
assertFalse(oper.isActorFailed(outcome));
OperationPartial oper2 = new OperationPartial(params, operator) {};
oper2.start();
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertNotNull(opend);
assertEquals(PolicyResult.FAILURE_EXCEPTION, opend.getResult());
// wrong actor - should be false
outcome.setActor(null);
assertFalse(oper.isSameOperation(outcome));
- outcome.setActor(TARGET);
+ outcome.setActor(MY_SINK);
assertFalse(oper.isSameOperation(outcome));
outcome.setActor(ACTOR);
// wrong operation - should be null
outcome.setOperation(null);
assertFalse(oper.isSameOperation(outcome));
- outcome.setOperation(TARGET);
+ outcome.setOperation(MY_SINK);
assertFalse(oper.isSameOperation(outcome));
outcome.setOperation(OPERATION);
@Test
public void testAnyOf() throws Exception {
// first task completes, others do not
- List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>();
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
final OperationOutcome outcome = params.makeOutcome();
- tasks.add(CompletableFuture.completedFuture(outcome));
- tasks.add(new CompletableFuture<>());
- tasks.add(new CompletableFuture<>());
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+ tasks.add(() -> new CompletableFuture<>());
+ tasks.add(() -> null);
+ tasks.add(() -> new CompletableFuture<>());
CompletableFuture<OperationOutcome> result = oper.anyOf(tasks);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertTrue(result.isDone());
+ assertSame(outcome, result.get());
+ // repeat using array form
+ @SuppressWarnings("unchecked")
+ Supplier<CompletableFuture<OperationOutcome>>[] taskArray = new Supplier[tasks.size()];
+ result = oper.anyOf(tasks.toArray(taskArray));
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isDone());
assertSame(outcome, result.get());
// second task completes, others do not
- tasks = new LinkedList<>();
-
- tasks.add(new CompletableFuture<>());
- tasks.add(CompletableFuture.completedFuture(outcome));
- tasks.add(new CompletableFuture<>());
+ tasks.clear();
+ tasks.add(() -> new CompletableFuture<>());
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+ tasks.add(() -> new CompletableFuture<>());
result = oper.anyOf(tasks);
- assertTrue(executor.runAll());
-
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isDone());
assertSame(outcome, result.get());
// third task completes, others do not
- tasks = new LinkedList<>();
-
- tasks.add(new CompletableFuture<>());
- tasks.add(new CompletableFuture<>());
- tasks.add(CompletableFuture.completedFuture(outcome));
+ tasks.clear();
+ tasks.add(() -> new CompletableFuture<>());
+ tasks.add(() -> new CompletableFuture<>());
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
result = oper.anyOf(tasks);
- assertTrue(executor.runAll());
-
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isDone());
assertSame(outcome, result.get());
}
@Test
@SuppressWarnings("unchecked")
public void testAnyOfEdge() throws Exception {
- List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>();
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
// zero items: check both using a list and using an array
- assertThatIllegalArgumentException().isThrownBy(() -> oper.anyOf(tasks));
- assertThatIllegalArgumentException().isThrownBy(() -> oper.anyOf());
+ assertNull(oper.anyOf(tasks));
+ assertNull(oper.anyOf());
// one item: : check both using a list and using an array
CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>();
- tasks.add(future1);
+ tasks.add(() -> future1);
assertSame(future1, oper.anyOf(tasks));
- assertSame(future1, oper.anyOf(future1));
+ assertSame(future1, oper.anyOf(() -> future1));
}
- /**
- * Tests both flavors of allOf(), because one invokes the other.
- */
@Test
- public void testAllOf() throws Exception {
- List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>();
+ public void testAllOfArray() throws Exception {
+ final OperationOutcome outcome = params.makeOutcome();
+
+ CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>();
+ CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>();
+ CompletableFuture<OperationOutcome> future3 = new CompletableFuture<>();
+ @SuppressWarnings("unchecked")
+ CompletableFuture<OperationOutcome> result =
+ oper.allOf(() -> future1, () -> future2, () -> null, () -> future3);
+
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertFalse(result.isDone());
+ future1.complete(outcome);
+
+ // complete 3 before 2
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertFalse(result.isDone());
+ future3.complete(outcome);
+
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertFalse(result.isDone());
+ future2.complete(outcome);
+
+ // all of them are now done
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertTrue(result.isDone());
+ assertSame(outcome, result.get());
+ }
+
+ @Test
+ public void testAllOfList() throws Exception {
final OperationOutcome outcome = params.makeOutcome();
CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>();
CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>();
CompletableFuture<OperationOutcome> future3 = new CompletableFuture<>();
- tasks.add(future1);
- tasks.add(future2);
- tasks.add(future3);
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
+ tasks.add(() -> future1);
+ tasks.add(() -> future2);
+ tasks.add(() -> null);
+ tasks.add(() -> future3);
CompletableFuture<OperationOutcome> result = oper.allOf(tasks);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertFalse(result.isDone());
future1.complete(outcome);
// complete 3 before 2
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertFalse(result.isDone());
future3.complete(outcome);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertFalse(result.isDone());
future2.complete(outcome);
// all of them are now done
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isDone());
assertSame(outcome, result.get());
}
@Test
@SuppressWarnings("unchecked")
public void testAllOfEdge() throws Exception {
- List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>();
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
// zero items: check both using a list and using an array
- assertThatIllegalArgumentException().isThrownBy(() -> oper.allOf(tasks));
- assertThatIllegalArgumentException().isThrownBy(() -> oper.allOf());
+ assertNull(oper.allOf(tasks));
+ assertNull(oper.allOf());
// one item: : check both using a list and using an array
CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>();
- tasks.add(future1);
+ tasks.add(() -> future1);
assertSame(future1, oper.allOf(tasks));
- assertSame(future1, oper.allOf(future1));
+ assertSame(future1, oper.allOf(() -> future1));
+ }
+
+ @Test
+ public void testAttachFutures() throws Exception {
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
+
+ // third task throws an exception during construction
+ CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>();
+ CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>();
+ CompletableFuture<OperationOutcome> future3 = new CompletableFuture<>();
+ tasks.add(() -> future1);
+ tasks.add(() -> future2);
+ tasks.add(() -> {
+ throw new IllegalStateException(EXPECTED_EXCEPTION);
+ });
+ tasks.add(() -> future3);
+
+ assertThatIllegalStateException().isThrownBy(() -> oper.anyOf(tasks)).withMessage(EXPECTED_EXCEPTION);
+
+ // should have canceled the first two, but not the last
+ assertTrue(future1.isCancelled());
+ assertTrue(future2.isCancelled());
+ assertFalse(future3.isCancelled());
}
@Test
verifyOutcomes(1, PolicyResult.SUCCESS, PolicyResult.FAILURE, PolicyResult.FAILURE_GUARD);
verifyOutcomes(2, PolicyResult.SUCCESS, PolicyResult.FAILURE_GUARD, PolicyResult.FAILURE);
- // null outcome
- final List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>();
- tasks.add(CompletableFuture.completedFuture(null));
+ // null outcome - takes precedence over a success
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
+ tasks.add(() -> CompletableFuture.completedFuture(params.makeOutcome()));
+ tasks.add(() -> CompletableFuture.completedFuture(null));
+ tasks.add(() -> CompletableFuture.completedFuture(params.makeOutcome()));
CompletableFuture<OperationOutcome> result = oper.allOf(tasks);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isDone());
assertNull(result.get());
IllegalStateException except = new IllegalStateException(EXPECTED_EXCEPTION);
tasks.clear();
- tasks.add(CompletableFuture.completedFuture(params.makeOutcome()));
- tasks.add(CompletableFuture.failedFuture(except));
- tasks.add(CompletableFuture.completedFuture(params.makeOutcome()));
+ tasks.add(() -> CompletableFuture.completedFuture(params.makeOutcome()));
+ tasks.add(() -> CompletableFuture.failedFuture(except));
+ tasks.add(() -> CompletableFuture.completedFuture(params.makeOutcome()));
result = oper.allOf(tasks);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isCompletedExceptionally());
result.whenComplete((unused, thrown) -> assertSame(except, thrown));
}
- private void verifyOutcomes(int expected, PolicyResult... results) throws Exception {
- List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>();
+ /**
+ * Tests both flavors of sequence(), because one invokes the other.
+ */
+ @Test
+ public void testSequence() throws Exception {
+ final OperationOutcome outcome = params.makeOutcome();
+
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+ tasks.add(() -> null);
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+
+ CompletableFuture<OperationOutcome> result = oper.sequence(tasks);
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertTrue(result.isDone());
+ assertSame(outcome, result.get());
+
+ // repeat using array form
+ @SuppressWarnings("unchecked")
+ Supplier<CompletableFuture<OperationOutcome>>[] taskArray = new Supplier[tasks.size()];
+ result = oper.sequence(tasks.toArray(taskArray));
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertTrue(result.isDone());
+ assertSame(outcome, result.get());
+
+ // second task fails, third should not run
+ OperationOutcome failure = params.makeOutcome();
+ failure.setResult(PolicyResult.FAILURE);
+ tasks.clear();
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+ tasks.add(() -> CompletableFuture.completedFuture(failure));
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
+
+ result = oper.sequence(tasks);
+ assertTrue(executor.runAll(MAX_REQUESTS));
+ assertTrue(result.isDone());
+ assertSame(failure, result.get());
+ }
+
+ /**
+ * Tests both flavors of sequence(), for edge cases: zero items, and one item.
+ */
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testSequenceEdge() throws Exception {
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
+ // zero items: check both using a list and using an array
+ assertNull(oper.sequence(tasks));
+ assertNull(oper.sequence());
+
+ // one item: : check both using a list and using an array
+ CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>();
+ tasks.add(() -> future1);
+
+ assertSame(future1, oper.sequence(tasks));
+ assertSame(future1, oper.sequence(() -> future1));
+ }
+
+ private void verifyOutcomes(int expected, PolicyResult... results) throws Exception {
+ List<Supplier<CompletableFuture<OperationOutcome>>> tasks = new LinkedList<>();
OperationOutcome expectedOutcome = null;
for (int count = 0; count < results.length; ++count) {
OperationOutcome outcome = params.makeOutcome();
outcome.setResult(results[count]);
- tasks.add(CompletableFuture.completedFuture(outcome));
+ tasks.add(() -> CompletableFuture.completedFuture(outcome));
if (count == expected) {
expectedOutcome = outcome;
CompletableFuture<OperationOutcome> result = oper.allOf(tasks);
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
assertTrue(result.isDone());
assertSame(expectedOutcome, result.get());
}
- private Function<OperationOutcome, CompletableFuture<OperationOutcome>> makeTask(
- final OperationOutcome taskOutcome) {
-
- return outcome -> CompletableFuture.completedFuture(taskOutcome);
- }
-
@Test
public void testDetmPriority() throws CoderException {
assertEquals(1, oper.detmPriority(null));
assertEquals(1, oper.detmPriority(outcome));
}
- /**
- * Tests doTask(Future) when the controller is not running.
- */
- @Test
- public void testDoTaskFutureNotRunning() throws Exception {
- CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>();
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
- controller.complete(params.makeOutcome());
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, params.makeOutcome(), taskFuture);
- assertFalse(future.isDone());
- assertTrue(executor.runAll());
-
- // should not have run the task
- assertFalse(future.isDone());
-
- // should have canceled the task future
- assertTrue(taskFuture.isCancelled());
- }
-
- /**
- * Tests doTask(Future) when the previous outcome was successful.
- */
- @Test
- public void testDoTaskFutureSuccess() throws Exception {
- CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>();
- final OperationOutcome taskOutcome = params.makeOutcome();
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, params.makeOutcome(), taskFuture);
-
- taskFuture.complete(taskOutcome);
- assertTrue(executor.runAll());
-
- assertTrue(future.isDone());
- assertSame(taskOutcome, future.get());
-
- // controller should not be done yet
- assertFalse(controller.isDone());
- }
-
- /**
- * Tests doTask(Future) when the previous outcome was failed.
- */
- @Test
- public void testDoTaskFutureFailure() throws Exception {
- CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>();
- final OperationOutcome failedOutcome = params.makeOutcome();
- failedOutcome.setResult(PolicyResult.FAILURE);
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, failedOutcome, taskFuture);
- assertFalse(future.isDone());
- assertTrue(executor.runAll());
-
- // should not have run the task
- assertFalse(future.isDone());
-
- // should have canceled the task future
- assertTrue(taskFuture.isCancelled());
-
- // controller SHOULD be done now
- assertTrue(controller.isDone());
- assertSame(failedOutcome, controller.get());
- }
-
- /**
- * Tests doTask(Future) when the previous outcome was failed, but not checking
- * success.
- */
- @Test
- public void testDoTaskFutureUncheckedFailure() throws Exception {
- CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>();
- final OperationOutcome failedOutcome = params.makeOutcome();
- failedOutcome.setResult(PolicyResult.FAILURE);
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, failedOutcome, taskFuture);
- assertFalse(future.isDone());
-
- // complete the task
- OperationOutcome taskOutcome = params.makeOutcome();
- taskFuture.complete(taskOutcome);
-
- assertTrue(executor.runAll());
-
- // should have run the task
- assertTrue(future.isDone());
-
- assertTrue(future.isDone());
- assertSame(taskOutcome, future.get());
-
- // controller should not be done yet
- assertFalse(controller.isDone());
- }
-
- /**
- * Tests doTask(Function) when the controller is not running.
- */
- @Test
- public void testDoTaskFunctionNotRunning() throws Exception {
- AtomicBoolean invoked = new AtomicBoolean();
-
- Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = outcome -> {
- invoked.set(true);
- return CompletableFuture.completedFuture(params.makeOutcome());
- };
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
- controller.complete(params.makeOutcome());
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, task).apply(params.makeOutcome());
- assertFalse(future.isDone());
- assertTrue(executor.runAll());
-
- // should not have run the task
- assertFalse(future.isDone());
-
- // should not have even invoked the task
- assertFalse(invoked.get());
- }
-
- /**
- * Tests doTask(Function) when the previous outcome was successful.
- */
- @Test
- public void testDoTaskFunctionSuccess() throws Exception {
- final OperationOutcome taskOutcome = params.makeOutcome();
-
- final OperationOutcome failedOutcome = params.makeOutcome();
-
- Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = makeTask(taskOutcome);
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, task).apply(failedOutcome);
-
- assertTrue(future.isDone());
- assertSame(taskOutcome, future.get());
-
- // controller should not be done yet
- assertFalse(controller.isDone());
- }
-
- /**
- * Tests doTask(Function) when the previous outcome was failed.
- */
- @Test
- public void testDoTaskFunctionFailure() throws Exception {
- final OperationOutcome failedOutcome = params.makeOutcome();
- failedOutcome.setResult(PolicyResult.FAILURE);
-
- AtomicBoolean invoked = new AtomicBoolean();
-
- Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = outcome -> {
- invoked.set(true);
- return CompletableFuture.completedFuture(params.makeOutcome());
- };
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, task).apply(failedOutcome);
- assertFalse(future.isDone());
- assertTrue(executor.runAll());
-
- // should not have run the task
- assertFalse(future.isDone());
-
- // should not have even invoked the task
- assertFalse(invoked.get());
-
- // controller should have the failed task
- assertTrue(controller.isDone());
- assertSame(failedOutcome, controller.get());
- }
-
- /**
- * Tests doTask(Function) when the previous outcome was failed, but not checking
- * success.
- */
- @Test
- public void testDoTaskFunctionUncheckedFailure() throws Exception {
- final OperationOutcome taskOutcome = params.makeOutcome();
-
- final OperationOutcome failedOutcome = params.makeOutcome();
- failedOutcome.setResult(PolicyResult.FAILURE);
-
- Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = makeTask(taskOutcome);
-
- PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>();
-
- CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, task).apply(failedOutcome);
-
- assertTrue(future.isDone());
- assertSame(taskOutcome, future.get());
-
- // controller should not be done yet
- assertFalse(controller.isDone());
- }
-
/**
* Tests callbackStarted() when the pipeline has already been stopped.
*/
oper = new MyOper();
future.set(oper.start());
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
// should have only run once
assertEquals(1, numStart);
oper = new MyOper();
future.set(oper.start());
- assertTrue(executor.runAll());
+ assertTrue(executor.runAll(MAX_REQUESTS));
// should not have been set
assertNull(opend);
assertTrue(oper.isTimeout(new CompletionException(timex)));
}
+ @Test
+ public void testLogMessage() {
+ final String infraStr = SINK_INFRA.toString();
+
+ // log structured data
+ appender.clearExtractions();
+ oper.logMessage(EventType.OUT, SINK_INFRA, MY_SINK, new MyData());
+ List<String> output = appender.getExtracted();
+ assertEquals(1, output.size());
+
+ assertThat(output.get(0)).contains(infraStr).contains(MY_SINK).contains("OUT")
+ .contains("{\n \"text\": \"my-text\"\n}");
+
+ // repeat with a response
+ appender.clearExtractions();
+ oper.logMessage(EventType.IN, SOURCE_INFRA, MY_SOURCE, new MyData());
+ output = appender.getExtracted();
+ assertEquals(1, output.size());
+
+ assertThat(output.get(0)).contains(SOURCE_INFRA.toString()).contains(MY_SOURCE).contains("IN")
+ .contains("{\n \"text\": \"my-text\"\n}");
+
+ // log a plain string
+ appender.clearExtractions();
+ oper.logMessage(EventType.OUT, SINK_INFRA, MY_SINK, TEXT);
+ output = appender.getExtracted();
+ assertEquals(1, output.size());
+ assertThat(output.get(0)).contains(infraStr).contains(MY_SINK).contains(TEXT);
+
+ // log a null request
+ appender.clearExtractions();
+ oper.logMessage(EventType.OUT, SINK_INFRA, MY_SINK, null);
+ output = appender.getExtracted();
+ assertEquals(1, output.size());
+
+ assertThat(output.get(0)).contains(infraStr).contains(MY_SINK).contains("null");
+
+ // generate exception from coder
+ setOperCoderException();
+
+ appender.clearExtractions();
+ oper.logMessage(EventType.OUT, SINK_INFRA, MY_SINK, new MyData());
+ output = appender.getExtracted();
+ assertEquals(2, output.size());
+ assertThat(output.get(0)).contains("cannot pretty-print request");
+ assertThat(output.get(1)).contains(infraStr).contains(MY_SINK);
+
+ // repeat with a response
+ appender.clearExtractions();
+ oper.logMessage(EventType.IN, SOURCE_INFRA, MY_SOURCE, new MyData());
+ output = appender.getExtracted();
+ assertEquals(2, output.size());
+ assertThat(output.get(0)).contains("cannot pretty-print response");
+ assertThat(output.get(1)).contains(MY_SOURCE);
+ }
+
@Test
public void testGetRetry() {
assertEquals(0, oper.getRetry(null));
manipulator.accept(future);
- assertTrue(testName, executor.runAll());
+ assertTrue(testName, executor.runAll(MAX_REQUESTS));
assertEquals(testName, expectedCallbacks, numStart);
assertEquals(testName, expectedCallbacks, numEnd);
assertEquals(testName, expectedOperations, oper.getCount());
}
+ /**
+ * Creates a new {@link #oper} whose coder will throw an exception.
+ */
+ private void setOperCoderException() {
+ oper = new MyOper() {
+ @Override
+ protected Coder makeCoder() {
+ return new StandardCoder() {
+ @Override
+ public String encode(Object object, boolean pretty) throws CoderException {
+ throw new CoderException(EXPECTED_EXCEPTION);
+ }
+ };
+ }
+ };
+ }
+
+
+ @Getter
+ public static class MyData {
+ private String text = TEXT;
+ }
+
+
private class MyOper extends OperationPartial {
@Getter
private int count = 0;
return 0L;
}
}
-
- /**
- * Executor that will run tasks until the queue is empty or a maximum number of tasks
- * have been executed. Doesn't actually run anything until {@link #runAll()} is
- * invoked.
- */
- private static class MyExec implements Executor {
- private static final int MAX_TASKS = MAX_PARALLEL_REQUESTS * 100;
-
- private Queue<Runnable> commands = new LinkedList<>();
-
- public MyExec() {
- // do nothing
- }
-
- public int getQueueLength() {
- return commands.size();
- }
-
- @Override
- public void execute(Runnable command) {
- commands.add(command);
- }
-
- public boolean runAll() {
- for (int count = 0; count < MAX_TASKS && !commands.isEmpty(); ++count) {
- commands.remove().run();
- }
-
- return commands.isEmpty();
- }
- }
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.parameters;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Consumer;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+
+public class BidirectionalTopicActorParamsTest {
+ private static final String CONTAINER = "my-container";
+
+ private static final String DFLT_SOURCE = "default-source";
+ private static final String DFLT_SINK = "default-target";
+ private static final int DFLT_TIMEOUT = 10;
+
+ private static final String OPER1_NAME = "oper A";
+ private static final String OPER1_SOURCE = "source A";
+ private static final String OPER1_SINK = "target A";
+ private static final int OPER1_TIMEOUT = 20;
+
+ // oper2 uses some default values
+ private static final String OPER2_NAME = "oper B";
+ private static final String OPER2_SOURCE = "source B";
+
+ // oper3 uses default values for everything
+ private static final String OPER3_NAME = "oper C";
+
+ private Map<String, Map<String, Object>> operMap;
+ private BidirectionalTopicActorParams params;
+
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ BidirectionalTopicParams oper1 = BidirectionalTopicParams.builder().sourceTopic(OPER1_SOURCE)
+ .sinkTopic(OPER1_SINK).timeoutSec(OPER1_TIMEOUT).build();
+
+ Map<String, Object> oper1Map = Util.translateToMap(OPER1_NAME, oper1);
+ Map<String, Object> oper2Map = Map.of("source", OPER2_SOURCE);
+ Map<String, Object> oper3Map = Collections.emptyMap();
+ operMap = Map.of(OPER1_NAME, oper1Map, OPER2_NAME, oper2Map, OPER3_NAME, oper3Map);
+
+ params = makeBidirectionalTopicActorParams();
+ }
+
+ @Test
+ public void testValidate() {
+ assertTrue(params.validate(CONTAINER).isValid());
+
+ // only a few fields are required
+ BidirectionalTopicActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operMap, "timeoutSec", 1),
+ BidirectionalTopicActorParams.class);
+ assertTrue(sparse.validate(CONTAINER).isValid());
+
+ testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
+
+ // check edge cases
+ params.setTimeoutSec(0);
+ assertFalse(params.validate(CONTAINER).isValid());
+
+ params.setTimeoutSec(1);
+ assertTrue(params.validate(CONTAINER).isValid());
+ }
+
+ private void testValidateField(String fieldName, String expected,
+ Consumer<BidirectionalTopicActorParams> makeInvalid) {
+
+ // original params should be valid
+ ValidationResult result = params.validate(CONTAINER);
+ assertTrue(fieldName, result.isValid());
+
+ // make invalid params
+ BidirectionalTopicActorParams params2 = makeBidirectionalTopicActorParams();
+ makeInvalid.accept(params2);
+ result = params2.validate(CONTAINER);
+ assertFalse(fieldName, result.isValid());
+ assertThat(result.getResult()).contains(CONTAINER).contains(fieldName).contains(expected);
+ }
+
+ private BidirectionalTopicActorParams makeBidirectionalTopicActorParams() {
+ BidirectionalTopicActorParams params2 = new BidirectionalTopicActorParams();
+ params2.setSinkTopic(DFLT_SINK);
+ params2.setSourceTopic(DFLT_SOURCE);
+ params2.setTimeoutSec(DFLT_TIMEOUT);
+ params2.setOperation(operMap);
+
+ return params2;
+ }
+}
import org.junit.Before;
import org.junit.Test;
import org.onap.policy.common.parameters.ValidationResult;
-import org.onap.policy.controlloop.actorserviceprovider.parameters.TopicParams.TopicParamsBuilder;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicParams.BidirectionalTopicParamsBuilder;
-public class TopicParamsTest {
+public class BidirectionalTopicParamsTest {
private static final String CONTAINER = "my-container";
- private static final String TARGET = "my-target";
+ private static final String SINK = "my-sink";
private static final String SOURCE = "my-source";
- private static final long TIMEOUT = 10;
+ private static final int TIMEOUT = 10;
- private TopicParams params;
+ private BidirectionalTopicParams params;
@Before
public void setUp() {
- params = TopicParams.builder().target(TARGET).source(SOURCE).timeoutSec(TIMEOUT).build();
+ params = BidirectionalTopicParams.builder().sinkTopic(SINK).sourceTopic(SOURCE).timeoutSec(TIMEOUT).build();
}
@Test
public void testValidate() {
- testValidateField("target", "null", bldr -> bldr.target(null));
- testValidateField("source", "null", bldr -> bldr.source(null));
+ assertTrue(params.validate(CONTAINER).isValid());
+
+ testValidateField("sink", "null", bldr -> bldr.sinkTopic(null));
+ testValidateField("source", "null", bldr -> bldr.sourceTopic(null));
testValidateField("timeoutSec", "minimum", bldr -> bldr.timeoutSec(-1));
// check edge cases
- assertTrue(params.toBuilder().timeoutSec(0).build().validate(CONTAINER).isValid());
+ assertFalse(params.toBuilder().timeoutSec(0).build().validate(CONTAINER).isValid());
assertTrue(params.toBuilder().timeoutSec(1).build().validate(CONTAINER).isValid());
}
@Test
public void testBuilder_testToBuilder() {
- assertEquals(TARGET, params.getTarget());
- assertEquals(SOURCE, params.getSource());
+ assertEquals(SINK, params.getSinkTopic());
+ assertEquals(SOURCE, params.getSourceTopic());
assertEquals(TIMEOUT, params.getTimeoutSec());
assertEquals(params, params.toBuilder().build());
}
private void testValidateField(String fieldName, String expected,
- Function<TopicParamsBuilder, TopicParamsBuilder> makeInvalid) {
+ Function<BidirectionalTopicParamsBuilder, BidirectionalTopicParamsBuilder> makeInvalid) {
// original params should be valid
ValidationResult result = params.validate(CONTAINER);
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.parameters;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import lombok.Setter;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+
+public class CommonActorParamsTest {
+
+ private static final String CONTAINER = "my-container";
+
+ private static final String PATH1 = "path #1";
+ private static final String PATH2 = "path #2";
+ private static final String URI1 = "uri #1";
+ private static final String URI2 = "uri #2";
+ private static final String TEXT1 = "hello";
+ private static final String TEXT2 = "world";
+ private static final String TEXT2B = "bye";
+
+ private Map<String, Map<String, Object>> operations;
+ private CommonActorParams params;
+
+ /**
+ * Initializes {@link #operations} with two items and {@link params} with a fully
+ * populated object.
+ */
+ @Before
+ public void setUp() {
+ operations = new TreeMap<>();
+ operations.put(PATH1, Map.of("path", URI1));
+ operations.put(PATH2, Map.of("path", URI2, "text2", TEXT2B));
+
+ params = makeCommonActorParams();
+ }
+
+ @Test
+ public void testMakeOperationParameters() {
+ Function<String, Map<String, Object>> maker = params.makeOperationParameters(CONTAINER);
+ assertNull(maker.apply("unknown-operation"));
+
+ Map<String, Object> subparam = maker.apply(PATH1);
+ assertNotNull(subparam);
+ assertEquals("{path=uri #1, text1=hello, text2=world}", new TreeMap<>(subparam).toString());
+
+ subparam = maker.apply(PATH2);
+ assertNotNull(subparam);
+ assertEquals("{path=uri #2, text1=hello, text2=bye}", new TreeMap<>(subparam).toString());
+ }
+
+ @Test
+ public void testDoValidation() {
+ assertThatCode(() -> params.doValidation(CONTAINER)).doesNotThrowAnyException();
+
+ // invalid param
+ params.setOperation(null);
+ assertThatThrownBy(() -> params.doValidation(CONTAINER))
+ .isInstanceOf(ParameterValidationRuntimeException.class);
+ }
+
+ @Test
+ public void testValidate() {
+ assertTrue(params.validate(CONTAINER).isValid());
+
+ // only a few fields are required
+ CommonActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations, "timeoutSec", 1),
+ CommonActorParams.class);
+ assertTrue(sparse.validate(CONTAINER).isValid());
+
+ testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ }
+
+ private void testValidateField(String fieldName, String expected, Consumer<CommonActorParams> makeInvalid) {
+
+ // original params should be valid
+ ValidationResult result = params.validate(CONTAINER);
+ assertTrue(fieldName, result.isValid());
+
+ // make invalid params
+ CommonActorParams params2 = makeCommonActorParams();
+ makeInvalid.accept(params2);
+ result = params2.validate(CONTAINER);
+ assertFalse(fieldName, result.isValid());
+ assertThat(result.getResult()).contains(CONTAINER).contains(fieldName).contains(expected);
+ }
+
+ private CommonActorParams makeCommonActorParams() {
+ MyParams params2 = new MyParams();
+ params2.setOperation(operations);
+ params2.setText1(TEXT1);
+ params2.setText2(TEXT2);
+
+ return params2;
+ }
+
+ @Setter
+ public static class MyParams extends CommonActorParams {
+ @SuppressWarnings("unused")
+ private String text1;
+
+ @SuppressWarnings("unused")
+ private String text2;
+ }
+}
package org.onap.policy.controlloop.actorserviceprovider.parameters;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Consumer;
-import java.util.function.Function;
import org.junit.Before;
import org.junit.Test;
import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
public class HttpActorParamsTest {
private static final String URI1 = "uri #1";
private static final String URI2 = "uri #2";
- private Map<String, String> paths;
+ private Map<String, Map<String, Object>> operations;
private HttpActorParams params;
/**
- * Initializes {@link #paths} with two items and {@link params} with a fully populated
- * object.
+ * Initializes {@link #operations} with two items and {@link params} with a fully
+ * populated object.
*/
@Before
public void setUp() {
- paths = new TreeMap<>();
- paths.put(PATH1, URI1);
- paths.put(PATH2, URI2);
+ operations = new TreeMap<>();
+ operations.put(PATH1, Map.of("path", URI1));
+ operations.put(PATH2, Map.of("path", URI2));
params = makeHttpActorParams();
}
- @Test
- public void testMakeOperationParameters() {
- Function<String, Map<String, Object>> maker = params.makeOperationParameters(CONTAINER);
- assertNull(maker.apply("unknown-operation"));
-
- Map<String, Object> subparam = maker.apply(PATH1);
- assertNotNull(subparam);
- assertEquals("{clientName=my-client, path=uri #1, timeoutSec=10}", new TreeMap<>(subparam).toString());
-
- subparam = maker.apply(PATH2);
- assertNotNull(subparam);
- assertEquals("{clientName=my-client, path=uri #2, timeoutSec=10}", new TreeMap<>(subparam).toString());
- }
-
- @Test
- public void testDoValidation() {
- assertThatCode(() -> params.doValidation(CONTAINER)).doesNotThrowAnyException();
-
- // invalid param
- params.setClientName(null);
- assertThatThrownBy(() -> params.doValidation(CONTAINER))
- .isInstanceOf(ParameterValidationRuntimeException.class);
- }
-
@Test
public void testValidate() {
assertTrue(params.validate(CONTAINER).isValid());
- testValidateField("clientName", "null", params2 -> params2.setClientName(null));
- testValidateField("path", "null", params2 -> params2.setPath(null));
+ // only a few fields are required
+ HttpActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations, "timeoutSec", 1),
+ HttpActorParams.class);
+ assertTrue(sparse.validate(CONTAINER).isValid());
+
+ testValidateField("operation", "null", params2 -> params2.setOperation(null));
testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
// check edge cases
params.setTimeoutSec(0);
- assertTrue(params.validate(CONTAINER).isValid());
+ assertFalse(params.validate(CONTAINER).isValid());
params.setTimeoutSec(1);
assertTrue(params.validate(CONTAINER).isValid());
-
- // one path value is null
- testValidateField(PATH2, "null", params2 -> paths.put(PATH2, null));
}
private void testValidateField(String fieldName, String expected, Consumer<HttpActorParams> makeInvalid) {
HttpActorParams params2 = new HttpActorParams();
params2.setClientName(CLIENT);
params2.setTimeoutSec(TIMEOUT);
- params2.setPath(paths);
+ params2.setOperation(operations);
return params2;
}
testValidateField("timeoutSec", "minimum", bldr -> bldr.timeoutSec(-1));
// check edge cases
- assertTrue(params.toBuilder().timeoutSec(0).build().validate(CONTAINER).isValid());
+ assertFalse(params.toBuilder().timeoutSec(0).build().validate(CONTAINER).isValid());
assertTrue(params.toBuilder().timeoutSec(1).build().validate(CONTAINER).isValid());
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.event.comm.TopicSource;
+import org.onap.policy.common.endpoints.event.comm.client.BidirectionalTopicClientException;
+
+public class BidirectionalTopicHandlerTest {
+ private static final String UNKNOWN = "unknown";
+ private static final String MY_SOURCE = "my-source";
+ private static final String MY_SINK = "my-sink";
+ private static final String KEY1 = "requestId";
+ private static final String KEY2 = "subRequestId";
+
+ @Mock
+ private TopicSink publisher;
+
+ @Mock
+ private TopicSource subscriber;
+
+ @Mock
+ private TopicEndpoint mgr;
+
+ private MyTopicHandler handler;
+
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() throws BidirectionalTopicClientException {
+ MockitoAnnotations.initMocks(this);
+
+ when(mgr.getTopicSinks(MY_SINK)).thenReturn(Arrays.asList(publisher));
+ when(mgr.getTopicSources(eq(Arrays.asList(MY_SOURCE)))).thenReturn(Arrays.asList(subscriber));
+
+ when(publisher.getTopicCommInfrastructure()).thenReturn(CommInfrastructure.NOOP);
+
+ handler = new MyTopicHandler(MY_SINK, MY_SOURCE);
+
+ handler.start();
+ }
+
+ @Test
+ public void testBidirectionalTopicHandler_testGetSource_testGetTarget() {
+ assertEquals(MY_SOURCE, handler.getSourceTopic());
+ assertEquals(MY_SINK, handler.getSinkTopic());
+
+ verify(mgr).getTopicSinks(anyString());
+ verify(mgr).getTopicSources(any());
+
+ // source not found
+ assertThatThrownBy(() -> new MyTopicHandler(MY_SINK, UNKNOWN))
+ .isInstanceOf(BidirectionalTopicClientException.class).hasMessageContaining("sources")
+ .hasMessageContaining(UNKNOWN);
+
+ // target not found
+ assertThatThrownBy(() -> new MyTopicHandler(UNKNOWN, MY_SOURCE))
+ .isInstanceOf(BidirectionalTopicClientException.class).hasMessageContaining("sinks")
+ .hasMessageContaining(UNKNOWN);
+ }
+
+ @Test
+ public void testShutdown() {
+ handler.shutdown();
+ verify(subscriber).unregister(any());
+ }
+
+ @Test
+ public void testStart() {
+ verify(subscriber).register(any());
+ }
+
+ @Test
+ public void testStop() {
+ handler.stop();
+ verify(subscriber).unregister(any());
+ }
+
+ @Test
+ public void testAddForwarder() {
+ // array form
+ Forwarder forwarder = handler.addForwarder(new SelectorKey(KEY1), new SelectorKey(KEY2));
+ assertNotNull(forwarder);
+
+ // repeat using list form
+ assertSame(forwarder, handler.addForwarder(Arrays.asList(new SelectorKey(KEY1), new SelectorKey(KEY2))));
+ }
+
+ @Test
+ public void testGetTopicEndpointManager() {
+ // setting "mgr" to null should cause it to use the superclass' method
+ mgr = null;
+ assertNotNull(handler.getTopicEndpointManager());
+ }
+
+
+ private class MyTopicHandler extends BidirectionalTopicHandler {
+ public MyTopicHandler(String sinkTopic, String sourceTopic) throws BidirectionalTopicClientException {
+ super(sinkTopic, sourceTopic);
+ }
+
+ @Override
+ protected TopicEndpoint getTopicEndpointManager() {
+ return (mgr != null ? mgr : super.getTopicEndpointManager());
+ }
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+
+public class ForwarderTest {
+ private static final String TEXT = "some text";
+
+ private static final String KEY1 = "requestId";
+ private static final String KEY2 = "container";
+ private static final String SUBKEY = "subRequestId";
+
+ private static final String VALUEA_REQID = "hello";
+ private static final String VALUEA_SUBREQID = "world";
+
+ // request id is shared with value A
+ private static final String VALUEB_REQID = "hello";
+ private static final String VALUEB_SUBREQID = "another world";
+
+ // unique values
+ private static final String VALUEC_REQID = "bye";
+ private static final String VALUEC_SUBREQID = "bye-bye";
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener1;
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener1b;
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener2;
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener3;
+
+ private Forwarder forwarder;
+
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ forwarder = new Forwarder(Arrays.asList(new SelectorKey(KEY1), new SelectorKey(KEY2, SUBKEY)));
+
+ forwarder.register(Arrays.asList(VALUEA_REQID, VALUEA_SUBREQID), listener1);
+ forwarder.register(Arrays.asList(VALUEA_REQID, VALUEA_SUBREQID), listener1b);
+ forwarder.register(Arrays.asList(VALUEB_REQID, VALUEB_SUBREQID), listener2);
+ forwarder.register(Arrays.asList(VALUEC_REQID, VALUEC_SUBREQID), listener3);
+ }
+
+ @Test
+ public void testRegister() {
+ // key size mismatches
+ assertThatIllegalArgumentException().isThrownBy(() -> forwarder.register(Arrays.asList(), listener1))
+ .withMessage("key/value mismatch");
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> forwarder.register(Arrays.asList(VALUEA_REQID), listener1))
+ .withMessage("key/value mismatch");
+ }
+
+ @Test
+ public void testUnregister() {
+ // remove listener1b
+ forwarder.unregister(Arrays.asList(VALUEA_REQID, VALUEA_SUBREQID), listener1b);
+
+ StandardCoderObject sco = makeMessage(Map.of(KEY1, VALUEA_REQID, KEY2, Map.of(SUBKEY, VALUEA_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+
+ verify(listener1).accept(TEXT, sco);
+ verify(listener1b, never()).accept(any(), any());
+
+ // remove listener1
+ forwarder.unregister(Arrays.asList(VALUEA_REQID, VALUEA_SUBREQID), listener1);
+ forwarder.onMessage(TEXT, sco);
+
+ // route a message to listener2
+ sco = makeMessage(Map.of(KEY1, VALUEB_REQID, KEY2, Map.of(SUBKEY, VALUEB_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+ verify(listener2).accept(TEXT, sco);
+
+ // no more messages to listener1 or 1b
+ verify(listener1).accept(any(), any());
+ verify(listener1b, never()).accept(any(), any());
+ }
+
+ @Test
+ public void testOnMessage() {
+ StandardCoderObject sco = makeMessage(Map.of(KEY1, VALUEA_REQID, KEY2, Map.of(SUBKEY, VALUEA_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+
+ verify(listener1).accept(TEXT, sco);
+ verify(listener1b).accept(TEXT, sco);
+
+ // repeat - counts should increment
+ forwarder.onMessage(TEXT, sco);
+
+ verify(listener1, times(2)).accept(TEXT, sco);
+ verify(listener1b, times(2)).accept(TEXT, sco);
+
+ // should not have been invoked
+ verify(listener2, never()).accept(any(), any());
+ verify(listener3, never()).accept(any(), any());
+
+ // try other listeners now
+ sco = makeMessage(Map.of(KEY1, VALUEB_REQID, KEY2, Map.of(SUBKEY, VALUEB_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+ verify(listener2).accept(TEXT, sco);
+
+ sco = makeMessage(Map.of(KEY1, VALUEC_REQID, KEY2, Map.of(SUBKEY, VALUEC_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+ verify(listener3).accept(TEXT, sco);
+
+ // message has no listeners
+ sco = makeMessage(Map.of(KEY1, "xyzzy", KEY2, Map.of(SUBKEY, VALUEB_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+
+ // message doesn't have both keys
+ sco = makeMessage(Map.of(KEY1, VALUEA_REQID));
+ forwarder.onMessage(TEXT, sco);
+
+ // counts should not have incremented
+ verify(listener1, times(2)).accept(any(), any());
+ verify(listener1b, times(2)).accept(any(), any());
+ verify(listener2).accept(any(), any());
+ verify(listener3).accept(any(), any());
+
+ // listener throws an exception
+ doThrow(new IllegalStateException("expected exception")).when(listener1).accept(any(), any());
+ }
+
+ /*
+ * Tests onMessage() when listener1 throws an exception.
+ */
+ @Test
+ public void testOnMessageListenerException1() {
+ doThrow(new IllegalStateException("expected exception")).when(listener1).accept(any(), any());
+
+ StandardCoderObject sco = makeMessage(Map.of(KEY1, VALUEA_REQID, KEY2, Map.of(SUBKEY, VALUEA_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+
+ verify(listener1b).accept(TEXT, sco);
+ }
+
+ /*
+ * Tests onMessage() when listener1b throws an exception.
+ */
+ @Test
+ public void testOnMessageListenerException1b() {
+ doThrow(new IllegalStateException("expected exception")).when(listener1b).accept(any(), any());
+
+ StandardCoderObject sco = makeMessage(Map.of(KEY1, VALUEA_REQID, KEY2, Map.of(SUBKEY, VALUEA_SUBREQID)));
+ forwarder.onMessage(TEXT, sco);
+
+ verify(listener1).accept(TEXT, sco);
+ }
+
+ /**
+ * Makes a message from a map.
+ */
+ private StandardCoderObject makeMessage(Map<String, Object> map) {
+ return Util.translate("", map, StandardCoderObject.class);
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+
+public class SelectorKeyTest {
+ private static final String FIELD1 = "map";
+ private static final String FIELD2 = "abc";
+ private static final String FIELDX = "abd";
+
+ private SelectorKey key;
+
+ @Before
+ public void setUp() {
+ key = new SelectorKey(FIELD1, FIELD2);
+ }
+
+ @Test
+ public void testHashCode_testEquals() {
+ SelectorKey key2 = new SelectorKey(FIELD1, FIELD2);
+ assertEquals(key, key2);
+ assertEquals(key.hashCode(), key2.hashCode());
+
+ key2 = new SelectorKey(FIELD1, FIELDX);
+ assertNotEquals(key, key2);
+ assertNotEquals(key.hashCode(), key2.hashCode());
+
+ // test empty key
+ key = new SelectorKey();
+ key2 = new SelectorKey();
+ assertEquals(key, key2);
+ assertEquals(key.hashCode(), key2.hashCode());
+ }
+
+ @Test
+ public void testExtractField() {
+ Map<String, Object> map = Map.of("hello", "world", FIELD1, Map.of("another", "", FIELD2, "value B"));
+ StandardCoderObject sco = Util.translate("", map, StandardCoderObject.class);
+
+ String result = key.extractField(sco);
+ assertNotNull(result);
+ assertEquals("value B", result);
+
+ // shorter key
+ assertEquals("world", new SelectorKey("hello").extractField(sco));
+ assertNull(new SelectorKey("bye").extractField(sco));
+
+ // not found
+ assertNull(new SelectorKey(FIELD1, "not field 2").extractField(sco));
+
+ // test with empty key
+ assertNull(new SelectorKey().extractField(sco));
+ }
+
+ @Getter
+ @Setter
+ @Builder
+ protected static class Data {
+ private String text;
+ private Map<String, String> map;
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.controlloop.actorserviceprovider.topic;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+
+public class TopicListenerImplTest {
+ private static final StandardCoder coder = new StandardCoder();
+ private static final CommInfrastructure INFRA = CommInfrastructure.NOOP;
+ private static final String MY_TOPIC = "my-topic";
+ private static final String KEY1 = "requestId";
+ private static final String KEY2 = "container";
+ private static final String SUBKEY = "subRequestId";
+
+ private static final String VALUEA_REQID = "hello";
+ private static final String VALUEA_SUBREQID = "world";
+
+ private static final String VALUEB_REQID = "bye";
+
+ private Forwarder forwarder1;
+ private Forwarder forwarder2;
+ private TopicListenerImpl topic;
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener1;
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener1b;
+
+ @Mock
+ private BiConsumer<String, StandardCoderObject> listener2;
+
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ topic = new TopicListenerImpl();
+
+ forwarder1 = topic.addForwarder(new SelectorKey(KEY1));
+ forwarder2 = topic.addForwarder(new SelectorKey(KEY1), new SelectorKey(KEY2, SUBKEY));
+
+ assertNotNull(forwarder1);
+ assertNotNull(forwarder2);
+ assertNotSame(forwarder1, forwarder2);
+
+ forwarder1.register(Arrays.asList(VALUEA_REQID), listener1);
+ forwarder1.register(Arrays.asList(VALUEB_REQID), listener1b);
+ forwarder2.register(Arrays.asList(VALUEA_REQID, VALUEA_SUBREQID), listener2);
+ }
+
+ @Test
+ public void testShutdown() {
+ // shut it down, which should clear all forwarders
+ topic.shutdown();
+
+ // should get a new forwarder now
+ Forwarder forwarder = topic.addForwarder(new SelectorKey(KEY1));
+ assertNotSame(forwarder1, forwarder);
+ assertNotSame(forwarder2, forwarder);
+
+ // new forwarder should be unchanged
+ assertSame(forwarder, topic.addForwarder(new SelectorKey(KEY1)));
+ }
+
+ @Test
+ public void testAddForwarder() {
+ assertSame(forwarder1, topic.addForwarder(new SelectorKey(KEY1)));
+ assertSame(forwarder2, topic.addForwarder(new SelectorKey(KEY1), new SelectorKey(KEY2, SUBKEY)));
+ }
+
+ @Test
+ public void testOnTopicEvent() {
+ /*
+ * send a message that should go to listener1 on forwarder1 and listener2 on
+ * forwarder2
+ */
+ String msg = makeMessage(Map.of(KEY1, VALUEA_REQID, KEY2, Map.of(SUBKEY, VALUEA_SUBREQID)));
+ topic.onTopicEvent(INFRA, MY_TOPIC, msg);
+
+ verify(listener1).accept(eq(msg), any());
+ verify(listener2).accept(eq(msg), any());
+
+ // not to listener1b
+ verify(listener1b, never()).accept(any(), any());
+
+ /*
+ * now send a message that should only go to listener1b on forwarder1
+ */
+ msg = makeMessage(Map.of(KEY1, VALUEB_REQID, KEY2, Map.of(SUBKEY, VALUEA_SUBREQID)));
+ topic.onTopicEvent(INFRA, MY_TOPIC, msg);
+
+ // should route to listener1 on forwarder1 and listener2 on forwarder2
+ verify(listener1b).accept(eq(msg), any());
+
+ // try one where the coder throws an exception
+ topic.onTopicEvent(INFRA, MY_TOPIC, "{invalid-json");
+
+ // no extra invocations
+ verify(listener1).accept(any(), any());
+ verify(listener1b).accept(any(), any());
+ verify(listener2).accept(any(), any());
+ }
+
+ /**
+ * Makes a message from a map.
+ */
+ private String makeMessage(Map<String, Object> map) {
+ try {
+ return coder.encode(map);
+ } catch (CoderException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+}
<appender-ref ref="STDOUT" />
</logger>
- <!-- this is required for HttpOperationTest -->
- <logger name="org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation" level="info" additivity="false">
+ <!-- this is required for OperationPartialTest -->
+ <logger
+ name="org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial"
+ level="info" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
</configuration>
<modules>
<module>actorServiceProvider</module>
<module>actor.test</module>
+ <module>actor.aai</module>
<module>actor.appc</module>
<module>actor.vfc</module>
<module>actor.sdnc</module>
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2019 Nordix Foundation.
+ * Copyright (C) 2019-2020 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import java.io.Serializable;
import javax.persistence.Embeddable;
-import lombok.Getter;
+import lombok.Data;
import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.ToString;
+import lombok.NonNull;
import org.onap.policy.models.pdp.enums.PdpEngineWorkerState;
@Embeddable
-@Getter
-@Setter
-@ToString
+@Data
@NoArgsConstructor
public class PdpEngineWorkerStatistics implements Serializable {
private static final long serialVersionUID = 8262176849743624013L;
*
* @param source source from which to copy
*/
- public PdpEngineWorkerStatistics(PdpEngineWorkerStatistics source) {
+ public PdpEngineWorkerStatistics(@NonNull PdpEngineWorkerStatistics source) {
this.engineId = source.engineId;
this.engineWorkerState = source.engineWorkerState;
this.engineTimeStamp = source.engineTimeStamp;
* ============LICENSE_START=======================================================
* Copyright (C) 2019 Nordix Foundation.
* Modifications Copyright (C) 2019 AT&T Intellectual Property.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import java.util.Date;
import java.util.List;
-import lombok.Getter;
+import lombok.Data;
import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.ToString;
+import lombok.NonNull;
import org.onap.policy.models.base.PfUtils;
/**
*
* @author Ram Krishna Verma (ram.krishna.verma@est.tech)
*/
-@Getter
-@Setter
-@ToString
+@Data
@NoArgsConstructor
public class PdpStatistics {
*
* @param source source from which to copy
*/
- public PdpStatistics(PdpStatistics source) {
+ public PdpStatistics(@NonNull PdpStatistics source) {
this.pdpInstanceId = source.pdpInstanceId;
this.timeStamp = source.timeStamp == null ? null : new Date(source.timeStamp.getTime());
this.pdpGroupName = source.pdpGroupName;
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.concepts;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Date;
+import org.junit.Test;
+import org.onap.policy.models.pdp.enums.PdpEngineWorkerState;
+
+public class PdpEngineWorkerStatisticsTest {
+
+ @Test
+ public void testCopyConstructor() {
+ assertThatThrownBy(() -> new PdpEngineWorkerStatistics(null)).hasMessageContaining("source");
+
+ PdpEngineWorkerStatistics stat = createPdpEngineWorkerStatistics();
+ PdpEngineWorkerStatistics stat2 = new PdpEngineWorkerStatistics(stat);
+ assertEquals(stat, stat2);
+ }
+
+ @Test
+ public void testClean() {
+ PdpEngineWorkerStatistics stat = createPdpEngineWorkerStatistics();
+ stat.setEngineId(" Engine0 ");
+ stat.clean();
+ assertEquals("Engine0", stat.getEngineId());
+ }
+
+ private PdpEngineWorkerStatistics createPdpEngineWorkerStatistics() {
+ PdpEngineWorkerStatistics stat = new PdpEngineWorkerStatistics();
+ stat.setEngineId("Engine0");
+ stat.setEngineWorkerState(PdpEngineWorkerState.READY);
+ stat.setEngineTimeStamp(new Date().getTime());
+ stat.setEventCount(1);
+ stat.setLastExecutionTime(100);
+ stat.setAverageExecutionTime(99);
+ stat.setUpTime(1000);
+ stat.setLastEnterTime(2000);
+ stat.setLastStart(3000);
+ return stat;
+ }
+}
\ No newline at end of file
* ONAP Policy Models
* ================================================================================
* Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
-import static org.onap.policy.models.pdp.concepts.PdpMessageUtils.removeVariableFields;
+import java.util.ArrayList;
+import java.util.Date;
import org.junit.Test;
public class PdpStatisticsTest {
@Test
public void testCopyConstructor() {
- assertThatThrownBy(() -> new PdpStatistics(null)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> new PdpStatistics(null)).hasMessageContaining("source");
- PdpStatistics orig = new PdpStatistics();
-
- // verify with null values
- assertEquals(removeVariableFields(orig.toString()), removeVariableFields(new PdpStatistics(orig).toString()));
-
- // verify with all values
- orig.setPdpInstanceId("my-instance");
-
- int count = 1;
- orig.setPolicyDeployCount(count++);
- orig.setPolicyDeployFailCount(count++);
- orig.setPolicyDeploySuccessCount(count++);
- orig.setPolicyExecutedCount(count++);
- orig.setPolicyExecutedFailCount(count++);
- orig.setPolicyExecutedSuccessCount(count++);
+ PdpStatistics orig = createPdpStatistics();
+ PdpStatistics copied = new PdpStatistics(orig);
+ assertEquals(orig, copied);
+ }
- assertEquals(removeVariableFields(orig.toString()), removeVariableFields(new PdpStatistics(orig).toString()));
+ private PdpStatistics createPdpStatistics() {
+ PdpStatistics pdpStat = new PdpStatistics();
+ pdpStat.setPdpInstanceId("PDP0");
+ pdpStat.setPdpGroupName("PDPGroup0");
+ pdpStat.setPdpSubGroupName("PDPSubGroup0");
+ pdpStat.setTimeStamp(new Date());
+ pdpStat.setPolicyDeployCount(3);
+ pdpStat.setPolicyDeploySuccessCount(1);
+ pdpStat.setPolicyDeployFailCount(2);
+ pdpStat.setPolicyExecutedCount(9);
+ pdpStat.setPolicyExecutedSuccessCount(4);
+ pdpStat.setPolicyExecutedFailCount(5);
+ pdpStat.setEngineStats(new ArrayList<>());
+ return pdpStat;
}
}
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2019 Nordix Foundation.
+ * Copyright (C) 2019-2020 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.policy.models.pdp.persistence.concepts;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import java.util.ArrayList;
+import java.util.Date;
import org.junit.Test;
+import org.onap.policy.models.base.PfTimestampKey;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.pdp.concepts.PdpStatistics;
/**
* Test the {@link JpaPdpStatistics} class.
- *
*/
public class JpaPdpStatisticsTest {
- private static final String NULL_KEY_ERROR = "key is marked @NonNull but is null";
- private static final String PDP1 = "ThePDP";
- // TODO More unit test cases will be added later.
@Test
- public void testJpaPdpStatistics() {
- assertThatThrownBy(() -> {
- new JpaPdpStatistics((JpaPdpStatistics) null);
- }).hasMessage("copyConcept is marked @NonNull but is null");
+ public void testConstructor() {
+ assertThatThrownBy(() -> new JpaPdpStatistics((PfTimestampKey) null)).hasMessageContaining("key");
+
+ assertThatThrownBy(() -> new JpaPdpStatistics((JpaPdpStatistics) null))
+ .hasMessageContaining("copyConcept");
+
+ assertThatThrownBy(() -> new JpaPdpStatistics((PdpStatistics) null))
+ .hasMessageContaining("authorativeConcept");
+
+ assertNotNull(new JpaPdpStatistics());
+ assertNotNull(new JpaPdpStatistics(new PfTimestampKey()));
+
+ PdpStatistics pdpStat = createPdpStatistics();
+ JpaPdpStatistics jpaPdpStat = new JpaPdpStatistics(createPdpStatistics());
+ checkEquals(pdpStat, jpaPdpStat);
+
+ JpaPdpStatistics jpaPdpStat2 = new JpaPdpStatistics(jpaPdpStat);
+ assertEquals(0, jpaPdpStat2.compareTo(jpaPdpStat));
+ }
+
+ @Test
+ public void testFromAuthorative() {
+ PdpStatistics pdpStat = createPdpStatistics();
+ JpaPdpStatistics jpaPdpStat = new JpaPdpStatistics();
+ jpaPdpStat.fromAuthorative(pdpStat);
+ checkEquals(pdpStat, jpaPdpStat);
+ }
+
+ @Test
+ public void testToAuthorative() {
+ PdpStatistics pdpStat = createPdpStatistics();
+ JpaPdpStatistics jpaPdpStat = new JpaPdpStatistics(pdpStat);
+ PdpStatistics toPdpStat = jpaPdpStat.toAuthorative();
+ assertEquals(pdpStat, toPdpStat);
+ }
+
+ @Test
+ public void testCompareTo() {
+ PdpStatistics pdpStat = createPdpStatistics();
+ JpaPdpStatistics jpaPdpStat1 = new JpaPdpStatistics(pdpStat);
+ assertEquals(-1, jpaPdpStat1.compareTo(null));
+
+ JpaPdpStatistics jpaPdpStat2 = new JpaPdpStatistics(pdpStat);
+ assertEquals(0, jpaPdpStat1.compareTo(jpaPdpStat2));
+
+ PdpStatistics pdpStat3 = createPdpStatistics();
+ pdpStat3.setPdpInstanceId("PDP3");
+ JpaPdpStatistics jpaPdpStat3 = new JpaPdpStatistics(pdpStat3);
+ assertNotEquals(0, jpaPdpStat1.compareTo(jpaPdpStat3));
+ }
+
+ @Test
+ public void testValidate() {
+ JpaPdpStatistics nullKeyJpaPdpStat = new JpaPdpStatistics();
+ assertFalse(nullKeyJpaPdpStat.validate(new PfValidationResult()).isOk());
+
+ PdpStatistics pdpStat = createPdpStatistics();
+ JpaPdpStatistics jpaPdpStat2 = new JpaPdpStatistics(pdpStat);
+ assertTrue(jpaPdpStat2.validate(new PfValidationResult()).isOk());
+ }
+
+ @Test
+ public void testClean() {
+ PdpStatistics pdpStat = createPdpStatistics();
+ JpaPdpStatistics jpaPdpStat = new JpaPdpStatistics(pdpStat);
+ jpaPdpStat.setPdpGroupName(" PDPGroup0 ");
+ jpaPdpStat.setPdpSubGroupName(" PDPSubGroup0 ");
+ jpaPdpStat.clean();
+ assertEquals("PDPGroup0", jpaPdpStat.getPdpGroupName());
+ assertEquals("PDPSubGroup0", jpaPdpStat.getPdpSubGroupName());
+ }
+
+ private void checkEquals(PdpStatistics pdpStat, JpaPdpStatistics jpaPdpStat) {
+ assertEquals(pdpStat.getPdpInstanceId(), jpaPdpStat.getKey().getName());
+ assertEquals(pdpStat.getPdpGroupName(), jpaPdpStat.getPdpGroupName());
+ assertEquals(pdpStat.getPdpSubGroupName(), jpaPdpStat.getPdpSubGroupName());
+ assertEquals(pdpStat.getTimeStamp(), jpaPdpStat.getKey().getTimeStamp());
+ assertEquals(pdpStat.getPolicyDeployCount(), jpaPdpStat.getPolicyDeployCount());
+ assertEquals(pdpStat.getPolicyDeploySuccessCount(), jpaPdpStat.getPolicyDeploySuccessCount());
+ assertEquals(pdpStat.getPolicyDeployFailCount(), jpaPdpStat.getPolicyDeployFailCount());
+ assertEquals(pdpStat.getPolicyExecutedCount(), jpaPdpStat.getPolicyExecutedCount());
+ assertEquals(pdpStat.getPolicyExecutedSuccessCount(), jpaPdpStat.getPolicyExecutedSuccessCount());
+ assertEquals(pdpStat.getPolicyExecutedFailCount(), jpaPdpStat.getPolicyExecutedFailCount());
+ }
+
+ private PdpStatistics createPdpStatistics() {
+ PdpStatistics pdpStat = new PdpStatistics();
+ pdpStat.setPdpInstanceId("PDP0");
+ pdpStat.setPdpGroupName("PDPGroup0");
+ pdpStat.setPdpSubGroupName("PDPSubGroup0");
+ pdpStat.setTimeStamp(new Date());
+ pdpStat.setPolicyDeployCount(3);
+ pdpStat.setPolicyDeploySuccessCount(1);
+ pdpStat.setPolicyDeployFailCount(2);
+ pdpStat.setPolicyExecutedCount(9);
+ pdpStat.setPolicyExecutedSuccessCount(4);
+ pdpStat.setPolicyExecutedFailCount(5);
+ pdpStat.setEngineStats(new ArrayList<>());
+ return pdpStat;
}
}
<class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpSubGroup</class>
<class>org.onap.policy.models.pdp.persistence.concepts.JpaPdp</class>
<class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpStatistics</class>
+ <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaTrigger</class>
+ <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaProperty</class>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
/*-
* ============LICENSE_START=======================================================
* Copyright (C) 2019-2020 Nordix Foundation.
+ * Modifications Copyright (C) 2020 AT&T.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// @formatter:off
private String[] policyInputResourceNames = {
- "policies/vCPE.policy.operational.input.json",
- "policies/vDNS.policy.operational.input.json",
- "policies/vFirewall.policy.operational.input.json"
+ "policies/vCPE.policy.operational.legacy.input.json",
+ "policies/vDNS.policy.operational.legacy.input.json",
+ "policies/vFirewall.policy.operational.legacy.input.json"
};
private String[] policyOutputResourceNames = {
- "policies/vCPE.policy.operational.output.json",
- "policies/vDNS.policy.operational.output.json",
- "policies/vFirewall.policy.operational.output.json"
+ "policies/vCPE.policy.operational.legacy.output.json",
+ "policies/vDNS.policy.operational.legacy.output.json",
+ "policies/vFirewall.policy.operational.legacy.output.json"
};
// @formatter:on
private void createPolicyTypes() throws CoderException, PfModelException, URISyntaxException {
Set<String> policyTypeResources = ResourceUtils.getDirectoryContents("policytypes");
- for (String policyTyoeResource : policyTypeResources) {
- Object yamlObject = new Yaml().load(ResourceUtils.getResourceAsString(policyTyoeResource));
+ for (String policyTypeResource : policyTypeResources) {
+ Object yamlObject = new Yaml().load(ResourceUtils.getResourceAsString(policyTypeResource));
String yamlAsJsonString = new StandardCoder().encode(yamlObject);
ToscaServiceTemplate toscaServiceTemplatePolicyType =
import org.junit.Test;
import org.onap.policy.common.utils.coder.CoderException;
import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.YamlJsonTranslator;
import org.onap.policy.common.utils.resources.ResourceUtils;
import org.onap.policy.models.base.PfModelException;
import org.onap.policy.models.provider.PolicyModelsProvider;
import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyFilter;
import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
-import org.yaml.snakeyaml.Yaml;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Test persistence of monitoring policies to and from the database.
* @author Liam Fallon (liam.fallon@est.tech)
*/
public class PolicyToscaPersistenceTest {
- private StandardCoder standardCoder;
+ private static final Logger LOGGER = LoggerFactory.getLogger(PolicyToscaPersistenceTest.class);
+
+ private YamlJsonTranslator yamlJsonTranslator = new YamlJsonTranslator();
+ private StandardCoder standardCoder = new StandardCoder();
private PolicyModelsProvider databaseProvider;
createPolicyTypes();
}
- /**
- * Set up standard coder.
- */
- @Before
- public void setupStandardCoder() {
- standardCoder = new StandardCoder();
- }
-
@After
public void teardown() throws Exception {
databaseProvider.close();
String policyString = ResourceUtils.getResourceAsString(policyResource);
if (policyResource.endsWith("yaml")) {
- testYamlStringPolicyPersistence(policyString);
+ testPolicyPersistence(yamlJsonTranslator.fromYaml(policyString, ToscaServiceTemplate.class));
} else {
- testJsonStringPolicyPersistence(policyString);
+ testPolicyPersistence(standardCoder.decode(policyString, ToscaServiceTemplate.class));
}
}
}
- private void testYamlStringPolicyPersistence(final String policyString) throws Exception {
- Object yamlObject = new Yaml().load(policyString);
- String yamlAsJsonString = new StandardCoder().encode(yamlObject);
+ @Test
+ public void testNamingPolicyGet() throws PfModelException {
+ String policyYamlString = ResourceUtils.getResourceAsString("policies/sdnc.policy.naming.input.tosca.yaml");
+ ToscaServiceTemplate serviceTemplate =
+ yamlJsonTranslator.fromYaml(policyYamlString, ToscaServiceTemplate.class);
- testJsonStringPolicyPersistence(yamlAsJsonString);
+ long createStartTime = System.currentTimeMillis();
+ databaseProvider.createPolicies(serviceTemplate);
+ LOGGER.trace("Naming policy create time (ms): {}", System.currentTimeMillis() - createStartTime);
+
+ long getStartTime = System.currentTimeMillis();
+ ToscaServiceTemplate namingServiceTemplate =
+ databaseProvider.getPolicies("SDNC_Policy.ONAP_VNF_NAMING_TIMESTAMP", "1.0.0");
+ LOGGER.trace("Naming policy get time (ms): {}", System.currentTimeMillis() - getStartTime);
+
+ assertEquals(1, namingServiceTemplate.getToscaTopologyTemplate().getPoliciesAsMap().size());
+ assertEquals(1, namingServiceTemplate.getPolicyTypesAsMap().size());
+ assertEquals(3, namingServiceTemplate.getDataTypesAsMap().size());
+
+ long deleteStartTime = System.currentTimeMillis();
+ databaseProvider.deletePolicy("SDNC_Policy.ONAP_VNF_NAMING_TIMESTAMP", "1.0.0");
+ LOGGER.trace("Naming policy delete time (ms): {}", System.currentTimeMillis() - deleteStartTime);
}
/**
* Check persistence of a policy.
*
- * @param policyString the policy as a string
+ * @param serviceTemplate the service template containing the policy
* @throws Exception any exception thrown
*/
- public void testJsonStringPolicyPersistence(@NonNull final String policyString) throws Exception {
- ToscaServiceTemplate serviceTemplate = standardCoder.decode(policyString, ToscaServiceTemplate.class);
-
+ public void testPolicyPersistence(@NonNull final ToscaServiceTemplate serviceTemplate) throws Exception {
assertNotNull(serviceTemplate);
databaseProvider.createPolicies(serviceTemplate);
Set<String> policyTypeResources = ResourceUtils.getDirectoryContents("policytypes");
for (String policyTypeResource : policyTypeResources) {
- Object yamlObject = new Yaml().load(ResourceUtils.getResourceAsString(policyTypeResource));
- String yamlAsJsonString = new StandardCoder().encode(yamlObject);
-
+ String policyTypeYamlString = ResourceUtils.getResourceAsString(policyTypeResource);
ToscaServiceTemplate toscaServiceTemplatePolicyType =
- standardCoder.decode(yamlAsJsonString, ToscaServiceTemplate.class);
+ yamlJsonTranslator.fromYaml(policyTypeYamlString, ToscaServiceTemplate.class);
assertNotNull(toscaServiceTemplatePolicyType);
databaseProvider.createPolicyTypes(toscaServiceTemplatePolicyType);
<class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyTypes</class>
<class>org.onap.policy.models.tosca.simple.concepts.JpaToscaTopologyTemplate</class>
<class>org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate</class>
+ <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaTrigger</class>
+ <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaProperty</class>
<class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpGroup</class>
<class>org.onap.policy.models.pdp.persistence.concepts.JpaPdpSubGroup</class>
<class>org.onap.policy.models.pdp.persistence.concepts.JpaPdp</class>
*/
private <T extends ToscaEntity> List<T> handlePfModelRuntimeException(final PfModelRuntimeException pfme) {
if (Status.NOT_FOUND.equals(pfme.getErrorResponse().getResponseCode())) {
+ LOGGER.trace("request did not find any results", pfme);
return Collections.emptyList();
} else {
throw pfme;
LOGGER.debug("->createOperationalPolicy: legacyOperationalPolicy={}", legacyOperationalPolicy);
- JpaToscaServiceTemplate incomingServiceTemplate =
+ JpaToscaServiceTemplate legacyOperationalServiceTemplate =
new LegacyOperationalPolicyMapper().toToscaServiceTemplate(legacyOperationalPolicy);
- JpaToscaServiceTemplate outgoingingServiceTemplate =
- new SimpleToscaProvider().createPolicies(dao, incomingServiceTemplate);
+
+ new SimpleToscaProvider().createPolicies(dao, legacyOperationalServiceTemplate);
LegacyOperationalPolicy createdLegacyOperationalPolicy =
- new LegacyOperationalPolicyMapper().fromToscaServiceTemplate(outgoingingServiceTemplate);
+ new LegacyOperationalPolicyMapper().fromToscaServiceTemplate(legacyOperationalServiceTemplate);
LOGGER.debug("<-createOperationalPolicy: createdLegacyOperationalPolicy={}", createdLegacyOperationalPolicy);
return createdLegacyOperationalPolicy;
return updatedLegacyGuardPolicyMap;
}
-
/**
* Delete legacy guard policy.
*
public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
PfValidationResult result = super.validate(resultIn);
+ if (PfKey.NULL_KEY_VERSION.equals(getKey().getVersion())) {
+ result.addValidationMessage(new PfValidationMessage(getKey(), this.getClass(), ValidationResult.INVALID,
+ "key version is a null version"));
+ }
+
if (type == null || type.isNullKey()) {
result.addValidationMessage(new PfValidationMessage(getKey(), this.getClass(), ValidationResult.INVALID,
"type is null or a null key"));
public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
PfValidationResult result = super.validate(resultIn);
+ if (PfKey.NULL_KEY_VERSION.equals(getKey().getVersion())) {
+ result.addValidationMessage(new PfValidationMessage(getKey(), this.getClass(), ValidationResult.INVALID,
+ "key version is a null version"));
+ }
+
if (properties != null) {
result = validateProperties(result);
}
PfValidationResult result = serviceTemplateToWrite.validate(new PfValidationResult());
if (!result.isValid()) {
- throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, result.toString());
+ throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, result.toString());
}
new SimpleToscaServiceTemplateProvider().write(dao, serviceTemplateToWrite);
"policy types for " + name + ":" + version + DO_NOT_EXIST);
}
+ JpaToscaServiceTemplate dataTypeServiceTemplate = new JpaToscaServiceTemplate(serviceTemplate);
+ dataTypeServiceTemplate.setPolicyTypes(null);
+
for (JpaToscaPolicyType policyType : serviceTemplate.getPolicyTypes().getConceptMap().values()) {
Collection<PfConceptKey> referencedDataTypeKeys = policyType.getReferencedDataTypes();
JpaToscaServiceTemplate dataTypeEntityTreeServiceTemplate =
getDataTypes(dao, referencedDataTypeKey.getName(), referencedDataTypeKey.getVersion());
- serviceTemplate =
- ToscaServiceTemplateUtils.addFragment(serviceTemplate, dataTypeEntityTreeServiceTemplate);
+ dataTypeServiceTemplate = ToscaServiceTemplateUtils.addFragment(dataTypeServiceTemplate,
+ dataTypeEntityTreeServiceTemplate);
}
}
+ serviceTemplate = ToscaServiceTemplateUtils.addFragment(serviceTemplate, dataTypeServiceTemplate);
+
LOGGER.debug("<-getPolicyTypes: name={}, version={}, serviceTemplate={}", name, version, serviceTemplate);
return serviceTemplate;
}
throws PfModelException {
LOGGER.debug("->getPolicies: name={}, version={}", name, version);
- JpaToscaServiceTemplate serviceTemplate = getServiceTemplate(dao);
+ JpaToscaServiceTemplate dbServiceTemplate = getServiceTemplate(dao);
+
+ JpaToscaServiceTemplate serviceTemplate = new JpaToscaServiceTemplate(dbServiceTemplate);
+ serviceTemplate.setDataTypes(new JpaToscaDataTypes());
+ serviceTemplate.setPolicyTypes(new JpaToscaPolicyTypes());
if (!ToscaUtils.doPoliciesExist(serviceTemplate)) {
throw new PfModelRuntimeException(Response.Status.NOT_FOUND,
"policies for " + name + ":" + version + DO_NOT_EXIST);
}
+ JpaToscaServiceTemplate returnServiceTemplate = new JpaToscaServiceTemplate(serviceTemplate);
+ returnServiceTemplate.getTopologyTemplate().setPolicies(new JpaToscaPolicies());
+
for (JpaToscaPolicy policy : serviceTemplate.getTopologyTemplate().getPolicies().getConceptMap().values()) {
- if (policy.getDerivedFrom() != null) {
- JpaToscaServiceTemplate referencedEntitiesServiceTemplate =
- getPolicyTypes(dao, policy.getDerivedFrom().getName(), policy.getDerivedFrom().getVersion());
+ JpaToscaServiceTemplate referencedEntitiesServiceTemplate =
+ getPolicyTypes(dao, policy.getType().getName(), policy.getType().getVersion());
- serviceTemplate =
- ToscaServiceTemplateUtils.addFragment(serviceTemplate, referencedEntitiesServiceTemplate);
- }
+ returnServiceTemplate.getTopologyTemplate().getPolicies().getConceptMap().put(policy.getKey(), policy);
+ returnServiceTemplate =
+ ToscaServiceTemplateUtils.addFragment(returnServiceTemplate, referencedEntitiesServiceTemplate);
}
- LOGGER.debug("<-getPolicies: name={}, version={}, serviceTemplate={}", name, version, serviceTemplate);
- return serviceTemplate;
+ LOGGER.debug("<-getPolicies: name={}, version={}, serviceTemplate={}", name, version, returnServiceTemplate);
+ return returnServiceTemplate;
}
/**
if (policyType == null) {
String errorMessage =
"policy type " + policyTypeKey.getId() + " for policy " + policy.getId() + " does not exist";
- throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, errorMessage);
+ throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, errorMessage);
}
}
// We should have one and only one returned entry
if (filterdPolicyTypeList.size() != 1) {
String errorMessage = "search for latest policy type " + policyTypeName + " returned more than one entry";
- throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, errorMessage);
+ throw new PfModelRuntimeException(Response.Status.CONFLICT, errorMessage);
}
return (JpaToscaPolicyType) filterdPolicyTypeList.get(0);
compositeTemplate.setPolicyTypes(
addFragmentEntitites(compositeTemplate.getPolicyTypes(), fragmentTemplate.getPolicyTypes(), result));
- if (originalTemplate.getTopologyTemplate() != null) {
+ if (originalTemplate.getTopologyTemplate() != null && fragmentTemplate.getTopologyTemplate() != null) {
if (originalTemplate.getTopologyTemplate()
.compareToWithoutEntities(fragmentTemplate.getTopologyTemplate()) == 0) {
compositeTemplate.getTopologyTemplate()
if (!result.isValid()) {
String message = result.toString();
- throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, message);
+ throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, message);
}
return compositeTemplate;
final Function<JpaToscaServiceTemplate, String> checkerFunction) {
String message = checkerFunction.apply(serviceTemplate);
if (message != null) {
- throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, message);
+ throw new PfModelRuntimeException(Response.Status.NOT_FOUND, message);
}
}
}
if (!result.isValid()) {
- throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, result.toString());
+ throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, result.toString());
}
entityTypes.getConceptMap().entrySet()
for (String policyResourceName : policyResourceNames) {
String policyString = ResourceUtils.getResourceAsString(policyResourceName);
if (policyResourceName.endsWith("yaml")) {
+ LOGGER.info("loading {}", policyResourceName);
Object yamlObject = new Yaml().load(policyString);
policyString = new GsonBuilder().setPrettyPrinting().create().toJson(yamlObject);
}
assertEquals(VERSION_100, filteredList.get(7).getVersion());
assertEquals(VERSION_100, filteredList.get(12).getVersion());
- assertEquals(24, policyList.size());
+ assertEquals(23, policyList.size());
assertEquals(22, filteredList.size());
policyList.get(10).setVersion("2.0.0");
public void testFilterNameVersion() {
ToscaPolicyFilter filter = ToscaPolicyFilter.builder().name("operational.modifyconfig").build();
List<ToscaPolicy> filteredList = filter.filter(policyList);
- assertEquals(2, filteredList.size());
+ assertEquals(1, filteredList.size());
filter = ToscaPolicyFilter.builder().name("guard.frequency.scaleout").build();
filteredList = filter.filter(policyList);
filter = ToscaPolicyFilter.builder().version(VERSION_100).build();
filteredList = filter.filter(policyList);
- assertEquals(22, filteredList.size());
+ assertEquals(21, filteredList.size());
filter = ToscaPolicyFilter.builder().name("OSDF_CASABLANCA.SubscriberPolicy_v1").version(VERSION_100).build();
filteredList = filter.filter(policyList);
filter = ToscaPolicyFilter.builder().name("operational.modifyconfig").version(VERSION_100).build();
filteredList = filter.filter(policyList);
- assertEquals(1, filteredList.size());
+ assertEquals(0, filteredList.size());
}
@Test
// null pattern
ToscaPolicyFilter filter = ToscaPolicyFilter.builder().versionPrefix(null).build();
List<ToscaPolicy> filteredList = filter.filter(policyList);
- assertEquals(24, filteredList.size());
+ assertEquals(23, filteredList.size());
filter = ToscaPolicyFilter.builder().versionPrefix("1.").build();
filteredList = filter.filter(policyList);
- assertEquals(22, filteredList.size());
+ assertEquals(21, filteredList.size());
filter = ToscaPolicyFilter.builder().versionPrefix("100.").build();
filteredList = filter.filter(policyList);
public void testFilterTypeVersion() {
ToscaPolicyFilter filter = ToscaPolicyFilter.builder().type("onap.policies.controlloop.Operational").build();
List<ToscaPolicy> filteredList = filter.filter(policyList);
- assertEquals(1, filteredList.size());
+ assertEquals(0, filteredList.size());
+
+ filter = ToscaPolicyFilter.builder().type("onap.policies.controlloop.operational.common.Apex").build();
+ filteredList = filter.filter(policyList);
+ assertEquals(0, filteredList.size());
filter = ToscaPolicyFilter.builder().type("onap.policies.controlloop.operational.common.Drools").build();
filteredList = filter.filter(policyList);
filter = ToscaPolicyFilter.builder().typeVersion(VERSION_000).build();
filteredList = filter.filter(policyList);
- assertEquals(4, filteredList.size());
+ assertEquals(3, filteredList.size());
filter = ToscaPolicyFilter.builder().type("onap.policies.optimization.resource.HpaPolicy")
.typeVersion(VERSION_100).build();
filter = ToscaPolicyFilter.builder().type("onap.policies.controlloop.Operational").typeVersion(VERSION_000)
.build();
filteredList = filter.filter(policyList);
- assertEquals(1, filteredList.size());
+ assertEquals(0, filteredList.size());
}
}
public void testFilterNameVersion() {
ToscaPolicyTypeFilter filter = ToscaPolicyTypeFilter.builder().name("onap.policies.Monitoring").build();
List<ToscaPolicyType> filteredList = filter.filter(typeList);
- assertEquals(2, filteredList.size());
+ assertEquals(1, filteredList.size());
filter = ToscaPolicyTypeFilter.builder().name("onap.policies.monitoring.cdap.tca.hi.lo.app").build();
filteredList = filter.filter(typeList);
filteredList = filter.filter(typeList);
assertEquals(0, filteredList.size());
- filter = ToscaPolicyTypeFilter.builder().version(VERSION_000).build();
+ filter = ToscaPolicyTypeFilter.builder().version(VERSION_100).build();
filteredList = filter.filter(typeList);
- assertEquals(1, filteredList.size());
+ assertEquals(20, filteredList.size());
filter = ToscaPolicyTypeFilter.builder().name("onap.policies.optimization.Vim_fit").version(VERSION_000)
.build();
*/
public class AuthorativeToscaProviderPolicyTypeTest {
private static final String VERSION = "version";
- private static final String POLICY_NO_VERSION_VERSION0 = "onap.policies.NoVersion:0.0.0";
+ private static final String POLICY_NO_VERSION_VERSION1 = "onap.policies.NoVersion:0.0.1";
private static final String POLICY_NO_VERSION = "onap.policies.NoVersion";
private static final String MISSING_POLICY_TYPES = "no policy types specified on service template";
private static final String DAO_IS_NULL = "^dao is marked .*on.*ull but is null$";
- private static final String VERSION_000 = "0.0.0";
+ private static final String VERSION_001 = "0.0.1";
private static String yamlAsJsonString;
private PfDao pfDao;
private StandardCoder standardCoder;
ToscaServiceTemplate createdServiceTemplate =
new AuthorativeToscaProvider().createPolicyTypes(pfDao, toscaServiceTemplate);
- PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION0);
+ PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION1);
ToscaPolicyType beforePolicyType = toscaServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaPolicyType createdPolicyType = createdServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
assertEquals(0, ObjectUtils.compare(beforePolicyType.getDescription(), createdPolicyType.getDescription()));
List<ToscaPolicyType> gotPolicyTypeList =
- new AuthorativeToscaProvider().getPolicyTypeList(pfDao, POLICY_NO_VERSION, VERSION_000);
+ new AuthorativeToscaProvider().getPolicyTypeList(pfDao, POLICY_NO_VERSION, VERSION_001);
assertEquals(2, gotPolicyTypeList.size());
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
assertEquals(2, gotPolicyTypeList.size());
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
- gotPolicyTypeList = new AuthorativeToscaProvider().getPolicyTypeList(pfDao, null, VERSION_000);
+ gotPolicyTypeList = new AuthorativeToscaProvider().getPolicyTypeList(pfDao, null, VERSION_001);
assertEquals(2, gotPolicyTypeList.size());
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
assertThatThrownBy(() -> new AuthorativeToscaProvider().getPolicyTypeList(new DefaultPfDao(), POLICY_NO_VERSION,
- VERSION_000)).hasMessageContaining("Policy Framework DAO has not been initialized");
+ VERSION_001)).hasMessageContaining("Policy Framework DAO has not been initialized");
- assertTrue(new AuthorativeToscaProvider().getPolicyTypeList(pfDao, "i.dont.Exist", VERSION_000).isEmpty());
+ assertTrue(new AuthorativeToscaProvider().getPolicyTypeList(pfDao, "i.dont.Exist", VERSION_001).isEmpty());
}
@Test
ToscaServiceTemplate createdServiceTemplate =
new AuthorativeToscaProvider().createPolicyTypes(pfDao, toscaServiceTemplate);
- PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION0);
+ PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION1);
ToscaPolicyType beforePolicyType = toscaServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaPolicyType createdPolicyType = createdServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
assertEquals(0, ObjectUtils.compare(beforePolicyType.getDescription(), gotPolicyType.getDescription()));
gotServiceTemplate = new AuthorativeToscaProvider().getFilteredPolicyTypes(pfDao,
- ToscaPolicyTypeFilter.builder().name(policyTypeKey.getName()).version(VERSION_000).build());
+ ToscaPolicyTypeFilter.builder().name(policyTypeKey.getName()).version(VERSION_001).build());
gotPolicyType = gotServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
assertEquals(0, ObjectUtils.compare(beforePolicyType.getDescription(), gotPolicyType.getDescription()));
List<ToscaPolicyType> gotPolicyTypeList =
- new AuthorativeToscaProvider().getPolicyTypeList(pfDao, POLICY_NO_VERSION, VERSION_000);
+ new AuthorativeToscaProvider().getPolicyTypeList(pfDao, POLICY_NO_VERSION, VERSION_001);
assertEquals(2, gotPolicyTypeList.size());
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
gotPolicyTypeList = new AuthorativeToscaProvider().getFilteredPolicyTypeList(pfDao,
- ToscaPolicyTypeFilter.builder().name(policyTypeKey.getName()).version(VERSION_000).build());
+ ToscaPolicyTypeFilter.builder().name(policyTypeKey.getName()).version(VERSION_001).build());
assertEquals(1, gotPolicyTypeList.size());
assertEquals(true, beforePolicyType.getName().equals(gotPolicyType.getName()));
ToscaServiceTemplate createdServiceTemplate =
new AuthorativeToscaProvider().createPolicyTypes(pfDao, toscaServiceTemplate);
- PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION0);
+ PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION1);
ToscaPolicyType beforePolicyType = toscaServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaPolicyType createdPolicyType = createdServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaServiceTemplate createdServiceTemplate =
new AuthorativeToscaProvider().createPolicyTypes(pfDao, toscaServiceTemplate);
- PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION0);
+ PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION1);
ToscaPolicyType beforePolicyType = toscaServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaPolicyType createdPolicyType = createdServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaServiceTemplate createdServiceTemplate =
new AuthorativeToscaProvider().createPolicyTypes(pfDao, toscaServiceTemplate);
- PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION0);
+ PfConceptKey policyTypeKey = new PfConceptKey(POLICY_NO_VERSION_VERSION1);
ToscaPolicyType beforePolicyType = toscaServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
ToscaPolicyType createdPolicyType = createdServiceTemplate.getPolicyTypes().get(policyTypeKey.getName());
assertThatThrownBy(() -> {
new AuthorativeToscaProvider().getPolicyTypes(pfDao, policyTypeKey.getName(), policyTypeKey.getVersion());
- }).hasMessage("policy types for onap.policies.NoVersion:0.0.0 do not exist");
+ }).hasMessage("policy types for onap.policies.NoVersion:0.0.1 do not exist");
}
@Test
/*-
* ============LICENSE_START=======================================================
* Copyright (C) 2019-2020 Nordix Foundation.
- * Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
JpaToscaServiceTemplate policyTypeServiceTemplate = new JpaToscaServiceTemplate();
policyTypeServiceTemplate.fromAuthorative(policyTypes);
- String vcpePolicyJson = ResourceUtils.getResourceAsString("policies/vCPE.policy.operational.input.json");
+ String vcpePolicyJson = ResourceUtils.getResourceAsString("policies/vCPE.policy.operational.legacy.input.json");
LegacyOperationalPolicy legacyOperationalPolicy =
standardCoder.decode(vcpePolicyJson, LegacyOperationalPolicy.class);
/*-
* ============LICENSE_START=======================================================
* Copyright (C) 2019-2020 Nordix Foundation.
- * Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
public class LegacyProvider4LegacyOperationalTest {
private static final String POLICY_ID_IS_NULL = "^policyId is marked .*on.*ull but is null$";
- private static final String VCPE_OUTPUT_JSON = "policies/vCPE.policy.operational.output.json";
- private static final String VCPE_INPUT_JSON = "policies/vCPE.policy.operational.input.json";
+ private static final String VCPE_OUTPUT_JSON = "policies/vCPE.policy.operational.legacy.output.json";
+ private static final String VCPE_INPUT_JSON = "policies/vCPE.policy.operational.legacy.input.json";
private static final String DAO_IS_NULL = "^dao is marked .*on.*ull but is null$";
private PfDao pfDao;
private StandardCoder standardCoder;
/*-
* ============LICENSE_START=======================================================
* Copyright (C) 2019-2020 Nordix Foundation.
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Entry<PfConceptKey, JpaToscaPolicyType> firstPolicyType = policyTypesIter.next();
assertEquals(MONITORING, firstPolicyType.getKey().getName());
- assertEquals(VERSION_000, firstPolicyType.getKey().getVersion());
+ assertEquals(VERSION_100, firstPolicyType.getKey().getVersion());
assertEquals("tosca.policies.Root", firstPolicyType.getValue().getDerivedFrom().getName());
- assertEquals("a base policy type for all policies that governs monitoring provisioning",
+ assertEquals("a base policy type for all policies that govern monitoring provisioning",
firstPolicyType.getValue().getDescription());
Entry<PfConceptKey, JpaToscaPolicyType> secondPolicyType = policyTypesIter.next();
assertEquals(MONITORING, firstPolicyType.getKey().getName());
assertEquals(VERSION_100, firstPolicyType.getKey().getVersion());
assertEquals("tosca.policies.Root", firstPolicyType.getValue().getDerivedFrom().getName());
- assertEquals("a base policy type for all policies that govern monitoring provision",
+ assertEquals("a base policy type for all policies that govern monitoring provisioning",
firstPolicyType.getValue().getDescription());
Entry<PfConceptKey, JpaToscaPolicyType> secondPolicyType = policyTypesIter.next();
assertEquals(DCAE, secondPolicyType.getKey().getName());
assertEquals(VERSION_100, secondPolicyType.getKey().getVersion());
- assertEquals("policy.nodes.Root", secondPolicyType.getValue().getDerivedFrom().getName());
+ assertEquals("onap.policies.Monitoring", secondPolicyType.getValue().getDerivedFrom().getName());
assertTrue(secondPolicyType.getValue().getProperties().size() == 2);
Iterator<JpaToscaProperty> propertiesIter = secondPolicyType.getValue().getProperties().values().iterator();
assertEquals(dt1, dtIterator.next());
assertEquals(pt0, compositeTemplate05.getPolicyTypes().getAll(null).iterator().next());
assertEquals(p0, compositeTemplate05.getTopologyTemplate().getPolicies().getAll(null).iterator().next());
+
+ JpaToscaServiceTemplate fragmentTemplate09 = new JpaToscaServiceTemplate();
+
+ fragmentTemplate09.setDataTypes(new JpaToscaDataTypes());
+ fragmentTemplate09.getDataTypes().getConceptMap().put(dt1.getKey(), dt1);
+
+ fragmentTemplate09.setPolicyTypes(new JpaToscaPolicyTypes());
+ fragmentTemplate09.getPolicyTypes().getConceptMap().put(pt0.getKey(), pt0);
+
+ fragmentTemplate09.setTopologyTemplate(null);
+
+ JpaToscaServiceTemplate compositeTemplate06 =
+ ToscaServiceTemplateUtils.addFragment(compositeTemplate05, fragmentTemplate09);
+ assertEquals(compositeTemplate05.getTopologyTemplate(), compositeTemplate06.getTopologyTemplate());
}
}
<class>org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyTypes</class>
<class>org.onap.policy.models.tosca.simple.concepts.JpaToscaTopologyTemplate</class>
<class>org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate</class>
+ <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaTrigger</class>
+ <class>org.onap.policy.models.tosca.simple.concepts.JpaToscaProperty</class>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
description: The base policy type for all policies that govern optimization
onap.policies.NoVersion:
derived_from: onap.policies.Optimization
+ version: 0.0.1
properties:
applicableResources:
type: list