Loading de.ugoe.cs.swe.T3Q/src/de/ugoe/cs/swe/T3Q/HttpFileServer.java 0 → 100644 +499 −0 Original line number Diff line number Diff line package de.ugoe.cs.swe.T3Q; import com.sun.net.httpserver.*; import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; import java.util.concurrent.*; public class HttpFileServer { //TODO: experimental pure java boilerplate to handle uploads private static final String TEMP_DIR_PREFIX = "fileserver_"; private final HttpServer server; private final ExecutorService executor; private final FileProcessor processor; public HttpFileServer(int port, FileProcessor processor) throws IOException { this.server = HttpServer.create(new InetSocketAddress(port), 0); this.executor = Executors.newFixedThreadPool(10); this.processor = processor; this.server.setExecutor(executor); // Register upload endpoint server.createContext("/compile_asn", new UploadHandler(processor)); server.createContext("/health", new HealthHandler()); } public void start() { server.start(); System.out.println("Server started on port " + server.getAddress().getPort()); } public void stop() { server.stop(0); executor.shutdown(); } /** * Interface for processing uploaded files */ public interface FileProcessor { /** * Process uploaded files and return path to resulting JSON file * @param files Map of field names to temporary file paths * @param fields Additional form fields from the request * @return Path to the generated JSON file */ Path process(Map<String, Path> files, Map<String, String> fields) throws Exception; } /** * Handler for file uploads */ static class UploadHandler implements HttpHandler { private final FileProcessor processor; public UploadHandler(FileProcessor processor) { this.processor = processor; } @Override public void handle(HttpExchange exchange) throws IOException { if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) { sendError(exchange, 405, "Method not allowed"); return; } Path tempDir = null; try { // Create temporary directory for uploaded files tempDir = Files.createTempDirectory(TEMP_DIR_PREFIX); // Parse multipart request String contentType = exchange.getRequestHeaders().getFirst("Content-Type"); if (contentType == null || !contentType.startsWith("multipart/form-data")) { sendError(exchange, 400, "Expected multipart/form-data"); return; } String boundary = extractBoundary(contentType); if (boundary == null) { sendError(exchange, 400, "No boundary found in Content-Type"); return; } MultipartParser parser = new MultipartParser(exchange.getRequestBody(), boundary, tempDir); MultipartData data = parser.parse(); if (data.files.isEmpty()) { sendError(exchange, 400, "No files uploaded"); return; } // Process files with external command Path jsonResult = processor.process(data.files, data.fields); if (jsonResult == null || !Files.exists(jsonResult)) { sendError(exchange, 500, "Processing failed to produce result"); return; } // Send JSON response byte[] jsonBytes = Files.readAllBytes(jsonResult); exchange.getResponseHeaders().set("Content-Type", "application/json"); exchange.sendResponseHeaders(200, jsonBytes.length); try (OutputStream os = exchange.getResponseBody()) { os.write(jsonBytes); } // Cleanup result file Files.deleteIfExists(jsonResult); } catch (Exception e) { e.printStackTrace(); sendError(exchange, 500, "Processing error: " + e.getMessage()); } finally { // Cleanup temp directory if (tempDir != null) { cleanupDirectory(tempDir); } } } private String extractBoundary(String contentType) { String[] parts = contentType.split(";"); for (String part : parts) { part = part.trim(); if (part.startsWith("boundary=")) { return part.substring(9); } } return null; } } /** * Health check endpoint */ static class HealthHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { String response = "{\"status\":\"ok\"}"; exchange.getResponseHeaders().set("Content-Type", "application/json"); exchange.sendResponseHeaders(200, response.length()); try (OutputStream os = exchange.getResponseBody()) { os.write(response.getBytes(StandardCharsets.UTF_8)); } } } /** * Multipart form data parser */ static class MultipartParser { private final InputStream input; private final String boundary; private final Path tempDir; public MultipartParser(InputStream input, String boundary, Path tempDir) { this.input = input; this.boundary = boundary; this.tempDir = tempDir; } public MultipartData parse() throws IOException { MultipartData data = new MultipartData(); BufferedInputStream bis = new BufferedInputStream(input); System.out.println("DEBUG: Starting parse, boundary=" + boundary); // Skip preamble and find first boundary (starts with --boundary, not \r\n--boundary) String firstBoundary = "--" + boundary; if (!findBoundary(bis, firstBoundary.getBytes(StandardCharsets.UTF_8))) { System.out.println("DEBUG: Could not find first boundary"); return data; } System.out.println("DEBUG: Found first boundary"); while (true) { // Check if this is the end boundary (--boundary--) bis.mark(4); int c1 = bis.read(); int c2 = bis.read(); System.out.println("DEBUG: After boundary, next chars: '" + (char)c1 + "' '" + (char)c2 + "'"); if (c1 == '-' && c2 == '-') { // End of multipart System.out.println("DEBUG: Found end boundary"); break; } bis.reset(); // Skip CRLF after boundary skipLine(bis); // Read headers Map<String, String> headers = readHeaders(bis); System.out.println("DEBUG: Read headers: " + headers); if (headers.isEmpty()) { System.out.println("DEBUG: No headers found, ending parse"); break; } // Read part data until next boundary byte[] partData = readUntilBoundary(bis); System.out.println("DEBUG: Read part data: " + partData.length + " bytes"); // Process this part processPart(headers, partData, data); System.out.println("DEBUG: Files so far: " + data.files.size() + ", Fields: " + data.fields.size()); } System.out.println("DEBUG: Parse complete. Files: " + data.files.size() + ", Fields: " + data.fields.size()); return data; } private boolean findBoundary(InputStream is, byte[] boundaryBytes) throws IOException { List<Integer> window = new ArrayList<>(); int b; while ((b = is.read()) != -1) { window.add(b); // Keep window size equal to boundary length if (window.size() > boundaryBytes.length) { window.remove(0); } // Check if window matches boundary if (window.size() == boundaryBytes.length) { boolean match = true; for (int i = 0; i < boundaryBytes.length; i++) { if (window.get(i) != (boundaryBytes[i] & 0xFF)) { match = false; break; } } if (match) { return true; } } } return false; } private byte[] readUntilBoundary(InputStream is) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); String boundaryMarker = "\r\n--" + boundary; byte[] boundaryBytes = boundaryMarker.getBytes(StandardCharsets.UTF_8); List<Integer> window = new ArrayList<>(); int b; while ((b = is.read()) != -1) { window.add(b); // Keep window size equal to boundary length if (window.size() > boundaryBytes.length) { buffer.write(window.remove(0)); } // Check if window matches boundary if (window.size() == boundaryBytes.length) { boolean match = true; for (int i = 0; i < boundaryBytes.length; i++) { if (window.get(i) != (boundaryBytes[i] & 0xFF)) { match = false; break; } } if (match) { // Found boundary, return data before it return buffer.toByteArray(); } } } // Flush remaining window for (int val : window) { buffer.write(val); } return buffer.toByteArray(); } private void skipLine(InputStream is) throws IOException { int b; while ((b = is.read()) != -1) { if (b == '\n') { break; } } } private Map<String, String> readHeaders(BufferedInputStream is) throws IOException { Map<String, String> headers = new HashMap<>(); ByteArrayOutputStream lineBuffer = new ByteArrayOutputStream(); int prev = 0; int b; while ((b = is.read()) != -1) { if (b == '\n' && prev == '\r') { // End of line byte[] lineBytes = lineBuffer.toByteArray(); if (lineBytes.length <= 1) { // Empty line marks end of headers break; } // Parse header line (remove trailing \r) String line = new String(lineBytes, 0, lineBytes.length - 1, StandardCharsets.UTF_8); int colon = line.indexOf(':'); if (colon > 0) { String name = line.substring(0, colon).trim(); String value = line.substring(colon + 1).trim(); headers.put(name.toLowerCase(), value); } lineBuffer.reset(); } else { lineBuffer.write(b); } prev = b; } return headers; } private void processPart(Map<String, String> headers, byte[] data, MultipartData result) throws IOException { String disposition = headers.get("content-disposition"); if (disposition == null) return; String name = extractValue(disposition, "name"); String filename = extractValue(disposition, "filename"); if (filename != null && !filename.isEmpty()) { // This is a file Path filePath = tempDir.resolve(filename); Files.write(filePath, data); result.files.put(name, filePath); } else if (name != null) { // This is a regular field result.fields.put(name, new String(data, StandardCharsets.UTF_8).trim()); } } private String extractValue(String header, String key) { int start = header.indexOf(key + "=\""); if (start == -1) return null; start += key.length() + 2; int end = header.indexOf("\"", start); if (end == -1) return null; return header.substring(start, end); } } static class MultipartData { Map<String, Path> files = new HashMap<>(); Map<String, String> fields = new HashMap<>(); } private static void sendError(HttpExchange exchange, int code, String message) throws IOException { String json = String.format("{\"error\":\"%s\"}", message); exchange.getResponseHeaders().set("Content-Type", "application/json"); exchange.sendResponseHeaders(code, json.length()); try (OutputStream os = exchange.getResponseBody()) { os.write(json.getBytes(StandardCharsets.UTF_8)); } } private static void cleanupDirectory(Path dir) { try { Files.walk(dir) .sorted(Comparator.reverseOrder()) .forEach(path -> { try { Files.delete(path); } catch (IOException e) { // Ignore cleanup errors } }); } catch (IOException e) { // Ignore cleanup errors } } // Example: External command processor public static class ExternalCommandProcessor implements FileProcessor { private final String command; private final Path workDir; private List<String> arguments; public ExternalCommandProcessor(String command, List<String> arguments, Path workDir) { this.command = command; this.arguments = arguments; this.workDir = workDir; } @Override public Path process(Map<String, Path> files, Map<String, String> fields) throws Exception { // Build command with file paths as arguments List<String> cmd = new ArrayList<>(); cmd.add(command); cmd.add("--ttcn2json"); // Add file paths as arguments for (Path file : files.values()) { cmd.add(file.toString()); } cmd.add("-"); String jsonFile = "asn.json"; cmd.add(jsonFile); // Execute command ProcessBuilder pb = new ProcessBuilder(cmd); pb.directory(workDir.toFile()); pb.redirectErrorStream(true); Process process = pb.start(); // Read output (assuming the command outputs JSON file path) String output; try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { output = reader.lines() .reduce((a, b) -> a + "\n" + b) .orElse(""); } int exitCode = process.waitFor(); if (exitCode != 0) { throw new RuntimeException("Command failed with exit code " + exitCode + ": " + output); } // The command should output the path to the generated JSON file File wd = workDir.toFile(); String jsonPath = new File(wd, jsonFile).getAbsolutePath(); Path result = Paths.get(jsonPath); if (!Files.exists(result)) { throw new FileNotFoundException("Expected JSON file not found: " + jsonPath); } return result; } } // Example usage public static void main(String[] args) { try { // Example 1: Simple processor that creates a JSON response FileProcessor simpleProcessor = (files, fields) -> { Path jsonFile = Files.createTempFile("result_", ".json"); // Create simple JSON response StringBuilder json = new StringBuilder("{"); json.append("\"filesProcessed\":").append(files.size()).append(","); json.append("\"files\":["); int i = 0; for (Map.Entry<String, Path> entry : files.entrySet()) { if (i++ > 0) json.append(","); json.append("{") .append("\"field\":\"").append(entry.getKey()).append("\",") .append("\"filename\":\"").append(entry.getValue().getFileName()).append("\",") .append("\"size\":").append(Files.size(entry.getValue())) .append("}"); } json.append("]}"); Files.write(jsonFile, json.toString().getBytes(StandardCharsets.UTF_8)); return jsonFile; }; // HttpFileServer server = new HttpFileServer(8080, simpleProcessor); // server.start(); // Example 2: External command processor // Uncomment and modify for your external command /* */ Path workDir = Paths.get("./"); Files.createDirectories(workDir); //TODO: expose as parameter FileProcessor cmdProcessor = new ExternalCommandProcessor( "./compiler", List.of(), workDir ); HttpFileServer server = new HttpFileServer(3005, cmdProcessor); server.start(); System.out.println("Server ready. Test with:"); System.out.println("curl -X POST -F 'file=@test.txt' http://localhost:3005/compile_asn"); } catch (Exception e) { e.printStackTrace(); } } } No newline at end of file de.ugoe.cs.swe.T3Q/src/de/ugoe/cs/swe/T3Q/T3Q.java +50 −1 Original line number Diff line number Diff line Loading @@ -6,10 +6,14 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Scanner; import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; Loading @@ -25,6 +29,8 @@ import org.apache.commons.io.filefilter.RegexFileFilter; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import de.ugoe.cs.swe.T3Q.HttpFileServer.ExternalCommandProcessor; import de.ugoe.cs.swe.T3Q.HttpFileServer.FileProcessor; import de.ugoe.cs.swe.TTCN3Configuration.QualityCheckProfile; import de.ugoe.cs.swe.common.ConfigTools; import de.ugoe.cs.swe.common.MiscTools; Loading Loading @@ -55,6 +61,7 @@ public class T3Q { private boolean analyzeUsage = false; //TODO: expose in configuration? private String schemaFile = "asn.json"; private boolean serveASN1Compiler; // private boolean formattingEnabled = false; Loading Loading @@ -115,6 +122,11 @@ public class T3Q { System.out.println("ERRORING OUT!"); } if (this.isServeASN1Compiler()) { serveASNCompiler(); return; } if (this.isConvertASN1toJSONSchema()) { //TODO: skip if none! convertASN1toJSONSchema(); Loading Loading @@ -276,6 +288,31 @@ public class T3Q { return true; } private void serveASNCompiler() { //TODO: this may need to be temporary.. Path workDir = Paths.get("./"); try { Files.createDirectories(workDir); FileProcessor cmdProcessor = new ExternalCommandProcessor( //DONE: expose as parameter // "./compiler", activeProfile.getTitanCompilerPath(), List.of(), workDir ); //TODO: send titan output as well HttpFileServer server = new HttpFileServer(3005, cmdProcessor); server.start(); System.out.println("Server ready at http://localhost:3005/compile_asn. Test with:"); System.out.println("curl -X POST -F 'file=@test.txt' http://localhost:3005/compile_asn"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private CommandLine parseCommandLineArguments(String[] args) { CommandLineParser parser = new DefaultParser(); T3QOptionsHandler optionsHandler = new T3QOptionsHandler(); Loading Loading @@ -331,6 +368,10 @@ public class T3Q { if (commandLine.hasOption("local-dependencies")) { this.setGenerateLocalDependencies(true); } if (commandLine.hasOption("serve-asn1-compiler")) { this.setServeASN1Compiler(true); } return commandLine.getArgs(); } Loading Loading @@ -573,4 +614,12 @@ public class T3Q { this.convertASN1toJSONSchema = convertASN1toJSONSchema; } public boolean isServeASN1Compiler() { return serveASN1Compiler; } public void setServeASN1Compiler(boolean serveASN1Compiler) { this.serveASN1Compiler = serveASN1Compiler; } } de.ugoe.cs.swe.TTCN3Configuration/src/de/ugoe/cs/swe/common/OptionsHandler.java +3 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,9 @@ public class OptionsHandler { optionWithID = new OptionWithID(500, "convert-asn1-to-schema", "Convert ASN.1 files to JSON schema\n"); getOptions().addOption(optionWithID); optionWithID = new OptionWithID(501, "serve-asn1-compiler", "Start a server for converting ASN.1 files to JSON schema\n"); getOptions().addOption(optionWithID); optionWithID = new OptionWithID(510, "convert-schemas", "Convert JSON schemas to TTCN-3\n"); getOptions().addOption(optionWithID); Loading Loading
de.ugoe.cs.swe.T3Q/src/de/ugoe/cs/swe/T3Q/HttpFileServer.java 0 → 100644 +499 −0 Original line number Diff line number Diff line package de.ugoe.cs.swe.T3Q; import com.sun.net.httpserver.*; import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; import java.util.concurrent.*; public class HttpFileServer { //TODO: experimental pure java boilerplate to handle uploads private static final String TEMP_DIR_PREFIX = "fileserver_"; private final HttpServer server; private final ExecutorService executor; private final FileProcessor processor; public HttpFileServer(int port, FileProcessor processor) throws IOException { this.server = HttpServer.create(new InetSocketAddress(port), 0); this.executor = Executors.newFixedThreadPool(10); this.processor = processor; this.server.setExecutor(executor); // Register upload endpoint server.createContext("/compile_asn", new UploadHandler(processor)); server.createContext("/health", new HealthHandler()); } public void start() { server.start(); System.out.println("Server started on port " + server.getAddress().getPort()); } public void stop() { server.stop(0); executor.shutdown(); } /** * Interface for processing uploaded files */ public interface FileProcessor { /** * Process uploaded files and return path to resulting JSON file * @param files Map of field names to temporary file paths * @param fields Additional form fields from the request * @return Path to the generated JSON file */ Path process(Map<String, Path> files, Map<String, String> fields) throws Exception; } /** * Handler for file uploads */ static class UploadHandler implements HttpHandler { private final FileProcessor processor; public UploadHandler(FileProcessor processor) { this.processor = processor; } @Override public void handle(HttpExchange exchange) throws IOException { if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) { sendError(exchange, 405, "Method not allowed"); return; } Path tempDir = null; try { // Create temporary directory for uploaded files tempDir = Files.createTempDirectory(TEMP_DIR_PREFIX); // Parse multipart request String contentType = exchange.getRequestHeaders().getFirst("Content-Type"); if (contentType == null || !contentType.startsWith("multipart/form-data")) { sendError(exchange, 400, "Expected multipart/form-data"); return; } String boundary = extractBoundary(contentType); if (boundary == null) { sendError(exchange, 400, "No boundary found in Content-Type"); return; } MultipartParser parser = new MultipartParser(exchange.getRequestBody(), boundary, tempDir); MultipartData data = parser.parse(); if (data.files.isEmpty()) { sendError(exchange, 400, "No files uploaded"); return; } // Process files with external command Path jsonResult = processor.process(data.files, data.fields); if (jsonResult == null || !Files.exists(jsonResult)) { sendError(exchange, 500, "Processing failed to produce result"); return; } // Send JSON response byte[] jsonBytes = Files.readAllBytes(jsonResult); exchange.getResponseHeaders().set("Content-Type", "application/json"); exchange.sendResponseHeaders(200, jsonBytes.length); try (OutputStream os = exchange.getResponseBody()) { os.write(jsonBytes); } // Cleanup result file Files.deleteIfExists(jsonResult); } catch (Exception e) { e.printStackTrace(); sendError(exchange, 500, "Processing error: " + e.getMessage()); } finally { // Cleanup temp directory if (tempDir != null) { cleanupDirectory(tempDir); } } } private String extractBoundary(String contentType) { String[] parts = contentType.split(";"); for (String part : parts) { part = part.trim(); if (part.startsWith("boundary=")) { return part.substring(9); } } return null; } } /** * Health check endpoint */ static class HealthHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { String response = "{\"status\":\"ok\"}"; exchange.getResponseHeaders().set("Content-Type", "application/json"); exchange.sendResponseHeaders(200, response.length()); try (OutputStream os = exchange.getResponseBody()) { os.write(response.getBytes(StandardCharsets.UTF_8)); } } } /** * Multipart form data parser */ static class MultipartParser { private final InputStream input; private final String boundary; private final Path tempDir; public MultipartParser(InputStream input, String boundary, Path tempDir) { this.input = input; this.boundary = boundary; this.tempDir = tempDir; } public MultipartData parse() throws IOException { MultipartData data = new MultipartData(); BufferedInputStream bis = new BufferedInputStream(input); System.out.println("DEBUG: Starting parse, boundary=" + boundary); // Skip preamble and find first boundary (starts with --boundary, not \r\n--boundary) String firstBoundary = "--" + boundary; if (!findBoundary(bis, firstBoundary.getBytes(StandardCharsets.UTF_8))) { System.out.println("DEBUG: Could not find first boundary"); return data; } System.out.println("DEBUG: Found first boundary"); while (true) { // Check if this is the end boundary (--boundary--) bis.mark(4); int c1 = bis.read(); int c2 = bis.read(); System.out.println("DEBUG: After boundary, next chars: '" + (char)c1 + "' '" + (char)c2 + "'"); if (c1 == '-' && c2 == '-') { // End of multipart System.out.println("DEBUG: Found end boundary"); break; } bis.reset(); // Skip CRLF after boundary skipLine(bis); // Read headers Map<String, String> headers = readHeaders(bis); System.out.println("DEBUG: Read headers: " + headers); if (headers.isEmpty()) { System.out.println("DEBUG: No headers found, ending parse"); break; } // Read part data until next boundary byte[] partData = readUntilBoundary(bis); System.out.println("DEBUG: Read part data: " + partData.length + " bytes"); // Process this part processPart(headers, partData, data); System.out.println("DEBUG: Files so far: " + data.files.size() + ", Fields: " + data.fields.size()); } System.out.println("DEBUG: Parse complete. Files: " + data.files.size() + ", Fields: " + data.fields.size()); return data; } private boolean findBoundary(InputStream is, byte[] boundaryBytes) throws IOException { List<Integer> window = new ArrayList<>(); int b; while ((b = is.read()) != -1) { window.add(b); // Keep window size equal to boundary length if (window.size() > boundaryBytes.length) { window.remove(0); } // Check if window matches boundary if (window.size() == boundaryBytes.length) { boolean match = true; for (int i = 0; i < boundaryBytes.length; i++) { if (window.get(i) != (boundaryBytes[i] & 0xFF)) { match = false; break; } } if (match) { return true; } } } return false; } private byte[] readUntilBoundary(InputStream is) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); String boundaryMarker = "\r\n--" + boundary; byte[] boundaryBytes = boundaryMarker.getBytes(StandardCharsets.UTF_8); List<Integer> window = new ArrayList<>(); int b; while ((b = is.read()) != -1) { window.add(b); // Keep window size equal to boundary length if (window.size() > boundaryBytes.length) { buffer.write(window.remove(0)); } // Check if window matches boundary if (window.size() == boundaryBytes.length) { boolean match = true; for (int i = 0; i < boundaryBytes.length; i++) { if (window.get(i) != (boundaryBytes[i] & 0xFF)) { match = false; break; } } if (match) { // Found boundary, return data before it return buffer.toByteArray(); } } } // Flush remaining window for (int val : window) { buffer.write(val); } return buffer.toByteArray(); } private void skipLine(InputStream is) throws IOException { int b; while ((b = is.read()) != -1) { if (b == '\n') { break; } } } private Map<String, String> readHeaders(BufferedInputStream is) throws IOException { Map<String, String> headers = new HashMap<>(); ByteArrayOutputStream lineBuffer = new ByteArrayOutputStream(); int prev = 0; int b; while ((b = is.read()) != -1) { if (b == '\n' && prev == '\r') { // End of line byte[] lineBytes = lineBuffer.toByteArray(); if (lineBytes.length <= 1) { // Empty line marks end of headers break; } // Parse header line (remove trailing \r) String line = new String(lineBytes, 0, lineBytes.length - 1, StandardCharsets.UTF_8); int colon = line.indexOf(':'); if (colon > 0) { String name = line.substring(0, colon).trim(); String value = line.substring(colon + 1).trim(); headers.put(name.toLowerCase(), value); } lineBuffer.reset(); } else { lineBuffer.write(b); } prev = b; } return headers; } private void processPart(Map<String, String> headers, byte[] data, MultipartData result) throws IOException { String disposition = headers.get("content-disposition"); if (disposition == null) return; String name = extractValue(disposition, "name"); String filename = extractValue(disposition, "filename"); if (filename != null && !filename.isEmpty()) { // This is a file Path filePath = tempDir.resolve(filename); Files.write(filePath, data); result.files.put(name, filePath); } else if (name != null) { // This is a regular field result.fields.put(name, new String(data, StandardCharsets.UTF_8).trim()); } } private String extractValue(String header, String key) { int start = header.indexOf(key + "=\""); if (start == -1) return null; start += key.length() + 2; int end = header.indexOf("\"", start); if (end == -1) return null; return header.substring(start, end); } } static class MultipartData { Map<String, Path> files = new HashMap<>(); Map<String, String> fields = new HashMap<>(); } private static void sendError(HttpExchange exchange, int code, String message) throws IOException { String json = String.format("{\"error\":\"%s\"}", message); exchange.getResponseHeaders().set("Content-Type", "application/json"); exchange.sendResponseHeaders(code, json.length()); try (OutputStream os = exchange.getResponseBody()) { os.write(json.getBytes(StandardCharsets.UTF_8)); } } private static void cleanupDirectory(Path dir) { try { Files.walk(dir) .sorted(Comparator.reverseOrder()) .forEach(path -> { try { Files.delete(path); } catch (IOException e) { // Ignore cleanup errors } }); } catch (IOException e) { // Ignore cleanup errors } } // Example: External command processor public static class ExternalCommandProcessor implements FileProcessor { private final String command; private final Path workDir; private List<String> arguments; public ExternalCommandProcessor(String command, List<String> arguments, Path workDir) { this.command = command; this.arguments = arguments; this.workDir = workDir; } @Override public Path process(Map<String, Path> files, Map<String, String> fields) throws Exception { // Build command with file paths as arguments List<String> cmd = new ArrayList<>(); cmd.add(command); cmd.add("--ttcn2json"); // Add file paths as arguments for (Path file : files.values()) { cmd.add(file.toString()); } cmd.add("-"); String jsonFile = "asn.json"; cmd.add(jsonFile); // Execute command ProcessBuilder pb = new ProcessBuilder(cmd); pb.directory(workDir.toFile()); pb.redirectErrorStream(true); Process process = pb.start(); // Read output (assuming the command outputs JSON file path) String output; try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { output = reader.lines() .reduce((a, b) -> a + "\n" + b) .orElse(""); } int exitCode = process.waitFor(); if (exitCode != 0) { throw new RuntimeException("Command failed with exit code " + exitCode + ": " + output); } // The command should output the path to the generated JSON file File wd = workDir.toFile(); String jsonPath = new File(wd, jsonFile).getAbsolutePath(); Path result = Paths.get(jsonPath); if (!Files.exists(result)) { throw new FileNotFoundException("Expected JSON file not found: " + jsonPath); } return result; } } // Example usage public static void main(String[] args) { try { // Example 1: Simple processor that creates a JSON response FileProcessor simpleProcessor = (files, fields) -> { Path jsonFile = Files.createTempFile("result_", ".json"); // Create simple JSON response StringBuilder json = new StringBuilder("{"); json.append("\"filesProcessed\":").append(files.size()).append(","); json.append("\"files\":["); int i = 0; for (Map.Entry<String, Path> entry : files.entrySet()) { if (i++ > 0) json.append(","); json.append("{") .append("\"field\":\"").append(entry.getKey()).append("\",") .append("\"filename\":\"").append(entry.getValue().getFileName()).append("\",") .append("\"size\":").append(Files.size(entry.getValue())) .append("}"); } json.append("]}"); Files.write(jsonFile, json.toString().getBytes(StandardCharsets.UTF_8)); return jsonFile; }; // HttpFileServer server = new HttpFileServer(8080, simpleProcessor); // server.start(); // Example 2: External command processor // Uncomment and modify for your external command /* */ Path workDir = Paths.get("./"); Files.createDirectories(workDir); //TODO: expose as parameter FileProcessor cmdProcessor = new ExternalCommandProcessor( "./compiler", List.of(), workDir ); HttpFileServer server = new HttpFileServer(3005, cmdProcessor); server.start(); System.out.println("Server ready. Test with:"); System.out.println("curl -X POST -F 'file=@test.txt' http://localhost:3005/compile_asn"); } catch (Exception e) { e.printStackTrace(); } } } No newline at end of file
de.ugoe.cs.swe.T3Q/src/de/ugoe/cs/swe/T3Q/T3Q.java +50 −1 Original line number Diff line number Diff line Loading @@ -6,10 +6,14 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Scanner; import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; Loading @@ -25,6 +29,8 @@ import org.apache.commons.io.filefilter.RegexFileFilter; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import de.ugoe.cs.swe.T3Q.HttpFileServer.ExternalCommandProcessor; import de.ugoe.cs.swe.T3Q.HttpFileServer.FileProcessor; import de.ugoe.cs.swe.TTCN3Configuration.QualityCheckProfile; import de.ugoe.cs.swe.common.ConfigTools; import de.ugoe.cs.swe.common.MiscTools; Loading Loading @@ -55,6 +61,7 @@ public class T3Q { private boolean analyzeUsage = false; //TODO: expose in configuration? private String schemaFile = "asn.json"; private boolean serveASN1Compiler; // private boolean formattingEnabled = false; Loading Loading @@ -115,6 +122,11 @@ public class T3Q { System.out.println("ERRORING OUT!"); } if (this.isServeASN1Compiler()) { serveASNCompiler(); return; } if (this.isConvertASN1toJSONSchema()) { //TODO: skip if none! convertASN1toJSONSchema(); Loading Loading @@ -276,6 +288,31 @@ public class T3Q { return true; } private void serveASNCompiler() { //TODO: this may need to be temporary.. Path workDir = Paths.get("./"); try { Files.createDirectories(workDir); FileProcessor cmdProcessor = new ExternalCommandProcessor( //DONE: expose as parameter // "./compiler", activeProfile.getTitanCompilerPath(), List.of(), workDir ); //TODO: send titan output as well HttpFileServer server = new HttpFileServer(3005, cmdProcessor); server.start(); System.out.println("Server ready at http://localhost:3005/compile_asn. Test with:"); System.out.println("curl -X POST -F 'file=@test.txt' http://localhost:3005/compile_asn"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private CommandLine parseCommandLineArguments(String[] args) { CommandLineParser parser = new DefaultParser(); T3QOptionsHandler optionsHandler = new T3QOptionsHandler(); Loading Loading @@ -331,6 +368,10 @@ public class T3Q { if (commandLine.hasOption("local-dependencies")) { this.setGenerateLocalDependencies(true); } if (commandLine.hasOption("serve-asn1-compiler")) { this.setServeASN1Compiler(true); } return commandLine.getArgs(); } Loading Loading @@ -573,4 +614,12 @@ public class T3Q { this.convertASN1toJSONSchema = convertASN1toJSONSchema; } public boolean isServeASN1Compiler() { return serveASN1Compiler; } public void setServeASN1Compiler(boolean serveASN1Compiler) { this.serveASN1Compiler = serveASN1Compiler; } }
de.ugoe.cs.swe.TTCN3Configuration/src/de/ugoe/cs/swe/common/OptionsHandler.java +3 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,9 @@ public class OptionsHandler { optionWithID = new OptionWithID(500, "convert-asn1-to-schema", "Convert ASN.1 files to JSON schema\n"); getOptions().addOption(optionWithID); optionWithID = new OptionWithID(501, "serve-asn1-compiler", "Start a server for converting ASN.1 files to JSON schema\n"); getOptions().addOption(optionWithID); optionWithID = new OptionWithID(510, "convert-schemas", "Convert JSON schemas to TTCN-3\n"); getOptions().addOption(optionWithID); Loading