Create request interceptor class to remove namespace attribute from xml request bodies 50/138850/10
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Tue, 27 Aug 2024 05:57:29 +0000 (07:57 +0200)
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Thu, 29 Aug 2024 05:46:38 +0000 (07:46 +0200)
- add interceptor that removes xmlns attribute from xml bodies
- update eclipse persistence (2.6.2 -> 2.7.7)
- use spring-boot-starter-test instead of spring framework's spring-test
- bump snapshot version to 1.14.7-SNAPSHOT
- remove unused imports

Issue-ID: AAI-3976
Change-Id: Iac7103ae003cb7bb7269ada983af97e003be155c
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
12 files changed:
aai-resources/pom.xml
aai-resources/src/main/java/org/onap/aai/interceptors/pre/NamespaceInterceptor.java [new file with mode: 0644]
aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java
aai-resources/src/test/java/org/onap/aai/rest/BulkProcessorTestAbstraction.java
aai-resources/src/test/java/org/onap/aai/rest/ConfigurationTest.java
aai-resources/src/test/java/org/onap/aai/rest/ExampleConsumerTest.java
aai-resources/src/test/java/org/onap/aai/rest/URLFromVertexIdConsumerTest.java
aai-resources/src/test/java/org/onap/aai/rest/VertexIdConsumerTest.java
aai-resources/src/test/resources/application-test.properties
aai-resources/src/test/resources/logback.xml
pom.xml
version.properties

index 51c9621..e2c66e0 100644 (file)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.aai.resources</groupId>
         <artifactId>resources</artifactId>
-        <version>1.14.6-SNAPSHOT</version>
+        <version>1.14.7-SNAPSHOT</version>
     </parent>
     <properties>
         <java.version>1.8</java.version>
 
         <javax.servlet.version>4.0.1</javax.servlet.version>
         <keycloak.version>11.0.2</keycloak.version>
-        <micrometer.version>1.6.6</micrometer.version>
         <testcontainers.version>1.6.1</testcontainers.version>
         <mockito.core.version>4.4.0</mockito.core.version>
-        <eclipse.persistence.version>2.6.2</eclipse.persistence.version>
+        <eclipse.persistence.version>2.7.7</eclipse.persistence.version>
         <!-- Setting some default value to not complain by editor but it will be overridden by gmaven plugin -->
 
         <!-- Integration tests will be skipped by default. Could be enabled here or by -DskipITs=false-->
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-hateoas</artifactId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-sleuth</artifactId>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jetty</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-test</artifactId>
-            <scope>test</scope>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-sleuth</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>io.micrometer</groupId>
-            <artifactId>micrometer-core</artifactId>
-            <version>${micrometer.version}</version>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
         </dependency>
         <dependency>
             <groupId>io.micrometer</groupId>
             <artifactId>micrometer-registry-prometheus</artifactId>
-            <version>${micrometer.version}</version>
         </dependency>
         <dependency>
             <groupId>io.micrometer</groupId>
             <artifactId>micrometer-jersey2</artifactId>
