package com.sebastian_daschner.jaxrs_analyzer; import com.sebastian_daschner.jaxrs_analyzer.analysis.ProjectAnalyzer; import com.sebastian_daschner.jaxrs_analyzer.backend.Backend; import com.sebastian_daschner.jaxrs_analyzer.model.rest.Project; import com.sebastian_daschner.jaxrs_analyzer.model.rest.Resources; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Path; import java.util.HashSet; import java.util.Objects; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.StreamSupport; /** * Generates REST documentation of JAX-RS projects automatically by bytecode analysis. * * @author Sebastian Daschner */ public class JAXRSAnalyzer { private final Set<Path> projectClassPaths = new HashSet<>(); private final Set<Path> projectSourcePaths = new HashSet<>(); private final Set<Path> classPaths = new HashSet<>(); private final String projectName; private final String projectVersion; private final Path outputLocation; private final Backend backend; /** * Constructs a JAX-RS Analyzer. * * @param projectClassPaths The paths of the projects classes to be analyzed (can either be directories or jar-files, at least one is mandatory) * @param projectSourcePaths The paths of the projects sources to be analyzed (can either be directories or jar-files, optional) * @param classPaths The additional class paths (can either be directories or jar-files) * @param projectName The project name * @param projectVersion The project version * @param backend The backend to render the output * @param outputLocation The location of the output file (output will be printed to standard out if {@code null}) */ public JAXRSAnalyzer(final Set<Path> projectClassPaths, final Set<Path> projectSourcePaths, final Set<Path> classPaths, final String projectName, final String projectVersion, final Backend backend, final Path outputLocation) { Objects.requireNonNull(projectClassPaths); Objects.requireNonNull(projectSourcePaths); Objects.requireNonNull(classPaths); Objects.requireNonNull(projectName); Objects.requireNonNull(projectVersion); Objects.requireNonNull(backend); if (projectClassPaths.isEmpty()) throw new IllegalArgumentException("At least one project path is mandatory"); this.projectClassPaths.addAll(projectClassPaths); this.projectSourcePaths.addAll(projectSourcePaths); this.classPaths.addAll(classPaths); this.projectName = projectName; this.projectVersion = projectVersion; this.outputLocation = outputLocation; this.backend = backend; } /** * Analyzes the JAX-RS project at the class path and produces the output as configured. */ public void analyze() { final Resources resources = new ProjectAnalyzer(classPaths).analyze(projectClassPaths, projectSourcePaths); if (resources.isEmpty()) { LogProvider.info("Empty JAX-RS analysis result, omitting output"); return; } final Project project = new Project(projectName, projectVersion, resources); final byte[] output = backend.render(project); if (outputLocation != null) { outputToFile(output, outputLocation); } else { outputToConsole(output); } } private void outputToConsole(final byte[] output) { try { System.out.write(output); System.out.flush(); } catch (IOException e) { LogProvider.error("Could not write the output, reason: " + e.getMessage()); LogProvider.debug(e); } } private static void outputToFile(final byte[] output, final Path outputLocation) { try (final OutputStream stream = new FileOutputStream(outputLocation.toFile())) { stream.write(output); stream.flush(); } catch (IOException e) { LogProvider.error("Could not write to the specified output location, reason: " + e.getMessage()); LogProvider.debug(e); } } public static Backend constructBackend(final String backendType) { final ServiceLoader<Backend> backends = ServiceLoader.load(Backend.class); return StreamSupport.stream(backends.spliterator(), false) .filter(b -> backendType.equalsIgnoreCase(b.getName())) .findAny() .orElseThrow(() -> new IllegalArgumentException("Unknown backend type " + backendType)); } }