1 package org.openecomp.simulator.controller;
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;
12 import org.mockserver.model.JsonBody;
13 import org.openecomp.simulator.errorHandling.VidSimulatorException;
14 import org.openecomp.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;
29 import javax.annotation.PostConstruct;
30 import javax.annotation.PreDestroy;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
38 import java.io.UnsupportedEncodingException;
39 import java.net.URLEncoder;
40 import java.util.stream.Collectors;
42 import static org.mockserver.integration.ClientAndServer.startClientAndServer;
43 import static org.mockserver.matchers.Times.exactly;
47 public class SimulatorController {
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;
58 Logger logger = LoggerFactory.getLogger(SimulatorController.class);
62 logger.info("Starting VID Simulator....");
64 mockServer = startClientAndServer(mockServerPort);
67 logger.info("VID Simulator started successfully");
71 public void tearDown(){
72 logger.info("Stopping VID Simulator....");
73 isInitialized = false;
78 private void presetRegister() {
80 if (enablePresetRegistration == null || !enablePresetRegistration){
81 logger.info("Preset registration property is false or not set - skipping preset registration...");
84 ClassLoader cl = this.getClass().getClassLoader();
85 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
86 List<Path> resources = new ArrayList<>();
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());
94 logger.error("preset_registration directory is not exists");
96 } catch (IOException e) {
97 logger.error("Error performing preset registration, error: ", e);
100 logger.info("Starting preset registrations, number of requests: {}", resources.size());
101 for (Path resource: resources){
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);
109 //register the preset request
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);
120 private void setProperties() {
121 Resource resource = new ClassPathResource("simulator.properties");
122 Properties props = new Properties();
124 props = PropertiesLoaderUtils.loadProperties(resource);
125 } catch (IOException e) {
126 logger.error("Error loading simulator properties, error: ", e);
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"));
136 @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.POST)
138 ResponseEntity registerRequest(HttpServletRequest request, @RequestBody String expectation) {
140 register(expectation);
141 } catch (VidSimulatorException e) {
142 return new ResponseEntity<>("Registration failure! Error: "+e.getMessage(),HttpStatus.BAD_REQUEST);
144 return new ResponseEntity<>("Registration successful!",HttpStatus.OK);
147 @RequestMapping(value = {"/echo"}, method = RequestMethod.GET)
148 ResponseEntity echo(HttpServletRequest request) {
149 return isInitialized ? new ResponseEntity<>("",HttpStatus.OK) : new ResponseEntity<>("",HttpStatus.SERVICE_UNAVAILABLE);
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);
160 @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.DELETE)
161 @ResponseStatus(value = HttpStatus.OK)
162 public void wipeOutAllExpectations() {
166 private void register(String expectation) throws VidSimulatorException{
167 ObjectMapper mapper = new ObjectMapper()
168 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
170 SimulatorRequestResponseExpectation[] expectationModels;
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());
178 for (SimulatorRequestResponseExpectation expectationModel : expectationModels) {
179 logger.info("Proceeding registration request: {}", expectationModel);
180 register(expectationModel);
185 @RequestMapping(value = {"/**"})
186 public String redirectToMockServer(HttpServletRequest request, HttpServletResponse response) {
187 //Currently, the easiest logic is redirecting
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);
192 //Building the redirect URL
193 String restOfTheUrl = (String) request.getAttribute(
194 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
196 //TODO encode only characters like spaces, not slashes
198 restOfTheUrl = URLEncoder.encode(restOfTheUrl, "UTF-8");
199 restOfTheUrl = restOfTheUrl.replaceAll("%2F", "/");
200 } catch (UnsupportedEncodingException e) {
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);
210 String redirectUrl = sb.toString();
211 logger.info("Redirecting the request to : {}", redirectUrl);
212 return ("redirect:"+redirectUrl);
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.
217 /* String restOfTheUrl = "/"+(String) request.getAttribute(
218 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
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);
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));
235 ResponseEntity<String> responseEntity =
236 restTemplate.exchange(uri, HttpMethod.resolve(request.getMethod()), new HttpEntity<String>(body, headers), String.class);
238 return responseEntity;*/
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();
246 request.withHeader("x-simulator-id", id);
248 String method = expectationModel.getSimulatorRequest().getMethod();
249 if (method != null) {
250 request.withMethod(method);
252 String path = expectationModel.getSimulatorRequest().getPath();
254 request.withPath(path);
256 String body = expectationModel.getSimulatorRequest().getBody();
258 request.withBody(new JsonBody(body));
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));
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);
276 logger.error("Invalid registration - response code cannot be empty");
277 throw new VidSimulatorException("Invalid registration - response code cannot be empty");
280 String respBody = expectationModel.getSimulatorResponse().getBody();
281 if (respBody != null) {
282 response.withBody(respBody);
285 String file = expectationModel.getSimulatorResponse().getFile();
287 response.withBody(loadFileString(file));
290 Map<String, String> responseHeaders = expectationModel.getSimulatorResponse().getResponseHeaders();
291 if (responseHeaders != null) {
292 responseHeaders.forEach(response::withHeader);
295 Times numberOfTimes = getExpectationNumberOfTimes(expectationModel);
297 if (expectationModel.getMisc().getReplace()) {
298 logger.info("Unregistering request expectation, if previously set, request: {}", expectationModel.getSimulatorRequest());
299 mockServer.clear(request);
303 .when(request, numberOfTimes).respond(response);
307 private byte[] loadFileString(String filePath) {
310 File file = new ClassPathResource("download_files/" + filePath).getFile();
311 bytes = new byte[(int)file.length()];
312 DataInputStream dataInputStream = null;
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);
320 } catch (IOException e) {
321 logger.error("Error reading file:" + filePath);
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;
333 effectiveNumberOfTimes = exactly(expectationModelNumberOfTimes);
335 return effectiveNumberOfTimes;