Initial commit for AAI-UI(sparky-backend)
[aai/sparky-be.git] / src / main / java / org / openecomp / sparky / dal / cache / PersistentEntityCache.java
1 /**
2  * ============LICENSE_START===================================================
3  * SPARKY (AAI UI service)
4  * ============================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
8  * ============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=====================================================
21  *
22  * ECOMP and OpenECOMP are trademarks
23  * and service marks of AT&T Intellectual Property.
24  */
25
26 package org.openecomp.sparky.dal.cache;
27
28 import static java.util.concurrent.CompletableFuture.supplyAsync;
29
30 import java.io.IOException;
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 import java.nio.file.Files;
34 import java.nio.file.LinkOption;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.concurrent.CompletableFuture;
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.ExecutorService;
42
43 import org.openecomp.cl.api.Logger;
44 import org.openecomp.cl.eelf.LoggerFactory;
45 import org.openecomp.sparky.dal.aai.ActiveInventoryAdapter;
46 import org.openecomp.sparky.dal.rest.OperationResult;
47 import org.openecomp.sparky.logging.AaiUiMsgs;
48 import org.openecomp.sparky.synchronizer.task.PersistOperationResultToDisk;
49 import org.openecomp.sparky.synchronizer.task.RetrieveOperationResultFromDisk;
50 import org.openecomp.sparky.util.NodeUtils;
51
52 import com.fasterxml.jackson.databind.ObjectMapper;
53
54 /**
55  * The Class PersistentEntityCache.
56  */
57 public class PersistentEntityCache implements EntityCache {
58
59   private static final Logger LOG =
60       LoggerFactory.getInstance().getLogger(ActiveInventoryAdapter.class);
61
62   /*
63    * TODO: <li>implement time-to-live on the cache, maybe pull in one of Guava's eviction caches?
64    * <li>implement abstract-base-cache to hold common cach-y things (like ttl)
65    */
66
67   private static final String DEFAULT_OUTPUT_PATH = "offlineEntityCache";
68   private ExecutorService persistentExecutor;
69   private ObjectMapper mapper;
70   private String storagePath;
71
72   /**
73    * Instantiates a new persistent entity cache.
74    */
75   public PersistentEntityCache() {
76     this(null, 10);
77   }
78
79   /**
80    * Instantiates a new persistent entity cache.
81    *
82    * @param numWorkers the num workers
83    */
84   public PersistentEntityCache(int numWorkers) {
85     this(null, numWorkers);
86   }
87
88   /**
89    * Instantiates a new persistent entity cache.
90    *
91    * @param storageFolderOverride the storage folder override
92    * @param numWorkers the num workers
93    */
94   public PersistentEntityCache(String storageFolderOverride, int numWorkers) {
95     persistentExecutor = NodeUtils.createNamedExecutor("PEC", numWorkers, LOG);
96     mapper = new ObjectMapper();
97
98     if (storageFolderOverride != null && storageFolderOverride.length() > 0) {
99       this.storagePath = storageFolderOverride;
100     } else {
101       this.storagePath = DEFAULT_OUTPUT_PATH;
102     }
103   }
104
105   /**
106    * Generate offline storage path from uri.
107    *
108    * @param link the link
109    * @return the string
110    */
111   private String generateOfflineStoragePathFromUri(String link) {
112
113     try {
114       URI uri = new URI(link);
115
116       String modHost = uri.getHost().replace(".", "_");
117
118       String[] tokens = uri.getPath().split("\\/");
119       List<String> resourcePathAndDomain = new ArrayList<String>();
120
121       if (tokens.length >= 4) {
122
123         int numElements = 0;
124         for (String w : tokens) {
125
126           if (numElements > 3) {
127             break;
128           }
129
130           if (w.length() > 0) {
131             resourcePathAndDomain.add(w);
132             numElements++;
133           }
134
135         }
136       } else {
137         return this.storagePath + "\\";
138       }
139
140       return this.storagePath + "\\" + modHost + "\\"
141           + NodeUtils.concatArray(resourcePathAndDomain, "_") + "\\";
142
143     } catch (Exception exc) {
144       LOG.error(AaiUiMsgs.OFFLINE_STORAGE_PATH_ERROR, link, exc.getMessage());
145     }
146
147     return this.storagePath + "\\";
148
149   }
150
151   /**
152    * Creates the dirs.
153    *
154    * @param directoryPath the directory path
155    */
156   private void createDirs(String directoryPath) {
157     if (directoryPath == null) {
158       return;
159     }
160
161     Path path = Paths.get(directoryPath);
162     // if directory exists?
163     if (!Files.exists(path)) {
164       try {
165         Files.createDirectories(path);
166       } catch (IOException exc) {
167         LOG.error(AaiUiMsgs.DISK_CREATE_DIR_IO_ERROR, exc.getMessage());
168       }
169     }
170
171   }
172
173   /* (non-Javadoc)
174    * @see org.openecomp.sparky.dal.cache.EntityCache#get(java.lang.String, java.lang.String)
175    */
176   @Override
177   public OperationResult get(String key, String link) {
178
179     final String storagePath = generateOfflineStoragePathFromUri(link);
180     createDirs(storagePath);
181     final String persistentFileName = storagePath + "\\" + key + ".json";
182
183     CompletableFuture<OperationResult> task = supplyAsync(
184         new RetrieveOperationResultFromDisk(persistentFileName, mapper, LOG), persistentExecutor);
185
186     try {
187       /*
188        * this will do a blocking get, but it will be blocking only on the thread that executed this
189        * method which should be one of the persistentWorker threads from the executor.
190        */
191       return task.get();
192     } catch (InterruptedException | ExecutionException exc) {
193       // TODO Auto-generated catch block
194       LOG.error(AaiUiMsgs.DISK_NAMED_DATA_READ_IO_ERROR, "txn", exc.getMessage());
195     }
196
197     return null;
198
199   }
200
201   /* (non-Javadoc)
202    * @see org.openecomp.sparky.dal.cache.EntityCache#put(java.lang.String, org.openecomp.sparky.dal.rest.OperationResult)
203    */
204   @Override
205   public void put(String key, OperationResult data) {
206
207     final String storagePath = generateOfflineStoragePathFromUri(data.getRequestLink());
208     createDirs(storagePath);
209     final String persistentFileName = storagePath + "\\" + key + ".json";
210
211     Path persistentFilePath = Paths.get(persistentFileName);
212
213     if (!Files.exists(persistentFilePath, LinkOption.NOFOLLOW_LINKS)) {
214
215       supplyAsync(new PersistOperationResultToDisk(persistentFileName, data, mapper, LOG),
216           persistentExecutor).whenComplete((opResult, error) -> {
217
218             if (error != null) {
219               LOG.error(AaiUiMsgs.DISK_DATA_WRITE_IO_ERROR, "entity", error.getMessage());
220             }
221
222           });
223     }
224
225   }
226
227   /**
228    * The main method.
229    *
230    * @param args the arguments
231    * @throws URISyntaxException the URI syntax exception
232    */
233   public static void main(String[] args) throws URISyntaxException {
234
235     OperationResult or = new OperationResult();
236     or.setResult("asdjashdkajsdhaksdj");
237     or.setResultCode(200);
238
239     String url1 = "https://aai-int1.dev.att.com:8443/aai/v8/search/nodes-query?"
240         + "search-node-type=tenant&filter=tenant-id:EXISTS";
241
242     or.setRequestLink(url1);
243
244     PersistentEntityCache pec = new PersistentEntityCache("e:\\my_special_folder", 5);
245     String k1 = NodeUtils.generateUniqueShaDigest(url1);
246     pec.put(k1, or);
247
248     String url2 =
249         "https://aai-int1.dev.att.com:8443/aai/v8/network/vnfcs/vnfc/trial-vnfc?nodes-only";
250     or.setRequestLink(url2);
251     String k2 = NodeUtils.generateUniqueShaDigest(url2);
252     pec.put(k2, or);
253
254     String url3 = "https://1.2.3.4:8443/aai/v8/network/vnfcs/vnfc/trial-vnfc?nodes-only";
255     or.setRequestLink(url3);
256     String k3 = NodeUtils.generateUniqueShaDigest(url3);
257     pec.put(k3, or);
258
259     pec.shutdown();
260
261     /*
262      * URI uri1 = new URI(url1);
263      * 
264      * System.out.println("schemea = " + uri1.getScheme()); System.out.println("host = " +
265      * uri1.getHost());
266      * 
267      * String host = uri1.getHost(); String[] tokens = host.split("\\.");
268      * System.out.println(Arrays.asList(tokens)); ArrayList<String> tokenList = new
269      * ArrayList(Arrays.asList(tokens)); //tokenList.remove(tokens.length-1); String
270      * hostAsPathElement = NodeUtils.concatArray(tokenList, "_");
271      * 
272      * System.out.println("hostAsPathElement = " + hostAsPathElement);
273      * 
274      * 
275      * System.out.println("port = " + uri1.getPort()); System.out.println("path = " +
276      * uri1.getPath()); System.out.println("query = " + uri1.getQuery()); System.out.println(
277      * "fragment = " + uri1.getFragment());
278      */
279
280
281   }
282
283   /* (non-Javadoc)
284    * @see org.openecomp.sparky.dal.cache.EntityCache#shutdown()
285    */
286   @Override
287   public void shutdown() {
288     if (persistentExecutor != null) {
289       persistentExecutor.shutdown();
290     }
291
292   }
293
294   /* (non-Javadoc)
295    * @see org.openecomp.sparky.dal.cache.EntityCache#clear()
296    */
297   @Override
298   public void clear() {
299     /*
300      * do nothing for this one, as it is not clear if we we really want to clear on the on-disk
301      * cache or not
302      */
303   }
304
305 }