71cddb8b4bc71fdc9ce57dabe1070286fcf3b62b
[sdnc/core.git] / sliPluginUtils / provider / src / main / java / org / openecomp / sdnc / sli / SliPluginUtils / SliPluginUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : SDN-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                         reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.openecomp.sdnc.sli.SliPluginUtils;
23
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Properties;
30 import java.util.UUID;
31
32 import org.apache.commons.lang3.StringUtils;
33 import org.openecomp.sdnc.sli.SvcLogicContext;
34 import org.openecomp.sdnc.sli.SvcLogicException;
35 import org.openecomp.sdnc.sli.SvcLogicJavaPlugin;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * A utility class used to streamline the interface between Java plugins,
41  * the Service Logic Context, and Directed Graphs.
42  * @version 7.0.1
43  * @see org.openecomp.sdnc.sli.SvcLogicContext
44  */
45 public class SliPluginUtils implements SvcLogicJavaPlugin {
46     public enum LogLevel {
47         TRACE, DEBUG, INFO, WARN, ERROR;
48     }
49
50     private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils.class);
51
52
53     // ========== CONSTRUCTORS ==========
54
55     public SliPluginUtils() {}
56
57     public SliPluginUtils( Properties props ) {}
58
59
60
61     // ========== CONTEXT MEMORY FUNCTIONS ==========
62
63     /**
64      * Removes 1 or more elements from a list in context memory.
65      * <p>
66      * Values are removed based on either the index in the list, a key-value
67      * pair, or a list of key-value pairs that all must match in the element.
68      * @param parameters
69      * @param ctx Reference to context memory
70      * @throws SvcLogicException All exceptions are wrapped in
71      * SvcLogicException for compatibility with SLI.
72      * @since 7.0.1
73      */
74     public void ctxListRemove( Map<String,String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
75         try{
76             LOG.debug( "ENTERING Execute Node \"ctxListRemove\"" );
77
78             // Validate, Log, & read parameters
79             checkParameters(parameters, new String[]{"list_pfx"}, LOG);
80             logExecuteNodeParameters(parameters, LOG, LogLevel.DEBUG);
81             String list_pfx = parameters.get("list_pfx");
82             String param_index = parameters.get("index");
83             String param_key = parameters.get("key");
84             String param_value = parameters.get("value");
85             String param_keys_length = parameters.get("keys_length");
86
87             // Initialize context memory list mimic
88             SvcLogicContextList list;
89
90             // Process based on input parameters:
91             //   index: remove object at specific index
92             //   key & value: remove all objects with key-value pair
93             //   keys_length: remove all objects that match all key-value pairs
94             //                in list
95             if( param_index != null ) {
96                 // Parse index
97                 LOG.trace("executing remove by index logic");
98                 int index;
99                 try {
100                     index = Integer.parseInt(param_index);
101                 }
102                 catch( NumberFormatException e ) {
103                     throw new IllegalArgumentException("\"index\" parameter is not a number. index = " + param_index, e);
104                 }
105
106                 // Extract list from context memory & remove object @ index
107                 LOG.trace("extracting list from context memory");
108                 list = SvcLogicContextList.extract(ctx, list_pfx);
109                 LOG.trace("removing elements from list");
110                 list.remove(index);
111             }
112             else if( param_value != null ) {
113                 if( param_key == null ) { param_key = ""; }
114
115                 // Extract list from context memory & remove objects with
116                 // key-value pair
117                 LOG.trace("executing remove by key-value pair logic");
118                 LOG.trace("extracting list from context memory");
119                 list = SvcLogicContextList.extract(ctx, list_pfx);
120                 LOG.trace("removing elements from list");
121                 list.remove( param_key, param_value );
122             }
123             else if( param_keys_length != null ) {
124                 // Parse keys_length
125                 LOG.trace("executing remove by key-value pair list logic");
126                 int keys_length;
127                 try {
128                     keys_length = Integer.parseInt(param_keys_length);
129                 }
130                 catch( NumberFormatException e ) {
131                     throw new IllegalArgumentException("\"keys_length\" parameters is not a number. keys_length = " + param_keys_length, e);
132                 }
133
134                 // Obtain key-value pairs to check from parameters
135                 LOG.trace("reading keys parameter list");
136                 HashMap<String,String> keys_values = new HashMap<>();
137                 for( int i = 0; i < keys_length; i++ ) {
138                     keys_values.put(parameters.get("keys[" + i + "].key"), parameters.get("keys[" + i + "].value"));
139                 }
140
141                 // Extract list from context memory & remove objects with all
142                 // key-value pairs matching
143                 LOG.trace("extracting list from context memory");
144                 list = SvcLogicContextList.extract(ctx, list_pfx);
145                 LOG.trace("removing elements from list");
146                 list.remove(keys_values);
147             }
148             else {
149                 throw new IllegalArgumentException("Required parameters missing. Requires one of: index, key & value, or keys_length array");
150             }
151
152             // Remove index from list
153             LOG.trace("writing list back into context memory");
154             list.writeToContext(ctx);
155         }
156         catch( Exception e ) {
157             throw new SvcLogicException( "An error occurred in the ctxListRemove Execute node", e );
158         }
159         finally {
160             LOG.debug( "EXITING Execute Node \"ctxListRemove\"" );
161         }
162     }
163
164     // TODO: javadoc
165     public void ctxSortList( Map<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
166         checkParameters(parameters, new String[]{"list","delimiter"}, LOG);
167         ArrayList<SortableCtxListElement> list = new ArrayList<>();
168
169         String[] sort_fields = null;
170         if( parameters.containsKey("sort-fields") ) {
171             sort_fields = parameters.get("sort-fields").split(parameters.get("delimiter"), 0);
172         }
173
174         String ctx_list_str = parameters.get("list");
175         int listSz = getArrayLength(ctx, ctx_list_str);
176
177
178
179         for( int i = 0; i < listSz; i++ ) {
180             list.add( new SortableCtxListElement(ctx, ctx_list_str + '[' + i + ']', sort_fields) );
181         }
182         Collections.sort(list);
183
184         ctxBulkErase(ctx, ctx_list_str);
185         int i = 0;
186         for( SortableCtxListElement list_element : list ) {
187             for( Map.Entry<String,String> entry : list_element.child_elements.entrySet() ) {
188                 if( sort_fields == null ) {
189                     ctx.setAttribute(ctx_list_str + '[' + i + ']', entry.getValue());
190                 }
191                 else {
192                     ctx.setAttribute(ctx_list_str + '[' + i + "]." + entry.getKey(), entry.getValue());
193                 }
194             }
195             i++;
196         }
197         // Reset list length (removed by ctxBulkErase above)
198         ctx.setAttribute(ctx_list_str+"_length",  ""+listSz);
199     }
200
201     // TODO: javadoc
202     public void generateUUID( Map<String, String> parameters, SvcLogicContext ctx )  throws SvcLogicException {
203         checkParameters(parameters, new String[]{"ctx-destination"}, LOG);
204         ctx.setAttribute(parameters.get("ctx-destination"), UUID.randomUUID().toString() );
205     }
206
207     /**
208      * Provides substring functionality to Directed Graphs.
209      * <p>
210      * Calls either String.substring(String beginIndex) or
211      * String.substring(String beginInded, String endIndex) if the end-index
212      * is present or not.
213      * @param parameters HashMap<String,String> of parameters passed by the DG to this function
214      * <table border="1">
215      *     <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead>
216      *     <tbody>
217      *         <tr><td>string</td><td>Mandatory</td><td>String to perform substring on</td></tr>
218      *         <tr><td>result</td><td>Mandatory</td><td>Key in context memory to populate the resulting string in</td></tr>
219      *         <tr><td>begin-index</td><td>Mandatory</td><td>Beginning index to pass to Java substring function</td></tr>
220      *         <tr><td>end-index</td><td>Optional</td><td>Ending index to pass to Java substring function. If not included, String.substring(begin) will be called.</td></tr>
221      *     </tbody>
222      * </table>
223      * @param ctx Reference to context memory
224      * @throws SvcLogicException
225      * @since 8.0.1
226      */
227     public void substring( Map<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
228         try {
229             checkParameters( parameters, new String[]{"string","begin-index","result"}, LOG );
230             final String string = parameters.get("string");
231             final String result = parameters.get("result");
232             final String begin = parameters.get("begin-index");
233             final String end = parameters.get("end-index");
234
235             if( StringUtils.isEmpty(end) ) {
236                 ctx.setAttribute( result, string.substring(Integer.parseInt(begin)) );
237             }
238             else {
239                 ctx.setAttribute( result, string.substring(Integer.parseInt(begin), Integer.parseInt(end)) );
240             }
241         }
242         catch( Exception e ) {
243             throw new SvcLogicException( "An error occurred while the Directed Graph was performing a substring", e );
244         }
245     }
246
247
248
249     // ========== PUBLIC STATIC UTILITY FUNCTIONS ==========
250
251     /**
252      * Throws an exception and writes an error to the log file if a required
253      * parameters is not found in the parametersMap.
254      * <p>
255      * Use at the beginning of functions that can be called by Directed Graphs
256      * and can take parameters to verify that all parameters have been provided
257      * by the Directed Graph.
258      * @param parametersMap parameters Map passed to this node
259      * @param requiredParams Array of parameters required by the calling function
260      * @param log Reference to Logger to log to
261      * @throws SvcLogicException if a String in the requiredParams array is
262      * not a key in parametersMap.
263      * @since 1.0
264      */
265     public static final void checkParameters( Map<String,String> parametersMap, String[] requiredParams, Logger log ) throws SvcLogicException {
266         for( String param : requiredParams ) {
267             if( !parametersMap.containsKey(param) ) {
268                 log.error("Required parameter \"" + param + "\" was not found in parameter list.");
269                 throw new SvcLogicException("Required parameter \"" + param + "\" was not found in parameter list");
270             }
271         }
272     }
273
274     /**
275      * Removes all key-value pairs with keys that begin with pfx
276      * @param ctx Reference to context memory
277      * @param pfx Prefix of key-value pairs to remove
278      * @since 1.0
279      */
280     public static final void ctxBulkErase( SvcLogicContext ctx, String pfx ) {
281         ArrayList<String> Keys = new ArrayList<>( ctx.getAttributeKeySet() );
282         for( String key : Keys ) {
283             if( key.startsWith( pfx ) ) {
284                 ctx.setAttribute( pfx + key.substring(pfx.length()) , null);
285             }
286         }
287     }
288
289     /**
290      * Copies all context memory key-value pairs that start with src_pfx to
291      * the keys that start with dest_pfx + suffix, where suffix is the result
292      * of {@code key.substring(src_pfx.length())}.
293      * <p>
294      * Does NOT guarantee removal of all keys at the destination before
295      * copying, but will overwrite any destination keys that have a
296      * corresponding source key. Use {@link #ctxBulkErase(SvcLogicContext, String) ctxBulkErase}
297      * before copy to erase destination root before copying from source.
298      * @param ctx Reference to context memory.
299      * @param src_pfx Prefix of the keys to copy values from.
300      * @param dest_pfx Prefix of the keys to copy values to.
301      * @since 1.0
302      */
303     public static final void ctxBulkCopy( SvcLogicContext ctx, String src_pfx, String dest_pfx ) {
304         // Remove trailing period from dest_pfx
305         if( dest_pfx.charAt(dest_pfx.length()-1) == '.' ) {
306             dest_pfx = dest_pfx.substring(0,dest_pfx.length()-1);
307         }
308
309         // For each context key that begins with src_pfx, set the value of the
310         // key dest_pfx + the suffix of the key to the key's value
311         ArrayList<String> Keys = new ArrayList<>(ctx.getAttributeKeySet());
312         for( String key : Keys ) {
313             if( key.startsWith(src_pfx) ) {
314                 // Get suffix (no leading period)
315                 String suffix = key.substring(src_pfx.length());
316                 if( suffix.charAt(0) == '.') {
317                     suffix = suffix.substring(1);
318                 }
319
320                 // Set destination's value to key's value
321                 ctx.setAttribute(dest_pfx + '.' + suffix, ctx.getAttribute(key));
322             }
323         }
324     }
325
326     /**
327      * Creates and returns a {@code Map<String, String>} that is a subset of
328      * context memory where all keys begin with the prefix.
329      * @param ctx Reference to context memory.
330      * @param prefix Returned map's keys should all begin with this value.
331      * @return A {@code Map<String, String>} containing all the key-value pairs
332      * in ctx whose key begins with prefix.
333      */
334     public static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) {
335         Map<String, String> prefixMap = new HashMap<>();
336
337         for( String key : ctx.getAttributeKeySet() ) {
338             if( key.startsWith(prefix) ) {
339                 prefixMap.put( key, ctx.getAttribute(key) );
340             }
341         }
342
343         return prefixMap;
344     }
345
346     /**
347      * Returns true if key's value in context memory is "" or if it doesn't
348      * exist in context memory.
349      * @param ctx Reference to context memory.
350      * @param key Key to search for.
351      * @return true if key's value in context memory is "" or if it doesn't
352      * exist in context memory.
353      * @since 1.0
354      */
355     public static final boolean ctxKeyEmpty( SvcLogicContext ctx, String key ) {
356         String value = ctx.getAttribute(key);
357         return value == null || value.isEmpty();
358     }
359
360     /**
361      * Adds all key-value pairs in the entries Map to context memory.
362      * @param ctx Reference to context memory. Value's {@code toString()}
363      * function is used to add it.
364      * @param entries {@code Map<String, ?>} of key-value pairs to add to
365      * context memory. Value's {@code toString()} function is used to add it.
366      * @return Reference to context memory to be used for function chaining.
367      */
368     public static final SvcLogicContext ctxPutAll( SvcLogicContext ctx, Map<String, ?> entries ) {
369         for( Map.Entry<String, ?> entry : entries.entrySet() ) {
370             ctxSetAttribute( ctx, entry.getKey(), entry.getValue() );
371             //ctx.setAttribute(entry.getKey(), entry.getValue().toString());
372         }
373
374         return ctx;
375     }
376
377     /**
378      * Sets a key in context memory to the output of object's toString(). The
379      * key is deleted from context memory if object is null.
380      * @param ctx Reference to context memory.
381      * @param key Key to set.
382      * @param object Object whose toString() will be the value set
383      */
384     public static final void ctxSetAttribute( SvcLogicContext ctx, String key, Object object ) {
385         if( object == null ) {
386             ctx.setAttribute(key, null);
387         }
388         else {
389             ctx.setAttribute(key, object.toString());
390         }
391     }
392
393     /**
394      * Sets a key in context memory to the output of object's toString().
395      * <p>
396      * The key is deleted from context memory if object is null. The key and
397      * value set in context memory are logged to the Logger at the provided
398      * logLevel level.
399      * @param <O> Any Java object
400      * @param ctx Reference to context memory.
401      * @param key Key to set.
402      * @param obj Object whose toString() will be the value set
403      * @param LOG Logger to log to
404      * @param logLevel level to log at in Logger
405      */
406     public static final <O extends Object> void ctxSetAttribute( SvcLogicContext ctx, String key, O obj, Logger LOG, LogLevel logLevel ) {
407         String value = Objects.toString( obj, null );
408         ctx.setAttribute( key, value );
409         if( logLevelIsEnabled(LOG, logLevel ) ) {
410             if( value == null ) {
411                 logMessageAtLevel( LOG, logLevel, "Deleting " + key );
412             }
413             else {
414                 logMessageAtLevel( LOG, logLevel, "Setting " + key + " = " + value );
415             }
416         }
417     }
418
419     /**
420      * Utility function used to get an array's length from context memory.
421      * Will return 0 if key doesn't exist in context memory or isn't numeric.
422      * <p>
423      * Use to obtain a context memory array length without having to worry
424      * about throwing a NumberFormatException.
425      * @param ctx Reference to context memory
426      * @param key Key in context memory whose value is the array's length. If
427      * the key doesn't end in "_length", then "_length is appended.
428      * @param log Reference to Logger to log to
429      * @return The array length or 0 if the key is not found in context memory.
430      * @since 1.0
431      */
432     public static final int getArrayLength( SvcLogicContext ctx, String key ) {
433         return getArrayLength(ctx, key, null, null, null);
434     }
435
436     /**
437      * Utility function used to get an array's length from context memory.
438      * Will return 0 if key doesn't exist in context memory or isn't numeric
439      * and print the provided log message to the configured log file.
440      * <p>
441      * Use to obtain a context memory array length without having to worry
442      * about throwing a NumberFormatException.
443      * @param ctx Reference to context memory.
444      * @param key Key in context memory whose value is the array's length. If
445      * the key doesn't end in "_length", then "_length is appended.
446      * @param log Reference to Logger to log to. Doesn't log if null.
447      * @param logLevel Logging level to log the message at if the context
448      * memory key isn't found. Doesn't log if null.
449      * @param log_message Message to log if the context memory key isn't found.
450      * Doesn't log if null.
451      * @return The array length or 0 if the key is not found in context memory.
452      * @since 1.0
453      */
454     public static final int getArrayLength( SvcLogicContext ctx, String key, Logger log, LogLevel logLevel, String log_message ) {
455         String ctxKey = key.endsWith("_length") ? key : key + "_length";
456         try {
457             return Integer.parseInt(ctx.getAttribute(ctxKey));
458         }
459         catch( NumberFormatException e ) {
460             if( log != null && logLevel != null && log_message != null ) {
461                 switch( logLevel ) {
462                     case TRACE:
463                         log.trace(log_message);
464                     case DEBUG:
465                         log.debug(log_message);
466                         break;
467                     case INFO:
468                         log.info(log_message);
469                         break;
470                     case WARN:
471                         log.warn(log_message);
472                         break;
473                     case ERROR:
474                         log.error(log_message);
475                         break;
476                 }
477             }
478         }
479
480         return 0;
481     }
482
483     /**
484      * Prints sorted context memory key-value pairs to the log file at the log
485      * level. Returns immediately if the log level isn't enabled.
486      * <p>
487      * O(n log(n)) time where n = size of context memory
488      * @param ctx Reference to context memory
489      * @param log Reference to Logger to log to
490      * @param logLevel Logging level to log the context memory key-value pairs
491      * at.
492      * @since 1.0
493      */
494     public static final void logContextMemory( SvcLogicContext ctx, Logger log, LogLevel logLevel ) {
495         logLevelIsEnabled( log, logLevel );
496
497         // Print sorted context memory key-value pairs to the log
498         ArrayList<String> keys = new ArrayList<>(ctx.getAttributeKeySet());
499         Collections.sort(keys);
500         for( String key : keys ) {
501             logMessageAtLevel( log, logLevel, key + " = " + ctx.getAttribute(key) );
502         }
503     }
504
505
506
507     // ========== PRIVATE FUNCTIONS ==========
508
509     // TODO: javadoc
510     /**
511      *
512      * @param parameters
513      * @param log
514      * @param loglevel
515      * @since 7.0.1
516      */
517     public static final void logExecuteNodeParameters( Map<String,String> parameters, Logger log, LogLevel loglevel ) {
518         logLevelIsEnabled( log, loglevel );
519
520         for( Map.Entry<String,String> param : parameters.entrySet() ) {
521             logMessageAtLevel( log, loglevel, "PARAM: " + param.getKey() + " = " + param.getValue() );
522         }
523     }
524
525     // TODO: javadoc
526     /**
527      * Returns true if the loglevel is enabled. Otherwise, returns false.
528      * @param log Reference to logger
529      * @param loglevel Log level to check if enabled
530      * @return True if the loglevel is enabled. Otherwise, false
531      * @since 7.0.1
532      */
533     private static final boolean logLevelIsEnabled( Logger log, LogLevel loglevel ) {
534         // Return immediately if logging level isn't enabled
535         switch( loglevel ) {
536             case TRACE:
537                 if( log.isTraceEnabled() ) { return true; }
538                 return false;
539             case DEBUG:
540                 if( log.isDebugEnabled() ) { return true; }
541                 return false;
542             case INFO:
543                 if( log.isInfoEnabled() ) { return true; }
544                 return false;
545             case WARN:
546                 if( log.isWarnEnabled() ) { return true; }
547                 return false;
548             case ERROR:
549                 if( log.isErrorEnabled() ) { return true; }
550                 return false;
551             default:
552                 throw new IllegalArgumentException("Unknown LogLevel: " + loglevel.toString());
553         }
554     }
555
556     // TODO: javadoc
557     /**
558      *
559      * @param log
560      * @param loglevel
561      * @param msg
562      * @since 7.0.1
563      */
564     private static final void logMessageAtLevel( Logger log, LogLevel loglevel, String msg ) {
565         switch( loglevel ) {
566             case TRACE:
567                 log.trace(msg);
568                 return;
569             case DEBUG:
570                 log.debug(msg);
571                 return;
572             case INFO:
573                 log.info(msg);
574                 return;
575             case WARN:
576                 log.warn(msg);
577                 return;
578             case ERROR:
579                 log.error(msg);
580                 return;
581         }
582     }
583
584
585
586     // ========== LOCAL CLASSES ==========
587
588     private class SortableCtxListElement implements Comparable<SortableCtxListElement> {
589         HashMap<String,String> child_elements = new HashMap<>();
590         String[] sort_fields;
591
592         public SortableCtxListElement( SvcLogicContext ctx, String root, String[] sort_fields ) {
593             this.sort_fields = sort_fields;
594
595             for( String key : ctx.getAttributeKeySet() ) {
596                 if( key.startsWith(root) ) {
597                     if( key.length() == root.length() ) {
598                         child_elements.put("", ctx.getAttribute(key));
599                         break;
600                     }
601                     else {
602                         child_elements.put(key.substring(root.length()+1), ctx.getAttribute(key));
603                     }
604                 }
605             }
606         }
607
608         @Override
609         public int compareTo(SortableCtxListElement arg0) {
610             if( sort_fields == null ) {
611                 return this.child_elements.get("").compareTo(arg0.child_elements.get(""));
612             }
613
614             for( String field : this.sort_fields ) {
615                 int result = this.child_elements.get(field).compareTo(arg0.child_elements.get(field));
616                 if( result != 0 ) {
617                     return result;
618                 }
619             }
620
621             return 0;
622         }
623     }
624 }