/* * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) * * Licensed under the EUPL, Version 1.1 or – as soon they * will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * * You may not use this work except in compliance with the Licence. * * You may obtain a copy of the Licence at: * * https://joinup.ec.europa.eu/community/eupl/og_page/eupl * * Unless required by applicable law or agreed to in * writing, software distributed under the Licence is * distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the Licence for the specific language governing * permissions and limitations under the Licence. */ package eu.sendregning.oxalis; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import no.difi.certvalidator.Validator; import no.difi.oxalis.api.model.TransmissionIdentifier; import no.difi.oxalis.outbound.OxalisOutboundComponent; import no.difi.vefa.peppol.common.model.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.net.URI; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.OptionalDouble; import java.util.concurrent.*; import java.util.stream.Collectors; /** * @author Steinar O. Cook * @author Nigel Parker * @author Thore Johnsen * @author erlend */ public class Main { public static final Logger log = LoggerFactory.getLogger(Main.class); private static OptionSpec<String> fileSpec; private static OptionSpec<String> sender; private static OptionSpec<String> recipient; private static OptionSpec<String> docType; // The PEPPOL document type (very long string) private static OptionSpec<String> profileType; // The PEPPOL document profile private static OptionSpec<File> evidencePath; // Path to persistent storage of evidence data private static OptionSpec<Integer> threadCount; // Number of parallell threads to use private static OptionSpec<Boolean> useRequestFactory; private static OptionSpec<Integer> repeatCount; private static OptionSpec<String> destinationUrl; private static OptionSpec<File> destinationCertificate; private static OptionSpec<Integer> maxTransmissions; // Maximum number of transmissions no matter what public static void main(String[] args) throws Exception { OptionParser optionParser = getOptionParser(); if (args.length == 0) { System.out.println(""); optionParser.printHelpOn(System.out); System.out.println(""); System.out.println("Configure logging: java -Dlogback.configurationFile=/path/to/config.xml -jar <this_jar_file> options"); return; } OptionSet optionSet; try { optionSet = optionParser.parse(args); } catch (Exception e) { printErrorMessage(e.getMessage()); return; } // bootstraps the Oxalis outbound module OxalisOutboundComponent oxalisOutboundComponent = new OxalisOutboundComponent(); TransmissionParameters params = new TransmissionParameters(oxalisOutboundComponent); // Verifies the existence of a directory in which transmission evidence is stored. File evidencePath = Main.evidencePath.value(optionSet); if (evidencePath == null) { evidencePath = new File("."); // Default is current directory } if (!evidencePath.exists() || !evidencePath.isDirectory()) { printErrorMessage(evidencePath + " does not exist or is not a directory"); } params.setEvidencePath(evidencePath); // --- Use Factory params.setUseFactory(useRequestFactory.value(optionSet)); // --- Recipient String recipientId = recipient.value(optionSet); if (recipientId != null) { params.setReceiver(ParticipantIdentifier.of(recipientId)); } // --- Sender String senderId = sender.value(optionSet); if (senderId != null) { params.setSender(ParticipantIdentifier.of(senderId)); } // --- Document type if (docType != null && docType.value(optionSet) != null) { String value = docType.value(optionSet); params.setDocType(DocumentTypeIdentifier.of(value)); } // --- Process type if (profileType != null && profileType.value(optionSet) != null) { String value = profileType.value(optionSet); params.setProcessIdentifier(ProcessIdentifier.of(value)); } // --- Destination URL, protocl and system identifier if (optionSet.has(destinationUrl)) { String destinationString = destinationUrl.value(optionSet); X509Certificate certificate; try (InputStream inputStream = new FileInputStream(destinationCertificate.value(optionSet))) { certificate = Validator.getCertificate(inputStream); } params.setEndpoint(Endpoint.of(TransportProfile.AS2_1_0, URI.create(destinationString), certificate)); } // Retrieves the name of the file to be transmitted String payloadFileSpec = fileSpec.value(optionSet); List<File> files = locateFiles(payloadFileSpec); System.out.println(""); System.out.println(""); Integer maxThreads = optionSet.valueOf(threadCount); log.info("Using " + maxThreads + " threads"); int repeats = optionSet.valueOf(repeatCount); int maximumTransmissions = optionSet.valueOf(maxTransmissions) ; ExecutorService exec = Executors.newFixedThreadPool(maxThreads); ExecutorCompletionService<TransmissionResult> ecs = new ExecutorCompletionService<TransmissionResult>(exec); long start = System.nanoTime(); int submittedTaskCount = 0; for (File file : files) { if (!file.isFile() || !file.canRead()) { log.error("File " + file + " is not a file or can not be read, skipping..."); continue; } for (int i = 0; i < repeats; i++) { TransmissionTask transmissionTask = new TransmissionTask(params, file); Future<TransmissionResult> submit = ecs.submit(transmissionTask); submittedTaskCount++; if (submittedTaskCount > maximumTransmissions) { log.info("Stopped submitting tasks at {} " + submittedTaskCount); break; } } if (submittedTaskCount > maximumTransmissions) { break; } } // Wait for the results to be available List<TransmissionResult> results = new ArrayList<>(); int failed = 0; for (int i = 0; i < submittedTaskCount; i++) { try { Future<TransmissionResult> future = ecs.take(); TransmissionResult transmissionResult = future.get(); results.add(transmissionResult); } catch (InterruptedException e) { System.err.println(e.getMessage()); } catch (ExecutionException e) { log.error("Execution failed: {}", e.getMessage(), e); failed++; } } long elapsed = System.nanoTime() - start; exec.shutdownNow(); // Shuts down the executor service for (TransmissionResult transmissionResult : results) { TransmissionIdentifier transmissionIdentifier = transmissionResult.getTransmissionIdentifier(); System.out.println(transmissionIdentifier + " transmission took " + transmissionResult.getDuration() + "ms"); } OptionalDouble average = results.stream().mapToLong(r -> r.getDuration()).average(); if (average.isPresent()) { System.out.println("Average transmission time was " + average.getAsDouble() + "ms"); } long elapsedInSeconds = TimeUnit.SECONDS.convert(elapsed, TimeUnit.NANOSECONDS); System.out.println("Total time spent: " + elapsedInSeconds + "s"); System.out.println("Attempted to send " + results.size() + " files"); System.out.println("Failed transmissions: " + failed); if (results.size() > 0 && elapsedInSeconds > 0) { System.out.println("Transmission speed " + results.size() / elapsedInSeconds + " documents per second"); } Thread.sleep(2000); } /** * Locates the files to load and transmit. * * @param payloadFileSpec specifies the name of the file or the directory holding files to be transmitted. * @return list of eligible files. */ static List<File> locateFiles(String payloadFileSpec) { List<File> files = new ArrayList<>(); if ("-".equals(payloadFileSpec)) { // Reads list of files from stdin try (Reader reader = new InputStreamReader(System.in); BufferedReader bufferedReader = new BufferedReader(reader)) { files = bufferedReader.lines() .map(File::new) .collect(Collectors.toList()); } catch (IOException e) { log.warn(e.getMessage(), e); } } else { File fileSpec = new File(payloadFileSpec); if (fileSpec.isDirectory()) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(payloadFileSpec), "*.{XML,xml}")) { for (Path path : stream) { files.add(path.toFile()); } } catch (IOException e) { throw new IllegalStateException("Unable to list files " + payloadFileSpec + "; " + e.getMessage(), e); } } else if (fileSpec.isFile()) { files.add(fileSpec); } } return files; } private static void printErrorMessage(String message) { System.out.println(""); System.out.println("*** " + message); System.out.println(""); } static OptionParser getOptionParser() { OptionParser optionParser = new OptionParser(); fileSpec = optionParser.accepts("f", "File(s) to be transmitted") .withRequiredArg().ofType(String.class).required(); docType = optionParser.accepts("d", "Document type").withRequiredArg(); profileType = optionParser.accepts("p", "Profile type").withRequiredArg(); sender = optionParser.accepts("s", "sender [e.g. 9908:976098897]").withRequiredArg(); recipient = optionParser.accepts("r", "recipient [e.g. 9908:976098897]").withRequiredArg(); evidencePath = optionParser.accepts("e", "Evidence storage dir") .withRequiredArg().ofType(File.class); threadCount = optionParser.accepts("x", "Number of threads to use ") .withRequiredArg().ofType(Integer.class).defaultsTo(10); useRequestFactory = optionParser.accepts("factory", "Use TransmissionRequestFactory (no overrides!)") .withOptionalArg().ofType(Boolean.class).defaultsTo(false); repeatCount = optionParser.accepts("repeat", "Number of repeats to use ") .withRequiredArg().ofType(Integer.class).defaultsTo(1); destinationUrl = optionParser.accepts("u", "destination URL").withRequiredArg(); destinationCertificate = optionParser.accepts("cert", "Receiving AP's certificate (when overriding endpoint)") .requiredIf("u").withRequiredArg().ofType(File.class); maxTransmissions = optionParser.accepts("m", "Max number of transmissions").withRequiredArg().ofType(Integer.class).defaultsTo(Integer.MAX_VALUE); return optionParser; } @SuppressWarnings("unused") private static String enterPassword() { System.out.print("Keystore password: "); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String password = null; try { password = bufferedReader.readLine(); } catch (Exception e) { log.error(e.getMessage(), e); System.exit(1); } finally { try { bufferedReader.close(); } catch (Exception e) { /* do nothing */ } } return password; } }