-            <version>${micrometer.version}</version>
         </dependency>
         <dependency>
             <groupId>javax.jms</groupId>
             <artifactId>netty-handler</artifactId>
             <version>${netty.handler.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-tomcat</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-jetty</artifactId>
-        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
             <artifactId>testcontainers</artifactId>
             <version>1.19.8</version>
             <scope>test</scope>
+            <!-- <exclusions>
+                <exclusion>
+                    <groupId>junit</groupId>
+                    <artifactId>junit</artifactId>
+                </exclusion>
+            </exclusions> -->
         </dependency>
         <dependency>
             <groupId>org.testcontainers</groupId>
             <version>1.19.8</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.xmlunit</groupId>
+            <artifactId>xmlunit-matchers</artifactId>
+            <version>2.7.0</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/NamespaceInterceptor.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/NamespaceInterceptor.java
new file mode 100644 (file)
index 0000000..c97e475
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 Deutsche Telekom. 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.aai.interceptors.pre;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.annotation.Priority;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.onap.aai.IncreaseNodesTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+
+/**
+ * With newer versions of EclipseLink Moxy, the xmlns attribute cannot be included
+ * in the xml structure anymore.
+ * Changes to the model-loader and babel would be required to 'properly' remove
+ * it for the model-distribution.
+ * Since the impact of such a change is hard to judge, this workaround is taken
+ * that is less invasive and with a lower risk of breaking model distribution.
+ *
+ * @deprecated This is only meant as a temporary compatibility layer and will be removed in the future
+ * once all clients have been updated to not include the xmlns attribute.
+ *
+ */
+@Deprecated
+@Component
+@Priority(AAIRequestFilterPriority.REQUEST_MODIFICATION)
+@ConditionalOnProperty(value = "aai.remove-xmlns.enabled", havingValue = "true", matchIfMissing = true)
+public class NamespaceInterceptor implements ReaderInterceptor {
+
+    private static final Logger log = LoggerFactory.getLogger(IncreaseNodesTool.class);
+    private static final String xslStr = String.join("\n",
+        "<xsl:transform xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">",
+        "<xsl:output version=\"1.0\" encoding=\"UTF-8\" indent=\"no\"/>",
+        "<xsl:strip-space elements=\"*\"/>",
+        "  <xsl:template match=\"@*|node()\">",
+        "   <xsl:element name=\"{local-name()}\">",
+        "     <xsl:apply-templates select=\"@*|node()\"/>",
+        "  </xsl:element>",
+        "  </xsl:template>",
+        "  <xsl:template match=\"text()\">",
+        "    <xsl:copy/>",
+        "  </xsl:template>",
+        "</xsl:transform>");
+
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+        if(MediaType.APPLICATION_XML.equalsIgnoreCase(context.getMediaType().toString())) {
+            Reader xmlReader = new InputStreamReader(context.getInputStream());
+            try {
+                ByteArrayInputStream inputStream = removeNameSpace(xmlReader);
+                context.setInputStream(inputStream);
+                return context.proceed();
+            } catch (Exception e) {
+                log.error("Could not remove namespace from model payload: " + e.getMessage());
+                return context.proceed();
+            }
+        }
+        return context.proceed();
+    }
+
+    /**
+     * Temporary solution to removing the xmlns attribute from the model payload.
+     * The payload is coming from babel and removing it there would be some larger effort.
+     * As such, this workaround is applied.
+     * Taken from: https://stackoverflow.com/questions/37354605/how-to-remove-xmlns-attribute-from-the-root-element-in-xml-and-java#answer-37357777
+     * @throws Exception
+     */
+    public ByteArrayInputStream removeNameSpace(Reader xmlReader) throws Exception {
+        // Parse XML and Build Document
+        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        InputSource is = new InputSource();
+        is.setCharacterStream(xmlReader);
+        Document doc = db.parse(is);
+
+        // Parse XSLT and Configure Transformer
+        Source xslt = new StreamSource(new StringReader(xslStr));
+        Transformer tf = TransformerFactory.newInstance().newTransformer(xslt);
+        tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+
+        // Output Result to String
+        DOMSource source = new DOMSource(doc);
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        StreamResult strresult = new StreamResult(outStream);
+        tf.transform(source, strresult);
+
+        return new ByteArrayInputStream(outStream.toByteArray());
+    }
+}
index 41ec8e1..8e68e8e 100644 (file)
@@ -35,6 +35,7 @@ import java.util.logging.Logger;
 import javax.annotation.Priority;
 import javax.ws.rs.container.ContainerRequestFilter;
 import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.ReaderInterceptor;
 
 import org.glassfish.jersey.server.ResourceConfig;
 import org.onap.aai.rest.BulkAddConsumer;
@@ -80,6 +81,7 @@ public class JerseyConfiguration {
                 LegacyMoxyConsumer.class, URLFromVertexIdConsumer.class);
         resourceConfig.registerClasses(classes);
         registerFiltersForClasses(resourceConfig, ContainerRequestFilter.class, ContainerResponseFilter.class,
+                ReaderInterceptor.class,
                 AuditLogContainerFilter.class);
 
         if (isLoggingEnabled()) {
index 706da69..f734360 100644 (file)
@@ -37,7 +37,6 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.mockito.Mockito;
 import org.onap.aai.AAISetup;
index 21f2fb4..b780d90 100644 (file)
@@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
 import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
+import org.springframework.boot.test.autoconfigure.actuate.metrics.AutoConfigureMetrics;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.web.server.LocalServerPort;
 import org.springframework.context.annotation.Import;
@@ -58,6 +59,7 @@ import org.springframework.web.client.RestTemplate;
  * Test REST requests against configuration resource
  */
 
+@AutoConfigureMetrics
 @TestPropertySource(locations = "classpath:application-test.properties")
 @ContextConfiguration(initializers = PropertyPasswordConfiguration.class, classes = {SpringContextAware.class})
 @EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class}) // there is no running cassandra instance for the test
