blob: f13764a68348fe1eb50618a49e424df8439d7fcf [file] [log] [blame]
Gamze Abaka1e5ccf52018-07-02 11:59:03 +00001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.opencord.sadis.impl;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
19import com.google.common.cache.Cache;
20import com.google.common.cache.CacheBuilder;
21import com.google.common.collect.Maps;
22import org.slf4j.Logger;
23import org.slf4j.LoggerFactory;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.net.MalformedURLException;
28import java.net.URL;
29import java.util.Map;
30import java.util.concurrent.TimeUnit;
31
32import org.onosproject.codec.JsonCodec;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.net.config.ConfigFactory;
35import org.onosproject.net.config.NetworkConfigRegistry;
36import org.opencord.sadis.BaseInformation;
37import org.opencord.sadis.BaseConfig;
38import org.opencord.sadis.BaseInformationService;
39import java.util.Set;
40
41
42public abstract class InformationAdapter<T extends BaseInformation, K extends BaseConfig<T>>
43 implements BaseInformationService<T> {
44
45 private final Logger log = LoggerFactory.getLogger(this.getClass());
46
47 protected static final int DEFAULT_MAXIMUM_CACHE_SIZE = 0;
48 protected static final long DEFAULT_TTL = 0;
49 protected String url;
50 protected ObjectMapper mapper;
51 protected Cache<String, T> cache;
52 protected int maxiumCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE;
53 protected long cacheEntryTtl = DEFAULT_TTL;
54
55 protected Map<String, T> localCfgData = null;
56
57 public final void updateConfig(NetworkConfigRegistry cfgService) {
58 K cfg = getConfig(cfgService);
59 if (cfg == null) {
60 this.log.warn("Configuration not available");
61 return;
62 }
63 this.log.info("Cache Max Size: {}", cfg.getCacheMaxSize());
64 this.log.info("Cache TTL: {}", cfg.getCacheTtl().getSeconds());
65 this.log.info("Entries: {}", cfg.getEntries());
66
67 configure(cfg);
68 }
69
70 public K getConfig(NetworkConfigRegistry cfgService) {
71 return cfgService.getConfig(getAppId(), getConfigClass());
72 }
73
74 /**
75 * Configures the Adapter for data source and cache parameters.
76 *
77 * @param cfg Configuration data.
78 */
79 public void configure(K cfg) {
80
81 String url = null;
82 try {
83 // if the url is not present then assume data is in netcfg
84 if (cfg.getUrl() != null) {
85 url = cfg.getUrl().toString();
86 } else {
87 localCfgData = Maps.newConcurrentMap();
88
89 cfg.getEntries().forEach(entry ->
90 localCfgData.put(entry.id(), entry));
91 log.info("url is null, data source is local netcfg data");
92 }
93 } catch (MalformedURLException mUrlEx) {
94 log.error("Invalid URL specified: {}", mUrlEx);
95 }
96
97 int maximumCacheSeize = cfg.getCacheMaxSize();
98 long cacheEntryTtl = cfg.getCacheTtl().getSeconds();
99
100 // Rebuild cache if needed
101 if (isUrlChanged(url) || maximumCacheSeize != this.maxiumCacheSize ||
102 cacheEntryTtl != this.cacheEntryTtl) {
103 this.maxiumCacheSize = maximumCacheSeize;
104 this.cacheEntryTtl = cacheEntryTtl;
105 this.url = url;
106
107 Cache<String, T> newCache = CacheBuilder.newBuilder()
108 .maximumSize(maxiumCacheSize).expireAfterAccess(cacheEntryTtl, TimeUnit.SECONDS).build();
109 Cache<String, T> oldCache = cache;
110
111 synchronized (this) {
112 cache = newCache;
113 }
114
115 oldCache.invalidateAll();
116 oldCache.cleanUp();
117 }
118 }
119
120 protected boolean isUrlChanged(String url) {
121 if (url == null && this.url == null) {
122 return false;
123 }
124 return !((url == this.url) || (url != null && url.equals(this.url)));
125 }
126
127 /*
128 * (non-Javadoc)
129 *
130 * @see
131 * org.opencord.sadis.SadisService#clearCache()
132 */
133 @Override
134 public void invalidateAll() {
135 cache.invalidateAll();
136 }
137
138 /*
139 * (non-Javadoc)
140 *
141 * @see
142 * org.opencord.sadis.BaseInformationService#invalidateId()
143 */
144 @Override
145 public void invalidateId(String id) {
146 cache.invalidate(id);
147 }
148
149 /*
150 * (non-Javadoc)
151 *
152 * @see
153 * org.opencord.sadis.BaseInformationService#getfromCache(java.lang.
154 * String)
155 */
156 @Override
157 public T getfromCache(String id) {
158 Cache<String, T> local;
159 synchronized (this) {
160 local = cache;
161 }
162 T info = local.getIfPresent(id);
163 if (info != null) {
164 return info;
165 }
166 return null;
167 }
168
169 /*
170 * (non-Javadoc)
171 *
172 * @see
173 * org.opencord.sadis.BaseInformationService#get(java.lang.
174 * String)
175 */
176 @Override
177 public T get(String id) {
178 Cache<String, T> local;
179 synchronized (this) {
180 local = cache;
181 }
182
183 T info = local.getIfPresent(id);
184 if (info != null) {
185 return info;
186 }
187
188 /*
189 * Not in cache, if we have a URL configured we can attempt to get it
190 * from there, else check for it in the locally configured data
191 */
192 if (this.url == null) {
193 info = (localCfgData == null) ? null : localCfgData.get(id);
194
195 if (info != null) {
196 local.put(id, info);
197 return info;
198 }
199 } else {
200 // Augment URL with query parameters
201 String urlWithSubId = this.url.replaceAll("%s", id);
Matteo Scandolofd4d68d2020-10-08 17:37:56 -0700202 log.debug("Getting data from the remote URL {}", urlWithSubId);
Gamze Abaka1e5ccf52018-07-02 11:59:03 +0000203
204 try (InputStream io = new URL(urlWithSubId).openStream()) {
205 info = mapper.readValue(io, getInformationClass());
206 local.put(id, info);
207 return info;
208 } catch (IOException e) {
209 // TODO use a better http library that allows us to read status code
210 log.debug("Exception while reading remote data {} ", e.getMessage());
211 }
212 }
Matteo Scandoloe5630bf2020-05-27 14:24:56 -0700213 log.warn("Data not found for id {}", id);
Gamze Abaka1e5ccf52018-07-02 11:59:03 +0000214 return null;
215 }
216
217 public abstract void registerModule();
218
219 public abstract Set<ConfigFactory> getConfigFactories();
220
221 public abstract JsonCodec<T> getCodec();
222
223 public abstract Class<T> getInformationClass();
224
225 public abstract Class<K> getConfigClass();
226
227 public abstract ApplicationId getAppId();
228}