aec43780673cfce97ebc5111db1cc31d4c2b4d81
[vid.git] / vid-ext-services-simulator / src / main / java / org / onap / simulator / controller / SimulatorController.java
1 package org.onap.simulator.controller;
2
3 import static org.apache.commons.lang3.StringUtils.isEmpty;
4 import static org.mockserver.integration.ClientAndServer.startClientAndServer;
5 import static org.mockserver.matchers.Times.exactly;
6 import static org.mockserver.model.JsonBody.json;
7
8 import com.fasterxml.jackson.databind.DeserializationFeature;
9 import com.fasterxml.jackson.databind.ObjectMapper;
10 import com.google.common.collect.ImmutableMap;
11 import com.google.gson.Gson;
12 import java.io.BufferedInputStream;
13 import java.io.DataInputStream;
14 import java.io.File;
15 import java.io.FileInputStream;
16 import java.io.FileNotFoundException;
17 import java.io.IOException;
18 import java.net.URI;
19 import java.net.URISyntaxException;
20 import java.net.URLEncoder;
21 import java.nio.file.Files;
22 import java.nio.file.Path;
23 import java.nio.file.Paths;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Enumeration;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.Scanner;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import javax.annotation.PostConstruct;
34 import javax.annotation.PreDestroy;
35 import javax.persistence.EntityManager;
36 import javax.persistence.EntityManagerFactory;
37 import javax.persistence.Persistence;
38 import javax.persistence.TypedQuery;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
41 import org.mockserver.integration.ClientAndServer;
42 import org.mockserver.matchers.MatchType;
43 import org.mockserver.matchers.Times;
44 import org.mockserver.model.HttpRequest;
45 import org.mockserver.model.HttpResponse;
46 import org.mockserver.model.JsonBody;
47 import org.onap.simulator.db.entities.Function;
48 import org.onap.simulator.db.entities.User;
49 import org.onap.simulator.errorHandling.VidSimulatorException;
50 import org.onap.simulator.model.SimulatorRequestResponseExpectation;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.springframework.core.io.ClassPathResource;
54 import org.springframework.core.io.Resource;
55 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
56 import org.springframework.core.io.support.PropertiesLoaderUtils;
57 import org.springframework.core.io.support.ResourcePatternResolver;
58 import org.springframework.http.HttpEntity;
59 import org.springframework.http.HttpHeaders;
60 import org.springframework.http.HttpMethod;
61 import org.springframework.http.HttpStatus;
62 import org.springframework.http.ResponseEntity;
63 import org.springframework.stereotype.Component;
64 import org.springframework.web.bind.annotation.RequestBody;
65 import org.springframework.web.bind.annotation.RequestMapping;
66 import org.springframework.web.bind.annotation.RequestMethod;
67 import org.springframework.web.bind.annotation.ResponseBody;
68 import org.springframework.web.bind.annotation.ResponseStatus;
69 import org.springframework.web.bind.annotation.RestController;
70 import org.springframework.web.client.HttpClientErrorException;
71 import org.springframework.web.client.RestTemplate;
72 import org.springframework.web.servlet.View;
73
74 @RestController
75 @Component
76 public class SimulatorController {
77
78     private static final Times DEFAULT_NUMBER_OF_TIMES = Times.unlimited();
79     private ClientAndServer mockServer;
80     private String mockServerProtocol;
81     private String mockServerHost;
82     private Integer mockServerPort;
83     private Boolean enablePresetRegistration;
84     private Boolean enableJPA;
85     private volatile boolean isInitialized = false;
86
87     private EntityManager entityManager;
88     private EntityManagerFactory entityManagerFactory;
89
90
91     private static final Logger logger = LoggerFactory.getLogger(SimulatorController.class);
92
93     @PostConstruct
94     public void init(){
95         logger.info("Starting VID Simulator....");
96         setProperties();
97         mockServer = startClientAndServer(mockServerPort);
98         presetRegister();
99         try {
100             initJPA();
101         } catch (RuntimeException e) {
102             isInitialized = false;
103             logger.error("Error during the JPA initialization:", e);
104             return;
105         }
106         isInitialized = true;
107         logger.info("VID Simulator started successfully");
108     }
109
110     private void initJPA() {
111         if (enableJPA) {
112             entityManagerFactory = Persistence.createEntityManagerFactory("vid", overrideConnectionUrl());
113             entityManager = entityManagerFactory.createEntityManager();
114         }
115     }
116
117     private Map<Object, Object> overrideConnectionUrl() {
118         final String connectionUrlEnvProperty = "hibernate.connection.url";
119         if (isEmpty(System.getProperty(connectionUrlEnvProperty))) {
120             return Collections.emptyMap();
121         } else {
122             return ImmutableMap.of(connectionUrlEnvProperty, System.getProperty(connectionUrlEnvProperty));
123         }
124     }
125
126     @PreDestroy
127     public void tearDown(){
128         logger.info("Stopping VID Simulator....");
129         entityManager.close();
130         entityManagerFactory.close();
131         isInitialized = false;
132         mockServer.stop(false);
133     }
134
135
136     private void presetRegister() {
137         //Checking if set
138         if (enablePresetRegistration == null || !enablePresetRegistration){
139             logger.info("Preset registration property is false or not set - skipping preset registration...");
140             return;
141         }
142         ClassLoader cl = this.getClass().getClassLoader();
143         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
144         List<Path> resources = new ArrayList<>();
145         try {
146             File presetDir = resolver.getResource("/preset_registration/").getFile();
147             if (presetDir.exists() && presetDir.isDirectory()) {
148                 try (Stream<Path> files = Files.walk(Paths.get(presetDir.getPath()))) {
149                     resources = files
150                             .filter(p -> p.toString().endsWith(".json"))
151                             .collect(Collectors.toList());
152                 }
153             } else {
154                 logger.error("preset_registration directory is not exists");
155             }
156         } catch (IOException e) {
157             logger.error("Error performing preset registration, error: ", e);
158             return;
159         }
160         logger.info("Starting preset registrations, number of requests: {}", resources.size());
161         for (Path resource: resources){
162             String content;
163             try (Scanner scanner = new Scanner(resource).useDelimiter("\\Z")){
164                 content = scanner.next();
165             } catch (IOException e){
166                 logger.error("Error reading preset registration file {}, skipping to next one. Error: ", resource.getFileName(), e);
167                 continue;
168             }
169             //register the preset request
170             try {
171                 register(content);
172             } catch (VidSimulatorException e) {
173                 logger.error("Error proceeding preset registration file {},skipping to next one. Check if the JSON is in correct format. Error: ", resource.getFileName(), e);
174             }
175         }
176     }
177
178
179
180     private void setProperties() {
181         Resource resource = new ClassPathResource("simulator.properties");
182         Properties props = new Properties();
183         try {
184             props = PropertiesLoaderUtils.loadProperties(resource);
185         } catch (IOException e) {
186             logger.error("Error loading simulator properties, error: ", e);
187             return;
188         }
189         logger.info("Simulator properties are {}", props);
190         mockServerProtocol = (String)props.get("simulator.mockserver.protocol");
191         mockServerHost = (String)props.get("simulator.mockserver.host");
192         mockServerPort = Integer.parseInt((String)props.get("simulator.mockserver.port"));
193         enablePresetRegistration = Boolean.parseBoolean((String)props.get("simulator.enablePresetRegistration"));
194         enableJPA = Boolean.parseBoolean((String)props.get("simulator.enableCentralizedRoleAccess"));
195     }
196
197     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.POST)
198     public @ResponseBody
199     ResponseEntity registerRequest(HttpServletRequest request, @RequestBody String expectation) {
200         try {
201             register(expectation);
202         } catch (VidSimulatorException e) {
203             return new ResponseEntity<>("Registration failure! Error: "+e.getMessage(),HttpStatus.BAD_REQUEST);
204         }
205         return new ResponseEntity<>("Registration successful!",HttpStatus.OK);
206     }
207
208     @RequestMapping(value = {"/echo"}, method = RequestMethod.GET)
209     ResponseEntity echo(HttpServletRequest request) {
210         return isInitialized ? new ResponseEntity<>("",HttpStatus.OK) : new ResponseEntity<>("",HttpStatus.SERVICE_UNAVAILABLE);
211     }
212
213     @RequestMapping(value = {"/retrieveRecordedRequests"}, method = RequestMethod.GET)
214     public List<HttpRequest> retrieveRecordedRequests() {
215         return Arrays.asList(mockServer.retrieveRecordedRequests(null));
216     }
217
218     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.DELETE)
219     @ResponseStatus(value = HttpStatus.OK)
220     public void wipeOutAllExpectations() {
221         mockServer.reset();
222     }
223
224     private void register(String expectation) throws VidSimulatorException{
225         ObjectMapper mapper = new ObjectMapper()
226                 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
227
228         SimulatorRequestResponseExpectation[] expectationModels;
229         try {
230             expectationModels = mapper.readValue(expectation, SimulatorRequestResponseExpectation[].class);
231         } catch (IOException e) {
232             logger.error("Couldn't deserialize register expectation {}, error:", expectation, e);
233             throw new VidSimulatorException(e.getMessage());
234         }
235
236         for (SimulatorRequestResponseExpectation expectationModel : expectationModels) {
237             logger.info("Proceeding registration request: {}", expectationModel);
238             register(expectationModel);
239         }
240     }
241
242     //*******portal role access simulator (added by ag137v)
243
244     @RequestMapping(value = {"/ecompportal_att/auxapi//{ver}/user/*", "/ONAPPORTAL/auxapi//{ver}/user/*"}, method = RequestMethod.GET)
245     public @ResponseBody
246     ResponseEntity auxapiGetUser(HttpServletRequest request) {
247         if (!enableJPA) {
248             return new ResponseEntity<>("Centralized Role Access is disabled", HttpStatus.SERVICE_UNAVAILABLE);
249         }
250         entityManager.clear();
251         String reqUri = request.getRequestURI();
252         String userName = reqUri.substring(reqUri.lastIndexOf('/') + 1);
253         TypedQuery<User> userQuery = entityManager.createQuery("select u from fn_user u where u.loginId = :userName", User.class);
254         userQuery.setParameter("userName", userName);
255         User user = userQuery.getSingleResult();
256
257         Gson g = new Gson();
258         String jsonString = g.toJson(user);
259
260         return new ResponseEntity<>(jsonString, HttpStatus.OK);
261
262     }
263
264     @RequestMapping(value = {"/ecompportal_att/auxapi//{ver}/functions", "/ONAPPORTAL/auxapi//{ver}/functions"}, method = RequestMethod.GET)
265     public @ResponseBody
266     ResponseEntity auxapiGetFunctions(HttpServletRequest request) {
267         if (!enableJPA) {
268             return new ResponseEntity<>("Centralized Role Access is disabled", HttpStatus.SERVICE_UNAVAILABLE);
269         }
270         TypedQuery<Function> userQuery = entityManager.createQuery("select f from fn_function f", Function.class);
271         List<Function> functions = userQuery.getResultList();
272         Gson g = new Gson();
273         String jsonString = g.toJson(functions);
274
275         return new ResponseEntity<>(jsonString, HttpStatus.OK);
276
277     }
278
279     //*******portal role access simulator end
280
281     @RequestMapping(value = {"/ecompportal_att/auxapi//{ver}/getSessionSlotCheckInterval", "/ONAPPORTAL/auxapi//{ver}/getSessionSlotCheckInterval"}, method = RequestMethod.GET)
282     @ResponseBody
283     public String getSessionSlotCheckInterval() {
284         return "300000";
285     }
286
287     @RequestMapping(value = {"/**"})
288     public ResponseEntity redirectToMockServer(HttpServletRequest request, HttpServletResponse response) throws IOException {
289         //This is needed to allow POST redirect - see http://www.baeldung.com/spring-redirect-and-forward
290         request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
291
292         //Building the redirect URL
293 //        String restOfTheUrl = (String) request.getAttribute(
294 //                HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
295         String requestUri = URLEncoder.encode(request.getRequestURI(), "UTF-8");
296         requestUri = requestUri.replaceAll("%2F", "/");
297         String restOfTheUrl = requestUri.replaceFirst(request.getContextPath(), "");
298
299         StringBuilder sb = new StringBuilder();
300         sb.append(mockServerProtocol).append("://").append(mockServerHost).append(":").append(mockServerPort).append(restOfTheUrl);
301         String queryString = request.getQueryString();
302         if (queryString != null){
303             sb.append("?").append(queryString);
304         }
305         String redirectUrl = sb.toString();
306         logger.info("Redirecting the request to : {}", redirectUrl);
307
308         URI uri;
309         try {
310             uri = new URI("http", null, "localhost", 1080, restOfTheUrl, request.getQueryString(), null);
311         } catch (URISyntaxException e) {
312             logger.error("Error during proxying request {}, error: ", request.getRequestURI(), e.getMessage());
313             return new ResponseEntity(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
314         }
315         RestTemplate restTemplate = new RestTemplate();
316         //Preparing the headers
317         HttpHeaders headers = new HttpHeaders();
318         Enumeration<String> headerNames =  request.getHeaderNames();
319         while (headerNames.hasMoreElements()){
320             String headerToSet = headerNames.nextElement();
321             headers.set(headerToSet, request.getHeader(headerToSet));
322         }
323         HttpEntity<String> proxyRequest;
324         if ("POST".equalsIgnoreCase(request.getMethod()))
325         {
326             String body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
327             proxyRequest = new HttpEntity<>(body, headers);
328         } else {
329             proxyRequest = new HttpEntity<>(headers);
330         }
331
332         ResponseEntity<String> responseEntity;
333         try {
334             responseEntity =
335                     restTemplate.exchange(uri, HttpMethod.resolve(request.getMethod()), proxyRequest, String.class);
336         } catch (HttpClientErrorException exception) {
337             responseEntity = new ResponseEntity<>(exception.getResponseBodyAsString(), exception.getStatusCode());
338         }
339
340         return responseEntity;
341     }
342
343     private void register(SimulatorRequestResponseExpectation expectationModel) throws VidSimulatorException{
344         //Setting request according to what is passed
345         HttpRequest request = HttpRequest.request();
346         String id = expectationModel.getSimulatorRequest().getId();
347         if (id != null) {
348             request.withHeader("x-simulator-id", id);
349         }
350
351         if (expectationModel.getSimulatorRequest().getHeaders()!=null) {
352             expectationModel.getSimulatorRequest().getHeaders().forEach(
353                     request::withHeader);
354         }
355
356         String method = expectationModel.getSimulatorRequest().getMethod();
357         if (method != null) {
358             request.withMethod(method);
359         }
360         String path = expectationModel.getSimulatorRequest().getPath();
361         if (path != null) {
362             request.withPath(path);
363         }
364         String body = expectationModel.getSimulatorRequest().getBody();
365         if (body != null) {
366             if (expectationModel.getSimulatorRequest().getStrict()) {
367                 request.withBody(json(body, MatchType.STRICT));
368             } else {
369                 request.withBody(new JsonBody(body));
370             }
371         }
372
373         //Queryparams
374         final Map<String, List<String>> queryParams = expectationModel.getSimulatorRequest().getQueryParams();
375         if (queryParams != null){
376             String[] arr = new String[0];
377             queryParams.forEach((key, value) -> request.withQueryStringParameter(key, value.toArray(arr)));
378         }
379
380         //Setting response according to what is passed
381         HttpResponse response = HttpResponse.response();
382         Integer responseCode = expectationModel.getSimulatorResponse().getResponseCode();
383         if (responseCode != null) {
384             response.withStatusCode(responseCode);
385         } else {
386             logger.error("Invalid registration - response code cannot be empty");
387             throw new VidSimulatorException("Invalid registration - response code cannot be empty");
388         }
389
390         String respBody = expectationModel.getSimulatorResponse().getBody();
391         if (respBody != null) {
392             response.withBody(respBody);
393         }
394
395         String file = expectationModel.getSimulatorResponse().getFile();
396         if (file != null) {
397             response.withBody(loadFileString(file));
398         }
399
400         Map<String, String> responseHeaders = expectationModel.getSimulatorResponse().getResponseHeaders();
401         if (responseHeaders != null) {
402             responseHeaders.forEach(response::withHeader);
403         }
404
405         Times numberOfTimes = getExpectationNumberOfTimes(expectationModel);
406
407         if (expectationModel.getMisc().getReplace()) {
408             logger.info("Unregistering request expectation, if previously set, request: {}", expectationModel.getSimulatorRequest());
409             mockServer.clear(request);
410         }
411
412         mockServer
413                 .when(request, numberOfTimes).respond(response);
414     }
415
416
417     private byte[] loadFileString(String filePath) {
418         byte[] bytes = null;
419         File file = null;
420         try {
421             file = new ClassPathResource("download_files/" + filePath).getFile();
422             try(DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.getPath())))) {
423                 bytes = new byte[(int)file.length()];
424                 dataInputStream.readFully(bytes);
425             }
426         } catch (FileNotFoundException e) {
427             logger.error("File not found for file:" + filePath);
428             e.printStackTrace();
429         } catch (IOException e) {
430             logger.error("Error reading file:" + filePath);
431             e.printStackTrace();
432         }
433
434         return bytes;
435     }
436     private Times getExpectationNumberOfTimes(SimulatorRequestResponseExpectation expectationModel) {
437         Integer expectationModelNumberOfTimes = expectationModel.getMisc().getNumberOfTimes();
438         Times effectiveNumberOfTimes;
439         if (expectationModelNumberOfTimes == null || expectationModelNumberOfTimes < 0) {
440             effectiveNumberOfTimes = DEFAULT_NUMBER_OF_TIMES;
441         } else {
442             effectiveNumberOfTimes = exactly(expectationModelNumberOfTimes);
443         }
444         return effectiveNumberOfTimes;
445     }
446 }