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