9d44cd38b5117a29599b1241da8a5faeda05fcab
[policy/apex-pdp.git] / context / context-management / src / main / java / org / onap / policy / apex / context / impl / ContextAlbumImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019 Nordix Foundation.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.context.impl;
23
24 import java.util.AbstractMap.SimpleEntry;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashSet;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.onap.policy.apex.context.ContextAlbum;
32 import org.onap.policy.apex.context.ContextException;
33 import org.onap.policy.apex.context.ContextRuntimeException;
34 import org.onap.policy.apex.context.Distributor;
35 import org.onap.policy.apex.context.SchemaHelper;
36 import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
37 import org.onap.policy.apex.context.monitoring.ContextMonitor;
38 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
39 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
40 import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
41 import org.onap.policy.common.utils.validation.Assertions;
42 import org.slf4j.ext.XLogger;
43 import org.slf4j.ext.XLoggerFactory;
44
45 /**
46  * The Class ContextAlbumImpl implements the methods on the {@link ContextAlbum} interface. It implements the getters
47  * and setters on the {@link Map} and uses the {@link Distributor} to handle distribution and locking.
48  *
49  * @author Liam Fallon (liam.fallon@ericsson.com)
50  */
51 public final class ContextAlbumImpl implements ContextAlbum, Comparable<ContextAlbumImpl> {
52     // Logger for this class
53     private static final XLogger LOGGER = XLoggerFactory.getXLogger(ContextAlbumImpl.class);
54
55     // Recurring string constants
56     private static final String NULL_VALUES_ILLEGAL = "null values are illegal on method parameter \"key\"";
57     private static final String ALBUM = "album \"";
58
59     // The definition of this context album
60     private final AxContextAlbum albumDefinition;
61
62     /// The map holding the items and their values for this context album
63     private final Map<String, Object> albumMap;
64
65     // The artifact stack of the artifacts currently using the context album
66     private AxConcept[] userArtifactStack = null;
67
68     // The context distributor we are using
69     private final Distributor distributor;
70
71     // The schema helper that handles translations of Java objects for this album
72     private SchemaHelper schemaHelper;
73
74     // The context monitor for this context album
75     private ContextMonitor monitor = null;
76
77     /**
78      * Constructor, instantiate the context album.
79      *
80      * @param albumDefinition The model definition of this context album
81      * @param distributor The context distributor passed to us to distribute context across ContextAlbum instances
82      * @param albumMap the album map
83      * @throws ContextException on errors creating context albums
84      */
85     public ContextAlbumImpl(final AxContextAlbum albumDefinition, final Distributor distributor,
86             final Map<String, Object> albumMap) throws ContextException {
87         Assertions.argumentNotNull(albumDefinition, "Context album definition may not be null");
88         Assertions.argumentNotNull(distributor, "Distributor may not be null");
89         Assertions.argumentNotNull(albumMap, "Album map may not be null");
90
91         this.albumDefinition = albumDefinition;
92
93         // Use the context distributor passed to us
94         this.distributor = distributor;
95
96         // The map to use to store objects
97         this.albumMap = albumMap;
98
99         try {
100             // Get a schema helper to manage the translations between objects on the album map for this album
101             schemaHelper = new SchemaHelperFactory().createSchemaHelper(albumDefinition.getKey(),
102                     albumDefinition.getItemSchema());
103         } catch (final ContextRuntimeException e) {
104             final String resultString = "could not initiate schema management for context album " + albumDefinition;
105             LOGGER.warn(resultString, e);
106             throw new ContextException(resultString, e);
107         }
108
109         // Create the context monitor
110         monitor = new ContextMonitor();
111
112     }
113
114     /**
115      * {@inheritDoc}.
116      */
117     @Override
118     public AxArtifactKey getKey() {
119         return albumDefinition.getKey();
120     }
121
122     /**
123      * {@inheritDoc}.
124      */
125     @Override
126     public String getName() {
127         return albumDefinition.getKey().getName();
128     }
129
130     /**
131      * {@inheritDoc}.
132      */
133     @Override
134     public AxContextAlbum getAlbumDefinition() {
135         return albumDefinition;
136     }
137
138     /**
139      * {@inheritDoc}.
140      */
141     @Override
142     public SchemaHelper getSchemaHelper() {
143         return schemaHelper;
144     }
145
146     /**
147      * {@inheritDoc}.
148      */
149     @Override
150     public void lockForReading(final String keyOnAlbum) throws ContextException {
151         distributor.lockForReading(albumDefinition.getKey(), keyOnAlbum);
152         monitor.monitorReadLock(albumDefinition.getKey(), albumDefinition.getItemSchema(), keyOnAlbum,
153                 userArtifactStack);
154     }
155
156     /**
157      * {@inheritDoc}.
158      */
159     @Override
160     public void lockForWriting(final String keyOnAlbum) throws ContextException {
161         distributor.lockForWriting(albumDefinition.getKey(), keyOnAlbum);
162         monitor.monitorWriteLock(albumDefinition.getKey(), albumDefinition.getItemSchema(), keyOnAlbum,
163                 userArtifactStack);
164     }
165
166     /**
167      * {@inheritDoc}.
168      */
169     @Override
170     public void unlockForReading(final String keyOnAlbum) throws ContextException {
171         distributor.unlockForReading(albumDefinition.getKey(), keyOnAlbum);
172         monitor.monitorReadUnlock(albumDefinition.getKey(), albumDefinition.getItemSchema(), keyOnAlbum,
173                 userArtifactStack);
174     }
175
176     /**
177      * {@inheritDoc}.
178      */
179     @Override
180     public void unlockForWriting(final String keyOnAlbum) throws ContextException {
181         distributor.unlockForWriting(albumDefinition.getKey(), keyOnAlbum);
182         monitor.monitorWriteUnlock(albumDefinition.getKey(), albumDefinition.getItemSchema(), keyOnAlbum,
183                 userArtifactStack);
184     }
185
186     /**
187      * {@inheritDoc}.
188      */
189     @Override
190     public AxConcept[] getUserArtifactStack() {
191         return userArtifactStack;
192     }
193
194     /**
195      * {@inheritDoc}.
196      */
197     @Override
198     public void setUserArtifactStack(final AxConcept[] userArtifactStack) {
199         this.userArtifactStack = userArtifactStack;
200     }
201
202     /**
203      * {@inheritDoc}.
204      */
205     @Override
206     public void flush() throws ContextException {
207         distributor.flushContextAlbum(this);
208     }
209
210     /*
211      * The Map interface
212      */
213
214     /**
215      * {@inheritDoc}.
216      */
217     @Override
218     public int size() {
219         return albumMap.size();
220     }
221
222     /**
223      * {@inheritDoc}.
224      */
225     @Override
226     public boolean isEmpty() {
227         return albumMap.isEmpty();
228     }
229
230     /**
231      * {@inheritDoc}.
232      */
233     @Override
234     public boolean containsKey(final Object key) {
235         if (key == null) {
236             LOGGER.warn(NULL_VALUES_ILLEGAL);
237             throw new ContextRuntimeException(NULL_VALUES_ILLEGAL);
238         }
239
240         return albumMap.containsKey(key);
241     }
242
243     /**
244      * {@inheritDoc}.
245      */
246     @Override
247     public boolean containsValue(final Object value) {
248         if (value == null) {
249             LOGGER.warn("null values are illegal on method parameter \"value\"");
250             throw new ContextRuntimeException("null values are illegal on method parameter \"value\"");
251         }
252
253         return albumMap.containsValue(value);
254     }
255
256     /**
257      * {@inheritDoc}.
258      */
259     @Override
260     public Object get(final Object key) {
261         if (key == null) {
262             final String returnString =
263                     ALBUM + albumDefinition.getId() + "\" null keys are illegal on keys for get()";
264             LOGGER.warn(returnString);
265             throw new ContextRuntimeException(returnString);
266         }
267
268         final Object item = albumMap.get(key);
269         if (item == null) {
270             return null;
271         }
272
273         // Get the context value and monitor it
274         monitor.monitorGet(albumDefinition.getKey(), albumDefinition.getItemSchema(), key.toString(), item,
275                 userArtifactStack);
276         return item;
277     }
278
279     /**
280      * {@inheritDoc}.
281      */
282     @Override
283     public Set<String> keySet() {
284         return albumMap.keySet();
285     }
286
287     /**
288      * {@inheritDoc}.
289      */
290     @Override
291     public Collection<Object> values() {
292         // Build the key set and return it
293         final ArrayList<Object> valueList = new ArrayList<>();
294
295         for (final Entry<String, Object> contextAlbumEntry : albumMap.entrySet()) {
296             final Object item = contextAlbumEntry.getValue();
297             monitor.monitorGet(albumDefinition.getKey(), albumDefinition.getItemSchema(), contextAlbumEntry.getKey(),
298                     item, userArtifactStack);
299             valueList.add(contextAlbumEntry.getValue());
300         }
301
302         return valueList;
303     }
304
305     /**
306      * {@inheritDoc}.
307      */
308     @Override
309     public Set<Entry<String, Object>> entrySet() {
310         // Build the entry set and return it
311         final Set<Entry<String, Object>> entrySet = new HashSet<>();
312
313         for (final Entry<String, Object> contextAlbumEntry : albumMap.entrySet()) {
314             final Object item = contextAlbumEntry.getValue();
315             monitor.monitorGet(albumDefinition.getKey(), albumDefinition.getItemSchema(), contextAlbumEntry.getKey(),
316                     item, userArtifactStack);
317             entrySet.add(new SimpleEntry<>(contextAlbumEntry.getKey(), contextAlbumEntry.getValue()));
318         }
319
320         return entrySet;
321     }
322
323     /**
324      * {@inheritDoc}.
325      */
326     @Override
327     public Object put(final String key, final Object incomingValue) {
328         if (key == null) {
329             final String returnString =
330                     ALBUM + albumDefinition.getId() + "\" null keys are illegal on keys for put()";
331             LOGGER.warn(returnString);
332             throw new ContextRuntimeException(returnString);
333         }
334
335         if (incomingValue == null) {
336             final String returnString = ALBUM + albumDefinition.getId() + "\" null values are illegal on key \""
337                     + key + "\" for put()";
338             LOGGER.warn(returnString);
339             throw new ContextRuntimeException(returnString);
340         }
341
342         if (!albumDefinition.isWritable()) {
343             final String returnString = ALBUM + albumDefinition.getId()
344                     + "\" put() not allowed on read only albums for key=\"" + key + "\", value=\"" + incomingValue;
345             LOGGER.warn(returnString);
346             throw new ContextRuntimeException(returnString);
347         }
348
349         try {
350             // Translate the object to a schema object
351             final Object valueToPut = schemaHelper.unmarshal(incomingValue);
352
353             // Check if the key is already in the map
354             if (albumMap.containsKey(key)) {
355                 // Update the value in the context item and in the context value map
356                 monitor.monitorSet(albumDefinition.getKey(), albumDefinition.getItemSchema(), key, incomingValue,
357                         userArtifactStack);
358             } else {
359                 // Update the value in the context item and in the context value map
360                 monitor.monitorInit(albumDefinition.getKey(), albumDefinition.getItemSchema(), key, incomingValue,
361                         userArtifactStack);
362             }
363
364             // Put the translated value on the map and return the old map value
365             return albumMap.put(key, valueToPut);
366         } catch (final ContextRuntimeException e) {
367             final String returnString = "Failed to set context value for key \"" + key + "\" in album \""
368                     + albumDefinition.getId() + "\": " + e.getMessage();
369             LOGGER.warn(returnString);
370             throw new ContextRuntimeException(returnString, e);
371         }
372     }
373
374     /**
375      * {@inheritDoc}.
376      */
377     @Override
378     public void putAll(final Map<? extends String, ? extends Object> incomingContextAlbum) {
379         if (!albumDefinition.isWritable()) {
380             final String returnString =
381                     ALBUM + albumDefinition.getId() + "\" putAll() not allowed on read only albums";
382             LOGGER.warn(returnString);
383             throw new ContextRuntimeException(returnString);
384         }
385
386         // Sanity check on incoming context
387         Assertions.argumentOfClassNotNull(incomingContextAlbum, ContextRuntimeException.class,
388                 "cannot update context, context album is null");
389
390         // Iterate over the incoming context
391         for (final Entry<String, Object> entry : albumMap.entrySet()) {
392             synchronized (albumDefinition) {
393                 // Get the key for the incoming name
394                 final Object incomingDataItem = incomingContextAlbum.get(entry.getKey());
395                 if (incomingDataItem != null) {
396                     // Update the value the context album
397                     put(entry.getKey(), incomingDataItem);
398                 }
399             }
400         }
401
402         // Put all the objects on the context album
403         for (final Entry<? extends String, ? extends Object> incomingMapEntry : incomingContextAlbum.entrySet()) {
404             // Put the entry on the map
405             this.put(incomingMapEntry.getKey(), incomingMapEntry.getValue());
406         }
407     }
408
409     /**
410      * {@inheritDoc}.
411      */
412     @Override
413     public Object remove(final Object key) {
414         if (!albumDefinition.isWritable()) {
415             final String returnString = ALBUM + albumDefinition.getId()
416                     + "\" remove() not allowed on read only albums for key=\"" + key + "\"";
417             LOGGER.warn(returnString);
418             throw new ContextRuntimeException(returnString);
419         }
420
421         if (key == null) {
422             LOGGER.warn(NULL_VALUES_ILLEGAL);
423             throw new ContextRuntimeException("null values are illegal on method parameter \"keyID\"");
424         }
425
426         // Delete the item
427         final Object removedValue = albumMap.remove(key);
428         monitor.monitorDelete(albumDefinition.getKey(), albumDefinition.getItemSchema(), key.toString(), removedValue,
429                 userArtifactStack);
430
431         // Return the value of the deleted item
432         return removedValue;
433     }
434
435     /**
436      * {@inheritDoc}.
437      */
438     @Override
439     public void clear() {
440         if (!albumDefinition.isWritable()) {
441             final String returnString =
442                     ALBUM + albumDefinition.getId() + "\" clear() not allowed on read only albums";
443             LOGGER.warn(returnString);
444             throw new ContextRuntimeException(returnString);
445         }
446
447         // Monitor deletion of each item
448         for (final Entry<String, Object> contextAlbumEntry : albumMap.entrySet()) {
449             final Object item = contextAlbumEntry.getValue();
450             monitor.monitorDelete(albumDefinition.getKey(), albumDefinition.getItemSchema(), contextAlbumEntry.getKey(),
451                     item, userArtifactStack);
452         }
453
454         // Clear the map
455         albumMap.clear();
456     }
457
458     /**
459      * {@inheritDoc}.
460      */
461     @Override
462     public int compareTo(ContextAlbumImpl otherContextAlbumImpl) {
463         return (equals(otherContextAlbumImpl) ? 0 : 1);
464     }
465
466     /**
467      * {@inheritDoc}.
468      */
469     @Override
470     public int hashCode() {
471         final int prime = 31;
472         int result = 1;
473         result = prime * result + albumDefinition.hashCode();
474         result = prime * result + albumMap.hashCode();
475         return result;
476     }
477
478     /**
479      * {@inheritDoc}.
480      */
481     @Override
482     public boolean equals(Object obj) {
483         if (this == obj) {
484             return true;
485         }
486         if (obj == null) {
487             return false;
488         }
489         if (!(obj instanceof ContextAlbumImpl)) {
490             return false;
491         }
492         ContextAlbumImpl other = (ContextAlbumImpl) obj;
493         if (!albumDefinition.equals(other.albumDefinition)) {
494             return false;
495         }
496         return albumMap.equals(other.albumMap);
497     }
498 }