All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jerkar.tool.Engine Maven / Gradle / Ivy

The newest version!
package org.jerkar.tool;

import org.jerkar.api.depmanagement.*;
import org.jerkar.api.file.JkPathMatcher;
import org.jerkar.api.file.JkPathSequence;
import org.jerkar.api.file.JkPathTree;
import org.jerkar.api.java.JkUrlClassLoader;
import org.jerkar.api.java.JkClasspath;
import org.jerkar.api.java.JkJavaCompileSpec;
import org.jerkar.api.java.JkJavaCompiler;
import org.jerkar.api.system.JkException;
import org.jerkar.api.system.JkLocator;
import org.jerkar.api.system.JkLog;
import org.jerkar.api.utils.*;
import org.jerkar.tool.CommandLine.MethodInvocation;

import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;

/**
 * Engine having responsibility of compiling run classes, instantiate and run them.
* Run class sources are expected to lie in [project base dir]/jerkar/def
* Classes having simple name starting with '_' are ignored. * * Run classes can have dependencies on jars :
    *
  • located in [base dir]/jerkar/boot directory
  • *
  • declared in {@link JkImport} annotation
  • *
*/ final class Engine { private final JkPathMatcher RUN_SOURCE_MATCHER = JkPathMatcher.of(true,"**.java").and(false, "**/_*", "_*"); private final Path projectBaseDir; private JkDependencySet runDependencies; private JkRepoSet runRepos; private List rootOfImportedRuns = new LinkedList<>(); private final RunResolver resolver; /** * Constructs an engine for the specified base directory. */ Engine(Path baseDir) { super(); JkUtilsAssert.isTrue(baseDir.isAbsolute(), baseDir + " is not absolute."); JkUtilsAssert.isTrue(Files.isDirectory(baseDir), baseDir + " is not directory."); this.projectBaseDir = baseDir.normalize(); runRepos = repos(); this.runDependencies = JkDependencySet.of(); this.resolver = new RunResolver(baseDir); } T getRun(Class baseClass) { if (resolver.needCompile()) { this.compile(); } return resolver.resolve(baseClass); } /** * Pre-compile and compile run classes (if needed) then execute methods mentioned in command line */ void execute(CommandLine commandLine, String runClassHint, JkLog.Verbosity verbosityToRestore) { runDependencies = runDependencies.andScopelessDependencies(commandLine.dependencies()); long start = System.nanoTime(); JkLog.startTask("Compile and initialise run classes"); JkRun jkRun = null; JkPathSequence path = JkPathSequence.of(); if (!commandLine.dependencies().isEmpty()) { final JkPathSequence cmdPath = pathOf(commandLine.dependencies()); path = path.andPrepending(cmdPath); JkLog.trace("Command line extra path : " + cmdPath); } preCompile(); // Need to pre-compile to get the declared run dependencies if (!JkUtilsString.isBlank(runClassHint)) { // First find a class in the existing classpath without compiling jkRun = getRunInstance(runClassHint, path); } if (jkRun == null) { path = compile().and(path); jkRun = getRunInstance(runClassHint, path); } jkRun.getImportedRuns().setImportedRunRoots(this.rootOfImportedRuns); if (jkRun == null) { throw new JkException("Can't find or guess any run class for project hosted in " + this.projectBaseDir + " .\nAre you sure this directory is a Jerkar project ?"); } JkLog.endTask("Done in " + JkUtilsTime.durationInMillis(start) + " milliseconds."); JkLog.info("Jerkar run is ready to start."); JkLog.setVerbosity(verbosityToRestore); try { this.launch(jkRun, commandLine); } catch (final RuntimeException e) { JkLog.error("Engine " + projectBaseDir + " failed"); throw e; } } private JkPathSequence pathOf(List dependencies) { JkDependencySet deps = JkDependencySet.of(); for (JkDependency dependency : dependencies) { deps = deps.and(dependency); } return JkDependencyResolver.of(this.runRepos).resolve(deps).getFiles(); } private void preCompile() { List sourceFiles = JkPathTree.of(resolver.runSourceDir).andMatcher(RUN_SOURCE_MATCHER).getFiles(); final SourceParser parser = SourceParser.of(this.projectBaseDir, sourceFiles); this.runDependencies = this.runDependencies.and(parser.dependencies()); this.runRepos = parser.importRepos().and(runRepos); this.rootOfImportedRuns = parser.projects(); } // Compiles and returns the runtime classpath private JkPathSequence compile() { final LinkedHashSet entries = new LinkedHashSet<>(); compile(new HashSet<>(), entries); return JkPathSequence.of(entries).withoutDuplicates(); } private void compile(Set yetCompiledProjects, LinkedHashSet path) { if (!this.resolver.hasDefSource() || yetCompiledProjects.contains(this.projectBaseDir)) { return; } yetCompiledProjects.add(this.projectBaseDir); preCompile(); // This enrich dependencies String msg = "Compiling run classes for project " + this.projectBaseDir.getFileName().toString(); long start = System.nanoTime(); JkLog.startTask(msg); final JkDependencyResolver runDependencyResolver = getRunDependencyResolver(); JkResolveResult resolveResult = runDependencyResolver.resolve(this.computeRunDependencies()); if (resolveResult.getErrorReport().hasErrors()) { JkLog.warn(resolveResult.getErrorReport().toString()); } final JkPathSequence runPath = resolveResult.getFiles(); path.addAll(runPath.getEntries()); path.addAll(compileDependentProjects(yetCompiledProjects, path).getEntries()); compileDef(JkPathSequence.of(path)); path.add(this.resolver.runClassDir); JkLog.endTask("Done in " + JkUtilsTime.durationInMillis(start) + " milliseconds."); } private JkRun getRunInstance(String runClassHint, JkPathSequence runtimePath) { final JkUrlClassLoader classLoader = JkUrlClassLoader.ofCurrent(); classLoader.addEntries(runtimePath); JkLog.trace("Setting run execution classpath to : " + classLoader.getDirectClasspath()); final JkRun run = resolver.resolve(runClassHint); if (run == null) { return null; } try { run.setRunDependencyResolver(this.computeRunDependencies(), getRunDependencyResolver()); return run; } catch (final RuntimeException e) { JkLog.error("Engine " + projectBaseDir + " failed"); throw e; } } private JkDependencySet computeRunDependencies() { // If true, we assume Jerkar is provided by IDE (development mode) final boolean devMode = Files.isDirectory(JkLocator.getJerkarJarPath()); return JkDependencySet.of(runDependencies .andFiles(localRunPath()) .andFiles(JkClasspath.ofCurrentRuntime()).withoutLastIf(!devMode) .andFiles(jerkarLibs()).withoutLastIf(devMode) .withDefaultScope(JkScopeMapping.ALL_TO_DEFAULT)); } private JkPathSequence localRunPath() { final List extraLibs = new LinkedList<>(); final Path localDefLibDir = this.projectBaseDir.resolve(JkConstants.BOOT_DIR); if (Files.exists(localDefLibDir)) { extraLibs.addAll(JkPathTree.of(localDefLibDir).andMatching(true,"**.jar").getFiles()); } return JkPathSequence.of(extraLibs).withoutDuplicates(); } private JkPathSequence compileDependentProjects(Set yetCompiledProjects, LinkedHashSet pathEntries) { JkPathSequence pathSequence = JkPathSequence.of(); if (!this.rootOfImportedRuns.isEmpty()) { JkLog.info("Compile run classes of dependent projects : " + toRelativePaths(this.projectBaseDir, this.rootOfImportedRuns)); } for (final Path file : this.rootOfImportedRuns) { final Engine engine = new Engine(file.toAbsolutePath().normalize()); engine.compile(yetCompiledProjects, pathEntries); pathSequence = pathSequence.and(file); } return pathSequence; } private void compileDef(JkPathSequence runPath) { JkJavaCompileSpec compileSpec = defCompileSpec().setClasspath(runPath); try { JkJavaCompiler.ofJdk().compile(compileSpec); } catch (JkException e) { JkLog.setVerbosity(JkLog.Verbosity.NORMAL); JkLog.info("Compilation of Jerkar files failed. You can run jerkar -RC=JkRun to use default Jerkar files" + " instead of the ones located in this project."); throw e; } JkPathTree.of(this.resolver.runSourceDir).andMatching(false, "**/*.java") .copyTo(this.resolver.runClassDir, StandardCopyOption.REPLACE_EXISTING); } private void launch(JkRun jkRun, CommandLine commandLine) { if (!commandLine.getSubProjectMethods().isEmpty()) { for (final JkRun importedRun : jkRun.getImportedRuns().getAll()) { runProject(importedRun, commandLine.getSubProjectMethods()); } runProject(jkRun, commandLine.getSubProjectMethods()); } runProject(jkRun, commandLine.getMasterMethods()); } private JkJavaCompileSpec defCompileSpec() { final JkPathTree defSource = JkPathTree.of(resolver.runSourceDir).andMatcher(RUN_SOURCE_MATCHER); JkUtilsPath.createDirectories(resolver.runClassDir); return JkJavaCompileSpec.of().setOutputDir(resolver.runClassDir) .addSources(defSource.getFiles()); } private JkDependencyResolver getRunDependencyResolver() { if (this.computeRunDependencies().hasModules()) { return JkDependencyResolver.of(this.runRepos); } return JkDependencyResolver.of(); } private static JkPathSequence jerkarLibs() { final List extraLibs = new LinkedList<>(); extraLibs.add(JkLocator.getJerkarJarPath()); return JkPathSequence.of(extraLibs).withoutDuplicates(); } private static void runProject(JkRun jkRun, List invokes) { for (MethodInvocation methodInvocation : invokes) { invokeMethodOnRunClassOrPlugin(jkRun, methodInvocation); } } private static void invokeMethodOnRunClassOrPlugin(JkRun jkRun, MethodInvocation methodInvocation) { if (methodInvocation.pluginName != null) { final JkPlugin plugin = jkRun.getPlugins().get(methodInvocation.pluginName); invokeMethodOnRunOrPlugin(plugin, methodInvocation.methodName); } else { invokeMethodOnRunOrPlugin(jkRun, methodInvocation.methodName); } } /** * Invokes the specified method in this run. */ private static void invokeMethodOnRunOrPlugin(Object run, String methodName) { final Method method; try { method = run.getClass().getMethod(methodName); } catch (final NoSuchMethodException e) { throw new JkException("No public zero-arg method '" + methodName + "' found in class '" + run.getClass()); } if (Environment.standardOptions.logHeaders) { JkLog.info("Method : " + methodName + " on " + run.getClass().getName()); } final long time = System.nanoTime(); try { JkUtilsReflect.invoke(run, method); if (Environment.standardOptions.logHeaders) { JkLog.info("Method " + methodName + " succeeded in " + JkUtilsTime.durationInMillis(time) + " milliseconds."); } } catch (final RuntimeException e) { JkLog.info("Method " + methodName + " failed in " + JkUtilsTime.durationInMillis(time) + " milliseconds."); throw e; } } static JkRepoSet repos() { return JkRepoSet.of(JkRepoConfigOptionLoader.runRepository(), JkRepo.ofLocal()); } private static List toRelativePaths(Path from, List files) { final List result = new LinkedList<>(); for (final Path file : files) { final String relPath = from.relativize(file).toString(); result.add(relPath); } return result; } @Override public String toString() { return this.projectBaseDir.getFileName().toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy