| /********************************************************************************* |
| * Copyright (c) 2020 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| ******************************************************************************** |
| */ |
| package org.eclipse.app4mc.validation.cloud.http; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.http.Part; |
| |
| import org.eclipse.app4mc.amalthea.model.Amalthea; |
| import org.eclipse.app4mc.amalthea.model.io.AmaltheaLoader; |
| import org.eclipse.app4mc.validation.core.IProfile; |
| import org.eclipse.app4mc.validation.util.ProfileManager; |
| import org.eclipse.app4mc.validation.util.ValidationExecutor; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ServiceScope; |
| |
| import com.fasterxml.jackson.core.JsonProcessingException; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| |
| @Component( |
| service=Servlet.class, |
| property= { |
| "osgi.http.whiteboard.servlet.pattern=/app4mc/validation/*", |
| "osgi.http.whiteboard.servlet.multipart.enabled=true" |
| }, |
| scope=ServiceScope.PROTOTYPE) |
| public class ValidationServlet extends HttpServlet { |
| |
| private static final long serialVersionUID = 1L; |
| |
| private static final String TEMP_DIR_PREFIX = "app4mc_validation_"; |
| |
| private final String defaultBaseDir = System.getProperty("java.io.tmpdir"); |
| |
| @Reference |
| private ProfileManager manager; |
| |
| private String[] validatePath(String pathInfo) { |
| // request to /app4mc/validation |
| if (pathInfo == null || pathInfo.equals("/")){ |
| return null; |
| } |
| |
| String[] splitPath = pathInfo.split("/"); |
| |
| if (splitPath.length > 3) { |
| return null; |
| } |
| |
| return splitPath; |
| } |
| |
| // POST /app4mc/validation/upload |
| |
| @Override |
| protected void doPost(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| String[] splitPath = validatePath(request.getPathInfo()); |
| |
| if (splitPath == null || splitPath.length != 2) { |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid path"); |
| return; |
| } |
| |
| if ("upload".equals(splitPath[1])){ |
| Part part = request.getPart("file"); |
| if (part != null) { |
| String filename = part.getSubmittedFileName(); |
| try (InputStream is = part.getInputStream()) { |
| Path path = Files.createTempDirectory(TEMP_DIR_PREFIX); |
| |
| // extract uuid from pathname |
| String uuid = path.toString().substring(path.toString().lastIndexOf('_') + 1); |
| |
| Files.copy(is, Paths.get(path.toString(), filename)); |
| |
| // return uuid |
| response.setContentType("text/html"); |
| response.getWriter().write(uuid); |
| return; |
| } |
| } |
| } |
| |
| // no content |
| response.setStatus(HttpServletResponse.SC_NO_CONTENT); |
| return; |
| } |
| |
| // PUT /app4mc/validation/{id}/validate |
| |
| @Override |
| protected void doPut(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| String[] splitPath = validatePath(request.getPathInfo()); |
| |
| if (splitPath == null || splitPath.length != 3) { |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid path"); |
| return; |
| } |
| |
| String uuid = splitPath[1]; |
| String action = splitPath[2]; |
| |
| if ("validate".equals(action)) { |
| // trigger validation |
| Path tempFolderPath = Paths.get(defaultBaseDir, TEMP_DIR_PREFIX + uuid); |
| List<Path> modelFilePaths = |
| Files.find(tempFolderPath, 1, (path, attrs) -> path.toString().endsWith(".amxmi")) |
| .collect(Collectors.toList()); |
| |
| if (modelFilePaths.isEmpty()) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, "No model file uploaded!"); |
| return; |
| } |
| |
| Path modelFilePath = modelFilePaths.get(0); |
| |
| // load uploaded model file |
| Amalthea model = AmaltheaLoader.loadFromFile(modelFilePath.toFile()); |
| |
| if (model == null) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, "Error: No model loaded!"); |
| return; |
| } |
| |
| // get profile selection out of request |
| String[] profiles = request.getParameterValues("profiles"); |
| List<String> selectedProfiles = profiles != null ? Arrays.asList(profiles) : Arrays.asList("Amalthea Standard Validations"); |
| |
| // get selected profiles from profile manager |
| List<Class<? extends IProfile>> profileList = manager.getRegisteredValidationProfiles().values().stream() |
| .filter(profile -> selectedProfiles.contains(profile.getName())) |
| .map(profile -> profile.getProfileClass()) |
| .collect(Collectors.toList()); |
| |
| // TODO execute in background thread and introduce status resource? |
| ValidationExecutor executor = new ValidationExecutor(profileList); |
| executor.validate(model); |
| |
| // dump validation result to file |
| Path resultFile = Files.createFile(Paths.get(tempFolderPath.toString(), "validation-results.txt")); |
| try (PrintStream print = new PrintStream(new FileOutputStream(resultFile.toFile()))) { |
| executor.dumpResultMap(print); |
| } |
| |
| boolean failed = executor.getResults().stream() |
| .anyMatch(diag -> diag.getSeverity() == org.eclipse.emf.common.util.Diagnostic.ERROR); |
| if (!failed) { |
| response.setStatus(HttpServletResponse.SC_OK); |
| } else { |
| response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); |
| } |
| return; |
| } |
| |
| // no content |
| response.setStatus(HttpServletResponse.SC_NO_CONTENT); |
| return; |
| } |
| |
| // GET /app4mc/validation/profiles |
| // GET /app4mc/validation/{id}/download |
| |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| String[] splitPath = validatePath(request.getPathInfo()); |
| |
| if (splitPath == null) { |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid path"); |
| return; |
| } |
| |
| if (splitPath.length == 2 && "profiles".equals(splitPath[1])) { |
| response.setContentType("application/json"); |
| ObjectMapper objectMapper = new ObjectMapper(); |
| try { |
| List<String> availableProfiles = manager.getRegisteredValidationProfiles().values().stream() |
| .sorted((p1, p2) -> p1.getName().compareTo(p2.getName())) |
| .map(profile -> profile.getName()) |
| .collect(Collectors.toList()); |
| String json = objectMapper.writeValueAsString(availableProfiles); |
| response.getWriter().write(json); |
| } catch (JsonProcessingException e) { |
| response.getWriter().write(objectMapper.writeValueAsString(e)); |
| } |
| |
| return; |
| } else if (splitPath.length == 3 && "download".equals(splitPath[2])) { |
| Path path = Paths.get(defaultBaseDir, TEMP_DIR_PREFIX + splitPath[1], "validation-results.txt"); |
| |
| response.setContentType("text/plain"); |
| response.setHeader("Content-Disposition","attachment; filename=validation-results.txt"); |
| |
| try (InputStream in = Files.newInputStream(path); |
| OutputStream out = response.getOutputStream()) { |
| |
| byte[] buffer = new byte[4096]; |
| |
| int numBytesRead; |
| while ((numBytesRead = in.read(buffer)) > 0) { |
| out.write(buffer, 0, numBytesRead); |
| } |
| } |
| } |
| |
| // no content |
| response.setStatus(HttpServletResponse.SC_NO_CONTENT); |
| return; |
| } |
| |
| // DELETE /app4mc/validation/{id}/delete |
| |
| @Override |
| protected void doDelete(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| String[] splitPath = validatePath(request.getPathInfo()); |
| |
| if (splitPath == null || splitPath.length != 3) { |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid path"); |
| return; |
| } |
| |
| String uuid = splitPath[1]; |
| String action = splitPath[2]; |
| |
| if ("delete".equals(action)) { |
| Path path = Paths.get(defaultBaseDir, TEMP_DIR_PREFIX + uuid); |
| |
| Files.walk(path) |
| .sorted(Comparator.reverseOrder()) |
| .map(Path::toFile) |
| .forEach(File::delete); |
| |
| response.setStatus(HttpServletResponse.SC_OK); |
| return; |
| } |
| |
| // no content |
| response.setStatus(HttpServletResponse.SC_NO_CONTENT); |
| return; |
| } |
| } |