1 package org.onap.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;
10 import org.mockserver.model.JsonBody;
11 import org.onap.simulator.errorHandling.VidSimulatorException;
12 import org.onap.simulator.model.SimulatorRequestResponseExpectation;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15 import org.springframework.core.io.ClassPathResource;
16 import org.springframework.core.io.Resource;
17 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
18 import org.springframework.core.io.support.PropertiesLoaderUtils;
19 import org.springframework.core.io.support.ResourcePatternResolver;
20 import org.springframework.http.HttpStatus;
21 import org.springframework.http.ResponseEntity;
22 import org.springframework.stereotype.Component;
23 import org.springframework.web.bind.annotation.*;
24 import org.springframework.web.servlet.HandlerMapping;
25 import org.springframework.web.servlet.View;
27 import javax.annotation.PostConstruct;
28 import javax.annotation.PreDestroy;
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
36 import java.util.stream.Collectors;
38 import static org.mockserver.integration.ClientAndServer.startClientAndServer;
39 import static org.mockserver.matchers.Times.exactly;
43 public class SimulatorController {
45 private static final Times DEFAULT_NUMBER_OF_TIMES = Times.unlimited();
46 private ClientAndServer mockServer;
47 private String mockServerProtocol;
48 private String mockServerHost;
49 private Integer mockServerPort;
50 private Boolean enablePresetRegistration;
51 private volatile boolean isInitialized = false;
53 Logger logger = LoggerFactory.getLogger(SimulatorController.class);
57 logger.info("Starting VID Simulator....");
59 mockServer = startClientAndServer(mockServerPort);
62 logger.info("VID Simulator started successfully");
66 public void tearDown(){
67 logger.info("Stopping VID Simulator....");
68 isInitialized = false;
73 private void presetRegister() {
75 if (enablePresetRegistration == null || !enablePresetRegistration){
76 logger.info("Preset registration property is false or not set - skipping preset registration...");
79 ClassLoader cl = this.getClass().getClassLoader();
80 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
81 List<Path> resources = new ArrayList<>();
83 File presetDir = resolver.getResource("/preset_registration/").getFile();
84 if (presetDir.exists() && presetDir.isDirectory()) {
85 resources = Files.walk(Paths.get(presetDir.getPath()))
86 .filter(p -> p.toString().endsWith(".json"))
87 .collect(Collectors.toList());
89 logger.error("preset_registration directory is not exists");
91 } catch (IOException e) {
92 logger.error("Error performing preset registration, error: ", e);
95 logger.info("Starting preset registrations, number of requests: {}", resources.size());
96 for (Path resource: resources){
99 content = new Scanner(resource).useDelimiter("\\Z").next();
100 } catch (IOException e){
101 logger.error("Error reading preset registration file {}, skipping to next one. Error: ", resource.getFileName(), e);
104 //register the preset request
107 } catch (VidSimulatorException e) {
108 logger.error("Error proceeding preset registration file {},skipping to next one. Check if the JSON is in correct format. Error: ", resource.getFileName(), e);
115 private void setProperties() {
116 Resource resource = new ClassPathResource("simulator.properties");
117 Properties props = new Properties();
119 props = PropertiesLoaderUtils.loadProperties(resource);
120 } catch (IOException e) {
121 logger.error("Error loading simulator properties, error: ", e);
124 logger.info("Simulator properties are {}", props);
125 mockServerProtocol = (String)props.get("simulator.mockserver.protocol");
126 mockServerHost = (String)props.get("simulator.mockserver.host");
127 mockServerPort = Integer.parseInt((String)props.get("simulator.mockserver.port"));
128 enablePresetRegistration = Boolean.parseBoolean((String)props.get("simulator.enablePresetRegistration"));
131 @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.POST)
133 ResponseEntity registerRequest(HttpServletRequest request, @RequestBody String expectation) {
135 register(expectation);
136 } catch (VidSimulatorException e) {
137 return new ResponseEntity<>("Registration failure! Error: "+e.getMessage(),HttpStatus.BAD_REQUEST);
139 return new ResponseEntity<>("Registration successful!",HttpStatus.OK);
142 @RequestMapping(value = {"/echo"}, method = RequestMethod.GET)
143 ResponseEntity echo(HttpServletRequest request) {
144 return isInitialized ? new ResponseEntity<>("",HttpStatus.OK) : new ResponseEntity<>("",HttpStatus.SERVICE_UNAVAILABLE);
147 // @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.GET)
148 // public ResponseEntity<String> getAllRegisteredRequests() throws JsonProcessingException {
149 // final Expectation[] expectations = mockServer.retrieveExistingExpectations(null);
150 // return new ResponseEntity<>(new ObjectMapper()
151 // .configure(SerializationFeature.INDENT_OUTPUT, true)
152 // .writeValueAsString(expectations), HttpStatus.OK);
155 @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.DELETE)
156 @ResponseStatus(value = HttpStatus.OK)
157 public void wipeOutAllExpectations() {
161 private void register(String expectation) throws VidSimulatorException{
162 ObjectMapper mapper = new ObjectMapper()
163 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
165 SimulatorRequestResponseExpectation[] expectationModels;
167 expectationModels = mapper.readValue(expectation, SimulatorRequestResponseExpectation[].class);
168 } catch (IOException e) {
169 logger.error("Couldn't deserialize register expectation {}, error:", expectation, e);
170 throw new VidSimulatorException(e.getMessage());
173 for (SimulatorRequestResponseExpectation expectationModel : expectationModels) {
174 logger.info("Proceeding registration request: {}", expectationModel);
175 register(expectationModel);
179 @RequestMapping(value = {"/**"})
180 public String redirectToMockServer(HttpServletRequest request, HttpServletResponse response) {
181 //Currently, the easiest logic is redirecting
183 //This is needed to allow POST redirect - see http://www.baeldung.com/spring-redirect-and-forward
184 request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
186 //Building the redirect URL
187 String restOfTheUrl = (String) request.getAttribute(
188 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
190 //TODO encode only characters like spaces, not slashes
192 restOfTheUrl = URLEncoder.encode(restOfTheUrl, "UTF-8");
193 restOfTheUrl = restOfTheUrl.replaceAll("%2F", "/");
194 } catch (UnsupportedEncodingException e) {
198 StringBuilder sb = new StringBuilder();
199 sb.append(mockServerProtocol+"://"+mockServerHost+":"+mockServerPort+"/"+restOfTheUrl);
200 String queryString = request.getQueryString();
201 if (queryString != null){
202 sb.append("?").append(queryString);
204 String redirectUrl = sb.toString();
205 logger.info("Redirecting the request to : {}", redirectUrl);
206 return ("redirect:"+redirectUrl);
208 //This was a try to setup a proxy instead of redirect
209 //Abandoned this direction when trying to return the original HTTP error code which was registered to mock server, instead of wrapped up HTTP 500.
211 /* String restOfTheUrl = "/"+(String) request.getAttribute(
212 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
215 uri = new URI("http", null, "localhost", 1080, restOfTheUrl, request.getQueryString(), null);
216 } catch (URISyntaxException e) {
217 logger.error("Error during proxying request {}, error: ", request.getRequestURI(), e.getMessage());
218 return new ResponseEntity(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
220 RestTemplate restTemplate = new RestTemplate();
221 //Preparing the headers
222 HttpHeaders headers = new HttpHeaders();
223 Enumeration<String> headerNames = request.getHeaderNames();
224 while (headerNames.hasMoreElements()){
225 String headerToSet = headerNames.nextElement();
226 headers.set(headerToSet, request.getHeader(headerToSet));
229 ResponseEntity<String> responseEntity =
230 restTemplate.exchange(uri, HttpMethod.resolve(request.getMethod()), new HttpEntity<String>(body, headers), String.class);
232 return responseEntity;*/
235 private void register(SimulatorRequestResponseExpectation expectationModel) throws VidSimulatorException{
236 //Setting request according to what is passed
237 HttpRequest request = HttpRequest.request();
238 String id = expectationModel.getSimulatorRequest().getId();
240 request.withHeader("x-simulator-id", id);
242 String method = expectationModel.getSimulatorRequest().getMethod();
243 if (method != null) {
244 request.withMethod(method);
246 String path = expectationModel.getSimulatorRequest().getPath();
248 request.withPath(path);
250 String body = expectationModel.getSimulatorRequest().getBody();
252 request.withBody(new JsonBody(body));
256 final Map<String, List<String>> queryParams = expectationModel.getSimulatorRequest().getQueryParams();
257 if (queryParams != null){
258 String[] arr = new String[0];
259 queryParams.entrySet().stream().forEach(x -> {
260 request.withQueryStringParameter(x.getKey(), x.getValue().toArray(arr));
264 //Setting response according to what is passed
265 HttpResponse response = HttpResponse.response();
266 Integer responseCode = expectationModel.getSimulatorResponse().getResponseCode();
267 if (responseCode != null) {
268 response.withStatusCode(responseCode);
270 logger.error("Invalid registration - response code cannot be empty");
271 throw new VidSimulatorException("Invalid registration - response code cannot be empty");
274 String respBody = expectationModel.getSimulatorResponse().getBody();
275 if (respBody != null) {
276 response.withBody(respBody);
279 String file = expectationModel.getSimulatorResponse().getFile();
281 response.withBody(loadFileString(file));
284 Map<String, String> responseHeaders = expectationModel.getSimulatorResponse().getResponseHeaders();
285 if (responseHeaders != null) {
286 responseHeaders.forEach(response::withHeader);
289 Times numberOfTimes = getExpectationNumberOfTimes(expectationModel);
291 if (expectationModel.getMisc().getReplace()) {
292 logger.info("Unregistering request expectation, if previously set, request: {}", expectationModel.getSimulatorRequest());
293 mockServer.clear(request);
297 .when(request, numberOfTimes).respond(response);
301 private byte[] loadFileString(String filePath) {
304 File file = new ClassPathResource("download_files/" + filePath).getFile();
305 bytes = new byte[(int)file.length()];
306 DataInputStream dataInputStream = null;
308 dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.getPath())));
309 dataInputStream.readFully(bytes);
310 dataInputStream.close();
311 } catch (FileNotFoundException e) {
312 logger.error("File not found for file:" + filePath);
314 } catch (IOException e) {
315 logger.error("Error reading file:" + filePath);
321 private Times getExpectationNumberOfTimes(SimulatorRequestResponseExpectation expectationModel) {
322 Integer expectationModelNumberOfTimes = expectationModel.getMisc().getNumberOfTimes();
323 Times effectiveNumberOfTimes;
324 if (expectationModelNumberOfTimes == null || expectationModelNumberOfTimes < 0) {
325 effectiveNumberOfTimes = DEFAULT_NUMBER_OF_TIMES;
327 effectiveNumberOfTimes = exactly(expectationModelNumberOfTimes);
329 return effectiveNumberOfTimes;