summaryrefslogtreecommitdiffstats
path: root/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java
diff options
context:
space:
mode:
authorhalil.cakal <halil.cakal@est.tech>2024-10-11 17:22:06 +0100
committerhalil.cakal <halil.cakal@est.tech>2024-10-25 11:51:22 +0100
commit62abb22f7c495892afc45d72b8994112ebf1dfb5 (patch)
tree3c525bd27f4c8aefda48efb18877e29efb4a921d /dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java
parent1e1f49f7947271b15fa9b096a022e827d952a043 (diff)
DMI simulator serving yang modules dynamically
- all modules and module references are created once, then are served as requested - 5 module-set-tag supported from tagA to tagE - 200 yang modules per module-set-tag - module sizes are fixed at 32KiB each - the average response size is 200 * 32KiB = 6.4MiB for each Issue-ID: CPS-2410 Change-Id: Ie7ac73b8c0bd464b114a8d76104db4b569ae36f4 Signed-off-by: halil.cakal <halil.cakal@est.tech>
Diffstat (limited to 'dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java')
-rw-r--r--dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java51
-rw-r--r--dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java26
-rw-r--r--dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java27
-rw-r--r--dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java27
-rw-r--r--dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java161
-rw-r--r--dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java27
6 files changed, 298 insertions, 21 deletions
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
index 85d454b2..cf18f239 100644
--- a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
+++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
@@ -20,6 +20,9 @@
package org.onap.cps.ncmp.dmi.rest.stub.controller;
+import static org.onap.cps.ncmp.dmi.rest.stub.utils.ModuleResponseType.MODULE_REFERENCE_RESPONSE;
+import static org.onap.cps.ncmp.dmi.rest.stub.utils.ModuleResponseType.MODULE_RESOURCE_RESPONSE;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -43,14 +46,15 @@ import org.onap.cps.ncmp.dmi.rest.stub.controller.aop.ModuleInitialProcess;
import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest;
import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiOperationCmHandle;
+import org.onap.cps.ncmp.dmi.rest.stub.service.YangModuleFactory;
import org.onap.cps.ncmp.dmi.rest.stub.utils.EventDateTimeFormatter;
+import org.onap.cps.ncmp.dmi.rest.stub.utils.ModuleResponseType;
import org.onap.cps.ncmp.dmi.rest.stub.utils.ResourceFileReaderUtil;
import org.onap.cps.ncmp.events.async1_0_0.Data;
import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
import org.onap.cps.ncmp.events.async1_0_0.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
-import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -73,11 +77,17 @@ import org.springframework.web.bind.annotation.RestController;
public class DmiRestStubController {
private static final String DEFAULT_PASSTHROUGH_OPERATION = "read";
- private static final String dataOperationEventType = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
+ private static final String DATA_OPERATION_EVENT_TYPE = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
private static final Map<String, String> moduleSetTagPerCmHandleId = new HashMap<>();
+ private static final List<String> MODULE_SET_TAGS = YangModuleFactory.generateTags();
+ private static final String DEFAULT_TAG = "tagDefault";
+
private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
private final ObjectMapper objectMapper;
private final ApplicationContext applicationContext;
+ private final AtomicInteger subJobWriteRequestCounter = new AtomicInteger();
+ private final YangModuleFactory yangModuleFactory;
+
@Value("${app.ncmp.async-m2m.topic}")
private String ncmpAsyncM2mTopic;
@Value("${delay.module-references-delay-ms}")
@@ -88,7 +98,6 @@ public class DmiRestStubController {
private long readDataForCmHandleDelayMs;
@Value("${delay.write-data-for-cm-handle-delay-ms}")
private long writeDataForCmHandleDelayMs;
- private final AtomicInteger subJobWriteRequestCounter = new AtomicInteger();
/**
* This code defines a REST API endpoint for adding new the module set tag mapping. The endpoint receives the
@@ -163,7 +172,7 @@ public class DmiRestStubController {
@ModuleInitialProcess
public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
@RequestBody final Object moduleReferencesRequest) {
- return processModuleRequest(moduleReferencesRequest, "ModuleResponse.json", moduleReferencesDelayMs);
+ return processModuleRequest(moduleReferencesRequest, MODULE_REFERENCE_RESPONSE, moduleReferencesDelayMs);
}
/**
@@ -179,7 +188,7 @@ public class DmiRestStubController {
public ResponseEntity<String> getModuleResources(
@PathVariable("cmHandleId") final String cmHandleId,
@RequestBody final Object moduleResourcesReadRequest) {
- return processModuleRequest(moduleResourcesReadRequest, "ModuleResourcesResponse.json", moduleResourcesDelayMs);
+ return processModuleRequest(moduleResourcesReadRequest, MODULE_RESOURCE_RESPONSE, moduleResourcesDelayMs);
}
/**
@@ -308,8 +317,8 @@ public class DmiRestStubController {
cloudEvent = CloudEventBuilder.v1()
.withId(UUID.randomUUID().toString())
.withSource(URI.create("DMI"))
- .withType(dataOperationEventType)
- .withDataSchema(URI.create("urn:cps:" + dataOperationEventType + ":1.0.0"))
+ .withType(DATA_OPERATION_EVENT_TYPE)
+ .withDataSchema(URI.create("urn:cps:" + DATA_OPERATION_EVENT_TYPE + ":1.0.0"))
.withTime(EventDateTimeFormatter.toIsoOffsetDateTime(
EventDateTimeFormatter.getCurrentIsoFormattedDateTime()))
.withData(objectMapper.writeValueAsBytes(dataOperationEvent))
@@ -350,11 +359,22 @@ public class DmiRestStubController {
return dataOperationEvent;
}
- private ResponseEntity<String> processModuleRequest(final Object moduleRequest, final String responseFileName,
+ private ResponseEntity<String> processModuleRequest(final Object moduleRequest,
+ final ModuleResponseType moduleResponseType,
final long simulatedResponseDelay) {
- final String moduleSetTag = extractModuleSetTagFromRequest(moduleRequest);
logRequestBody(moduleRequest);
- final String moduleResponseContent = getModuleResponseContent(moduleSetTag, responseFileName);
+ String moduleResponseContent = "";
+ String moduleSetTag = extractModuleSetTagFromRequest(moduleRequest);
+
+ moduleSetTag = (!isModuleSetTagNullOrEmpty(moduleSetTag)
+ && MODULE_SET_TAGS.contains(moduleSetTag)) ? moduleSetTag : DEFAULT_TAG;
+
+ if (MODULE_RESOURCE_RESPONSE == moduleResponseType) {
+ moduleResponseContent = yangModuleFactory.getModuleResourcesJson(moduleSetTag);
+ } else {
+ moduleResponseContent = yangModuleFactory.getModuleReferencesJson(moduleSetTag);
+ }
+
delay(simulatedResponseDelay);
return ResponseEntity.ok(moduleResponseContent);
}
@@ -376,17 +396,6 @@ public class DmiRestStubController {
}
}
- private String getModuleResponseContent(final String moduleSetTag, final String responseFileName) {
- final String moduleResponseFilePath = isModuleSetTagNullOrEmpty(moduleSetTag)
- ? String.format("module/ietfYang-%s", responseFileName)
- : String.format("module/%s-%s", moduleSetTag, responseFileName);
- log.info("Using module responses from : {}", moduleResponseFilePath);
-
- final Resource moduleResponseResource = applicationContext.getResource(
- ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath);
- return ResourceFileReaderUtil.getResourceFileContent(moduleResponseResource);
- }
-
private String getPassthroughOperationType(final String requestBody) {
try {
final JsonNode rootNode = objectMapper.readTree(requestBody);
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java
new file mode 100644
index 00000000..5b1c30e2
--- /dev/null
+++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 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.cps.ncmp.dmi.rest.stub.model.module;
+
+public record ModuleReference (
+ String moduleName,
+ String revision
+){ }
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java
new file mode 100644
index 00000000..36516452
--- /dev/null
+++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 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.cps.ncmp.dmi.rest.stub.model.module;
+
+import java.util.List;
+
+public record ModuleReferences(
+ List<ModuleReference> schemas
+) { }
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java
new file mode 100644
index 00000000..f4decb8d
--- /dev/null
+++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 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.cps.ncmp.dmi.rest.stub.model.module;
+
+public record ModuleResource (
+ String moduleName,
+ String revision,
+ String yangSource
+) { }
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java
new file mode 100644
index 00000000..42459222
--- /dev/null
+++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java
@@ -0,0 +1,161 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 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.cps.ncmp.dmi.rest.stub.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.annotation.PostConstruct;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.rest.stub.model.module.ModuleReference;
+import org.onap.cps.ncmp.dmi.rest.stub.model.module.ModuleReferences;
+import org.onap.cps.ncmp.dmi.rest.stub.model.module.ModuleResource;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class YangModuleFactory {
+
+ private static final int TARGET_FILE_SIZE_IN_KB = 32 * 1024;
+ private static final List<String> MODULE_SET_TAGS = generateTags();
+ private static final String DEFAULT_TAG = "tagDefault";
+ private static final int NUMBER_OF_MODULES_PER_MODULE_SET = 200;
+ private static final int NUMBER_OF_MODULES_NOT_IN_MODULE_SET = 10;
+ private static final String SERIALIZATION_ERROR = "Error serializing {}: {}";
+ private static final String MODULE_TEMPLATE = """
+ module <MODULE_NAME> {
+ yang-version 1.1;
+ namespace "org:onap:cps:test:<MODULE_NAME>";
+ prefix tree;
+ revision "<MODULE_REVISION>" {
+ description "<DESCRIPTION>";
+ }
+ container tree {
+ list branch {
+ key "name";
+ leaf name {
+ type string;
+ }
+ }
+ }
+ }
+ """;
+
+ private final ObjectMapper objectMapper;
+ private final Map<String, String> moduleReferencesJsonMap = new HashMap<>();
+ private final Map<String, String> moduleResourcesJsonMap = new HashMap<>();
+
+ @PostConstruct
+ private void initializeModuleJsonStrings() {
+ MODULE_SET_TAGS.forEach(tag -> {
+ moduleReferencesJsonMap.put(tag, createModuleReferencesJson(tag, NUMBER_OF_MODULES_PER_MODULE_SET));
+ moduleResourcesJsonMap.put(tag, createModuleResourcesJson(tag, NUMBER_OF_MODULES_PER_MODULE_SET));
+ });
+
+ // Initialize default tag
+ moduleReferencesJsonMap.put(DEFAULT_TAG,
+ createModuleReferencesJson(DEFAULT_TAG, NUMBER_OF_MODULES_NOT_IN_MODULE_SET));
+ moduleResourcesJsonMap.put(DEFAULT_TAG,
+ createModuleResourcesJson(DEFAULT_TAG, NUMBER_OF_MODULES_NOT_IN_MODULE_SET));
+ }
+
+ /**
+ * Retrieves the JSON representation of module references for the given tag.
+ *
+ * @param tag the tag identifying the set of module references
+ * @return the JSON string of module references for the specified tag, or the default tag if not found
+ */
+ public String getModuleReferencesJson(final String tag) {
+ return moduleReferencesJsonMap.getOrDefault(tag, moduleReferencesJsonMap.get(DEFAULT_TAG));
+ }
+
+ /**
+ * Retrieves the JSON representation of module resources for the given tag.
+ *
+ * @param tag the tag identifying the set of module resources
+ * @return the JSON string of module resources for the specified tag, or the default tag if not found
+ */
+ public String getModuleResourcesJson(final String tag) {
+ return moduleResourcesJsonMap.getOrDefault(tag, moduleResourcesJsonMap.get(DEFAULT_TAG));
+ }
+
+ /**
+ * Generates a list of tags from 'A' to 'E'.
+ *
+ * @return a list of tags in the format "tagX" where X is each character from 'A' to 'E'
+ */
+ public static List<String> generateTags() {
+ final List<String> tags = new ArrayList<>(5);
+ for (char currentChar = 'A'; currentChar <= 'E'; currentChar++) {
+ tags.add("tag" + currentChar);
+ }
+ return tags;
+ }
+
+ private String createModuleReferencesJson(final String tag, final int numberOfModules) {
+ final List<ModuleReference> moduleReferencesList = new ArrayList<>(numberOfModules);
+ final String moduleRevision = generateModuleRevision(tag);
+ for (int i = 0; i < numberOfModules; i++) {
+ moduleReferencesList.add(new ModuleReference("module" + i, moduleRevision));
+ }
+ return serializeToJson(new ModuleReferences(moduleReferencesList), "ModuleReferences");
+ }
+
+ private String createModuleResourcesJson(final String tag, final int numberOfModules) {
+ final List<ModuleResource> moduleResourceList = new ArrayList<>(numberOfModules);
+ final String moduleRevision = generateModuleRevision(tag);
+ for (int i = 0; i < numberOfModules; i++) {
+ final String moduleName = "module" + i;
+ final String yangSource = generateYangSource(moduleName, moduleRevision);
+ moduleResourceList.add(new ModuleResource(moduleName, moduleRevision, yangSource));
+ }
+ return serializeToJson(moduleResourceList, "ModuleResources");
+ }
+
+ private String serializeToJson(final Object objectToSerialize, final String objectType) {
+ try {
+ return objectMapper.writeValueAsString(objectToSerialize);
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.error(SERIALIZATION_ERROR, objectType, jsonProcessingException.getMessage());
+ return null;
+ }
+ }
+
+ private String generateModuleRevision(final String tag) {
+ // set tagIndex to 0 for the default tag, otherwise set it to the index of the tag in the list
+ final int tagIndex = tag.equals(DEFAULT_TAG) ? 0 : MODULE_SET_TAGS.indexOf(tag);
+ return LocalDate.of(2024, tagIndex + 1, tagIndex + 1).toString();
+ }
+
+ private static String generateYangSource(final String moduleName, final String moduleRevision) {
+ final int paddingSize = TARGET_FILE_SIZE_IN_KB - MODULE_TEMPLATE.length();
+ final String padding = "*".repeat(Math.max(0, paddingSize));
+ return MODULE_TEMPLATE.replaceAll("<MODULE_NAME>", moduleName)
+ .replace("<MODULE_REVISION>", moduleRevision)
+ .replace("<DESCRIPTION>", padding);
+ }
+} \ No newline at end of file
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java
new file mode 100644
index 00000000..9c5482d1
--- /dev/null
+++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 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.cps.ncmp.dmi.rest.stub.utils;
+
+public enum ModuleResponseType {
+ MODULE_REFERENCE_RESPONSE,
+ MODULE_RESOURCE_RESPONSE;
+}
+