2
0
Fork 0
mirror of https://github.com/ethauvin/bld.git synced 2025-04-26 00:37:10 -07:00

First commit of standalone repo

This commit is contained in:
Geert Bevin 2023-05-09 21:20:16 -04:00
commit 696b23b57a
241 changed files with 28028 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import rife.resources.ResourceFinderClasspath;
import rife.resources.exceptions.ResourceFinderErrorException;
/**
* Singleton class that provides access to the current RIFE2 version as a string.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public class BldVersion {
private String version_;
BldVersion() {
ResourceFinderClasspath resource_finder = ResourceFinderClasspath.instance();
try {
version_ = resource_finder.getContent("BLD_VERSION");
} catch (ResourceFinderErrorException e) {
version_ = null;
}
if (version_ != null) {
version_ = version_.trim();
}
if (null == version_) {
version_ = "unknown version";
}
}
private String getVersionString() {
return version_;
}
public static String getVersion() {
return BldVersionSingleton.INSTANCE.getVersionString();
}
}

View file

@ -0,0 +1,14 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
/**
* Helper class to avoid Double Check Locking
* and still have a thread-safe singleton pattern
*/
class BldVersionSingleton {
static final BldVersion INSTANCE = new BldVersion();
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import java.lang.annotation.*;
/**
* Declares a {@link BuildExecutor} method to be used as a build command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface BuildCommand {
/**
* When provided, specifies a name for the build command that can be
* different from the method name.
*
* @return a string representing the build command name
* @since 1.5
*/
String value() default "";
/**
* When provided, specifies a short description about the command.
*
* @return the short summary, defaults to {@code ""}
* @since 1.5.12
*/
String summary() default "";
/**
* When provided, specifies the full help description of a command.
*
* @return the full help description, defaults to {@code ""}
* @since 1.5.12
*/
String description() default "";
/**
* When provided, specifies a class that provides help about the
* build command.
*
* @return a class providing help information
* @since 1.5
*/
Class<? extends CommandHelp> help() default CommandHelp.class;
}

View file

