objectionary/eo

View on GitHub
eo-maven-plugin/src/main/java/org/eolang/maven/SafeMojo.java

Summary

Maintainability
D
1 day
Test Coverage
File `SafeMojo.java` has 342 lines of code (exceeds 250 allowed). Consider refactoring.
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.maven;
 
import com.jcabi.log.Logger;
import com.yegor256.xsline.Shift;
import com.yegor256.xsline.TrLambda;
import com.yegor256.xsline.Train;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.cactoos.scalar.Sticky;
import org.cactoos.set.SetOf;
import org.slf4j.impl.StaticLoggerBinder;
 
/**
* Abstract Mojo for all others.
*
* @since 0.1
*/
@SuppressWarnings("PMD.TooManyFields")
abstract class SafeMojo extends AbstractMojo {
 
/**
* Maven project.
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(defaultValue = "${project}", readonly = true)
protected MavenProject project;
 
/**
* Maven session.
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(defaultValue = "${session}", readonly = true)
protected MavenSession session;
 
/**
* Maven plugin manager.
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Component
protected BuildPluginManager manager;
 
/**
* Directory where classes are stored in target.
* @checkstyle VisibilityModifierCheck (10 lines)
* @checkstyle MemberNameCheck (8 lines)
*/
@Parameter(
defaultValue = "${project.build.directory}/classes",
readonly = true,
required = true
)
protected File classesDir;
 
/**
* File with foreign "tojos".
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(
property = "eo.foreign",
required = true,
defaultValue = "${project.build.directory}/eo-foreign.json"
)
protected File foreign;
 
/**
* Format of "foreign" file ("json" or "csv").
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(property = "eo.foreignFormat", required = true, defaultValue = "csv")
protected String foreignFormat = "csv";
 
/**
* Directory in which .eo files are located.
*
* @checkstyle VisibilityModifierCheck (10 lines)
* @checkstyle MemberNameCheck (8 lines)
*/
@Parameter(
property = "eo.sourcesDir",
required = true,
defaultValue = "${project.basedir}/src/main/eo"
)
protected File sourcesDir;
 
/**
* Target directory.
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(
property = "eo.targetDir",
required = true,
defaultValue = "${project.build.directory}/eo"
)
protected File targetDir;
 
/**
* Output.
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(
property = "eo.outputDir",
required = true,
defaultValue = "${project.build.outputDirectory}"
)
protected File outputDir;
 
/**
* Current scope (either "compile" or "test").
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(property = "eo.scope")
protected String scope = "compile";
 
/**
* The path to a text file where paths of all added
* .class (and maybe others) files are placed.
* @since 0.11.0
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(
property = "eo.placed",
required = true,
defaultValue = "${project.build.directory}/eo-placed.json"
)
protected File placed;
 
/**
* Format of "placed" file ("json" or "csv").
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(property = "eo.placedFormat", required = true, defaultValue = "json")
protected String placedFormat = "json";
 
/**
* The path to a text file where paths of generated java files per EO program.
* @since 0.11.0
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(
property = "eo.transpiled",
required = true,
defaultValue = "${project.build.directory}/eo-transpiled.json"
)
protected File transpiled;
 
/**
* The path of the file where XSL measurements (time of execution
* in milliseconds) will be stored.
* @since 0.41.0
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(
property = "eo.xslMeasuresFile",
required = true,
defaultValue = "${project.build.directory}/eo/xsl-measures.json"
)
protected File xslMeasures;
 
/**
* Mojo execution timeout in seconds.
* @since 0.28.12
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(property = "eo.timeout")
protected Integer timeout = Integer.MAX_VALUE;
 
/**
* Format of "transpiled" file ("json" or "csv").
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(property = "eo.transpiledFormat", required = true, defaultValue = "csv")
protected String transpiledFormat = "csv";
 
/**
* Track optimization steps into intermediate XMIR files?
*
* @since 0.24.0
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@SuppressWarnings("PMD.LongVariable")
@Parameter(property = "eo.trackTransformationSteps", required = true, defaultValue = "false")
protected boolean trackTransformationSteps;
 
/**
* If set to TRUE, the exception on exit will be printed in details
* to the log.
* @since 0.29.0
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(property = "eo.unrollExitError")
protected boolean unrollExitError = true;
 
/**
* EO cache directory.
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (10 lines)
*/
@Parameter(property = "eo.cache")
protected File cache = Paths.get(System.getProperty("user.home")).resolve(".eo").toFile();
 
/**
* Rewrite binaries in output directory or not.
* @since 0.32.0
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(property = "eo.rewriteBinaries", defaultValue = "true")
@SuppressWarnings("PMD.ImmutableField")
protected boolean rewriteBinaries = true;
 
/**
* If we are offline and not able to download anything from the internet.
* @since 0.32.0
* @checkstyle VisibilityModifierCheck (10 lines)
* @checkstyle MemberNameCheck (8 lines)
*/
@Parameter(property = "eo.offline", required = true, defaultValue = "false")
protected boolean offline;
 
/**
* The Git tag to pull objects from, in objectionary.
* @since 0.21.0
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@SuppressWarnings("PMD.ImmutableField")
@Parameter(property = "eo.tag", required = true, defaultValue = "master")
protected String tag = "master";
 
/**
* Pull again even if the .eo file is already present?
* @since 0.10.0
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(property = "eo.overWrite", required = true, defaultValue = "false")
protected boolean overWrite;
 
/**
* Skip artifact with the version 0.0.0.
*
* @since 0.9.0
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@Parameter(property = "eo.skipZeroVersions", required = true, defaultValue = "true")
protected boolean skipZeroVersions;
 
/**
* Place only binaries that have EO sources inside jar.
* @since 0.31
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter
@SuppressWarnings("PMD.LongVariable")
protected boolean placeBinariesThatHaveSources;
 
/**
* Fail resolution process on conflicting dependencies.
*
* @since 0.1.0
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(property = "eo.ignoreVersionConflicts", required = true, defaultValue = "false")
@SuppressWarnings("PMD.LongVariable")
protected boolean ignoreVersionConflicts;
 
/**
* Shall we discover JAR artifacts for .EO sources?
*
* @since 0.12.0
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(property = "eo.discoverSelf", required = true, defaultValue = "false")
protected boolean discoverSelf;
 
/**
* List of inclusion GLOB filters for finding class files.
* @since 0.15
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter
protected Set<String> includeBinaries = new SetOf<>("**");
 
/**
* List of exclusion GLOB filters for finding class files.
* @since 0.15
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter
protected Set<String> excludeBinaries = new SetOf<>();
 
/**
* Add eo-runtime dependency to the classpath.
*
* <p>That property is useful only for eo-runtime library compilation.
* When you compile eo-runtime, you don't want to add eo-runtime from foreign sources
* (since you compile an eo-runtime library and classpath will anyway have all required classes)
* and in this case, you should set this property to true.
* In any other cases, the eo-runtime
* dependency will be downloaded and added to the classpath automatically.</p>
*
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(property = "eo.ignoreRuntime", required = true, defaultValue = "false")
@SuppressWarnings("PMD.ImmutableField")
protected boolean ignoreRuntime;
 
/**
* Fail resolution process on transitive dependencies.
*
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(property = "eo.ignoreTransitive", required = true, defaultValue = "false")
@SuppressWarnings("PMD.ImmutableField")
protected boolean ignoreTransitive;
 
/**
* Whether we should fail on warning.
*
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@SuppressWarnings("PMD.ImmutableField")
@Parameter(property = "eo.failOnWarning", required = true, defaultValue = "true")
protected boolean failOnWarning;
 
/**
* Whether we should lint all the sources together as package.
*
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@SuppressWarnings("PMD.ImmutableField")
@Parameter(property = "eo.lintAsPackage", required = true, defaultValue = "true")
protected boolean lintAsPackage;
 
/**
* Whether we should skip linting at all.
*
* @checkstyle MemberNameCheck (10 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@SuppressWarnings("PMD.ImmutableField")
@Parameter(property = "eo.skipLinting", required = true, defaultValue = "false")
protected boolean skipLinting;
 
/**
* The current version of eo-maven-plugin.
* Maven 3 only.
* You can read more about that property
* <a href="https://maven.apache.org/plugin-tools/maven-plugin-tools-annotations/index.html#Supported_Annotations">here</a>.
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (7 lines)
*/
@Parameter(defaultValue = "${plugin}", readonly = true)
protected PluginDescriptor plugin;
 
/**
* Placed tojos.
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
protected final TjsPlaced placedTojos = new TjsPlaced(
new Sticky<>(() -> Catalogs.INSTANCE.make(this.placed.toPath(), this.placedFormat))
);
 
/**
* Cached transpiled tojos.
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
protected final TjsTranspiled transpiledTojos = new TjsTranspiled(
new Sticky<>(() -> Catalogs.INSTANCE.make(this.transpiled.toPath(), this.transpiledFormat))
);
 
/**
* The central.
*
* @checkstyle MemberNameCheck (7 lines)
* @checkstyle VisibilityModifierCheck (5 lines)
*/
@SuppressWarnings("PMD.ImmutableField")
protected BiConsumer<Dependency, Path> central;
 
/**
* Cached tojos.
* @checkstyle VisibilityModifierCheck (5 lines)
*/
private final TjsForeign tojos = new TjsForeign(
() -> Catalogs.INSTANCE.make(this.foreign.toPath(), this.foreignFormat),
() -> this.scope
);
 
/**
* Whether we should skip goal execution.
*/
@Parameter(property = "eo.skip", defaultValue = "false")
@SuppressWarnings("PMD.ImmutableField")
private boolean skip;
 
@Override
public String toString() {
return this.getClass().getSimpleName();
}
 
/**
* Execute it.
* @throws MojoFailureException If fails during build
* @checkstyle NoJavadocForOverriddenMethodsCheck (10 lines)
* @checkstyle CyclomaticComplexityCheck (70 lines)
*/
Method `execute` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.
Method `execute` has 47 lines of code (exceeds 25 allowed). Consider refactoring.
@Override
@SuppressWarnings("PMD.CognitiveComplexity")
public final void execute() throws MojoFailureException {
StaticLoggerBinder.getSingleton().setMavenLog(this.getLog());
if (this.skip) {
if (Logger.isInfoEnabled(this)) {
Logger.info(
this, "Execution skipped due to eo.skip option"
);
}
} else {
try {
if (this.central == null) {
this.central = new Central(this.project, this.session, this.manager);
}
final long start = System.nanoTime();
this.execWithTimeout();
if (Logger.isDebugEnabled(this)) {
Logger.debug(
this,
"Execution of %s took %[nano]s",
this.getClass().getSimpleName(),
System.nanoTime() - start
);
}
} catch (final TimeoutException ex) {
this.exitError(
Logger.format(
"Timeout %[ms]s for Mojo execution is reached",
TimeUnit.SECONDS.toMillis(this.timeout)
),
ex
);
} catch (final ExecutionException ex) {
this.exitError(
String.format("'%s' execution failed", this),
ex
);
} finally {
if (this.foreign != null) {
SafeMojo.closeTojos(this.tojos);
}
if (this.placed != null) {
SafeMojo.closeTojos(this.placedTojos);
}
if (this.transpiled != null) {
SafeMojo.closeTojos(this.transpiledTojos);
}
}
}
}
 
/**
* Tojos to use, in my scope only.
* @return Tojos to use
* @checkstyle AnonInnerLengthCheck (100 lines)
*/
protected final TjsForeign scopedTojos() {
return this.tojos;
}
 
/**
* Tojos to use, in "compile" scope only.
* @return Tojos to use
* @checkstyle AnonInnerLengthCheck (100 lines)
*/
protected final TjsForeign compileTojos() {
return new TjsForeign(
() -> Catalogs.INSTANCE.make(this.foreign.toPath(), this.foreignFormat),
() -> "compile"
);
}
 
/**
* Make a measured train from another train.
* @param train The train
* @return Measured train
*/
Method `measured` has 27 lines of code (exceeds 25 allowed). Consider refactoring.
protected final Train<Shift> measured(final Train<Shift> train) {
if (this.xslMeasures.getParentFile().mkdirs()) {
Logger.debug(this, "Directory created for %[file]s", this.xslMeasures);
}
if (!this.xslMeasures.getParentFile().exists()) {
throw new IllegalArgumentException(
String.format(
"For some reason, the directory %s is absent, can't write measures to %s",
this.xslMeasures.getParentFile(),
this.xslMeasures
)
);
}
if (this.xslMeasures.isDirectory()) {
throw new IllegalArgumentException(
String.format(
"This is not a file but a directory, can't write to it: %s",
this.xslMeasures
)
);
}
return new TrLambda(
train,
shift -> new StMeasured(
shift,
this.xslMeasures.toPath()
)
);
}
 
/**
* Exec it.
* @throws IOException If fails
*/
abstract void exec() throws IOException;
 
/**
* Runs exec command with timeout if needed.
*
* @throws ExecutionException If unexpected exception happened during execution
* @throws TimeoutException If timeout limit reached
*/
Method `execWithTimeout` has 32 lines of code (exceeds 25 allowed). Consider refactoring.
Method `execWithTimeout` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
@SuppressWarnings("PMD.CloseResource")
private void execWithTimeout() throws ExecutionException, TimeoutException {
final ExecutorService service = Executors.newSingleThreadExecutor();
try {
service.submit(
() -> {
this.exec();
return new Object();
}
).get(this.timeout.longValue(), TimeUnit.SECONDS);
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(
Logger.format(
"Timeout %[ms]s thread was interrupted",
TimeUnit.SECONDS.toMillis(this.timeout.longValue())
),
ex
);
} finally {
boolean terminated = false;
service.shutdown();
while (!terminated) {
try {
terminated = service.awaitTermination(60, TimeUnit.SECONDS);
if (terminated) {
service.shutdownNow();
}
} catch (final InterruptedException ex) {
service.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}
 
/**
* Close it safely.
* @param res The resource
* @throws MojoFailureException If fails
*/
private static void closeTojos(final Closeable res) throws MojoFailureException {
try {
res.close();
} catch (final IOException ex) {
throw new MojoFailureException(ex);
}
}
 
/**
* Make an error for the exit and throw it.
* @param msg The message
* @param exp Original problem
* @throws MojoFailureException For sure
*/
Method `exitError` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.
Method `exitError` has 32 lines of code (exceeds 25 allowed). Consider refactoring.
private void exitError(final String msg, final Throwable exp)
throws MojoFailureException {
if (!this.unrollExitError) {
return;
}
final MojoFailureException out = new MojoFailureException(msg, exp);
final List<String> causes = SafeMojo.causes(exp);
for (int pos = 0; pos < causes.size(); ++pos) {
final String cause = causes.get(pos);
if (cause == null) {
causes.remove(pos);
break;
}
}
int idx = 0;
while (true) {
if (idx >= causes.size()) {
break;
}
final String cause = causes.get(idx);
for (int later = idx + 1; later < causes.size(); ++later) {
final String another = causes.get(later);
if (another != null && cause.contains(another)) {
causes.remove(idx);
idx -= 1;
break;
}
}
idx += 1;
}
for (final String cause : new LinkedHashSet<>(causes)) {
Logger.error(this, cause);
}
throw out;
}
 
/**
* Turn the exception into an array of causes.
* @param exp Original problem
* @return List of causes
*/
private static List<String> causes(final Throwable exp) {
final List<String> causes = new LinkedList<>();
causes.add(exp.getMessage());
final Throwable cause = exp.getCause();
if (cause != null) {
causes.addAll(SafeMojo.causes(cause));
}
return causes;
}
}