9791aa23f7ac33a96616d77fc5c9dbaab5f63568
[appc.git] /
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.onap.appc.simulator.client.impl;
26
27 import static java.lang.Character.toLowerCase;
28 import static java.lang.Character.toUpperCase;
29
30 import com.att.eelf.configuration.EELFLogger;
31 import com.att.eelf.configuration.EELFManager;
32 import com.fasterxml.jackson.databind.JsonNode;
33 import com.fasterxml.jackson.databind.ObjectMapper;
34 import com.fasterxml.jackson.databind.node.ObjectNode;
35 import java.io.BufferedReader;
36 import java.io.File;
37 import java.io.FileNotFoundException;
38 import java.io.FileReader;
39 import java.io.IOException;
40 import java.lang.reflect.InvocationTargetException;
41 import java.lang.reflect.Method;
42 import java.util.HashMap;
43 import java.util.Properties;
44 import org.onap.appc.client.lcm.api.AppcClientServiceFactoryProvider;
45 import org.onap.appc.client.lcm.api.AppcLifeCycleManagerServiceFactory;
46 import org.onap.appc.client.lcm.api.ApplicationContext;
47 import org.onap.appc.client.lcm.api.LifeCycleManagerStateful;
48 import org.onap.appc.client.lcm.api.ResponseHandler;
49 import org.onap.appc.client.lcm.exceptions.AppcClientException;
50 import org.onap.appc.simulator.client.RequestHandler;
51
52 public class JsonRequestHandler implements RequestHandler {
53
54
55     private final EELFLogger logger = EELFManager.getInstance().getLogger(JsonRequestHandler.class);
56     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
57     private static final String INPUT_PARAM = "input";
58
59     private String inputClassName = null;
60     private String actionName = null;
61     private String methodName = null;
62     private String packageName = null;
63     private LifeCycleManagerStateful service = null;
64     private Properties properties;
65     private HashMap<String, String> exceptRpcMap = null;
66
67     private AppcLifeCycleManagerServiceFactory appcLifeCycleManagerServiceFactory = null;
68
69     public JsonRequestHandler() {/*default constructor*/}
70
71     public JsonRequestHandler(Properties prop) throws AppcClientException {
72         properties = prop;
73         packageName = properties.getProperty("ctx.model.package") + ".";
74         try {
75             service = createService();
76         } catch (AppcClientException e) {
77             logger.error("An error occurred while instantiating JsonRequestHandler", e);
78         }
79         exceptRpcMap = prepareExceptionsMap();
80     }
81
82     private HashMap<String, String> prepareExceptionsMap() {
83         exceptRpcMap = new HashMap<>();
84
85         try (BufferedReader reader = new BufferedReader(
86             new FileReader(properties.getProperty(
87                 "client.rpc.exceptions.map.file")))) {
88             String line;
89             while ((line = reader.readLine()) != null) {
90                 String[] parts = line.split(":", 2);
91                 if (parts.length >= 2) {
92                     String key = parts[0];
93                     String value = parts[1];
94                     exceptRpcMap.put(key, value);
95                 } else {
96                     logger.info("ignoring line: " + line);
97                 }
98             }
99         } catch (FileNotFoundException e) {
100             logger.error("Map file not found", e);
101             return exceptRpcMap;
102         } catch (IOException e) {
103             logger.error("An error occurred while preparing exceptions map", e);
104         }
105
106         return exceptRpcMap;
107     }
108
109     @Override
110     public void proceedFile(File source, File log) throws IOException {
111         final JsonNode inputNode = OBJECT_MAPPER.readTree(source);
112
113         try {
114             // proceed with inputNode and get some xxxInput object, depends on action
115             prepareNames(inputNode);
116
117             Object input = prepareObject(inputNode);
118
119             JsonResponseHandler response = new JsonResponseHandler();
120             response.setFile(source.getPath());
121             switch (isSyncMode(inputNode)) {
122                 case SYNCH:
123                     processSync(input, response);
124                     break;
125                 case ASYNCH:
126                     processAsync(input, response);
127                     break;
128                 default:
129                     throw new InvalidRequestException("Unrecognized request mode");
130             }
131         } catch (Exception e) {
132             logger.error("An error occurred when proceeding file", e);
133         }
134
135         logger.debug("Action <" + actionName + "> from input file <" + source.getPath() + "> processed");
136     }
137
138     private void processAsync(Object input, JsonResponseHandler response)
139         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
140         logger.debug("Received input request will be processed in asynchronously mode");
141         Method rpc = LifeCycleManagerStateful.class
142             .getDeclaredMethod(methodName, input.getClass(), ResponseHandler.class);
143         rpc.invoke(service, input, response);
144     }
145
146     private void processSync(Object input, JsonResponseHandler response)
147         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
148         logger.debug("Received input request will be processed in synchronously mode");
149         Method rpc = LifeCycleManagerStateful.class.getDeclaredMethod(methodName, input.getClass());
150         response.onResponse(rpc.invoke(service, input));
151     }
152
153     private modeT isSyncMode(JsonNode inputNode) {
154         // The following solution is for testing purposes only
155         // the sync/async decision logic may change upon request
156         try {
157             int mode = Integer
158                 .parseInt(
159                     inputNode.findValue(INPUT_PARAM).findValue("common-header").findValue("sub-request-id").asText());
160             if ((mode % 2) == 0) {
161                 return modeT.SYNCH;
162             }
163         } catch (Exception e) {
164             logger.error("Failed to parse sub-request-id", e);
165             //use ASYNC as default, if value is not integer.
166         }
167         return modeT.ASYNCH;
168     }
169
170     private LifeCycleManagerStateful createService() throws AppcClientException {
171         appcLifeCycleManagerServiceFactory = AppcClientServiceFactoryProvider
172             .getFactory(AppcLifeCycleManagerServiceFactory.class);
173         return appcLifeCycleManagerServiceFactory.createLifeCycleManagerStateful(new ApplicationContext(), properties);
174     }
175
176     @Override
177     public void shutdown(boolean isForceShutdown) {
178         appcLifeCycleManagerServiceFactory.shutdownLifeCycleManager(isForceShutdown);
179     }
180
181     public Object prepareObject(JsonNode input) {
182         try {
183             Class cls = Class.forName(inputClassName);
184             tryAlignPayload(input);
185             return OBJECT_MAPPER.treeToValue(input.get(INPUT_PARAM), cls);
186         } catch (Exception ex) {
187             logger.error("Failed to prepare object", ex);
188         }
189         return null;
190     }
191
192     private void tryAlignPayload(JsonNode input) {
193         try {
194             // since payload is not mandatory field and not all actions contains payload
195             // so we have to check that during input parsing
196             alignPayload(input);
197         } catch (NoSuchFieldException e) {
198             logger.debug("In " + actionName + " no payload defined", e);
199         }
200     }
201
202     private void prepareNames(JsonNode input) throws NoSuchFieldException {
203         JsonNode inputNode = input.findValue(INPUT_PARAM);
204         actionName = inputNode.findValue("action").asText();
205         if (actionName.isEmpty()) {
206             throw new NoSuchFieldException("Input doesn't contains field <action>");
207         }
208         inputClassName = packageName + actionName + "Input";
209         methodName = prepareMethodName(prepareRpcFromAction(actionName));
210     }
211
212     private void alignPayload(JsonNode input) throws NoSuchFieldException {
213         JsonNode inputNode = input.findValue(INPUT_PARAM);
214         JsonNode payload = inputNode.findValue("payload");
215         if (payload == null || payload.asText().isEmpty() || payload.toString().isEmpty()) {
216             throw new NoSuchFieldException("Input doesn't contains field <payload>");
217         }
218
219         String payloadData = payload.asText();
220         if (payloadData.isEmpty()) {
221             payloadData = payload.toString();
222         }
223         ((ObjectNode) inputNode).put("payload", payloadData);
224     }
225
226     private String prepareRpcFromAction(String action) {
227         String exRpc = checkExceptionalRpcList(action);
228         if (exRpc != null && !exRpc.isEmpty()) {
229             return exRpc; // we found exceptional rpc, so no need to format it
230         }
231
232         StringBuilder rpc = new StringBuilder();
233         boolean makeItLowerCase = true;
234         for (int i = 0; i < action.length(); i++) {
235             if (makeItLowerCase) // first character will make lower case
236             {
237                 rpc.append(toLowerCase(action.charAt(i)));
238                 makeItLowerCase = false;
239             } else if ((i + 1 < action.length()) && Character.isUpperCase(action.charAt(i + 1))) {
240                 rpc.append(action.charAt(i)).append('-');
241                 makeItLowerCase = true;
242             } else {
243                 rpc.append(action.charAt(i));
244                 makeItLowerCase = false;
245             }
246         }
247         return rpc.toString();
248     }
249
250     private String checkExceptionalRpcList(String action) {
251         if (exceptRpcMap.isEmpty()) {
252             return null;
253         }
254         return exceptRpcMap.get(action);
255     }
256
257     private String prepareMethodName(String inputRpcName) {
258         boolean makeItUpperCase = false;
259         StringBuilder method = new StringBuilder();
260
261         for (int i = 0; i < inputRpcName.length(); i++)  //to check the characters of string..
262         {
263             if (Character.isLowerCase(inputRpcName.charAt(i))
264                 && makeItUpperCase) // skip first character if it lower case
265             {
266                 method.append(toUpperCase(inputRpcName.charAt(i)));
267                 makeItUpperCase = false;
268             } else if (inputRpcName.charAt(i) == '-') {
269                 makeItUpperCase = true;
270             } else {
271                 method.append(inputRpcName.charAt(i));
272                 makeItUpperCase = false;
273             }
274         }
275         return method.toString();
276     }
277
278     private enum modeT {
279         SYNCH,
280         ASYNCH
281     }
282 }