@ -0,0 +1,467 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import rife.bld.dependencies.DependencyResolver;
import rife.bld.dependencies.Repository;
import rife.bld.help.HelpHelp;
import rife.bld.operations.HelpOperation;
import rife.bld.operations.exceptions.ExitStatusException;
import rife.ioc.HierarchicalProperties;
import rife.tools.ExceptionUtils;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* Base class that executes build commands from a list of arguments.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @see BuildCommand
* @see CommandDefinition
* @since 1.5
*/
public class BuildExecutor {
public static final File RIFE2_USER_DIR = new File(System.getProperty("user.home"), ".rife2");
public static final String BLD_PROPERTIES = "bld.properties";
public static final String LOCAL_PROPERTIES = "local.properties";
private static final String ARG_HELP1 = "--help";
private static final String ARG_HELP2 = "-h";
private static final String ARG_HELP3 = "-?";
private static final String ARG_STACKTRACE1 = "--stacktrace";
private static final String ARG_STACKTRACE2 = "-s";
private final HierarchicalProperties properties_;
private List<String> arguments_ = Collections.emptyList();
private Map<String, CommandDefinition> buildCommands_ = null;
private final AtomicReference<String> currentCommandName_ = new AtomicReference<>();
private final AtomicReference<CommandDefinition> currentCommandDefinition_ = new AtomicReference<>();
private int exitStatus_ = 0;
/**
* Show the full Java stacktrace when exceptions occur, as opposed
* to the chain of messages.
* <p>
* Defaults to {@code false}, can be set to {@code true} by setting
* through code or by adding {@code --stacktrace} as a CLI argument.
*
* @since 1.5.19
*/
protected boolean showStacktrace = false;
/**
* Creates a new build executor instance.
*
* @since 1.5
*/
public BuildExecutor() {
properties_ = setupProperties(workDirectory());
Repository.resolveMavenLocal(properties());
}
/**
* Creates a properties hierarchy for bld execution.
*
* @param workDirectory the directory where the project build files are location
* @return the properties hierarchy
* @since 1.5.12
*/
public static HierarchicalProperties setupProperties(File workDirectory) {
var system_properties = HierarchicalProperties.createSystemInstance();
var java_properties = system_properties;
system_properties = java_properties.getParent();
HierarchicalProperties bld_properties = null;
HierarchicalProperties local_properties = null;
var bld_properties_file = new File(RIFE2_USER_DIR, BLD_PROPERTIES);
if (bld_properties_file.exists() && bld_properties_file.isFile() && bld_properties_file.canRead()) {
try {
var bld = new Properties();
bld.load(new FileReader(bld_properties_file));
bld_properties = new HierarchicalProperties();
bld_properties.putAll(bld);
bld_properties.parent(system_properties);
} catch (IOException e) {
Logger.getLogger("rife.bld").warning("Unable to parse " + bld_properties_file + " as a properties file:\n" + e.getMessage());
}
}
var local_properties_file = new File(workDirectory, LOCAL_PROPERTIES);
if (local_properties_file.exists() && local_properties_file.isFile() && local_properties_file.canRead()) {
try {
var local = new Properties();
local.load(new FileReader(local_properties_file));
local_properties = new HierarchicalProperties();
local_properties.putAll(local);
local_properties.parent(Objects.requireNonNullElse(bld_properties, system_properties));
} catch (IOException e) {
Logger.getLogger("rife.bld").warning("Unable to parse " + local_properties_file + " as a properties file:\n" + e.getMessage());
}
}
java_properties.parent(
Objects.requireNonNullElse(local_properties,
Objects.requireNonNullElse(bld_properties, system_properties)));
final HierarchicalProperties properties = new HierarchicalProperties();
properties.parent(java_properties);
return properties;
}
/**
* Returns the properties uses by this conversation.
*
* @return the instance of {@code HierarchicalProperties} that is used
* by this build executor
* @since 1.5
*/
public HierarchicalProperties properties() {
return properties_;
}
/**
* Retrieve a property from the {@link #properties()}.
*
* @param name the name of the property
* @return the requested property; or {@code null} if it doesn't exist
* @since 1.5.15
*/
public String property(String name) {
return properties().getValueString(name);
}
/**
* Retrieve a property from the {@link #properties()} with a default value.
*
* @param name the name of the property
* @param defaultValue the value that should be used as a fallback
* @return the requested property; or the default value if it doesn't exist
* @since 1.5.15
*/
public String property(String name, String defaultValue) {
return properties().getValueString(name, defaultValue);
}
/**
* Checks for the existence of a property in {@link #properties()}.
*
* @param name the name of the property
* @return {@code true} if the property exists; or {@code false} otherwise
* @since 1.5.15
*/
public boolean hasProperty(String name) {
return properties().contains(name);
}
/**
* Returns the work directory of the project.
* Defaults to this process's user working directory, which when running
* through the bld wrapper corresponds to the top-level project directory.
*
* @since 1.5.12
*/
public File workDirectory() {
return new File(System.getProperty("user.dir"));
}
/**
* Set the exist status to use at the end of the execution.
*
* @param status sets the exit status
* @since 1.5.1
*/
public void exitStatus(int status) {
exitStatus_ = status;
}
/**
* Retrieves the exit status.
*
* @return the exit status
* @since 1.5.1
*/
public int exitStatus() {
return exitStatus_;
}
/**
* Execute the build commands from the provided arguments.
* <p>
* While the build is executing, the arguments can be retrieved
* using {@link #arguments()}.
*
* @param arguments the arguments to execute the build with
* @return the exist status
* @since 1.5.1
*/
public int execute(String[] arguments) {
arguments_ = new ArrayList<>(Arrays.asList(arguments));
var show_help = false;
show_help |= arguments_.removeAll(List.of(ARG_HELP1, ARG_HELP2, ARG_HELP3));
showStacktrace |= arguments_.removeAll(List.of(ARG_STACKTRACE1, ARG_STACKTRACE2));
show_help |= arguments_.isEmpty();
if (show_help) {
new HelpOperation(this, Collections.emptyList()).execute();
return exitStatus_;
}
while (!arguments_.isEmpty()) {
var command = arguments_.remove(0);
try {
if (!executeCommand(command)) {
break;
}
} catch (Throwable e) {
exitStatus(1);
System.err.println();
if (showStacktrace) {
System.err.println(ExceptionUtils.getExceptionStackTrace(e));
} else {
boolean first = true;
var e2 = e;
while (e2 != null) {
if (e2.getMessage() != null) {
if (!first) {
System.err.print("> ");
}
System.err.println(e2.getMessage());
first = false;
}
e2 = e2.getCause();
}
if (first) {
System.err.println(ExceptionUtils.getExceptionStackTrace(e));
}
}
}
}
return exitStatus_;
}
/**
* Starts the execution of the build. This method will call
* System.exit() when done with the appropriate exit status.
*
* @param arguments the arguments to execute the build with
* @see #execute
* @since 1.5.1
*/
public void start(String[] arguments) {
System.exit(execute(arguments));
}
/**
* Retrieves the list of arguments that are being processed.
*
* @return the list of arguments
* @since 1.5
*/
public List<String> arguments() {
return arguments_;
}
/**
* Retrieves the commands that can be executed by this {@code BuildExecutor}.
*
* @return a map containing the name of the build command and the method that
* corresponds to execution
* @see BuildCommand
* @since 1.5
*/
public Map<String, CommandDefinition> buildCommands() {
if (buildCommands_ == null) {
var build_commands = new TreeMap<String, CommandDefinition>();
Class<?> klass = getClass();
try {
while (klass != null) {
for (var method : klass.getDeclaredMethods()) {
if (method.getParameters().length == 0 && method.isAnnotationPresent(BuildCommand.class)) {
method.setAccessible(true);
var name = method.getName();
var annotation = method.getAnnotation(BuildCommand.class);
var annotation_name = annotation.value();
if (annotation_name != null && !annotation_name.isEmpty()) {
name = annotation_name;
}
if (!build_commands.containsKey(name)) {
var build_help = annotation.help();
CommandHelp command_help = null;
if (build_help != null && build_help != CommandHelp.class) {
command_help = build_help.getDeclaredConstructor().newInstance();
}
var summary = annotation.summary();
var description = annotation.description();
if ((summary != null && !summary.isBlank()) ||
(description != null && !description.isBlank())) {
if (summary == null) summary = "";
if (description == null) description = "";
if (command_help != null) {
if (summary.isBlank()) summary = command_help.getSummary();
if (description.isBlank()) description = command_help.getDescription(name);
}
command_help = new AnnotatedCommandHelp(summary, description);
}
build_commands.put(name, new CommandAnnotated(this, method, command_help));
}
}
}
klass = klass.getSuperclass();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
buildCommands_ = build_commands;
}
return buildCommands_;
}
private static class AnnotatedCommandHelp implements CommandHelp {
private final String summary_;
private final String description_;
AnnotatedCommandHelp(String summary, String description) {
summary_ = summary;
description_ = description;
}
@Override
public String getSummary() {
return summary_;
}
@Override
public String getDescription(String topic) {
return description_;
}
}
/**
* Performs the execution of a single command.
*
* @param command the name of the command to execute
* @return {@code true} when the command was found and executed; or
* {@code false} if the command couldn't be found
* @throws Throwable when an exception occurred during the command execution
* @see BuildCommand
* @since 1.5
*/
public boolean executeCommand(String command)
throws Throwable {
var matched_command = command;
var definition = buildCommands().get(command);
// try to find a match for the provided command amongst
// the ones that are known
if (definition == null) {
// try to find starting matching options
var matches = new ArrayList<>(buildCommands().keySet().stream()
.filter(c -> c.toLowerCase().startsWith(command.toLowerCase()))
.toList());
if (matches.isEmpty()) {
// try to find fuzzy matching options
var fuzzy_regexp = new StringBuilder("^.*");
for (var ch : command.toCharArray()) {
fuzzy_regexp.append("\\Q");
fuzzy_regexp.append(ch);
fuzzy_regexp.append("\\E.*");
}
fuzzy_regexp.append("$");
var fuzzy_pattern = Pattern.compile(fuzzy_regexp.toString());
matches.addAll(buildCommands().keySet().stream()
.filter(c -> fuzzy_pattern.matcher(c.toLowerCase()).matches())
.toList());
}
// only proceed if exactly one match was found
if (matches.size() == 1) {
matched_command = matches.get(0);
System.out.println("Executing matched command: " + matched_command);
definition = buildCommands().get(matched_command);
}
}
// execute the command if we found one
if (definition != null) {
try {
currentCommandName_.set(matched_command);
currentCommandDefinition_.set(definition);
definition.execute();
} catch (ExitStatusException e) {
exitStatus(e.getExitStatus());
return e.getExitStatus() == ExitStatusException.EXIT_SUCCESS;
} finally {
currentCommandDefinition_.set(null);
currentCommandName_.set(null);
}
} else {
new HelpOperation(this, arguments()).executePrintOverviewHelp();
System.err.println();
System.err.println("ERROR: unknown command '" + command + "'");
return false;
}
return true;
}
/**
* Retrieves the name of the currently executing command.
*
* @return the name of the current command; or
* {@code null} if no command is currently executing
* @since 1.5.12
*/
public String getCurrentCommandName() {
return currentCommandName_.get();
}
/**
* Retrieves the definition of the currently executing command.
*
* @return the definition of the current command; or
* {@code null} if no command is currently executing
* @since 1.5.12
*/
public CommandDefinition getCurrentCommandDefinition() {
return currentCommandDefinition_.get();
}
/**
* The standard {@code help} command.
*
* @since 1.5
*/
@BuildCommand(help = HelpHelp.class)
public void help() {
new HelpOperation(this, arguments()).execute();
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import rife.bld.help.*;
import rife.bld.operations.*;
/**
* Implements the RIFE2 CLI build executor that is available when running
* the RIFE2 jar as an executable jar.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class Cli extends BuildExecutor {
private final CreateBaseOperation createBaseOperation_ = new CreateBaseOperation();
private final CreateBlankOperation createBlankOperation_ = new CreateBlankOperation();
private final CreateLibOperation createLibOperation_ = new CreateLibOperation();
private final CreateRife2Operation createRife2Operation_ = new CreateRife2Operation();
private final UpgradeOperation upgradeOperation_ = new UpgradeOperation();
private final VersionOperation versionOperation_ = new VersionOperation();
/**
* The standard {@code create} command.
*
* @throws Exception when an error occurred during the creation process
* @since 1.5
*/
@BuildCommand(help = CreateRife2Help.class)
public void create()
throws Exception {
createRife2Operation_.executeOnce(() -> createRife2Operation_.fromArguments(arguments()));
}
/**
* The standard {@code create-blank} command.
*
* @throws Exception when an error occurred during the creation process
* @since 1.5
*/
@BuildCommand(value = "create-blank", help = CreateBlankHelp.class)
public void createBlank()
throws Exception {
createBlankOperation_.executeOnce(() -> createBlankOperation_.fromArguments(arguments()));
}
/**
* The standard {@code create-base} command.
*
* @throws Exception when an error occurred during the creation process
* @since 1.5.20
*/
@BuildCommand(value = "create-base", help = CreateBaseHelp.class)
public void createBase()
throws Exception {
createBaseOperation_.executeOnce(() -> createBaseOperation_.fromArguments(arguments()));
}
/**
* The standard {@code create-lib} command.
*
* @throws Exception when an error occurred during the creation process
* @since 1.6
*/
@BuildCommand(value = "create-lib", help = CreateLibHelp.class)
public void createLib()
throws Exception {
createLibOperation_.executeOnce(() -> createLibOperation_.fromArguments(arguments()));
}
/**
* The standard {@code upgrade} command.
*
* @throws Exception when an error occurred during the upgrade process
* @since 1.5
*/
@BuildCommand(help = UpgradeHelp.class)
public void upgrade()
throws Exception {
upgradeOperation_.executeOnce();
}
/**
* The standard {@code version} command.
*
* @since 1.5.2
*/
@BuildCommand(help = VersionHelp.class)
public void version()
throws Exception {
versionOperation_.executeOnce();
}
public static void main(String[] arguments)
throws Exception {
new Cli().start(arguments);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class CommandAnnotated implements CommandDefinition {
private final BuildExecutor executor_;
private final Method method_;
private final CommandHelp help_;
CommandAnnotated(BuildExecutor executor, Method method, CommandHelp help) {
executor_ = executor;
method_ = method;
if (help == null) {
help = new CommandHelp() {};
}
help_ = help;
}
public void execute()
throws Throwable {
try {
method_.invoke(executor_);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
public CommandHelp getHelp() {
return help_;
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
/**
* Defines the logic for a build command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public interface CommandDefinition {
/**
* Called when a build command is executed.
*
* @throws Throwable when an error occurred during the execution of the build command
* @since 1.5
*/
void execute() throws Throwable;
/**
* Retrieves the help information of a build command.
* <p>
* Defaults to blank help sections.
*
* @return this build command's help information
* @since 1.5
*/
default CommandHelp getHelp() {
return new CommandHelp() {};
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
/**
* Interface that provides help texts to display about {@link BuildExecutor} commands.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public interface CommandHelp {
/**
* Returns a short description about the command.
*
* @return the short summary, defaults to {@code ""}
* @since 1.5
*/
default String getSummary() {
return "";
}
/**
* Returns the full help description of a command.
*
* @return the full help description, defaults to {@code ""}
* @since 1.5
*/
default String getDescription(String topic) {
return "";
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import java.io.File;
/**
* Combines the information of a filesystem file with the name it's intended
* to have.
*
* @param name the intended name of the file
* @param file the location on the filesystem
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public record NamedFile(String name, File file) {
}

View file

@ -0,0 +1,195 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import rife.bld.help.*;
import rife.bld.operations.*;
import java.util.*;
import java.util.jar.Attributes;
/**
* Provides the configuration and commands of a Java project for the
* build system with all standard commands ready to go.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class Project extends BaseProject {
/*
* Standard build commands
*/
private final JavadocOperation javadocOperation_ = new JavadocOperation();
private final PrecompileOperation precompileOperation_ = new PrecompileOperation();
private final JarOperation jarOperation_ = new JarOperation();
private final JarOperation jarSourcesOperation_ = new JarOperation();
private final JarOperation jarJavadocOperation_ = new JarOperation();
private final JUnitOperation junitTestOperation_ = new JUnitOperation();
private final UberJarOperation uberJarOperation_ = new UberJarOperation();
@Override
public JUnitOperation testOperation() {
return junitTestOperation_;
}
/**
* Retrieves the project's default javadoc operation.
*
* @return the default javadoc operation instance
* @since 1.5.18
*/
public JavadocOperation javadocOperation() {
return javadocOperation_;
}
/**
* Retrieves the project's default precompile operation.
*
* @return the default precompile operation instance
* @since 1.5.18
*/
public PrecompileOperation precompileOperation() {
return precompileOperation_;
}
/**
* Retrieves the project's default jar operation.
*
* @return the default jar operation instance
* @since 1.5.18
*/
public JarOperation jarOperation() {
return jarOperation_;
}
/**
* Retrieves the project's default jar operation for sources.
*
* @return the default jar operation instance for sources
* @since 1.5.18
*/
public JarOperation jarSourcesOperation() {
return jarSourcesOperation_;
}
/**
* Retrieves the project's default jar operation for javadoc.
*
* @return the default jar operation instance for javadoc
* @since 1.5.18
*/
public JarOperation jarJavadocOperation() {
return jarJavadocOperation_;
}
/**
* Retrieves the project's default uberjar operation.
*
* @return the default uberjar operation instance
* @since 1.5.18
*/
public UberJarOperation uberJarOperation() {
return uberJarOperation_;
}
@BuildCommand(help = JUnitHelp.class)
@Override
public void test()
throws Exception {
super.test();
}
/**
* Standard build command, generates javadoc.
*
* @since 1.5.10
*/
@BuildCommand(help = JavadocHelp.class)
public void javadoc()
throws Exception {
javadocOperation().executeOnce(() -> javadocOperation().fromProject(this));
}
/**
* Standard build command, pre-compiles RIFE2 templates to class files.
*
* @since 1.5
*/
@BuildCommand(help = PrecompileHelp.class)
public void precompile()
throws Exception {
precompileOperation().executeOnce(() -> precompileOperation().fromProject(this));
}
/**
* Standard build command, creates a jar archive for the project.
*
* @since 1.5
*/
@BuildCommand(help = JarHelp.class)
public void jar()
throws Exception {
compile();
precompile();
jarOperation().executeOnce(() -> jarOperation().fromProject(this));
}
/**
* Standard build command, creates a sources jar archive for the project.
*
* @since 1.5.10
*/
@BuildCommand(value = "jar-sources", help = JarSourcesHelp.class)
public void jarSources()
throws Exception {
jarSourcesOperation().executeOnce(() -> jarSourcesOperation()
.manifestAttributes(Map.of(Attributes.Name.MANIFEST_VERSION, "1.0"))
.sourceDirectories(List.of(srcMainJavaDirectory()))
.destinationDirectory(buildDistDirectory())
.destinationFileName(sourcesJarFileName()));
}
/**
* Standard build command, creates a javadoc jar archive for the project.
*
* @since 1.5.10
*/
@BuildCommand(value = "jar-javadoc", help = JarJavadocHelp.class)
public void jarJavadoc()
throws Exception {
compile();
javadoc();
jarJavadocOperation().executeOnce(() -> jarJavadocOperation().manifestAttributes(Map.of(Attributes.Name.MANIFEST_VERSION, "1.0"))
.sourceDirectories(List.of(buildJavadocDirectory()))
.destinationDirectory(buildDistDirectory())
.destinationFileName(javadocJarFileName()));
}
/**
* Standard build command, creates an UberJar archive for the project.
*
* @since 1.5
*/
@BuildCommand(help = UberJarHelp.class)
public void uberjar()
throws Exception {
jar();
uberJarOperation().executeOnce(() -> uberJarOperation().fromProject(this));
}
/**
* Standard publish command, uploads artifacts to the publication repository.
*
* @since 1.5.7
*/
@BuildCommand(help = PublishHelp.class)
public void publish()
throws Exception {
jar();
jarSources();
jarJavadoc();
publishOperation().executeOnce(() -> publishOperation().fromProject(this));
}
}

View file

@ -0,0 +1,134 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld;
import rife.bld.help.*;
import rife.bld.operations.*;
import rife.bld.publish.PublishArtifact;
import java.io.File;
import java.util.List;
import java.util.Objects;
/**
* Provides the configuration and commands of a Java web project for the
* build system.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class WebProject extends Project {
/**
* The main war archive creation.
*
* @see #warFileName()
* @since 1.5.0
*/
protected String warFileName = null;
/**
* The main webapp directory.
*
* @see #srcMainWebappDirectory()
* @since 1.5
*/
protected File srcMainWebappDirectory = null;
/*
* Standard build commands
*/
private final WarOperation warOperation_ = new WarOperation();
/**
* Retrieves the project's default war operation.
*
* @return the default war operation instance
* @since 1.5.18
*/
protected WarOperation warOperation() {
return warOperation_;
}
/**
* Standard build command, creates an UberJar archive for the web project.
*
* @since 1.5
*/
@Override
public void uberjar()
throws Exception {
jar();
uberJarOperation().executeOnce(() -> {
uberJarOperation().fromProject(this);
uberJarOperation().sourceDirectories(List.of(new NamedFile("webapp", srcMainWebappDirectory())));
uberJarOperation().jarSourceFiles().addAll(standaloneClasspathJars());
});
}
/**
* Standard build command, creates a war archive for the web project.
*
* @since 1.5
*/
@BuildCommand(help = WarHelp.class)
public void war()
throws Exception {
jar();
warOperation().executeOnce(() -> warOperation().fromProject(this));
}
/**
* Standard publish-web command, uploads artifacts to the publication repository.
*
* @since 1.5.7
*/
@BuildCommand(help = PublishWebHelp.class)
public void publish()
throws Exception {
jar();
jarSources();
jarJavadoc();
uberjar();
war();
publishOperation().executeOnce(() -> {
publishOperation().fromProject(this);
publishOperation().artifacts().add(new PublishArtifact(new File(buildDistDirectory(), uberJarFileName()), "uber", "jar"));
publishOperation().artifacts().add(new PublishArtifact(new File(buildDistDirectory(), warFileName()), "", "war"));
});
}
/*
* Project directories
*/
@Override
public File libStandaloneDirectory() {
return Objects.requireNonNullElseGet(libStandaloneDirectory, () -> new File(libDirectory(), "standalone"));
}
/**
* Returns the project main webapp directory.
* Defaults to {@code "webapp"} relative to {@link #srcMainDirectory()}.
*
* @since 1.5
*/
public File srcMainWebappDirectory() {
return Objects.requireNonNullElseGet(srcMainWebappDirectory, () -> new File(srcMainDirectory(), "webapp"));
}
/*
* Project options
*/
/**
* Returns filename to use for the main war archive creation.
* By default, appends the version and the {@code war} extension to the {@link #archiveBaseName()}.
*
* @since 1.5.0
*/
public String warFileName() {
return Objects.requireNonNullElseGet(warFileName, () -> archiveBaseName() + "-" + version() + ".war");
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.blueprints;
import rife.bld.Project;
import rife.bld.dependencies.VersionNumber;
import rife.tools.StringUtils;
import java.io.File;
import java.util.List;
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
import static rife.bld.dependencies.Repository.SONATYPE_SNAPSHOTS;
import static rife.bld.dependencies.Scope.test;
/**
* Provides the dependency information required to create a new base project.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.20
*/
public class BaseProjectBlueprint extends Project {
public BaseProjectBlueprint(File work, String packageName, String projectName) {
this(work, packageName, projectName, new VersionNumber(0,0,1));
}
public BaseProjectBlueprint(File work, String packageName, String projectName, VersionNumber versionNumber) {
workDirectory = work;
pkg = packageName;
name = projectName;
mainClass = packageName + "." + StringUtils.capitalize(projectName) + "Main";
version = versionNumber;
downloadSources = true;
repositories = List.of(MAVEN_CENTRAL, SONATYPE_SNAPSHOTS);
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.blueprints;
import rife.bld.Project;
import rife.bld.dependencies.VersionNumber;
import rife.tools.StringUtils;
import java.io.File;
import java.util.List;
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
import static rife.bld.dependencies.Repository.SONATYPE_SNAPSHOTS;
import static rife.bld.dependencies.Scope.test;
/**
* Provides the dependency information required to create a new blank project.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class BlankProjectBlueprint extends Project {
public BlankProjectBlueprint(File work, String packageName, String projectName) {
this(work, packageName, projectName, new VersionNumber(0,0,1));
}
public BlankProjectBlueprint(File work, String packageName, String projectName, VersionNumber versionNumber) {
workDirectory = work;
pkg = packageName;
name = projectName;
mainClass = packageName + "." + StringUtils.capitalize(projectName) + "Main";
version = versionNumber;
downloadSources = true;
repositories = List.of(MAVEN_CENTRAL, SONATYPE_SNAPSHOTS);
scope(test)
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5,9,3)))
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1,9,3)));
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.blueprints;
import rife.bld.Project;
import rife.bld.dependencies.VersionNumber;
import rife.tools.StringUtils;
import java.io.File;
import java.util.List;
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
import static rife.bld.dependencies.Repository.SONATYPE_SNAPSHOTS;
/**
* Provides the dependency information required to create a new lib project.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.6
*/
public class LibProjectBlueprint extends Project {
public LibProjectBlueprint(File work, String packageName, String projectName) {
this(work, packageName, projectName, new VersionNumber(0,0,1));
}
public LibProjectBlueprint(File work, String packageName, String projectName, VersionNumber versionNumber) {
workDirectory = work;
pkg = packageName;
name = projectName;
mainClass = packageName + "." + StringUtils.capitalize(projectName) + "Lib";
version = versionNumber;
downloadSources = true;
repositories = List.of(MAVEN_CENTRAL, SONATYPE_SNAPSHOTS);
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.blueprints;
import rife.bld.WebProject;
import rife.bld.dependencies.VersionNumber;
import rife.bld.operations.TemplateType;
import rife.tools.StringUtils;
import java.io.File;
import java.util.List;
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
import static rife.bld.dependencies.Repository.SONATYPE_SNAPSHOTS;
import static rife.bld.dependencies.Scope.*;
/**
* Provides the dependency information required to create a new RIFE2 project.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class Rife2ProjectBlueprint extends WebProject {
public Rife2ProjectBlueprint(File work, String packageName, String projectName) {
this(work, packageName, projectName, new VersionNumber(0,0,1));
}
public Rife2ProjectBlueprint(File work, String packageName, String projectName, VersionNumber versionNumber) {
workDirectory = work;
pkg = packageName;
name = projectName;
mainClass = packageName + "." + StringUtils.capitalize(projectName) + "Site";
version = versionNumber;
downloadSources = true;
repositories = List.of(MAVEN_CENTRAL, SONATYPE_SNAPSHOTS);
scope(compile)
.include(dependency("com.uwyn.rife2", "rife2", version(1,6,3)));
scope(test)
.include(dependency("org.jsoup", "jsoup", version(1,16,1)))
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5,9,3)))
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1,9,3)));
scope(standalone)
.include(dependency("org.eclipse.jetty", "jetty-server", version(11,0,15)))
.include(dependency("org.eclipse.jetty", "jetty-servlet", version(11,0,15)))
.include(dependency("org.slf4j", "slf4j-simple", version(2,0,7)));
precompileOperation().templateTypes(TemplateType.HTML);
}
}

View file

@ -0,0 +1,10 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
/**
* Provides blueprints for project creation.
* @since 1.5
*/
package rife.bld.blueprints;

View file

@ -0,0 +1,202 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import rife.tools.FileUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.security.MessageDigest;
import java.util.*;
import static rife.tools.HttpUtils.HEADER_AUTHORIZATION;
import static rife.tools.HttpUtils.basicAuthorizationHeader;
import static rife.tools.StringUtils.encodeHexLower;
/**
* Retrieves artifact data.
* <p>
* To instantiate, use either {@link #instance()} for direct retrieval of
* each request, or {@link #cachingInstance()} where previous retrievals
* of remote string content will be cached for faster future retrieval.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.18
*/
public abstract class ArtifactRetriever {
private final static ArtifactRetriever UNCACHED = new ArtifactRetriever() {
String getCached(RepositoryArtifact artifact) {
return null;
}
void cache(RepositoryArtifact artifact, String content) {
}
};
/**
* Gets an artifact retriever that does direct retrieval of each request.
*
* @return the direct retrieval instance
* @since 1.5.18
*/
public static ArtifactRetriever instance() {
return UNCACHED;
}
/**
* Creates a caching artifact retriever where previous retrievals
* of remote string content will be cached for faster future retrieval.
*
* @return a caching instance
* @since 1.5.18
*/
public static ArtifactRetriever cachingInstance() {
return new ArtifactRetriever() {
private final Map<RepositoryArtifact, String> artifactCache = new HashMap<>();
String getCached(RepositoryArtifact artifact) {
return artifactCache.get(artifact);
}
void cache(RepositoryArtifact artifact, String content) {
artifactCache.put(artifact, content);
}
};
}
private ArtifactRetriever() {
}
abstract String getCached(RepositoryArtifact artifact);
abstract void cache(RepositoryArtifact artifact, String content);
/**
* Reads the contents of an artifact as a string.
*
* @param artifact the artifact who's content to retrieve
* @return the string content of the artifact
* @throws FileUtilsErrorException when an error occurred when reading the contents
* @since 1.5.18
*/
public String readString(RepositoryArtifact artifact)
throws FileUtilsErrorException {
if (artifact.repository().isLocal()) {
return FileUtils.readString(new File(artifact.location()));
} else {
var cached = getCached(artifact);
if (cached != null) {
return cached;
}
try {
var connection = new URL(artifact.location()).openConnection();
connection.setUseCaches(false);
if (artifact.repository().username() != null && artifact.repository().password() != null) {
connection.setRequestProperty(
HEADER_AUTHORIZATION,
basicAuthorizationHeader(artifact.repository().username(), artifact.repository().password()));
}
try (var input_stream = connection.getInputStream()) {
var result = FileUtils.readString(input_stream);
cache(artifact, result);
return result;
}
} catch (IOException e) {
throw new FileUtilsErrorException("Error while reading URL '" + artifact.location() + ".", e);
}
}
}
/**
* Transfers artifact into the provided directory.
* <p>
* The destination directory must exist and be writable.
*
* @param artifact the artifact to transfer
* @param directory the directory to transfer the artifact into
* @return {@code true} when the artifact is present in the directory (it could already have been
* there and be validated as correct); or {@code false} when the artifact couldn't be transferred
* @throws IOException when an error occurred during the transfer
* @throws FileUtilsErrorException when an error occurred during the transfer
* @since 1.5.18
*/
public boolean transferIntoDirectory(RepositoryArtifact artifact, File directory)
throws IOException, FileUtilsErrorException {
if (directory == null) throw new IllegalArgumentException("directory can't be null");
if (!directory.exists()) throw new IllegalArgumentException("directory '" + directory + "' doesn't exit");
if (!directory.canWrite()) throw new IllegalArgumentException("directory '" + directory + "' can't be written to");
if (!directory.isDirectory()) throw new IllegalArgumentException("directory '" + directory + "' is not a directory");
var download_filename = artifact.location().substring(artifact.location().lastIndexOf("/") + 1);
var download_file = new File(directory, download_filename);
System.out.print("Downloading: " + artifact.location() + " ... ");
System.out.flush();
try {
if (artifact.repository().isLocal()) {
var source = new File(artifact.location());
if (source.exists()) {
FileUtils.copy(source, download_file);
System.out.print("done");
return true;
} else {
System.out.print("not found");
return false;
}
} else {
try {
if (download_file.exists() && download_file.canRead()) {
if (checkHash(artifact, download_file, ".sha256", "SHA-256") ||
checkHash(artifact, download_file, ".md5", "MD5")) {
System.out.print("exists");
return true;
}
}
var connection = new URL(artifact.location()).openConnection();
connection.setUseCaches(false);
if (artifact.repository().username() != null && artifact.repository().password() != null) {
connection.setRequestProperty(
HEADER_AUTHORIZATION,
basicAuthorizationHeader(artifact.repository().username(), artifact.repository().password()));
}
try (var input_stream = connection.getInputStream()) {
var readableByteChannel = Channels.newChannel(input_stream);
try (var fileOutputStream = new FileOutputStream(download_file)) {
var fileChannel = fileOutputStream.getChannel();
fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
System.out.print("done");
return true;
}
}
} catch (FileNotFoundException e) {
System.out.print("not found");
return false;
}
}
} finally {
System.out.println();
}
}
private boolean checkHash(RepositoryArtifact artifact, File downloadFile, String extension, String algorithm) {
try {
var hash_sum = readString(artifact.appendPath(extension));
var digest = MessageDigest.getInstance(algorithm);
digest.update(FileUtils.readBytes(downloadFile));
return hash_sum.equals(encodeHexLower(digest.digest()));
} catch (Exception e) {
// no-op, the hash file couldn't be found or calculated, so it couldn't be checked
}
return false;
}
}

View file

@ -0,0 +1,169 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* Contains the information required to describe an url dependency in the build system.
*
* @param groupId the dependency group identifier
* @param artifactId the dependency url identifier
* @param version the dependency version
* @param classifier the dependency classier
* @param type the dependency type
* @param exclusions the dependency exclusions for transitive resolution
* @param parent the parent dependency that created this dependency (only for information purposes)
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public record Dependency(String groupId, String artifactId, VersionNumber version, String classifier, String type, ExclusionSet exclusions, Dependency parent) {
public static final String CLASSIFIER_SOURCES = "sources";
public static final String CLASSIFIER_JAVADOC = "javadoc";
public Dependency(String groupId, String artifactId) {
this(groupId, artifactId, null, null, null);
}
public Dependency(String groupId, String artifactId, VersionNumber version) {
this(groupId, artifactId, version, null, null);
}
public Dependency(String groupId, String artifactId, VersionNumber version, String classifier) {
this(groupId, artifactId, version, classifier, null);
}
public Dependency(String groupId, String artifactId, VersionNumber version, String classifier, String type) {
this(groupId, artifactId, version, classifier, type, null);
}
public Dependency(String groupId, String artifactId, VersionNumber version, String classifier, String type, ExclusionSet exclusions) {
this(groupId, artifactId, version, classifier, type, null, null);
}
public Dependency(String groupId, String artifactId, VersionNumber version, String classifier, String type, ExclusionSet exclusions, Dependency parent) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = (version == null ? VersionNumber.UNKNOWN : version);
this.classifier = (classifier == null ? "" : classifier);
this.type = (type == null ? "jar" : type);
this.exclusions = (exclusions == null ? new ExclusionSet() : exclusions);
this.parent = parent;
}
private static final Pattern DEPENDENCY_PATTERN = Pattern.compile("^(?<groupId>[^:@]+):(?<artifactId>[^:@]+)(?::(?<version>[^:@]+)(?::(?<classifier>[^:@]+))?)?(?:@(?<type>[^:@]+))?$");
/**
* Parses a dependency from a string representation.
* The format is {@code groupId:artifactId:version:classifier@type}.
* The {@code version}, {@code classifier} and {@code type} are optional.
* <p>
* If the string can't be successfully parsed, {@code null} will be returned.
*
* @param dependency the dependency string to parse
* @return a parsed instance of {@code Dependency}; or
* {@code null} when the string couldn't be parsed
* @since 1.5.2
*/
public static Dependency parse(String dependency) {
if (dependency == null || dependency.isEmpty()) {
return null;
}
var matcher = DEPENDENCY_PATTERN.matcher(dependency);
if (!matcher.matches()) {
return null;
}
var groupId = matcher.group("groupId");
var artifactId = matcher.group("artifactId");
var version = VersionNumber.parse(matcher.group("version"));
var classifier = matcher.group("classifier");
var type = matcher.group("type");
return new Dependency(groupId, artifactId, version, classifier, type);
}
/**
* Returns the base dependency of this dependency, replacing the version number
* with an unknown version number.
*
* @return this dependency's base dependency
* @since 1.5
*/
public Dependency baseDependency() {
return new Dependency(groupId, artifactId, VersionNumber.UNKNOWN, classifier, type);
}
/**
* Adds an exclusion to this dependency.
*
* @param groupId the exclusion group identifier, use {@code "*"} to exclude all groupIds
* @param artifactId the exclusion url identifier, use {@code "*"} to exclude all artifactIds
* @return this dependency instance
* @since 1.5
*/
public Dependency exclude(String groupId, String artifactId) {
exclusions.add(new DependencyExclusion(groupId, artifactId));
return this;
}
/**
* Returns a new dependency with the same data, except for the provided classifier.
*
* @param classifier the classifier to use for the new dependency
* @return the new dependency with the changed classifier
* @since 1.5.6
*/
public Dependency withClassifier(String classifier) {
return new Dependency(groupId, artifactId, version, classifier, type);
}
/**
* Returns a filename that corresponds to the dependency information.
*
* @return a filename for the dependency
* @since 1.5.4
*/
public String toFileName() {
var result = new StringBuilder(artifactId());
result.append("-").append(version());
if (!classifier().isEmpty()) {
result.append("-").append(classifier());
}
result.append(".").append(type());
return result.toString();
}
public String toString() {
var result = new StringBuilder(groupId).append(":").append(artifactId);
if (!version.equals(VersionNumber.UNKNOWN)) {
result.append(":").append(version);
}
if (!classifier.isEmpty()) {
result.append(":").append(classifier);
}
if (!type.isEmpty() && !type.equals("jar")) {
result.append("@").append(type);
}
return result.toString();
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var that = (Dependency) o;
return groupId.equals(that.groupId) &&
artifactId.equals(that.artifactId) &&
classifier.equals(that.classifier) &&
type.equals(that.type);
}
public int hashCode() {
return Objects.hash(groupId, artifactId, classifier, type);
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.Objects;
/**
* Contains the information to describe a dependency exclusion.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public record DependencyExclusion(String groupId, String artifactId) {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DependencyExclusion that = (DependencyExclusion) o;
return Objects.equals(groupId, that.groupId) && Objects.equals(artifactId, that.artifactId);
}
public int hashCode() {
return Objects.hash(groupId, artifactId);
}
boolean matches(PomDependency dependency) {
return (groupId().equals("*") && artifactId().equals("*")) ||
(groupId().equals("*") && artifactId().equals(dependency.artifactId())) ||
(groupId().equals(dependency.groupId()) && artifactId().equals("*")) ||
(groupId().equals(dependency.groupId()) && artifactId().equals(dependency.artifactId()));
}
}

View file

@ -0,0 +1,427 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import rife.bld.dependencies.exceptions.*;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* Resolves a dependency within a list of Maven-compatible repositories.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class DependencyResolver {
private final ArtifactRetriever retriever_;
private final List<Repository> repositories_;
private final Dependency dependency_;
private MavenMetadata metadata_ = null;
private MavenMetadata snapshotMetadata_ = null;
/**
* Creates a new resolver for a particular dependency.
* <p>
* The repositories will be checked in the order they're listed.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the resolution
* @param dependency the dependency to resolve
* @since 1.5.18
*/
public DependencyResolver(ArtifactRetriever retriever, List<Repository> repositories, Dependency dependency) {
retriever_ = retriever;
if (repositories == null) {
repositories = Collections.emptyList();
}
repositories_ = repositories;
dependency_ = dependency;
}
/**
* Checks whether the dependency exists in any of the provided repositories.
*
* @return {@code true} if the dependency exists; {@code false} otherwise
* @since 1.5
*/
public boolean exists() {
try {
if (getMavenMetadata() == null) {
return false;
}
if (!dependency_.version().equals(VersionNumber.UNKNOWN)) {
return getMavenMetadata().getVersions().contains(dependency_.version());
}
return true;
} catch (ArtifactNotFoundException | ArtifactRetrievalErrorException e) {
return false;
}
}
/**
* Resolves the dependency version in the provided repositories.
* <p>
* When the dependency was defined without a specific version number,
* the latest version number will be returned if the dependency exists.
* If the dependency couldn't be found and no specific version number
* was provided, {@linkplain VersionNumber#UNKNOWN} will be returned.
*
* @return the resolved version
* @since 1.5
*/
public VersionNumber resolveVersion() {
var version = dependency_.version();
if (version.equals(VersionNumber.UNKNOWN)) {
return latestVersion();
}
return version;
}
/**
* Retrieves the direct dependencies of the resolved dependency for the
* provided scopes.
*
* @param scopes the scopes to return the direct dependencies for
* @return the requested direct dependencies; or an empty {@linkplain DependencySet}
* if no direct dependencies could be found or the dependency doesn't exist in
* the provided repositories
* @since 1.5
*/
public DependencySet getDirectDependencies(Scope... scopes) {
var pom_dependencies = getMavenPom(dependency_).getDependencies(scopes);
var result = new DependencySet();
for (var dependency : pom_dependencies) {
result.add(dependency.convertToDependency());
}
return result;
}
/**
* Retrieves all the transitive dependencies of the resolved dependency for the
* provided scopes. This includes the dependency of this resolver also.
* <p>
* This can be a slow and expensive operation since querying continues through
* the complete POM hierarchy until all transitive dependencies have been found.
*
* @param scopes the scopes to return the transitive dependencies for
* @return the requested transitive dependencies; or an empty {@linkplain DependencySet}
* if no transitive dependencies could be found or the dependency doesn't exist in
* the provided repositories
* @since 1.5
*/
public DependencySet getAllDependencies(Scope... scopes) {
var result = new DependencySet();
result.add(dependency_);
var dependency_queue = new ArrayList<PomDependency>();
var parent = dependency_;
var next_dependencies = getMavenPom(parent).getDependencies(scopes);
while (parent != null && next_dependencies != null) {
// remove any next dependencies that are already queued
dependency_queue.forEach(next_dependencies::remove);
// remove any next dependencies that match the current exclusion context
final var exclusion_context = parent;
next_dependencies.removeIf(it -> matchesExclusions(exclusion_context, it));
// add all next dependencies to the queue
dependency_queue.addAll(next_dependencies);
// unless we find a next set of dependencies to add, stop resolving
parent = null;
next_dependencies = null;
// iterate through the dependency queue until we find one that isn't
// part of the results yet
while (!dependency_queue.isEmpty()) {
var candidate = dependency_queue.remove(0);
var dependency = candidate.convertToDependency();
if (!result.contains(dependency)) {
result.add(dependency);
// we found a dependency that was added to the result, get its
// dependencies so that they can be added to the queue after
// filtering
parent = dependency;
next_dependencies = new DependencyResolver(retriever_, repositories_, dependency).getMavenPom(parent).getDependencies(scopes);
break;
}
}
}
return result;
}
private boolean matchesExclusions(Dependency context, PomDependency checked) {
while (context != null) {
if (context.exclusions() != null) {
for (var exclusion : context.exclusions()) {
if (exclusion.matches(checked)) {
return true;
}
}
}
context = context.parent();
}
if (dependency_.exclusions() != null) {
for (var exclusion : dependency_.exclusions()) {
if (exclusion.matches(checked)) {
return true;
}
}
}
return false;
}
/**
* Retrieves all the versions of the resolved dependency.
*
* @return this dependency's version list; or an empty list if the dependency
* couldn't be found in the provided repositories
* @since 1.5
*/
public List<VersionNumber> listVersions() {
return getMavenMetadata().getVersions();
}
/**
* Retrieves the latest version of the resolved dependency.
*
* @return this dependency's latest version; or {@link VersionNumber#UNKNOWN}
* if the dependency couldn't be found in the provided repositories
* @since 1.5
*/
public VersionNumber latestVersion() {
return getMavenMetadata().getLatest();
}
/**
* Retrieves the release version of the resolved dependency.
*
* @return this dependency's release version; or {@link VersionNumber#UNKNOWN}
* if the dependency couldn't be found in the provided repositories
* @since 1.5
*/
public VersionNumber releaseVersion() {
return getMavenMetadata().getRelease();
}
/**
* Transfers the artifacts for the resolved dependency into the provided directory.
* <p>
* The destination directory must exist and be writable.
*
* @param directory the directory to transfer the dependency artifact into
* @return the artifact that was successfully transferred; or {@code null} if no artifact was transferred
* @throws DependencyTransferException when an error occurred during the transfer
* @since 1.5.10
*/
public RepositoryArtifact transferIntoDirectory(File directory)
throws DependencyTransferException {
for (var artifact : getTransferArtifacts()) {
try {
if (retriever_.transferIntoDirectory(artifact, directory)) {
return artifact;
}
} catch (IOException e) {
throw new DependencyTransferException(dependency_, artifact.location(), directory, e);
}
}
return null;
}
/**
* Retrieve the repositories that are used by this dependency resolver.
*
* @return the dependency resolver's repositories
* @since 1.5
*/
public List<Repository> repositories() {
return repositories_;
}
/**
* Retrieve the dependency that is resolved.
*
* @return the resolved dependency
* @since 1.5
*/
public Dependency dependency() {
return dependency_;
}
/**
* Retrieves all the potential locations for the dependency
* within the provided repositories.
*
* @return a list of potential transfer locations
* @since 1.5.10
*/
public List<String> getTransferLocations() {
return getTransferArtifacts().stream().map(RepositoryArtifact::location).toList();
}
/**
* Returns the main Maven metadata for this dependency
*
* @return this dependency's metadata
* @since 1.5.8
*/
public MavenMetadata getMavenMetadata() {
if (metadata_ == null) {
var locations = getMetadataLocations();
metadata_ = parseMavenMetadata(locations);
}
return metadata_;
}
/**
* THe Maven metadata with snapshot information when this is a snapshot version.
*
* @return Maven metadata if this is a snapshot dependency version; or
* {@code null} if this version is not a snapshot
* @since 1.5.8
*/
public MavenMetadata getSnapshotMavenMetadata() {
if (snapshotMetadata_ == null) {
final var version = resolveVersion();
if (version.isSnapshot()) {
var locations = getSnapshotMetadataLocations();
snapshotMetadata_ = parseMavenMetadata(locations);
}
}
return snapshotMetadata_;
}
private List<RepositoryArtifact> getTransferArtifacts() {
final var version = resolveVersion();
final VersionNumber pom_version;
if (version.isSnapshot()) {
var metadata = getSnapshotMavenMetadata();
pom_version = metadata.getSnapshot();
} else {
pom_version = version;
}
return getArtifactLocations().stream().map(a -> {
var result = new StringBuilder();
result.append(version).append("/").append(dependency_.artifactId()).append("-").append(pom_version);
if (!dependency_.classifier().isEmpty()) {
result.append("-").append(dependency_.classifier());
}
var type = dependency_.type();
if (type == null) {
type = "jar";
}
result.append(".").append(type);
return a.appendPath(result.toString());
}).toList();
}
private List<RepositoryArtifact> getArtifactLocations() {
return repositories_.stream().map(repository -> new RepositoryArtifact(repository, repository.getArtifactLocation(dependency_))).toList();
}
private List<RepositoryArtifact> getMetadataLocations() {
return getArtifactLocations().stream().map(a -> a.appendPath(a.repository().getMetadataName())).toList();
}
private List<RepositoryArtifact> getSnapshotMetadataLocations() {
var version = resolveVersion();
return getArtifactLocations().stream().map(a -> a.appendPath(version + "/" + a.repository().getMetadataName())).toList();
}
private MavenMetadata parseMavenMetadata(List<RepositoryArtifact> artifacts) {
RepositoryArtifact retrieved_artifact = null;
String metadata = null;
for (var artifact : artifacts) {
try {
var content = retriever_.readString(artifact);
if (content == null) {
throw new ArtifactNotFoundException(dependency_, artifact.location());
}
retrieved_artifact = artifact;
metadata = content;
break;
} catch (FileUtilsErrorException e) {
if (e.getCause() instanceof FileNotFoundException) {
continue;
}
throw new ArtifactRetrievalErrorException(dependency_, artifact.location(), e);
}
}
if (metadata == null) {
throw new ArtifactNotFoundException(dependency_, artifacts.stream().map(RepositoryArtifact::location).collect(Collectors.joining(", ")));
}
var xml = new Xml2MavenMetadata();
if (!xml.processXml(metadata)) {
throw new DependencyXmlParsingErrorException(dependency_, retrieved_artifact.location(), xml.getErrors());
}
return xml;
}
private List<RepositoryArtifact> getPomLocations() {
final var version = resolveVersion();
final VersionNumber pom_version;
if (version.isSnapshot()) {
var metadata = getSnapshotMavenMetadata();
pom_version = metadata.getSnapshot();
} else {
pom_version = version;
}
return getArtifactLocations().stream().map(a -> a.appendPath(version + "/" + dependency_.artifactId() + "-" + pom_version + ".pom")).toList();
}
Xml2MavenPom getMavenPom(Dependency parent) {
RepositoryArtifact retrieved_artifact = null;
String pom = null;
var artifacts = getPomLocations();
for (var artifact : artifacts) {
try {
var content = retriever_.readString(artifact);
if (content == null) {
throw new ArtifactNotFoundException(dependency_, artifact.location());
}
retrieved_artifact = artifact;
pom = content;
break;
} catch (FileUtilsErrorException e) {
if (e.getCause() instanceof FileNotFoundException) {
continue;
}
throw new ArtifactRetrievalErrorException(dependency_, artifact.location(), e);
}
}
if (pom == null) {
throw new ArtifactNotFoundException(dependency_, artifacts.stream().map(RepositoryArtifact::location).collect(Collectors.joining(", ")));
}
var xml = new Xml2MavenPom(parent, retriever_, repositories_);
if (!xml.processXml(pom)) {
throw new DependencyXmlParsingErrorException(dependency_, retrieved_artifact.location(), xml.getErrors());
}
return xml;
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Convenience class to map a {@link Scope} to its dependencies.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class DependencyScopes extends LinkedHashMap<Scope, DependencySet> {
/**
* Creates an empty dependency scope map.
*
* @since 1.5
*/
public DependencyScopes() {
}
/**
* Creates a dependency scope map from another one.
*
* @param other the other map to create this one from
* @since 1.5
*/
public DependencyScopes(DependencyScopes other) {
for (var entry : other.entrySet()) {
put(entry.getKey(), new DependencySet(entry.getValue()));
}
}
/**
* Includes all the dependencies from another dependency scope map.
*
* @param other the other map to include dependencies from
* @since 1.5
*/
public void include(DependencyScopes other) {
for (var entry : other.entrySet()) {
var dependencies = get(entry.getKey());
if (dependencies == null) {
dependencies = new DependencySet();
put(entry.getKey(), dependencies);
}
dependencies.addAll(entry.getValue());
}
}
/**
* Retrieves the {@link DependencySet} for a particular scope.
*
* @param scope the scope to retrieve the dependencies for
* @return the scope's {@code DependencySet};
* or an empty {@code DependencySet} if none have been defined for the provided scope.
* @since 1.5
*/
public DependencySet scope(Scope scope) {
return computeIfAbsent(scope, k -> new DependencySet());
}
/**
* Returns the transitive set of dependencies that would be used for the compile scope in a project.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the resolution
* @return the compile scope dependency set
* @since 1.6
*/
public DependencySet resolveCompileDependencies(ArtifactRetriever retriever, List<Repository> repositories) {
return resolveScopedDependencies(retriever, repositories,
new Scope[]{Scope.provided, Scope.compile},
new Scope[]{Scope.compile},
null);
}
/**
* Returns the transitive set of dependencies that would be used for the runtime scope in a project.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the resolution
* @return the runtime scope dependency set
* @since 1.6
*/
public DependencySet resolveRuntimeDependencies(ArtifactRetriever retriever, List<Repository> repositories) {
return resolveScopedDependencies(retriever, repositories,
new Scope[]{Scope.provided, Scope.compile, Scope.runtime},
new Scope[]{Scope.compile, Scope.runtime},
resolveCompileDependencies(retriever, repositories));
}
/**
* Returns the transitive set of dependencies that would be used for the standalone scope in a project.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the resolution
* @return the standalone scope dependency set
* @since 1.6
*/
public DependencySet resolveStandaloneDependencies(ArtifactRetriever retriever, List<Repository> repositories) {
return resolveScopedDependencies(retriever, repositories,
new Scope[]{Scope.standalone},
new Scope[]{Scope.compile, Scope.runtime},
null);
}
/**
* Returns the transitive set of dependencies that would be used for the test scope in a project.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the resolution
* @return the test scope dependency set
* @since 1.6
*/
public DependencySet resolveTestDependencies(ArtifactRetriever retriever, List<Repository> repositories) {
return resolveScopedDependencies(retriever, repositories,
new Scope[]{Scope.test},
new Scope[]{Scope.compile, Scope.runtime},
null);
}
private DependencySet resolveScopedDependencies(ArtifactRetriever retriever, List<Repository> repositories, Scope[] resolvedScopes, Scope[] transitiveScopes, DependencySet excluded) {
var dependencies = new DependencySet();
for (var scope : resolvedScopes) {
var scoped_dependencies = get(scope);
if (scoped_dependencies != null) {
for (var dependency : scoped_dependencies) {
dependencies.addAll(new DependencyResolver(retriever, repositories, dependency).getAllDependencies(transitiveScopes));
}
}
}
if (excluded != null) {
dependencies.removeAll(excluded);
}
return dependencies;
}
}

View file

@ -0,0 +1,266 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import rife.bld.dependencies.exceptions.DependencyTransferException;
import java.io.File;
import java.util.*;
/**
* Convenience class to handle a set of {@link Dependency} objects.
* <p>
* Only a single version of each dependency can exist in this set.
* When adding a new dependency, it will only be added if it didn't exist
* in the set yet, or if the new dependency has a higher version than
* the existing one.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class DependencySet extends AbstractSet<Dependency> implements Set<Dependency> {
private final Map<Dependency, Dependency> dependencies_ = new LinkedHashMap<>();
private final Set<LocalDependency> localDependencies_ = new LinkedHashSet<>();
/**
* Creates an empty dependency set.
*
* @since 1.5
*/
public DependencySet() {
}
/**
* Creates a dependency set from another one.
*
* @param other the other set to create this one from
* @since 1.5
*/
public DependencySet(DependencySet other) {
addAll(other);
}
/**
* Includes a dependency into the dependency set.
*
* @param dependency the dependency to include
* @return this dependency set instance
* @since 1.5
*/
public DependencySet include(Dependency dependency) {
add(dependency);
return this;
}
/**
* Includes a local dependency into the dependency set.
* <p>
* Local dependencies aren't resolved and point to a location on
* the file system.
*
* @param dependency the dependency to include
* @return this dependency set instance
* @since 1.5.2
*/
public DependencySet include(LocalDependency dependency) {
localDependencies_.add(dependency);
return this;
}
/**
* Retrieves the local dependencies.
*
* @return the set of local dependencies
* @since 1.5.2
*/
public Set<LocalDependency> localDependencies() {
return localDependencies_;
}
/**
* Transfers the artifacts for the dependencies into the provided directory.
* <p>
* The destination directory must exist and be writable.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the transfer
* @param directory the directory to transfer the artifacts into
* @return the list of artifacts that were transferred successfully
* @throws DependencyTransferException when an error occurred during the transfer
* @since 1.5.10
*/
public List<RepositoryArtifact> transferIntoDirectory(ArtifactRetriever retriever, List<Repository> repositories, File directory) {
return transferIntoDirectory(retriever, repositories, directory, (String[]) null);
}
/**
* Transfers the artifacts for the dependencies into the provided directory,
* including other classifiers.
* <p>
* The destination directory must exist and be writable.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to use for the download
* @param directory the directory to download the artifacts into
* @param classifiers the additional classifiers to transfer
* @return the list of artifacts that were transferred successfully
* @throws DependencyTransferException when an error occurred during the transfer
* @since 1.5.10
*/
public List<RepositoryArtifact> transferIntoDirectory(ArtifactRetriever retriever, List<Repository> repositories, File directory, String... classifiers) {
var result = new ArrayList<RepositoryArtifact>();
for (var dependency : this) {
var artifact = new DependencyResolver(retriever, repositories, dependency).transferIntoDirectory(directory);
if (artifact != null) {
result.add(artifact);
}
if (classifiers != null) {
for (var classifier : classifiers) {
if (classifier != null) {
var classifier_artifact = new DependencyResolver(retriever, repositories, dependency.withClassifier(classifier)).transferIntoDirectory(directory);
if (classifier_artifact != null) {
result.add(classifier_artifact);
}
}
}
}
}
return result;
}
/**
* Returns the dependency that was stored in the set.
* <p>
* The version can be different from the dependency passed in and this
* method can be used to look up the actual version of the dependency in the set.
*
* @param dependency the dependency to look for
* @return the dependency in the set; or
* {@code null} if no such dependency exists
* @since 1.5
*/
public Dependency get(Dependency dependency) {
return dependencies_.get(dependency);
}
/**
* Generates the string description of the transitive hierarchical tree of
* dependencies for a particular scope.
*
* @param retriever the retriever to use to get artifacts
* @param repositories the repositories to look for dependencies in
* @param scopes the scopes to return the transitive dependencies for
* @return the generated tree description string; or an empty string if
* there were no dependencies to describe
* @since 1.5.21
*/
public String generateTransitiveDependencyTree(ArtifactRetriever retriever, List<Repository> repositories, Scope... scopes) {
var compile_dependencies = new DependencySet();
for (var dependency : this) {
compile_dependencies.addAll(new DependencyResolver(retriever, repositories, dependency).getAllDependencies(scopes));
}
return compile_dependencies.generateDependencyTree();
}
/**
* Generates the string description of the hierarchical tree of
* dependencies in this {@code DependencySet}. This relies on the {@code Dependency}
* {@code parent} field to be set correctly to indicate their relationships.
*
* @return the generated tree description string; or an empty string if
* there were no dependencies to describe
* @since 1.5.21
*/
public String generateDependencyTree() {
var result = new StringBuilder();
var dependency_list = new ArrayList<>(this);
var dependency_stack = new Stack<DependencyTreeEntry>();
var roots = dependency_list.stream().filter(dependency -> dependency.parent() == null).toList();
dependency_list.removeIf(dependency -> dependency.parent() == null);
var roots_it = roots.iterator();
while (roots_it.hasNext()) {
var root = roots_it.next();
dependency_list.add(0, root);
stack:
do {
var list_it = dependency_list.iterator();
while (list_it.hasNext()) {
var list_dep = list_it.next();
if (list_dep.parent() == null) {
list_it.remove();
var entry = new DependencyTreeEntry(null, list_dep, !roots_it.hasNext());
result.append(entry).append(System.lineSeparator());
dependency_stack.add(entry);
} else {
var stack_entry = dependency_stack.peek();
if (list_dep.parent().equals(stack_entry.dependency())) {
list_it.remove();
boolean last = dependency_list.stream().noneMatch(d -> d.parent().equals(stack_entry.dependency()));
var entry = new DependencyTreeEntry(stack_entry, list_dep, last);
result.append(entry).append(System.lineSeparator());
dependency_stack.add(entry);
continue stack;
}
}
}
dependency_stack.pop();
} while (!dependency_stack.isEmpty());
}
return result.toString();
}
private record DependencyTreeEntry(DependencyTreeEntry parent, Dependency dependency, boolean last) {
public String toString() {
var result = new StringBuilder();
if (last) {
result.insert(0, "└─ ");
} else {
result.insert(0, "├─ ");
}
var p = parent();
while (p != null) {
if (p.last()) {
result.insert(0, " ");
} else {
result.insert(0, "");
}
p = p.parent();
}
return result.toString() + dependency;
}
}
public boolean add(Dependency dependency) {
var existing = dependencies_.get(dependency);
if (existing == null) {
dependencies_.put(dependency, dependency);
return true;
}
if (dependency.version().compareTo(existing.version()) > 0) {
dependencies_.remove(dependency);
dependencies_.put(dependency, dependency);
return true;
}
return false;
}
public Iterator<Dependency> iterator() {
return dependencies_.keySet().iterator();
}
public int size() {
return dependencies_.size();
}
}

View file

@ -0,0 +1,16 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.LinkedHashSet;
/**
* Convenience class to handle a set of {@link DependencyExclusion} objects.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class ExclusionSet extends LinkedHashSet<DependencyExclusion> {
}

View file

@ -0,0 +1,17 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
/**
* Contains the information required to describe a local dependency for the build system.
* <p>
* If the local dependency points to a directory, it will be scanned for jar files.
*
* @param path the file system path of the local dependency
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.2
*/
public record LocalDependency(String path) {
}

View file

@ -0,0 +1,63 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.List;
/**
* Provides Maven metadata information
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.8
*/
public interface MavenMetadata {
/**
* Returns latest version number in the metadata.
*
* @return the latest version number
* @since 1.5.8
*/
VersionNumber getLatest();
/**
* Returns release version number in the metadata.
*
* @return the release version number
* @since 1.5.8
*/
VersionNumber getRelease();
/**
* Returns snapshot version number in the metadata.
*
* @return the snapshot version number
* @since 1.5.8
*/
VersionNumber getSnapshot();
/**
* Returns snapshot timestamp in the metadata.
*
* @return the snapshot timestamp
* @since 1.5.8
*/
String getSnapshotTimestamp();
/**
* Returns snapshot build number in the metadata.
*
* @return the snapshot build number
* @since 1.5.8
*/
Integer getSnapshotBuildNumber();
/**
* Returns all the release or snapshot versions in the metadata.
*
* @return the version number list
* @since 1.5.8
*/
List<VersionNumber> getVersions();
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.*;
/**
* Contains the information required to describe a dependency with all
* the details stored in a Maven POM descriptor.
* <p>
* This is used by the {@linkplain DependencyResolver} while traversing
* the dependency graph, eventually resulting into fully resolved
* {@linkplain Dependency} instances.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public record PomDependency(String groupId, String artifactId, String version, String classifier, String type,
String scope, String optional, ExclusionSet exclusions, Dependency parent) {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PomDependency that = (PomDependency) o;
return Objects.equals(groupId, that.groupId) && Objects.equals(artifactId, that.artifactId) && Objects.equals(classifier, that.classifier) && Objects.equals(type, that.type);
}
boolean isPomImport() {
return "pom".equals(type()) && "import".equals(scope());
}
Dependency convertToDependency() {
return new Dependency(
groupId(),
artifactId(),
VersionNumber.parse(version()),
classifier(),
type(),
exclusions(),
parent());
}
public int hashCode() {
return Objects.hash(groupId, artifactId, classifier, type);
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import rife.ioc.HierarchicalProperties;
import rife.tools.StringEncryptor;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
/**
* Contains the information required to locate a Maven-compatible repository.
*
* @param location the base location of the repository
* @param username the username to access the repository
* @param password the password to access the repository
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public record Repository(String location, String username, String password) {
public static Repository MAVEN_LOCAL = null;
public static final Repository MAVEN_CENTRAL = new Repository("https://repo1.maven.org/maven2/");
public static final Repository SONATYPE_RELEASES = new Repository("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/");
public static final Repository SONATYPE_SNAPSHOTS = new Repository("https://s01.oss.sonatype.org/content/repositories/snapshots/");
public static final Repository SONATYPE_SNAPSHOTS_LEGACY = new Repository("https://oss.sonatype.org/content/repositories/snapshots/");
public static final Repository APACHE = new Repository("https://repo.maven.apache.org/maven2/");
public static final Repository RIFE2_RELEASES = new Repository("https://repo.rife2.com/releases/");
public static final Repository RIFE2_SNAPSHOTS = new Repository("https://repo.rife2.com/snapshots/");
private static final String MAVEN_LOCAL_REPO_PROPERTY = "maven.repo.local";
public static final String PROPERTY_BLD_REPO_PREFIX = "bld.repo.";
public static final String PROPERTY_BLD_REPO_USERNAME_SUFFIX = ".username";
public static final String PROPERTY_BLD_REPO_PASSWORD_SUFFIX = ".password";
/**
* This method will be called as soon as hierarchical properties
* are initialized in the build executor. It is not intended to be called
* manually.
*
* @param properties the hierarchical properties to use for resolving
* the maven local repository
* @since 1.5.12
*/
public static void resolveMavenLocal(HierarchicalProperties properties) {
var user_home = properties.getValueString("user.home");
if (user_home == null) {
user_home = System.getProperty("user.home");
}
var maven_local = properties.getValueString(
MAVEN_LOCAL_REPO_PROPERTY,
Path.of(user_home, ".m2", "repository").toString());
MAVEN_LOCAL = new Repository(maven_local);
}
/**
* Resolves the repository in the provided hierarchical properties.
* <p>
* For instance, using the name {@code myrepo} will look for the following properties:<br>
* {@code bld.repo.myrepo}<br>
* {@code bld.repo.myrepo.username} (optional)<br>
* {@code bld.repo.myrepo.password} (optional)
* <p>
* If the {@code bld.repo.myrepo} property isn't found, the {@code locationOrName}
* parameter will be used as a location instead.
*
* @param properties the hierarchical properties to look into
* @param locationOrName the text to resolve a repository name or to be used as a location
* @return the repository instance
* @since 1.5.12
*/
public static Repository resolveRepository(HierarchicalProperties properties, String locationOrName) {
if (properties != null && properties.contains(PROPERTY_BLD_REPO_PREFIX + locationOrName)) {
var location = properties.getValueString(PROPERTY_BLD_REPO_PREFIX + locationOrName);
var username = properties.getValueString(PROPERTY_BLD_REPO_PREFIX + locationOrName + PROPERTY_BLD_REPO_USERNAME_SUFFIX);
var password = properties.getValueString(PROPERTY_BLD_REPO_PREFIX + locationOrName + PROPERTY_BLD_REPO_PASSWORD_SUFFIX);
return new Repository(location, username, password);
}
return switch (locationOrName) {
case "MAVEN_LOCAL" -> Repository.MAVEN_LOCAL;
case "MAVEN_CENTRAL" -> Repository.MAVEN_CENTRAL;
case "SONATYPE_RELEASES" -> Repository.SONATYPE_RELEASES;
case "SONATYPE_SNAPSHOTS" -> Repository.SONATYPE_SNAPSHOTS;
case "SONATYPE_SNAPSHOTS_LEGACY" -> Repository.SONATYPE_SNAPSHOTS_LEGACY;
case "APACHE" -> Repository.APACHE;
case "RIFE2_RELEASES" -> Repository.RIFE2_RELEASES;
case "RIFE2_SNAPSHOTS" -> Repository.RIFE2_SNAPSHOTS;
default -> new Repository(locationOrName);
};
}
/**
* Creates a new repository with only a location.
*
* @param location the location to create the repository for
* @since 1.5
*/
public Repository(String location) {
this(location, null, null);
}
/**
* Indicates whether this repository is local.
*
* @return {@code true} when this repository is local; or
* {@code false} otherwise
* @since 1.5.10
*/
public boolean isLocal() {
return location().startsWith("/") || location().startsWith("file:");
}
/**
* Creates a new repository instance of the same location, but with
* different credentials.
*
* @param username the username to access the repository
* @param password the password to access the repository
* @return the new repository
* @since 1.5.10
*/
public Repository withCredentials(String username, String password) {
return new Repository(location(), username, password);
}
/**
* Constructs the location for a dependency if it would be located in this repository.
*
* @param dependency the dependency to create the location for
* @return the constructed location
* @since 1.5.10
*/
public String getArtifactLocation(Dependency dependency) {
return getArtifactLocation(dependency.groupId(), dependency.artifactId());
}
/**
* Constructs the location for a dependency if it would be located in this repository.
*
* @param groupId the groupId dependency to create the location for
* @param artifactId the artifactId dependency to create the location for
* @return the constructed location
* @since 1.5.10
*/
public String getArtifactLocation(String groupId, String artifactId) {
var group_path = groupId.replace(".", "/");
var result = new StringBuilder();
if (isLocal()) {
if (location().startsWith("file://")) {
result.append(location().substring("file://".length()));
} else {
result.append(location());
}
} else {
result.append(location());
}
if (!location().endsWith("/")) {
result.append("/");
}
return result.append(group_path).append("/").append(artifactId).append("/").toString();
}
/**
* Returns the appropriate metadata name.
*
* @return the metadata name for this repository.
* @since 1.5.10
*/
public String getMetadataName() {
if (isLocal()) {
return "maven-metadata-local.xml";
} else {
return "maven-metadata.xml";
}
}
public String toString() {
var result = new StringBuilder(location);
if (username() != null) {
result.append(":");
try {
result.append(StringEncryptor.MD5HLO.performEncryption(username(), null));
if (password() != null) {
result.append(":");
result.append(StringEncryptor.MD5HLO.performEncryption(password(), null));
}
} catch (NoSuchAlgorithmException e) {
// should never happen
throw new RuntimeException(e);
}
}
return result.toString();
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
/**
* Represents an artifact location in a repository.
*
* @param repository the repository of the artifact
* @param location the location of the artifact in the repository
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.6
*/
public record RepositoryArtifact(Repository repository, String location) {
public RepositoryArtifact appendPath(String path) {
return new RepositoryArtifact(repository, location + path);
}
public String toString() {
return repository + ":" + location;
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
/**
* Provides all the dependency scopes that are supported by
* the RIFE2 build system.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public enum Scope {
/**
* Used for compiling the main source code.
* @since 1.5
*/
compile,
/**
* Used when running the main source code.
* @since 1.5
*/
runtime,
/**
* Used when compiling and running the test source code.
* @since 1.5
*/
test,
/**
* Used when running the main source code without container.
* @since 1.5
*/
standalone,
/**
* Provided by a container when running the main source code.
* @since 1.5
*/
provided
}

View file

@ -0,0 +1,249 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* Contains the information required to describe a dependency version number.
* <p>
* This operates according to the versioning scheme specified by Maven.
* <p>
* When the version number is undefined, {@link VersionNumber#UNKNOWN} should be used.
*
* @param major the major version component
* @param minor the minor version component
* @param revision the revision of the version
* @param qualifier a string qualifier for the version
* @param separator the separator used to separate the qualifier from the version number
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public record VersionNumber(Integer major, Integer minor, Integer revision, String qualifier, String separator) implements Comparable<VersionNumber> {
public static final String SNAPSHOT_QUALIFIER = "SNAPSHOT";
/**
* Singleton to use when the version is not specified.
*
* @since 1.5
*/
public static final VersionNumber UNKNOWN = new VersionNumber(0, 0, 0, "");
private static final Pattern VERSION_PATTERN = Pattern.compile("^(?<major>\\d+)(?:\\.(?<minor>\\d+)(?:\\.(?<revision>\\d+))?)?(?:(?<separator>[.\\-])(?<qualifier>.*[^.\\-]))??$");
/**
* Parses a version number from a string representation.
* <p>
* If the string can't be successfully parsed, {@link VersionNumber#UNKNOWN} will be returned.
*
* @param version the version string to parse
* @return a parsed instance of {@code VersionNumber}; or
* {@link VersionNumber#UNKNOWN} when the string couldn't be parsed
* @since 1.5
*/
public static VersionNumber parse(String version) {
if (version == null || version.isEmpty()) {
return UNKNOWN;
}
var matcher = VERSION_PATTERN.matcher(version);
if (!matcher.matches()) {
return UNKNOWN;
}
var major = matcher.group("major");
var minor = matcher.group("minor");
var revision = matcher.group("revision");
var major_integer = (major != null ? Integer.parseInt(major) : null);
var minor_integer = (minor != null ? Integer.parseInt(minor) : null);
var revision_integer = (revision != null ? Integer.parseInt(revision) : null);
var qualifier = matcher.group("qualifier");
var separator = matcher.group("separator");
return new VersionNumber(major_integer, minor_integer, revision_integer, qualifier, separator);
}
/**
* Constructs a version number with only a major component.
*
* @param major the major version component
* @since 1.5
*/
public VersionNumber(Integer major) {
this(major, null, null, "");
}
/**
* Constructs a version number with a major and minor component.
*
* @param major the major version component
* @param minor the minor version component
* @since 1.5
*/
public VersionNumber(Integer major, Integer minor) {
this(major, minor, null, "");
}
/**
* Constructs a version number with major, minor and revision components.
*
* @param major the major version component
* @param minor the minor version component
* @param revision the version revision component
* @since 1.5
*/
public VersionNumber(Integer major, Integer minor, Integer revision) {
this(major, minor, revision, "");
}
/**
* Constructs a complete version number with qualifier, the separator will default to "{@code -}".
*
* @param major the major version component
* @param minor the minor version component
* @param revision the version revision component
* @param qualifier the version qualifier
* @since 1.5
*/
public VersionNumber(Integer major, Integer minor, Integer revision, String qualifier) {
this(major, minor, revision, qualifier, "-");
}
/**
* Constructs a complete version number with qualifier.
*
* @param major the major version component
* @param minor the minor version component
* @param revision the version revision component
* @param qualifier the version qualifier
* @param separator the separator for the version qualifier
* @since 1.5
*/
public VersionNumber(Integer major, Integer minor, Integer revision, String qualifier, String separator) {
this.major = major;
this.minor = minor;
this.revision = revision;
this.qualifier = (qualifier == null ? "" : qualifier);
this.separator = separator;
}
/**
* Retrieves the base version number without the qualifier.
*
* @return the base version number instance
* @since 1.5
*/
public VersionNumber getBaseVersion() {
return new VersionNumber(major, minor, revision, null);
}
/**
* Retrieves the version number with a different qualifier.
*
* @return this version number with a different qualifier
* @since 1.5.8
*/
public VersionNumber withQualifier(String qualifier) {
return new VersionNumber(major, minor, revision, qualifier);
}
/**
* Returns a primitive integer for the major version component.
*
* @return the major version component as an {@code int}
* @since 1.5
*/
public int majorInt() {
return major == null ? 0 : major;
}
/**
* Returns a primitive integer for the minor version component.
*
* @return the minor version component as an {@code int}
* @since 1.5
*/
public int minorInt() {
return minor == null ? 0 : minor;
}
/**
* Returns a primitive integer for the version revision component.
*
* @return the version revision component as an {@code int}
* @since 1.5
*/
public int revisionInt() {
return revision == null ? 0 : revision;
}
/**
* Indicates whether this is a snapshot version.
*
* @return {@code true} if this is a snapshot version; or
* {@code false} otherwise
* @since 1.5.8
*/
public boolean isSnapshot() {
return qualifier().toUpperCase().contains(SNAPSHOT_QUALIFIER);
}
public int compareTo(VersionNumber other) {
if (majorInt() != other.majorInt()) {
return majorInt() - other.majorInt();
}
if (minorInt() != other.minorInt()) {
return minorInt() - other.minorInt();
}
if (revisionInt() != other.revisionInt()) {
return revisionInt() - other.revisionInt();
}
if (qualifier.equals(other.qualifier)) {
return 0;
} else if (qualifier.isEmpty()) {
return 1;
} else if (other.qualifier.isEmpty()) {
return -1;
}
return qualifier.toLowerCase().compareTo(other.qualifier.toLowerCase());
}
public String toString() {
var version = new StringBuilder();
version.append(majorInt());
if (minor != null || revision != null) {
version.append(".");
version.append(minorInt());
}
if (revision != null) {
version.append(".");
version.append(revisionInt());
}
if (qualifier != null && !qualifier.isEmpty()) {
version.append(separator);
version.append(qualifier);
}
return version.toString();
}
@Override
public boolean equals(Object other) {
return other instanceof VersionNumber && compareTo((VersionNumber) other) == 0;
}
@Override
public int hashCode() {
int result = majorInt();
result = 31 * result + minorInt();
result = 31 * result + revisionInt();
result = 31 * result + Objects.hashCode(qualifier.toLowerCase());
return result;
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import rife.xml.Xml2Data;
import java.util.*;
import java.util.regex.Pattern;
/**
* Parses an XML document to generate {@link MavenMetadata}, this is an internal class.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.8
*/
public class Xml2MavenMetadata extends Xml2Data implements MavenMetadata {
private VersionNumber latest_ = VersionNumber.UNKNOWN;
private VersionNumber release_ = VersionNumber.UNKNOWN;
private final List<VersionNumber> versions_;
private VersionNumber snapshot_ = VersionNumber.UNKNOWN;
private StringBuilder characterData_ = null;
private String snapshotTimestamp_ = null;
private Integer snapshotBuildNumber_ = null;
public Xml2MavenMetadata() {
versions_ = new ArrayList<>();
}
public VersionNumber getLatest() {
return latest_;
}
public VersionNumber getRelease() {
return release_;
}
public VersionNumber getSnapshot() {
return snapshot_;
}
public String getSnapshotTimestamp() {
return snapshotTimestamp_;
}
public Integer getSnapshotBuildNumber() {
return snapshotBuildNumber_;
}
public List<VersionNumber> getVersions() {
return versions_;
}
public void startElement(String uri, String localName, String qName, Attributes attributes) {
characterData_ = new StringBuilder();
}
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case "latest" -> latest_ = VersionNumber.parse(characterData_.toString());
case "release" -> release_ = VersionNumber.parse(characterData_.toString());
case "version" -> versions_.add(VersionNumber.parse(characterData_.toString()));
case "timestamp" -> snapshotTimestamp_ = characterData_.toString();
case "buildNumber" -> snapshotBuildNumber_ = Integer.parseInt(characterData_.toString());
case "snapshot" -> {
if (!versions_.isEmpty()) {
var version = versions_.get(0);
var qualifier = VersionNumber.SNAPSHOT_QUALIFIER;
if (snapshotTimestamp_ != null && snapshotBuildNumber_ != null) {
qualifier = snapshotTimestamp_ + "-" + snapshotBuildNumber_;
}
snapshot_ = new VersionNumber(version.major(), version.minor(), version.revision(), qualifier);
}
}
}
characterData_ = null;
}
private static final Pattern MILESTONE = Pattern.compile("^m\\d*$");
private static final Pattern BETA = Pattern.compile("^b\\d*$");
private static final Pattern ALPHA = Pattern.compile("^a\\d*$");
public void endDocument()
throws SAXException {
// determine latest stable version by removing pre-release qualifiers
var filtered_versions = new TreeSet<VersionNumber>();
filtered_versions.addAll(versions_.stream()
.filter(v -> {
if (v.qualifier() == null) return true;
var q = v.qualifier().toLowerCase();
return !q.startsWith("rc") &&
!q.startsWith("cr") &&
!q.contains("milestone") &&
!MILESTONE.matcher(q).matches() &&
!q.contains("beta") &&
!BETA.matcher(q).matches() &&
!q.contains("alpha") &&
!ALPHA.matcher(q).matches();
}).toList());
// only replace the stable version from the metadata when
// something remained from the filtering, then use the
// last version in the sorted set
if (!filtered_versions.isEmpty()) {
latest_ = filtered_versions.last();
}
}
public void characters(char[] ch, int start, int length) {
if (characterData_ != null) {
characterData_.append(String.copyValueOf(ch, start, length));
}
}
}

View file

@ -0,0 +1,375 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies;
import org.xml.sax.Attributes;
import rife.xml.Xml2Data;
import java.util.*;
import java.util.regex.Pattern;
/**
* Parses an XML document to retrieve POM information, this is an internal class.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.18
*/
class Xml2MavenPom extends Xml2Data {
private final Dependency parent_;
private final ArtifactRetriever retriever_;
private final List<Repository> repositories_;
private Map<Scope, Set<PomDependency>> resolvedDependencies_ = null;
private final Map<PomDependency, PomDependency> dependencyManagement_ = new LinkedHashMap<>();
private final Set<PomDependency> dependencies_ = new LinkedHashSet<>();
private final Map<String, String> properties_ = new HashMap<>();
private final Stack<String> elementStack_ = new Stack<>();
private ExclusionSet exclusions_ = null;
private boolean collectProperties_ = false;
private boolean collectDependencyManagement_ = false;
private boolean collectDependencies_ = false;
private boolean collectExclusions_ = false;
private StringBuilder characterData_ = null;
private String lastGroupId_ = null;
private String lastArtifactId_ = null;
private String lastVersion_ = null;
private String lastType_ = null;
private String lastClassifier_ = null;
private String lastScope_ = null;
private String lastOptional_ = null;
private String lastExclusionGroupId_ = null;
private String lastExclusionArtifactId_ = null;
Xml2MavenPom(Dependency parent, ArtifactRetriever retriever, List<Repository> repositories) {
parent_ = parent;
retriever_ = retriever;
repositories_ = repositories;
}
Set<PomDependency> getDependencies(Scope... scopes) {
if (scopes == null || scopes.length == 0) {
return Collections.emptySet();
}
var scopes_list = Arrays.asList(scopes);
if (resolvedDependencies_ == null) {
var resolved_dependencies = new HashMap<Scope, Set<PomDependency>>();
if (!dependencies_.isEmpty()) {
for (var dependency : dependencies_) {
var managed_dependency = dependencyManagement_.get(dependency);
var version = dependency.version();
var dep_scope = dependency.scope();
var optional = dependency.optional();
var exclusions = dependency.exclusions();
if (managed_dependency != null) {
if (version == null) {
version = managed_dependency.version();
}
if (dep_scope == null) {
dep_scope = managed_dependency.scope();
}
if (optional == null) {
optional = managed_dependency.optional();
}
if (exclusions == null) {
exclusions = managed_dependency.exclusions();
}
}
if (dep_scope == null) {
dep_scope = "compile";
}
optional = resolveProperties(optional);
if ("true".equals(optional)) {
continue;
}
var resolved_dependency = new PomDependency(
resolveProperties(dependency.groupId()),
resolveProperties(dependency.artifactId()),
resolveProperties(version),
resolveProperties(dependency.classifier()),
resolveProperties(dependency.type()),
dep_scope,
"false",
exclusions,
dependency.parent());
if (resolved_dependency.type() == null || resolved_dependency.type().equals("jar")) {
var scope = Scope.valueOf(resolved_dependency.scope());
if (scopes_list.contains(scope)) {
var resolved_dependency_set = resolved_dependencies.computeIfAbsent(scope, k -> new LinkedHashSet<>());
resolved_dependency_set.add(resolved_dependency);
}
}
}
}
resolvedDependencies_ = resolved_dependencies;
}
var result = new LinkedHashSet<PomDependency>();
for (var scope : scopes) {
var deps = resolvedDependencies_.get(scope);
if (deps != null) {
result.addAll(deps);
}
}
return result;
}
PomDependency resolveDependency(PomDependency dependency) {
return new PomDependency(
resolveProperties(dependency.groupId()),
resolveProperties(dependency.artifactId()),
resolveProperties(dependency.version()),
resolveProperties(dependency.classifier()),
resolveProperties(dependency.type()),
dependency.scope(),
resolveProperties(dependency.optional()),
dependency.exclusions(),
dependency.parent());
}
public void startElement(String uri, String localName, String qName, Attributes attributes) {
characterData_ = new StringBuilder();
switch (qName) {
case "parent" -> resetState();
case "properties" -> {
if (isChildOfProject()) {
collectProperties_ = true;
}
}
case "dependencyManagement" -> {
if (isChildOfProject()) {
collectDependencyManagement_ = true;
}
}
case "dependencies" -> {
if (isChildOfProject()) {
resetState();
collectDependencies_ = true;
}
}
case "exclusions" -> {
if (collectDependencyManagement_ || collectDependencies_) {
collectExclusions_ = true;
exclusions_ = new ExclusionSet();
}
}
case "dependency" -> {
if (collectDependencies_) resetState();
}
}
elementStack_.push(qName);
}
public void endElement(String uri, String localName, String qName) {
elementStack_.pop();
switch (qName) {
case "parent" -> {
if (isChildOfProject()) {
var parent_dependency = new Dependency(resolveProperties(lastGroupId_), resolveProperties(lastArtifactId_), VersionNumber.parse(resolveProperties(lastVersion_)));
var parent = new DependencyResolver(retriever_, repositories_, parent_dependency).getMavenPom(parent_);
parent.properties_.keySet().removeAll(properties_.keySet());
properties_.putAll(parent.properties_);
parent.dependencyManagement_.keySet().removeAll(dependencyManagement_.keySet());
dependencyManagement_.putAll(parent.dependencyManagement_);
parent.dependencies_.removeAll(dependencies_);
dependencies_.addAll(parent.dependencies_);
resetState();
}
}
case "properties" -> collectProperties_ = false;
case "dependencyManagement" -> collectDependencyManagement_ = false;
case "dependencies" -> collectDependencies_ = false;
case "exclusions" -> collectExclusions_ = false;
case "exclusion" -> {
if (collectExclusions_) {
exclusions_.add(new DependencyExclusion(lastExclusionGroupId_, lastExclusionArtifactId_));
}
}
case "dependency" -> {
var dependency = new PomDependency(lastGroupId_, lastArtifactId_, lastVersion_, lastClassifier_, lastType_, lastScope_, lastOptional_, exclusions_, parent_);
if (collectDependencyManagement_) {
if (dependency.isPomImport()) {
var import_dependency = new Dependency(resolveProperties(lastGroupId_), resolveProperties(lastArtifactId_), VersionNumber.parse(resolveProperties(lastVersion_)));
var imported_pom = new DependencyResolver(retriever_, repositories_, import_dependency).getMavenPom(parent_);
imported_pom.dependencyManagement_.keySet().removeAll(dependencyManagement_.keySet());
var resolved_dependencies = new LinkedHashSet<PomDependency>();
for (var managed_dependency : imported_pom.dependencyManagement_.keySet()) {
resolved_dependencies.add(imported_pom.resolveDependency(managed_dependency));
}
resolved_dependencies.removeAll(dependencyManagement_.keySet());
for (var resolved_dependency : resolved_dependencies) {
dependencyManagement_.put(resolved_dependency, resolved_dependency);
}
} else {
dependencyManagement_.put(dependency, dependency);
}
} else if (collectDependencies_) {
dependencies_.add(dependency);
}
resetState();
}
case "groupId" -> {
if (isChildOfProject()) {
addProjectProperty(qName);
} else if (isChildOfParent() || isChildOfDependency()) {
lastGroupId_ = getCharacterData();
} else if (collectExclusions_ && isChildOfExclusion()) {
lastExclusionGroupId_ = getCharacterData();
}
}
case "artifactId" -> {
if (isChildOfProject()) {
addProjectProperty(qName);
} else if (isChildOfParent() || isChildOfDependency()) {
lastArtifactId_ = getCharacterData();
} else if (collectExclusions_ && isChildOfExclusion()) {
lastExclusionArtifactId_ = getCharacterData();
}
}
case "version" -> {
if (isChildOfProject()) {
addProjectProperty(qName);
} else if (isChildOfParent() || isChildOfDependency()) {
lastVersion_ = getCharacterData();
}
}
case "type" -> {
if (isChildOfDependency()) {
lastType_ = getCharacterData();
}
}
case "classifier" -> {
if (isChildOfDependency()) {
lastClassifier_ = getCharacterData();
}
}
case "scope" -> {
if (isChildOfDependency()) {
lastScope_ = getCharacterData();
}
}
case "optional" -> {
if (isChildOfDependency()) {
lastOptional_ = getCharacterData();
}
}
case "packaging", "name", "description", "url", "inceptionYear" -> {
if (isChildOfProject()) {
addProjectProperty(qName);
}
}
default -> {
if (collectProperties_) {
properties_.put(qName, getCharacterData());
}
}
}
characterData_ = null;
}
private boolean isChildOfProject() {
return elementStack_.peek().equals("project");
}
private boolean isChildOfParent() {
return elementStack_.peek().equals("parent");
}
private boolean isChildOfDependency() {
return elementStack_.peek().equals("dependency");
}
private boolean isChildOfExclusion() {
return elementStack_.peek().equals("exclusion");
}
private void addProjectProperty(String name) {
properties_.put("project." + name, getCharacterData());
}
private String getCharacterData() {
if (characterData_ == null) {
return null;
}
var result = characterData_.toString().trim();
if (result.isEmpty()) {
return null;
}
return result;
}
private static final Pattern MAVEN_PROPERTY = Pattern.compile("\\$\\{([^<>{}]+)}");
private String resolveProperties(String data) {
if (data == null) {
return null;
}
boolean replaced;
do {
replaced = false;
var processed_data = new StringBuilder();
var matcher = MAVEN_PROPERTY.matcher(data);
var last_end = 0;
while (matcher.find()) {
if (matcher.groupCount() == 1) {
var property = matcher.group(1);
if (properties_.containsKey(property)) {
processed_data.append(data, last_end, matcher.start());
processed_data.append(properties_.get(property));
last_end = matcher.end();
replaced = true;
}
}
}
if (last_end < data.length()) {
processed_data.append(data.substring(last_end));
}
data = processed_data.toString();
} while (replaced);
return data;
}
private void resetState() {
lastGroupId_ = null;
lastArtifactId_ = null;
lastVersion_ = null;
lastType_ = null;
lastClassifier_ = null;
lastScope_ = null;
lastOptional_ = null;
lastExclusionArtifactId_ = null;
lastExclusionGroupId_ = null;
exclusions_ = null;
}
public void characters(char[] ch, int start, int length) {
if (characterData_ != null) {
characterData_.append(String.copyValueOf(ch, start, length));
}
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies.exceptions;
import rife.bld.dependencies.Dependency;
import java.io.Serial;
public class ArtifactNotFoundException extends DependencyException {
@Serial private static final long serialVersionUID = 3137804373567469249L;
private final Dependency dependency_;
private final String location_;
public ArtifactNotFoundException(Dependency dependency, String location) {
super("Couldn't find artifact for dependency '" + dependency + "' at " + location);
dependency_ = dependency;
location_ = location;
}
public Dependency getDependency() {
return dependency_;
}
public String getLocation() {
return location_;
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies.exceptions;
import rife.bld.dependencies.Dependency;
import java.io.Serial;
public class ArtifactRetrievalErrorException extends DependencyException {
@Serial private static final long serialVersionUID = 5570184718213503548L;
private final Dependency dependency_;
private final String location_;
public ArtifactRetrievalErrorException(Dependency dependency, String location, Throwable e) {
super("Unexpected error while retrieving artifact for dependency '" + dependency + "' from '" + location + "'", e);
dependency_ = dependency;
location_ = location;
}
public Dependency getDependency() {
return dependency_;
}
public String getLocation() {
return location_;
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies.exceptions;
import java.io.Serial;
public class DependencyException extends RuntimeException {
@Serial private static final long serialVersionUID = 7683888067001718316L;
public DependencyException(String message) {
super(message);
}
public DependencyException(String message, Throwable cause) {
super(message, cause);
}
public DependencyException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies.exceptions;
import rife.bld.dependencies.Dependency;
import java.io.File;
import java.io.Serial;
public class DependencyTransferException extends DependencyException {
@Serial private static final long serialVersionUID = 2128741620203670830L;
private final Dependency dependency_;
private final String location_;
private final File destination_;
public DependencyTransferException(Dependency dependency, String location, File destination, Throwable e) {
super("Unable to transfer dependency '" + dependency + "' from '" + location + "' into '" + destination + "'", e);
dependency_ = dependency;
location_ = location;
destination_ = destination;
}
public Dependency getDependency() {
return dependency_;
}
public String getLocation() {
return location_;
}
public File getDestination() {
return destination_;
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.dependencies.exceptions;
import rife.bld.dependencies.Dependency;
import rife.tools.StringUtils;
import java.io.Serial;
import java.util.Set;
public class DependencyXmlParsingErrorException extends DependencyException {
@Serial private static final long serialVersionUID = -1050469071912675264L;
private final Dependency dependency_;
private final String location_;
private final Set<String> errors_;
public DependencyXmlParsingErrorException(Dependency dependency, String location, Set<String> errors) {
super("Unable to parse artifact document for dependency '" + dependency + "' from '" + location + "' :\n" + StringUtils.join(errors, "\n"));
dependency_ = dependency;
location_ = location;
errors_ = errors;
}
public Dependency getDependency() {
return dependency_;
}
public String getLocation() {
return location_;
}
public Set<String> getErrors() {
return errors_;
}
}

View file

@ -0,0 +1,10 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
/**
* Provides exception classes build system dependency handling.
* @since 1.5
*/
package rife.bld.dependencies.exceptions;

View file

@ -0,0 +1,10 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
/**
* Provides functionalities to resolve and retrieve dependency artifacts.
* @since 1.5
*/
package rife.bld.dependencies;

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the clean command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CleanHelp implements CommandHelp {
public String getSummary() {
return "Cleans the build files";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Cleans the build files.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the compile command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CompileHelp implements CommandHelp {
public String getSummary() {
return "Compiles the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Compiles the project.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the create-blank command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.20
*/
public class CreateBaseHelp implements CommandHelp {
public String getSummary() {
return "Creates a new baseline Java project with minimal commands";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a new baseline Java project with minimal commands.
Usage : ${topic} <package> <name>
package The package of the project to create
name The name of the project to create""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the create-blank command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CreateBlankHelp implements CommandHelp {
public String getSummary() {
return "Creates a new blank Java project with standard commands";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a new blank Java project with standard commands.
Usage : ${topic} <package> <name>
package The package of the project to create
name The name of the project to create""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the create-lib command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.6
*/
public class CreateLibHelp implements CommandHelp {
public String getSummary() {
return "Creates a new Java library with minimal commands";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a new library Java project with minimal commands.
Usage : ${topic} <package> <name>
package The package of the project to create
name The name of the project to create""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the create command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CreateRife2Help implements CommandHelp {
public String getSummary() {
return "Creates a new RIFE2 project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a new RIFE2 project.
Usage : ${topic} <package> <name>
package The package of the project to create
name The name of the project to create""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the dependency tree command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.21
*/
public class DependencyTreeHelp implements CommandHelp {
public String getSummary() {
return "Outputs the dependency tree of the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Outputs the dependency tree of the project
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the download command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class DownloadHelp implements CommandHelp {
public String getSummary() {
return "Downloads all dependencies of the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Downloads all dependencies of the project
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
/**
* Provides help for the help command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class HelpHelp implements CommandHelp {
public String getSummary() {
return "Provides help about any of the other commands";
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the JUnit test command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.20
*/
public class JUnitHelp implements CommandHelp {
public String getSummary() {
return "Tests the project with JUnit (takes options)";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Tests the project with JUnit.
Additional JUnit console launcher options can be
provided after the command.
These commandline options are provided by this command:
--junit-help see the full list of JUnit launcher options
--junit-clear clear the JUnit launcher options the build uses
(needs to be provided before other options)
Usage : ${topic} [OPTIONS]""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the jar command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class JarHelp implements CommandHelp {
public String getSummary() {
return "Creates a jar archive for the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a jar archive for the project.
The standard jar command will automatically also execute
the compile and precompile commands beforehand.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the jar-javadoc command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.10
*/
public class JarJavadocHelp implements CommandHelp {
public String getSummary() {
return "Creates a javadoc jar archive for the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a javadoc jar archive for the project.
The standard jar-javadoc command will automatically also execute
the compile and javadoc commands beforehand.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the jar-sources command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.10
*/
public class JarSourcesHelp implements CommandHelp {
public String getSummary() {
return "Creates a sources jar archive for the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a sources jar archive for the project.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the javadoc command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.10
*/
public class JavadocHelp implements CommandHelp {
public String getSummary() {
return "Generates javadoc for the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Generates javadoc for the project.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the precompile command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class PrecompileHelp implements CommandHelp {
public String getSummary() {
return "Pre-compiles RIFE2 templates to class files";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Pre-compiles RIFE2 templates to class files
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the publish command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.7
*/
public class PublishHelp implements CommandHelp {
public String getSummary() {
return "Publishes the artifacts of your project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Publishes the artifacts of the project to the publication
repository.
The standard publish command will automatically also execute
the jar, jar-sources and jar-javadoc commands beforehand.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the publish web command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.7
*/
public class PublishWebHelp implements CommandHelp {
public String getSummary() {
return "Publishes the artifacts of your web project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Publishes the artifacts of the web project to the publication
repository.
The standard web publish command will automatically also execute
the jar, jar-sources, jar-javadoc, uberjar and war commands
beforehand.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the purge command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class PurgeHelp implements CommandHelp {
public String getSummary() {
return "Purges all unused artifacts from the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Purges all the unused dependency artifacts from the project
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the run command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class RunHelp implements CommandHelp {
public String getSummary() {
return "Runs the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Runs the project.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the test command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class TestHelp implements CommandHelp {
public String getSummary() {
return "Tests the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Tests the project.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the uberjar command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class UberJarHelp implements CommandHelp {
public String getSummary() {
return "Creates an UberJar archive for the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates an UberJar archive for the project.
The standard uberjar command will automatically also execute
the jar command beforehand.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the updates command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class UpdatesHelp implements CommandHelp {
public String getSummary() {
return "Checks for updates of the project dependencies";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Checks which updates are available for the project dependencies.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the upgrade command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class UpgradeHelp implements CommandHelp {
public String getSummary() {
return "Upgrades the bld wrapper to the latest version";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Upgrades the bld wrapper to the latest version.
This command should be executed in the root directory of
your project.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the version command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.2
*/
public class VersionHelp implements CommandHelp {
public String getSummary() {
return "Outputs the version of the build system";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Outputs the version of the build system.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.help;
import rife.bld.CommandHelp;
import rife.tools.StringUtils;
/**
* Provides help for the war command.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class WarHelp implements CommandHelp {
public String getSummary() {
return "Creates a war archive for the project";
}
public String getDescription(String topic) {
return StringUtils.replace("""
Creates a war archive for the project.
The standard war command will automatically also execute
the jar command beforehand.
Usage : ${topic}""", "${topic}", topic);
}
}

View file

@ -0,0 +1,10 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
/**
* Provides help texts for build commands.
* @since 1.5
*/
package rife.bld.help;

View file

@ -0,0 +1,526 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BldVersion;
import rife.bld.Project;
import rife.bld.operations.exceptions.OperationOptionException;
import rife.bld.wrapper.Wrapper;
import rife.template.TemplateFactory;
import rife.tools.FileUtils;
import rife.tools.StringUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import rife.validation.ValidityChecks;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Provides the baseline foundation for creating a project structure.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public abstract class AbstractCreateOperation<T extends AbstractCreateOperation<T, P>, P extends Project> extends AbstractOperation<AbstractCreateOperation<T, P>> {
final String templateBase_;
File workDirectory_ = new File(System.getProperty("user.dir"));
String packageName_;
String projectName_;
boolean downloadDependencies_;
P project_;
String projectClassName_;
String projectBuildName_;
String projectMainName_;
String projectMainUberName_;
String projectTestName_;
File bldPackageDirectory_;
File mainPackageDirectory_;
File testPackageDirectory_;
File ideaDirectory_;
File ideaLibrariesDirectory_;
File ideaRunConfigurationsDirectory_;
File vscodeDirectory_;
protected AbstractCreateOperation(String templateBase) {
templateBase_ = templateBase;
}
/**
* Performs the creation operation.
*
* @throws FileUtilsErrorException when an error occurred during the creation operation
* @throws IOException when an error occurred during the creation operation
* @since 1.5
*/
public void execute()
throws FileUtilsErrorException, IOException {
if (packageName() == null || projectName() == null) {
System.err.println("ERROR: Missing package or project name.");
return;
}
executeConfigure();
executeCreateProjectStructure();
executePopulateProjectStructure();
executePopulateIdeaProject();
executePopulateVscodeProject();
if (downloadDependencies()) {
executeDownloadDependencies();
}
if (!silent()) {
System.out.println("The project was successfully created at '" + project_.workDirectory() + "'.");
}
}
/**
* Create a blueprint of the project.
*
* @return a blueprint for project creation
* @since 1.5
*/
protected abstract P createProjectBlueprint();
/**
* Part of the {@link #execute} operation, configures the project.
*
* @since 1.5
*/
protected void executeConfigure() {
project_ = createProjectBlueprint();
// standard names
projectClassName_ = StringUtils.capitalize(project_.name());
projectBuildName_ = projectBuildClassName(projectClassName_);
projectMainName_ = projectMainClassName(projectClassName_);
projectMainUberName_ = projectMainUberClassName(projectClassName_);
projectTestName_ = projectTestClassName(projectClassName_);
// create the main project structure
ideaDirectory_ = new File(project_.workDirectory(), ".idea");
ideaLibrariesDirectory_ = new File(ideaDirectory_, "libraries");
ideaRunConfigurationsDirectory_ = new File(ideaDirectory_, "runConfigurations");
vscodeDirectory_ = new File(project_.workDirectory(), ".vscode");
var package_dir = project_.pkg().replace('.', File.separatorChar);
bldPackageDirectory_ = new File(project_.srcBldJavaDirectory(), package_dir);
mainPackageDirectory_ = new File(project_.srcMainJavaDirectory(), package_dir);
testPackageDirectory_ = new File(project_.srcTestJavaDirectory(), package_dir);
}
/**
* Generates the build class name from the project class name
* @param projectClassName the project class name
* @return the generated build class name
* @since 1.6
*/
protected String projectBuildClassName(String projectClassName) {
return projectClassName + "Build";
}
/**
* Generates the main class name from the project class name
* @param projectClassName the project class name
* @return the generated main class name
* @since 1.6
*/
protected String projectMainClassName(String projectClassName) {
return projectClassName + "Main";
}
/**
* Generates the main uber class name from the project class name
* @param projectClassName the project class name
* @return the generated main uber class name
* @since 1.6
*/
protected String projectMainUberClassName(String projectClassName) {
return projectClassName + "Main";
}
/**
* Generates the test class name from the project class name
* @param projectClassName the project class name
* @return the generated test class name
* @since 1.6
*/
protected String projectTestClassName(String projectClassName) {
return projectClassName + "Test";
}
/**
* Part of the {@link #execute} operation, creates the project structure.
*
* @since 1.5
*/
protected void executeCreateProjectStructure() {
project_.createProjectStructure();
bldPackageDirectory_.mkdirs();
mainPackageDirectory_.mkdirs();
testPackageDirectory_.mkdirs();
ideaDirectory_.mkdirs();
ideaLibrariesDirectory_.mkdirs();
ideaRunConfigurationsDirectory_.mkdirs();
vscodeDirectory_.mkdirs();
}
/**
* Part of the {@link #execute} operation, populates the project structure.
*
* @since 1.5
*/
protected void executePopulateProjectStructure()
throws FileUtilsErrorException, IOException {
// project gitignore
FileUtils.writeString(
TemplateFactory.TXT.get(templateBase_ + "project_gitignore").getContent(),
new File(project_.workDirectory(), ".gitignore"));
// project main
var site_template = TemplateFactory.TXT.get(templateBase_ + "project_main");
site_template.setValue("package", project_.pkg());
site_template.setValue("projectMain", projectMainName_);
var project_main_file = new File(mainPackageDirectory_, projectMainName_ + ".java");
FileUtils.writeString(site_template.getContent(), project_main_file);
// project test
var test_template = TemplateFactory.TXT.get(templateBase_ + "project_test");
test_template.setValue("package", project_.pkg());
test_template.setValue("projectTest", projectTestName_);
test_template.setValue("projectMain", projectMainName_);
if (test_template.hasValueId("project")) {
test_template.setValue("project", projectClassName_);
}
var project_test_file = new File(testPackageDirectory_, projectTestName_ + ".java");
FileUtils.writeString(test_template.getContent(), project_test_file);
// project build
var build_template = TemplateFactory.TXT.get(templateBase_ + "project_build");
build_template.setValue("projectBuild", projectBuildName_);
build_template.setValue("package", project_.pkg());
build_template.setValue("project", projectClassName_);
if (build_template.hasValueId("projectMain")) {
build_template.setValue("projectMain", projectMainName_);
}
if (build_template.hasValueId("projectTest")) {
build_template.setValue("projectTest", projectTestName_);
}
if (build_template.hasValueId("projectMainUber")) {
build_template.setValue("projectMainUber", projectMainUberName_);
}
for (var entry : project_.dependencies().entrySet()) {
build_template.blankValue("dependencies");
for (var dependency : entry.getValue()) {
build_template.setValue("groupId", dependency.groupId());
build_template.setValue("artifactId", dependency.artifactId());
var version = dependency.version();
var version_string = version.major() + "," + version.minor() + "," + version.revision();
if (!version.qualifier().isEmpty()) {
version_string += ",\"" + version.qualifier() + "\"";
}
build_template.setValue("version", version_string);
build_template.appendBlock("dependencies", "dependency");
}
build_template.setValue("name", entry.getKey().name());
build_template.appendBlock("scopes", "scope");
}
var project_build_file = new File(bldPackageDirectory_, projectBuildName_ + ".java");
FileUtils.writeString(build_template.getContent(), project_build_file);
// build shell scripts
var build_sh_template = TemplateFactory.TXT.get("bld.bld");
build_sh_template.setValue("projectBuild", projectBuildName_);
build_sh_template.setValue("package", project_.pkg());
var build_sh_file = new File(project_.workDirectory(), "bld");
FileUtils.writeString(build_sh_template.getContent(), build_sh_file);
build_sh_file.setExecutable(true);
var build_bat_template = TemplateFactory.TXT.get("bld.bld_bat");
build_bat_template.setValue("projectBuild", projectBuildName_);
build_bat_template.setValue("package", project_.pkg());
var build_bat_file = new File(project_.workDirectory(), "bld.bat");
FileUtils.writeString(build_bat_template.getContent(), build_bat_file);
// create the wrapper files
new Wrapper().createWrapperFiles(project_.libBldDirectory(), BldVersion.getVersion());
}
/**
* Part of the {@link #execute} operation, populates the IDEA project structure.
*
* @since 1.5
*/
protected void executePopulateIdeaProject()
throws FileUtilsErrorException {
// IDEA project files
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.app_iml").getContent(),
new File(ideaDirectory_, "app.iml"));
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.bld_iml").getContent(),
new File(ideaDirectory_, "bld.iml"));
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.misc").getContent(),
new File(ideaDirectory_, "misc.xml"));
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.modules").getContent(),
new File(ideaDirectory_, "modules.xml"));
var bld_xml_template = TemplateFactory.XML.get(templateBase_ + "idea.libraries.bld");
bld_xml_template.setValue("version", BldVersion.getVersion());
var bld_xml_file = new File(ideaLibrariesDirectory_, "bld.xml");
FileUtils.writeString(bld_xml_template.getContent(), bld_xml_file);
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.libraries.compile").getContent(),
new File(ideaLibrariesDirectory_, "compile.xml"));
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.libraries.runtime").getContent(),
new File(ideaLibrariesDirectory_, "runtime.xml"));
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.libraries.test").getContent(),
new File(ideaLibrariesDirectory_, "test.xml"));
// IDEA run site
if (createIdeaRunMain()) {
var run_site_template = TemplateFactory.XML.get(templateBase_ + "idea.runConfigurations.Run_Main");
run_site_template.setValue("package", project_.pkg());
run_site_template.setValue("projectMain", projectMainName_);
var run_site_file = new File(ideaRunConfigurationsDirectory_, "Run Main.xml");
FileUtils.writeString(run_site_template.getContent(), run_site_file);
}
// IDEA run tests
var run_tests_template = TemplateFactory.XML.get(templateBase_ + "idea.runConfigurations.Run_Tests");
run_tests_template.setValue("package", project_.pkg());
if (run_tests_template.hasValueId("projectTest")) {
run_tests_template.setValue("projectTest", projectTestName_);
}
var run_tests_file = new File(ideaRunConfigurationsDirectory_, "Run Tests.xml");
FileUtils.writeString(run_tests_template.getContent(), run_tests_file);
}
/**
* Indicates whether the IDEA main run target should be generated
* @return {@code true} of it should be generated; or {@code false} otherwise
* @since 1.6
*/
protected boolean createIdeaRunMain() {
return true;
}
/**
* Part of the {@link #execute} operation, populates the vscode project structure.
*
* @since 1.5.6
*/
protected void executePopulateVscodeProject()
throws FileUtilsErrorException {
var launch_template = TemplateFactory.JSON.get(templateBase_ + "vscode.launch");
launch_template.setValue("package", project_.pkg());
if (launch_template.hasValueId("projectMain")) {
launch_template.setValue("projectMain", projectMainName_);
}
if (launch_template.hasValueId("projectTest")) {
launch_template.setValue("projectTest", projectTestName_);
}
var launch_file = new File(vscodeDirectory_, "launch.json");
FileUtils.writeString(launch_template.getContent(), launch_file);
var settings_template = TemplateFactory.JSON.get(templateBase_ + "vscode.settings");
if (settings_template.hasValueId("version")) {
settings_template.setValue("version", BldVersion.getVersion());
}
var settings_file = new File(vscodeDirectory_, "settings.json");
FileUtils.writeString(settings_template.getContent(), settings_file);
}
/**
* Part of the {@link #execute} operation, downloads the dependencies, when enabled.
*
* @since 1.5
*/
protected void executeDownloadDependencies() {
new DownloadOperation().fromProject(project_).execute();
}
/**
* Configures a creation operation from command-line arguments.
*
* @param arguments the arguments that will be considered
* @return this operation instance
* @since 1.5
*/
public T fromArguments(List<String> arguments) {
String package_name = null;
String project_name = null;
if (arguments.size() > 0) {
package_name = arguments.remove(0);
}
if (arguments.size() > 0) {
project_name = arguments.remove(0);
}
if ((package_name == null || project_name == null) && System.console() == null) {
throw new OperationOptionException("ERROR: Expecting the package and project names as the arguments.");
}
if (package_name == null || package_name.isEmpty()) {
System.out.println("Please enter a package name (for instance: com.example):");
package_name = System.console().readLine();
} else {
System.out.println("Using package name: " + package_name);
}
if (project_name == null || project_name.isEmpty()) {
System.out.println("Please enter a project name (for instance: myapp):");
project_name = System.console().readLine();
} else {
System.out.println("Using project name: " + project_name);
}
return workDirectory(new File(System.getProperty("user.dir")))
.packageName(package_name)
.projectName(project_name)
.downloadDependencies(true);
}
/**
* Provides the work directory in which the project will be created.
* <p>
* If no work directory is provided, the JVM working directory will be used.
*
* @param directory the directory to use as a work directory
* @return this operation instance
* @since 1.5
*/
public T workDirectory(File directory) {
if (!directory.exists()) {
throw new OperationOptionException("ERROR: The work directory '" + directory + "' doesn't exist.");
}
if (!directory.isDirectory()) {
throw new OperationOptionException("ERROR: '" + directory + "' is not a directory.");
}
if (!directory.canWrite()) {
throw new OperationOptionException("ERROR: The work directory '" + directory + "' is not writable.");
}
workDirectory_ = directory;
return (T) this;
}
/**
* Provides the package of the project that will be created.
*
* @param name the package name
* @return this operation instance
* @since 1.5
*/
public T packageName(String name) {
packageName_ = StringUtils.trim(name);
if (packageName_.isEmpty()) {
throw new OperationOptionException("ERROR: The package name should not be blank.");
}
if (!ValidityChecks.checkJavaPackage(packageName_)) {
throw new OperationOptionException("ERROR: The package name is invalid.");
}
packageName_ = name;
return (T) this;
}
/**
* Provides the name of the project that will be created.
*
* @param name the project name
* @return this operation instance
* @since 1.5
*/
public T projectName(String name) {
projectName_ = StringUtils.trim(name);
if (projectName_.isEmpty()) {
throw new OperationOptionException("ERROR: The project name should not be blank.");
}
if (!ValidityChecks.checkJavaIdentifier(projectName_)) {
throw new OperationOptionException("ERROR: The project name is invalid.");
}
projectName_ = name;
return (T) this;
}
/**
* Indicates whether the dependencies for the project should be downloaded
* upon creation, by default this is {@code false}.
*
* @param flag {@code true} if the dependencies should be downloaded; or
* {@code false} otherwise
* @return this operation instance
* @since 1.5
*/
public T downloadDependencies(boolean flag) {
downloadDependencies_ = flag;
return (T) this;
}
/**
* Retrieves the work directory that is used for the project creation.
*
* @return the work directory
* @since 1.5
*/
public File workDirectory() {
return workDirectory_;
}
/**
* Retrieves the package that is used for the project creation.
*
* @return the package name
* @since 1.5
*/
public String packageName() {
return packageName_;
}
/**
* Retrieves the name that is used for the project creation.
*
* @return the project name
* @since 1.5
*/
public String projectName() {
return projectName_;
}
/**
* Retrieves whether dependencies will be downloaded at project creation.
*
* @return {@code true} if dependencies will be downloaded; or
* {@code false} otherwise
* @since 1.5
*/
public boolean downloadDependencies() {
return downloadDependencies_;
}
/**
* Retrieves the project instance that was used as a blueprint for the
* project creation.
*
* @return the project creation blueprint instance
* @since 1.5
*/
public P project() {
return project_;
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
/**
* Provides common features across all operations
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.2
*/
public abstract class AbstractOperation<T extends AbstractOperation<T>> {
private boolean silent_ = false;
private boolean executed_ = false;
/**
* Changes whether the operation should be silent or not.
* <p>
* Defaults to not silent.
*
* @param silent {@code true} if the operation should be silent;
* {@code false} otherwise
* @return this operation instance
* @since 1.5.2
*/
public T silent(boolean silent) {
silent_ = silent;
return (T) this;
}
/**
* Indicates whether the operation should be silent or not.
*
* @return {@code true} if the operation should be silent;
* {@code false} otherwise
* @since 1.5.2
*/
public boolean silent() {
return silent_;
}
/**
* Ensures that this operation instance is executed once and only once.
*
* @throws Exception when an exception was thrown by the {@link #execute()} call
* @see #executeOnce(Runnable)
* @since 1.5.17
*/
public void executeOnce()
throws Exception {
executeOnce(null);
}
/**
* Ensures that this operation instance is executed once and only once.
* <p>
* A setup lambda can be provided that is called when the only execution takes place.
*
* @param setup the setup lambda that will be called with the only execution
* @throws Exception when an exception was thrown by the {@link #execute()} call
* @see #executeOnce()
* @since 1.5.17
*/
public void executeOnce(Runnable setup)
throws Exception {
if (executed_) {
return;
}
executed_ = true;
if (setup != null) {
setup.run();
}
execute();
}
/**
* Performs the operation execution that can be wrapped by the {@code #executeOnce} call.
*
* @throws Exception when an exception occurs during the execution
* @since 1.5.10
*/
public abstract void execute()
throws Exception;
}

View file

@ -0,0 +1,337 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.operations.exceptions.ExitStatusException;
import rife.bld.operations.exceptions.OperationOptionException;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
* Abstract operation that starts a Java application as a separate process.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.18
*/
public abstract class AbstractProcessOperation<T extends AbstractProcessOperation<T>> extends AbstractOperation<T> {
public static final String DEFAULT_JAVA_TOOL = "java";
protected File workDirectory_ = new File(System.getProperty("user.dir"));
protected String javaTool_ = DEFAULT_JAVA_TOOL;
protected final JavaOptions javaOptions_ = new JavaOptions();
protected final List<String> classpath_ = new ArrayList<>();
protected String mainClass_;
protected Function<String, Boolean> outputProcessor_;
protected Function<String, Boolean> errorProcessor_;
protected Process process_;
protected boolean successful_;
protected Thread outputProcessorThread_;
protected Thread errorProcessorThread_;
/**
* Performs the operation.
*
* @throws InterruptedException when the operation was interrupted
* @throws IOException when an exception occurred during the execution of the process
* @throws FileUtilsErrorException when an exception occurred during the retrieval of the operation output
* @throws ExitStatusException when the exit status was changed during the operation
* @since 1.5
*/
public void execute()
throws IOException, FileUtilsErrorException, InterruptedException, ExitStatusException {
successful_ = true;
outputProcessorThread_ = null;
errorProcessorThread_ = null;
process_ = executeStartProcess();
int status = process_.waitFor();
if (outputProcessorThread_ != null) {
outputProcessorThread_.join();
}
if (errorProcessorThread_ != null) {
errorProcessorThread_.join();
}
if (!successful_) {
status = ExitStatusException.EXIT_FAILURE;
}
ExitStatusException.throwOnFailure(status);
}
/**
* Part of the {@link #execute} operation, constructs the command list
* to use for building the process.
*
* @since 1.5
*/
abstract protected List<String> executeConstructProcessCommandList();
/**
* Part of the {@link #execute} operation, starts the process.
*
* @since 1.5
*/
protected Process executeStartProcess()
throws IOException {
var builder = new ProcessBuilder(executeConstructProcessCommandList());
builder.directory(workDirectory());
final var output_processor = outputProcessor();
if (output_processor == null) {
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
} else {
builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
}
final var error_processor = errorProcessor();
if (error_processor == null) {
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
} else {
builder.redirectError(ProcessBuilder.Redirect.PIPE);
}
final var process = builder.start();
if (output_processor != null) {
outputProcessorThread_ = startProcessStreamProcessor(process.getInputStream(), output_processor);
}
if (error_processor != null) {
errorProcessorThread_ = startProcessStreamProcessor(process.getErrorStream(), error_processor);
}
return process;
}
private Thread startProcessStreamProcessor(InputStream stream, Function<String, Boolean> processor) {
var processor_thread = new Thread(() -> {
try {
String line;
var in = new BufferedReader(new InputStreamReader(stream));
while ((line = in.readLine()) != null) {
successful_ &= processor.apply(line);
}
} catch (Exception e) {
// ignore
}
});
processor_thread.start();
return processor_thread;
}
/**
* Configures the operation from a {@link BaseProject}.
*
* @param project the project to configure the operation from
* @since 1.5
*/
abstract public T fromProject(BaseProject project);
/**
* Provides the work directory in which the operation will be performed.
* <p>
* If no work directory is provided, the JVM working directory will be used.
*
* @param directory the directory to use as a work directory
* @return this operation instance
* @since 1.5
*/
public T workDirectory(File directory) {
if (!directory.exists()) {
throw new OperationOptionException("ERROR: The work directory '" + directory + "' doesn't exist.");
}
if (!directory.isDirectory()) {
throw new OperationOptionException("ERROR: '" + directory + "' is not a directory.");
}
if (!directory.canWrite()) {
throw new OperationOptionException("ERROR: The work directory '" + directory + "' is not writable.");
}
workDirectory_ = directory;
return (T) this;
}
/**
* Provides the name of the tool to use for {@code java} execution.
* <p>
* If no java tool is provided {@code java} will be used.
*
* @param tool the name of the java tool
* @return this operation instance
* @since 1.5
*/
public T javaTool(String tool) {
javaTool_ = tool;
return (T) this;
}
/**
* Provides the options to provide to the java tool.
*
* @param options the java tool's options
* @return this operation instance
* @since 1.5
*/
public T javaOptions(List<String> options) {
javaOptions_.addAll(options);
return (T) this;
}
/**
* Provides classpath entries to use for the operation.
*
* @param classpath classpath entries for the operation
* @return this operation instance
* @since 1.5.18
*/
public T classpath(String... classpath) {
classpath_.addAll(List.of(classpath));
return (T) this;
}
/**
* Provides a list of classpath entries to use for the operation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param classpath a list of classpath entries for the operation
* @return this operation instance
* @since 1.5
*/
public T classpath(List<String> classpath) {
classpath_.addAll(classpath);
return (T) this;
}
/**
* Provides the main class to launch with the java tool.
*
* @param name the main class to launch
* @return this operation instance
* @since 1.5
*/
public T mainClass(String name) {
mainClass_ = name;
return (T) this;
}
/**
* Provides the processor that will be used to handle the process output.
* <p>
* It will be called for each line in the output.
*
* @param processor the output processor
* @return this operation instance
* @since 1.5.1
*/
public T outputProcessor(Function<String, Boolean> processor) {
outputProcessor_ = processor;
return (T) this;
}
/**
* Provides the processor that will be used to handle the process errors.
* <p>
* It will be called for each line in the error output.
*
* @param processor the error processor
* @return this operation instance
* @since 1.5.1
*/
public T errorProcessor(Function<String, Boolean> processor) {
errorProcessor_ = processor;
return (T) this;
}
/**
* Retrieves the work directory in which the operation will be performed.
*
* @return the directory to use as a work directory
* @since 1.5
*/
public File workDirectory() {
return workDirectory_;
}
/**
* retrieves the name of the tool to use for {@code java} execution.
*
* @return the name of the java tool
* @since 1.5
*/
public String javaTool() {
return javaTool_;
}
/**
* Retrieves the options to provide to the java tool.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the java tool's options
* @since 1.5
*/
public JavaOptions javaOptions() {
return javaOptions_;
}
/**
* Retrieves the classpath to use for the operation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the operation's classpath
* @since 1.5
*/
public List<String> classpath() {
return classpath_;
}
/**
* Retrieves the main class to launch with the java tool.
*
* @return the main class to launch
* @since 1.5
*/
public String mainClass() {
return mainClass_;
}
/**
* Retrieves the processor that is used to handle the process output.
*
* @return the output processor
* @since 1.5.1
*/
public Function<String, Boolean> outputProcessor() {
return outputProcessor_;
}
/**
* Retrieves the processor that is used to handle the process errors.
*
* @return the error processor
* @since 1.5.1
*/
public Function<String, Boolean> errorProcessor() {
return errorProcessor_;
}
/**
* Retrieves the process that was used for the execution.
*
* @return the process that was executed
* @since 1.5
*/
public Process process() {
return process_;
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.Project;
import rife.tools.FileUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Cleans by deleting a list of directories and all their contents.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CleanOperation extends AbstractOperation<CleanOperation> {
private final List<File> directories_ = new ArrayList<>();
/**
* Performs the clean operation.
*
* @since 1.5
*/
public void execute() {
for (var directory : directories()) {
executeCleanDirectory(directory);
}
if (!silent()) {
System.out.println("Cleaning finished successfully.");
}
}
/**
* Part of the {@link #execute} operation, cleans an individual directory.
*
* @param directory the directory to clean.
* @since 1.5
*/
protected void executeCleanDirectory(File directory) {
try {
FileUtils.deleteDirectory(directory);
} catch (FileUtilsErrorException e) {
// no-op
}
}
/**
* Configures a clean operation from a {@link BaseProject}.
*
* @param project the project to configure the clean operation from
* @since 1.5
*/
public CleanOperation fromProject(BaseProject project) {
return directories(project.buildDirectory()
.listFiles(f -> !f.equals(project.buildBldDirectory())));
}
/**
* Provides directories to clean.
*
* @param directories directories to clean
* @return this operation instance
* @since 1.5.18
*/
public CleanOperation directories(File... directories) {
directories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of directories to clean.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of directories to clean
* @return this operation instance
* @since 1.5
*/
public CleanOperation directories(List<File> directories) {
directories_.addAll(directories);
return this;
}
/**
* Retrieves the list of directories to clean.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of directories to clean.
* @since 1.5
*/
public List<File> directories() {
return directories_;
}
}

View file

@ -0,0 +1,480 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.Project;
import rife.bld.operations.exceptions.ExitStatusException;
import rife.tools.FileUtils;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Compiles main and test sources in the relevant build directories.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CompileOperation extends AbstractOperation<CompileOperation> {
private File buildMainDirectory_;
private File buildTestDirectory_;
private final List<String> compileMainClasspath_ = new ArrayList<>();
private final List<String> compileTestClasspath_ = new ArrayList<>();
private final List<File> mainSourceFiles_ = new ArrayList<>();
private final List<File> testSourceFiles_ = new ArrayList<>();
private final List<File> mainSourceDirectories_ = new ArrayList<>();
private final List<File> testSourceDirectories_ = new ArrayList<>();
private final JavacOptions compileOptions_ = new JavacOptions();
private final List<Diagnostic<? extends JavaFileObject>> diagnostics_ = new ArrayList<>();
/**
* Performs the compile operation.
*
* @since 1.5
*/
public void execute()
throws IOException, ExitStatusException {
executeCreateBuildDirectories();
executeBuildMainSources();
executeBuildTestSources();
if (!diagnostics().isEmpty()) {
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
}
if (!silent()) {
System.out.println("Compilation finished successfully.");
}
}
/**
* Part of the {@link #execute} operation, creates the build directories.
*
* @since 1.5
*/
protected void executeCreateBuildDirectories() {
if (buildMainDirectory() != null) {
buildMainDirectory().mkdirs();
}
if (buildTestDirectory() != null) {
buildTestDirectory().mkdirs();
}
}
/**
* Part of the {@link #execute} operation, builds the main sources.
*
* @since 1.5
*/
protected void executeBuildMainSources()
throws IOException {
var sources = new ArrayList<>(mainSourceFiles());
for (var directory : mainSourceDirectories()) {
sources.addAll(FileUtils.getJavaFileList(directory));
}
executeBuildSources(
compileMainClasspath(),
sources,
buildMainDirectory());
}
/**
* Part of the {@link #execute} operation, builds the test sources.
*
* @since 1.5
*/
protected void executeBuildTestSources()
throws IOException {
var sources = new ArrayList<>(testSourceFiles());
for (var directory : testSourceDirectories()) {
sources.addAll(FileUtils.getJavaFileList(directory));
}
executeBuildSources(
compileTestClasspath(),
sources,
buildTestDirectory());
}
/**
* Part of the {@link #execute} operation, build sources to a destination.
*
* @param classpath the classpath list used for the compilation
* @param sources the source files to compile
* @param destination the destination directory
* @since 1.5
*/
protected void executeBuildSources(List<String> classpath, List<File> sources, File destination)
throws IOException {
if (sources.isEmpty() || destination == null) {
return;
}
var compiler = ToolProvider.getSystemJavaCompiler();
try (var file_manager = compiler.getStandardFileManager(null, null, null)) {
var compilation_units = file_manager.getJavaFileObjectsFromFiles(sources);
var diagnostics = new DiagnosticCollector<JavaFileObject>();
var options = new ArrayList<>(List.of("-d", destination.getAbsolutePath(), "-cp", FileUtils.joinPaths(classpath)));
options.addAll(compileOptions());
var compilation_task = compiler.getTask(null, file_manager, diagnostics, options, null, compilation_units);
if (!compilation_task.call()) {
diagnostics_.addAll(diagnostics.getDiagnostics());
executeProcessDiagnostics(diagnostics);
}
}
}
/**
* Part of the {@link #execute} operation, processes the compilation diagnostics.
*
* @param diagnostics the diagnostics to process
* @since 1.5
*/
protected void executeProcessDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) {
for (var diagnostic : diagnostics.getDiagnostics()) {
System.err.print(executeFormatDiagnostic(diagnostic));
}
}
/**
* Part of the {@link #execute} operation, format a single diagnostic.
*
* @param diagnostic the diagnostic to format
* @return a string representation of the diagnostic
* @since 1.5
*/
protected String executeFormatDiagnostic(Diagnostic<? extends JavaFileObject> diagnostic) {
return diagnostic.toString() + System.lineSeparator();
}
/**
* Configures a compile operation from a {@link BaseProject}.
*
* @param project the project to configure the compile operation from
* @since 1.5
*/
public CompileOperation fromProject(BaseProject project) {
var operation = buildMainDirectory(project.buildMainDirectory())
.buildTestDirectory(project.buildTestDirectory())
.compileMainClasspath(project.compileMainClasspath())
.compileTestClasspath(project.compileTestClasspath())
.mainSourceFiles(project.mainSourceFiles())
.testSourceFiles(project.testSourceFiles());
if (project.javaRelease() != null && !compileOptions().containsRelease()) {
compileOptions().release(project.javaRelease());
}
return operation;
}
/**
* Provides the main build destination directory.
*
* @param directory the directory to use for the main build destination
* @return this operation instance
* @since 1.5
*/
public CompileOperation buildMainDirectory(File directory) {
buildMainDirectory_ = directory;
return this;
}
/**
* Provides the test build destination directory.
*
* @param directory the directory to use for the test build destination
* @return this operation instance
* @since 1.5
*/
public CompileOperation buildTestDirectory(File directory) {
buildTestDirectory_ = directory;
return this;
}
/**
* Provides entries for the main compilation classpath.
*
* @param classpath classpath entries
* @return this operation instance
* @since 1.5.18
*/
public CompileOperation compileMainClasspath(String... classpath) {
compileMainClasspath_.addAll(Arrays.asList(classpath));
return this;
}
/**
* Provides a list of entries for the main compilation classpath.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param classpath a list of classpath entries
* @return this operation instance
* @since 1.5
*/
public CompileOperation compileMainClasspath(List<String> classpath) {
compileMainClasspath_.addAll(classpath);
return this;
}
/**
* Provides entries for the test compilation classpath.
*
* @param classpath classpath entries
* @return this operation instance
* @since 1.5.18
*/
public CompileOperation compileTestClasspath(String... classpath) {
compileTestClasspath_.addAll(Arrays.asList(classpath));
return this;
}
/**
* Provides a list of entries for the test compilation classpath.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param classpath a list of classpath entries
* @return this operation instance
* @since 1.5
*/
public CompileOperation compileTestClasspath(List<String> classpath) {
compileTestClasspath_.addAll(classpath);
return this;
}
/**
* Provides main files that should be compiled.
*
* @param files main files
* @return this operation instance
* @since 1.5.18
*/
public CompileOperation mainSourceFiles(File... files) {
mainSourceFiles_.addAll(Arrays.asList(files));
return this;
}
/**
* Provides a list of main files that should be compiled.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of main files
* @return this operation instance
* @since 1.5
*/
public CompileOperation mainSourceFiles(List<File> files) {
mainSourceFiles_.addAll(files);
return this;
}
/**
* Provides test files that should be compiled.
*
* @param files test files
* @return this operation instance
* @since 1.5.18
*/
public CompileOperation testSourceFiles(File... files) {
testSourceFiles_.addAll(Arrays.asList(files));
return this;
}
/**
* Provides a list of test files that should be compiled.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of test files
* @return this operation instance
* @since 1.5
*/
public CompileOperation testSourceFiles(List<File> files) {
testSourceFiles_.addAll(files);
return this;
}
/**
* Provides main source directories that should be compiled.
*
* @param directories main source directories
* @return this operation instance
* @since 1.5.10
*/
public CompileOperation mainSourceDirectories(File... directories) {
mainSourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of main source directories that should be compiled.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of main source directories
* @return this operation instance
* @since 1.5.10
*/
public CompileOperation mainSourceDirectories(List<File> directories) {
mainSourceDirectories_.addAll(directories);
return this;
}
/**
* Provides test source directories that should be compiled.
*
* @param directories test source directories
* @return this operation instance
* @since 1.5.10
*/
public CompileOperation testSourceDirectories(File... directories) {
testSourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of test source directories that should be compiled.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of test source directories
* @return this operation instance
* @since 1.5.10
*/
public CompileOperation testSourceDirectories(List<File> directories) {
testSourceDirectories_.addAll(directories);
return this;
}
/**
* Provides a list of compilation options to provide to the compiler.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options the list of compilation options
* @return this operation instance
* @since 1.5
*/
public CompileOperation compileOptions(List<String> options) {
compileOptions_.addAll(options);
return this;
}
/**
* Retrieves the main build destination directory.
*
* @return the main build destination
* @since 1.5
*/
public File buildMainDirectory() {
return buildMainDirectory_;
}
/**
* Retrieves the test build destination directory.
*
* @return the test build destination
* @since 1.5
*/
public File buildTestDirectory() {
return buildTestDirectory_;
}
/**
* Retrieves the list of entries for the main compilation classpath.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the main compilation classpath list
* @since 1.5
*/
public List<String> compileMainClasspath() {
return compileMainClasspath_;
}
/**
* Retrieves the list of entries for the test compilation classpath.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the test compilation classpath list
* @since 1.5
*/
public List<String> compileTestClasspath() {
return compileTestClasspath_;
}
/**
* Retrieves the list of main files that should be compiled.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of main files to compile
* @since 1.5
*/
public List<File> mainSourceFiles() {
return mainSourceFiles_;
}
/**
* Retrieves the list of test files that should be compiled.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of test files to compile
* @since 1.5
*/
public List<File> testSourceFiles() {
return testSourceFiles_;
}
/**
* Retrieves the list of main source directories that should be compiled.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of main source directories to compile
* @since 1.5.10
*/
public List<File> mainSourceDirectories() {
return mainSourceDirectories_;
}
/**
* Retrieves the list of test source directories that should be compiled.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of test source directories to compile
* @since 1.5.10
*/
public List<File> testSourceDirectories() {
return testSourceDirectories_;
}
/**
* Retrieves the list of compilation options for the compiler.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of compiler options
* @since 1.5
*/
public JavacOptions compileOptions() {
return compileOptions_;
}
/**
* Retrieves the list of diagnostics resulting from the compilation.
*
* @return the list of compilation diagnostics
* @since 1.5
*/
public List<Diagnostic<? extends JavaFileObject>> diagnostics() {
return diagnostics_;
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.Project;
import rife.bld.blueprints.BaseProjectBlueprint;
import java.io.File;
/**
* Creates a new base project structure.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.20
*/
public class CreateBaseOperation extends AbstractCreateOperation<CreateBaseOperation, Project> {
public CreateBaseOperation() {
super("bld.base.");
}
protected Project createProjectBlueprint() {
return new BaseProjectBlueprint(new File(workDirectory(), projectName()), packageName(), projectName());
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.Project;
import rife.bld.blueprints.BlankProjectBlueprint;
import java.io.File;
/**
* Creates a new blank project structure.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CreateBlankOperation extends AbstractCreateOperation<CreateBlankOperation, Project> {
public CreateBlankOperation() {
super("bld.blank.");
}
protected Project createProjectBlueprint() {
return new BlankProjectBlueprint(new File(workDirectory(), projectName()), packageName(), projectName());
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.Project;
import rife.bld.blueprints.BaseProjectBlueprint;
import rife.bld.blueprints.LibProjectBlueprint;
import java.io.File;
/**
* Creates a new lib project structure.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.6
*/
public class CreateLibOperation extends AbstractCreateOperation<CreateLibOperation, Project> {
public CreateLibOperation() {
super("bld.lib.");
}
protected Project createProjectBlueprint() {
return new LibProjectBlueprint(new File(workDirectory(), projectName()), packageName(), projectName());
}
protected String projectMainClassName(String projectClassName) {
return projectClassName + "Lib";
}
protected boolean createIdeaRunMain() {
return false;
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.blueprints.Rife2ProjectBlueprint;
import rife.bld.dependencies.*;
import rife.template.TemplateFactory;
import rife.tools.FileUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.io.IOException;
/**
* Creates a new RIFE2 project structure.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class CreateRife2Operation extends AbstractCreateOperation<CreateRife2Operation, Rife2ProjectBlueprint> {
File srcMainWebappCssDirectory_;
File srcMainWebappWebInfDirectory_;
public CreateRife2Operation() {
super("bld.rife2_hello.");
}
protected Rife2ProjectBlueprint createProjectBlueprint() {
return new Rife2ProjectBlueprint(new File(workDirectory(), projectName()), packageName(), projectName());
}
@Override
protected void executeConfigure() {
super.executeConfigure();
projectMainName_ = projectClassName_ + "Site";
projectMainUberName_ = projectMainName_ + "Uber";
srcMainWebappCssDirectory_ = new File(project_.srcMainWebappDirectory(), "css");
srcMainWebappWebInfDirectory_ = new File(project_.srcMainWebappDirectory(), "WEB-INF");
}
@Override
protected void executeCreateProjectStructure() {
super.executeCreateProjectStructure();
srcMainWebappCssDirectory_.mkdirs();
srcMainWebappWebInfDirectory_.mkdirs();
}
@Override
protected void executePopulateProjectStructure()
throws FileUtilsErrorException, IOException {
super.executePopulateProjectStructure();
// project site uber
var main_uber_template = TemplateFactory.TXT.get(templateBase_ + "project_main_uber");
main_uber_template.setValue("package", project_.pkg());
main_uber_template.setValue("projectMain", projectMainName_);
main_uber_template.setValue("projectMainUber", projectMainUberName_);
var project_main_uber_file = new File(mainPackageDirectory_, projectMainUberName_ + ".java");
FileUtils.writeString(main_uber_template.getContent(), project_main_uber_file);
// project template
var template_template = TemplateFactory.HTML.get(templateBase_ + "project_template");
template_template.setValue("project", projectClassName_);
var project_template_file = new File(project_.srcMainResourcesTemplatesDirectory(), "hello.html");
FileUtils.writeString(template_template.getContent(), project_template_file);
// project css
FileUtils.writeString(
TemplateFactory.TXT.get(templateBase_ + "project_style").getContent(),
new File(srcMainWebappCssDirectory_, "style.css"));
// project web.xml
var web_xml_template = TemplateFactory.XML.get(templateBase_ + "project_web");
web_xml_template.setValue("package", project_.pkg());
web_xml_template.setValue("projectMain", projectMainName_);
var project_web_xml_file = new File(srcMainWebappWebInfDirectory_, "web.xml");
FileUtils.writeString(web_xml_template.getContent(), project_web_xml_file);
}
@Override
protected void executePopulateIdeaProject()
throws FileUtilsErrorException {
super.executePopulateIdeaProject();
FileUtils.writeString(
TemplateFactory.XML.get(templateBase_ + "idea.libraries.standalone").getContent(),
new File(ideaLibrariesDirectory_, "standalone.xml"));
}
}

View file

@ -0,0 +1,181 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.dependencies.*;
import java.util.ArrayList;
import java.util.List;
import static rife.bld.dependencies.Scope.compile;
import static rife.bld.dependencies.Scope.runtime;
/**
* Transitively generates a hierarchical tree of dependencies.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.21
*/
public class DependencyTreeOperation extends AbstractOperation<DependencyTreeOperation> {
private ArtifactRetriever retriever_ = null;
private final List<Repository> repositories_ = new ArrayList<>();
private final DependencyScopes dependencies_ = new DependencyScopes();
private final StringBuilder dependencyTree_ = new StringBuilder();
/**
* Performs the dependency tree operation.
*
* @since 1.5.21
*/
public void execute() {
var compile_tree = executeGenerateCompileDependencies();
var runtime_tree = executeGenerateRuntimeDependencies();
dependencyTree_.setLength(0);
dependencyTree_.append(compile_tree);
dependencyTree_.append(System.lineSeparator());
dependencyTree_.append(runtime_tree);
dependencyTree_.append(System.lineSeparator());
System.out.println(compile_tree);
System.out.println(runtime_tree);
}
/**
* Part of the {@link #execute} operation, generates the tree for the compile scope.
*
* @since 1.5.21
*/
protected String executeGenerateCompileDependencies() {
var compile_tree = dependencies().scope(compile).generateTransitiveDependencyTree(artifactRetriever(), repositories(), compile);
if (compile_tree.isEmpty()) {
compile_tree = "no dependencies" + System.lineSeparator();
}
return "compile:" + System.lineSeparator() + compile_tree;
}
/**
* Part of the {@link #execute} operation, generates the tree for the runtime scope.
*
* @since 1.5.21
*/
protected String executeGenerateRuntimeDependencies() {
var runtime_tree = dependencies().scope(runtime).generateTransitiveDependencyTree(artifactRetriever(), repositories(), compile, runtime);
if (runtime_tree.isEmpty()) {
runtime_tree = "no dependencies" + System.lineSeparator();
}
return "runtime:" + System.lineSeparator() + runtime_tree;
}
/**
* Configures a dependency tree operation from a {@link BaseProject}.
*
* @param project the project to configure the operation from
* @since 1.5.21
*/
public DependencyTreeOperation fromProject(BaseProject project) {
return artifactRetriever(project.artifactRetriever())
.repositories(project.repositories())
.dependencies(project.dependencies());
}
/**
* Provides repositories to resolve the dependencies against.
*
* @param repositories repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5.21
*/
public DependencyTreeOperation repositories(Repository... repositories) {
repositories_.addAll(List.of(repositories));
return this;
}
/**
* Provides a list of repositories to resolve the dependencies against.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param repositories a list of repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5.21
*/
public DependencyTreeOperation repositories(List<Repository> repositories) {
repositories_.addAll(repositories);
return this;
}
/**
* Provides scoped dependencies to generate a tree for.
*
* @param dependencies the dependencies that will be resolved for tree generation
* @return this operation instance
* @since 1.5.21
*/
public DependencyTreeOperation dependencies(DependencyScopes dependencies) {
dependencies_.include(dependencies);
return this;
}
/**
* Provides the artifact retriever to use.
*
* @param retriever the artifact retriever
* @return this operation instance
* @since 1.5.21
*/
public DependencyTreeOperation artifactRetriever(ArtifactRetriever retriever) {
retriever_ = retriever;
return this;
}
/**
* Retrieves the repositories in which the dependencies will be resolved.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the repositories used for dependency resolution
* @since 1.5.21
*/
public List<Repository> repositories() {
return repositories_;
}
/**
* Retrieves the scoped dependencies that will be used for tree generation.
* <p>
* This is a modifiable structure that can be retrieved and changed.
*
* @return the scoped dependencies
* @since 1.5.21
*/
public DependencyScopes dependencies() {
return dependencies_;
}
/**
* Returns the artifact retriever that is used.
*
* @return the artifact retriever
* @since 1.5.21
*/
public ArtifactRetriever artifactRetriever() {
if (retriever_ == null) {
return ArtifactRetriever.instance();
}
return retriever_;
}
/**
* Returns the last generated dependency tree.
*
* @return the last generated dependency tree
* @since 1.5.21
*/
public String dependencyTree() {
return dependencyTree_.toString();
}
}

View file

@ -0,0 +1,355 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.dependencies.*;
import java.io.File;
import java.util.*;
import static rife.bld.dependencies.Dependency.CLASSIFIER_JAVADOC;
import static rife.bld.dependencies.Dependency.CLASSIFIER_SOURCES;
/**
* Transitively downloads all the artifacts for dependencies into
* directories that are separated out by scope.
* <p>
* If a directory is not provided, no download will occur for that
* dependency scope.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class DownloadOperation extends AbstractOperation<DownloadOperation> {
private ArtifactRetriever retriever_ = null;
private final List<Repository> repositories_ = new ArrayList<>();
private final DependencyScopes dependencies_ = new DependencyScopes();
private File libCompileDirectory_;
private File libRuntimeDirectory_;
private File libStandaloneDirectory_;
private File libTestDirectory_;
private boolean downloadSources_ = false;
private boolean downloadJavadoc_ = false;
/**
* Performs the download operation.
*
* @since 1.5
*/
public void execute() {
executeDownloadCompileDependencies();
executeDownloadRuntimeDependencies();
executeDownloadStandaloneDependencies();
executeDownloadTestDependencies();
if (!silent()) {
System.out.println("Downloading finished successfully.");
}
}
/**
* Part of the {@link #execute} operation, download the {@code compile} scope artifacts.
*
* @since 1.5
*/
protected void executeDownloadCompileDependencies() {
executeDownloadDependencies(libCompileDirectory(), dependencies().resolveCompileDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, download the {@code runtime} scope artifacts.
*
* @since 1.5
*/
protected void executeDownloadRuntimeDependencies() {
executeDownloadDependencies(libRuntimeDirectory(), dependencies().resolveRuntimeDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, download the {@code standalone} scope artifacts.
*
* @since 1.5
*/
protected void executeDownloadStandaloneDependencies() {
executeDownloadDependencies(libStandaloneDirectory(), dependencies().resolveStandaloneDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, download the {@code test} scope artifacts.
*
* @since 1.5
*/
protected void executeDownloadTestDependencies() {
executeDownloadDependencies(libTestDirectory(), dependencies().resolveTestDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, download the artifacts for a particular dependency scope.
*
* @param destinationDirectory the directory in which the artifacts should be downloaded
* @param dependencies the dependencies to download
* @since 1.6
*/
protected void executeDownloadDependencies(File destinationDirectory, DependencySet dependencies) {
if (destinationDirectory == null) {
return;
}
destinationDirectory.mkdirs();
var additional_classifiers = new String[0];
if (downloadSources_ || downloadJavadoc_) {
var classifiers = new ArrayList<String>();
if (downloadSources_) classifiers.add(CLASSIFIER_SOURCES);
if (downloadJavadoc_) classifiers.add(CLASSIFIER_JAVADOC);
additional_classifiers = classifiers.toArray(new String[0]);
}
dependencies.transferIntoDirectory(artifactRetriever(), repositories(), destinationDirectory, additional_classifiers);
}
/**
* Configures a compile operation from a {@link BaseProject}.
*
* @param project the project to configure the compile operation from
* @since 1.5
*/
public DownloadOperation fromProject(BaseProject project) {
return artifactRetriever(project.artifactRetriever())
.repositories(project.repositories())
.dependencies(project.dependencies())
.libCompileDirectory(project.libCompileDirectory())
.libRuntimeDirectory(project.libRuntimeDirectory())
.libStandaloneDirectory(project.libStandaloneDirectory())
.libTestDirectory(project.libTestDirectory())
.downloadSources(project.downloadSources())
.downloadJavadoc(project.downloadJavadoc());
}
/**
* Provides repositories to resolve the dependencies against.
*
* @param repositories repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5.18
*/
public DownloadOperation repositories(Repository... repositories) {
repositories_.addAll(List.of(repositories));
return this;
}
/**
* Provides a list of repositories to resolve the dependencies against.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param repositories a list of repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5
*/
public DownloadOperation repositories(List<Repository> repositories) {
repositories_.addAll(repositories);
return this;
}
/**
* Provides scoped dependencies for artifact download.
*
* @param dependencies the dependencies that will be resolved for artifact download
* @return this operation instance
* @since 1.5
*/
public DownloadOperation dependencies(DependencyScopes dependencies) {
dependencies_.include(dependencies);
return this;
}
/**
* Provides the {@code compile} scope download directory.
*
* @param directory the directory to download the {@code compile} scope artifacts into
* @return this operation instance
* @since 1.5
*/
public DownloadOperation libCompileDirectory(File directory) {
libCompileDirectory_ = directory;
return this;
}
/**
* Provides the {@code runtime} scope download directory.
*
* @param directory the directory to download the {@code runtime} scope artifacts into
* @return this operation instance
* @since 1.5
*/
public DownloadOperation libRuntimeDirectory(File directory) {
libRuntimeDirectory_ = directory;
return this;
}
/**
* Provides the {@code standalone} scope download directory.
*
* @param directory the directory to download the {@code standalone} scope artifacts into
* @return this operation instance
* @since 1.5
*/
public DownloadOperation libStandaloneDirectory(File directory) {
libStandaloneDirectory_ = directory;
return this;
}
/**
* Provides the {@code test} scope download directory.
*
* @param directory the directory to download the {@code test} scope artifacts into
* @return this operation instance
* @since 1.5
*/
public DownloadOperation libTestDirectory(File directory) {
libTestDirectory_ = directory;
return this;
}
/**
* Indicates whether the sources classifier should also be downloaded.
*
* @param flag {@code true} if the sources classifier should be downloaded; or
* {@code false} otherwise
* @return this operation instance
* @since 1.5.6
*/
public DownloadOperation downloadSources(boolean flag) {
downloadSources_ = flag;
return this;
}
/**
* Indicates whether the javadoc classifier should also be downloaded.
*
* @param flag {@code true} if the javadoc classifier should be downloaded; or
* {@code false} otherwise
* @return this operation instance
* @since 1.5.6
*/
public DownloadOperation downloadJavadoc(boolean flag) {
downloadJavadoc_ = flag;
return this;
}
/**
* Provides the artifact retriever to use.
*
* @param retriever the artifact retriever
* @return this operation instance
* @since 1.5.18
*/
public DownloadOperation artifactRetriever(ArtifactRetriever retriever) {
retriever_ = retriever;
return this;
}
/**
* Retrieves the repositories in which the dependencies will be resolved.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the repositories used for dependency resolution
* @since 1.5
*/
public List<Repository> repositories() {
return repositories_;
}
/**
* Retrieves the scoped dependencies that will be used for artifact download.
* <p>
* This is a modifiable structure that can be retrieved and changed.
*
* @return the scoped dependencies
* @since 1.5
*/
public DependencyScopes dependencies() {
return dependencies_;
}
/**
* Retrieves the {@code compile} scope download directory.
*
* @return the {@code compile} scope download directory
* @since 1.5
*/
public File libCompileDirectory() {
return libCompileDirectory_;
}
/**
* Retrieves the {@code runtime} scope download directory.
*
* @return the {@code runtime} scope download directory
* @since 1.5
*/
public File libRuntimeDirectory() {
return libRuntimeDirectory_;
}
/**
* Retrieves the {@code standalone} scope download directory.
*
* @return the {@code standalone} scope download directory
* @since 1.5
*/
public File libStandaloneDirectory() {
return libStandaloneDirectory_;
}
/**
* Retrieves the {@code test} scope download directory.
*
* @return the {@code test} scope download directory
* @since 1.5
*/
public File libTestDirectory() {
return libTestDirectory_;
}
/**
* Retrieves whether the sources classifier should also be downloaded.
*
* @return {@code true} if the sources classifier should be downloaded; or
* {@code false} otherwise
* @since 1.5.6
*/
public boolean downloadSources() {
return downloadSources_;
}
/**
* Retrieves whether the javadoc classifier should also be downloaded.
*
* @return {@code true} if the sources classifier should be downloaded; or
* {@code false} otherwise
* @since 1.5.6
*/
public boolean downloadJavadoc() {
return downloadJavadoc_;
}
/**
* Returns the artifact retriever that is used.
*
* @return the artifact retriever
* @since 1.5.18
*/
public ArtifactRetriever artifactRetriever() {
if (retriever_ == null) {
return ArtifactRetriever.instance();
}
return retriever_;
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BldVersion;
import rife.bld.BuildExecutor;
import java.util.List;
import static java.util.Comparator.comparingInt;
/**
* Provides help about the build system commands.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class HelpOperation {
private final BuildExecutor executor_;
private final List<String> arguments_;
/**
* Creates a new help operation.
*
* @param executor the build executor that commands are running into
* @param arguments the arguments that were provided to the build executor
* @since 1.5
*/
public HelpOperation(BuildExecutor executor, List<String> arguments) {
executor_ = executor;
arguments_ = arguments;
}
/**
* Performs the help operation.
*
* @since 1.5
*/
public void execute() {
var topic = "";
if (!arguments_.isEmpty()) {
topic = arguments_.remove(0);
}
System.err.println("Welcome to bld " + BldVersion.getVersion() + ".");
System.err.println();
boolean print_full_help = true;
try {
var commands = executor_.buildCommands();
if (commands.containsKey(topic)) {
var command = commands.get(topic);
var help = command.getHelp().getDescription(topic);
if (!help.isEmpty()) {
System.err.println(help);
print_full_help = false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (print_full_help) {
executePrintOverviewHelp();
}
}
/**
* Part of the {@link #execute} operation, prints the help overview
* with summaries of all build commands.
*
* @since 1.5
*/
public void executePrintOverviewHelp() {
var commands = executor_.buildCommands();
System.err.println("""
The RIFE2 CLI provides its features through a series of commands that
perform specific tasks. The help command provides more information about
the other commands.
Usage : help [command]
The following commands are supported.
""");
var command_length = commands.keySet().stream().max(comparingInt(String::length)).get().length() + 2;
for (var command : commands.entrySet()) {
System.err.print(" ");
System.err.printf("%-" + command_length + "s", command.getKey());
var build_help = command.getValue().getHelp();
System.err.print(build_help.getSummary());
System.err.println();
}
System.err.println("""
-?, -h, --help Shows this help message
-D<name>=<value> Set a JVM system property
-s, --stacktrace Print out the stacktrace for exceptions
""");
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
/**
* Tests a Java application with JUnit.
* <p>
* If no JUnit options are specified, the {@link JUnitOptions#defaultOptions()}
* are used. To tweak the default options, manually add them with this method
* and use the other desired options.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.20
*/
public class JUnitOperation extends TestOperation<JUnitOperation, JUnitOptions> {
public static final String DEFAULT_TEST_TOOL_JUNIT5 = "org.junit.platform.console.ConsoleLauncher";
@Override
protected JUnitOptions createTestToolOptions() {
return new JUnitOptions();
}
@Override
public JUnitOperation fromProject(BaseProject project) {
super.fromProject(project);
// use the default JUnit 5 console launcher as the test tool
if (mainClass() == null) {
mainClass(DEFAULT_TEST_TOOL_JUNIT5);
}
// add the default JUnit options if none were specified
if (testToolOptions().isEmpty() && mainClass().equals(DEFAULT_TEST_TOOL_JUNIT5)) {
testToolOptions().defaultOptions();
}
// evaluate the next arguments and pass them to the JUnit console launcher
// if they meet the required conditions
var arguments = project.arguments();
while (!arguments.isEmpty()) {
var argument = arguments.get(0);
if (argument.startsWith("-")) {
arguments.remove(0);
if (argument.equals("--junit-help")) {
testToolOptions().add("--help");
} else if (argument.equals("--junit-clear")) {
testToolOptions().clear();
} else {
testToolOptions().add(argument);
// check whether this option could have the need for an additional argument
if (argument.length() == 2 && !arguments.isEmpty()) {
switch (argument.charAt(1)) {
// these are options in the form of -x where the next argument is separated
// by a space and should also be passed on to the JUnit console launcher
case 'f', 'd', 'o', 'p', 'c', 'm', 'r', 'i', 'n', 'N', 't', 'T', 'e', 'E' ->
testToolOptions().add(arguments.remove(0));
}
}
}
} else {
break;
}
}
return this;
}
}

View file

@ -0,0 +1,508 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
/**
* Options for JUnit 5.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.20
*/
public class JUnitOptions extends ArrayList<String> {
public enum Details {
NONE, SUMMARY, FLAT, TREE, VERBOSE
}
public enum Theme {
ASCII, UNICODE
}
private void removeMutuallyExclusiveOptions(String element) {
if (element.startsWith("--scan-classpath")) {
removeIf(s -> s.startsWith("--select-"));
} else if (element.startsWith("--select-")) {
removeIf(s -> s.startsWith("--scan-classpath"));
}
switch (element) {
// these are shorthand options for the longer --select-* options
case "-u", "-f", "-d", "-o", "-p", "-c", "-m", "-r", "-i" ->
removeIf(s -> s.startsWith("--scan-classpath"));
}
}
@Override
public boolean addAll(Collection<? extends String> c) {
var result = super.addAll(c);
if (result) {
for (var element : c) {
removeMutuallyExclusiveOptions(element);
}
}
return result;
}
@Override
public boolean addAll(int index, Collection<? extends String> c) {
var result = super.addAll(index, c);
if (result) {
for (var element : c) {
removeMutuallyExclusiveOptions(element);
}
}
return result;
}
@Override
public String set(int index, String element) {
var result = super.set(index, element);
removeMutuallyExclusiveOptions(element);
return result;
}
@Override
public boolean add(String s) {
var result = super.add(s);
removeMutuallyExclusiveOptions(s);
return result;
}
@Override
public void add(int index, String element) {
super.add(index, element);
removeMutuallyExclusiveOptions(element);
}
/**
* Configures the default options that RIFE2 uses when no
* options have been explicitly set.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions defaultOptions() {
config("junit.jupiter.testclass.order.default", "org.junit.jupiter.api.ClassOrderer$ClassName");
details(Details.VERBOSE);
scanClassPath();
disableBanner();
disableAnsiColors();
excludeEngine("junit-platform-suite");
excludeEngine("junit-vintage");
failIfNoTests();
return this;
}
/**
* Scan all directories on the classpath.
* <p>
* Only directories on the system classpath as well as additional classpath
* entries supplied via -cp (directories and JAR files) are scanned.
* <p>
* Removes all the {@code select} options since they are mutually exclusive.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions scanClassPath() {
add("--scan-classpath");
return this;
}
/**
* Scan an explicit classpath root.
* <p>
* Explicit classpath roots that are not on the classpath will be silently
* ignored.
* <p>
* Removes all the {@code select} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions scanClassPath(String path) {
add("--scan-classpath=" + path);
return this;
}
/**
* EXPERIMENTAL: Scan all resolved modules for test discovery
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions scanModules() {
add("--scan-modules");
return this;
}
/**
* Select a URI for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectUri(String uri) {
add("--select-uri=" + uri);
return this;
}
/**
* Select a file for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectFile(File file) {
add("--select-file=" + file);
return this;
}
/**
* Select a directory for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectDirectory(File file) {
add("--select-directory=" + file);
return this;
}
/**
* EXPERIMENTAL: Select single module for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectModule(String name) {
add("--select-module=" + name);
return this;
}
/**
* Select a package for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectPackage(String name) {
add("--select-package=" + name);
return this;
}
/**
* Select a class for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectClass(String name) {
add("--select-class=" + name);
return this;
}
/**
* Select a method for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectMethod(String name) {
add("--select-method=" + name);
return this;
}
/**
* Select a classpath resource for test discovery.
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectResource(String resource) {
add("--select-resource=" + resource);
return this;
}
/**
* Select iterations for test discovery of format {@code TYPE:VALUE[INDEX(..INDEX)?(,INDEX(..INDEX)?)*]}
* (e.g. method:com.acme.Foo#m()[1..2]).
* <p>
* Removes all the {@code scanClasspath} options since they are mutually exclusive.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions selectIteration(String iteration) {
add("--select-iteration=" + iteration);
return this;
}
/**
* Provide a regular expression to include only classes whose fully
* qualified names match. To avoid loading classes unnecessarily,
* the default pattern only includes class names that begin with
* "Test" or end with "Test" or "Tests". When this option is
* repeated, all patterns will be combined using OR semantics.
* Default: ^(Test.*|.+[.$]Test.*|.*Tests?)$
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions includeClassname(String regexPattern) {
add("--include-classname=" + regexPattern);
return this;
}
/**
* Provide a regular expression to exclude those classes whose fully
* qualified names match. When this option is repeated, all
* patterns will be combined using OR semantics.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions excludeClassname(String regexPattern) {
add("--exclude-classname=" + regexPattern);
return this;
}
/**
* Provide a package to be included in the test run. This option can
* be repeated.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions includePackage(String name) {
add("--include-package=" + name);
return this;
}
/**
* Provide a package to be excluded from the test run. This option
* can be repeated.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions excludePackage(String name) {
add("--exclude-package=" + name);
return this;
}
/**
* Provide a tag or tag expression to include only tests whose tags
* match. When this option is repeated, all patterns will be
* combined using OR semantics.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions includeTag(String tag) {
add("--include-tag=" + tag);
return this;
}
/**
* Provide a tag or tag expression to exclude those tests whose tags
* match. When this option is repeated, all patterns will be
* combined using OR semantics.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions excludeTag(String tag) {
add("--exclude-tag=" + tag);
return this;
}
/**
* Provide the ID of an engine to be included in the test run.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions includeEngine(String id) {
add("--include-engine=" + id);
return this;
}
/**
* Provide the ID of an engine to be excluded in the test run.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions excludeEngine(String id) {
add("--exclude-engine=" + id);
return this;
}
/**
* Set a configuration parameter for test discovery and execution.
* <p>
* This option can be repeated.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions config(String key, String value) {
add("--config=" + key + "=" + value);
return this;
}
/**
* Fail and return exit status code 2 if no tests are found.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions failIfNoTests() {
add("--fail-if-no-tests");
return this;
}
/**
* Enable report output into a specified local directory (will be
* created if it does not exist).
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions reportsDir(File dir) {
add("--reports-dir=" + dir);
return this;
}
/**
* Disable ANSI colors in output (not supported by all terminals).
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions disableAnsiColors() {
add("--disable-ansi-colors");
return this;
}
/**
* Specify a path to a properties file to customize ANSI style of
* output (not supported by all terminals).
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions colorPalette(File file) {
add("--color-palette=" + file);
return this;
}
/**
* Style test output using only text attributes, no color (not
* supported by all terminals).
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions singleColor() {
add("--single-color");
return this;
}
/**
* Disable print out of the welcome message.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions disableBanner() {
add("--disable-banner");
return this;
}
/**
* Select an output details mode for when tests are executed.
* <p>
* If 'none' is selected, then only the summary and test failures are shown.
* <p>
* Default: {@link Details#TREE}.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions details(Details details) {
add("--details=" + details.name().toLowerCase());
return this;
}
/**
* Select an output details tree theme for when tests are executed.
* <p>
* Default is detected based on default character encoding.
*
* @return this list of options
* @since 1.5.20
*/
public JUnitOptions detailsTheme(Theme theme) {
add("--details-theme=" + theme.name().toLowerCase());
return this;
}
}

View file

@ -0,0 +1,418 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.NamedFile;
import rife.bld.Project;
import rife.tools.FileUtils;
import rife.tools.StringUtils;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.regex.Pattern;
/**
* Creates a jar archive of the provided sources and directories.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class JarOperation extends AbstractOperation<JarOperation> {
private final Map<Attributes.Name, Object> manifestAttributes_ = new HashMap<>();
private final List<File> sourceDirectories_ = new ArrayList<>();
private final List<NamedFile> sourceFiles_ = new ArrayList<>();
private File destinationDirectory_;
private String destinationFileName_;
private final List<Pattern> included_ = new ArrayList<>();
private final List<Pattern> excluded_ = new ArrayList<>();
private final byte[] buffer_ = new byte[1024];
/**
* Performs the jar operation.
*
* @throws IOException when an exception occurred during the jar creation process
* @since 1.5
*/
public void execute()
throws IOException {
executeCreateDestinationDirectory();
executeCreateJarArchive();
if (!silent()) {
System.out.println("The jar archive was created at '" + destinationFile() + "'");
}
}
/**
* Part of the {@link #execute} operation, create the destination directory.
*
* @since 1.5
*/
protected void executeCreateDestinationDirectory() {
destinationDirectory().mkdirs();
}
/**
* Part of the {@link #execute} operation, create the jar archive.
*
* @since 1.5
*/
protected void executeCreateJarArchive()
throws IOException {
var out_file = new File(destinationDirectory(), destinationFileName());
try (var jar = new JarOutputStream(new FileOutputStream(out_file), executeCreateManifest())) {
for (var source_dir : sourceDirectories()) {
for (var file_name : FileUtils.getFileList(source_dir)) {
var file = new File(source_dir, file_name);
if (StringUtils.filter(file.getAbsolutePath(), included(), excluded(), false)) {
executeAddFileToJar(jar, new NamedFile(file_name, file));
}
}
}
for (var source_file : sourceFiles()) {
if (StringUtils.filter(source_file.file().getAbsolutePath(), included(), excluded(), false)) {
executeAddFileToJar(jar, source_file);
}
}
jar.flush();
}
}
/**
* Part of the {@link #execute} operation, create the manifest for the jar archive.
*
* @since 1.5
*/
protected Manifest executeCreateManifest() {
var manifest = new Manifest();
var attributes = manifest.getMainAttributes();
for (var entry : manifestAttributes().entrySet()) {
// don't use putAll since Attributes does an instanceof check
// on the map being passed in, causing it to fail if it's not
// and instance of Attributes
attributes.put(entry.getKey(), entry.getValue());
// ^^^ READ above, don't use putAll
}
return manifest;
}
/**
* Part of the {@link #execute} operation, add a single file to the jar archive.
*
* @since 1.5
*/
protected void executeAddFileToJar(JarOutputStream jar, NamedFile file)
throws IOException {
var entry = new JarEntry(file.name().replace('\\', '/'));
entry.setTime(file.file().lastModified());
jar.putNextEntry(entry);
try (var in = new BufferedInputStream(new FileInputStream(file.file()))) {
int count;
while ((count = in.read(buffer_)) != -1) {
jar.write(buffer_, 0, count);
}
jar.closeEntry();
}
}
/**
* Configures a jar operation from a {@link BaseProject}.
*
* @param project the project to configure the jar operation from
* @since 1.5
*/
public JarOperation fromProject(BaseProject project) {
return manifestAttributes(Map.of(Attributes.Name.MANIFEST_VERSION, "1.0"))
.sourceDirectories(project.buildMainDirectory(), project.srcMainResourcesDirectory())
.destinationDirectory(project.buildDistDirectory())
.destinationFileName(project.jarFileName())
.excluded(Pattern.compile("(?:(?:^.*[/\\\\])|^)\\.DS_Store$"), Pattern.compile("^\\Q" + project.srcMainResourcesTemplatesDirectory().getAbsolutePath() + "\\E.*"));
}
/**
* Provides an attribute to put in the jar manifest.
*
* @param name the attribute name to put in the manifest
* @param value the attribute value to put in the manifest
* @return this operation instance
* @since 1.5.18
*/
public JarOperation manifestAttribute(Attributes.Name name, Object value) {
manifestAttributes_.put(name, value);
return this;
}
/**
* Provides a map of attributes to put in the jar manifest.
* <p>
* A copy will be created to allow this map to be independently modifiable.
*
* @param attributes the attributes to put in the manifest
* @return this operation instance
* @since 1.5
*/
public JarOperation manifestAttributes(Map<Attributes.Name, Object> attributes) {
manifestAttributes_.putAll(attributes);
return this;
}
/**
* Provides source directories that will be used for the jar archive creation.
*
* @param directories source directories
* @return this operation instance
* @since 1.5.18
*/
public JarOperation sourceDirectories(File... directories) {
sourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of source directories that will be used for the jar archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of source directories
* @return this operation instance
* @since 1.5
*/
public JarOperation sourceDirectories(List<File> directories) {
sourceDirectories_.addAll(directories);
return this;
}
/**
* Provides source files that will be used for the jar archive creation.
*
* @param files source files
* @return this operation instance
* @since 1.5.18
*/
public JarOperation sourceFiles(NamedFile... files) {
sourceFiles_.addAll(List.of(files));
return this;
}
/**
* Provides a list of source files that will be used for the jar archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of source files
* @return this operation instance
* @since 1.5
*/
public JarOperation sourceFiles(List<NamedFile> files) {
sourceFiles_.addAll(files);
return this;
}
/**
* Provides the destination directory in which the jar archive will be created.
*
* @param directory the jar destination directory
* @return this operation instance
* @since 1.5
*/
public JarOperation destinationDirectory(File directory) {
destinationDirectory_ = directory;
return this;
}
/**
* Provides the destination file name that will be used for the jar archive creation.
*
* @param name the jar archive destination file name
* @return this operation instance
* @since 1.5
*/
public JarOperation destinationFileName(String name) {
destinationFileName_ = name;
return this;
}
/**
* Provides regex patterns that will be found to determine which files
* will be included in the javadoc generation.
*
* @param included inclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JarOperation included(String... included) {
included_.addAll(Arrays.stream(included).map(Pattern::compile).toList());
return this;
}
/**
* Provides patterns that will be found to determine which files
* will be included in the jar archive.
*
* @param included inclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JarOperation included(Pattern... included) {
included_.addAll(List.of(included));
return this;
}
/**
* Provides a list of patterns that will be found to determine which files
* will be included in the jar archive.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param included a list of inclusion patterns
* @return this operation instance
* @since 1.5
*/
public JarOperation included(List<Pattern> included) {
included_.addAll(included);
return this;
}
/**
* Provides regex patterns that will be found to determine which files
* will be excluded from the javadoc generation.
*
* @param excluded exclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JarOperation excluded(String... excluded) {
excluded_.addAll(Arrays.stream(excluded).map(Pattern::compile).toList());
return this;
}
/**
* Provides patterns that will be found to determine which files
* will be excluded from the jar archive.
*
* @param excluded exclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JarOperation excluded(Pattern... excluded) {
excluded_.addAll(List.of(excluded));
return this;
}
/**
* Provides a list of patterns that will be found to determine which files
* will be excluded from the jar archive.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param excluded a list of exclusion patterns
* @return this operation instance
* @since 1.5
*/
public JarOperation excluded(List<Pattern> excluded) {
excluded_.addAll(excluded);
return this;
}
/**
* Retrieves the map of attributes that will be put in the jar manifest.
* <p>
* This is a modifiable map that can be retrieved and changed.
*
* @return the manifest's attributes map
* @since 1.5
*/
public Map<Attributes.Name, Object> manifestAttributes() {
return manifestAttributes_;
}
/**
* Retrieves the list of source directories that will be used for the
* jar archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the jar archive's source directories
* @since 1.5
*/
public List<File> sourceDirectories() {
return sourceDirectories_;
}
/**
* Retrieves the list of source files that will be used for the
* jar archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the jar archive's source files
* @since 1.5
*/
public List<NamedFile> sourceFiles() {
return sourceFiles_;
}
/**
* Retrieves the destination directory in which the jar archive will
* be created.
*
* @return the jar archive's destination directory
* @since 1.5
*/
public File destinationDirectory() {
return destinationDirectory_;
}
/**
* Retrieves the destination file name that will be used for the jar
* archive creation.
*
* @return the jar archive's destination file name
* @since 1.5
*/
public String destinationFileName() {
return destinationFileName_;
}
/**
* Retrieves the destination file where the jar archive will be created.
*
* @return the jar archive's destination file
* @since 1.5.18
*/
public File destinationFile() {
return new File(destinationDirectory(), destinationFileName());
}
/**
* Retrieves the list of patterns that will be evaluated to determine which files
* will be included in the jar archive.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the jar's archive's inclusion patterns
* @since 1.5
*/
public List<Pattern> included() {
return included_;
}
/**
* Retrieves the list of patterns that will be evaluated to determine which files
* will be excluded the jar archive.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the jar's archive's exclusion patterns
* @since 1.5
*/
public List<Pattern> excluded() {
return excluded_;
}
}

View file

@ -0,0 +1,308 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.tools.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Options for the standard java tool.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.18
*/
public class JavaOptions extends ArrayList<String> {
public enum Verbose {
CLASS, MODULE, GC, JNI
}
/**
* Select the "truffle" VM.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions truffle() {
add("-truffle");
return this;
}
/**
* A list of directories, each directory is a directory of modules.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions modulePath(File... modules) {
return modulePath(Arrays.asList(modules));
}
/**
* A list of directories, each directory is a directory of modules.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions modulePath(List<File> modules) {
add("--module-path");
add(StringUtils.join(modules, ":"));
return this;
}
/**
* List of directories, each directory is a directory of modules
* that replace upgradeable modules in the runtime image
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions upgradeModulePath(File... modulePath) {
return upgradeModulePath(Arrays.asList(modulePath));
}
/**
* List of directories, each directory is a directory of modules
* that replace upgradeable modules in the runtime image
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions upgradeModulePath(List<File> modulePath) {
add("--upgrade-module-path");
add(StringUtils.join(modulePath, ":"));
return this;
}
/**
* Root modules to resolve in addition to the initial module.
* The module name can also be ALL-DEFAULT, ALL-SYSTEM,
* ALL-MODULE-PATH.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions addModules(String... modules) {
return addModules(Arrays.asList(modules));
}
/**
* Root modules to resolve in addition to the initial module.
* The module name can also be ALL-DEFAULT, ALL-SYSTEM,
* ALL-MODULE-PATH.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions addModules(List<String> modules) {
add("--add-modules");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Modules that are permitted to perform restricted native operations.
* The module name can also be ALL-UNNAMED.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions enableNativeAccess(List... modules) {
return enableNativeAccess(Arrays.asList(modules));
}
/**
* Modules that are permitted to perform restricted native operations.
* The module name can also be ALL-UNNAMED.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions enableNativeAccess(List<String> modules) {
add("--enable-native-access");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Set a system property.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions property(String key, String value) {
add("-D" + key + "=" + value);
return this;
}
/**
* Enable verbose output for the given subsystem
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions verbose(Verbose verbose) {
add("-verbose:" + verbose.name().toLowerCase());
return this;
}
/**
* Show module resolution output during startup.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions showModuleResolution() {
add("--show-module-resolution");
return this;
}
/**
* Enable assertions with specified granularity, either
* package name or class name.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions enableAssertions(String name) {
add("-enableassertions:"+name);
return this;
}
/**
* Disable assertions with specified granularity, either
* package name or class name.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions disableAssertions(String name) {
add("-disableassertions:"+name);
return this;
}
/**
* Enable system assertions.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions enableSystemAssertions() {
add("-enablesystemassertions");
return this;
}
/**
* Disable system assertions.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions disableSystemAssertions() {
add("-disablesystemassertions");
return this;
}
/**
* Load native agent library.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions agentLib(String libName) {
return agentLib(libName, null);
}
/**
* Load native agent library.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions agentLib(String libName, String options) {
add("-agentlib:" + libName + (options == null ? "" : ":" + options));
return this;
}
/**
* Load native agent library by full pathname.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions agentPath(File pathName) {
return agentPath(pathName, null);
}
/**
* Load native agent library by full pathname.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions agentPath(File pathName, String options) {
add("-agentpath:" + pathName + (options == null ? "" : ":" + options));
return this;
}
/**
* Load Java programming language agent.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions javaAgent(File jarPath) {
return javaAgent(jarPath, null);
}
/**
* Load Java programming language agent.
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions javaAgent(File jarPath, String options) {
add("-javaagent:" + jarPath + (options == null ? "" : ":" + options));
return this;
}
/**
* Allow classes to depend on preview features of this release
*
* @return this list of options
* @since 1.5.18
*/
public JavaOptions enablePreview() {
add("--enable-preview");
return this;
}
/**
* Set the initial Java heap size in megabytes.
*
* @param megabytes the size
* @return this list of options
* @since 1.5.18
*/
public JavaOptions initialHeapSize(int megabytes) {
add("-Xms" + megabytes + "m");
return this;
}
/**
* Set the maximum Java heap size in megabytes.
*
* @param megabytes the size
* @return this list of options
* @since 1.5.18
*/
public JavaOptions maximumHeapSize(int megabytes) {
add("-Xmx" + megabytes + "m");
return this;
}
}

View file

@ -0,0 +1,427 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.tools.Convert;
import rife.tools.FileUtils;
import rife.tools.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Options for the standard javac tool.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.18
*/
public class JavacOptions extends ArrayList<String> {
public enum DebuggingInfo {
ALL, NONE, LINES, VAR, SOURCE
}
public enum Implicit {
NONE, CLASS
}
public enum Processing {
NONE, ONLY
}
/**
* Option to pass to annotation processors
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions annotationOption(String key, String value) {
add("-A" + key + "=" + value);
return this;
}
/**
* Root modules to resolve in addition to the initial modules,
* or all modules on the module path if a module is
* ALL-MODULE-PATH.
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions addModules(String... modules) {
return addModules(Arrays.asList(modules));
}
/**
* Root modules to resolve in addition to the initial modules,
* or all modules on the module path if a module is
* ALL-MODULE-PATH.
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions addModules(List<String> modules) {
add("--add-modules");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Specify character encoding used by source files
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions encoding(String name) {
add("-encoding");
add("name");
return this;
}
/**
* Output source locations where deprecated APIs are used
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions deprecation() {
add("-deprecation");
return this;
}
/**
* Enable preview language features. To be used in conjunction with {@link #release}.
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions enablePreview() {
add("--enable-preview");
return this;
}
/**
* Override location of endorsed standards path
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions endorsedDirs(File... dirs) {
return endorsedDirs(Arrays.asList(dirs));
}
/**
* Override location of endorsed standards path
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions endorsedDirs(List<File> dirs) {
add("-endorseddirs");
add(dirs.stream().map(File::getAbsolutePath).collect(Collectors.joining(",")));
return this;
}
/**
* Override location of installed extensions
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions extDirs(File... dirs) {
return extDirs(Arrays.asList(dirs));
}
/**
* Override location of installed extensions
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions extDirs(List<File> dirs) {
add("-extdirs");
add(dirs.stream().map(File::getAbsolutePath).collect(Collectors.joining(",")));
return this;
}
/**
* Indicates whether the Java SE release was set.
*
* @return {@code true} if the release was set; or
* {@code false} otherwise
* @since 1.5.18
*/
public boolean containsRelease() {
return contains("-release");
}
/**
* Compile for the specified Java SE release.
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions release(int version) {
add("--release");
add(Convert.toString(version));
return this;
}
/**
* Generate debugging info
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions debuggingInfo(DebuggingInfo option) {
if (option.equals(DebuggingInfo.ALL)) {
add("-g");
} else {
add("-g:" + option.name().toLowerCase());
}
return this;
}
/**
* Specify where to place generated native header files
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions nativeHeaders(File path) {
add("-h");
add(path.getAbsolutePath());
return this;
}
/**
* Specify whether or not to generate class files for implicitly referenced files
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions implicit(Implicit option) {
add("-implicit:" + option.name().toLowerCase());
return this;
}
/**
* Limit the universe of observable modules
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions limitModules(String... modules) {
return limitModules(Arrays.asList(modules));
}
/**
* Limit the universe of observable modules
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions limitModules(List<String> modules) {
add("--limit-modules");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Compile only the specified module(s), check timestamps
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions module(String... modules) {
return module(Arrays.asList(modules));
}
/**
* Compile only the specified module(s), check timestamps
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions module(List<String> modules) {
add("--module");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Specify where to find application modules
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions modulePath(File... paths) {
return modulePath(Arrays.asList(paths));
}
/**
* Specify where to find application modules
*
* @return this list of options
* @since 1.6.2
*/
public JavacOptions modulePath(List<File> paths) {
add("--module-path");
add(FileUtils.joinPaths(paths.stream().map(File::getAbsolutePath).toList()));
return this;
}
/**
* Specify where to find input source files for multiple modules
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions moduleSourcePath(File path) {
add("--module-source-path");
add(path.getAbsolutePath());
return this;
}
/**
* Specify version of modules that are being compiled
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions moduleVersion(String version) {
add("--module-version");
add(version);
return this;
}
/**
* Generate no warnings
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions noWarn() {
add("-nowarn");
return this;
}
/**
* Generate metadata for reflection on method parameters
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions parameters() {
add("-parameters");
return this;
}
/**
* Control whether annotation processing and/or compilation is done.
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions process(Processing option) {
add("-proc:" + option.name().toLowerCase());
return this;
}
/**
* Names of the annotation processors to run; bypasses default discovery process
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions processors(String... classnames) {
return processors(Arrays.asList(classnames));
}
/**
* Names of the annotation processors to run; bypasses default discovery process
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions processors(List<String> classnames) {
add("-processor");
add(StringUtils.join(classnames, ","));
return this;
}
/**
* Specify a module path where to find annotation processors
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions processorModulePath(File path) {
add("--processor-module-path");
add(path.getAbsolutePath());
return this;
}
/**
* Specify where to find annotation processors
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions processorPath(File path) {
add("--processor-path");
add(path.getAbsolutePath());
return this;
}
/**
* Check that API used is available in the specified profile
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions profile(String profile) {
add("-profile");
add(profile);
return this;
}
/**
* Override location of system modules. Option is &lt;jdk&gt; or none.
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions system(String option) {
add("--system");
add(option);
return this;
}
/**
* Override location of upgradeable modules
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions upgradeModulePath(File path) {
add("--upgrade-module-path");
add(path.getAbsolutePath());
return this;
}
/**
* Terminate compilation if warnings occur
*
* @return this list of options
* @since 1.5.18
*/
public JavacOptions warningError() {
add("-Werror");
return this;
}
}

View file

@ -0,0 +1,436 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.Project;
import rife.bld.operations.exceptions.ExitStatusException;
import rife.tools.FileUtils;
import rife.tools.StringUtils;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* Generates javadocs for the main project sources.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.10
*/
public class JavadocOperation extends AbstractOperation<JavadocOperation> {
private File buildDirectory_;
private final List<String> classpath_ = new ArrayList<>();
private final List<File> sourceFiles_ = new ArrayList<>();
private final List<File> sourceDirectories_ = new ArrayList<>();
private final JavadocOptions javadocOptions_ = new JavadocOptions();
private final List<Diagnostic<? extends JavaFileObject>> diagnostics_ = new ArrayList<>();
private final List<Pattern> included_ = new ArrayList<>();
private final List<Pattern> excluded_ = new ArrayList<>();
/**
* Performs the compile operation.
*
* @since 1.5.10
*/
public void execute()
throws IOException, ExitStatusException {
executeCreateBuildDirectories();
executeBuildSources();
if (!diagnostics().isEmpty()) {
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
}
if (!silent()) {
System.out.println("Javadoc generated successfully.");
}
}
/**
* Part of the {@link #execute} operation, creates the build directories.
*
* @since 1.5.10
*/
protected void executeCreateBuildDirectories() {
if (buildDirectory() != null) {
buildDirectory().mkdirs();
}
}
/**
* Part of the {@link #execute} operation, builds the main sources.
*
* @since 1.5.10
*/
protected void executeBuildSources()
throws IOException {
var sources = new ArrayList<>(sourceFiles());
for (var directory : sourceDirectories()) {
sources.addAll(FileUtils.getJavaFileList(directory));
}
executeBuildSources(
classpath(),
sources,
buildDirectory());
}
/**
* Part of the {@link #execute} operation, build sources to a destination.
*
* @param classpath the classpath list used for the compilation
* @param sources the source files to compile
* @param destination the destination directory
* @since 1.5.10
*/
protected void executeBuildSources(List<String> classpath, List<File> sources, File destination)
throws IOException {
if (sources.isEmpty() || destination == null) {
return;
}
var filtered_sources = new ArrayList<File>();
for (var source : sources) {
if (StringUtils.filter(source.getAbsolutePath(), included(), excluded(), false)) {
filtered_sources.add(source);
}
}
var documentation = ToolProvider.getSystemDocumentationTool();
try (var file_manager = documentation.getStandardFileManager(null, null, null)) {
var compilation_units = file_manager.getJavaFileObjectsFromFiles(filtered_sources);
var diagnostics = new DiagnosticCollector<JavaFileObject>();
var options = new ArrayList<>(List.of("-d", destination.getAbsolutePath(), "-cp", FileUtils.joinPaths(classpath)));
options.addAll(javadocOptions());
var documentation_task = documentation.getTask(null, file_manager, diagnostics, null, options, compilation_units);
if (!documentation_task.call()) {
diagnostics_.addAll(diagnostics.getDiagnostics());
executeProcessDiagnostics(diagnostics);
}
}
}
/**
* Part of the {@link #execute} operation, processes the compilation diagnostics.
*
* @param diagnostics the diagnostics to process
* @since 1.5.10
*/
protected void executeProcessDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) {
for (var diagnostic : diagnostics.getDiagnostics()) {
System.err.print(executeFormatDiagnostic(diagnostic));
}
}
/**
* Part of the {@link #execute} operation, format a single diagnostic.
*
* @param diagnostic the diagnostic to format
* @return a string representation of the diagnostic
* @since 1.5.10
*/
protected String executeFormatDiagnostic(Diagnostic<? extends JavaFileObject> diagnostic) {
return diagnostic.toString() + System.lineSeparator();
}
/**
* Configures a javadoc operation from a {@link BaseProject}.
*
* @param project the project to configure the javadoc operation from
* @since 1.5.10
*/
public JavadocOperation fromProject(BaseProject project) {
var operation = buildDirectory(project.buildJavadocDirectory())
.classpath(project.compileMainClasspath())
.classpath(project.buildMainDirectory().getAbsolutePath())
.sourceFiles(project.mainSourceFiles());
if (project.javaRelease() != null && !javadocOptions().containsRelease()) {
javadocOptions().release(project.javaRelease());
}
return operation;
}
/**
* Provides the javadoc build destination directory.
*
* @param directory the directory to use for the javadoc build destination
* @return this operation instance
* @since 1.5.10
*/
public JavadocOperation buildDirectory(File directory) {
buildDirectory_ = directory;
return this;
}
/**
* Provides entries for the javadoc classpath.
*
* @param classpath classpath entries
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation classpath(String... classpath) {
classpath_.addAll(Arrays.asList(classpath));
return this;
}
/**
* Provides a list of entries for the javadoc classpath.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param classpath a list of classpath entries
* @return this operation instance
* @since 1.5.10
*/
public JavadocOperation classpath(List<String> classpath) {
classpath_.addAll(classpath);
return this;
}
/**
* Provides files for which documentation should be generated.
*
* @param files source files
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation sourceFiles(File... files) {
sourceFiles_.addAll(Arrays.asList(files));
return this;
}
/**
* Provides a list of files for which documentation should be generated.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of source files
* @return this operation instance
* @since 1.5.10
*/
public JavadocOperation sourceFiles(List<File> files) {
sourceFiles_.addAll(files);
return this;
}
/**
* Provides directories for which documentation should be generated.
*
* @param directories source directories
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation sourceDirectories(File... directories) {
sourceDirectories_.addAll(Arrays.asList(directories));
return this;
}
/**
* Provides a list of directories for which documentation should be generated.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of source directories
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation sourceDirectories(List<File> directories) {
sourceDirectories_.addAll(directories);
return this;
}
/**
* Provides a list of options to provide to the javadoc tool.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options the list of javadoc options
* @return this operation instance
* @since 1.5.10
*/
public JavadocOperation javadocOptions(List<String> options) {
javadocOptions_.addAll(options);
return this;
}
/**
* Provides regex patterns that will be found to determine which files
* will be included in the javadoc generation.
*
* @param included inclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation included(String... included) {
included_.addAll(Arrays.stream(included).map(Pattern::compile).toList());
return this;
}
/**
* Provides patterns that will be found to determine which files
* will be included in the javadoc generation.
*
* @param included inclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation included(Pattern... included) {
included_.addAll(Arrays.asList(included));
return this;
}
/**
* Provides a list of patterns that will be found to determine which files
* will be included in the javadoc generation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param included the list of inclusion patterns
* @return this operation instance
* @since 1.5.10
*/
public JavadocOperation included(List<Pattern> included) {
included_.addAll(included);
return this;
}
/**
* Provides regex patterns that will be found to determine which files
* will be excluded from the javadoc generation.
*
* @param excluded exclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation excluded(String... excluded) {
excluded_.addAll(Arrays.stream(excluded).map(Pattern::compile).toList());
return this;
}
/**
* Provides patterns that will be found to determine which files
* will be excluded from the javadoc generation.
*
* @param excluded exclusion patterns
* @return this operation instance
* @since 1.5.18
*/
public JavadocOperation excluded(Pattern... excluded) {
excluded_.addAll(Arrays.asList(excluded));
return this;
}
/**
* Provides a list of patterns that will be found to determine which files
* will be excluded from the javadoc generation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param excluded the list of exclusion patterns
* @return this operation instance
* @since 1.5.10
*/
public JavadocOperation excluded(List<Pattern> excluded) {
excluded_.addAll(excluded);
return this;
}
/**
* Retrieves the build destination directory.
*
* @return the javadoc build destination
* @since 1.5.10
*/
public File buildDirectory() {
return buildDirectory_;
}
/**
* Retrieves the list of entries for the javadoc classpath.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the javadoc classpath list
* @since 1.5.10
*/
public List<String> classpath() {
return classpath_;
}
/**
* Retrieves the list of files for which documentation should be generation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of files documentation is generated for
* @since 1.5.10
*/
public List<File> sourceFiles() {
return sourceFiles_;
}
/**
* Retrieves the list of directories for which documentation should be generated.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of source directories documentation is generated for
* @since 1.5.18
*/
public List<File> sourceDirectories() {
return sourceDirectories_;
}
/**
* Retrieves the list of options for the javadoc tool.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of javadoc options
* @since 1.5.10
*/
public JavadocOptions javadocOptions() {
return javadocOptions_;
}
/**
* Retrieves the list of diagnostics resulting from the compilation.
*
* @return the list of compilation diagnostics
* @since 1.5.10
*/
public List<Diagnostic<? extends JavaFileObject>> diagnostics() {
return diagnostics_;
}
/**
* Retrieves the list of patterns that will be evaluated to determine which files
* will be included in the javadoc generation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the javadoc inclusion patterns
* @since 1.5.10
*/
public List<Pattern> included() {
return included_;
}
/**
* Retrieves the list of patterns that will be evaluated to determine which files
* will be excluded the javadoc generation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the javadoc exclusion patterns
* @since 1.5.10
*/
public List<Pattern> excluded() {
return excluded_;
}
}

View file

@ -0,0 +1,959 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.tools.Convert;
import rife.tools.FileUtils;
import rife.tools.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Options for the standard javadoc tool.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.12
*/
public class JavadocOptions extends ArrayList<String> {
public enum Level {
PUBLIC, PROTECTED, PACKAGE, PRIVATE
}
public enum ModuleContent {
API, ALL
}
public enum ModulePackages {
EXPORTED, ALL
}
public enum Override {
DETAILS, SUMMARY
}
public enum DocLinkOption {
ALL("all"), NONE("none"),
ACCESSIBILITY("accessibility"), HTML("html"), MISSING("missing"),
REFERENCE("reference"), SYNTAX("syntax"),
NO_ACCESSIBILITY("-accessibility"), NO_HTML("-html"), NO_MISSING("-missing"),
NO_REFERENCE("-reference"), NO_SYNTAX("-syntax");
private final String option_;
DocLinkOption(String option) {
option_ = option;
}
private String getOption() {
return option_;
}
}
/**
* Root modules to resolve in addition to the initial modules,
* or all modules on the module path if a module is
* ALL-MODULE-PATH.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions addModules(String... modules) {
return addModules(Arrays.asList(modules));
}
/**
* Root modules to resolve in addition to the initial modules,
* or all modules on the module path if a module is
* ALL-MODULE-PATH.
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions addModules(List<String> modules) {
add("--add-modules");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Compute first sentence with BreakIterator.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions breakIterator() {
add("-breakiterator");
return this;
}
/**
* Generate output via alternate doclet
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions doclet(String className) {
add("-doclet");
add("className");
return this;
}
/**
* Specify where to find doclet class files
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions docletPath(String path) {
add("-docletpath");
add("path");
return this;
}
/**
* Enable preview language features. To be used in conjunction with {@link #release}.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions enablePreview() {
add("--enable-preview");
return this;
}
/**
* Source file encoding name
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions encoding(String name) {
add("-encoding");
add("name");
return this;
}
/**
* Specify a list of packages to exclude
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions excludePackages(String... name) {
return excludePackages(Arrays.asList(name));
}
/**
* Specify a list of packages to exclude
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions excludePackages(List<String> name) {
add("-exclude");
add(StringUtils.join(name, ","));
return this;
}
/**
* Override location of installed extensions
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions extDirs(File... dirs) {
return extDirs(Arrays.asList(dirs));
}
/**
* Override location of installed extensions
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions extDirs(List<File> dirs) {
add("-extdirs");
add(dirs.stream().map(File::getAbsolutePath).collect(Collectors.joining(",")));
return this;
}
/**
* Limit the universe of observable modules
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions limitModules(String... modules) {
return limitModules(Arrays.asList(modules));
}
/**
* Limit the universe of observable modules
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions limitModules(List<String> modules) {
add("--limit-modules");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Locale to be used, e.g. en_US or en_US_WIN
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions locale(String name) {
add("-locale");
add("name");
return this;
}
/**
* Document the specified module(s)
*
* @return this list of options
* @since 1.6.3
*/
public JavadocOptions module(String... modules) {
return module(Arrays.asList(modules));
}
/**
* Document the specified module(s)
*
* @return this list of options
* @since 1.6.3
*/
public JavadocOptions module(List<String> modules) {
add("--module");
add(StringUtils.join(modules, ","));
return this;
}
/**
* Specify where to find application modules
*
* @return this list of options
* @since 1.6.3
*/
public JavadocOptions modulePath(File... paths) {
return modulePath(Arrays.asList(paths));
}
/**
* Specify where to find application modules
*
* @return this list of options
* @since 1.6.3
*/
public JavadocOptions modulePath(List<File> paths) {
add("--module-path");
add(FileUtils.joinPaths(paths.stream().map(File::getAbsolutePath).toList()));
return this;
}
/**
* Specify where to find input source files for multiple modules
*
* @return this list of options
* @since 1.6.3
*/
public JavadocOptions moduleSourcePath(File path) {
add("--module-source-path");
add(path.getAbsolutePath());
return this;
}
/**
* Show package/protected/public types and members.
* <p>
* For named modules, show all packages and all module details.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showPackage() {
add("-package");
return this;
}
/**
* Show all types and members.
* <p>
* For named modules, show all packages and all module details.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showPrivate() {
add("-private");
return this;
}
/**
* Show protected/public types and members (default).
* <p>
* For named modules, show exported packages and the module's API.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showProtected() {
add("-protected");
return this;
}
/**
* Show only public types and members.
* <p>
* For named modules, show exported packages and the module's API.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showPublic() {
add("-public");
return this;
}
/**
* Do not display status messages
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions quiet() {
add("-quiet");
return this;
}
/**
* Indicates whether the Java SE release was set.
*
* @return {@code true} if the release was set; or
* {@code false} otherwise
* @since 1.5.18
*/
public boolean containsRelease() {
return contains("-release");
}
/**
* Provide source compatibility with specified release
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions release(int version) {
add("--release");
add(Convert.toString(version));
return this;
}
/**
* Specifies which members (fields, methods, etc.) will be
* documented, where value can be one of "public", "protected",
* "package" or "private". The default is "protected", which will
* show public and protected members, "public" will show only
* public members, "package" will show public, protected and
* package members and "private" will show all members.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showMembers(Level option) {
add("--show-members");
add(option.name().toLowerCase());
return this;
}
/**
* Specifies the documentation granularity of module
* declarations. Possible values are "api" or "all".
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showModuleContent(ModuleContent option) {
add("--show-module-contents");
add(option.name().toLowerCase());
return this;
}
/**
* Specifies which modules packages will be documented. Possible
* values are "exported" or "all" packages.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showPackages(ModulePackages option) {
add("--show-packages");
add(option.name().toLowerCase());
return this;
}
/**
* Specifies which types (classes, interfaces, etc.) will be
* documented, where value can be one of "public", "protected",
* "package" or "private". The default is "protected", which will
* show public and protected types, "public" will show only
* public types, "package" will show public, protected and
* package types and "private" will show all types.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions showTypes(Level option) {
add("--show-types");
add(option.name().toLowerCase());
return this;
}
/**
* Add a script file to the generated documentation
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions addScript(File file) {
add("--add-script");
add(file.getAbsolutePath());
return this;
}
/**
* Add a stylesheet file to the generated documentation
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions addStylesheet(File file) {
add("--add-stylesheet");
add(file.getAbsolutePath());
return this;
}
/**
* Allow JavaScript in options and comments
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions allowScriptInComments() {
add("--allow-script-in-comments");
return this;
}
/**
* Include @author paragraphs
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions author() {
add("-author");
return this;
}
/**
* Include bottom text for each page
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions bottom(String html) {
add("-bottom");
add(html);
return this;
}
/**
* Include title for the overview page
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions docTitle(String html) {
add("-doctitle");
add(html);
return this;
}
/**
* Include footer text for each page
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions footer(String html) {
add("-footer");
add(html);
return this;
}
/**
* Include header text for each page
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions header(String html) {
add("-header");
add(html);
return this;
}
/**
* Include HTML meta tags with package, class and member info
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions keywords() {
add("-keywords");
return this;
}
/**
* Create links to javadoc output at {@code url}
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions link(String url) {
add("-link");
add(url);
return this;
}
/**
* Link to docs at {@code url1} using package list at {@code url2}
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions linkOffline(String url1, String url2) {
add("-linkoffline");
add(url1);
add(url2);
return this;
}
/**
* Link to platform documentation URLs declared in properties file at {@code url}
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions linkPlatformProperties(String url) {
add("--link-platform-properties");
add(url);
return this;
}
/**
* Generate source in HTML
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions linkSource() {
add("-linksource");
return this;
}
/**
* File to change style of the generated documentation
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions stylesheet(File file) {
add("--main-stylesheet");
add(file.getAbsolutePath());
return this;
}
/**
* Suppress description and tags, generate only declarations
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noComment() {
add("-nocomment");
return this;
}
/**
* Do not include @deprecated information
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noDeprecated() {
add("-nodeprecated");
return this;
}
/**
* Do not generate deprecated list
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noDeprecatedList() {
add("-nodeprecatedlist");
return this;
}
/**
* Do not generate help link
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noHelp() {
add("-nohelp");
return this;
}
/**
* Do not generate index
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noIndex() {
add("-noindex");
return this;
}
/**
* Do not generate navigation bar
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noNavbar() {
add("-nonavbar");
return this;
}
/**
* Do not generate links to the platform documentation
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noPlatformLinks() {
add("--no-platform-links");
return this;
}
/**
* Exclude the list of qualifiers from the output
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noQualifier(String... qualifiers) {
return noQualifier(Arrays.asList(qualifiers));
}
/**
* Exclude the list of qualifiers from the output
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions noQualifier(List<String> qualifiers) {
add("-noqualifier");
add(StringUtils.join(qualifiers, ":"));
return this;
}
/**
* Do not include @since information
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noSince() {
add("-nosince");
return this;
}
/**
* Do not include hidden time stamp
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noTimestamp() {
add("-notimestamp");
return this;
}
/**
* Do not generate class hierarchy
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions noTree() {
add("-notree");
return this;
}
/**
* Document overridden methods in the detail or summary sections.
* The default is 'detail'.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions overrideMethods(Override option) {
add("--override-methods");
add(option.name().toLowerCase());
return this;
}
/**
* Read overview documentation from HTML file
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions overview(File htmlFile) {
add("-overview");
add(htmlFile.getAbsolutePath());
return this;
}
/**
* Generate warning about @serial tag
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions serialWarn() {
add("-serialwarn");
return this;
}
/**
* Document new and deprecated API in the specified releases
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions since(String... release) {
return since(Arrays.asList(release));
}
/**
* Document new and deprecated API in the specified releases
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions since(List<String> release) {
add("-since");
add(StringUtils.join(release, ","));
return this;
}
/**
* Provide text to use in the heading of the "New API" page
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions sinceLabel(String text) {
add("--since-label");
add(text);
return this;
}
/**
* The path for external snippets
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions snippetPath(File path) {
add("--snippet-path");
add(path.getAbsolutePath());
return this;
}
/**
* Specify the number of spaces each tab takes up in the source
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions sourceTab(int number) {
add("-sourcetab");
add(Convert.toString(number));
return this;
}
/**
* Split index into one file per letter
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions splitIndex() {
add("-splitindex");
return this;
}
/**
* Specify single argument custom tags
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions tag(String name, String locations, String header) {
add("-tag");
add(name + ":" + locations + ":" + header);
return this;
}
/**
* The fully qualified name of Taglet to register
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions taglet(String name) {
add("-taglet");
add(name);
return this;
}
/**
* The path to Taglets
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions tagletPath(File path) {
add("-tagletpath");
add(path.getAbsolutePath());
return this;
}
/**
* Include top text for each page
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions top(String html) {
add("-top");
add(html);
return this;
}
/**
* Create class and package usage pages
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions use() {
add("-use");
return this;
}
/**
* Include @version paragraphs
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions version() {
add("-version");
return this;
}
/**
* Browser window title for the documentation
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions windowTitle(String text) {
add("-windowtitle");
add(text);
return this;
}
/**
* Enable recommended checks for problems in javadoc comments
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions docLint() {
add("-Xdoclint");
return this;
}
/**
* Enable or disable specific checks for problems in javadoc
* comments.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions docLint(DocLinkOption option) {
add("-Xdoclint:" + option.getOption());
return this;
}
/**
* Enable or disable checks in specific packages.
* <p>
* A package specifier is either a qualified name of a package or a package
* name prefix followed by .*, which expands to all sub-packages
* of the given package. Prefix the package specifier with - to
* disable checks for the specified packages.
*
* @return this list of options
* @since 1.5.12
*/
public JavadocOptions docLintPackage(String... packages) {
return docLintPackage(Arrays.asList(packages));
}
/**
* Enable or disable checks in specific packages.
* <p>
* A package specifier is either a qualified name of a package or a package
* name prefix followed by .*, which expands to all sub-packages
* of the given package. Prefix the package specifier with - to
* disable checks for the specified packages.
*
* @return this list of options
* @since 1.5.18
*/
public JavadocOptions docLintPackage(List<String> packages) {
add("-Xdoclint/package:" + (StringUtils.join(packages, ",")));
return this;
}
}

View file

@ -0,0 +1,189 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.Project;
import rife.template.TemplateDeployer;
import rife.template.TemplateFactory;
import rife.tools.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Pre-compiles RIFE2 templates.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class PrecompileOperation extends AbstractOperation<PrecompileOperation> {
private final List<TemplateType> templateTypes_ = new ArrayList<>();
private final List<File> sourceDirectories_ = new ArrayList<>();
private File destinationDirectory_;
/**
* Performs the precompile operation.
*
* @since 1.5
*/
public void execute() {
if (templateTypes_.isEmpty()) {
return;
}
if (destinationDirectory() != null) {
destinationDirectory().mkdirs();
}
executeCreateTemplateDeployer().execute();
if (!silent()) {
System.out.println("Template pre-compilation finished successfully.");
}
}
/**
* Part of the {@link #execute} operation, gets the template factories for
* the registered template types.
*
* @since 1.5
*/
protected List<TemplateFactory> executeGetTemplateFactories() {
var template_factories = new ArrayList<TemplateFactory>();
for (var type : templateTypes()) {
var factory = TemplateFactory.getFactory(type.identifier());
if (factory == null) {
System.err.println("ERROR: unknown template type '" + type.identifier() + "'/");
} else {
template_factories.add(factory);
}
}
return template_factories;
}
/**
* Part of the {@link #execute} operation, creates the {@code TemplateDeployer}
* that will precompile the templates.
*
* @since 1.5
*/
protected TemplateDeployer executeCreateTemplateDeployer() {
return new TemplateDeployer()
.verbose(true)
.directoryPaths(FileUtils.combineToAbsolutePaths(sourceDirectories()))
.generationPath(destinationDirectory().getAbsolutePath())
.templateFactories(executeGetTemplateFactories());
}
/**
* Configures a precompile operation from a {@link BaseProject}.
*
* @param project the project to configure the precompile operation from
* @since 1.5
*/
public PrecompileOperation fromProject(BaseProject project) {
return sourceDirectories(project.srcMainResourcesTemplatesDirectory())
.destinationDirectory(project.buildTemplatesDirectory());
}
/**
* Provides template types that will be pre-compiled.
*
* @param types pre-compiled template types
* @return this operation instance
* @since 1.5.18
*/
public PrecompileOperation templateTypes(TemplateType... types) {
templateTypes_.addAll(List.of(types));
return this;
}
/**
* Provides a list of template types that will be pre-compiled.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param types a list of pre-compiled template types
* @return this operation instance
* @since 1.5
*/
public PrecompileOperation templateTypes(List<TemplateType> types) {
templateTypes_.addAll(types);
return this;
}
/**
* Provides source directories that will be used for the template pre-compilation.
*
* @param sources source directories
* @return this operation instance
* @since 1.5.18
*/
public PrecompileOperation sourceDirectories(File... sources) {
sourceDirectories_.addAll(List.of(sources));
return this;
}
/**
* Provides a list of source directories that will be used for the template pre-compilation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param sources a list of source directories
* @return this operation instance
* @since 1.5
*/
public PrecompileOperation sourceDirectories(List<File> sources) {
sourceDirectories_.addAll(sources);
return this;
}
/**
* Provides the destination directory in which the pre-compiled templates will be stored.
*
* @param directory the pre-compilation destination directory
* @return this operation instance
* @since 1.5
*/
public PrecompileOperation destinationDirectory(File directory) {
destinationDirectory_ = directory;
return this;
}
/**
* Retrieves the template types that will be pre-compiled.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the pre-compiled template types
* @since 1.5
*/
public List<TemplateType> templateTypes() {
return templateTypes_;
}
/**
* Retrieves the source directories that will be used for the template pre-compilation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the source directories
* @since 1.5
*/
public List<File> sourceDirectories() {
return sourceDirectories_;
}
/**
* Provides the destination directory in which the pre-compiled templates will be stored.
*
* @return the pre-compilation destination directory
* @since 1.5
*/
public File destinationDirectory() {
return destinationDirectory_;
}
}

View file

@ -0,0 +1,706 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.BldVersion;
import rife.bld.dependencies.*;
import rife.bld.dependencies.exceptions.DependencyException;
import rife.bld.operations.exceptions.OperationOptionException;
import rife.bld.operations.exceptions.SignException;
import rife.bld.operations.exceptions.UploadException;
import rife.bld.publish.*;
import rife.tools.FileUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.*;
import java.net.*;
import java.net.http.*;
import java.net.http.HttpRequest.BodyPublishers;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.*;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import static rife.bld.publish.MetadataBuilder.SNAPSHOT_TIMESTAMP_FORMATTER;
import static rife.tools.HttpUtils.*;
import static rife.tools.StringUtils.encodeHexLower;
/**
* Published artifacts to a Maven repository.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.7
*/
public class PublishOperation extends AbstractOperation<PublishOperation> {
private ArtifactRetriever retriever_ = null;
private final HttpClient client_ = HttpClient.newHttpClient();
private ZonedDateTime moment_ = null;
private final List<Repository> repositories_ = new ArrayList<>();
private final DependencyScopes dependencies_ = new DependencyScopes();
private PublishInfo info_ = new PublishInfo();
private final List<PublishArtifact> artifacts_ = new ArrayList<>();
/**
* Performs the publish operation.
*
* @since 1.5.7
*/
public void execute() {
if (repositories().isEmpty()) {
throw new OperationOptionException("ERROR: the publication repositories should be specified");
}
var moment = moment_;
if (moment == null) {
moment = ZonedDateTime.now();
}
executeValidateArtifacts();
var actual_version = info().version();
for (var repository : repositories()) {
System.out.println("Publishing to '" + repository.location() + "'");
// treat a snapshot version differently
if (info().version().isSnapshot()) {
actual_version = executePublishSnapshotMetadata(repository, moment);
}
executePublishArtifacts(repository, actual_version);
executePublishPom(repository, actual_version);
executePublishMetadata(repository, moment);
}
if (!silent()) {
System.out.println("Publishing finished successfully.");
}
}
/**
* Part of the {@link #execute} operation, validates the publishing artifacts.
*
* @since 1.5.10
*/
protected void executeValidateArtifacts() {
artifacts().removeIf(artifact -> {
if (!artifact.file().exists()) {
System.out.println("WARNING: Missing artifact file '" + artifact.file() + "', skipping.");
return true;
}
return false;
});
}
/**
* Part of the {@link #execute} operation, publishes snapshot metadata if this
* is a snapshot version.
*
* @param repository the repository to publish to
* @param moment the timestamp at which the operation started executing
* @return the adapted version number with the snapshot timestamp and build number
* @since 1.5.10
*/
protected VersionNumber executePublishSnapshotMetadata(Repository repository, ZonedDateTime moment) {
var metadata = new MetadataBuilder();
VersionNumber actual_version;
if (repository.isLocal()) {
actual_version = info().version();
metadata.snapshotLocal();
} else {
var snapshot_timestamp = SNAPSHOT_TIMESTAMP_FORMATTER.format(moment.withZoneSameInstant(ZoneId.of("UTC")));
// determine which build number to use
var snapshot_build_number = 1;
try {
var resolver = new DependencyResolver(artifactRetriever(), List.of(repository), new Dependency(info().groupId(), info().artifactId(), info().version()));
var snapshot_meta = resolver.getSnapshotMavenMetadata();
snapshot_build_number = snapshot_meta.getSnapshotBuildNumber() + 1;
} catch (DependencyException e) {
// start the build number from the beginning
System.out.println("Unable to retrieve previous snapshot metadata, using first build number.");
System.out.println("This is expected for a first publication or for publication to a staging repository.");
}
// adapt the actual version that's use by the artifacts
var snapshot_qualifier = snapshot_timestamp + "-" + snapshot_build_number;
actual_version = info().version().withQualifier(snapshot_qualifier);
// record the snapshot information in the metadata
metadata.snapshot(moment, snapshot_build_number);
}
// include version information about each artifact in this snapshot
for (var artifact : artifacts()) {
metadata.snapshotVersions().add(new SnapshotVersion(artifact.classifier(), artifact.type(), actual_version.toString(), moment));
}
metadata.snapshotVersions().add(new SnapshotVersion(null, "pom", actual_version.toString(), moment));
// publish snapshot metadata
executePublishStringArtifact(
repository,
metadata
.info(info())
.updated(moment)
.build(),
info().version() + "/" + repository.getMetadataName(), true);
return actual_version;
}
/**
* Part of the {@link #execute} operation, publishes all the artifacts
* in this operation.
*
* @param repository the repository to publish to
* @param actualVersion the version that was potentially adapted if this is a snapshot
* @since 1.5.10
*/
protected void executePublishArtifacts(Repository repository, VersionNumber actualVersion) {
// upload artifacts
for (var artifact : artifacts()) {
var artifact_name = new StringBuilder(info().artifactId()).append("-").append(actualVersion);
if (!artifact.classifier().isEmpty()) {
artifact_name.append("-").append(artifact.classifier());
}
var type = artifact.type();
if (type == null) {
type = "jar";
}
artifact_name.append(".").append(type);
executePublishFileArtifact(repository, artifact.file(), info().version() + "/" + artifact_name);
}
}
/**
* Part of the {@link #execute} operation, publishes the Maven POM.
*
* @param repository the repository to publish to
* @param actualVersion the version that was potentially adapted if this is a snapshot
* @since 1.5.10
*/
protected void executePublishPom(Repository repository, VersionNumber actualVersion) {
// generate and upload pom
executePublishStringArtifact(
repository,
new PomBuilder().info(info()).dependencies(dependencies()).build(),
info().version() + "/" + info().artifactId() + "-" + actualVersion + ".pom", true);
}
/**
* Part of the {@link #execute} operation, publishes the artifact metadata.
*
* @param repository the repository to publish to
* @param moment the timestamp at which the operation started executing
* @since 1.5.8
*/
protected void executePublishMetadata(Repository repository, ZonedDateTime moment) {
var current_versions = new ArrayList<VersionNumber>();
var resolver = new DependencyResolver(artifactRetriever(), List.of(repository), new Dependency(info().groupId(), info().artifactId(), info().version()));
try {
current_versions.addAll(resolver.getMavenMetadata().getVersions());
} catch (DependencyException e) {
// no existing versions could be found
System.out.println("Unable to retrieve previous artifact metadata, proceeding with empty version list.");
System.out.println("This is expected for a first publication or for publication to a staging repository.");
}
// upload metadata
executePublishStringArtifact(
repository,
new MetadataBuilder()
.info(info())
.updated(moment)
.otherVersions(current_versions)
.build(),
repository.getMetadataName(), false);
}
/**
* Part of the {@link #execute} operation, publishes a single artifact with
* hashes and a potential signature.
*
* @param repository the repository to publish to
* @param content the content of the file that needs to be published
* @param path the path of the artifact within the artifact folder
* @param sign indicates whether the artifact should be signed
* @since 1.5.18
*/
protected void executePublishStringArtifact(Repository repository, String content, String path, boolean sign)
throws UploadException {
try {
if (sign && info().signKey() != null) {
var tmp_file = File.createTempFile(path, "gpg");
FileUtils.writeString(content, tmp_file);
try {
executeTransferArtifact(repository, executeSignFile(tmp_file), path + ".asc");
} finally {
tmp_file.delete();
}
}
if (!repository.isLocal()) {
executeTransferArtifact(repository, generateHash(content, "MD5"), path + ".md5");
executeTransferArtifact(repository, generateHash(content, "SHA-1"), path + ".sha1");
executeTransferArtifact(repository, generateHash(content, "SHA-256"), path + ".sha256");
executeTransferArtifact(repository, generateHash(content, "SHA-512"), path + ".sha512");
}
executeTransferArtifact(repository, content, path);
} catch (NoSuchAlgorithmException | IOException e) {
throw new UploadException(path, e);
}
}
/**
* Generates the hash for a particular string and algorithm.
*
* @param content the string to generate the hash for
* @param algorithm the hashing algorithm to use
* @return the generates hash, encoded in lowercase hex
* @throws NoSuchAlgorithmException when the hashing algorithm couldn't be found
* @since 1.5.18
*/
protected String generateHash(String content, String algorithm)
throws NoSuchAlgorithmException {
var digest = MessageDigest.getInstance(algorithm);
digest.update(content.getBytes(StandardCharsets.UTF_8));
return encodeHexLower(digest.digest());
}
/**
* Part of the {@link #execute} operation, publishes a single artifact with
* hashes and a potential signature.
*
* @param repository the repository to publish to
* @param file the file that needs to be published
* @param path the path of the artifact within the artifact folder
* @since 1.5.8
*/
protected void executePublishFileArtifact(Repository repository, File file, String path)
throws UploadException {
try {
var digest_md5 = MessageDigest.getInstance("MD5");
var digest_sha1 = MessageDigest.getInstance("SHA-1");
var digest_sha256 = MessageDigest.getInstance("SHA-256");
var digest_sha512 = MessageDigest.getInstance("SHA-512");
try (var is = Files.newInputStream(file.toPath())) {
var buffer = new byte[1024];
var return_value = -1;
while (-1 != (return_value = is.read(buffer))) {
digest_md5.update(buffer, 0, return_value);
digest_sha1.update(buffer, 0, return_value);
digest_sha256.update(buffer, 0, return_value);
digest_sha512.update(buffer, 0, return_value);
}
if (info().signKey() != null) {
executeTransferArtifact(repository, executeSignFile(file), path + ".asc");
}
if (!repository.isLocal()) {
executeTransferArtifact(repository, encodeHexLower(digest_md5.digest()), path + ".md5");
executeTransferArtifact(repository, encodeHexLower(digest_sha1.digest()), path + ".sha1");
executeTransferArtifact(repository, encodeHexLower(digest_sha256.digest()), path + ".sha256");
executeTransferArtifact(repository, encodeHexLower(digest_sha512.digest()), path + ".sha512");
}
executeTransferArtifact(repository, file, path);
}
} catch (IOException | NoSuchAlgorithmException e) {
throw new UploadException(path, e);
}
}
/**
* Part of the {@link #execute} operation, generates the signature of a file.
*
* @param file the file whose signature will be generated
* @since 1.5.8
*/
protected String executeSignFile(File file)
throws IOException, FileUtilsErrorException {
var gpg_path = info().signGpgPath();
if (gpg_path == null) {
gpg_path = "gpg";
}
var gpg_arguments = new ArrayList<>(List.of(
gpg_path,
"--pinentry-mode=loopback",
"--no-tty", "--batch", "--detach-sign", "--armor", "-o-",
"--local-user", info().signKey()));
if (info().signPassphrase() != null) {
gpg_arguments.addAll(List.of("--passphrase", info().signPassphrase()));
}
gpg_arguments.add(file.getAbsolutePath());
var builder = new ProcessBuilder(gpg_arguments);
builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
builder.redirectError(ProcessBuilder.Redirect.PIPE);
var process = builder.start();
try {
process.waitFor();
} catch (InterruptedException e) {
throw new SignException(file, e.getMessage());
}
if (process.exitValue() != 0) {
var error = FileUtils.readString(process.getErrorStream());
throw new SignException(file, error);
}
return FileUtils.readString(process.getInputStream());
}
/**
* Part of the {@link #execute} operation, transfers an artifact.
*
* @param repository the repository to transfer to
* @param file the file to transfer
* @param path the path of the file within the artifact folder
* @since 1.5.18
*/
protected void executeTransferArtifact(Repository repository, File file, String path)
throws FileUtilsErrorException, IOException {
if (repository.isLocal()) {
executeStoreArtifact(repository, file, path);
} else {
executeUploadArtifact(repository, BodyPublishers.ofFile(file.toPath()), path);
}
}
/**
* Part of the {@link #execute} operation, transfers an artifact.
*
* @param repository the repository to transfer to
* @param content the content to transfer
* @param path the path of the file within the artifact folder
* @since 1.5.18
*/
protected void executeTransferArtifact(Repository repository, String content, String path)
throws FileUtilsErrorException, IOException {
if (repository.isLocal()) {
executeStoreArtifact(repository, content, path);
} else {
executeUploadArtifact(repository, BodyPublishers.ofString(content), path);
}
}
/**
* Part of the {@link #execute} operation, stores an artifact.
*
* @param repository the repository to store in
* @param file the file to store
* @param path the path of the file within the artifact folder
* @since 1.5.18
*/
protected void executeStoreArtifact(Repository repository, File file, String path)
throws FileUtilsErrorException {
var location = repository.getArtifactLocation(info().groupId(), info().artifactId()) + path;
System.out.print("Storing: " + location + " ... ");
try {
var target = new File(location);
target.getParentFile().mkdirs();
FileUtils.copy(file, target);
System.out.print("done");
} catch (FileUtilsErrorException e) {
System.out.print("error");
throw e;
} finally {
System.out.println();
}
}
/**
* Part of the {@link #execute} operation, stores an artifact.
*
* @param repository the repository to store in
* @param content the content to store
* @param path the path of the file within the artifact folder
* @since 1.5.18
*/
protected void executeStoreArtifact(Repository repository, String content, String path)
throws FileUtilsErrorException {
var location = repository.getArtifactLocation(info().groupId(), info().artifactId()) + path;
System.out.print("Storing: " + location + " ... ");
try {
var target = new File(location);
target.getParentFile().mkdirs();
FileUtils.writeString(content, target);
System.out.print("done");
} catch (FileUtilsErrorException e) {
System.out.print("error");
throw e;
} finally {
System.out.println();
}
}
/**
* Part of the {@link #execute} operation, uploads an artifact.
*
* @param repository the repository to upload to
* @param body the body of the file to upload
* @param path the path of the file within the artifact folder
* @since 1.5.18
*/
protected void executeUploadArtifact(Repository repository, HttpRequest.BodyPublisher body, String path) {
var url = repository.getArtifactLocation(info().groupId(), info().artifactId()) + path;
System.out.print("Uploading: " + url + " ... ");
System.out.flush();
try {
var builder = HttpRequest.newBuilder()
.PUT(body)
.uri(URI.create(url))
.header(HEADER_USER_AGENT, "bld/" + BldVersion.getVersion() +
" (" + System.getProperty("os.name") + "; " + System.getProperty("os.version") + "; " + System.getProperty("os.arch") + ") " +
"(" + System.getProperty("java.vendor") + " " + System.getProperty("java.vm.name") + "; " + System.getProperty("java.version") + "; " + System.getProperty("java.vm.version") + ")");
if (repository.username() != null && repository.password() != null) {
builder.header(HEADER_AUTHORIZATION, basicAuthorizationHeader(repository.username(), repository.password()));
}
var request = builder.build();
HttpResponse<String> response;
try {
response = client_.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException e) {
System.out.print("I/O error");
throw new UploadException(url, e);
} catch (InterruptedException e) {
System.out.print("interrupted");
throw new UploadException(url, e);
}
if (response.statusCode() >= 200 &&
response.statusCode() < 300) {
System.out.print("done");
} else {
System.out.print("failed");
throw new UploadException(url, response.statusCode());
}
} finally {
System.out.println();
}
}
/**
* Configures a publish operation from a {@link BaseProject}.
*
* @param project the project to configure the publish operation from
* @since 1.5.7
*/
public PublishOperation fromProject(BaseProject project) {
artifactRetriever(project.artifactRetriever());
dependencies().include(project.dependencies());
artifacts(List.of(
new PublishArtifact(new File(project.buildDistDirectory(), project.jarFileName()), "", "jar"),
new PublishArtifact(new File(project.buildDistDirectory(), project.sourcesJarFileName()), "sources", "jar"),
new PublishArtifact(new File(project.buildDistDirectory(), project.javadocJarFileName()), "javadoc", "jar")));
if (info().groupId() == null) {
info().groupId(project.pkg());
}
if (info().artifactId() == null) {
info().artifactId(project.name().toLowerCase());
}
if (info().version() == null) {
info().version(project.version());
}
if (info().name() == null) {
info().name(project.name());
}
return this;
}
/**
* Provides the moment of publication.
* <p>
* If this is not provided, the publication will use the current data and time.
*
* @param moment the publication moment
* @return this operation instance
* @since 1.5.8
*/
public PublishOperation moment(ZonedDateTime moment) {
moment_ = moment;
return this;
}
/**
* Retrieves the moment of publication.
*
* @return the moment of publication; or
* {@code null} if it wasn't provided
* @since 1.5.8
*/
public ZonedDateTime moment() {
return moment_;
}
/**
* Provides a repository to publish to, can be called multiple times to
* add more repositories.
*
* @param repository a repository that the artifacts will be published to
* @return this operation instance
* @since 1.5.7
*/
public PublishOperation repository(Repository repository) {
repositories_.add(repository);
return this;
}
/**
* Provides repositories to publish to.
*
* @param repositories repositories where the artifacts will be published
* @return this operation instance
* @since 1.5.18
*/
public PublishOperation repositories(Repository... repositories) {
repositories_.addAll(List.of(repositories));
return this;
}
/**
* Provides a list of repositories to publish to.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param repositories a list of repositories where the artifacts will be published
* @return this operation instance
* @since 1.5
*/
public PublishOperation repositories(List<Repository> repositories) {
repositories_.addAll(repositories);
return this;
}
/**
* Provides scoped dependencies to reference in the publication.
*
* @param dependencies the dependencies that will be references in the publication
* @return this operation instance
* @since 1.5.7
*/
public PublishOperation dependencies(DependencyScopes dependencies) {
dependencies_.include(dependencies);
return this;
}
/**
* Provides the publication info structure.
*
* @param info the publication info
* @return this operation instance
* @since 1.5.18
*/
public PublishOperation info(PublishInfo info) {
info_ = info;
return this;
}
/**
* Provides artifacts that will be published.
*
* @param artifacts artifacts to publish
* @return this operation instance
* @since 1.5.18
*/
public PublishOperation artifacts(PublishArtifact... artifacts) {
artifacts_.addAll(List.of(artifacts));
return this;
}
/**
* Provides a list of artifacts that will be published.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param artifacts a list of artifacts to publish
* @return this operation instance
* @since 1.5.7
*/
public PublishOperation artifacts(List<PublishArtifact> artifacts) {
artifacts_.addAll(artifacts);
return this;
}
/**
* Provides the artifact retriever to use.
*
* @param retriever the artifact retriever
* @return this operation instance
* @since 1.5.18
*/
public PublishOperation artifactRetriever(ArtifactRetriever retriever) {
retriever_ = retriever;
return this;
}
/**
* Retrieves the repositories to which will be published.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the repositories where the artifacts will be published
* @since 1.51.8
*/
public List<Repository> repositories() {
return repositories_;
}
/**
* Retrieves the scoped dependencies to reference in the publication.
* <p>
* This is a modifiable structure that can be retrieved and changed.
*
* @return the scoped dependencies
* @since 1.5.7
*/
public DependencyScopes dependencies() {
return dependencies_;
}
/**
* Retrieves the publication info structure.
* <p>
* This is a modifiable structure that can be retrieved and changed.
*
* @return the publication info
* @since 1.5.7
*/
public PublishInfo info() {
return info_;
}
/**
* Retrieves the list of artifacts that will be published.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the list of artifacts to publish
* @since 1.5.7
*/
public List<PublishArtifact> artifacts() {
return artifacts_;
}
/**
* Returns the artifact retriever that is used.
*
* @return the artifact retriever
* @since 1.5.18
*/
public ArtifactRetriever artifactRetriever() {
if (retriever_ == null) {
return ArtifactRetriever.instance();
}
return retriever_;
}
}

View file

@ -0,0 +1,369 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.dependencies.*;
import java.io.File;
import java.util.*;
import static rife.bld.dependencies.Dependency.CLASSIFIER_JAVADOC;
import static rife.bld.dependencies.Dependency.CLASSIFIER_SOURCES;
/**
* Transitively checks all the artifacts for dependencies in the directories
* that are separated out by scope, any files that aren't required will be deleted.
* <p>
* If a directory is not provided, no purge will occur for that
* dependency scope.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class PurgeOperation extends AbstractOperation<PurgeOperation> {
private ArtifactRetriever retriever_ = null;
private final List<Repository> repositories_ = new ArrayList<>();
private final DependencyScopes dependencies_ = new DependencyScopes();
private File libCompileDirectory_;
private File libRuntimeDirectory_;
private File libStandaloneDirectory_;
private File libTestDirectory_;
private boolean preserveSources_ = false;
private boolean preserveJavadoc_ = false;
/**
* Performs the purge operation.
*
* @since 1.5
*/
public void execute() {
executePurgeCompileDependencies();
executePurgeRuntimeDependencies();
executePurgeStandaloneDependencies();
executePurgeTestDependencies();
if (!silent()) {
System.out.println("Purging finished successfully.");
}
}
/**
* Part of the {@link #execute} operation, purge the {@code compile} scope artifacts.
*
* @since 1.5
*/
protected void executePurgeCompileDependencies() {
executePurgeDependencies(libCompileDirectory(), dependencies().resolveCompileDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, purge the {@code runtime} scope artifacts.
*
* @since 1.5
*/
protected void executePurgeRuntimeDependencies() {
executePurgeDependencies(libRuntimeDirectory(), dependencies().resolveRuntimeDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, purge the {@code standalone} scope artifacts.
*
* @since 1.5
*/
protected void executePurgeStandaloneDependencies() {
executePurgeDependencies(libStandaloneDirectory(), dependencies().resolveStandaloneDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, purge the {@code test} scope artifacts.
*
* @since 1.5
*/
protected void executePurgeTestDependencies() {
executePurgeDependencies(libTestDirectory(), dependencies().resolveTestDependencies(artifactRetriever(), repositories()));
}
/**
* Part of the {@link #execute} operation, purge the artifacts for a particular dependency scope.
*
* @param destinationDirectory the directory from which the artifacts should be purged
* @param dependencies the dependencies to purge
* @since 1.6
*/
protected void executePurgeDependencies(File destinationDirectory, DependencySet dependencies) {
if (destinationDirectory == null) {
return;
}
var filenames = new HashSet<String>();
for (var dependency : dependencies) {
addTransferLocations(filenames, dependency);
if (preserveSources_) {
addTransferLocations(filenames, dependency.withClassifier(CLASSIFIER_SOURCES));
}
if (preserveJavadoc_) {
addTransferLocations(filenames, dependency.withClassifier(CLASSIFIER_JAVADOC));
}
}
boolean printed_header = false;
for (var file : destinationDirectory.listFiles()) {
if (!filenames.contains(file.getName())) {
if (!printed_header) {
printed_header = true;
System.out.println("Deleting from " + destinationDirectory.getName() + ":");
}
System.out.println(" " + file.getName());
file.delete();
}
}
}
private void addTransferLocations(HashSet<String> filenames, Dependency dependency) {
for (var location : new DependencyResolver(artifactRetriever(), repositories(), dependency).getTransferLocations()) {
filenames.add(location.substring(location.lastIndexOf("/") + 1));
}
}
/**
* Configures a compile operation from a {@link BaseProject}.
*
* @param project the project to configure the compile operation from
* @since 1.5
*/
public PurgeOperation fromProject(BaseProject project) {
return artifactRetriever(project.artifactRetriever())
.repositories(project.repositories())
.dependencies(project.dependencies())
.libCompileDirectory(project.libCompileDirectory())
.libRuntimeDirectory(project.libRuntimeDirectory())
.libStandaloneDirectory(project.libStandaloneDirectory())
.libTestDirectory(project.libTestDirectory())
.preserveSources(project.downloadSources())
.preserveJavadoc(project.downloadJavadoc());
}
/**
* Indicates whether the sources classifier files should be preserved.
*
* @param flag {@code true} if the sources classifier files should be preserved; or
* {@code false} otherwise
* @return this operation instance
* @since 1.5.6
*/
public PurgeOperation preserveSources(boolean flag) {
preserveSources_ = flag;
return this;
}
/**
* Indicates whether the javadoc classifier files should be preserved.
*
* @param flag {@code true} if the javadoc classifier files should be preserved; or
* {@code false} otherwise
* @return this operation instance
* @since 1.5.6
*/
public PurgeOperation preserveJavadoc(boolean flag) {
preserveJavadoc_ = flag;
return this;
}
/**
* Provides repositories to resolve the dependencies against.
*
* @param repositories repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5.18
*/
public PurgeOperation repositories(Repository... repositories) {
repositories_.addAll(List.of(repositories));
return this;
}
/**
* Provides a list of repositories to resolve the dependencies against.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param repositories a list of repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5
*/
public PurgeOperation repositories(List<Repository> repositories) {
repositories_.addAll(repositories);
return this;
}
/**
* Provides scoped dependencies for artifact purge.
*
* @param dependencies the dependencies that will be resolved for artifact purge
* @return this operation instance
* @since 1.5
*/
public PurgeOperation dependencies(DependencyScopes dependencies) {
dependencies_.include(dependencies);
return this;
}
/**
* Provides the {@code compile} scope purge directory.
*
* @param directory the directory to purge the {@code compile} scope artifacts from
* @return this operation instance
* @since 1.5
*/
public PurgeOperation libCompileDirectory(File directory) {
libCompileDirectory_ = directory;
return this;
}
/**
* Provides the {@code runtime} scope purge directory.
*
* @param directory the directory to purge the {@code runtime} scope artifacts from
* @return this operation instance
* @since 1.5
*/
public PurgeOperation libRuntimeDirectory(File directory) {
libRuntimeDirectory_ = directory;
return this;
}
/**
* Provides the {@code standalone} scope purge directory.
*
* @param directory the directory to purge the {@code standalone} scope artifacts from
* @return this operation instance
* @since 1.5
*/
public PurgeOperation libStandaloneDirectory(File directory) {
libStandaloneDirectory_ = directory;
return this;
}
/**
* Provides the {@code test} scope purge directory.
*
* @param directory the directory to purge the {@code test} scope artifacts from
* @return this operation instance
* @since 1.5
*/
public PurgeOperation libTestDirectory(File directory) {
libTestDirectory_ = directory;
return this;
}
/**
* Provides the artifact retriever to use.
*
* @param retriever the artifact retriever
* @return this operation instance
* @since 1.5.18
*/
public PurgeOperation artifactRetriever(ArtifactRetriever retriever) {
retriever_ = retriever;
return this;
}
/**
* Retrieves the repositories in which the dependencies will be resolved.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the repositories used for dependency resolution
* @since 1.5
*/
public List<Repository> repositories() {
return repositories_;
}
/**
* Retrieves the scoped dependencies that will be used for artifact purge.
* <p>
* This is a modifiable structure that can be retrieved and changed.
*
* @return the scoped dependencies
* @since 1.5
*/
public DependencyScopes dependencies() {
return dependencies_;
}
/**
* Retrieves the {@code compile} scope purge directory.
*
* @return the {@code compile} scope purge directory
* @since 1.5
*/
public File libCompileDirectory() {
return libCompileDirectory_;
}
/**
* Retrieves the {@code runtime} scope purge directory.
*
* @return the {@code runtime} scope purge directory
* @since 1.5
*/
public File libRuntimeDirectory() {
return libRuntimeDirectory_;
}
/**
* Retrieves the {@code standalone} scope purge directory.
*
* @return the {@code standalone} scope purge directory
* @since 1.5
*/
public File libStandaloneDirectory() {
return libStandaloneDirectory_;
}
/**
* Retrieves the {@code test} scope purge directory.
*
* @return the {@code test} scope purge directory
* @since 1.5
*/
public File libTestDirectory() {
return libTestDirectory_;
}
/**
* Retrieves whether the sources classifier files should be preserved.
*
* @return {@code true} if the sources classifier should be preserved; or
* {@code false} otherwise
* @since 1.5.6
*/
public boolean preserveSources() {
return preserveSources_;
}
/**
* Retrieves whether the javadoc classifier files should be preserved.
*
* @return {@code true} if the javadoc classifier should be preserved; or
* {@code false} otherwise
* @since 1.5.6
*/
public boolean preserveJavadoc() {
return preserveJavadoc_;
}
/**
* Returns the artifact retriever that is used.
*
* @return the artifact retriever
* @since 1.5.18
*/
public ArtifactRetriever artifactRetriever() {
if (retriever_ == null) {
return ArtifactRetriever.instance();
}
return retriever_;
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.tools.FileUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Runs a Java application.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class RunOperation extends AbstractProcessOperation<RunOperation> {
protected final List<String> runOptions_ = new ArrayList<>();
/**
* Part of the {@link #execute} operation, constructs the command list
* to use for building the process.
*
* @since 1.5
*/
protected List<String> executeConstructProcessCommandList() {
var args = new ArrayList<String>();
args.add(javaTool());
args.addAll(javaOptions());
if (!classpath().isEmpty()) {
args.add("-cp");
args.add(FileUtils.joinPaths(classpath()));
}
args.add(mainClass());
args.addAll(runOptions());
return args;
}
/**
* Configures a run operation from a {@link BaseProject}.
*
* @param project the project to configure the run operation from
* @since 1.5
*/
public RunOperation fromProject(BaseProject project) {
var operation = workDirectory(project.workDirectory())
.javaTool(project.javaTool())
.classpath(project.runClasspath())
.mainClass(project.mainClass());
if (project.usesRife2Agent()) {
operation.javaOptions().javaAgent(project.getRife2AgentFile());
}
return operation;
}
/**
* Provides options for the run operation
*
* @param options run options
* @return this operation instance
* @since 1.5.18
*/
public RunOperation runOptions(String... options) {
runOptions_.addAll(List.of(options));
return this;
}
/**
* Provides options for the run operation
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options run options
* @return this operation instance
* @since 1.5.18
*/
public RunOperation runOptions(List<String> options) {
runOptions_.addAll(options);
return this;
}
/**
* Retrieves the run options
*
* @return the run options
* @since 1.5.18
*/
public List<String> runOptions() {
return runOptions_;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.io.Serial;
import java.io.Serializable;
/**
* Allows template types to be specified for pre-compilation.
*/
public class TemplateType implements Serializable {
@Serial private static final long serialVersionUID = -2736320275307140837L;
/**
* The {@code html} template type.
*/
public static TemplateType HTML = new TemplateType("html");
/**
* The {@code json} template type.
*/
public static TemplateType JSON = new TemplateType("json");
/**
* The {@code svg} template type.
*/
public static TemplateType SVG = new TemplateType("svg");
/**
* The {@code xml} template type.
*/
public static TemplateType XML = new TemplateType("xml");
/**
* The {@code txt} template type.
*/
public static TemplateType TXT = new TemplateType("txt");
/**
* The {@code sql} template type.
*/
public static TemplateType SQL = new TemplateType("sql");
private final String identifier_;
/**
* Creates a new template type instance.
*
* @param identifier the identifier of this template type
*/
public TemplateType(String identifier) {
identifier_ = identifier;
}
/**
* Retrieves the identifier for this template type
* @return the template type identifier as a string
*/
public String identifier() {
return identifier_;
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.Project;
import rife.tools.FileUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Tests a Java application.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class TestOperation<T extends TestOperation<T, O>, O extends List<String>> extends AbstractProcessOperation<T> {
protected final O testToolOptions_;
/**
* Instantiates a new test operation.
* @since 1.5.20
*/
public TestOperation() {
testToolOptions_ = createTestToolOptions();
}
/**
* Creates a new collection of test tool options.
*
* @return the test tool options to use
* @since 1.5.20
*/
protected O createTestToolOptions() {
return (O)new ArrayList<String>();
}
/**
* Part of the {@link #execute} operation, constructs the command list
* to use for building the process.
*
* @since 1.5
*/
protected List<String> executeConstructProcessCommandList() {
if (mainClass() == null) {
throw new IllegalArgumentException("ERROR: Missing main class for test execution.");
}
var args = new ArrayList<String>();
args.add(javaTool());
args.addAll(javaOptions());
args.add("-cp");
args.add(FileUtils.joinPaths(classpath()));
args.add(mainClass());
args.addAll(testToolOptions());
return args;
}
/**
* Configures a test operation from a {@link BaseProject}.
*
* @param project the project to configure the test operation from
* @since 1.5
*/
public T fromProject(BaseProject project) {
var operation = workDirectory(project.workDirectory())
.javaTool(project.javaTool())
.classpath(project.testClasspath());
if (project.usesRife2Agent()) {
operation.javaOptions().javaAgent(project.getRife2AgentFile());
}
return operation;
}
/**
* Provides options to provide to the test tool.
*
* @param options test tool options
* @return this operation instance
* @since 1.5.18
*/
public T testToolOptions(String... options) {
testToolOptions_.addAll(List.of(options));
return (T)this;
}
/**
* Provides options to provide to the test tool.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options test tool options
* @return this operation instance
* @since 1.5
*/
public T testToolOptions(List<String> options) {
testToolOptions_.addAll(options);
return (T)this;
}
/**
* Retrieves the options for the test tool.
*
* @return the test tool's options
* @since 1.5
*/
public O testToolOptions() {
return (O)testToolOptions_;
}
}

View file

@ -0,0 +1,268 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.NamedFile;
import rife.bld.Project;
import rife.tools.FileUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.jar.Attributes;
import java.util.regex.Pattern;
/**
* Creates an uberjar archive of the provided jars and resources.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class UberJarOperation extends AbstractOperation<UberJarOperation> {
private final List<File> jarSourceFiles_ = new ArrayList<>();
private final List<NamedFile> sourceDirectories_ = new ArrayList<>();
private File destinationDirectory_;
private String destinationFileName_;
private String mainClass_;
/**
* Performs the uberjar operation.
*
* @throws IOException when an exception occurred during the uberjar creation process
* @throws FileUtilsErrorException when an exception occurred during the uberjar creation process
* @since 1.5
*/
public void execute()
throws IOException, FileUtilsErrorException {
var tmp_dir = Files.createTempDirectory("uberjar").toFile();
try {
executeCollectSourceJarContents(tmp_dir);
executeCollectSourceResources(tmp_dir);
executeCreateUberJarArchive(tmp_dir);
if (!silent()) {
System.out.println("The uberjar archive was created at '" + new File(destinationDirectory(), destinationFileName()) + "'");
}
} finally {
FileUtils.deleteDirectory(tmp_dir);
}
}
/**
* Part of the {@link #execute} operation, collect the contents of all the source jars.
*
* @since 1.5
*/
protected void executeCollectSourceJarContents(File stagingDirectory)
throws FileUtilsErrorException {
for (var jar : jarSourceFiles()) {
FileUtils.unzipFile(jar, stagingDirectory);
}
}
/**
* Part of the {@link #execute} operation, collect the source resources.
*
* @since 1.5
*/
protected void executeCollectSourceResources(File stagingDirectory)
throws FileUtilsErrorException {
for (var named_file : sourceDirectories()) {
if (named_file.file().exists()) {
var destination_file = new File(stagingDirectory, named_file.name());
destination_file.mkdirs();
FileUtils.copyDirectory(named_file.file(), destination_file);
}
}
}
/**
* Part of the {@link #execute} operation, create the uberjar archive.
*
* @since 1.5
*/
protected void executeCreateUberJarArchive(File stagingDirectory)
throws IOException {
var existing_manifest = new File(new File(stagingDirectory, "META-INF"), "MANIFEST.MF");
existing_manifest.delete();
new JarOperation()
.manifestAttributes(Map.of(
Attributes.Name.MANIFEST_VERSION, "1.0",
Attributes.Name.MAIN_CLASS, mainClass()))
.sourceDirectories(stagingDirectory)
.destinationDirectory(destinationDirectory())
.destinationFileName(destinationFileName())
.excluded(List.of(Pattern.compile("(?:(?:^.*[/\\\\])|^)\\.DS_Store$")))
.silent(true)
.execute();
}
/**
* Configures an uberjar operation from a {@link BaseProject}.
*
* @param project the project to configure the uberjar operation from
* @since 1.5
*/
public UberJarOperation fromProject(BaseProject project) {
var jars = new ArrayList<>(project.compileClasspathJars());
jars.addAll(project.runtimeClasspathJars());
jars.add(new File(project.buildDistDirectory(), project.jarFileName()));
return jarSourceFiles(jars)
.destinationDirectory(project.buildDistDirectory())
.destinationFileName(project.uberJarFileName())
.mainClass(project.uberJarMainClass());
}
/**
* Provides source jar files that will be used for the uberjar archive creation.
*
* @param files source files
* @return this operation instance
* @since 1.5.18
*/
public UberJarOperation jarSourceFiles(File... files) {
jarSourceFiles_.addAll(List.of(files));
return this;
}
/**
* Provides a list of source jar files that will be used for the uberjar archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of jar source files
* @return this operation instance
* @since 1.5
*/
public UberJarOperation jarSourceFiles(List<File> files) {
jarSourceFiles_.addAll(files);
return this;
}
/**
* Provides source directories that will be used for the uberjar archive creation.
*
* @param directories source directories
* @return this operation instance
* @since 1.5.18
*/
public UberJarOperation sourceDirectories(NamedFile... directories) {
sourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of source directories that will be used for the uberjar archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of source directories
* @return this operation instance
* @since 1.5
*/
public UberJarOperation sourceDirectories(List<NamedFile> directories) {
sourceDirectories_.addAll(directories);
return this;
}
/**
* Provides the destination directory in which the uberjar archive will be created.
*
* @param directory the uberjar destination directory
* @return this operation instance
* @since 1.5
*/
public UberJarOperation destinationDirectory(File directory) {
destinationDirectory_ = directory;
return this;
}
/**
* Provides the destination file name that will be used for the uberjar archive creation.
*
* @param name the uberjar archive destination file name
* @return this operation instance
* @since 1.5
*/
public UberJarOperation destinationFileName(String name) {
destinationFileName_ = name;
return this;
}
/**
* Provides the main class to run from the uberjar archive.
*
* @param name the main class to run
* @return this operation instance
* @since 1.5
*/
public UberJarOperation mainClass(String name) {
mainClass_ = name;
return this;
}
/**
* Retrieves the list of jar source files that will be used for the
* uberjar archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the uberjar archive's jar source files
* @since 1.5
*/
public List<File> jarSourceFiles() {
return jarSourceFiles_;
}
/**
* Retrieves the list of source directories that will be used for the
* uberjar archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the uberjar archive's source directories
* @since 1.5
*/
public List<NamedFile> sourceDirectories() {
return sourceDirectories_;
}
/**
* Retrieves the destination directory in which the uberjar archive will
* be created.
*
* @return the uberjar archive's destination directory
* @since 1.5
*/
public File destinationDirectory() {
return destinationDirectory_;
}
/**
* Retrieves the destination file name that will be used for the uberjar
* archive creation.
*
* @return the uberjar archive's destination file name
* @since 1.5
*/
public String destinationFileName() {
return destinationFileName_;
}
/**
* Retrieves the main class to run from the uberjar archive.
*
* @return the main class to run
* @since 1.5
*/
public String mainClass() {
return mainClass_;
}
}

View file

@ -0,0 +1,170 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.BaseProject;
import rife.bld.Project;
import rife.bld.dependencies.*;
import java.util.ArrayList;
import java.util.List;
/**
* Determines which updates are available for provides dependencies.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class UpdatesOperation extends AbstractOperation<UpdatesOperation> {
private ArtifactRetriever retriever_ = null;
private final List<Repository> repositories_ = new ArrayList<>();
private final DependencyScopes dependencies_ = new DependencyScopes();
private DependencyScopes updates_ = new DependencyScopes();
/**
* Performs the updates operation.
*
* @since 1.5
*/
public void execute() {
var result = new DependencyScopes();
for (var entry : dependencies_.entrySet()) {
var scope = entry.getKey();
for (var dependency : entry.getValue()) {
var latest = new DependencyResolver(artifactRetriever(), repositories(), dependency).latestVersion();
if (latest.compareTo(dependency.version()) > 0) {
var latest_dependency = new Dependency(dependency.groupId(), dependency.artifactId(), latest,
dependency.classifier(), dependency.type());
result.scope(scope).include(latest_dependency);
}
}
}
if (result.isEmpty()) {
if (!silent()) {
System.out.println("No dependency updates found.");
}
} else {
System.out.println("The following dependency updates were found.");
for (var entry : result.entrySet()) {
var scope = entry.getKey();
System.out.println(scope + ":");
for (var dependency : entry.getValue()) {
System.out.println(" " + dependency);
}
}
}
updates_ = result;
}
/**
* Configures an updates operation from a {@link BaseProject}.
*
* @param project the project to configure the updates operation from
* @since 1.5
*/
public UpdatesOperation fromProject(BaseProject project) {
return artifactRetriever(project.artifactRetriever())
.repositories(project.repositories())
.dependencies(project.dependencies());
}
/**
* Provides repositories to resolve the dependencies against.
*
* @param repositories repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5.18
*/
public UpdatesOperation repositories(Repository... repositories) {
repositories_.addAll(List.of(repositories));
return this;
}
/**
* Provides a list of repositories to resolve the dependencies against.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param repositories a list of repositories against which dependencies will be resolved
* @return this operation instance
* @since 1.5
*/
public UpdatesOperation repositories(List<Repository> repositories) {
repositories_.addAll(repositories);
return this;
}
/**
* Provides scoped dependencies that will be checked for updates.
*
* @param dependencies the dependencies that will be checked for updates
* @return this operation instance
* @since 1.5
*/
public UpdatesOperation dependencies(DependencyScopes dependencies) {
dependencies_.include(dependencies);
return this;
}
/**
* Provides the artifact retriever to use.
*
* @param retriever the artifact retriever
* @return this operation instance
* @since 1.5.18
*/
public UpdatesOperation artifactRetriever(ArtifactRetriever retriever) {
retriever_ = retriever;
return this;
}
/**
* Retrieves the repositories in which the dependencies will be resolved.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the repositories used for dependency resolution
* @since 1.5
*/
public List<Repository> repositories() {
return repositories_;
}
/**
* Retrieves the scoped dependencies that will be checked for updates.
* <p>
* This is a modifiable structure that can be retrieved and changed.
*
* @return the scoped dependencies
* @since 1.5
*/
public DependencyScopes dependencies() {
return dependencies_;
}
/**
* Retrieves the scoped dependencies with updates found after execution.
*
* @return the scoped dependencies with updates
* @since 1.5
*/
public DependencyScopes updates() {
return updates_;
}
/**
* Returns the artifact retriever that is used.
*
* @return the artifact retriever
* @since 1.5.18
*/
public ArtifactRetriever artifactRetriever() {
if (retriever_ == null) {
return ArtifactRetriever.instance();
}
return retriever_;
}
}

View file

@ -0,0 +1,33 @@
package rife.bld.operations;
import rife.bld.BldVersion;
import rife.bld.wrapper.Wrapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
/**
* Upgrades the project's bld wrapper to the version of the running
* RIFE2 framework.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class UpgradeOperation extends AbstractOperation<UpgradeOperation> {
/**
* Performs the upgrade operation.
*
* @throws IOException when an error occurred during the upgrade operation
* @since 1.5
*/
public void execute()
throws IOException {
new Wrapper().createWrapperFiles(Path.of("lib", "bld").toFile(), BldVersion.getVersion());
new Wrapper().upgradeIdeaBldLibrary(new File(".idea"), BldVersion.getVersion());
new Wrapper().upgradeVscodeSettings(new File(".vscode"), BldVersion.getVersion());
if (!silent()) {
System.out.println("The wrapper was successfully upgraded to " + BldVersion.getVersion() + ".");
}
}
}

View file

@ -0,0 +1,26 @@
package rife.bld.operations;
import rife.bld.BldVersion;
import java.io.IOException;
/**
* Outputs the version of the build system.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.2
*/
public class VersionOperation extends AbstractOperation<VersionOperation> {
/**
* Performs the version operation.
*
* @throws IOException when an error occurred during the upgrade operation
* @since 1.5.2
*/
public void execute() {
if (!silent()) {
System.out.println("bld " + BldVersion.getVersion());
}
}
}

View file

@ -0,0 +1,377 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.*;
import rife.tools.FileUtils;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* Creates a war archive.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class WarOperation extends AbstractOperation<WarOperation> {
private final List<File> libSourceDirectories_ = new ArrayList<>();
private final List<File> classesSourceDirectories_ = new ArrayList<>();
private final List<NamedFile> jarSourceFiles_ = new ArrayList<>();
private File webappDirectory_;
private File webXmlFile_;
private File destinationDirectory_;
private String destinationFileName_;
/**
* Performs the war operation.
*
* @throws IOException when an exception occurred during the war creation process
* @throws FileUtilsErrorException when an exception occurred war the uberjar creation process
* @since 1.5
*/
public void execute()
throws IOException, FileUtilsErrorException {
var tmp_dir = Files.createTempDirectory("war").toFile();
try {
var web_inf_dir = executeCreateWebInfDirectory(tmp_dir);
executeCopyWebappDirectory(tmp_dir);
executeCopyWebInfLibJars(web_inf_dir);
executeCopyWebInfClassesFiles(web_inf_dir);
executeCopyWebXmlFile(web_inf_dir);
executeCreateWarArchive(tmp_dir);
if (!silent()) {
System.out.println("The war archive was created at '" + new File(destinationDirectory(), destinationFileName()) + "'");
}
} finally {
FileUtils.deleteDirectory(tmp_dir);
}
}
/**
* Part of the {@link #execute} operation, create the staging {@code WEB-INF} directory.
*
* @since 1.5
*/
protected File executeCreateWebInfDirectory(File stagingDirectory) {
var web_inf_dir = new File(stagingDirectory, "WEB-INF");
web_inf_dir.mkdirs();
return web_inf_dir;
}
/**
* Part of the {@link #execute} operation, create the staging webapp directory.
*
* @since 1.5
*/
protected void executeCopyWebappDirectory(File stagingDirectory)
throws FileUtilsErrorException {
if (webappDirectory() != null) {
FileUtils.copyDirectory(webappDirectory(), stagingDirectory);
}
}
/**
* Part of the {@link #execute} operation, copy the staging {@code WEB-INF} jars.
*
* @since 1.5
*/
protected void executeCopyWebInfLibJars(File stagingWebInfDirectory)
throws FileUtilsErrorException {
var web_inf_lib_dir = new File(stagingWebInfDirectory, "lib");
if (!libSourceDirectories().isEmpty()) {
web_inf_lib_dir.mkdirs();
for (var dir : libSourceDirectories()) {
FileUtils.copyDirectory(dir, web_inf_lib_dir);
}
}
if (!jarSourceFiles().isEmpty()) {
web_inf_lib_dir.mkdirs();
for (var file : jarSourceFiles()) {
FileUtils.copy(file.file(), new File(web_inf_lib_dir, file.name()));
}
}
}
/**
* Part of the {@link #execute} operation, copy the staging {@code WEB-INF} classes.
*
* @since 1.5
*/
protected void executeCopyWebInfClassesFiles(File stagingWebInfDirectory)
throws FileUtilsErrorException {
var web_inf_classes_dir = new File(stagingWebInfDirectory, "classes");
if (!classesSourceDirectories().isEmpty()) {
web_inf_classes_dir.mkdirs();
for (var dir : classesSourceDirectories()) {
FileUtils.copyDirectory(dir, web_inf_classes_dir);
}
}
}
/**
* Part of the {@link #execute} operation, copy the staging {@code web.xml} file.
*
* @since 1.5
*/
protected void executeCopyWebXmlFile(File stagingWebInfDirectory)
throws FileUtilsErrorException {
if (webXmlFile() != null) {
FileUtils.copy(webXmlFile(), new File(stagingWebInfDirectory, "web.xml"));
}
}
/**
* Part of the {@link #execute} operation, create the war archive from the staging directory.
*
* @since 1.5
*/
protected void executeCreateWarArchive(File stagingDirectory)
throws IOException {
new JarOperation()
.sourceDirectories(stagingDirectory)
.destinationDirectory(destinationDirectory())
.destinationFileName(destinationFileName())
.excluded(Pattern.compile("(?:(?:^.*[/\\\\])|^)\\.DS_Store$"))
.silent(true)
.execute();
}
/**
* Configures a war operation from a {@link Project}.
*
* @param project the project to configure the war operation from
* @since 1.5
*/
public WarOperation fromProject(WebProject project) {
var jar_source_files = new ArrayList<NamedFile>();
jar_source_files.add(new NamedFile(project.jarFileName(), new File(project.buildDistDirectory(), project.jarFileName())));
var class_path_jars = new ArrayList<File>();
class_path_jars.addAll(project.compileClasspathJars());
class_path_jars.addAll(project.runtimeClasspathJars());
for (var jar_file : class_path_jars) {
jar_source_files.add(new NamedFile(jar_file.getName(), jar_file));
}
return jarSourceFiles(jar_source_files)
.webappDirectory(project.srcMainWebappDirectory())
.destinationDirectory(project.buildDistDirectory())
.destinationFileName(project.warFileName());
}
/**
* Provides lib source directories that will be used for the war archive creation.
*
* @param directories lib source directories
* @return this operation instance
* @since 1.5.18
*/
public WarOperation libSourceDirectories(File... directories) {
libSourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of lib source directories that will be used for the war archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of lib source directories
* @return this operation instance
* @since 1.5
*/
public WarOperation libSourceDirectories(List<File> directories) {
libSourceDirectories_.addAll(directories);
return this;
}
/**
* Provides classes source directories that will be used for the war archive creation.
*
* @param directories classes source directories
* @return this operation instance
* @since 1.5.18
*/
public WarOperation classesSourceDirectories(File... directories) {
classesSourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of classes source directories that will be used for the war archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of classes source directories
* @return this operation instance
* @since 1.5
*/
public WarOperation classesSourceDirectories(List<File> directories) {
classesSourceDirectories_.addAll(directories);
return this;
}
/**
* Provides jar files that will be included in the war archive creation.
*
* @param files jar source directories
* @return this operation instance
* @since 1.5.18
*/
public WarOperation jarSourceFiles(NamedFile... files) {
jarSourceFiles_.addAll(List.of(files));
return this;
}
/**
* Provides a list of jar files that will be included in the war archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of jar source directories
* @return this operation instance
* @since 1.5
*/
public WarOperation jarSourceFiles(List<NamedFile> files) {
jarSourceFiles_.addAll(files);
return this;
}
/**
* Provides web application directory that will provide resources for the war archive creation.
*
* @param directory the webapp directory
* @return this operation instance
* @since 1.5
*/
public WarOperation webappDirectory(File directory) {
webappDirectory_ = directory;
return this;
}
/**
* Provides web.xml file that will be used for the war archive creation.
*
* @param file the web.xml file
* @return this operation instance
* @since 1.5
*/
public WarOperation webXmlFile(File file) {
webXmlFile_ = file;
return this;
}
/**
* Provides the destination directory in which the war archive will be created.
*
* @param directory the war destination directory
* @return this operation instance
* @since 1.5
*/
public WarOperation destinationDirectory(File directory) {
destinationDirectory_ = directory;
return this;
}
/**
* Provides the destination file name that will be used for the war archive creation.
*
* @param name the war archive destination file name
* @return this operation instance
* @since 1.5
*/
public WarOperation destinationFileName(String name) {
destinationFileName_ = name;
return this;
}
/**
* Retrieves the lib source directories that will be used for the war archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the lib source directories
* @since 1.5
*/
public List<File> libSourceDirectories() {
return libSourceDirectories_;
}
/**
* Retrieves the classes source directories that will be used for the war archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the classes source directories
* @since 1.5
*/
public List<File> classesSourceDirectories() {
return classesSourceDirectories_;
}
/**
* Retrieves jar files that will be included in the war archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the jar source directories
* @since 1.5
*/
public List<NamedFile> jarSourceFiles() {
return jarSourceFiles_;
}
/**
* Retrieves web application directory that will provide resources for the war archive creation.
*
* @return the webapp directory
* @since 1.5
*/
public File webappDirectory() {
return webappDirectory_;
}
/**
* Retrieves web.xml file that will be used for the war archive creation.
*
* @return the web.xml file
* @since 1.5
*/
public File webXmlFile() {
return webXmlFile_;
}
/**
* Retrieves the destination directory in which the war archive will
* be created.
*
* @return the war archive's destination directory
* @since 1.5
*/
public File destinationDirectory() {
return destinationDirectory_;
}
/**
* Retrieves the destination file name that will be used for the war
* archive creation.
*
* @return the war archive's destination file name
* @since 1.5
*/
public String destinationFileName() {
return destinationFileName_;
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations.exceptions;
import java.io.Serial;
/**
* When thrown, indicates that the exit status has changed due
* to the operation execution.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class ExitStatusException extends Exception {
public static final int EXIT_SUCCESS = 0;
public static final int EXIT_FAILURE = 1;
@Serial private static final long serialVersionUID = 5474282790384325513L;
private final int exitStatus_;
public ExitStatusException(int exitStatus) {
exitStatus_ = exitStatus;
}
public int getExitStatus() {
return exitStatus_;
}
/**
* Throws an {@code ExitStatusException} when the status is a failure.
*
* @param status the status to check
* @throws ExitStatusException when the provided status is different from {@code EXIT_SUCCESS}
* @since 1.5.7
*/
public static void throwOnFailure(int status)
throws ExitStatusException {
if (status != EXIT_SUCCESS) {
throw new ExitStatusException(status);
}
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations.exceptions;
import java.io.Serial;
/**
* When thrown, indicates that wrong options were provided to the operation.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5
*/
public class OperationOptionException extends RuntimeException {
@Serial private static final long serialVersionUID = 5577728010329494164L;
public OperationOptionException(String message) {
super(message);
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations.exceptions;
import rife.tools.HttpUtils;
import java.io.File;
import java.io.Serial;
/**
* When thrown, indicates that something went wrong during signing
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.19
*/
public class SignException extends RuntimeException {
@Serial private static final long serialVersionUID = -7352738410475855871L;
private final File file_;
private final String reason_;
public SignException(File file, String reason) {
super("An error occurred while signing '" + file + "':\n" + reason);
file_ = file;
reason_ = reason;
}
public File getFile() {
return file_;
}
public String getReason() {
return reason_;
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations.exceptions;
import rife.tools.HttpUtils;
import java.io.Serial;
/**
* When thrown, indicates that something went wrong during upload
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.5.7
*/
public class UploadException extends RuntimeException {
@Serial private static final long serialVersionUID = 8815214756135684019L;
private final String url_;
public UploadException(String url, int status) {
super("An error occurred while uploading to '" + url + "'\nHTTP status code " + status + " : " + HttpUtils.statusReason(status));
url_ = url;
}
public UploadException(String url, Throwable cause) {
super("An error occurred while uploading to '" + url + "'", cause);
url_ = url;
}
public String getUrl() {
return url_;
}
}

Some files were not shown because too many files have changed in this diff Show more