From: Mathieu Baudier Date: Thu, 10 Feb 2022 10:08:43 +0000 (+0100) Subject: Botstrap and build of the base runtime. X-Git-Tag: argeo-slc-2.3.1~14 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=8b6ef8eb76eb746509b2f48f1d927f532d0c986a;p=gpl%2Fargeo-slc.git Botstrap and build of the base runtime. --- diff --git a/Makefile b/Makefile index 599b85de8..0f0c8455c 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,9 @@ A2_CATEGORY = org.argeo.slc BUNDLES = \ org.argeo.slc.api \ org.argeo.slc.factory \ -ext/org.argeo.ext.slf4j +org.argeo.slc.runtime \ +ext/org.argeo.ext.slf4j \ +ext/org.argeo.ext.equinox.jetty \ BUILD_CLASSPATH_FEDORA = \ /usr/share/java/osgi-core/osgi.core.jar:$\ @@ -31,17 +33,9 @@ BUILD_CLASSPATH = \ /usr/share/java/commons-cli.jar:$\ /usr/share/java/commons-exec.jar:$\ -DISTRIBUTION_CLASSPATH = \ -$(SDK_BUILD_BASE)/a2/org.argeo.slc/org.argeo.slc.api.$(MAJOR).$(MINOR).jar:$\ -$(SDK_BUILD_BASE)/a2/org.argeo.slc/org.argeo.slc.factory.$(MAJOR).$(MINOR).jar:$\ -/usr/share/java/bndlib.jar:$\ -/usr/share/java/slf4j-api.jar - # TODO relativize from SDK_SRC_BASE BUILD_BASE = $(SDK_BUILD_BASE) -distribution: osgi - $(JVM) -cp $(DISTRIBUTION_CLASSPATH) tp/Make.java # @@ -49,17 +43,20 @@ distribution: osgi # JVM := /usr/bin/java JAVADOC := /usr/bin/javadoc -ECJ_JAR := /usr/share/java/ecj.jar +ECJ_JAR := $(SDK_BUILD_BASE)/a2/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.3.28.jar BND_TOOL := /usr/bin/bnd WORKSPACE_BNDS := $(shell cd $(SDK_SRC_BASE) && find cnf -name '*.bnd') -#BND_WORKSPACES := $(foreach bundle, $(BUNDLES), ./$(dir $(bundle))) BUILD_WORKSPACE_BNDS := $(WORKSPACE_BNDS:%=$(SDK_BUILD_BASE)/%) $(WORKSPACE_BNDS:%=$(SDK_BUILD_BASE)/ext/%) cnf: $(BUILD_WORKSPACE_BNDS) A2_BUNDLES = $(BUNDLES:%=$(SDK_BUILD_BASE)/a2/$(A2_CATEGORY)/%.$(MAJOR).$(MINOR).jar) +A2_JARS = $(shell find $(SDK_BUILD_BASE)/a2 -name '*.jar') +A2_CLASSPATH = $(subst $(space),$(pathsep),$(strip $(A2_JARS))) + + #JAVA_SRCS = $(shell find $(BUNDLE_PREFIX).* -name '*.java') JAVA_SRCS = $(foreach bundle, $(BUNDLES), $(shell find $(bundle) -name '*.java')) ECJ_SRCS = $(foreach bundle, $(BUNDLES), $(bundle)/src[-d $(BUILD_BASE)/$(bundle)/bin]) @@ -67,8 +64,24 @@ ECJ_SRCS = $(foreach bundle, $(BUNDLES), $(bundle)/src[-d $(BUILD_BASE)/$(bundle osgi: cnf $(A2_BUNDLES) mkdir -p $(SDK_BUILD_BASE)/a2/org.argeo.tp mv $(SDK_BUILD_BASE)/a2/$(A2_CATEGORY)/ext/org.argeo.ext.slf4j.$(MAJOR).$(MINOR).jar $(SDK_BUILD_BASE)/a2/org.argeo.tp + mv $(SDK_BUILD_BASE)/a2/$(A2_CATEGORY)/ext/org.argeo.ext.equinox.jetty.$(MAJOR).$(MINOR).jar $(SDK_BUILD_BASE)/a2/org.argeo.tp.eclipse.equinox rmdir $(SDK_BUILD_BASE)/a2/$(A2_CATEGORY)/ext +distribution: bootstrap + $(JVM) -cp \ + $(SDK_BUILD_BASE)/bootstrap/bndlib.jar:$(SDK_BUILD_BASE)/bootstrap/slf4j-api.jar:$(SDK_BUILD_BASE)/org.argeo.slc.api/bin:$(SDK_BUILD_BASE)/org.argeo.slc.factory/bin \ + tp/Make.java + +bootstrap : + mkdir -p $(SDK_BUILD_BASE)/bootstrap + wget -c -O $(SDK_BUILD_BASE)/bootstrap/ecj.jar https://repo1.maven.org/maven2/org/eclipse/jdt/ecj/3.28.0/ecj-3.28.0.jar + wget -c -O $(SDK_BUILD_BASE)/bootstrap/slf4j-api.jar https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.28/slf4j-api-1.7.28.jar + wget -c -O $(SDK_BUILD_BASE)/bootstrap/bndlib.jar https://repo1.maven.org/maven2/biz/aQute/bnd/biz.aQute.bndlib/5.3.0/biz.aQute.bndlib-5.3.0.jar + $(JVM) -cp $(SDK_BUILD_BASE)/bootstrap/ecj.jar org.eclipse.jdt.internal.compiler.batch.Main -11 -nowarn -time -cp \ + $(SDK_BUILD_BASE)/bootstrap/bndlib.jar:$(SDK_BUILD_BASE)/bootstrap/slf4j.jar \ + org.argeo.slc.api/src[-d $(SDK_BUILD_BASE)/org.argeo.slc.api/bin] \ + org.argeo.slc.factory/src[-d $(SDK_BUILD_BASE)/org.argeo.slc.factory/bin] \ + clean: rm -rf $(BUILD_BASE)/*-compiled rm -rf $(BUILD_BASE)/cnf @@ -77,6 +90,7 @@ clean: rm -rf $(BUILD_BASE)/ext rm -rf $(BUILD_BASE)/build rm -rf $(BUILD_BASE)/deb + rm -rf $(BUILD_BASE)/bootstrap # SDK level $(SDK_BUILD_BASE)/cnf/%.bnd: cnf/%.bnd @@ -102,7 +116,7 @@ $(BUILD_BASE)/%/bundle.jar : %/bnd.bnd $(BUILD_BASE)/java-compiled mv $(dir $@)generated/*.jar $(dir $@)bundle.jar $(BUILD_BASE)/java-compiled : $(JAVA_SRCS) - $(JVM) -cp $(ECJ_JAR) org.eclipse.jdt.internal.compiler.batch.Main -11 -nowarn -time -cp $(BUILD_CLASSPATH) \ + $(JVM) -jar $(ECJ_JAR) -11 -nowarn -time -cp $(A2_CLASSPATH) \ $(ECJ_SRCS) touch $@ @@ -110,5 +124,4 @@ null := space := $(null) # pathsep := : -#WITH_LIST := $(subst $(space),$(pathsep),$(strip $(WITH_LIST))) diff --git a/org.argeo.slc.runtime/ext/test/org/argeo/fs/FsUtilsTest.java b/org.argeo.slc.runtime/ext/test/org/argeo/fs/FsUtilsTest.java index 587efd7ab..5d31f4aa1 100644 --- a/org.argeo.slc.runtime/ext/test/org/argeo/fs/FsUtilsTest.java +++ b/org.argeo.slc.runtime/ext/test/org/argeo/fs/FsUtilsTest.java @@ -5,7 +5,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import org.argeo.util.FsUtils; +import org.argeo.slc.sync.FsSyncUtils; /** {@link FsUtils} tests. */ public class FsUtilsTest { @@ -16,26 +16,26 @@ public class FsUtilsTest { public void testDelete() throws IOException { Path dir = createDir00(); assert Files.exists(dir); - FsUtils.delete(dir); + FsSyncUtils.delete(dir); assert !Files.exists(dir); } public void testSync() throws IOException { Path source = createDir00(); Path target = Files.createTempDirectory(getClass().getName()); - FsUtils.sync(source, target); + FsSyncUtils.sync(source, target); assert Files.exists(target.resolve(FILE00)); assert Files.exists(target.resolve(SUB_DIR)); assert Files.exists(target.resolve(SUB_DIR + File.separator + FILE01)); - FsUtils.delete(source.resolve(SUB_DIR)); - FsUtils.sync(source, target, true); + FsSyncUtils.delete(source.resolve(SUB_DIR)); + FsSyncUtils.sync(source, target, true); assert Files.exists(target.resolve(FILE00)); assert !Files.exists(target.resolve(SUB_DIR)); assert !Files.exists(target.resolve(SUB_DIR + File.separator + FILE01)); // clean up - FsUtils.delete(source); - FsUtils.delete(target); + FsSyncUtils.delete(source); + FsSyncUtils.delete(target); } diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/ArgeoCli.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/ArgeoCli.java deleted file mode 100644 index b573a67b5..000000000 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/ArgeoCli.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.slc.cli; - -import org.apache.commons.cli.Option; -import org.argeo.cms.cli.CommandsCli; -import org.argeo.slc.cli.fs.FsCommands; -import org.argeo.slc.cli.posix.PosixCommands; - -/** Argeo command line tools. */ -public class ArgeoCli extends CommandsCli { - - public ArgeoCli(String commandName) { - super(commandName); - // Common options - options.addOption(Option.builder("v").hasArg().argName("verbose").desc("verbosity").build()); - options.addOption( - Option.builder("D").hasArgs().argName("property=value").desc("use value for given property").build()); - - addCommandsCli(new PosixCommands("posix")); - addCommandsCli(new FsCommands("fs")); -// addCommandsCli(new JcrCommands("jcr")); - } - - @Override - public String getDescription() { - return "Argeo command line utilities"; - } - - public static void main(String[] args) { - mainImpl(new ArgeoCli("argeo"), args); - } - -} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandArgsException.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandArgsException.java new file mode 100644 index 000000000..4d4d759d2 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandArgsException.java @@ -0,0 +1,32 @@ +package org.argeo.slc.cli; + +public class CommandArgsException extends IllegalArgumentException { + private static final long serialVersionUID = -7271050747105253935L; + private String commandName; + private volatile CommandsCli commandsCli; + + public CommandArgsException(Exception cause) { + super(cause.getMessage(), cause); + } + + public CommandArgsException(String message) { + super(message); + } + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public CommandsCli getCommandsCli() { + return commandsCli; + } + + public void setCommandsCli(CommandsCli commandsCli) { + this.commandsCli = commandsCli; + } + +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandRuntimeException.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandRuntimeException.java new file mode 100644 index 000000000..907254456 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandRuntimeException.java @@ -0,0 +1,35 @@ +package org.argeo.slc.cli; + +import java.util.List; + +/** {@link RuntimeException} referring during a command run. */ +public class CommandRuntimeException extends RuntimeException { + private static final long serialVersionUID = 5595999301269377128L; + + private final DescribedCommand command; + private final List arguments; + + public CommandRuntimeException(Throwable e, DescribedCommand command, List arguments) { + this(null, e, command, arguments); + } + + public CommandRuntimeException(String message, DescribedCommand command, List arguments) { + this(message, null, command, arguments); + } + + public CommandRuntimeException(String message, Throwable e, DescribedCommand command, List arguments) { + super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")" + : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e); + this.command = command; + this.arguments = arguments; + } + + public DescribedCommand getCommand() { + return command; + } + + public List getArguments() { + return arguments; + } + +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandsCli.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandsCli.java new file mode 100644 index 000000000..5146fb731 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandsCli.java @@ -0,0 +1,131 @@ +package org.argeo.slc.cli; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Function; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** Base class for a CLI managing sub commands. */ +public abstract class CommandsCli implements DescribedCommand { + public final static String HELP = "help"; + + private final String commandName; + private Map, ?>> commands = new TreeMap<>(); + + protected final Options options = new Options(); + + public CommandsCli(String commandName) { + this.commandName = commandName; + } + + @Override + public Object apply(List args) { + String cmd = null; + List newArgs = new ArrayList<>(); + try { + CommandLineParser clParser = new DefaultParser(); + CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true); + List leftOvers = commonCl.getArgList(); + for (String arg : leftOvers) { + if (!arg.startsWith("-") && cmd == null) { + cmd = arg; + } else { + newArgs.add(arg); + } + } + } catch (ParseException e) { + CommandArgsException cae = new CommandArgsException(e); + throw cae; + } + + Function, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand(); + if (function == null) + throw new IllegalArgumentException("Uknown command " + cmd); + try { + return function.apply(newArgs).toString(); + } catch (CommandArgsException e) { + if (e.getCommandName() == null) { + e.setCommandName(cmd); + e.setCommandsCli(this); + } + throw e; + } catch (IllegalArgumentException e) { + CommandArgsException cae = new CommandArgsException(e); + cae.setCommandName(cmd); + throw cae; + } + } + + @Override + public Options getOptions() { + return options; + } + + protected void addCommand(String cmd, Function, ?> function) { + commands.put(cmd, function); + + } + + @Override + public String getUsage() { + return "[command]"; + } + + protected void addCommandsCli(CommandsCli commandsCli) { + addCommand(commandsCli.getCommandName(), commandsCli); + commandsCli.addCommand(HELP, new HelpCommand(this, commandsCli)); + } + + public String getCommandName() { + return commandName; + } + + public Set getSubCommands() { + return commands.keySet(); + } + + public Function, ?> getCommand(String command) { + return commands.get(command); + } + + public HelpCommand getHelpCommand() { + return (HelpCommand) getCommand(HELP); + } + + public Function, String> getDefaultCommand() { + return getHelpCommand(); + } + + /** In order to implement quickly a main method. */ + public static void mainImpl(CommandsCli cli, String[] args) { + try { + cli.addCommand(CommandsCli.HELP, new HelpCommand(null, cli)); + Object output = cli.apply(Arrays.asList(args)); + System.out.println(output); + System.exit(0); + } catch (CommandArgsException e) { + System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage()); + if (e.getCommandName() != null) { + StringWriter out = new StringWriter(); + HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out); + System.err.println(out.toString()); + } else { + e.printStackTrace(); + } + System.exit(1); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/DescribedCommand.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/DescribedCommand.java new file mode 100644 index 000000000..fc1b13947 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/DescribedCommand.java @@ -0,0 +1,55 @@ +package org.argeo.slc.cli; + +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** A command that can be described. */ +public interface DescribedCommand extends Function, T> { + default Options getOptions() { + return new Options(); + } + + String getDescription(); + + default String getUsage() { + return null; + } + + default String getExamples() { + return null; + } + + default CommandLine toCommandLine(List args) { + try { + DefaultParser parser = new DefaultParser(); + return parser.parse(getOptions(), args.toArray(new String[args.size()])); + } catch (ParseException e) { + throw new CommandArgsException(e); + } + } + + /** In order to implement quickly a main method. */ + public static void mainImpl(DescribedCommand command, String[] args) { + try { + Object output = command.apply(Arrays.asList(args)); + System.out.println(output); + System.exit(0); + } catch (IllegalArgumentException e) { + StringWriter out = new StringWriter(); + HelpCommand.printHelp(command, out); + System.err.println(out.toString()); + System.exit(1); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/HelpCommand.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/HelpCommand.java new file mode 100644 index 000000000..8400d0502 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/HelpCommand.java @@ -0,0 +1,143 @@ +package org.argeo.slc.cli; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.List; +import java.util.function.Function; + +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; + +/** A special command that can describe {@link DescribedCommand}. */ +public class HelpCommand implements DescribedCommand { + private CommandsCli commandsCli; + private CommandsCli parentCommandsCli; + + // Help formatting + private static int helpWidth = 80; + private static int helpLeftPad = 4; + private static int helpDescPad = 20; + + public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) { + super(); + this.parentCommandsCli = parentCommandsCli; + this.commandsCli = commandsCli; + } + + @Override + public String apply(List args) { + StringWriter out = new StringWriter(); + + if (args.size() == 0) {// overview + printHelp(commandsCli, out); + } else { + String cmd = args.get(0); + Function, ?> function = commandsCli.getCommand(cmd); + if (function == null) + return "Command " + cmd + " not found."; + Options options; + String examples; + DescribedCommand command = null; + if (function instanceof DescribedCommand) { + command = (DescribedCommand) function; + options = command.getOptions(); + examples = command.getExamples(); + } else { + options = new Options(); + examples = null; + } + String description = getShortDescription(function); + String commandCall = getCommandUsage(cmd, command); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad, + helpDescPad, examples, false); + } + return out.toString(); + } + + private static String getShortDescription(Function, ?> function) { + if (function instanceof DescribedCommand) { + return ((DescribedCommand) function).getDescription(); + } else { + return function.toString(); + } + } + + public String getCommandUsage(String cmd, DescribedCommand command) { + String commandCall = getCommandCall(commandsCli) + " " + cmd; + assert command != null; + if (command != null && command.getUsage() != null) { + commandCall = commandCall + " " + command.getUsage(); + } + return commandCall; + } + + @Override + public String getDescription() { + return "Shows this help or describes a command"; + } + + @Override + public String getUsage() { + return "[command]"; + } + + public CommandsCli getParentCommandsCli() { + return parentCommandsCli; + } + + protected String getCommandCall(CommandsCli commandsCli) { + HelpCommand hc = commandsCli.getHelpCommand(); + if (hc.getParentCommandsCli() != null) { + return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName(); + } else { + return commandsCli.getCommandName(); + } + } + + public static void printHelp(DescribedCommand command, StringWriter out) { + String usage = "java " + command.getClass().getName() + + (command.getUsage() != null ? " " + command.getUsage() : ""); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(), + helpLeftPad, helpDescPad, command.getExamples(), false); + + } + + public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) { + DescribedCommand command = (DescribedCommand) commandsCli.getCommand(commandName); + String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(), + helpLeftPad, helpDescPad, command.getExamples(), false); + + } + + public static void printHelp(CommandsCli commandsCli, StringWriter out) { + out.append(commandsCli.getDescription()).append('\n'); + String leftPad = spaces(helpLeftPad); + for (String cmd : commandsCli.getSubCommands()) { + Function, ?> function = commandsCli.getCommand(cmd); + assert function != null; + out.append(leftPad); + out.append(cmd); + // TODO deal with long commands + out.append(spaces(helpDescPad - cmd.length())); + out.append(getShortDescription(function)); + out.append('\n'); + } + } + + private static String spaces(int count) { + // Java 11 + // return " ".repeat(count); + if (count <= 0) + return ""; + else { + StringBuilder sb = new StringBuilder(count); + for (int i = 0; i < count; i++) + sb.append(' '); + return sb.toString(); + } + } +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FileSync.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FileSync.java index 6b8cbfa72..dfff27ce6 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FileSync.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FileSync.java @@ -9,9 +9,9 @@ import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.argeo.cms.cli.CommandArgsException; -import org.argeo.cms.cli.DescribedCommand; -import org.argeo.util.SyncResult; +import org.argeo.slc.cli.CommandArgsException; +import org.argeo.slc.cli.DescribedCommand; +import org.argeo.slc.sync.SyncResult; public class FileSync implements DescribedCommand> { final static Option deleteOption = Option.builder().longOpt("delete").desc("delete from target").build(); diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FsCommands.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FsCommands.java index 6df81f844..cab3c1e2b 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FsCommands.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FsCommands.java @@ -1,6 +1,6 @@ package org.argeo.slc.cli.fs; -import org.argeo.cms.cli.CommandsCli; +import org.argeo.slc.cli.CommandsCli; /** File utilities. */ public class FsCommands extends CommandsCli { diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/PathSync.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/PathSync.java index abd0a20d5..844af0154 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/PathSync.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/PathSync.java @@ -8,7 +8,7 @@ import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import java.util.concurrent.Callable; -import org.argeo.util.SyncResult; +import org.argeo.slc.sync.SyncResult; /** Synchronises two paths. */ public class PathSync implements Callable> { diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/SyncFileVisitor.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/SyncFileVisitor.java index 712ec5f6b..43af11144 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/SyncFileVisitor.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/SyncFileVisitor.java @@ -1,13 +1,15 @@ package org.argeo.slc.cli.fs; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.nio.file.Path; +import java.util.Objects; -import org.argeo.api.cms.CmsLog; -import org.argeo.util.BasicSyncFileVisitor; +import org.argeo.slc.sync.BasicSyncFileVisitor; /** Synchronises two directory structures. */ public class SyncFileVisitor extends BasicSyncFileVisitor { - private final static CmsLog log = CmsLog.getLog(SyncFileVisitor.class); + private final static Logger logger = System.getLogger(SyncFileVisitor.class.getName()); public SyncFileVisitor(Path sourceBasePath, Path targetBasePath, boolean delete, boolean recursive) { super(sourceBasePath, targetBasePath, delete, recursive); @@ -15,16 +17,16 @@ public class SyncFileVisitor extends BasicSyncFileVisitor { @Override protected void error(Object obj, Throwable e) { - log.error(obj, e); + logger.log(Level.ERROR, Objects.toString(obj), e); } @Override protected boolean isTraceEnabled() { - return log.isTraceEnabled(); + return logger.isLoggable(Level.TRACE); } @Override protected void trace(Object obj) { - log.trace(obj); + logger.log(Level.TRACE, Objects.toString(obj)); } } diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/package-info.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/package-info.java new file mode 100644 index 000000000..0216a288d --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/package-info.java @@ -0,0 +1,2 @@ +/** Command line API. */ +package org.argeo.slc.cli; \ No newline at end of file diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/Echo.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/Echo.java index d773760bc..a479e96d1 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/Echo.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/Echo.java @@ -5,7 +5,7 @@ import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.argeo.cms.cli.DescribedCommand; +import org.argeo.slc.cli.DescribedCommand; public class Echo implements DescribedCommand { diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/PosixCommands.java b/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/PosixCommands.java index bb5920e55..67544108d 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/PosixCommands.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/PosixCommands.java @@ -1,6 +1,6 @@ package org.argeo.slc.cli.posix; -import org.argeo.cms.cli.CommandsCli; +import org.argeo.slc.cli.CommandsCli; /** POSIX commands. */ public class PosixCommands extends CommandsCli { diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/AbstractExecutionModulesManager.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/AbstractExecutionModulesManager.java index ff69dda25..149665cdf 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/AbstractExecutionModulesManager.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/AbstractExecutionModulesManager.java @@ -2,7 +2,6 @@ package org.argeo.slc.runtime; import java.util.Map; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.execution.ExecutionContext; import org.argeo.slc.execution.ExecutionFlow; import org.argeo.slc.execution.ExecutionFlowDescriptorConverter; @@ -12,8 +11,6 @@ import org.argeo.slc.execution.RealizedFlow; /** Provides the base feature of an execution module manager. */ public abstract class AbstractExecutionModulesManager implements ExecutionModulesManager { - private final static CmsLog log = CmsLog - .getLog(AbstractExecutionModulesManager.class); // private List filteredNotifiers = Collections // .synchronizedList(new ArrayList()); @@ -28,9 +25,6 @@ public abstract class AbstractExecutionModulesManager implements String moduleName, String moduleVersion); public void execute(RealizedFlow realizedFlow) { - if (log.isTraceEnabled()) - log.trace("Executing " + realizedFlow); - String moduleName = realizedFlow.getModuleName(); String moduleVersion = realizedFlow.getModuleVersion(); diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ArgeoCli.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ArgeoCli.java new file mode 100644 index 000000000..40470fea0 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ArgeoCli.java @@ -0,0 +1,32 @@ +package org.argeo.slc.runtime; + +import org.apache.commons.cli.Option; +import org.argeo.slc.cli.CommandsCli; +import org.argeo.slc.cli.fs.FsCommands; +import org.argeo.slc.cli.posix.PosixCommands; + +/** Argeo command line tools. */ +public class ArgeoCli extends CommandsCli { + + public ArgeoCli(String commandName) { + super(commandName); + // Common options + options.addOption(Option.builder("v").hasArg().argName("verbose").desc("verbosity").build()); + options.addOption( + Option.builder("D").hasArgs().argName("property=value").desc("use value for given property").build()); + + addCommandsCli(new PosixCommands("posix")); + addCommandsCli(new FsCommands("fs")); +// addCommandsCli(new JcrCommands("jcr")); + } + + @Override + public String getDescription() { + return "Argeo command line utilities"; + } + + public static void main(String[] args) { + mainImpl(new ArgeoCli("argeo"), args); + } + +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultAgentCli.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultAgentCli.java index 11a79d07f..dbe1d7f6e 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultAgentCli.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultAgentCli.java @@ -8,7 +8,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.DefaultNameVersion; import org.argeo.slc.NameVersion; import org.argeo.slc.SlcException; @@ -24,8 +23,6 @@ import org.argeo.slc.execution.SlcAgentCli; * Reference implementation of args to URIs algorithm. */ public class DefaultAgentCli implements SlcAgentCli { - private final static CmsLog log = CmsLog.getLog(DefaultAgentCli.class); - private final static String UTF8 = "UTF-8"; private SlcAgent agent; // private AuthenticationManager authenticationManager; @@ -44,7 +41,7 @@ public class DefaultAgentCli implements SlcAgentCli { if (args.length > 0 && args[0].equals("help")) { StringBuilder buf = new StringBuilder(); help(args, buf); - log.info("\n" + buf); +// log.info("\n" + buf); return buf.toString(); } else { List uris = asURIs(args); diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java index 7726177cc..9e31306ad 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java @@ -1,12 +1,15 @@ package org.argeo.slc.runtime; +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.WARNING; + +import java.lang.System.Logger.Level; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; import org.argeo.slc.execution.ExecutionContext; import org.argeo.slc.execution.ExecutionFlow; @@ -15,7 +18,7 @@ import org.argeo.slc.execution.ExecutionSpecAttribute; /** Default implementation of an execution flow. */ public class DefaultExecutionFlow implements ExecutionFlow { - private final static CmsLog log = CmsLog.getLog(DefaultExecutionFlow.class); + private final static System.Logger logger = System.getLogger(DefaultExecutionFlow.class.getName()); private final ExecutionSpec executionSpec; private String name = null; @@ -83,7 +86,7 @@ public class DefaultExecutionFlow implements ExecutionFlow { try { for (Runnable executable : executables) { if (Thread.interrupted()) { - log.error("Flow '" + getName() + "' killed before '" + executable + "'"); + logger.log(ERROR, "Flow '" + getName() + "' killed before '" + executable + "'"); Thread.currentThread().interrupt(); return; // throw new ThreadDeath(); @@ -92,7 +95,7 @@ public class DefaultExecutionFlow implements ExecutionFlow { } } catch (RuntimeException e) { if (Thread.interrupted()) { - log.error("Flow '" + getName() + "' killed while receiving an unrelated exception", e); + logger.log(ERROR, "Flow '" + getName() + "' killed while receiving an unrelated exception", e); Thread.currentThread().interrupt(); return; // throw new ThreadDeath(); @@ -100,9 +103,9 @@ public class DefaultExecutionFlow implements ExecutionFlow { if (failOnError) throw e; else { - log.error("Execution flow failed," + " but process did not fail" + " because failOnError property" - + " is set to false: " + e); - if (log.isTraceEnabled()) + logger.log(ERROR, "Execution flow failed," + " but process did not fail" + + " because failOnError property" + " is set to false: " + e); + if (logger.isLoggable(Level.TRACE)) e.printStackTrace(); } } @@ -155,7 +158,7 @@ public class DefaultExecutionFlow implements ExecutionFlow { DefaultExecutionFlow flow = (DefaultExecutionFlow) executable; String newPath = path + '/' + flow.getName(); flow.setPath(newPath); - log.warn(newPath + " was forcibly set on " + flow); + logger.log(WARNING, newPath + " was forcibly set on " + flow); } } } diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionStack.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionStack.java index 9b5460bd9..b30d51788 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionStack.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionStack.java @@ -5,7 +5,6 @@ import java.util.Map; import java.util.Stack; import java.util.UUID; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; import org.argeo.slc.execution.ExecutionFlow; import org.argeo.slc.execution.ExecutionSpecAttribute; @@ -13,22 +12,16 @@ import org.argeo.slc.execution.ExecutionStack; /** Canonical implementation of an execution stack. */ public class DefaultExecutionStack implements ExecutionStack { - - private final static CmsLog log = CmsLog - .getLog(DefaultExecutionStack.class); - private final Stack stack = new Stack(); public synchronized void enterFlow(ExecutionFlow executionFlow) { ExecutionFlowRuntime runtime = new ExecutionFlowRuntime(executionFlow); stack.push(runtime); - Map specAttrs = executionFlow - .getExecutionSpec().getAttributes(); + Map specAttrs = executionFlow.getExecutionSpec().getAttributes(); for (String key : specAttrs.keySet()) { if (executionFlow.isSetAsParameter(key)) { - runtime.getLocalVariables().put(key, - executionFlow.getParameter(key)); + runtime.getLocalVariables().put(key, executionFlow.getParameter(key)); } } } @@ -60,10 +53,8 @@ public class DefaultExecutionStack implements ExecutionStack { public synchronized void leaveFlow(ExecutionFlow executionFlow) { ExecutionFlowRuntime leftEf = stack.pop(); - if (!leftEf.getExecutionFlow().getName() - .equals(executionFlow.getName())) - throw new SlcException("Asked to leave " + executionFlow - + " but last is " + leftEf); + if (!leftEf.getExecutionFlow().getName().equals(executionFlow.getName())) + throw new SlcException("Asked to leave " + executionFlow + " but last is " + leftEf); leftEf.getScopedObjects().clear(); leftEf.getLocalVariables().clear(); @@ -72,12 +63,12 @@ public class DefaultExecutionStack implements ExecutionStack { public synchronized void addScopedObject(String name, Object obj) { ExecutionFlowRuntime runtime = stack.peek(); // TODO: check that the object is not set yet ? - if (log.isDebugEnabled()) { - Object existing = findScopedObject(name); - if (existing != null) - log.warn("Scoped object " + name + " of type " + obj.getClass() - + " already registered in " + runtime); - } +// if (log.isDebugEnabled()) { +// Object existing = findScopedObject(name); +// if (existing != null) +// log.warn("Scoped object " + name + " of type " + obj.getClass() +// + " already registered in " + runtime); +// } runtime.getScopedObjects().put(name, obj); } diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ExecutionThread.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ExecutionThread.java index c5be2cf0a..7e0e73778 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ExecutionThread.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ExecutionThread.java @@ -1,5 +1,8 @@ package org.argeo.slc.runtime; +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.WARNING; + import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedActionException; @@ -9,7 +12,6 @@ import java.util.List; import javax.security.auth.Subject; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.execution.ExecutionFlowDescriptor; import org.argeo.slc.execution.ExecutionModulesManager; import org.argeo.slc.execution.ExecutionStep; @@ -19,7 +21,7 @@ import org.argeo.slc.execution.RealizedFlow; /** Thread of a single execution */ public class ExecutionThread extends Thread { public final static String SYSPROP_EXECUTION_AUTO_UPGRADE = "slc.execution.autoupgrade"; - private final static CmsLog log = CmsLog.getLog(ExecutionThread.class); + private final static System.Logger logger = System.getLogger(ExecutionThread.class.getName()); private ExecutionModulesManager executionModulesManager; private final RealizedFlow realizedFlow; @@ -75,13 +77,13 @@ public class ExecutionThread extends Thread { } } catch (FlowConfigurationException e) { String msg = "Configuration problem with flow " + flowName + ":\n" + e.getMessage(); - log.error(msg); + logger.log(ERROR, msg); getProcessThreadGroup().dispatchAddStep( new ExecutionStep(realizedFlow.getModuleName(), ExecutionStep.ERROR, msg + " " + e.getMessage())); } catch (Exception e) { // TODO: re-throw exception ? String msg = "Execution of flow " + flowName + " failed."; - log.error(msg, e); + logger.log(ERROR, msg, e); getProcessThreadGroup().dispatchAddStep( new ExecutionStep(realizedFlow.getModuleName(), ExecutionStep.ERROR, msg + " " + e.getMessage())); } finally { @@ -96,14 +98,14 @@ public class ExecutionThread extends Thread { try { destructionCallbacks.get(i).run(); } catch (Exception e) { - log.warn("Could not process destruction callback " + i + " in thread " + getName(), e); + logger.log(WARNING, "Could not process destruction callback " + i + " in thread " + getName(), e); } } } /** - * Gather object destruction callback to be called in reverse order at the - * end of the thread + * Gather object destruction callback to be called in reverse order at the end + * of the thread */ public synchronized void registerDestructionCallback(String name, Runnable callback) { destructionCallbacks.add(callback); diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/InstantiationManager.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/InstantiationManager.java index ecac292e2..2e29438e3 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/InstantiationManager.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/InstantiationManager.java @@ -1,8 +1,8 @@ package org.argeo.slc.runtime; +import java.lang.System.Logger.Level; import java.util.Stack; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; import org.argeo.slc.execution.ExecutionFlow; import org.argeo.slc.execution.ExecutionSpecAttribute; @@ -13,16 +13,15 @@ import org.argeo.slc.primitive.PrimitiveUtils; /** Manage parameters that need to be set during the instantiation of a flow */ public class InstantiationManager { - private final static CmsLog log = CmsLog - .getLog(InstantiationManager.class); + private final static System.Logger logger = System.getLogger(InstantiationManager.class.getName()); private ThreadLocal> flowStack = new ThreadLocal>(); public Object createRef(String name) { if ((flowStack.get() == null) || flowStack.get().empty()) { - throw new SlcException("No flow is currently initializing." - + " Declare ParameterRef as inner beans or prototypes."); + throw new SlcException( + "No flow is currently initializing." + " Declare ParameterRef as inner beans or prototypes."); } return getInitializingFlowParameter(name); @@ -34,9 +33,8 @@ public class InstantiationManager { ((DefaultExecutionFlow) flow).setName(flowName); } - if (log.isTraceEnabled()) - log.trace("Start initialization of " + flow.hashCode() + " (" - + flow + " - " + flow.getClass() + ")"); + logger.log(Level.TRACE, + () -> "Start initialization of " + flow.hashCode() + " (" + flow + " - " + flow.getClass() + ")"); // log.info("# flowInitializationStarted " + flowName); // create a stack for this thread if there is none @@ -47,9 +45,8 @@ public class InstantiationManager { } public void flowInitializationFinished(ExecutionFlow flow, String flowName) { - if (log.isTraceEnabled()) - log.trace("Finish initialization of " + flow.hashCode() + " (" - + flow + " - " + flow.getClass() + ")"); + logger.log(Level.TRACE, + () -> "Finish initialization of " + flow.hashCode() + " (" + flow + " - " + flow.getClass() + ")"); if (flowStack.get() != null) { ExecutionFlow registeredFlow = flowStack.get().pop(); @@ -61,7 +58,7 @@ public class InstantiationManager { } } else { // happens for flows imported as services - log.warn("flowInitializationFinished - Flow Stack is null"); + logger.log(Level.WARNING, "flowInitializationFinished - Flow Stack is null"); } } @@ -75,9 +72,8 @@ public class InstantiationManager { return flowStack.get().elementAt(i); } } - throw new SlcException("Key " + key + " is not set as parameter in " - + flowStack.get().firstElement().toString() + " (stack size=" - + flowStack.get().size() + ")"); + throw new SlcException("Key " + key + " is not set as parameter in " + flowStack.get().firstElement().toString() + + " (stack size=" + flowStack.get().size() + ")"); } @@ -86,16 +82,15 @@ public class InstantiationManager { } public Class getInitializingFlowParameterClass(String key) { - ExecutionSpecAttribute attr = findInitializingFlowWithParameter(key) - .getExecutionSpec().getAttributes().get(key); + ExecutionSpecAttribute attr = findInitializingFlowWithParameter(key).getExecutionSpec().getAttributes() + .get(key); if (attr instanceof RefSpecAttribute) return ((RefSpecAttribute) attr).getTargetClass(); else if (attr instanceof PrimitiveSpecAttribute) { String type = ((PrimitiveSpecAttribute) attr).getType(); Class clss = PrimitiveUtils.typeAsClass(type); if (clss == null) - throw new SlcException("Cannot convert type " + type - + " to class."); + throw new SlcException("Cannot convert type " + type + " to class."); return clss; } else return null; diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ProcessThread.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ProcessThread.java index 97f83e2f9..33ea9f07a 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ProcessThread.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/ProcessThread.java @@ -1,5 +1,6 @@ package org.argeo.slc.runtime; +import java.lang.System.Logger.Level; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedActionException; @@ -12,7 +13,6 @@ import java.util.Set; import javax.security.auth.Subject; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; import org.argeo.slc.execution.ExecutionModulesManager; import org.argeo.slc.execution.ExecutionProcess; @@ -24,7 +24,7 @@ import org.argeo.slc.execution.RealizedFlow; * sequential {@link ExecutionThread}s. */ public class ProcessThread extends Thread { - private final static CmsLog log = CmsLog.getLog(ProcessThread.class); + private final static System.Logger logger = System.getLogger(ProcessThread.class.getName()); private final ExecutionModulesManager executionModulesManager; private final ExecutionProcess process; @@ -54,7 +54,7 @@ public class ProcessThread extends Thread { // throw new SlcException("Can only execute authenticated threads"); // SecurityContextHolder.getContext().setAuthentication(authentication); - log.info("\n##\n## SLC Process #" + process.getUuid() + " STARTED\n##\n"); + logger.log(Level.INFO, "\n##\n## SLC Process #" + process.getUuid() + " STARTED\n##\n"); // Start logging new LoggingThread().start(); @@ -85,7 +85,7 @@ public class ProcessThread extends Thread { return; } catch (Exception e) { String msg = "Process " + getProcess().getUuid() + " failed unexpectedly."; - log.error(msg, e); + logger.log(Level.ERROR, msg, e); getProcessThreadGroup() .dispatchAddStep(new ExecutionStep("Process", ExecutionStep.ERROR, msg + " " + e.getMessage())); } @@ -118,7 +118,7 @@ public class ProcessThread extends Thread { process.setStatus(ExecutionProcess.COMPLETED); // executionModulesManager.dispatchUpdateStatus(process, oldStatus, // process.getStatus()); - log.info("\n## SLC Process #" + process.getUuid() + " " + process.getStatus() + "\n"); + logger.log(Level.INFO, "\n## SLC Process #" + process.getUuid() + " " + process.getStatus() + "\n"); } /** Called when being killed */ @@ -129,7 +129,7 @@ public class ProcessThread extends Thread { try { executionThread.interrupt(); } catch (Exception e) { - log.error("Cannot interrupt " + executionThread); + logger.log(Level.ERROR, "Cannot interrupt " + executionThread); } } processThreadGroup.interrupt(); diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/Echo.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/Echo.java index 778550aa2..6695313a3 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/Echo.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/Echo.java @@ -2,27 +2,28 @@ package org.argeo.slc.runtime.tasks; import java.io.File; import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.nio.file.Path; import org.apache.commons.io.FileUtils; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; public class Echo implements Runnable { - private final static CmsLog defaultLog = CmsLog.getLog(Echo.class); + private final static Logger defaultLogger = System.getLogger(Echo.class.getName()); private Path writeTo = null; - private CmsLog log; + private Logger log; private Object message; public void run() { - log().info(message); + log().log(Level.INFO, message); if (writeTo != null) { try { File file = writeTo.toFile(); - if (log().isDebugEnabled()) - log().debug("Write to " + file); + + log().log(Level.DEBUG, () -> "Write to " + file); if (message != null) FileUtils.writeStringToFile(file, message.toString()); } catch (IOException e) { @@ -31,8 +32,8 @@ public class Echo implements Runnable { } } - private CmsLog log() { - return log != null ? log : defaultLog; + private Logger log() { + return log != null ? log : defaultLogger; } public void setMessage(Object message) { diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/SystemCall.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/SystemCall.java index 5b9f9dce0..d421cd2f2 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/SystemCall.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/SystemCall.java @@ -1,5 +1,11 @@ package org.argeo.slc.runtime.tasks; +import static java.lang.System.Logger.Level.DEBUG; +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.INFO; +import static java.lang.System.Logger.Level.TRACE; +import static java.lang.System.Logger.Level.WARNING; + import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; @@ -9,6 +15,7 @@ import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.Writer; +import java.lang.System.Logger; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -32,7 +39,6 @@ import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.exec.ShutdownHookProcessDestroyer; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; import org.argeo.slc.UnsupportedException; import org.argeo.slc.execution.ExecutionResources; @@ -44,7 +50,7 @@ import org.argeo.slc.test.TestStatus; public class SystemCall implements Runnable { public final static String LOG_STDOUT = "System.out"; - private final CmsLog log = CmsLog.getLog(getClass()); + private final Logger logger = System.getLogger(getClass().getName()); private String execDir; @@ -172,11 +178,9 @@ public class SystemCall implements Runnable { throw new SlcException("Cannot open a stream for " + stdInFile, e2); } - if (log.isTraceEnabled()) { - log.debug("os.name=" + System.getProperty("os.name")); - log.debug("os.arch=" + System.getProperty("os.arch")); - log.debug("os.version=" + System.getProperty("os.version")); - } + logger.log(TRACE, () -> "os.name=" + System.getProperty("os.name")); + logger.log(TRACE, () -> "os.arch=" + System.getProperty("os.arch")); + logger.log(TRACE, () -> "os.version=" + System.getProperty("os.version")); // Execution directory File dir = new File(getExecDirToUse()); @@ -206,7 +210,7 @@ public class SystemCall implements Runnable { // Command line to use final CommandLine commandLine = createCommandLine(); if (logCommand) - log.info("Execute command:\n" + commandLine + "\n in working directory: \n" + dir + "\n"); + logger.log(INFO, "Execute command:\n" + commandLine + "\n in working directory: \n" + dir + "\n"); // Env variables Map environmentVariablesToUse = null; @@ -342,8 +346,8 @@ public class SystemCall implements Runnable { commandLine = new CommandLine(commandToUse.get(0).toString()); for (int i = 1; i < commandToUse.size(); i++) { - if (log.isTraceEnabled()) - log.debug(commandToUse.get(i)); + if (logger.isLoggable(TRACE)) + logger.log(TRACE, commandToUse.get(i)); commandLine.addArgument(commandToUse.get(i).toString()); } } else { @@ -437,8 +441,7 @@ public class SystemCall implements Runnable { public void onProcessComplete(int exitValue) { String msg = "System call '" + commandLine + "' properly completed."; - if (log.isTraceEnabled()) - log.trace(msg); + logger.log(TRACE, () -> msg); if (testResult != null) { forwardPath(testResult); testResult.addResultPart(new SimpleResultPart(TestStatus.PASSED, msg)); @@ -456,7 +459,7 @@ public class SystemCall implements Runnable { if (exceptionOnFailed) throw new SlcException(msg, e); else - log.error(msg, e); + logger.log(ERROR, msg, e); } releaseWatchdog(); } @@ -503,15 +506,17 @@ public class SystemCall implements Runnable { // } if ("ERROR".equals(logLevel)) - log.error(line); + logger.log(ERROR, line); else if ("WARN".equals(logLevel)) - log.warn(line); + logger.log(WARNING, line); + else if ("WARNING".equals(logLevel)) + logger.log(WARNING, line); else if ("INFO".equals(logLevel)) - log.info(line); + logger.log(INFO, line); else if ("DEBUG".equals(logLevel)) - log.debug(line); + logger.log(DEBUG, line); else if ("TRACE".equals(logLevel)) - log.trace(line); + logger.log(TRACE, line); else if (LOG_STDOUT.equals(logLevel)) System.out.println(line); else if ("System.err".equals(logLevel)) @@ -525,7 +530,7 @@ public class SystemCall implements Runnable { try { writer.append(line).append('\n'); } catch (IOException e) { - log.error("Cannot write to log file", e); + logger.log(ERROR, "Cannot write to log file", e); } } @@ -541,7 +546,7 @@ public class SystemCall implements Runnable { file = target.toFile(); writer = new FileWriter(file, append); } catch (IOException e) { - log.error("Cannot get file for " + target, e); + logger.log(ERROR, "Cannot get file for " + target, e); IOUtils.closeQuietly(writer); } return writer; @@ -559,7 +564,7 @@ public class SystemCall implements Runnable { file = target.toFile(); out = new FileOutputStream(file, false); } catch (IOException e) { - log.error("Cannot get file for " + target, e); + logger.log(ERROR, "Cannot get file for " + target, e); IOUtils.closeQuietly(out); } return out; diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/ContextUtils.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/ContextUtils.java index f3ce14a3d..96ce5e1bf 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/ContextUtils.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/ContextUtils.java @@ -3,7 +3,6 @@ package org.argeo.slc.runtime.test; import java.util.Map; import java.util.TreeMap; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.test.TestResult; import org.argeo.slc.test.TestStatus; import org.argeo.slc.test.context.ContextAware; @@ -11,19 +10,15 @@ import org.argeo.slc.test.context.ParentContextAware; /** Utilities for comparing and synchronising contexts. */ public class ContextUtils { - private final static CmsLog log = CmsLog.getLog(ContextUtils.class); - - public static void compareReachedExpected(ContextAware contextAware, - TestResult testResult) { + public static void compareReachedExpected(ContextAware contextAware, TestResult testResult) { for (String key : contextAware.getExpectedValues().keySet()) { // Compare expected values with reached ones Object expectedValue = contextAware.getExpectedValues().get(key); - if (expectedValue.toString().equals( - contextAware.getContextSkipFlag())) { - if (log.isDebugEnabled()) - log.debug("Skipped check for key '" + key + "'"); + if (expectedValue.toString().equals(contextAware.getContextSkipFlag())) { +// if (log.isDebugEnabled()) +// log.debug("Skipped check for key '" + key + "'"); continue; } @@ -31,42 +26,35 @@ public class ContextUtils { Object reachedValue = contextAware.getValues().get(key); if (expectedValue.equals(contextAware.getContextAnyFlag())) { - testResult.addResultPart(new SimpleResultPart( - TestStatus.PASSED, "Expected any value for key '" - + key + "'")); + testResult.addResultPart( + new SimpleResultPart(TestStatus.PASSED, "Expected any value for key '" + key + "'")); } else if (expectedValue.equals(reachedValue)) { - testResult.addResultPart(new SimpleResultPart( - TestStatus.PASSED, "Values matched for key '" + key - + "'")); + testResult.addResultPart( + new SimpleResultPart(TestStatus.PASSED, "Values matched for key '" + key + "'")); } else { - testResult.addResultPart(new SimpleResultPart( - TestStatus.FAILED, "Mismatch for key '" + key - + "': expected '" + expectedValue - + "' but reached '" + reachedValue + "'")); + testResult.addResultPart(new SimpleResultPart(TestStatus.FAILED, "Mismatch for key '" + key + + "': expected '" + expectedValue + "' but reached '" + reachedValue + "'")); } } else { - testResult.addResultPart(new SimpleResultPart( - TestStatus.FAILED, "No value reached for key '" + key - + "'")); + testResult.addResultPart( + new SimpleResultPart(TestStatus.FAILED, "No value reached for key '" + key + "'")); } } } /** - * Makes sure that all children and sub-children of parent share the same - * maps for values and expected values. + * Makes sure that all children and sub-children of parent share the same maps + * for values and expected values. */ public static void synchronize(ParentContextAware parent) { - Map expectedValuesCommon = new TreeMap( - parent.getExpectedValues()); + Map expectedValuesCommon = new TreeMap(parent.getExpectedValues()); synchronize(parent, expectedValuesCommon); - if (log.isDebugEnabled()) - log.debug("Synchronized context " + parent); +// if (log.isDebugEnabled()) +// log.debug("Synchronized context " + parent); } - private static void synchronize(ParentContextAware parent, - Map expectedValuesCommon) { + private static void synchronize(ParentContextAware parent, Map expectedValuesCommon) { for (ContextAware child : parent.getChildContexts()) { // Values putNotContained(parent.getValues(), child.getValues()); @@ -79,26 +67,22 @@ public class ContextUtils { // Creates a new Map in order not to disturb other context using the // same keys - Map expectedValuesCommonChild = new TreeMap( - expectedValuesCommon); - putNotContained(expectedValuesCommonChild, - child.getExpectedValues()); + Map expectedValuesCommonChild = new TreeMap(expectedValuesCommon); + putNotContained(expectedValuesCommonChild, child.getExpectedValues()); if (child instanceof ParentContextAware) { // Recursive sync - synchronize((ParentContextAware) child, - expectedValuesCommonChild); + synchronize((ParentContextAware) child, expectedValuesCommonChild); } } } /** - * Put into common map the values from child map which are not already - * defined in common map. + * Put into common map the values from child map which are not already defined + * in common map. */ - public static void putNotContained(Map commonMap, - Map childMap) { + public static void putNotContained(Map commonMap, Map childMap) { for (String key : childMap.keySet()) { if (!commonMap.containsKey(key)) { commonMap.put(key, childMap.get(key)); @@ -107,8 +91,7 @@ public class ContextUtils { } /** Overrides child map values with the values already set in common map */ - public static void overrideContained(Map commonMap, - Map childMap) { + public static void overrideContained(Map commonMap, Map childMap) { for (String key : childMap.keySet()) { if (commonMap.containsKey(key)) { childMap.put(key, commonMap.get(key)); diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/SimpleTestResult.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/SimpleTestResult.java index 436903317..88f0a24b0 100644 --- a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/SimpleTestResult.java +++ b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/SimpleTestResult.java @@ -7,7 +7,6 @@ import java.util.TreeMap; import java.util.UUID; import java.util.Vector; -import org.argeo.api.cms.CmsLog; import org.argeo.slc.SlcException; import org.argeo.slc.test.TestResult; import org.argeo.slc.test.TestResultPart; @@ -17,8 +16,6 @@ import org.argeo.slc.test.TestRun; * Basic implementation of a test result containing only a list of result parts. */ public class SimpleTestResult implements TestResult { - private static CmsLog log = CmsLog.getLog(SimpleTestResult.class); - private String uuid; private String currentTestRunUuid; @@ -31,13 +28,11 @@ public class SimpleTestResult implements TestResult { public void addResultPart(TestResultPart part) { if (throwError && part.getStatus() == ERROR) { - throw new SlcException( - "There was an error in the underlying test: " - + part.getExceptionMessage()); + throw new SlcException("There was an error in the underlying test: " + part.getExceptionMessage()); } parts.add(part); - if (log.isDebugEnabled()) - log.debug(part); +// if (log.isDebugEnabled()) +// log.debug(part); } public void close() { diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/sync/BasicSyncFileVisitor.java b/org.argeo.slc.runtime/src/org/argeo/slc/sync/BasicSyncFileVisitor.java new file mode 100644 index 000000000..066812d16 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/sync/BasicSyncFileVisitor.java @@ -0,0 +1,162 @@ +package org.argeo.slc.sync; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; + +/** Synchronises two directory structures. */ +public class BasicSyncFileVisitor extends SimpleFileVisitor { + // TODO make it configurable + private boolean trace = false; + + private final Path sourceBasePath; + private final Path targetBasePath; + private final boolean delete; + private final boolean recursive; + + private SyncResult syncResult = new SyncResult<>(); + + public BasicSyncFileVisitor(Path sourceBasePath, Path targetBasePath, boolean delete, boolean recursive) { + this.sourceBasePath = sourceBasePath; + this.targetBasePath = targetBasePath; + this.delete = delete; + this.recursive = recursive; + } + + @Override + public FileVisitResult preVisitDirectory(Path sourceDir, BasicFileAttributes attrs) throws IOException { + if (!recursive && !sourceDir.equals(sourceBasePath)) + return FileVisitResult.SKIP_SUBTREE; + Path targetDir = toTargetPath(sourceDir); + Files.createDirectories(targetDir); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path sourceDir, IOException exc) throws IOException { + if (delete) { + Path targetDir = toTargetPath(sourceDir); + for (Path targetPath : Files.newDirectoryStream(targetDir)) { + Path sourcePath = sourceDir.resolve(targetPath.getFileName()); + if (!Files.exists(sourcePath)) { + try { + FsSyncUtils.delete(targetPath); + deleted(targetPath); + } catch (Exception e) { + deleteFailed(targetPath, exc); + } + } + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path sourceFile, BasicFileAttributes attrs) throws IOException { + Path targetFile = toTargetPath(sourceFile); + try { + if (!Files.exists(targetFile)) { + Files.copy(sourceFile, targetFile); + added(sourceFile, targetFile); + } else { + if (shouldOverwrite(sourceFile, targetFile)) { + Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING); + } + } + } catch (Exception e) { + copyFailed(sourceFile, targetFile, e); + } + return FileVisitResult.CONTINUE; + } + + protected boolean shouldOverwrite(Path sourceFile, Path targetFile) throws IOException { + long sourceSize = Files.size(sourceFile); + long targetSize = Files.size(targetFile); + if (sourceSize != targetSize) { + return true; + } + FileTime sourceLastModif = Files.getLastModifiedTime(sourceFile); + FileTime targetLastModif = Files.getLastModifiedTime(targetFile); + if (sourceLastModif.compareTo(targetLastModif) > 0) + return true; + return shouldOverwriteLaterSameSize(sourceFile, targetFile); + } + + protected boolean shouldOverwriteLaterSameSize(Path sourceFile, Path targetFile) { + return false; + } + +// @Override +// public FileVisitResult visitFileFailed(Path sourceFile, IOException exc) throws IOException { +// error("Cannot sync " + sourceFile, exc); +// return FileVisitResult.CONTINUE; +// } + + private Path toTargetPath(Path sourcePath) { + Path relativePath = sourceBasePath.relativize(sourcePath); + Path targetPath = targetBasePath.resolve(relativePath.toString()); + return targetPath; + } + + public Path getSourceBasePath() { + return sourceBasePath; + } + + public Path getTargetBasePath() { + return targetBasePath; + } + + protected void added(Path sourcePath, Path targetPath) { + syncResult.getAdded().add(targetPath); + if (isTraceEnabled()) + trace("Added " + sourcePath + " as " + targetPath); + } + + protected void modified(Path sourcePath, Path targetPath) { + syncResult.getModified().add(targetPath); + if (isTraceEnabled()) + trace("Overwritten from " + sourcePath + " to " + targetPath); + } + + protected void copyFailed(Path sourcePath, Path targetPath, Exception e) { + syncResult.addError(sourcePath, targetPath, e); + if (isTraceEnabled()) + error("Cannot copy " + sourcePath + " to " + targetPath, e); + } + + protected void deleted(Path targetPath) { + syncResult.getDeleted().add(targetPath); + if (isTraceEnabled()) + trace("Deleted " + targetPath); + } + + protected void deleteFailed(Path targetPath, Exception e) { + syncResult.addError(null, targetPath, e); + if (isTraceEnabled()) + error("Cannot delete " + targetPath, e); + } + + /** Log error. */ + protected void error(Object obj, Throwable e) { + System.err.println(obj); + e.printStackTrace(); + } + + protected boolean isTraceEnabled() { + return trace; + } + + protected void trace(Object obj) { + System.out.println(obj); + } + + public SyncResult getSyncResult() { + return syncResult; + } + +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/sync/FsSyncUtils.java b/org.argeo.slc.runtime/src/org/argeo/slc/sync/FsSyncUtils.java new file mode 100644 index 000000000..4321778ec --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/sync/FsSyncUtils.java @@ -0,0 +1,62 @@ +package org.argeo.slc.sync; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +public class FsSyncUtils { + /** Sync a source path with a target path. */ + public static void sync(Path sourceBasePath, Path targetBasePath) { + sync(sourceBasePath, targetBasePath, false); + } + + /** Sync a source path with a target path. */ + public static void sync(Path sourceBasePath, Path targetBasePath, boolean delete) { + sync(new BasicSyncFileVisitor(sourceBasePath, targetBasePath, delete, true)); + } + + public static void sync(BasicSyncFileVisitor syncFileVisitor) { + try { + Files.walkFileTree(syncFileVisitor.getSourceBasePath(), syncFileVisitor); + } catch (Exception e) { + throw new RuntimeException("Cannot sync " + syncFileVisitor.getSourceBasePath() + " with " + + syncFileVisitor.getTargetBasePath(), e); + } + } + + /** + * Deletes this path, recursively if needed. Does nothing if the path does not + * exist. + */ + public static void delete(Path path) { + try { + if (!Files.exists(path)) + return; + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult postVisitDirectory(Path directory, IOException e) throws IOException { + if (e != null) + throw e; + Files.delete(directory); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new RuntimeException("Cannot delete " + path, e); + } + } + + /** Singleton. */ + private FsSyncUtils() { + + } +} diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/sync/SyncResult.java b/org.argeo.slc.runtime/src/org/argeo/slc/sync/SyncResult.java new file mode 100644 index 000000000..22a2f5676 --- /dev/null +++ b/org.argeo.slc.runtime/src/org/argeo/slc/sync/SyncResult.java @@ -0,0 +1,101 @@ +package org.argeo.slc.sync; + +import java.time.Instant; +import java.util.Set; +import java.util.TreeSet; + +/** Describes what happendend during a sync operation. */ +public class SyncResult { + private final Set added = new TreeSet<>(); + private final Set modified = new TreeSet<>(); + private final Set deleted = new TreeSet<>(); + private final Set errors = new TreeSet<>(); + + public Set getAdded() { + return added; + } + + public Set getModified() { + return modified; + } + + public Set getDeleted() { + return deleted; + } + + public Set getErrors() { + return errors; + } + + public void addError(T sourcePath, T targetPath, Exception e) { + Error error = new Error(sourcePath, targetPath, e); + errors.add(error); + } + + public boolean noModification() { + return modified.isEmpty() && deleted.isEmpty() && added.isEmpty(); + } + + @Override + public String toString() { + if (noModification()) + return "No modification."; + StringBuffer sb = new StringBuffer(); + for (T p : modified) + sb.append("MOD ").append(p).append('\n'); + for (T p : deleted) + sb.append("DEL ").append(p).append('\n'); + for (T p : added) + sb.append("ADD ").append(p).append('\n'); + for (Error error : errors) + sb.append(error).append('\n'); + return sb.toString(); + } + + public class Error implements Comparable { + private final T sourcePath;// if null this is a failed delete + private final T targetPath; + private final Exception exception; + private final Instant timestamp = Instant.now(); + + public Error(T sourcePath, T targetPath, Exception e) { + super(); + this.sourcePath = sourcePath; + this.targetPath = targetPath; + this.exception = e; + } + + public T getSourcePath() { + return sourcePath; + } + + public T getTargetPath() { + return targetPath; + } + + public Exception getException() { + return exception; + } + + public Instant getTimestamp() { + return timestamp; + } + + @Override + public int compareTo(Error o) { + return timestamp.compareTo(o.timestamp); + } + + @Override + public int hashCode() { + return timestamp.hashCode(); + } + + @Override + public String toString() { + return "ERR " + timestamp + (sourcePath == null ? "Deletion failed" : "Copy failed " + sourcePath) + " " + + targetPath + " " + exception.getMessage(); + } + + } +} diff --git a/tp/org.argeo.tp.sdk/biz.aQute.bndlib.bnd b/tp/org.argeo.tp.sdk/biz.aQute.bndlib.bnd index 698dbf6ed..0fa851b8e 100644 --- a/tp/org.argeo.tp.sdk/biz.aQute.bndlib.bnd +++ b/tp/org.argeo.tp.sdk/biz.aQute.bndlib.bnd @@ -1,3 +1,3 @@ Bundle-License: Apache-2.0 -SLC-Origin-M2: biz.aQute.bnd:biz.aQute.bndlib:4.2.0 +SLC-Origin-M2: biz.aQute.bnd:biz.aQute.bndlib:5.3.0 SLC-Origin-ManifestNotModified: true diff --git a/tp/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.bnd b/tp/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.bnd index b3a28caf5..54f802f9f 100644 --- a/tp/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.bnd +++ b/tp/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.bnd @@ -1,3 +1,4 @@ Bundle-License: EPL-2.0 SLC-Origin-M2: org.eclipse.jdt:ecj:3.28.0 -SLC-Origin-ManifestNotModified: true \ No newline at end of file +SLC-Origin-ManifestNotModified: true +Main-Class: org.eclipse.jdt.internal.compiler.batch.Main \ No newline at end of file