2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.openecomp.sdnc.sli.SliPluginUtils;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
28 import java.util.Objects;
29 import java.util.Properties;
30 import java.util.UUID;
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;
40 * A utility class used to streamline the interface between Java plugins,
41 * the Service Logic Context, and Directed Graphs.
43 * @see org.openecomp.sdnc.sli.SvcLogicContext
45 public class SliPluginUtils implements SvcLogicJavaPlugin {
46 public enum LogLevel {
47 TRACE, DEBUG, INFO, WARN, ERROR;
50 private static final Logger LOG = LoggerFactory.getLogger(SliPluginUtils.class);
53 // ========== CONSTRUCTORS ==========
55 public SliPluginUtils() {}
57 public SliPluginUtils( Properties props ) {}
61 // ========== CONTEXT MEMORY FUNCTIONS ==========
64 * Removes 1 or more elements from a list in context memory.
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.
69 * @param ctx Reference to context memory
70 * @throws SvcLogicException All exceptions are wrapped in
71 * SvcLogicException for compatibility with SLI.
74 public void ctxListRemove( Map<String,String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
76 LOG.debug( "ENTERING Execute Node \"ctxListRemove\"" );
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");
87 // Initialize context memory list mimic
88 SvcLogicContextList list;
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
95 if( param_index != null ) {
97 LOG.trace("executing remove by index logic");
100 index = Integer.parseInt(param_index);
102 catch( NumberFormatException e ) {
103 throw new IllegalArgumentException("\"index\" parameter is not a number. index = " + param_index, e);
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");
112 else if( param_value != null ) {
113 if( param_key == null ) { param_key = ""; }
115 // Extract list from context memory & remove objects with
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 );
123 else if( param_keys_length != null ) {
125 LOG.trace("executing remove by key-value pair list logic");
128 keys_length = Integer.parseInt(param_keys_length);
130 catch( NumberFormatException e ) {
131 throw new IllegalArgumentException("\"keys_length\" parameters is not a number. keys_length = " + param_keys_length, e);
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"));
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);
149 throw new IllegalArgumentException("Required parameters missing. Requires one of: index, key & value, or keys_length array");
152 // Remove index from list
153 LOG.trace("writing list back into context memory");
154 list.writeToContext(ctx);
156 catch( Exception e ) {
157 throw new SvcLogicException( "An error occurred in the ctxListRemove Execute node", e );
160 LOG.debug( "EXITING Execute Node \"ctxListRemove\"" );
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<>();
169 String[] sort_fields = null;
170 if( parameters.containsKey("sort-fields") ) {
171 sort_fields = parameters.get("sort-fields").split(parameters.get("delimiter"), 0);
174 String ctx_list_str = parameters.get("list");
175 int listSz = getArrayLength(ctx, ctx_list_str);
179 for( int i = 0; i < listSz; i++ ) {
180 list.add( new SortableCtxListElement(ctx, ctx_list_str + '[' + i + ']', sort_fields) );
182 Collections.sort(list);
184 ctxBulkErase(ctx, ctx_list_str);
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());
192 ctx.setAttribute(ctx_list_str + '[' + i + "]." + entry.getKey(), entry.getValue());
197 // Reset list length (removed by ctxBulkErase above)
198 ctx.setAttribute(ctx_list_str+"_length", ""+listSz);
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() );
208 * Provides substring functionality to Directed Graphs.
210 * Calls either String.substring(String beginIndex) or
211 * String.substring(String beginInded, String endIndex) if the end-index
213 * @param parameters HashMap<String,String> of parameters passed by the DG to this function
215 * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th></thead>
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>
223 * @param ctx Reference to context memory
224 * @throws SvcLogicException
227 public void substring( Map<String, String> parameters, SvcLogicContext ctx ) throws SvcLogicException {
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");
235 if( StringUtils.isEmpty(end) ) {
236 ctx.setAttribute( result, string.substring(Integer.parseInt(begin)) );
239 ctx.setAttribute( result, string.substring(Integer.parseInt(begin), Integer.parseInt(end)) );
242 catch( Exception e ) {
243 throw new SvcLogicException( "An error occurred while the Directed Graph was performing a substring", e );
249 // ========== PUBLIC STATIC UTILITY FUNCTIONS ==========
252 * Throws an exception and writes an error to the log file if a required
253 * parameters is not found in the parametersMap.
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.
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");
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
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);
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())}.
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.
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);
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);
320 // Set destination's value to key's value
321 ctx.setAttribute(dest_pfx + '.' + suffix, ctx.getAttribute(key));
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.
334 public static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) {
335 Map<String, String> prefixMap = new HashMap<>();
337 for( String key : ctx.getAttributeKeySet() ) {
338 if( key.startsWith(prefix) ) {
339 prefixMap.put( key, ctx.getAttribute(key) );
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.
355 public static final boolean ctxKeyEmpty( SvcLogicContext ctx, String key ) {
356 String value = ctx.getAttribute(key);
357 return value == null || value.isEmpty();
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.
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());
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
384 public static final void ctxSetAttribute( SvcLogicContext ctx, String key, Object object ) {
385 if( object == null ) {
386 ctx.setAttribute(key, null);
389 ctx.setAttribute(key, object.toString());
394 * Sets a key in context memory to the output of object's toString().
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
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
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 );
414 logMessageAtLevel( LOG, logLevel, "Setting " + key + " = " + value );
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.
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.
432 public static final int getArrayLength( SvcLogicContext ctx, String key ) {
433 return getArrayLength(ctx, key, null, null, null);
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.
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.
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";
457 return Integer.parseInt(ctx.getAttribute(ctxKey));
459 catch( NumberFormatException e ) {
460 if( log != null && logLevel != null && log_message != null ) {
463 log.trace(log_message);
465 log.debug(log_message);
468 log.info(log_message);
471 log.warn(log_message);
474 log.error(log_message);
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.
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
494 public static final void logContextMemory( SvcLogicContext ctx, Logger log, LogLevel logLevel ) {
495 logLevelIsEnabled( log, logLevel );
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) );
507 // ========== PRIVATE FUNCTIONS ==========
517 public static final void logExecuteNodeParameters( Map<String,String> parameters, Logger log, LogLevel loglevel ) {
518 logLevelIsEnabled( log, loglevel );
520 for( Map.Entry<String,String> param : parameters.entrySet() ) {
521 logMessageAtLevel( log, loglevel, "PARAM: " + param.getKey() + " = " + param.getValue() );
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
533 private static final boolean logLevelIsEnabled( Logger log, LogLevel loglevel ) {
534 // Return immediately if logging level isn't enabled
537 if( log.isTraceEnabled() ) { return true; }
540 if( log.isDebugEnabled() ) { return true; }
543 if( log.isInfoEnabled() ) { return true; }
546 if( log.isWarnEnabled() ) { return true; }
549 if( log.isErrorEnabled() ) { return true; }
552 throw new IllegalArgumentException("Unknown LogLevel: " + loglevel.toString());
564 private static final void logMessageAtLevel( Logger log, LogLevel loglevel, String msg ) {
586 // ========== LOCAL CLASSES ==========
588 private class SortableCtxListElement implements Comparable<SortableCtxListElement> {
589 HashMap<String,String> child_elements = new HashMap<>();
590 String[] sort_fields;
592 public SortableCtxListElement( SvcLogicContext ctx, String root, String[] sort_fields ) {
593 this.sort_fields = sort_fields;
595 for( String key : ctx.getAttributeKeySet() ) {
596 if( key.startsWith(root) ) {
597 if( key.length() == root.length() ) {
598 child_elements.put("", ctx.getAttribute(key));
602 child_elements.put(key.substring(root.length()+1), ctx.getAttribute(key));
609 public int compareTo(SortableCtxListElement arg0) {
610 if( sort_fields == null ) {
611 return this.child_elements.get("").compareTo(arg0.child_elements.get(""));
614 for( String field : this.sort_fields ) {
615 int result = this.child_elements.get(field).compareTo(arg0.child_elements.get(field));