@@ -66,6 +68,7 @@ import org.springframework.web.client.RestTemplate;
         webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
         classes = {SpringContextAware.class, ResourcesApp.class})
 public class ConfigurationTest extends AbstractSpringRestTest {
+
     @Autowired
     RestTemplate restTemplate;
 
@@ -92,7 +95,7 @@ public class ConfigurationTest extends AbstractSpringRestTest {
         headersGet.add("X-FromAppId", "JUNIT");
         headersGet.add("X-TransactionId", "JUNIT");
 
-        headersGet.setBasicAuth("AAI","AAI");
+        headersGet.setBasicAuth("AAI", "AAI");
 
         headersPutPatch = new HttpHeaders();
         headersPutPatch.putAll(headersGet);
@@ -112,7 +115,8 @@ public class ConfigurationTest extends AbstractSpringRestTest {
         responseEntity = restTemplate.exchange(baseUrl + endpoint, HttpMethod.GET, httpEntityGet, String.class);
         assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
 
-        // String putBody = " configuration-id, configuration-type configuration-sub-type";
+        // String putBody = " configuration-id, configuration-type
+        // configuration-sub-type";
         String putBody = "{" + "\"configuration-id\": \"" + cid + "\"," + "\"configuration-type\": \"type1\","
                 + "\"configuration-sub-type\": \"subtype1\", " + "\"operational-status\": \"example1\", "
                 + "\"orchestration-status\": \"example1\", " + "\"configuration-selflink\": \"example1\", "
@@ -171,29 +175,31 @@ public class ConfigurationTest extends AbstractSpringRestTest {
         ResponseEntity<String> responseEntity = null;
         String responseBody = null;
 
-        // set Accept as text/plain in order to get access of endpoint "/actuator/prometheus"
+        // set Accept as text/plain in order to get access of endpoint
+        // "/actuator/prometheus"
         headersGet.set("Accept", "text/plain");
         headersGet.setAccept(Arrays.asList(MediaType.TEXT_PLAIN));
         httpEntityGet = new HttpEntity<String>(headersGet);
-        responseEntity =
-                restTemplate.exchange(actuatorurl + "/actuator/prometheus", HttpMethod.GET, httpEntityGet, String.class);
+        responseEntity = restTemplate.exchange(actuatorurl + "/actuator/prometheus", HttpMethod.GET, httpEntityGet,
+                String.class);
         responseBody = (String) responseEntity.getBody();
         assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
         assertTrue(responseBody.contains("group_id"));
         assertTrue(responseBody.contains("aai_uri"));
 
-        // Set Accept as MediaType.APPLICATION_JSON in order to get access of endpoint "/actuator/info" and
+        // Set Accept as MediaType.APPLICATION_JSON in order to get access of endpoint
+        // "/actuator/info" and
         // "/actuator/health"
         headersGet.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
         httpEntityGet = new HttpEntity<String>(headersGet);
-        responseEntity =
-                restTemplate.exchange(actuatorurl + "/actuator/info", HttpMethod.GET, httpEntityGet, String.class);
+        responseEntity = restTemplate.exchange(actuatorurl + "/actuator/info", HttpMethod.GET, httpEntityGet,
+                String.class);
         responseBody = (String) responseEntity.getBody();
         assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
         assertTrue(responseBody.contains("aai-resources"));
 
-        responseEntity =
-                restTemplate.exchange(actuatorurl + "/actuator/health", HttpMethod.GET, httpEntityGet, String.class);
+        responseEntity = restTemplate.exchange(actuatorurl + "/actuator/health", HttpMethod.GET, httpEntityGet,
+                String.class);
         responseBody = (String) responseEntity.getBody();
         assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
         assertTrue(responseBody.contains("UP"));
index 73eed51..b48cdb2 100644 (file)
@@ -38,7 +38,6 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
index e515098..e97a43a 100644 (file)
@@ -41,7 +41,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.json.JSONException;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
index 8c62950..d0455b1 100644 (file)
@@ -41,7 +41,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.json.JSONException;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
index 253a191..b4cd460 100644 (file)
@@ -76,6 +76,8 @@ schema.translator.list=config
 
 #To expose the Prometheus scraping endpoint in unit test
 management.server.port=0
+management.endpoint.metrics.enabled=true
+management.endpoint.prometheus.enabled=true
 management.endpoints.enabled-by-default=true
 management.endpoints.web.exposure.include=info, health, prometheus
 management.metrics.web.server.request.autotime.enabled=false
index a550336..80050ca 100644 (file)
        <logger name="org.testcontainers" level="INFO"/>
        <logger name="com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.wire" level="OFF"/>
 
-       <root level="DEBUG">
+       <root level="INFO">
                <appender-ref ref="external" />
                <appender-ref ref="STDOUT" />
        </root>
diff --git a/pom.xml b/pom.xml
index f6f2e92..6bb5a2b 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
     </parent>
     <groupId>org.onap.aai.resources</groupId>
     <artifactId>resources</artifactId>
-    <version>1.14.6-SNAPSHOT</version>
+    <version>1.14.7-SNAPSHOT</version>
     <name>aai-resources</name>
     <packaging>pom</packaging>
     <modules>
index cae43c2..e8f490e 100644 (file)
@@ -5,7 +5,7 @@
 
 major_version=1
 minor_version=14
-patch_version=6
+patch_version=7
 
 base_version=${major_version}.${minor_version}.${patch_version}