b67c98ee91d71d949afa167a5c528c751a225398
[vid.git] / vid-ext-services-simulator / src / main / java / org / onap / simulator / controller / SimulatorController.java
1 package org.onap.simulator.controller;
2
3 import com.fasterxml.jackson.databind.DeserializationFeature;
4 import com.fasterxml.jackson.databind.ObjectMapper;
5 import org.mockserver.integration.ClientAndServer;
6 import org.mockserver.matchers.Times;
7 import org.mockserver.model.HttpRequest;
8 import org.mockserver.model.HttpResponse;
9 import static org.mockserver.model.HttpRequest.request;
10 import static org.mockserver.model.HttpResponse.response;
11
12 import org.mockserver.model.JsonBody;
13 import org.onap.simulator.errorHandling.VidSimulatorException;
14 import org.onap.simulator.model.SimulatorRequestResponseExpectation;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import org.springframework.core.io.ClassPathResource;
18 import org.springframework.core.io.Resource;
19 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
20 import org.springframework.core.io.support.PropertiesLoaderUtils;
21 import org.springframework.core.io.support.ResourcePatternResolver;
22 import org.springframework.http.HttpStatus;
23 import org.springframework.http.ResponseEntity;
24 import org.springframework.stereotype.Component;
25 import org.springframework.web.bind.annotation.*;
26 import org.springframework.web.servlet.HandlerMapping;
27 import org.springframework.web.servlet.View;
28
29 import javax.annotation.PostConstruct;
30 import javax.annotation.PreDestroy;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33 import java.io.*;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.util.*;
38 import java.io.UnsupportedEncodingException;
39 import java.net.URLEncoder;
40 import java.util.stream.Collectors;
41
42 import static org.mockserver.integration.ClientAndServer.startClientAndServer;
43 import static org.mockserver.matchers.Times.exactly;
44
45 @RestController
46 @Component
47 public class SimulatorController {
48
49     private static final Times DEFAULT_NUMBER_OF_TIMES = Times.unlimited();
50     private ClientAndServer mockServer;
51     private String mockServerProtocol;
52     private String mockServerHost;
53     private Integer mockServerPort;
54     private Boolean enablePresetRegistration;
55     private volatile boolean isInitialized = false;
56
57
58     Logger logger = LoggerFactory.getLogger(SimulatorController.class);
59
60     @PostConstruct
61     public void init(){
62         logger.info("Starting VID Simulator....");
63         setProperties();
64         mockServer = startClientAndServer(mockServerPort);
65         presetRegister();
66         isInitialized = true;
67         logger.info("VID Simulator started successfully");
68     }
69
70     @PreDestroy
71     public void tearDown(){
72         logger.info("Stopping VID Simulator....");
73         isInitialized = false;
74         mockServer.stop();
75     }
76
77
78     private void presetRegister() {
79         //Checking if set
80         if (enablePresetRegistration == null || !enablePresetRegistration){
81             logger.info("Preset registration property is false or not set - skipping preset registration...");
82             return;
83         }
84         ClassLoader cl = this.getClass().getClassLoader();
85         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
86         List<Path> resources = new ArrayList<>();
87         try {
88             File presetDir = resolver.getResource("/preset_registration/").getFile();
89             if (presetDir.exists() && presetDir.isDirectory()) {
90                 resources = Files.walk(Paths.get(presetDir.getPath()))
91                         .filter(p -> p.toString().endsWith(".json"))
92                         .collect(Collectors.toList());
93             } else {
94                 logger.error("preset_registration directory is not exists");
95             }
96         } catch (IOException e) {
97             logger.error("Error performing preset registration, error: ", e);
98             return;
99         }
100         logger.info("Starting preset registrations, number of requests: {}", resources.size());
101         for (Path resource: resources){
102             String content;
103             try {
104                 content = new Scanner(resource).useDelimiter("\\Z").next();
105             } catch (IOException e){
106                 logger.error("Error reading preset registration file {}, skipping to next one. Error: ", resource.getFileName(), e);
107                 continue;
108             }
109             //register the preset request
110             try {
111                 register(content);
112             } catch (VidSimulatorException e) {
113                 logger.error("Error proceeding preset registration file {},skipping to next one. Check if the JSON is in correct format. Error: ", resource.getFileName(), e);
114             }
115         }
116     }
117
118
119
120     private void setProperties() {
121         Resource resource = new ClassPathResource("simulator.properties");
122         Properties props = new Properties();
123         try {
124             props = PropertiesLoaderUtils.loadProperties(resource);
125         } catch (IOException e) {
126             logger.error("Error loading simulator properties, error: ", e);
127             return;
128         }
129         logger.info("Simulator properties are {}", props);
130         mockServerProtocol = (String)props.get("simulator.mockserver.protocol");
131         mockServerHost = (String)props.get("simulator.mockserver.host");
132         mockServerPort = Integer.parseInt((String)props.get("simulator.mockserver.port"));
133         enablePresetRegistration = Boolean.parseBoolean((String)props.get("simulator.enablePresetRegistration"));
134     }
135
136     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.POST)
137     public @ResponseBody
138     ResponseEntity registerRequest(HttpServletRequest request, @RequestBody String expectation) {
139         try {
140             register(expectation);
141         } catch (VidSimulatorException e) {
142             return new ResponseEntity<>("Registration failure! Error: "+e.getMessage(),HttpStatus.BAD_REQUEST);
143         }
144         return new ResponseEntity<>("Registration successful!",HttpStatus.OK);
145     }
146
147     @RequestMapping(value = {"/echo"}, method = RequestMethod.GET)
148     ResponseEntity echo(HttpServletRequest request) {
149         return isInitialized ? new ResponseEntity<>("",HttpStatus.OK) : new ResponseEntity<>("",HttpStatus.SERVICE_UNAVAILABLE);
150     }
151
152 //    @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.GET)
153 //    public ResponseEntity<String> getAllRegisteredRequests() throws JsonProcessingException {
154 //        final Expectation[] expectations = mockServer.retrieveExistingExpectations(null);
155 //        return new ResponseEntity<>(new ObjectMapper()
156 //                .configure(SerializationFeature.INDENT_OUTPUT, true)
157 //                .writeValueAsString(expectations), HttpStatus.OK);
158 //    }
159
160     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.DELETE)
161     @ResponseStatus(value = HttpStatus.OK)
162     public void wipeOutAllExpectations() {
163         mockServer.reset();
164     }
165
166     private void register(String expectation) throws VidSimulatorException{
167         ObjectMapper mapper = new ObjectMapper()
168                 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
169
170         SimulatorRequestResponseExpectation[] expectationModels;
171         try {
172             expectationModels = mapper.readValue(expectation, SimulatorRequestResponseExpectation[].class);
173         } catch (IOException e) {
174             logger.error("Couldn't deserialize register expectation {}, error:", expectation, e);
175             throw new VidSimulatorException(e.getMessage());
176         }
177
178         for (SimulatorRequestResponseExpectation expectationModel : expectationModels) {
179             logger.info("Proceeding registration request: {}", expectationModel);
180             register(expectationModel);
181         }
182     }
183
184
185     @RequestMapping(value = {"/**"})
186     public String redirectToMockServer(HttpServletRequest request, HttpServletResponse response) {
187         //Currently, the easiest logic is redirecting
188
189         //This is needed to allow POST redirect - see http://www.baeldung.com/spring-redirect-and-forward
190         request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
191
192         //Building the redirect URL
193         String restOfTheUrl = (String) request.getAttribute(
194                 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
195
196         //TODO encode only characters like spaces, not slashes
197        /* try {
198             restOfTheUrl = URLEncoder.encode(restOfTheUrl, "UTF-8");
199             restOfTheUrl = restOfTheUrl.replaceAll("%2F", "/");
200         } catch (UnsupportedEncodingException e) {
201             e.printStackTrace();
202         }*/
203
204         StringBuilder sb = new StringBuilder();
205         sb.append(mockServerProtocol+"://"+mockServerHost+":"+mockServerPort+"/"+restOfTheUrl);
206         String queryString = request.getQueryString();
207         if (queryString != null){
208             sb.append("?").append(queryString);
209         }
210         String redirectUrl = sb.toString();
211         logger.info("Redirecting the request to : {}", redirectUrl);
212         return ("redirect:"+redirectUrl);
213
214         //This was a try to setup a proxy instead of redirect
215         //Abandoned this direction when trying to return the original HTTP error code which was registered to mock server,  instead of wrapped up HTTP 500.
216
217        /* String restOfTheUrl = "/"+(String) request.getAttribute(
218                 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
219         URI uri = null;
220         try {
221             uri = new URI("http", null, "localhost", 1080, restOfTheUrl, request.getQueryString(), null);
222         } catch (URISyntaxException e) {
223             logger.error("Error during proxying request {}, error: ", request.getRequestURI(), e.getMessage());
224             return new ResponseEntity(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
225         }
226         RestTemplate restTemplate = new RestTemplate();
227         //Preparing the headers
228         HttpHeaders headers = new HttpHeaders();
229         Enumeration<String> headerNames =  request.getHeaderNames();
230         while (headerNames.hasMoreElements()){
231             String headerToSet = headerNames.nextElement();
232             headers.set(headerToSet, request.getHeader(headerToSet));
233         }
234
235         ResponseEntity<String> responseEntity =
236                 restTemplate.exchange(uri, HttpMethod.resolve(request.getMethod()), new HttpEntity<String>(body, headers), String.class);
237         
238         return responseEntity;*/
239     }
240
241     private void register(SimulatorRequestResponseExpectation expectationModel) throws VidSimulatorException{
242         //Setting request according to what is passed
243         HttpRequest request = HttpRequest.request();
244         String id = expectationModel.getSimulatorRequest().getId();
245         if (id != null) {
246             request.withHeader("x-simulator-id", id);
247         }
248         String method = expectationModel.getSimulatorRequest().getMethod();
249         if (method != null) {
250             request.withMethod(method);
251         }
252         String path = expectationModel.getSimulatorRequest().getPath();
253         if (path != null) {
254             request.withPath(path);
255         }
256         String body = expectationModel.getSimulatorRequest().getBody();
257         if (body != null) {
258             request.withBody(new JsonBody(body));
259         }
260
261         //Queryparams
262         final Map<String, List<String>> queryParams = expectationModel.getSimulatorRequest().getQueryParams();
263         if (queryParams != null){
264             String[] arr = new String[0];
265             queryParams.entrySet().stream().forEach(x -> {
266                 request.withQueryStringParameter(x.getKey(), x.getValue().toArray(arr));
267             });
268         }
269
270         //Setting response according to what is passed
271         HttpResponse response = HttpResponse.response();
272         Integer responseCode = expectationModel.getSimulatorResponse().getResponseCode();
273         if (responseCode != null) {
274             response.withStatusCode(responseCode);
275         } else {
276             logger.error("Invalid registration - response code cannot be empty");
277             throw new VidSimulatorException("Invalid registration - response code cannot be empty");
278         }
279
280         String respBody = expectationModel.getSimulatorResponse().getBody();
281         if (respBody != null) {
282             response.withBody(respBody);
283         }
284
285         String file = expectationModel.getSimulatorResponse().getFile();
286         if (file != null) {
287             response.withBody(loadFileString(file));
288         }
289
290         Map<String, String> responseHeaders = expectationModel.getSimulatorResponse().getResponseHeaders();
291         if (responseHeaders != null) {
292             responseHeaders.forEach(response::withHeader);
293         }
294
295         Times numberOfTimes = getExpectationNumberOfTimes(expectationModel);
296
297         if (expectationModel.getMisc().getReplace()) {
298             logger.info("Unregistering request expectation, if previously set, request: {}", expectationModel.getSimulatorRequest());
299             mockServer.clear(request);
300         }
301
302         mockServer
303                 .when(request, numberOfTimes).respond(response);
304     }
305
306
307     private byte[] loadFileString(String filePath) {
308         byte[] bytes = null;
309         try {
310             File file = new ClassPathResource("download_files/" + filePath).getFile();
311             bytes = new byte[(int)file.length()];
312             DataInputStream dataInputStream = null;
313
314             dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.getPath())));
315             dataInputStream.readFully(bytes);
316             dataInputStream.close();
317         } catch (FileNotFoundException e) {
318             logger.error("File not found for file:" + filePath);
319             e.printStackTrace();
320         } catch (IOException e) {
321             logger.error("Error reading file:" + filePath);
322             e.printStackTrace();
323         }
324
325         return bytes;
326     }
327     private Times getExpectationNumberOfTimes(SimulatorRequestResponseExpectation expectationModel) {
328         Integer expectationModelNumberOfTimes = expectationModel.getMisc().getNumberOfTimes();
329         Times effectiveNumberOfTimes;
330         if (expectationModelNumberOfTimes == null || expectationModelNumberOfTimes < 0) {
331             effectiveNumberOfTimes = DEFAULT_NUMBER_OF_TIMES;
332         } else {
333             effectiveNumberOfTimes = exactly(expectationModelNumberOfTimes);
334         }
335         return effectiveNumberOfTimes;
336     }
337 }