2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 ONAP
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.sdnc.sli.SliPluginUtils;
24 import java.io.FileOutputStream;
25 import java.io.PrintStream;
26 import java.text.SimpleDateFormat;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
32 import java.util.Objects;
33 import java.util.Properties;
35 import java.util.UUID;
37 import org.apache.commons.lang3.StringUtils;
38 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
39 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
40 import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * A utility class used to streamline the interface between Java plugins,
46 * the Service Logic Context, and Directed Graphs.
48 * @see org.onap.ccsdk.sli.core.sli.SvcLogicContext
50 public class SliPluginUtils implements SvcLogicJavaPlugin {
51 public enum LogLevel {
52 TRACE, DEBUG, INFO, WARN, ERROR;
55 private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils.class);
58 // ========== CONSTRUCTORS ==========
60 public SliPluginUtils() {}
62 public SliPluginUtils( Properties props ) {}
66 // ========== CONTEXT MEMORY FUNCTIONS ==========
69 * Removes 1 or more elements from a list in context memory.
71 * Values are removed based on either the index in the list, a key-value
72 * pair, or a list of key-value pairs that all must match in the element.
74 * @param ctx Reference to context memory
75 * @throws SvcLogicException All exceptions are wrapped in
76 * SvcLogicException for compatibility with SLI.
79 public void ctxListRemove( Map<String,String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
81 LOG.debug( "ENTERING Execute Node \"ctxListRemove\"" );
83 // Validate, Log, & read parameters
84 checkParameters(parameters, new String[]{"list_pfx"}, LOG);
85 logExecuteNodeParameters(parameters, LOG, LogLevel.DEBUG);
86 String list_pfx = parameters.get("list_pfx");
87 String param_index = parameters.get("index");
88 String param_key = parameters.get("key");
89 String param_value = parameters.get("value");
90 String param_keys_length = parameters.get("keys_length");
92 // Initialize context memory list mimic
93 SvcLogicContextList list;
95 // Process based on input parameters:
96 // index: remove object at specific index
97 // key & value: remove all objects with key-value pair
98 // keys_length: remove all objects that match all key-value pairs
100 if( param_index != null ) {
102 LOG.trace("executing remove by index logic");
105 index = Integer.parseInt(param_index);
107 catch( NumberFormatException e ) {
108 throw new IllegalArgumentException("\"index\" parameter is not a number. index = " + param_index, e);
111 // Extract list from context memory & remove object @ index
112 LOG.trace("extracting list from context memory");
113 list = SvcLogicContextList.extract(ctx, list_pfx);
114 LOG.trace("removing elements from list");
117 else if( param_value != null ) {
118 if( param_key == null ) { param_key = ""; }
120 // Extract list from context memory & remove objects with
122 LOG.trace("executing remove by key-value pair logic");
123 LOG.trace("extracting list from context memory");
124 list = SvcLogicContextList.extract(ctx, list_pfx);
125 LOG.trace("removing elements from list");
126 list.remove( param_key, param_value );
128 else if( param_keys_length != null ) {
130 LOG.trace("executing remove by key-value pair list logic");
133 keys_length = Integer.parseInt(param_keys_length);
135 catch( NumberFormatException e ) {
136 throw new IllegalArgumentException("\"keys_length\" parameters is not a number. keys_length = " + param_keys_length, e);
139 // Obtain key-value pairs to check from parameters
140 LOG.trace("reading keys parameter list");
141 HashMap<String,String> keys_values = new HashMap<String,String>();
142 for( int i = 0; i < keys_length; i++ ) {
143 keys_values.put(parameters.get("keys[" + i + "].key"), parameters.get("keys[" + i + "].value"));
146 // Extract list from context memory & remove objects with all
147 // key-value pairs matching
148 LOG.trace("extracting list from context memory");
149 list = SvcLogicContextList.extract(ctx, list_pfx);
150 LOG.trace("removing elements from list");
151 list.remove(keys_values);
154 throw new IllegalArgumentException("Required parameters missing. Requires one of: index, key & value, or keys_length array");
157 // Remove index from list
158 LOG.trace("writing list back into context memory");
159 list.writeToContext(ctx);
161 catch( Exception e ) {
162 throw new SvcLogicException( "An error occurred in the ctxListRemove Execute node", e );
165 LOG.debug( "EXITING Execute Node \"ctxListRemove\"" );
171 * @param parameters - the set of required parameters must contain list and delimiter.
172 * @param ctx Reference to context memory
173 * @throws SvcLogicException if a required parameter is missing an exception is thrown
175 public void ctxSortList( Map<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
176 checkParameters(parameters, new String[]{"list","delimiter"}, LOG);
177 ArrayList<SortableCtxListElement> list = new ArrayList<SortableCtxListElement>();
179 String[] sort_fields = null;
180 if( parameters.containsKey("sort-fields") ) {
181 sort_fields = parameters.get("sort-fields").split(parameters.get("delimiter"), 0);
184 String ctx_list_str = parameters.get("list");
185 int listSz = getArrayLength(ctx, ctx_list_str);
189 for( int i = 0; i < listSz; i++ ) {
190 list.add( new SortableCtxListElement(ctx, ctx_list_str + '[' + i + ']', sort_fields) );
192 Collections.sort(list);
194 ctxBulkErase(ctx, ctx_list_str);
196 for( SortableCtxListElement list_element : list ) {
197 for( Map.Entry<String,String> entry : list_element.child_elements.entrySet() ) {
198 if( sort_fields == null ) {
199 ctx.setAttribute(ctx_list_str + '[' + i + ']', entry.getValue());
202 ctx.setAttribute(ctx_list_str + '[' + i + "]." + entry.getKey(), entry.getValue());
207 // Reset list length (removed by ctxBulkErase above)
208 ctx.setAttribute(ctx_list_str+"_length", ""+listSz);
212 * generates a UUID and writes it to context memory
213 * @param parameters - ctx-destination is a required parameter
214 * @param ctx Reference to context memory
215 * @throws SvcLogicException thrown if a UUID cannot be generated or if ctx-destination is missing or null
217 public void generateUUID( Map<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
218 checkParameters(parameters, new String[]{"ctx-destination"}, LOG);
219 ctx.setAttribute(parameters.get("ctx-destination"), UUID.randomUUID().toString() );
223 * Provides substring functionality to Directed Graphs.
225 * Calls either String.substring(String beginIndex) or
226 * String.substring(String beginInded, String endIndex) if the end-index
228 * @param parameters HashMap<String,String> of parameters passed by the DG to this function
230 * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead>
232 * <tr><td>string</td><td>Mandatory</td><td>String to perform substring on</td></tr>
233 * <tr><td>result</td><td>Mandatory</td><td>Key in context memory to populate the resulting string in</td></tr>
234 * <tr><td>begin-index</td><td>Mandatory</td><td>Beginning index to pass to Java substring function</td></tr>
235 * <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>
238 * @param ctx Reference to context memory
239 * @throws SvcLogicException
241 * @see SliPluginUtils#substring(Map, SvcLogicContext)
244 public void substring( Map<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
246 checkParameters( parameters, new String[]{"string","begin-index","result"}, LOG );
247 final String string = parameters.get("string");
248 final String result = parameters.get("result");
249 final String begin = parameters.get("begin-index");
250 final String end = parameters.get("end-index");
252 if( StringUtils.isEmpty(end) ) {
253 ctx.setAttribute( result, string.substring(Integer.parseInt(begin)) );
256 ctx.setAttribute( result, string.substring(Integer.parseInt(begin), Integer.parseInt(end)) );
259 catch( Exception e ) {
260 throw new SvcLogicException( "An error occurred while the Directed Graph was performing a substring", e );
266 // ========== PUBLIC STATIC UTILITY FUNCTIONS ==========
269 * Throws an exception and writes an error to the log file if a required
270 * parameters is not found in the parametersMap.
272 * Use at the beginning of functions that can be called by Directed Graphs
273 * and can take parameters to verify that all parameters have been provided
274 * by the Directed Graph.
275 * @param parametersMap parameters Map passed to this node
276 * @param requiredParams Array of parameters required by the calling function
277 * @param log Reference to Logger to log to
278 * @throws SvcLogicException if a String in the requiredParams array is
279 * not a key in parametersMap.
282 public static final void checkParameters(Map<String, String> parametersMap, String[] requiredParams, Logger log) throws SvcLogicException {
283 if( requiredParams == null || requiredParams.length < 1){
284 log.debug("required parameters was empty, exiting early.");
287 if (parametersMap == null || parametersMap.keySet().size() < 1){
288 String errorMessage = "This method requires the parameters [" + StringUtils.join(requiredParams,",") + "], but no parameters were passed in.";
289 log.error(errorMessage);
290 throw new SvcLogicException(errorMessage);
293 for (String param : requiredParams) {
294 if (!parametersMap.containsKey(param)) {
295 String errorMessage = "Required parameter \"" + param + "\" was not found in parameter list.";
296 log.error(errorMessage);
297 log.error("Total list of required parameters is [" + StringUtils.join(requiredParams, ",") + "].");
298 throw new SvcLogicException(errorMessage);
304 * Removes all key-value pairs with keys that begin with pfx
305 * @param ctx Reference to context memory
306 * @param pfx Prefix of key-value pairs to remove
309 public static final void ctxBulkErase( SvcLogicContext ctx, String pfx ) {
310 ArrayList<String> Keys = new ArrayList<String>( ctx.getAttributeKeySet() );
311 for( String key : Keys ) {
312 if( key.startsWith( pfx ) ) {
313 ctx.setAttribute( pfx + key.substring(pfx.length()) , null);
319 * Copies all context memory key-value pairs that start with src_pfx to
320 * the keys that start with dest_pfx + suffix, where suffix is the result
321 * of {@code key.substring(src_pfx.length())}.
323 * Does NOT guarantee removal of all keys at the destination before
324 * copying, but will overwrite any destination keys that have a
325 * corresponding source key. Use {@link #ctxBulkErase(SvcLogicContext, String) ctxBulkErase}
326 * before copy to erase destination root before copying from source.
327 * @param ctx Reference to context memory.
328 * @param src_pfx Prefix of the keys to copy values from.
329 * @param dest_pfx Prefix of the keys to copy values to.
332 public static final void ctxBulkCopy( SvcLogicContext ctx, String src_pfx, String dest_pfx ) {
333 // Remove trailing period from dest_pfx
334 if( dest_pfx.charAt(dest_pfx.length()-1) == '.' ) {
335 dest_pfx = dest_pfx.substring(0,dest_pfx.length()-1);
338 // For each context key that begins with src_pfx, set the value of the
339 // key dest_pfx + the suffix of the key to the key's value
340 ArrayList<String> Keys = new ArrayList<String>(ctx.getAttributeKeySet());
341 for( String key : Keys ) {
342 if( key.startsWith(src_pfx) ) {
343 // Get suffix (no leading period)
344 String suffix = key.substring(src_pfx.length());
345 if( suffix.charAt(0) == '.') {
346 suffix = suffix.substring(1);
349 // Set destination's value to key's value
350 ctx.setAttribute(dest_pfx + '.' + suffix, ctx.getAttribute(key));
356 * Creates and returns a {@code Map<String, String>} that is a subset of
357 * context memory where all keys begin with the prefix.
358 * @param ctx Reference to context memory.
359 * @param prefix Returned map's keys should all begin with this value.
360 * @return A {@code Map<String, String>} containing all the key-value pairs
361 * in ctx whose key begins with prefix.
363 public static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) {
364 Map<String, String> prefixMap = new HashMap<String, String>();
366 for( String key : ctx.getAttributeKeySet() ) {
367 if( key.startsWith(prefix) ) {
368 prefixMap.put( key, ctx.getAttribute(key) );
376 * Returns true if key's value in context memory is "" or if it doesn't
377 * exist in context memory.
378 * @param ctx Reference to context memory.
379 * @param key Key to search for.
380 * @return true if key's value in context memory is "" or if it doesn't
381 * exist in context memory.
384 public static final boolean ctxKeyEmpty( SvcLogicContext ctx, String key ) {
385 String value = ctx.getAttribute(key);
386 return value == null || value.isEmpty();
390 * Adds all key-value pairs in the entries Map to context memory.
391 * @param ctx Reference to context memory. Value's {@code toString()}
392 * function is used to add it.
393 * @param entries {@code Map<String, ?>} of key-value pairs to add to
394 * context memory. Value's {@code toString()} function is used to add it.
395 * @return Reference to context memory to be used for function chaining.
397 public static final SvcLogicContext ctxPutAll( SvcLogicContext ctx, Map<String, ?> entries ) {
398 for( Map.Entry<String, ?> entry : entries.entrySet() ) {
399 ctxSetAttribute( ctx, entry.getKey(), entry.getValue() );
400 //ctx.setAttribute(entry.getKey(), entry.getValue().toString());
407 * Sets a key in context memory to the output of object's toString(). The
408 * key is deleted from context memory if object is null.
409 * @param ctx Reference to context memory.
410 * @param key Key to set.
411 * @param object Object whose toString() will be the value set
413 public static final void ctxSetAttribute( SvcLogicContext ctx, String key, Object object ) {
414 if( object == null ) {
415 ctx.setAttribute(key, null);
418 ctx.setAttribute(key, object.toString());
423 * Sets a key in context memory to the output of object's toString().
425 * The key is deleted from context memory if object is null. The key and
426 * value set in context memory are logged to the Logger at the provided
428 * @param <O> Any Java object
429 * @param ctx Reference to context memory.
430 * @param key Key to set.
431 * @param obj Object whose toString() will be the value set
432 * @param LOG Logger to log to
433 * @param logLevel level to log at in Logger
435 public static final <O extends Object> void ctxSetAttribute( SvcLogicContext ctx, String key, O obj, Logger LOG, LogLevel logLevel ) {
436 String value = Objects.toString( obj, null );
437 ctx.setAttribute( key, value );
438 if( logLevelIsEnabled(LOG, logLevel ) ) {
439 if( value == null ) {
440 logMessageAtLevel( LOG, logLevel, "Deleting " + key );
443 logMessageAtLevel( LOG, logLevel, "Setting " + key + " = " + value );
449 * Utility function used to get an array's length from context memory.
450 * Will return 0 if key doesn't exist in context memory or isn't numeric.
452 * Use to obtain a context memory array length without having to worry
453 * about throwing a NumberFormatException.
454 * @param ctx Reference to context memory
455 * @param key Key in context memory whose value is the array's length. If
456 * the key doesn't end in "_length", then "_length is appended.
457 * @param log Reference to Logger to log to
458 * @return The array length or 0 if the key is not found in context memory.
461 public static final int getArrayLength( SvcLogicContext ctx, String key ) {
462 return getArrayLength(ctx, key, null, null, null);
466 * Utility function used to get an array's length from context memory.
467 * Will return 0 if key doesn't exist in context memory or isn't numeric
468 * and print the provided log message to the configured log file.
470 * Use to obtain a context memory array length without having to worry
471 * about throwing a NumberFormatException.
472 * @param ctx Reference to context memory.
473 * @param key Key in context memory whose value is the array's length. If
474 * the key doesn't end in "_length", then "_length is appended.
475 * @param log Reference to Logger to log to. Doesn't log if null.
476 * @param logLevel Logging level to log the message at if the context
477 * memory key isn't found. Doesn't log if null.
478 * @param log_message Message to log if the context memory key isn't found.
479 * Doesn't log if null.
480 * @return The array length or 0 if the key is not found in context memory.
483 public static final int getArrayLength( SvcLogicContext ctx, String key, Logger log, LogLevel logLevel, String log_message ) {
484 String ctxKey = ( key.endsWith("_length") ) ? key : key + "_length";
486 return Integer.parseInt(ctx.getAttribute(ctxKey));
488 catch( NumberFormatException e ) {
489 if( log != null && logLevel != null && log_message != null ) {
492 log.trace(log_message);
494 log.debug(log_message);
497 log.info(log_message);
500 log.warn(log_message);
503 log.error(log_message);
513 * Prints sorted context memory key-value pairs to the log file at the log
514 * level. Returns immediately if the log level isn't enabled.
516 * O(n log(n)) time where n = size of context memory
517 * @param ctx Reference to context memory
518 * @param log Reference to Logger to log to
519 * @param logLevel Logging level to log the context memory key-value pairs
523 public static final void logContextMemory( SvcLogicContext ctx, Logger log, LogLevel logLevel ) {
524 logLevelIsEnabled( log, logLevel );
526 // Print sorted context memory key-value pairs to the log
527 ArrayList<String> keys = new ArrayList<String>(ctx.getAttributeKeySet());
528 Collections.sort(keys);
529 for( String key : keys ) {
530 logMessageAtLevel( log, logLevel, key + " = " + ctx.getAttribute(key) );
536 // ========== PRIVATE FUNCTIONS ==========
546 public static final void logExecuteNodeParameters( Map<String,String> parameters, Logger log, LogLevel loglevel ) {
547 logLevelIsEnabled( log, loglevel );
549 for( Map.Entry<String,String> param : parameters.entrySet() ) {
550 logMessageAtLevel( log, loglevel, "PARAM: " + param.getKey() + " = " + param.getValue() );
556 * Returns true if the loglevel is enabled. Otherwise, returns false.
557 * @param log Reference to logger
558 * @param loglevel Log level to check if enabled
559 * @return True if the loglevel is enabled. Otherwise, false
562 private static final boolean logLevelIsEnabled( Logger log, LogLevel loglevel ) {
563 // Return immediately if logging level isn't enabled
566 if( log.isTraceEnabled() ) { return true; }
569 if( log.isDebugEnabled() ) { return true; }
572 if( log.isInfoEnabled() ) { return true; }
575 if( log.isWarnEnabled() ) { return true; }
578 if( log.isErrorEnabled() ) { return true; }
581 throw new IllegalArgumentException("Unknown LogLevel: " + loglevel.toString());
593 private static final void logMessageAtLevel( Logger log, LogLevel loglevel, String msg ) {
615 // ========== LOCAL CLASSES ==========
617 private class SortableCtxListElement implements Comparable<SortableCtxListElement> {
618 HashMap<String,String> child_elements = new HashMap<String,String>();
619 String[] sort_fields;
621 public SortableCtxListElement( SvcLogicContext ctx, String root, String[] sort_fields ) {
622 this.sort_fields = sort_fields;
624 for( String key : ctx.getAttributeKeySet() ) {
625 if( key.startsWith(root) ) {
626 if( key.length() == root.length() ) {
627 child_elements.put("", ctx.getAttribute(key));
631 child_elements.put(key.substring(root.length()+1), ctx.getAttribute(key));
638 public int compareTo(SortableCtxListElement arg0) {
639 if( sort_fields == null ) {
640 return this.child_elements.get("").compareTo(arg0.child_elements.get(""));
643 for( String field : this.sort_fields ) {
644 int result = this.child_elements.get(field).compareTo(arg0.child_elements.get(field));
655 * Creates a file that contains the content of context memory.
656 * @param parameters - must contain the parameter filename
657 * @param ctx Reference to context memory
658 * @throws SvcLogicException thrown if file cannot be created or if parameters are missing
660 public static void printContext(Map<String, String> parameters, SvcLogicContext ctx) throws SvcLogicException {
661 if (parameters == null || parameters.isEmpty()) {
662 throw new SvcLogicException("no parameters passed");
665 checkParameters(parameters, new String[]{"filename"}, LOG);
667 String fileName = parameters.get("filename");
670 try (FileOutputStream fstr = new FileOutputStream(new File(fileName));
671 PrintStream pstr = new PrintStream(fstr, true);)
673 pstr.println("#######################################");
674 for (String attr : ctx.getAttributeKeySet()) {
675 pstr.println(attr + " = " + ctx.getAttribute(attr));
677 } catch (Exception e) {
678 throw new SvcLogicException("Cannot write context to file " + fileName, e);
685 * Checks context memory for a set of required parameters
686 * Every parameter aside from prefix will be treated as mandatory
687 * @param parameters HashMap<String,String> of parameters passed by the DG to this function
689 * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead>
691 * <tr><td>prefix</td><td>Optional</td><td>the prefix will be added to each parameter</td></tr>
694 * @param ctx Reference to context memory
695 * @throws SvcLogicException
698 public static void requiredParameters(Map<String, String> parameters, SvcLogicContext ctx) throws SvcLogicException {
699 if (parameters == null || parameters.keySet().size() < 1) {
700 String errorMessage = "requiredParameters should not be called if the parameters hashmap is null or empty!";
701 LOG.error(errorMessage);
702 throw new SvcLogicException(errorMessage);
704 String prefixValue = null;
705 String prefix = "prefix";
706 if(parameters.containsKey(prefix)){
707 prefixValue = parameters.get(prefix);
708 parameters.remove(prefix);
710 checkParameters(prefixValue, ctx.getAttributeKeySet(), parameters.keySet(), LOG);
713 private static void checkParameters(String prefixValue, Set<String> ctx, Set<String> parameters, Logger log) throws SvcLogicException {
714 for (String param : parameters) {
715 if (prefixValue != null) {
716 param = prefixValue + param;
718 if (!ctx.contains(param)) {
719 String errorMessage = "This method requires the parameters [" + StringUtils.join(parameters, ",")
720 + "], but " + param + " was not passed in.";
721 log.error(errorMessage);
722 throw new SvcLogicException(errorMessage);
728 * is in a different DG invocation just before/after we call NCS and set the state to InProgress
731 * setTime write the current date time to a string located at outputPath
732 * @param parameters - requires outputPath to not be null
733 * @param ctx Reference to context memory
734 * @throws SvcLogicException if a required parameter is missing an exception is thrown
736 public static void setTime(Map<String, String> parameters, SvcLogicContext ctx) throws SvcLogicException
738 checkParameters(parameters, new String[] { "outputPath" }, LOG);
740 // Set the DateFormat
741 // "2015-03-16T12:18:35.138Z"
742 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
745 String ctxVariable = parameters.get("outputPath");
747 String dateTime = format.format(new Date());
748 ctx.setAttribute(ctxVariable, dateTime);
749 } catch (Exception ex) {
750 throw new SvcLogicException("problem with setTime", ex);