Merge "[APPC-127] NPE on execution of LCM action Lock"
[appc.git] / appc-oam / appc-oam-bundle / src / main / java / org / openecomp / appc / oam / processor / BaseActionRunnable.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.processor;
26
27 import org.openecomp.appc.i18n.Msg;
28 import org.openecomp.appc.oam.OAMCommandStatus;
29 import org.openecomp.appc.statemachine.impl.readers.AppcOamStates;
30
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.concurrent.Future;
34
35 /**
36  * Base runnable actions for OAM APIs, such as maintenance mode, restart, start and stop API.
37  *
38  * <p>This class holds the general action async handling methods for all OAM APIs.
39  * <p>Specific API action runnable will overwrite the general methods to add specific behaviors.
40  *
41  * <p>Subclass constructor must set the following class variables:
42  *   <br>  - actionName
43  *   <br>  - auditMsg
44  *   <br>  - finalState
45  */
46 abstract class BaseActionRunnable extends BaseCommon implements Runnable {
47     final String OAM_OPERATION_TIMEOUT_SECOND = "appc.OAM.api.timeout";
48     /** Default operation tiemout set to 1 minute */
49     final int DEFAULT_OAM_OPERATION_TIMEOUT = 60;
50     /** Abort message format with flexible operation name */
51     final String ABORT_MESSAGE_FORMAT = "Aborting %s operation.";
52     /** Timeout message format with flexible operation name */
53     final String TIMEOUT_MESSAGE_FORMAT = "%s operation has reached timeout %d milliseconds.";
54     /** Failure message format with flexible number of bundles */
55     final String BUNDLE_OPERATION_FAILED_FORMAT = "%d bundle(s) failed, see logs for details.";
56
57     private boolean isWaiting = false;
58     private AppcOamStates currentState;
59     long startTimeMs = 0;
60     long timeoutMs = 0;
61     boolean doTimeoutChecking = false;
62
63     String actionName = "Need to be reset";
64     Msg auditMsg;
65     AppcOamStates finalState;
66
67     BaseProcessor myParent;
68     Map<String, Future<?>> bundleNameToFuture = new HashMap<>();
69
70     /**
71      * Constructor
72      *
73      * @param parent BaseProcessor who has called this constructor.
74      */
75     BaseActionRunnable(BaseProcessor parent) {
76         super(parent.logger, parent.configurationHelper, parent.stateHelper, parent.operationHelper);
77
78         rpc = parent.rpc;
79         commonHeader = parent.commonHeader;
80         startTime = parent.startTime;
81         myParent = parent;
82
83         setTimeoutValues();
84     }
85
86     /**
87      * Set timeout in milliseconds
88      */
89     void setTimeoutValues() {
90         Integer timeoutSeconds = myParent.timeoutSeconds;
91         if (timeoutSeconds == null) {
92             timeoutMs = configurationHelper.getConfig().getIntegerProperty(
93                     OAM_OPERATION_TIMEOUT_SECOND, DEFAULT_OAM_OPERATION_TIMEOUT) * 1000;
94         } else {
95             timeoutMs = timeoutSeconds.longValue() * 1000;
96         }
97
98         doTimeoutChecking = timeoutMs != 0;
99         if (doTimeoutChecking) {
100             startTimeMs = startTime.getTime();
101         }
102         logDebug("%s action runnable check timeout (%s) with timeout (%d)ms, and startMs (%d)",
103                 rpc.name(), Boolean.toString(doTimeoutChecking), timeoutMs, startTimeMs);
104     }
105
106     @Override
107     public void run() {
108         try {
109             setInitialLogProperties();
110             logDebug(String.format("===========in %s run (waiting: %s)=======",
111                     actionName, Boolean.toString(isWaiting)));
112
113             if (isWaiting) {
114                 if (!checkState()) {
115                     keepWaiting();
116                 }
117             } else {
118                 if (doAction()) {
119                     isWaiting = !checkState();
120                 } else {
121                     postDoAction(false);
122                 }
123             }
124         } catch (Exception e) {
125             logDebug(String.format("%s got exception %s", actionName, e.getMessage()));
126             logger.error(actionName + " exception", e);
127
128         } finally {
129             clearRequestLogProperties();
130         }
131     }
132
133     /**
134      * Keep waiting to be override by children classes for different behaviors.
135      * Timeout is validated here.
136      */
137     void keepWaiting() {
138         logDebug(String.format("%s runnable waiting, current state is %s.",
139                 actionName, currentState == null ? "null" : currentState.toString()));
140
141         isTimeout("keepWaiting");
142     }
143
144     /**
145      * Check if the timeout milliseconds has reached.
146      *
147      * @param parentName String of the caller, for logging purpose.
148      * @return true if the timeout has reached, otherwise false.
149      */
150     boolean isTimeout(String parentName) {
151         logDebug(String.format("%s task isTimeout called from %s", actionName, parentName));
152         if (doTimeoutChecking
153                 && System.currentTimeMillis() - startTimeMs > timeoutMs) {
154             logger.error(String.format("%s operation timeout (%d) ms has reached, abort with error state.",
155                     actionName, timeoutMs));
156
157             setStatus(OAMCommandStatus.TIMEOUT, String.format(TIMEOUT_MESSAGE_FORMAT, rpc.name(), timeoutMs));
158             postAction(AppcOamStates.Error);
159             return true;
160         }
161         return false;
162     }
163
164     /**
165      * Check if all bundle operations are successful through BundleHelper.
166      * If there's failed bundler operation, set error status and trigger postAction with Error state.
167      *
168      * @return true if bundler operations have failure, otherwise false.
169      */
170     boolean hasBundleOperationFailure() {
171         long failedTask = myParent.bundleHelper.getFailedMetrics(bundleNameToFuture);
172         if (failedTask == 0) {
173             return false;
174         }
175
176         setStatus(OAMCommandStatus.UNEXPECTED_ERROR, String.format(BUNDLE_OPERATION_FAILED_FORMAT, failedTask));
177         postAction(AppcOamStates.Error);
178         return true;
179     }
180
181     /**
182      * Set class <b>status</b> to REJECTED with abort message.
183      */
184     void setAbortStatus() {
185         setStatus(OAMCommandStatus.REJECTED, String.format(ABORT_MESSAGE_FORMAT, rpc.name()));
186     }
187
188     /**
189      * Final handling. The thread is cancelled.
190      *
191      * @param setState boolean to indicate if set OAM state or not
192      */
193     void postDoAction(boolean setState) {
194         logDebug(String.format("Finished %s task", actionName));
195     }
196
197     /**
198      * Handling for after doAction. does post notification, issue audit log and set OAM state based on input.
199      *
200      * @param state of AppcOamState to be set as OAM state when it is not null.
201      */
202     void postAction(AppcOamStates state) {
203         operationHelper.sendNotificationMessage(rpc, commonHeader, status);
204
205         if (state != null) {
206             stateHelper.setState(state);
207         }
208
209         auditInfoLog(auditMsg);
210
211         myParent.cancelAsyncTask();
212     }
213
214     /**
215      * Check state
216      *
217      * @return true if final state reached, otherwise return false
218      */
219     boolean checkState() {
220         if (isTimeout("checkState")) {
221             myParent.bundleHelper.cancelUnfinished(bundleNameToFuture);
222             return true;
223         }
224
225         if (!myParent.bundleHelper.isAllTaskDone(bundleNameToFuture)) {
226             return false;
227         }
228
229         if (hasBundleOperationFailure()) {
230             return true;
231         }
232
233         currentState = stateHelper.getBundlesState();
234         if (currentState == finalState) {
235             setStatus(OAMCommandStatus.SUCCESS);
236             postDoAction(true);
237             return true;
238         }
239         return false;
240     }
241
242     abstract boolean doAction();
243 }