package com.github.sriki77.apiproxy.instrument; import com.github.sriki77.apiproxy.instrument.io.ProxyFileHandler; import com.github.sriki77.apiproxy.instrument.io.ProxyZipFileHandler; import com.github.sriki77.apiproxy.instrument.model.Endpoint; import com.github.sriki77.apiproxy.instrument.model.FlowSteps; import com.github.sriki77.apiproxy.instrument.model.Step; import com.github.sriki77.apiproxy.instrument.report.KVMapInstrumentReportGenerator; import org.apache.commons.cli.*; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Instrumenter { public static final String OPT_HELP = "help"; public static final String OPT_ZIP = "z"; public static final String OPT_DIR = "d"; public static final String OPT_KV = "kv"; public static final String OPT_REPORT_DIR = "o"; public static final File DEFAULT_REPORT_DIRECTORY = new File("report"); private ProxyFileHandler proxyFileHandler; private File kvInstrumentFile; private boolean generateReport; private File reportDirectory; public static void main(String... args) throws Exception { final Instrumenter instrumenter = new Instrumenter(); processCommandLineArgs(instrumenter, args); instrumenter.doWork(); } private void doWork() throws IOException { if (generateReport) { writeReport(); } else { instrumetProxy(); } } private void instrumetProxy() throws IOException { try (ProxyFileHandler proxyFileHandler = this.proxyFileHandler) { final List<Endpoint> endpoints = proxyFileHandler.getEndpoints(); final ProxyInstrumeter proxyInstrumenter = getProxyInstrumenter(endpoints); final List<Endpoint> instrumentedEndPoints = proxyInstrumenter.instrument(); instrumentedEndPoints.forEach(proxyFileHandler::updateEndpoint); } } private void writeReport() throws IOException { final List<Endpoint> endpoints = proxyFileHandler.getEndpoints(); try (final KVMapInstrumentReportGenerator reportGenerator = new KVMapInstrumentReportGenerator(proxyFileHandler.proxyName(),kvInstrumentFile, reportDirectory)) { endpoints.forEach(reportGenerator::generateReport); } System.err.println("Generated report in directory: " + reportDirectory.getAbsolutePath()); } private int policiesCount(List<Endpoint> endpoints) { Set<String> policies = new HashSet<>(); endpoints.forEach(e -> { e.getFaultRules().getFaultRules().forEach(f -> policies.addAll(policies(f))); policies.addAll(policies(e.getPreflow().getRequestFlow())); policies.addAll(policies(e.getPreflow().getResponseFlow())); policies.addAll(policies(e.getPostflow().getRequestFlow())); policies.addAll(policies(e.getPostflow().getResponseFlow())); e.getFlows().getFlows().forEach(f -> { policies.addAll(policies(f.getRequestFlow())); policies.addAll(policies(f.getResponseFlow())); }); }); return policies.size(); } private Set<String> policies(FlowSteps f) { return f.getSteps().stream().map(Step::getName).collect(Collectors.toSet()); } private static void processCommandLineArgs(Instrumenter instrumenter, String[] args) throws Exception { CommandLineParser parser = new BasicParser(); Options options = buildOptions(); processOptions(instrumenter, getCommandLine(args, parser, options), options); } private static CommandLine getCommandLine(String[] args, CommandLineParser parser, Options options) throws ParseException { try { return parser.parse(options, args); } catch (AlreadySelectedException ase) { final OptionGroup optionGroup = ase.getOptionGroup(); System.out.print("Error: Only one of "); String separator = ""; for (Object option : optionGroup.getOptions()) { Option opt = (Option) option; System.out.print(separator + "-" + opt.getOpt()); separator = ","; } System.out.println(" is allowed"); printHelpAndExit(options); } return null; } private static Options buildOptions() { Options options = new Options(); options.addOption(new Option(OPT_HELP, "Print this message")); final Option zipFileOption = OptionBuilder.withArgName("file") .hasArg().withDescription("Proxy Zip File Name") .create(OPT_ZIP); final Option dirOption = OptionBuilder.withArgName("directory") .hasArg().withDescription("Proxy Directory Name") .create(OPT_DIR); final OptionGroup optionGroup = new OptionGroup(); optionGroup.addOption(zipFileOption); optionGroup.addOption(dirOption); final Option kv = OptionBuilder.withArgName("kvm.json").hasArg() .withDescription("Instrument Key Value Map JSON file") .create(OPT_KV); final Option optReportDir = OptionBuilder.withArgName("report directory").hasArg() .withDescription("Directory for report files") .create(OPT_REPORT_DIR); options.addOptionGroup(optionGroup); options.addOption(kv); options.addOption(optReportDir); return options; } private static void processOptions(Instrumenter instrumenter, CommandLine cli, Options options) throws Exception { processReportDirectory(instrumenter, cli); if (cli.hasOption(OPT_KV)) { processKVFile(instrumenter, cli); } if (cli.hasOption(OPT_ZIP)) { processZipFile(instrumenter, cli); return; } if (cli.hasOption(OPT_DIR)) { processDir(instrumenter, cli); return; } System.err.println("Proxy zip or directory must be specified"); printHelpAndExit(options); } private static void processReportDirectory(Instrumenter instrumenter, CommandLine cli) { File reportDirectory = DEFAULT_REPORT_DIRECTORY; if (cli.hasOption(OPT_REPORT_DIR)) { final String value = cli.getOptionValue(OPT_REPORT_DIR); reportDirectory = new File(value); } FileUtils.deleteQuietly(reportDirectory); reportDirectory.mkdirs(); instrumenter.setReportDirectory(reportDirectory); } private static void processDir(Instrumenter instrumenter, CommandLine cli) throws Exception { final String value = cli.getOptionValue(OPT_DIR); File file = new File(value); if (!file.exists()) { System.err.println("Specified proxy directory not found: " + file); System.exit(-1); } if (!file.isDirectory()) { System.err.println("Specified value is not directory: " + file); System.exit(-1); } if (!file.canRead() || !file.canWrite()) { System.err.println("Specified proxy directory should be readable and writeable: " + file); System.exit(-1); } instrumenter.setProxyDirectory(file); } private void setProxyDirectory(File file) throws Exception { proxyFileHandler = new ProxyZipFileHandler(file); } private static void processKVFile(Instrumenter instrumenter, CommandLine cli) throws Exception { final String value = cli.getOptionValue(OPT_KV); File file = new File(value); if (!file.exists()) { System.err.println("Specified Key Value Map file not found: " + file); System.exit(-1); } if (file.isDirectory()) { System.err.println("Specified file is not a plain JSON file: " + file); System.exit(-1); } if (!file.canRead()) { System.err.println("Specified Key Value map file should be readable: " + file); System.exit(-1); } instrumenter.setKVFile(file); } private void setKVFile(File kvfile) { this.kvInstrumentFile = kvfile; this.generateReport = true; } private static void processZipFile(Instrumenter instrumenter, CommandLine cli) throws Exception { final String value = cli.getOptionValue(OPT_ZIP); File file = new File(value); if (!file.exists()) { System.err.println("Specified proxy file not found: " + file); System.exit(-1); } if (file.isDirectory()) { System.err.println("Specified file is not a zip file: " + file); System.exit(-1); } if (!file.canRead() || !file.canWrite()) { System.err.println("Specified proxy file should be readable and writeable: " + file); System.exit(-1); } instrumenter.setProxyFile(file); } private void setProxyFile(File file) throws Exception { proxyFileHandler = new ProxyZipFileHandler(file); } private static void printHelpAndExit(Options options) { System.out.println(); final HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("java " + Instrumenter.class.getName(), options); System.exit(-1); } public ProxyInstrumeter getProxyInstrumenter(List<Endpoint> endpoints) { return new KVMapBasedProxyInstrumenter(endpoints); } public void setReportDirectory(File reportDirectory) { this.reportDirectory = reportDirectory; } }