diff options
10 files changed, 98 insertions, 93 deletions
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java index 155c0ff..ca00696 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java @@ -22,10 +22,6 @@ package org.onap.pnfsimulator.simulator; import com.google.common.base.Strings; import com.google.gson.JsonObject; -import java.io.IOException; -import java.net.MalformedURLException; -import java.security.GeneralSecurityException; -import java.util.Optional; import org.onap.pnfsimulator.event.EventData; import org.onap.pnfsimulator.event.EventDataService; import org.onap.pnfsimulator.rest.model.FullEvent; @@ -41,6 +37,10 @@ import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Optional; + @Service public class SimulatorService { diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java index 6ea1157..0291878 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java @@ -20,15 +20,6 @@ package org.onap.pnfsimulator.simulator.client; -import static org.onap.pnfsimulator.logging.MDCVariables.REQUEST_ID; -import static org.onap.pnfsimulator.logging.MDCVariables.X_INVOCATION_ID; -import static org.onap.pnfsimulator.logging.MDCVariables.X_ONAP_REQUEST_ID; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.GeneralSecurityException; -import java.util.UUID; - import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; @@ -43,6 +34,15 @@ import org.slf4j.MDC; import org.slf4j.Marker; import org.slf4j.MarkerFactory; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.util.UUID; + +import static org.onap.pnfsimulator.logging.MDCVariables.REQUEST_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.X_INVOCATION_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.X_ONAP_REQUEST_ID; + public class HttpClientAdapterImpl implements HttpClientAdapter { private static final int CONNECTION_TIMEOUT = 1000; @@ -55,7 +55,7 @@ public class HttpClientAdapterImpl implements HttpClientAdapter { .setSocketTimeout(CONNECTION_TIMEOUT) .build(); private static final Marker INVOKE = MarkerFactory.getMarker("INVOKE"); - private SslSupportLevel sslSupportLevel; + private SslSupportLevel sslSupportLevel; private HttpClient client; private final String targetUrl; @@ -82,11 +82,11 @@ public class HttpClientAdapterImpl implements HttpClientAdapter { EntityUtils.consumeQuietly(response.getEntity()); LOGGER.info(INVOKE, "Message sent, ves response code: {}", response.getStatusLine()); } catch (IOException e) { - LOGGER.warn("Error sending message to ves: " + e.getMessage(), e.getCause()); + LOGGER.warn("Error sending message to ves: {}", e.getMessage(), e.getCause()); } } - public SslSupportLevel getSslSupportLevel(){ + public SslSupportLevel getSslSupportLevel() { return sslSupportLevel; } diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java index edafe8f..b7f6fb3 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java @@ -21,10 +21,12 @@ package org.onap.pnfsimulator.simulator.keywords; import io.vavr.Function1; import io.vavr.Function2; + import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.stream.Collectors; + import lombok.Getter; @Getter @@ -34,24 +36,24 @@ public class Keyword { protected static final String NONLETTERS_REGEX = "([^a-zA-Z]+)"; protected static final Function1<String, String> OPTIONAL = - (regex) -> regex + "?"; + regex -> regex + "?"; private final String name; private final List<String> meaningfulParts; public static final Function2<Keyword, String, Boolean> IS_MATCHING_KEYWORD_NAME = (keyword, key) -> - keyword != null && keyword.getName() != null && keyword.getName().equals(key); + keyword != null && keyword.getName() != null && keyword.getName().equals(key); /** * Returns list of independent parts inside the keyword. Current implementation assumes that customer can join keywords with integer values, so * keyword is decomposed to parts then some parts of the keyword is skipped because of replacement process. * - * @param matcher - Matcher to check find independent groups inside the keyword + * @param matcher - Matcher to check find independent groups inside the keyword * @param skipGroups Informs this method about which groups should be consider as part of the replacement process * @return list of independent parts inside the keywords */ - static List<String> extractPartsFrom(Matcher matcher, List skipGroups) { - List<String> parts = new ArrayList<String>(); + static List<String> extractPartsFrom(Matcher matcher, List<Integer> skipGroups) { + List<String> parts = new ArrayList<>(); for (int i = 1; i <= matcher.groupCount(); i++) { if (matcher.group(i) != null && !skipGroups.contains(i)) { parts.add(matcher.group(i)); @@ -67,8 +69,8 @@ public class Keyword { public String substituteKeyword(String substitution) { return meaningfulParts.stream() - .map(part -> part.equals(name) ? substitution : part) - .collect(Collectors.joining()); + .map(part -> part.equals(name) ? substitution : part) + .collect(Collectors.joining()); } } diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java index c4b40fc..6e4411d 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java @@ -69,24 +69,25 @@ public class EventJob implements Job { LOGGER.error("Could not send event as client is not available"); } } + private Optional<HttpClientAdapter> getHttpClientAdapter(JobDataMap jobDataMap, String vesUrl) { HttpClientAdapter adapter = null; try { adapter = (HttpClientAdapter) (jobDataMap.containsKey(CLIENT_ADAPTER) ? jobDataMap.get(CLIENT_ADAPTER) : - new HttpClientAdapterImpl(vesUrl, new SSLAuthenticationHelper())); + new HttpClientAdapterImpl(vesUrl, new SSLAuthenticationHelper())); } catch (MalformedURLException e) { LOGGER.error("Invalid format of vesServerUr: {}", vesUrl); - } catch (IOException | GeneralSecurityException e){ + } catch (IOException | GeneralSecurityException e) { LOGGER.error("Invalid configuration of client certificate"); } return Optional.ofNullable(adapter); } private void logEventDetails(String templateName, String vesUrl, String body, String jobKey) { - LOGGER.info(String.format("Job %s:Sending event to %s from template %s", - jobKey, vesUrl, templateName)); + LOGGER.info("Job {}:Sending event to {} from template {}", + jobKey, vesUrl, templateName); if (LOGGER.isDebugEnabled()) { - LOGGER.debug(String.format("Job %s: Request body %s", jobKey, body)); + LOGGER.debug("Job {}: Request body {}", jobKey, body); } } diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java index 08e24f8..bd2c577 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java @@ -20,22 +20,7 @@ package org.onap.pnfsimulator.simulator.scheduler; -import static org.onap.pnfsimulator.simulator.scheduler.EventJob.BODY; -import static org.onap.pnfsimulator.simulator.scheduler.EventJob.CLIENT_ADAPTER; -import static org.onap.pnfsimulator.simulator.scheduler.EventJob.EVENT_ID; -import static org.onap.pnfsimulator.simulator.scheduler.EventJob.KEYWORDS_HANDLER; -import static org.onap.pnfsimulator.simulator.scheduler.EventJob.TEMPLATE_NAME; -import static org.onap.pnfsimulator.simulator.scheduler.EventJob.VES_URL; -import static org.quartz.SimpleScheduleBuilder.simpleSchedule; - import com.google.gson.JsonObject; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.security.GeneralSecurityException; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; import org.onap.pnfsimulator.simulator.KeywordsHandler; import org.onap.pnfsimulator.simulator.client.HttpClientAdapterImpl; import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper; @@ -51,23 +36,37 @@ import org.quartz.TriggerBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.onap.pnfsimulator.simulator.scheduler.EventJob.BODY; +import static org.onap.pnfsimulator.simulator.scheduler.EventJob.CLIENT_ADAPTER; +import static org.onap.pnfsimulator.simulator.scheduler.EventJob.EVENT_ID; +import static org.onap.pnfsimulator.simulator.scheduler.EventJob.KEYWORDS_HANDLER; +import static org.onap.pnfsimulator.simulator.scheduler.EventJob.TEMPLATE_NAME; +import static org.onap.pnfsimulator.simulator.scheduler.EventJob.VES_URL; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; + @Component public class EventScheduler { private final Scheduler scheduler; private final KeywordsHandler keywordsHandler; - private final SSLAuthenticationHelper SSLAuthenticationHelper; + private final SSLAuthenticationHelper sslAuthenticationHelper; @Autowired - public EventScheduler(Scheduler scheduler, KeywordsHandler keywordsHandler, SSLAuthenticationHelper SSLAuthenticationHelper) { + public EventScheduler(Scheduler scheduler, KeywordsHandler keywordsHandler, SSLAuthenticationHelper sslAuthenticationHelper) { this.scheduler = scheduler; this.keywordsHandler = keywordsHandler; - this.SSLAuthenticationHelper = SSLAuthenticationHelper; + this.sslAuthenticationHelper = sslAuthenticationHelper; } public String scheduleEvent(String vesUrl, Integer repeatInterval, Integer repeatCount, - String templateName, String eventId, JsonObject body) + String templateName, String eventId, JsonObject body) throws SchedulerException, IOException, GeneralSecurityException { JobDetail jobDetail = createJobDetail(vesUrl, templateName, eventId, body); @@ -89,10 +88,10 @@ public class EventScheduler { private SimpleTrigger createTrigger(int interval, int repeatCount) { return TriggerBuilder.newTrigger() - .withSchedule(simpleSchedule() - .withIntervalInSeconds(interval) - .withRepeatCount(repeatCount - 1)) - .build(); + .withSchedule(simpleSchedule() + .withIntervalInSeconds(interval) + .withRepeatCount(repeatCount - 1)) + .build(); } private JobDetail createJobDetail(String vesUrl, String templateName, String eventId, JsonObject body) throws IOException, GeneralSecurityException { @@ -102,20 +101,20 @@ public class EventScheduler { jobDataMap.put(EVENT_ID, eventId); jobDataMap.put(KEYWORDS_HANDLER, keywordsHandler); jobDataMap.put(BODY, body); - jobDataMap.put(CLIENT_ADAPTER, new HttpClientAdapterImpl(vesUrl, SSLAuthenticationHelper)); + jobDataMap.put(CLIENT_ADAPTER, new HttpClientAdapterImpl(vesUrl, sslAuthenticationHelper)); return JobBuilder - .newJob(EventJob.class) - .withDescription(templateName) - .usingJobData(jobDataMap) - .build(); + .newJob(EventJob.class) + .withDescription(templateName) + .usingJobData(jobDataMap) + .build(); } private List<JobKey> getActiveJobsKeys() throws SchedulerException { return scheduler.getCurrentlyExecutingJobs() - .stream() - .map(JobExecutionContext::getJobDetail) - .map(JobDetail::getKey) - .collect(Collectors.toList()); + .stream() + .map(JobExecutionContext::getJobDetail) + .map(JobDetail::getKey) + .collect(Collectors.toList()); } } diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java index 881585b..91c5a67 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java @@ -66,7 +66,7 @@ public class FsToDbTemplateSynchronizer { WatcherEventProcessor.MODIFIED.processEvent(path, storage); } catch (IOException | JsonParseException e) { LOGGER - .error("Cannot synchronize template: " + path.getFileName().toString(), e); + .error("Cannot synchronize template: {}", path.getFileName().toString(), e); } }); } diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java index 42ed4d3..9dfa002 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java @@ -7,9 +7,9 @@ * 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. @@ -31,6 +31,7 @@ import java.time.Instant; import java.util.Collections; import java.util.HashMap; import java.util.Optional; + import org.bson.Document; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -84,18 +85,18 @@ class WatcherEventProcessorTest { } private void verifyPersistedValue() { - Assertions.assertEquals(storage.getAll().size(), 1); + Assertions.assertEquals(1, storage.getAll().size()); Optional<Template> templateFromStorage = storage.get("test1.json"); if (templateFromStorage.isPresent()) { Template retrievedTemplate = templateFromStorage.get(); Document templateContent = retrievedTemplate.getContent(); Document flatContent = retrievedTemplate.getFlatContent(); - Assertions.assertEquals(templateContent.getString("field1"), "value1"); - Assertions.assertEquals(templateContent.getInteger("field2", 0), 2); - Assertions.assertEquals(flatContent.getInteger(":nested:key1[0]", 0), 1); - Assertions.assertEquals(flatContent.getInteger(":nested:key1[1]", 0), 2); - Assertions.assertEquals(flatContent.getInteger(":nested:key1[2]", 0), 3); - Assertions.assertEquals(flatContent.getString(":nested:key2"), "sampleValue2"); + Assertions.assertEquals("value1", templateContent.getString("field1")); + Assertions.assertEquals(2, templateContent.getInteger("field2", 0)); + Assertions.assertEquals(1, flatContent.getInteger(":nested:key1[0]", 0)); + Assertions.assertEquals(2, flatContent.getInteger(":nested:key1[1]", 0)); + Assertions.assertEquals(3, flatContent.getInteger(":nested:key1[2]", 0)); + Assertions.assertEquals("sampleValue2", flatContent.getString(":nested:key2")); } else { fail(); } @@ -113,7 +114,7 @@ class WatcherEventProcessorTest { Mockito.when(watchEvent.kind()).thenReturn(StandardWatchEventKinds.ENTRY_DELETE); WatcherEventProcessor.process(watchEvent, storage, templatesDir); // then - Assertions.assertEquals(storage.getAll().size(), 0); + Assertions.assertEquals(0, storage.getAll().size()); } private void initStubs() { diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java index 0d62ee9..66187bc 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Map; + import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -39,8 +40,8 @@ class ResponseBuilderTest { ResponseEntity responseEntity = ResponseBuilder.status(SAMPLE_STATUS).build(); assertAll( - () -> assertEquals(responseEntity.getStatusCode(), SAMPLE_STATUS), - () -> assertNull(responseEntity.getBody()) + () -> assertEquals(SAMPLE_STATUS, responseEntity.getStatusCode()), + () -> assertNull(responseEntity.getBody()) ); } @@ -49,15 +50,15 @@ class ResponseBuilderTest { String key = "key"; String value = "value"; ResponseEntity response = ResponseBuilder - .status(SAMPLE_STATUS) - .put(key, value) - .build(); + .status(SAMPLE_STATUS) + .put(key, value) + .build(); Map<String, Object> body = (Map<String, Object>) response.getBody(); assertAll( - () -> assertEquals(SAMPLE_STATUS, response.getStatusCode()), - () -> assertEquals(value, body.get(key)) + () -> assertEquals(SAMPLE_STATUS, response.getStatusCode()), + () -> assertEquals(value, body.get(key)) ); } diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java index 73e4c31..ac54237 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.DEFAULT_STRING_LENGTH; import java.util.Random; + import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; @@ -33,49 +34,49 @@ class KeywordsValueProviderTest { @RepeatedTest(10) void randomLimitedStringTest() { String supplierResult = KeywordsValueProvider.getRandomLimitedString().apply(); - assertEquals(supplierResult.length(), DEFAULT_STRING_LENGTH); + assertEquals(DEFAULT_STRING_LENGTH, supplierResult.length()); } @RepeatedTest(10) void randomStringTest() { int length = new Random().nextInt(15) + 1; String supplierResult = KeywordsValueProvider.getRandomString().apply(length); - assertEquals(supplierResult.length(), length); + assertEquals(length, supplierResult.length()); } @RepeatedTest(10) - void randomIntegerTest(){ + void randomIntegerTest() { int min = new Random().nextInt(10) + 1; int max = new Random().nextInt(1000) + 20; String supplierResult = KeywordsValueProvider.getRandomInteger().apply(min, max); - assertTrue(Integer.parseInt(supplierResult)>=min); - assertTrue(Integer.parseInt(supplierResult)<=max); + assertTrue(Integer.parseInt(supplierResult) >= min); + assertTrue(Integer.parseInt(supplierResult) <= max); } @Test - void randomIntegerContainsMaximalAndMinimalValuesTest(){ + void randomIntegerContainsMaximalAndMinimalValuesTest() { int anyNumber = new Random().nextInt(10) + 1; String supplierResult = KeywordsValueProvider.getRandomInteger().apply(anyNumber, anyNumber); assertEquals(Integer.parseInt(supplierResult), anyNumber); } @Test - void randomIntegerFromNegativeRangeTest(){ + void randomIntegerFromNegativeRangeTest() { String supplierResult = KeywordsValueProvider.getRandomInteger().apply(-20, -20); assertEquals(Integer.parseInt(supplierResult), -20); } @RepeatedTest(10) - void randomIntegerFromParametersWithDifferentOrdersTest(){ + void randomIntegerFromParametersWithDifferentOrdersTest() { String supplierResult = KeywordsValueProvider.getRandomInteger().apply(-20, -10); - assertTrue(Integer.parseInt(supplierResult)>=-20); - assertTrue(Integer.parseInt(supplierResult)<=-10); + assertTrue(Integer.parseInt(supplierResult) >= -20); + assertTrue(Integer.parseInt(supplierResult) <= -10); } @RepeatedTest(10) - void epochSecondGeneratedInCorrectFormatTest(){ + void epochSecondGeneratedInCorrectFormatTest() { String supplierResult = KeywordsValueProvider.getEpochSecond().apply(); - assertEquals(supplierResult.length(), 10); + assertEquals(10, supplierResult.length()); } } diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java index ff41c44..3a7dbf2 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java @@ -35,17 +35,17 @@ class SslSupportLevelTest { @Test void testShouldReturnAlwaysTrustSupportLevelForHttpsUrl() throws MalformedURLException { SslSupportLevel actualSupportLevel = SslSupportLevel.getSupportLevelBasedOnProtocol(HTTPS_URL); - assertEquals(actualSupportLevel, SslSupportLevel.ALWAYS_TRUST); + assertEquals(SslSupportLevel.ALWAYS_TRUST, actualSupportLevel); } @Test void testShouldReturnNoneSupportLevelForHttpUrl() throws MalformedURLException { SslSupportLevel actualSupportLevel = SslSupportLevel.getSupportLevelBasedOnProtocol(HTTP_URL); - assertEquals(actualSupportLevel, SslSupportLevel.NONE); + assertEquals(SslSupportLevel.NONE, actualSupportLevel); } @Test - void testShouldRaiseExceptionWhenInvalidUrlPassed(){ + void testShouldRaiseExceptionWhenInvalidUrlPassed() { assertThrows(MalformedURLException.class, () -> SslSupportLevel.getSupportLevelBasedOnProtocol("http://bla:VES-PORT/")); } |