serializing OAM async task
[appc.git] / appc-oam / appc-oam-bundle / src / main / java / org / openecomp / appc / oam / util / BundleHelper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
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  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.openecomp.appc.oam.util;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import org.apache.commons.lang3.ArrayUtils;
29 import org.openecomp.appc.exceptions.APPCException;
30 import org.openecomp.appc.oam.AppcOam;
31 import org.openecomp.appc.oam.processor.BaseCommon;
32 import org.openecomp.appc.statemachine.impl.readers.AppcOamStates;
33 import org.osgi.framework.Bundle;
34 import org.osgi.framework.BundleContext;
35 import org.osgi.framework.BundleException;
36 import org.osgi.framework.FrameworkUtil;
37
38 import java.util.Map;
39 import java.util.concurrent.Callable;
40 import java.util.concurrent.Future;
41
42 /**
43  * Utility class provides general bundle operational helps.
44  */
45 public class BundleHelper {
46     private final static String PROP_BUNDLE_TO_STOP = "appc.OAM.ToStop.properties";
47     private final static String PROP_BUNDLES_TO_NOT_STOP = "appc.OAM.ToNotStop.properties";
48
49     private final EELFLogger logger;
50     private final StateHelper stateHelper;
51     private final ConfigurationHelper configurationHelper;
52
53     /**
54      * Constructor
55      *
56      * @param eelfLogger of the logger
57      * @param configurationHelperIn of ConfigurationHelper instance
58      * @param stateHelperIn of StateHelper instance
59      */
60     public BundleHelper(EELFLogger eelfLogger,
61                         ConfigurationHelper configurationHelperIn,
62                         StateHelper stateHelperIn) {
63         logger = eelfLogger;
64         configurationHelper = configurationHelperIn;
65         stateHelper = stateHelperIn;
66     }
67
68     /**
69      * Handle bundle operations, such as stop or start bundle.
70      *
71      * @param rpc enum indicate if the operation is to stop, start or restart
72      * @return boolean to indicate if the operation is successful (true) or failed (false)
73      * @throws APPCException when error occurs
74      */
75     public boolean bundleOperations(AppcOam.RPC rpc,
76                                     Map<String, Future<?>> threads,
77                                     AsyncTaskHelper taskHelper,
78                                     BaseCommon baseCommon)
79         throws APPCException {
80         long mStartTime = System.currentTimeMillis();
81         logDebug(String.format("Entering OAM bundleOperations with rpc (%s).", rpc.name()));
82
83         String action = rpc.getAppcOperation().toString();
84         if (rpc != AppcOam.RPC.stop && rpc != AppcOam.RPC.start) {
85             throw new APPCException("rpc(" + rpc + ") is not supported by bundleOperation.");
86         }
87
88         AppcOamStates originalState = stateHelper.getState();
89
90         boolean isBundleOperationComplete = true;
91
92         Map<String, Bundle> appcLcmBundles = getAppcLcmBundles();
93         for (Map.Entry<String, Bundle> bundleEntry : appcLcmBundles.entrySet()) {
94             String bundleName = bundleEntry.getKey();
95             Bundle bundle = bundleEntry.getValue();
96
97             logDebug("OAM launch thread for %s bundle %s", action, bundleName);
98             if (rpc == AppcOam.RPC.start) {
99                 // Abort in the interruption case.
100                 // such as when a Stop request is receive while APPC is still trying to Start Up.
101                 if (!stateHelper.isSameState(originalState)) {
102                     logger.warn("OAM %s bundle operation aborted since OAM state is no longer %s!",
103                         originalState.name());
104                     isBundleOperationComplete = false;
105                     break;
106                 }
107             }
108
109             threads.put(bundleName,
110                 taskHelper.submitBaseSubCallable(new BundleTask(rpc, bundle,baseCommon)));
111         }
112
113         logDebug(String.format("Leaving OAM bundleOperations with rpc (%s) with complete(%s), elasped (%d) ms.",
114             rpc.name(), Boolean.toString(isBundleOperationComplete), getElapseTimeMs(mStartTime)));
115
116         return isBundleOperationComplete;
117     }
118
119     private long getElapseTimeMs(long mStartTime) {
120         return System.currentTimeMillis() - mStartTime;
121     }
122
123     /**
124      * Check if all BundleTasks are completed
125      * @param bundleNameFutureMap with bundle name and BundleTask Future object
126      * @return true if all are done, otherwise, false
127      */
128     public boolean isAllTaskDone(Map<String, Future<?>> bundleNameFutureMap) {
129         boolean anyNotDone = bundleNameFutureMap.values().stream().anyMatch((f) -> !f.isDone());
130         return !anyNotDone;
131     }
132
133     /**
134      * Cancel BundleTasks which are not finished
135      * @param bundleNameFutureMap with bundle name and BundleTask Future object
136      */
137     public void cancelUnfinished(Map<String, Future<?>> bundleNameFutureMap) {
138         bundleNameFutureMap.values().stream().filter((f)
139             -> !f.isDone()).forEach((f)
140             -> f.cancel(true));
141     }
142
143     /**
144      * Get number of failed BundleTasks
145      * @param bundleNameFutureMap with bundle name and BundleTask Future object
146      * @return number(long) of the failed BundleTasks
147      */
148     public long getFailedMetrics(Map<String, Future<?>> bundleNameFutureMap) {
149         return bundleNameFutureMap.values().stream().map((f) -> {
150             try {
151                 return f.get();
152             } catch (Exception e) {
153                 // should not get here
154                 throw new RuntimeException(e);
155             }
156         }).filter((b) -> ((BundleTask)b).failException != null).count();
157     }
158
159     /**
160      * Gets the list of Appc-bundles to be stopped/started
161      *
162      * @return Map of bundle symbolic name and bundle instance
163      */
164     Map<String, Bundle> getAppcLcmBundles() {
165         logDebug("In getAppcLcmBundles");
166
167         String[] bundlesToStop = readPropsFromPropListName(PROP_BUNDLE_TO_STOP);
168         String[] regExBundleNotStop = readPropsFromPropListName(PROP_BUNDLES_TO_NOT_STOP);
169
170         BundleFilter bundleList = new BundleFilter(bundlesToStop, regExBundleNotStop, getBundleList());
171
172         logger.info(String.format("(%d) APPC bundles to Stop/Start: %s.", bundleList.getBundlesToStop().size(),
173             bundleList.getBundlesToStop().toString()));
174
175         logger.debug(String.format("(%d) APPC bundles that won't be Stopped/Started: %s.",
176             bundleList.getBundlesToNotStop().size(), bundleList.getBundlesToNotStop().toString()));
177
178         return bundleList.getBundlesToStop();
179     }
180
181     /**
182      * Gets a list of all user desired bundles that should be stopped/Started as part of
183      * OAM Stop and Start API
184      *
185      * @param propListKey String of the properties list property name
186      * @return properties values of the related
187      */
188     String[] readPropsFromPropListName(String propListKey) {
189         // get properties list by properties list name
190         String[] propNames = configurationHelper.readProperty(propListKey);
191         // go through each property to get the property values
192         String[] propValue = ArrayUtils.EMPTY_STRING_ARRAY;
193         if (propNames != null) {
194             for (String aPropName : propNames) {
195                 propValue = ArrayUtils.addAll(propValue, configurationHelper.readProperty(aPropName));
196             }
197         }
198         return propValue;
199     }
200
201     /**
202      * Get all bundle list of APP-C
203      * @return Array of Bundle
204      */
205     Bundle[] getBundleList() {
206         BundleContext myBundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
207         if (myBundleContext != null) {
208             return myBundleContext.getBundles();
209         }
210         return null;
211     }
212
213     /**
214      * Genral debug log when debug logging level is enabled.
215      * @param message of the log message format
216      * @param args of the objects listed in the message format
217      */
218     private void logDebug(String message, Object... args) {
219         if (logger.isDebugEnabled()) {
220             logger.debug(String.format(message, args));
221         }
222     }
223
224     /**
225      * Runnable to execute bundle operations: start or stop
226      */
227     class BundleTask implements Callable<BundleTask> {
228         Exception failException;
229
230         private AppcOam.RPC rpc;
231         private Bundle bundle;
232         private String bundleName;
233         private String actionName;
234         private final BaseCommon baseCommon;
235
236         BundleTask(AppcOam.RPC rpcIn, Bundle bundleIn, BaseCommon baseCommon) {
237             rpc = rpcIn;
238             actionName = rpc.getAppcOperation().toString();
239             bundle = bundleIn;
240             bundleName = bundle.getSymbolicName();
241             this.baseCommon = baseCommon;
242         }
243
244         @Override
245         public BundleTask call() throws Exception {
246             try {
247                 baseCommon.setInitialLogProperties();
248
249                 long bundleOperStartTime = System.currentTimeMillis();
250                 logDebug(String.format("OAM %s bundle %s ===>", actionName, bundleName));
251                 switch (rpc) {
252                     case start:
253                         bundle.start();
254                         break;
255                     case stop:
256                         bundle.stop();
257                         break;
258                     default:
259                         // should do nothing
260                 }
261                 logDebug(String.format("OAM %s bundle %s completed <=== elasped %d",
262                     actionName, bundleName, getElapseTimeMs(bundleOperStartTime)));
263             } catch (BundleException e) {
264                 logger.error(String.format("Exception encountered when OAM %s bundle %s ",
265                     actionName, bundleName), e);
266                 failException = e;
267             }
268             finally {
269                 baseCommon.clearRequestLogProperties();
270             }
271             return this;
272         }
273     }
274 }