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