diff options
Diffstat (limited to 'controlloop')
12 files changed, 322 insertions, 16 deletions
diff --git a/controlloop/common/eventmanager/pom.xml b/controlloop/common/eventmanager/pom.xml index deb17b8aa..6897317d5 100644 --- a/controlloop/common/eventmanager/pom.xml +++ b/controlloop/common/eventmanager/pom.xml @@ -188,6 +188,12 @@ <scope>provided</scope> </dependency> <dependency> + <groupId>org.onap.policy.common</groupId> + <artifactId>utils-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.onap.policy.drools-applications.controlloop.common</groupId> <artifactId>simulators</artifactId> <version>${project.version}</version> diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java index 930f9578f..8641ddc27 100644 --- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java +++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java @@ -90,7 +90,7 @@ public class ControlLoopEventManager implements LockCallback, Serializable { public final UUID requestID; private String controlLoopResult; - private transient ControlLoopProcessor processor = null; + private ControlLoopProcessor processor = null; private VirtualControlLoopEvent onset; private Integer numOnsets = 0; private Integer numAbatements = 0; @@ -101,7 +101,7 @@ public class ControlLoopEventManager implements LockCallback, Serializable { private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>(); private ControlLoopOperationManager currentOperation = null; private ControlLoopOperationManager lastOperationManager = null; - private transient TargetLock targetLock = null; + private TargetLock targetLock = null; private AaiGetVnfResponse vnfResponse = null; private AaiGetVserverResponse vserverResponse = null; diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java index 1ad1e5af7..466c826b1 100644 --- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java +++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java @@ -77,13 +77,13 @@ public class ControlLoopOperationManager implements Serializable { // for Drools Rule statements. // public final ControlLoopEvent onset; - public final transient Policy policy; + public final Policy policy; // // Properties used to track the Operation // private int attempts = 0; - private transient Operation currentOperation = null; + private Operation currentOperation = null; private LinkedList<Operation> operationHistory = new LinkedList<>(); private PolicyResult policyResult = null; private ControlLoopEventManager eventManager = null; @@ -104,7 +104,9 @@ public class ControlLoopOperationManager implements Serializable { // // Internal class used for tracking // - private class Operation { + private class Operation implements Serializable { + private static final long serialVersionUID = 1L; + private ControlLoopOperation clOperation = new ControlLoopOperation(); private PolicyResult policyResult = null; private int attempt = 0; diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/processor/ControlLoopProcessor.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/processor/ControlLoopProcessor.java index 3dbc25fcf..ac684fcda 100644 --- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/processor/ControlLoopProcessor.java +++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/processor/ControlLoopProcessor.java @@ -20,6 +20,7 @@ package org.onap.policy.controlloop.processor; +import java.io.Serializable; import org.onap.policy.controlloop.ControlLoopException; import org.onap.policy.controlloop.policy.ControlLoop; import org.onap.policy.controlloop.policy.ControlLoopPolicy; @@ -29,8 +30,9 @@ import org.onap.policy.controlloop.policy.PolicyResult; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor; -public class ControlLoopProcessor { - +public class ControlLoopProcessor implements Serializable { + private static final long serialVersionUID = 1L; + private final String yaml; private final ControlLoopPolicy policy; private String currentNestedPolicyId = null; diff --git a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java index 562a46e4e..1673df3be 100644 --- a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java +++ b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java @@ -54,6 +54,7 @@ import org.onap.policy.aai.RelationshipData; import org.onap.policy.aai.RelationshipList; import org.onap.policy.aai.util.AaiException; import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.utils.io.Serializer; import org.onap.policy.controlloop.ControlLoopEventStatus; import org.onap.policy.controlloop.ControlLoopException; import org.onap.policy.controlloop.ControlLoopNotificationType; @@ -212,7 +213,7 @@ public class ControlLoopEventManagerTest { } @Test - public void subsequentOnsetTest() { + public void subsequentOnsetTest() throws IOException { UUID requestId = UUID.randomUUID(); VirtualControlLoopEvent event = new VirtualControlLoopEvent(); event.setClosedLoopControlName("TwoOnsetTest"); @@ -555,6 +556,9 @@ public class ControlLoopEventManagerTest { VirtualControlLoopNotification clfNotification = manager.isControlLoopFinal(); assertNull(clfNotification); + + // serialize and de-serialize manager + manager = Serializer.roundTrip(manager); manager.getProcessor().nextPolicyForResult(PolicyResult.SUCCESS); clfNotification = manager.isControlLoopFinal(); @@ -626,6 +630,9 @@ public class ControlLoopEventManagerTest { ControlLoopOperationManager clom = manager.processControlLoop(); assertNotNull(clom); assertNull(clom.getOperationResult()); + + // serialize and de-serialize manager + manager = Serializer.roundTrip(manager); // Test operation in progress try { @@ -715,6 +722,9 @@ public class ControlLoopEventManagerTest { } assertNull(manager.unlockCurrentOperation()); + + // serialize and de-serialize manager + manager = Serializer.roundTrip(manager); ControlLoopOperationManager clom = manager.processControlLoop(); assertNotNull(clom); diff --git a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java index d1763f52c..dae03aa6e 100644 --- a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java +++ b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java @@ -35,15 +35,12 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.HashMap; import java.util.UUID; - import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; import javax.persistence.Persistence; import javax.persistence.Query; - - import org.apache.commons.io.IOUtils; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -59,6 +56,7 @@ import org.onap.policy.appclcm.LcmRequestWrapper; import org.onap.policy.appclcm.LcmResponse; import org.onap.policy.appclcm.LcmResponseWrapper; import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.utils.io.Serializer; import org.onap.policy.controlloop.ControlLoopEventStatus; import org.onap.policy.controlloop.ControlLoopException; import org.onap.policy.controlloop.ControlLoopNotificationType; @@ -850,4 +848,63 @@ public class ControlLoopOperationManagerTest { assertEquals(1, numEventsAfter - numEventsBefore); } + + @Test + public void testSerialization() throws Exception { + InputStream is = new FileInputStream(new File("src/test/resources/test.yaml")); + final String yamlString = IOUtils.toString(is, StandardCharsets.UTF_8); + + UUID requestId = UUID.randomUUID(); + VirtualControlLoopEvent onsetEvent = new VirtualControlLoopEvent(); + onsetEvent.setClosedLoopControlName("TwoOnsetTest"); + onsetEvent.setRequestId(requestId); + onsetEvent.setTarget("generic-vnf.vnf-id"); + onsetEvent.setClosedLoopAlarmStart(Instant.now()); + onsetEvent.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET); + onsetEvent.setAai(new HashMap<>()); + onsetEvent.getAai().put("generic-vnf.vnf-name", "onsetOne"); + + ControlLoopEventManager manager = + new ControlLoopEventManager(onsetEvent.getClosedLoopControlName(), onsetEvent.getRequestId()); + VirtualControlLoopNotification notification = manager.activate(yamlString, onsetEvent); + assertNotNull(notification); + assertEquals(ControlLoopNotificationType.ACTIVE, notification.getNotification()); + + Policy policy = manager.getProcessor().getCurrentPolicy(); + ControlLoopOperationManager clom = new ControlLoopOperationManager(onsetEvent, policy, manager); + assertNotNull(clom); + + clom.startOperation(onsetEvent); + assertTrue(clom.isOperationRunning()); + + clom = Serializer.roundTrip(clom); + assertNotNull(clom); + assertTrue(clom.isOperationRunning()); + + SOResponse soResponse = new SOResponse(); + final SOResponseWrapper soRw = new SOResponseWrapper(soResponse, null); + + PolicyEngine.manager.setEnvironmentProperty("guard.disabled", "false"); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.ONAP_KEY_URL, + "http://somewhere.over.the.rainbow"); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.ONAP_KEY_USER, "Dorothy"); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.ONAP_KEY_PASS, "Toto"); + + assertEquals(PolicyResult.FAILURE, clom.onResponse(soRw)); + assertFalse(clom.isOperationRunning()); + assertEquals(1, clom.getHistory().size()); + + clom = Serializer.roundTrip(clom); + assertNotNull(clom); + assertFalse(clom.isOperationRunning()); + assertEquals(1, clom.getHistory().size()); + + System.setProperty("OperationsHistoryPU", "TestOperationsHistoryPU"); + assertEquals(PolicyResult.FAILURE, clom.onResponse(soRw)); + + clom = Serializer.roundTrip(clom); + assertNotNull(clom); + assertFalse(clom.isOperationRunning()); + assertEquals(1, clom.getHistory().size()); + } } diff --git a/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoop.java b/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoop.java index a8edf7a81..19cca8c82 100644 --- a/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoop.java +++ b/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoop.java @@ -20,6 +20,7 @@ package org.onap.policy.controlloop.policy; +import java.io.Serializable; import java.util.LinkedList; import java.util.List; @@ -27,7 +28,8 @@ import org.onap.policy.aai.Pnf; import org.onap.policy.sdc.Resource; import org.onap.policy.sdc.Service; -public class ControlLoop { +public class ControlLoop implements Serializable { + private static final long serialVersionUID = 1L; private static final String COMPILER_VERSION = "2.0.0"; diff --git a/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoopPolicy.java b/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoopPolicy.java index 161fe1def..bbc7747e4 100644 --- a/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoopPolicy.java +++ b/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/ControlLoopPolicy.java @@ -20,9 +20,11 @@ package org.onap.policy.controlloop.policy; +import java.io.Serializable; import java.util.List; -public class ControlLoopPolicy { +public class ControlLoopPolicy implements Serializable { + private static final long serialVersionUID = 1L; private ControlLoop controlLoop; diff --git a/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/Policy.java b/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/Policy.java index 8585b674b..22c58c8ab 100644 --- a/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/Policy.java +++ b/controlloop/common/policy-yaml/src/main/java/org/onap/policy/controlloop/policy/Policy.java @@ -20,11 +20,13 @@ package org.onap.policy.controlloop.policy; +import java.io.Serializable; import java.util.Collections; import java.util.Map; import java.util.UUID; -public class Policy { +public class Policy implements Serializable { + private static final long serialVersionUID = 1L; private String id = UUID.randomUUID().toString(); private String name; @@ -264,7 +266,7 @@ public class Policy { } public boolean isValid() { - return id == null || name == null || actor == null || recipe == null || target == null; + return id != null && name != null && actor != null && recipe != null && target != null; } @Override diff --git a/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopPolicyTest.java b/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopPolicyTest.java index fcfe1dcfe..a4d1baa75 100644 --- a/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopPolicyTest.java +++ b/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopPolicyTest.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import org.junit.Test; +import org.onap.policy.common.utils.io.Serializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.DumperOptions; @@ -114,6 +115,12 @@ public class ControlLoopPolicyTest { // Seems we cannot use assertEquals here. Need advice. // //assertEquals(newObject, obj); + + // test serialization + ControlLoopPolicy policy = (ControlLoopPolicy) obj; + ControlLoopPolicy policy2 = Serializer.roundTrip(policy); + assertTrue(policy.equals(policy2)); + } catch (FileNotFoundException e) { fail(e.getLocalizedMessage()); } catch (IOException e) { diff --git a/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopTest.java b/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopTest.java index daab1a26b..0349552af 100644 --- a/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopTest.java +++ b/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/ControlLoopTest.java @@ -23,12 +23,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.junit.Ignore; import org.junit.Test; import org.onap.policy.aai.Pnf; +import org.onap.policy.common.utils.io.Serializer; import org.onap.policy.sdc.Resource; import org.onap.policy.sdc.ResourceType; import org.onap.policy.sdc.Service; @@ -82,7 +84,7 @@ public class ControlLoopTest { } @Test - public void testEquals() { + public void testEquals() throws IOException { final Pnf pnf = new Pnf(); pnf.setPnfName("pnf 1"); @@ -128,6 +130,10 @@ public class ControlLoopTest { assertTrue(controlLoop1.equals(controlLoop2)); assertEquals(controlLoop1.hashCode(), controlLoop2.hashCode()); + + controlLoop2 = Serializer.roundTrip(controlLoop1); + assertTrue(controlLoop1.equals(controlLoop2)); + assertEquals(controlLoop1.hashCode(), controlLoop2.hashCode()); } @Test diff --git a/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/PolicyTest.java b/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/PolicyTest.java new file mode 100644 index 000000000..6e13b326b --- /dev/null +++ b/controlloop/common/policy-yaml/src/test/java/org/onap/policy/controlloop/policy/PolicyTest.java @@ -0,0 +1,210 @@ +/* + * ============LICENSE_START======================================================= + * policy-yaml unit test + * ================================================================================ + * Copyright (C) 2018 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.policy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.common.utils.io.Serializer; + +public class PolicyTest { + private Policy policy; + + @Before + public void setUp() { + policy = new Policy(); + } + + @Test + public void testHashCode() { + assertTrue(policy.hashCode() != 0); + + policy.setActor("a"); + int hc1 = policy.hashCode(); + + policy.setActor("b"); + assertTrue(hc1 != policy.hashCode()); + } + + @Test + public void test() throws IOException { + OperationsAccumulateParams operationsAccumulateParams = new OperationsAccumulateParams(); + operationsAccumulateParams.setLimit(10); + + Map<String, String> payload = new TreeMap<>(); + payload.put("mykey", "myvalue"); + + Target target = new Target(); + target.setResourceID("myresource"); + + policy.setActor("act"); + policy.setDescription("desc"); + policy.setFailure("fail"); + policy.setFailure_exception("failex"); + policy.setFailure_guard("failguard"); + policy.setFailure_retries("failretry"); + policy.setFailure_timeout("failtimeout"); + policy.setId("myid"); + policy.setName("myname"); + policy.setOperationsAccumulateParams(operationsAccumulateParams); + policy.setPayload(payload); + policy.setRecipe("myrecipe"); + policy.setRetry(20); + policy.setSuccess("succ"); + policy.setTarget(target); + policy.setTimeout(30); + + assertEquals("act", policy.getActor()); + assertEquals("desc", policy.getDescription()); + assertEquals("fail", policy.getFailure()); + assertEquals("failex", policy.getFailure_exception()); + assertEquals("failguard", policy.getFailure_guard()); + assertEquals("failretry", policy.getFailure_retries()); + assertEquals("failtimeout", policy.getFailure_timeout()); + assertEquals("myid", policy.getId()); + assertEquals("myname", policy.getName()); + assertEquals(operationsAccumulateParams, policy.getOperationsAccumulateParams()); + assertEquals(payload, policy.getPayload()); + assertEquals("myrecipe", policy.getRecipe()); + assertEquals(20, policy.getRetry().intValue()); + assertEquals("succ", policy.getSuccess()); + assertEquals(target, policy.getTarget()); + assertEquals(30, policy.getTimeout().intValue()); + + assertTrue(policy.equals(policy)); + assertTrue(policy.hashCode() != new Policy().hashCode()); + assertFalse(policy.equals(new Policy())); + + Policy policy2 = Serializer.roundTrip(policy); + assertTrue(policy.equals(policy2)); + assertEquals(policy.hashCode(), policy2.hashCode()); + + policy2 = new Policy(policy); + assertTrue(policy.equals(policy2)); + assertEquals(policy.hashCode(), policy2.hashCode()); + } + + @Test + public void testPolicyString() { + policy = new Policy("justId"); + assertEquals("justId", policy.getId()); + } + + @Test + public void testPolicyStringStringStringMapOfStringStringTarget() { + Map<String, String> payload = new TreeMap<>(); + payload.put("mykeyB", "myvalueB"); + + Target target = new Target(); + target.setResourceID("myresourceB"); + + policy = new Policy("nameB", "actorB", "recipeB", payload, target); + assertEquals("nameB", policy.getName()); + assertEquals("actorB", policy.getActor()); + assertEquals("recipeB", policy.getRecipe()); + assertEquals(payload, policy.getPayload()); + assertEquals(target, policy.getTarget()); + + assertTrue(policy.hashCode() != new Policy().hashCode()); + } + + @Test + public void testPolicyStringStringStringMapOfStringStringTargetIntegerInteger() { + Map<String, String> payload = new TreeMap<>(); + payload.put("mykeyC", "myvalueC"); + + Target target = new Target(); + target.setResourceID("myresourceC"); + + policy = new Policy("nameC", "actorC", "recipeC", payload, target, 201, 202); + assertEquals("nameC", policy.getName()); + assertEquals("actorC", policy.getActor()); + assertEquals("recipeC", policy.getRecipe()); + assertEquals(payload, policy.getPayload()); + assertEquals(target, policy.getTarget()); + assertEquals(201, policy.getRetry().intValue()); + assertEquals(202, policy.getTimeout().intValue()); + + assertTrue(policy.hashCode() != new Policy().hashCode()); + } + + @Test + public void testPolicyStringStringStringStringMapOfStringStringTargetStringIntegerInteger() { + Map<String, String> payload = new TreeMap<>(); + payload.put("mykeyD", "myvalueD"); + + Target target = new Target(); + target.setResourceID("myresourceD"); + + policy = new Policy("idD", "nameD", "descD", "actorD", payload, target, "recipeD", 301, 302); + assertEquals("idD", policy.getId()); + assertEquals("nameD", policy.getName()); + assertEquals("descD", policy.getDescription()); + assertEquals("actorD", policy.getActor()); + assertEquals(payload, policy.getPayload()); + assertEquals(target, policy.getTarget()); + assertEquals("recipeD", policy.getRecipe()); + assertEquals(301, policy.getRetry().intValue()); + assertEquals(302, policy.getTimeout().intValue()); + + assertTrue(policy.hashCode() != new Policy().hashCode()); + } + + @Test + public void testIsValid() { + assertFalse(policy.isValid()); + + Target target = new Target(); + target.setResourceID("myresourceV"); + + policy = new Policy("nameV", "actorV", "recipeV", null, target); + assertEquals(null, policy.getPayload()); + assertTrue(policy.isValid()); + } + + @Test + public void testToString() { + assertNotNull(policy.toString()); + } + + @Test + public void testEqualsObject() { + assertTrue(policy.equals(policy)); + + policy.setId("idE"); + assertFalse(policy.equals(new Policy())); + + Policy policy2 = new Policy(); + policy2.setId(policy.getId()); + assertTrue(policy.equals(policy2)); + + policy2.setId("idX"); + assertFalse(policy.equals(policy2)); + } + +} |