summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Smokowski <ss835w@att.com>2020-11-30 14:49:48 +0000
committerGerrit Code Review <gerrit@onap.org>2020-11-30 14:49:48 +0000
commit0fb130d77336e7476bbc09cc453efabf6fa1b16e (patch)
treebc26a41332365895dd4c9949ff229fbd49f46a15
parentaec0236281f227a0a68b38ca7b0e56e50ffa95b7 (diff)
parenta16231657fe29334a589c98290ac8b6b2710a144 (diff)
Merge "add caching to graph inventory client"
-rw-r--r--adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/AaiClientPropertiesImpl.java20
-rw-r--r--asdc-controller/src/main/java/org/onap/so/asdc/tenantIsolation/AaiClientPropertiesImpl.java20
-rw-r--r--bpmn/MSOCommonBPMN/src/main/java/org/onap/so/client/restproperties/AAIPropertiesImpl.java19
-rw-r--r--common/pom.xml12
-rw-r--r--common/src/main/java/org/onap/so/client/AddCacheHeaders.java28
-rw-r--r--common/src/main/java/org/onap/so/client/CacheFactory.java25
-rw-r--r--common/src/main/java/org/onap/so/client/CacheProperties.java13
-rw-r--r--common/src/main/java/org/onap/so/client/RestClient.java14
-rw-r--r--common/src/main/java/org/onap/so/client/RestClientSSL.java4
-rw-r--r--common/src/main/java/org/onap/so/client/RestProperties.java8
-rw-r--r--common/src/test/java/org/onap/so/client/RestClientTest.java4
-rw-r--r--common/src/test/resources/logback-test.xml91
-rw-r--r--graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheControlFeature.java137
-rw-r--r--graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheLogger.java53
-rw-r--r--graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/FlushCache.java41
-rw-r--r--graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/aai/AAIProperties.java12
-rw-r--r--graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/graphinventory/GraphInventoryRestClient.java20
-rw-r--r--graph-inventory/aai-client/src/test/java/org/onap/aaiclient/client/aai/AAIRestClientTest.java116
-rw-r--r--mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/tenantisolation/AaiClientPropertiesImpl.java20
-rw-r--r--so-etsi-nfvo/so-etsi-nfvo-ns-lcm/so-etsi-nfvo-ns-lcm-bpmn-flows/src/main/java/org/onap/so/etsi/nfvo/ns/lcm/bpmn/flows/extclients/aai/AaiPropertiesImpl.java20
20 files changed, 625 insertions, 52 deletions
diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/AaiClientPropertiesImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/AaiClientPropertiesImpl.java
index b7e214f9fc..cd32cc208a 100644
--- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/AaiClientPropertiesImpl.java
+++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/AaiClientPropertiesImpl.java
@@ -24,6 +24,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import org.onap.aaiclient.client.aai.AAIProperties;
import org.onap.aaiclient.client.aai.AAIVersion;
+import org.onap.so.client.CacheProperties;
import org.onap.so.spring.SpringContextHelper;
import org.springframework.context.ApplicationContext;
@@ -33,6 +34,8 @@ public class AaiClientPropertiesImpl implements AAIProperties {
private String auth;
private String key;
private Long readTimeout;
+ private boolean enableCaching;
+ private Long cacheMaxAge;
private static final String SYSTEM_NAME = "MSO";
public AaiClientPropertiesImpl() {
@@ -41,6 +44,8 @@ public class AaiClientPropertiesImpl implements AAIProperties {
this.auth = context.getEnvironment().getProperty("aai.auth");
this.key = context.getEnvironment().getProperty("mso.msoKey");
this.readTimeout = context.getEnvironment().getProperty("aai.readTimeout", Long.class, new Long(60000));
+ this.enableCaching = context.getEnvironment().getProperty("aai.caching.enabled", Boolean.class, false);
+ this.cacheMaxAge = context.getEnvironment().getProperty("aai.caching.maxAge", Long.class, 60000L);
}
@Override
@@ -72,4 +77,19 @@ public class AaiClientPropertiesImpl implements AAIProperties {
public Long getReadTimeout() {
return this.readTimeout;
}
+
+ @Override
+ public boolean isCachingEnabled() {
+ return this.enableCaching;
+ }
+
+ @Override
+ public CacheProperties getCacheProperties() {
+ return new AAICacheProperties() {
+ @Override
+ public Long getMaxAge() {
+ return cacheMaxAge;
+ }
+ };
+ }
}
diff --git a/asdc-controller/src/main/java/org/onap/so/asdc/tenantIsolation/AaiClientPropertiesImpl.java b/asdc-controller/src/main/java/org/onap/so/asdc/tenantIsolation/AaiClientPropertiesImpl.java
index 7b89af0910..ace0ff1f57 100644
--- a/asdc-controller/src/main/java/org/onap/so/asdc/tenantIsolation/AaiClientPropertiesImpl.java
+++ b/asdc-controller/src/main/java/org/onap/so/asdc/tenantIsolation/AaiClientPropertiesImpl.java
@@ -24,6 +24,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import org.onap.aaiclient.client.aai.AAIProperties;
import org.onap.aaiclient.client.aai.AAIVersion;
+import org.onap.so.client.CacheProperties;
import org.onap.so.spring.SpringContextHelper;
import org.springframework.context.ApplicationContext;
@@ -33,6 +34,8 @@ public class AaiClientPropertiesImpl implements AAIProperties {
private String auth;
private String key;
private Long readTimeout;
+ private boolean enableCaching;
+ private Long cacheMaxAge;
private static final String SYSTEM_NAME = "MSO";
public AaiClientPropertiesImpl() {
@@ -41,6 +44,8 @@ public class AaiClientPropertiesImpl implements AAIProperties {
this.auth = context.getEnvironment().getProperty("aai.auth");
this.key = context.getEnvironment().getProperty("mso.msoKey");
this.readTimeout = context.getEnvironment().getProperty("aai.readTimeout", Long.class, new Long(60000));
+ this.enableCaching = context.getEnvironment().getProperty("aai.caching.enabled", Boolean.class, false);
+ this.cacheMaxAge = context.getEnvironment().getProperty("aai.caching.maxAge", Long.class, 60000L);
}
@Override
@@ -74,4 +79,19 @@ public class AaiClientPropertiesImpl implements AAIProperties {
public Long getReadTimeout() {
return this.readTimeout;
}
+
+ @Override
+ public boolean isCachingEnabled() {
+ return this.enableCaching;
+ }
+
+ @Override
+ public CacheProperties getCacheProperties() {
+ return new AAICacheProperties() {
+ @Override
+ public Long getMaxAge() {
+ return cacheMaxAge;
+ }
+ };
+ }
}
diff --git a/bpmn/MSOCommonBPMN/src/main/java/org/onap/so/client/restproperties/AAIPropertiesImpl.java b/bpmn/MSOCommonBPMN/src/main/java/org/onap/so/client/restproperties/AAIPropertiesImpl.java
index f67af20ef1..98a14fc0e5 100644
--- a/bpmn/MSOCommonBPMN/src/main/java/org/onap/so/client/restproperties/AAIPropertiesImpl.java
+++ b/bpmn/MSOCommonBPMN/src/main/java/org/onap/so/client/restproperties/AAIPropertiesImpl.java
@@ -25,6 +25,7 @@ import java.net.URL;
import org.onap.aaiclient.client.aai.AAIProperties;
import org.onap.aaiclient.client.aai.AAIVersion;
import org.onap.so.bpmn.core.UrnPropertiesReader;
+import org.onap.so.client.CacheProperties;
import org.springframework.stereotype.Component;
@Component
@@ -34,6 +35,9 @@ public class AAIPropertiesImpl implements AAIProperties {
public static final String AAI_AUTH = "aai.auth";
public static final String AAI_ENDPOINT = "aai.endpoint";
public static final String AAI_READ_TIMEOUT = "aai.readTimeout";
+ public static final String AAI_ENABLE_CACHING = "aai.caching.enable";
+ public static final String AAI_CACHE_MAX_AGE = "aai.caching.maxAge";
+
private UrnPropertiesReader reader;
@Override
@@ -66,4 +70,19 @@ public class AAIPropertiesImpl implements AAIProperties {
return Long.valueOf(reader.getVariable(AAI_READ_TIMEOUT, "60000"));
}
+ @Override
+ public boolean isCachingEnabled() {
+ return Boolean.parseBoolean(reader.getVariable(AAI_ENABLE_CACHING, "false"));
+ }
+
+ @Override
+ public CacheProperties getCacheProperties() {
+ return new AAICacheProperties() {
+ @Override
+ public Long getMaxAge() {
+ return Long.valueOf(reader.getVariable(AAI_CACHE_MAX_AGE, "60000"));
+ }
+ };
+ }
+
}
diff --git a/common/pom.xml b/common/pom.xml
index 74e51805ad..6e265925c3 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -293,6 +293,16 @@
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
+ <dependency>
+ <groupId>javax.cache</groupId>
+ <artifactId>cache-api</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ehcache</groupId>
+ <artifactId>ehcache</artifactId>
+ <version>3.8.1</version>
+ </dependency>
</dependencies>
<dependencyManagement>
<dependencies>
@@ -366,4 +376,4 @@
</plugin>
</plugins>
</build>
-</project> \ No newline at end of file
+</project>
diff --git a/common/src/main/java/org/onap/so/client/AddCacheHeaders.java b/common/src/main/java/org/onap/so/client/AddCacheHeaders.java
new file mode 100644
index 0000000000..1a41be1233
--- /dev/null
+++ b/common/src/main/java/org/onap/so/client/AddCacheHeaders.java
@@ -0,0 +1,28 @@
+package org.onap.so.client;
+
+import java.io.IOException;
+import java.util.Collections;
+import javax.annotation.Priority;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+@Priority(1)
+public class AddCacheHeaders implements ClientResponseFilter {
+
+ private final CacheProperties props;
+
+ public AddCacheHeaders(CacheProperties props) {
+ this.props = props;
+ }
+
+ public void filter(ClientRequestContext request, ClientResponseContext response) throws IOException {
+ if (request.getMethod().equalsIgnoreCase("GET")) {
+ response.getHeaders().putIfAbsent("Cache-Control",
+ Collections.singletonList("public, max-age=" + (props.getMaxAge() / 1000)));
+ }
+
+ }
+}
diff --git a/common/src/main/java/org/onap/so/client/CacheFactory.java b/common/src/main/java/org/onap/so/client/CacheFactory.java
new file mode 100644
index 0000000000..6bc4858463
--- /dev/null
+++ b/common/src/main/java/org/onap/so/client/CacheFactory.java
@@ -0,0 +1,25 @@
+package org.onap.so.client;
+
+
+import java.util.concurrent.TimeUnit;
+import javax.cache.configuration.Factory;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.expiry.TouchedExpiryPolicy;
+
+public class CacheFactory implements Factory<ExpiryPolicy> {
+
+ private static final long serialVersionUID = 8948728679233836929L;
+
+ private final CacheProperties props;
+
+ public CacheFactory(CacheProperties props) {
+ this.props = props;
+ }
+
+ @Override
+ public ExpiryPolicy create() {
+ return TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, props.getMaxAge())).create();
+ }
+
+}
diff --git a/common/src/main/java/org/onap/so/client/CacheProperties.java b/common/src/main/java/org/onap/so/client/CacheProperties.java
new file mode 100644
index 0000000000..4fb2a87a5b
--- /dev/null
+++ b/common/src/main/java/org/onap/so/client/CacheProperties.java
@@ -0,0 +1,13 @@
+package org.onap.so.client;
+
+public interface CacheProperties {
+
+
+ default Long getMaxAge() {
+ return 60000L;
+ }
+
+ default String getCacheName() {
+ return "default-http-cache";
+ }
+}
diff --git a/common/src/main/java/org/onap/so/client/RestClient.java b/common/src/main/java/org/onap/so/client/RestClient.java
index 9fce328b1d..be0a0f3f9e 100644
--- a/common/src/main/java/org/onap/so/client/RestClient.java
+++ b/common/src/main/java/org/onap/so/client/RestClient.java
@@ -188,8 +188,20 @@ public abstract class RestClient {
return APPLICATION_MERGE_PATCH_JSON;
}
+ protected ClientBuilder getClientBuilder() {
+ ClientBuilder builder = ClientBuilder.newBuilder();
+ if (props.isCachingEnabled()) {
+ enableCaching(builder);
+ }
+ return builder.readTimeout(props.getReadTimeout(), TimeUnit.MILLISECONDS);
+ }
+
+ protected ClientBuilder enableCaching(ClientBuilder builder) {
+ return builder;
+ }
+
protected Client getClient() {
- return ClientBuilder.newBuilder().readTimeout(props.getReadTimeout(), TimeUnit.MILLISECONDS).build();
+ return getClientBuilder().build();
}
protected abstract ONAPComponentsList getTargetEntity();
diff --git a/common/src/main/java/org/onap/so/client/RestClientSSL.java b/common/src/main/java/org/onap/so/client/RestClientSSL.java
index 8956e20a5a..c6252e4652 100644
--- a/common/src/main/java/org/onap/so/client/RestClientSSL.java
+++ b/common/src/main/java/org/onap/so/client/RestClientSSL.java
@@ -24,7 +24,6 @@ import java.net.URI;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
@@ -57,8 +56,7 @@ public abstract class RestClientSSL extends RestClient {
}
}
// Use default SSL context
- client = ClientBuilder.newBuilder().sslContext(SSLContext.getDefault())
- .readTimeout(props.getReadTimeout(), TimeUnit.MILLISECONDS).build();
+ client = getClientBuilder().sslContext(SSLContext.getDefault()).build();
logger.info("RestClientSSL using default SSL context!");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
diff --git a/common/src/main/java/org/onap/so/client/RestProperties.java b/common/src/main/java/org/onap/so/client/RestProperties.java
index 36da424f93..a7a0ef614c 100644
--- a/common/src/main/java/org/onap/so/client/RestProperties.java
+++ b/common/src/main/java/org/onap/so/client/RestProperties.java
@@ -49,4 +49,12 @@ public interface RestProperties {
public default Long getReadTimeout() {
return Long.valueOf(60000);
}
+
+ public default boolean isCachingEnabled() {
+ return false;
+ }
+
+ public default CacheProperties getCacheProperties() {
+ return new CacheProperties() {};
+ }
}
diff --git a/common/src/test/java/org/onap/so/client/RestClientTest.java b/common/src/test/java/org/onap/so/client/RestClientTest.java
index c6e282c14a..d40576b69f 100644
--- a/common/src/test/java/org/onap/so/client/RestClientTest.java
+++ b/common/src/test/java/org/onap/so/client/RestClientTest.java
@@ -49,6 +49,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.onap.logging.filter.base.ONAPComponents;
import org.onap.logging.filter.base.ONAPComponentsList;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
@RunWith(MockitoJUnitRunner.class)
@@ -61,7 +62,8 @@ public class RestClientTest {
public ExpectedException thrown = ExpectedException.none();
@Rule
- public WireMockRule wireMockRule = new WireMockRule(WireMockConfiguration.options().dynamicPort());
+ public WireMockRule wireMockRule = new WireMockRule(
+ WireMockConfiguration.options().dynamicPort().extensions(new ResponseTemplateTransformer(false)));
@Test
public void retries() throws Exception {
diff --git a/common/src/test/resources/logback-test.xml b/common/src/test/resources/logback-test.xml
index b52e6be022..3c5f259817 100644
--- a/common/src/test/resources/logback-test.xml
+++ b/common/src/test/resources/logback-test.xml
@@ -19,61 +19,60 @@
-->
<configuration>
- <property name="p_tim" value="%d{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;, UTC}"/>
- <property name="p_lvl" value="%level"/>
- <property name="p_log" value="%logger"/>
- <property name="p_mdc" value="%replace(%replace(%mdc){'\t','\\\\t'}){'\n', '\\\\n'}"/>
- <property name="p_msg" value="%replace(%replace(%msg){'\t', '\\\\t'}){'\n','\\\\n'}"/>
- <property name="p_exc" value="%replace(%replace(%rootException){'\t', '\\\\t'}){'\n','\\\\n'}"/>
- <property name="p_mak" value="%replace(%replace(%marker){'\t', '\\\\t'}){'\n','\\\\n'}"/>
- <property name="p_thr" value="%thread"/>
- <property name="pattern" value="%nopexception${p_tim}\t${p_thr}\t${p_lvl}\t${p_log}\t${p_mdc}\t${p_msg}\t${p_exc}\t${p_mak}\t%n"/>
+ <property name="p_tim" value="%d{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;, UTC}" />
+ <property name="p_lvl" value="%level" />
+ <property name="p_log" value="%logger" />
+ <property name="p_mdc" value="%replace(%replace(%mdc){'\t','\\\\t'}){'\n', '\\\\n'}" />
+ <property name="p_msg" value="%replace(%replace(%msg){'\t', '\\\\t'}){'\n','\\\\n'}" />
+ <property name="p_exc" value="%replace(%replace(%rootException){'\t', '\\\\t'}){'\n','\\\\n'}" />
+ <property name="p_mak" value="%replace(%replace(%marker){'\t', '\\\\t'}){'\n','\\\\n'}" />
+ <property name="p_thr" value="%thread" />
+ <property name="pattern"
+ value="%nopexception${p_tim}\t${p_thr}\t${p_lvl}\t${p_log}\t${p_mdc}\t${p_msg}\t${p_exc}\t${p_mak}\t%n" />
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>${pattern}</pattern>
- </encoder>
- </appender>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>${pattern}</pattern>
+ </encoder>
+ </appender>
- <appender name="test"
- class="org.onap.so.utils.TestAppender" />
+ <appender name="test" class="org.onap.so.utils.TestAppender" />
- <logger name="com.att.ecomp.audit" level="info" additivity="false">
- <appender-ref ref="STDOUT" />
- </logger>
+ <logger name="com.att.ecomp.audit" level="info" additivity="false">
+ <appender-ref ref="STDOUT" />
+ </logger>
- <logger name="com.att.eelf.metrics" level="info" additivity="false">
- <appender-ref ref="STDOUT" />
- </logger>
+ <logger name="com.att.eelf.metrics" level="info" additivity="false">
+ <appender-ref ref="STDOUT" />
+ </logger>
- <logger name="com.att.eelf.error" level="WARN" additivity="false">
- <appender-ref ref="STDOUT" />
- </logger>
+ <logger name="com.att.eelf.error" level="WARN" additivity="false">
+ <appender-ref ref="STDOUT" />
+ </logger>
- <logger name="org.onap" level="${so.log.level:-DEBUG}" additivity="false">
- <appender-ref ref="STDOUT" />
- <appender-ref ref="test" />
- </logger>
-
- <logger name="org.flywaydb" level="DEBUG" additivity="false">
- <appender-ref ref="STDOUT" />
- </logger>
-
+ <logger name="org.onap" level="${so.log.level:-DEBUG}" additivity="false">
+ <appender-ref ref="STDOUT" />
+ <appender-ref ref="test" />
+ </logger>
- <logger name="ch.vorburger" level="WARN" additivity="false">
- <appender-ref ref="STDOUT" />
- </logger>
-
- <logger name="org.reflections" level="ERROR" additivity="false">
- <appender-ref ref="STDOUT" />
- </logger>
-
+ <logger name="org.flywaydb" level="DEBUG" additivity="false">
+ <appender-ref ref="STDOUT" />
+ </logger>
- <root level="WARN">
- <appender-ref ref="STDOUT" />
- <appender-ref ref="test" />
- </root>
+
+ <logger name="ch.vorburger" level="WARN" additivity="false">
+ <appender-ref ref="STDOUT" />
+ </logger>
+
+ <logger name="org.reflections" level="ERROR" additivity="false">
+ <appender-ref ref="STDOUT" />
+ </logger>
+
+ <root level="WARN">
+ <appender-ref ref="STDOUT" />
+ <appender-ref ref="test" />
+ </root>
</configuration> \ No newline at end of file
diff --git a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheControlFeature.java b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheControlFeature.java
new file mode 100644
index 0000000000..1e7c3e7f71
--- /dev/null
+++ b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheControlFeature.java
@@ -0,0 +1,137 @@
+package org.onap.aaiclient.client;
+
+import java.io.Closeable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Properties;
+import javax.annotation.PreDestroy;
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.Factory;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.configuration.MutableCacheEntryListenerConfiguration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheWriter;
+import javax.cache.spi.CachingProvider;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+import org.apache.cxf.jaxrs.client.cache.CacheControlClientReaderInterceptor;
+import org.apache.cxf.jaxrs.client.cache.CacheControlClientRequestFilter;
+import org.apache.cxf.jaxrs.client.cache.Entry;
+import org.apache.cxf.jaxrs.client.cache.Key;
+
+
+@Provider
+public class CacheControlFeature implements Feature, Closeable {
+ private CachingProvider provider;
+ private CacheManager manager;
+ private Cache<Key, Entry> cache;
+ private boolean cacheResponseInputStream;
+ private Factory<ExpiryPolicy> expiryPolicy;
+
+ @Override
+ public boolean configure(final FeatureContext context) {
+ // TODO: read context properties to exclude some patterns?
+ final Cache<Key, Entry> entryCache = createCache(context.getConfiguration().getProperties());
+ context.register(new CacheControlClientRequestFilter(entryCache));
+ CacheControlClientReaderInterceptor reader = new CacheControlClientReaderInterceptor(entryCache);
+ reader.setCacheResponseInputStream(cacheResponseInputStream);
+ context.register(reader);
+ return true;
+ }
+
+ @PreDestroy // TODO: check it is called
+ public void close() {
+ for (final Closeable c : Arrays.asList(cache, manager, provider)) {
+ try {
+ if (c != null) {
+ c.close();
+ }
+ } catch (final Exception e) {
+ // no-op
+ }
+ }
+ }
+
+ private Cache<Key, Entry> createCache(final Map<String, Object> properties) {
+ final Properties props = new Properties();
+ props.putAll(properties);
+
+ final String prefix = this.getClass().getName() + ".";
+ final String uri = props.getProperty(prefix + "config-uri");
+ final String name = props.getProperty(prefix + "name", this.getClass().getName());
+
+ final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+
+ provider = Caching.getCachingProvider();
+ try {
+ synchronized (contextClassLoader) {
+ manager = provider.getCacheManager(uri == null ? provider.getDefaultURI() : new URI(uri),
+ contextClassLoader, props);
+ if (manager.getCache(name) == null) {
+ final MutableConfiguration<Key, Entry> configuration = new MutableConfiguration<Key, Entry>()
+ .setReadThrough("true".equalsIgnoreCase(props.getProperty(prefix + "readThrough", "false")))
+ .setWriteThrough(
+ "true".equalsIgnoreCase(props.getProperty(prefix + "writeThrough", "false")))
+ .setManagementEnabled(
+ "true".equalsIgnoreCase(props.getProperty(prefix + "managementEnabled", "false")))
+ .setStatisticsEnabled(
+ "true".equalsIgnoreCase(props.getProperty(prefix + "statisticsEnabled", "false")))
+ .setStoreByValue(
+ "true".equalsIgnoreCase(props.getProperty(prefix + "storeByValue", "false")));
+
+ final String loader = props.getProperty(prefix + "loaderFactory");
+ if (loader != null) {
+ @SuppressWarnings("unchecked")
+ Factory<? extends CacheLoader<Key, Entry>> f =
+ newInstance(contextClassLoader, loader, Factory.class);
+ configuration.setCacheLoaderFactory(f);
+ }
+ final String writer = props.getProperty(prefix + "writerFactory");
+ if (writer != null) {
+ @SuppressWarnings("unchecked")
+ Factory<? extends CacheWriter<Key, Entry>> f =
+ newInstance(contextClassLoader, writer, Factory.class);
+ configuration.setCacheWriterFactory(f);
+ }
+ if (expiryPolicy != null) {
+
+ configuration.setExpiryPolicyFactory(expiryPolicy);
+ }
+ configuration.addCacheEntryListenerConfiguration(new MutableCacheEntryListenerConfiguration(
+ FactoryBuilder.factoryOf(new CacheLogger()), null, true, true));
+
+ cache = manager.createCache(name, configuration);
+ } else {
+ cache = manager.getCache(name);
+ }
+ return cache;
+ }
+ } catch (final URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T newInstance(final ClassLoader contextClassLoader, final String clazz, final Class<T> cast) {
+ try {
+ return (T) contextClassLoader.loadClass(clazz).newInstance();
+ } catch (final Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public void setCacheResponseInputStream(boolean cacheStream) {
+ this.cacheResponseInputStream = cacheStream;
+ }
+
+ public void setExpiryPolicyFactory(Factory<ExpiryPolicy> f) {
+ this.expiryPolicy = f;
+ }
+}
diff --git a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheLogger.java b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheLogger.java
new file mode 100644
index 0000000000..f3dc610125
--- /dev/null
+++ b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/CacheLogger.java
@@ -0,0 +1,53 @@
+package org.onap.aaiclient.client;
+
+import java.io.Serializable;
+import javax.cache.event.CacheEntryCreatedListener;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryExpiredListener;
+import javax.cache.event.CacheEntryListenerException;
+import javax.cache.event.CacheEntryRemovedListener;
+import javax.cache.event.CacheEntryUpdatedListener;
+import org.apache.cxf.jaxrs.client.cache.Entry;
+import org.apache.cxf.jaxrs.client.cache.Key;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CacheLogger implements CacheEntryExpiredListener<Key, Entry>, CacheEntryCreatedListener<Key, Entry>,
+ CacheEntryUpdatedListener<Key, Entry>, CacheEntryRemovedListener<Key, Entry>, Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private static final Logger logger = LoggerFactory.getLogger(CacheLogger.class);
+
+ @Override
+ public void onExpired(Iterable<CacheEntryEvent<? extends Key, ? extends Entry>> events)
+ throws CacheEntryListenerException {
+ for (CacheEntryEvent<? extends Key, ? extends Entry> event : events) {
+ logger.debug("{} expired key: {}", event.getSource().getName(), event.getKey().getUri());
+ }
+ }
+
+ @Override
+ public void onRemoved(Iterable<CacheEntryEvent<? extends Key, ? extends Entry>> events)
+ throws CacheEntryListenerException {
+ for (CacheEntryEvent<? extends Key, ? extends Entry> event : events) {
+ logger.debug("{} removed key: {}", event.getSource().getName(), event.getKey().getUri());
+ }
+ }
+
+ @Override
+ public void onUpdated(Iterable<CacheEntryEvent<? extends Key, ? extends Entry>> events)
+ throws CacheEntryListenerException {
+ for (CacheEntryEvent<? extends Key, ? extends Entry> event : events) {
+ logger.debug("{} updated key: {}", event.getSource().getName(), event.getKey().getUri());
+ }
+ }
+
+ @Override
+ public void onCreated(Iterable<CacheEntryEvent<? extends Key, ? extends Entry>> events)
+ throws CacheEntryListenerException {
+ for (CacheEntryEvent<? extends Key, ? extends Entry> event : events) {
+ logger.debug("{} created key: {}", event.getSource().getName(), event.getKey().getUri());
+ }
+ }
+
+}
diff --git a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/FlushCache.java b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/FlushCache.java
new file mode 100644
index 0000000000..0f290ff620
--- /dev/null
+++ b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/FlushCache.java
@@ -0,0 +1,41 @@
+package org.onap.aaiclient.client;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import org.apache.cxf.jaxrs.client.cache.Key;
+import org.onap.so.client.CacheProperties;
+
+public class FlushCache implements ClientResponseFilter {
+
+ private static final Set<String> modifyMethods =
+ new HashSet<>(Arrays.asList(HttpMethod.DELETE, HttpMethod.PATCH, HttpMethod.PUT, HttpMethod.POST));
+
+ private final CacheProperties props;
+
+ public FlushCache(CacheProperties props) {
+ this.props = props;
+ }
+
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+
+ if (responseContext.getStatus() >= 200 && responseContext.getStatus() <= 299) {
+ if (FlushCache.modifyMethods.contains(requestContext.getMethod())) {
+
+ CacheManager cacheManager = Caching.getCachingProvider().getCacheManager(
+ Caching.getCachingProvider().getDefaultURI(), Thread.currentThread().getContextClassLoader());
+ cacheManager.getCache(props.getCacheName()).remove(
+ new Key(requestContext.getUri(), requestContext.getAcceptableMediaTypes().get(0).toString()));
+ }
+ }
+ }
+
+}
diff --git a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/aai/AAIProperties.java b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/aai/AAIProperties.java
index ac8a6e6e52..9c7798f4c5 100644
--- a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/aai/AAIProperties.java
+++ b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/aai/AAIProperties.java
@@ -20,6 +20,7 @@
package org.onap.aaiclient.client.aai;
+import org.onap.so.client.CacheProperties;
import org.onap.so.client.RestProperties;
public interface AAIProperties extends RestProperties {
@@ -34,4 +35,15 @@ public interface AAIProperties extends RestProperties {
public default boolean mapNotFoundToEmpty() {
return true;
}
+
+ default CacheProperties getCacheProperties() {
+ return new AAICacheProperties() {};
+ }
+
+ public interface AAICacheProperties extends CacheProperties {
+
+ default String getCacheName() {
+ return "aai-http-cache";
+ }
+ }
}
diff --git a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/graphinventory/GraphInventoryRestClient.java b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/graphinventory/GraphInventoryRestClient.java
index c2422085aa..c22f2f5f8e 100644
--- a/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/graphinventory/GraphInventoryRestClient.java
+++ b/graph-inventory/aai-client/src/main/java/org/onap/aaiclient/client/graphinventory/GraphInventoryRestClient.java
@@ -23,8 +23,13 @@ package org.onap.aaiclient.client.graphinventory;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
+import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
+import org.onap.aaiclient.client.CacheControlFeature;
+import org.onap.aaiclient.client.FlushCache;
import org.onap.logging.filter.base.ONAPComponentsList;
+import org.onap.so.client.AddCacheHeaders;
+import org.onap.so.client.CacheFactory;
import org.onap.so.client.ResponseExceptionMapper;
import org.onap.so.client.RestClientSSL;
import org.onap.so.client.RestProperties;
@@ -41,6 +46,21 @@ public abstract class GraphInventoryRestClient extends RestClientSSL {
super(props, Optional.of(uri));
}
+
+ protected ClientBuilder enableCaching(ClientBuilder builder) {
+ builder.register(new AddCacheHeaders(props.getCacheProperties()));
+ builder.register(new FlushCache(props.getCacheProperties()));
+ CacheControlFeature cacheControlFeature = new CacheControlFeature();
+ cacheControlFeature.setCacheResponseInputStream(true);
+ cacheControlFeature.setExpiryPolicyFactory(new CacheFactory(props.getCacheProperties()));
+ builder.property("org.onap.aaiclient.client.CacheControlFeature.name",
+ props.getCacheProperties().getCacheName());
+
+ builder.register(cacheControlFeature);
+
+ return builder;
+ }
+
@Override
public abstract ONAPComponentsList getTargetEntity();
diff --git a/graph-inventory/aai-client/src/test/java/org/onap/aaiclient/client/aai/AAIRestClientTest.java b/graph-inventory/aai-client/src/test/java/org/onap/aaiclient/client/aai/AAIRestClientTest.java
index b73454fe67..d0f7847726 100644
--- a/graph-inventory/aai-client/src/test/java/org/onap/aaiclient/client/aai/AAIRestClientTest.java
+++ b/graph-inventory/aai-client/src/test/java/org/onap/aaiclient/client/aai/AAIRestClientTest.java
@@ -25,7 +25,11 @@ import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.matching;
+import static com.github.tomakehurst.wiremock.client.WireMock.put;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
+import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.ArgumentMatchers.any;
@@ -35,8 +39,10 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URL;
import java.util.HashMap;
import javax.ws.rs.core.Response;
import org.junit.Rule;
@@ -48,6 +54,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.onap.aaiclient.client.defaultproperties.DefaultAAIPropertiesImpl;
import org.onap.aaiclient.client.graphinventory.GraphInventoryPatchConverter;
import org.onap.aaiclient.client.graphinventory.exceptions.GraphInventoryPatchDepthExceededException;
+import org.onap.so.client.RestClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.google.common.collect.ImmutableMap;
@@ -96,4 +103,113 @@ public class AAIRestClientTest {
wireMockRule.verify(getRequestedFor(urlPathEqualTo("/test")).withHeader("X-FromAppId", equalTo("MSO"))
.withHeader("X-TransactionId", matching(".*")).withHeader("test", equalTo("value")));
}
+
+
+ @Test
+ public void cacheGetTest() throws URISyntaxException, InterruptedException {
+
+ wireMockRule.stubFor(get(urlPathMatching("/cached"))
+ .willReturn(aResponse().withStatus(200).withHeader("Content-Type", "text/plain").withBody("value")));
+
+ AAIProperties props = new AAIProperties() {
+
+ @Override
+ public URL getEndpoint() throws MalformedURLException {
+ return new URL(String.format("http://localhost:%s", wireMockRule.port()));
+ }
+
+ @Override
+ public String getSystemName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isCachingEnabled() {
+ return true;
+ }
+
+ @Override
+ public AAIVersion getDefaultVersion() {
+ return AAIVersion.LATEST;
+ }
+
+ @Override
+ public String getAuth() {
+ return null;
+ }
+
+ @Override
+ public String getKey() {
+ return null;
+ }
+
+ };
+ RestClient client = new AAIRestClient(props, new URI("/cached"), new HashMap<String, String>());
+
+ Response response = client.get();
+
+ response.readEntity(String.class);
+ response = client.get();
+ response.readEntity(String.class);
+ verify(1, getRequestedFor(urlEqualTo("/cached")));
+
+ }
+
+ @Test
+ public void cachePutTest() throws URISyntaxException, InterruptedException {
+
+ wireMockRule.stubFor(put(urlPathMatching("/cached/1")).willReturn(aResponse().withStatus(200)));
+
+ wireMockRule.stubFor(get(urlPathMatching("/cached/1"))
+ .willReturn(aResponse().withStatus(200).withHeader("Content-Type", "application/json").withBody("{}")));
+
+ AAIProperties props = new AAIProperties() {
+
+ @Override
+ public URL getEndpoint() throws MalformedURLException {
+ return new URL(String.format("http://localhost:%s", wireMockRule.port()));
+ }
+
+ @Override
+ public String getSystemName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isCachingEnabled() {
+ return true;
+ }
+
+ @Override
+ public AAIVersion getDefaultVersion() {
+ return AAIVersion.LATEST;
+ }
+
+ @Override
+ public String getAuth() {
+ return null;
+ }
+
+ @Override
+ public String getKey() {
+ return null;
+ }
+
+ };
+
+ RestClient client = new AAIRestClient(props, new URI("/cached/1"), new HashMap<String, String>());
+
+
+ Response response = client.get();
+
+ response.readEntity(String.class);
+ client.put("wow");
+
+ client.get();
+ response.readEntity(String.class);
+ verify(2, getRequestedFor(urlEqualTo("/cached/1")));
+
+ }
}
diff --git a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/tenantisolation/AaiClientPropertiesImpl.java b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/tenantisolation/AaiClientPropertiesImpl.java
index 6e6a9d2b07..1492baf1cd 100644
--- a/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/tenantisolation/AaiClientPropertiesImpl.java
+++ b/mso-api-handlers/mso-api-handler-infra/src/main/java/org/onap/so/apihandlerinfra/tenantisolation/AaiClientPropertiesImpl.java
@@ -24,6 +24,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import org.onap.aaiclient.client.aai.AAIProperties;
import org.onap.aaiclient.client.aai.AAIVersion;
+import org.onap.so.client.CacheProperties;
import org.onap.so.spring.SpringContextHelper;
import org.springframework.context.ApplicationContext;
@@ -33,6 +34,8 @@ public class AaiClientPropertiesImpl implements AAIProperties {
private String auth;
private String key;
private Long readTimeout;
+ private boolean enableCaching;
+ private Long cacheMaxAge;
public AaiClientPropertiesImpl() {
@@ -41,6 +44,8 @@ public class AaiClientPropertiesImpl implements AAIProperties {
this.auth = context.getEnvironment().getProperty("aai.auth");
this.key = context.getEnvironment().getProperty("mso.msoKey");
this.readTimeout = context.getEnvironment().getProperty("aai.readTimeout", Long.class, new Long(60000));
+ this.enableCaching = context.getEnvironment().getProperty("aai.caching.enabled", Boolean.class, false);
+ this.cacheMaxAge = context.getEnvironment().getProperty("aai.caching.maxAge", Long.class, 60000L);
}
@Override
@@ -72,4 +77,19 @@ public class AaiClientPropertiesImpl implements AAIProperties {
public Long getReadTimeout() {
return this.readTimeout;
}
+
+ @Override
+ public boolean isCachingEnabled() {
+ return this.enableCaching;
+ }
+
+ @Override
+ public CacheProperties getCacheProperties() {
+ return new AAICacheProperties() {
+ @Override
+ public Long getMaxAge() {
+ return cacheMaxAge;
+ }
+ };
+ }
}
diff --git a/so-etsi-nfvo/so-etsi-nfvo-ns-lcm/so-etsi-nfvo-ns-lcm-bpmn-flows/src/main/java/org/onap/so/etsi/nfvo/ns/lcm/bpmn/flows/extclients/aai/AaiPropertiesImpl.java b/so-etsi-nfvo/so-etsi-nfvo-ns-lcm/so-etsi-nfvo-ns-lcm-bpmn-flows/src/main/java/org/onap/so/etsi/nfvo/ns/lcm/bpmn/flows/extclients/aai/AaiPropertiesImpl.java
index 910d5fa75c..0aa14c711f 100644
--- a/so-etsi-nfvo/so-etsi-nfvo-ns-lcm/so-etsi-nfvo-ns-lcm-bpmn-flows/src/main/java/org/onap/so/etsi/nfvo/ns/lcm/bpmn/flows/extclients/aai/AaiPropertiesImpl.java
+++ b/so-etsi-nfvo/so-etsi-nfvo-ns-lcm/so-etsi-nfvo-ns-lcm-bpmn-flows/src/main/java/org/onap/so/etsi/nfvo/ns/lcm/bpmn/flows/extclients/aai/AaiPropertiesImpl.java
@@ -22,6 +22,7 @@ package org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.extclients.aai;
import org.onap.aaiclient.client.aai.AAIProperties;
import org.onap.aaiclient.client.aai.AAIVersion;
+import org.onap.so.client.CacheProperties;
import org.onap.so.spring.SpringContextHelper;
import org.springframework.context.ApplicationContext;
import java.net.MalformedURLException;
@@ -34,6 +35,8 @@ public class AaiPropertiesImpl implements AAIProperties {
private final String encryptionKey;
private final String aaiVersion;
private final Long readTimeout;
+ private final boolean enableCaching;
+ private final Long cacheMaxAge;
public AaiPropertiesImpl() {
final ApplicationContext context = SpringContextHelper.getAppContext();
@@ -42,6 +45,8 @@ public class AaiPropertiesImpl implements AAIProperties {
this.encryptionKey = context.getEnvironment().getProperty("mso.key");
this.aaiVersion = context.getEnvironment().getProperty("aai.version");
this.readTimeout = context.getEnvironment().getProperty("aai.readTimeout", Long.class, new Long(60000));
+ this.enableCaching = context.getEnvironment().getProperty("aai.caching.enabled", Boolean.class, false);
+ this.cacheMaxAge = context.getEnvironment().getProperty("aai.caching.maxAge", Long.class, 60000L);
}
@Override
@@ -79,4 +84,19 @@ public class AaiPropertiesImpl implements AAIProperties {
public Long getReadTimeout() {
return this.readTimeout;
}
+
+ @Override
+ public boolean isCachingEnabled() {
+ return this.enableCaching;
+ }
+
+ @Override
+ public CacheProperties getCacheProperties() {
+ return new AAICacheProperties() {
+ @Override
+ public Long getMaxAge() {
+ return cacheMaxAge;
+ }
+ };
+ }
}