Botstrap and build of the base runtime.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 10 Feb 2022 10:08:43 +0000 (11:08 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 10 Feb 2022 10:08:43 +0000 (11:08 +0100)
32 files changed:
Makefile
org.argeo.slc.runtime/ext/test/org/argeo/fs/FsUtilsTest.java
org.argeo.slc.runtime/src/org/argeo/slc/cli/ArgeoCli.java [deleted file]
org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandArgsException.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandRuntimeException.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/cli/CommandsCli.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/cli/DescribedCommand.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/cli/HelpCommand.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FileSync.java
org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/FsCommands.java
org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/PathSync.java
org.argeo.slc.runtime/src/org/argeo/slc/cli/fs/SyncFileVisitor.java
org.argeo.slc.runtime/src/org/argeo/slc/cli/package-info.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/Echo.java
org.argeo.slc.runtime/src/org/argeo/slc/cli/posix/PosixCommands.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/AbstractExecutionModulesManager.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/ArgeoCli.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultAgentCli.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionStack.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/ExecutionThread.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/InstantiationManager.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/ProcessThread.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/Echo.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/tasks/SystemCall.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/ContextUtils.java
org.argeo.slc.runtime/src/org/argeo/slc/runtime/test/SimpleTestResult.java
org.argeo.slc.runtime/src/org/argeo/slc/sync/BasicSyncFileVisitor.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/sync/FsSyncUtils.java [new file with mode: 0644]
org.argeo.slc.runtime/src/org/argeo/slc/sync/SyncResult.java [new file with mode: 0644]
tp/org.argeo.tp.sdk/biz.aQute.bndlib.bnd
tp/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.bnd

index 599b85de81e5bac77752d252dc5324ed6dcae53b..0f0c8455c725750545a07174a8555047ad012dcb 100644 (file)
--- 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)))
        
index 587efd7abcf212b4a90b8b1673d8a04f947f7e29..5d31f4aa165e44bf2530282121eba1844b2f1e6d 100644 (file)
@@ -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 (file)
index b573a67..0000000
+++ /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 (file)
index 0000000..4d4d759
--- /dev/null
@@ -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 (file)
index 0000000..9072544
--- /dev/null
@@ -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<String> arguments;
+
+       public CommandRuntimeException(Throwable e, DescribedCommand<?> command, List<String> arguments) {
+               this(null, e, command, arguments);
+       }
+
+       public CommandRuntimeException(String message, DescribedCommand<?> command, List<String> arguments) {
+               this(message, null, command, arguments);
+       }
+
+       public CommandRuntimeException(String message, Throwable e, DescribedCommand<?> command, List<String> 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<String> 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 (file)
index 0000000..5146fb7
--- /dev/null
@@ -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<Object> {
+       public final static String HELP = "help";
+
+       private final String commandName;
+       private Map<String, Function<List<String>, ?>> commands = new TreeMap<>();
+
+       protected final Options options = new Options();
+
+       public CommandsCli(String commandName) {
+               this.commandName = commandName;
+       }
+
+       @Override
+       public Object apply(List<String> args) {
+               String cmd = null;
+               List<String> newArgs = new ArrayList<>();
+               try {
+                       CommandLineParser clParser = new DefaultParser();
+                       CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true);
+                       List<String> 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<List<String>, ?> 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<List<String>, ?> 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<String> getSubCommands() {
+               return commands.keySet();
+       }
+
+       public Function<List<String>, ?> getCommand(String command) {
+               return commands.get(command);
+       }
+
+       public HelpCommand getHelpCommand() {
+               return (HelpCommand) getCommand(HELP);
+       }
+
+       public Function<List<String>, 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 (file)
index 0000000..fc1b139
--- /dev/null
@@ -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<T> extends Function<List<String>, T> {
+       default Options getOptions() {
+               return new Options();
+       }
+
+       String getDescription();
+
+       default String getUsage() {
+               return null;
+       }
+
+       default String getExamples() {
+               return null;
+       }
+
+       default CommandLine toCommandLine(List<String> 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 (file)
index 0000000..8400d05
--- /dev/null
@@ -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<String> {
+       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<String> args) {
+               StringWriter out = new StringWriter();
+
+               if (args.size() == 0) {// overview
+                       printHelp(commandsCli, out);
+               } else {
+                       String cmd = args.get(0);
+                       Function<List<String>, ?> 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<List<String>, ?> 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<List<String>, ?> 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();
+               }
+       }
+}
index 6b8cbfa72af12204d68b4a3ec59026ed3064bb8f..dfff27ce626fe3c0f93743b6cba87a13c93459f1 100644 (file)
@@ -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<SyncResult<Path>> {
        final static Option deleteOption = Option.builder().longOpt("delete").desc("delete from target").build();
index 6df81f84410436f82b151c47b6cba9412432ff79..cab3c1e2b45281588efa60b95b38c152e75a5e52 100644 (file)
@@ -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 {
index abd0a20d5b192d19f9a2fecdab5079632426fc70..844af0154d7840e03772cff39f0e475a0a3f917e 100644 (file)
@@ -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<SyncResult<Path>> {
index 712ec5f6b0f1bfbdee385caa18982336cc2f7939..43af11144ef226efcf1d221f75c73d4c20f47ab6 100644 (file)
@@ -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 (file)
index 0000000..0216a28
--- /dev/null
@@ -0,0 +1,2 @@
+/** Command line API. */
+package org.argeo.slc.cli;
\ No newline at end of file
index d773760bc23809e3e9522264d478e5578be8ab3e..a479e96d197399d8712bc69cf262a17b59c0e740 100644 (file)
@@ -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<String> {
 
index bb5920e55ba259ee9d533ad5213bb36f1ff9eef4..67544108dedc0edaf17ff1b5f1f69ceb31096fab 100644 (file)
@@ -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 {
index ff69dda2542c793abd71c518243e4d0530cce011..149665cdf425897957c5dad2e5fea1720e2c7251 100644 (file)
@@ -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<FilteredNotifier> filteredNotifiers = Collections
        // .synchronizedList(new ArrayList<FilteredNotifier>());
@@ -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 (file)
index 0000000..40470fe
--- /dev/null
@@ -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);
+       }
+
+}
index 11a79d07f708a642a9d9bc4957182a2dac157688..dbe1d7f6e296055d3c6ee3bc8eb71e2c63a68c8f 100644 (file)
@@ -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<URI> uris = asURIs(args);
index 7726177cc32205e8f0432a7a9113972ddd788705..9e31306adf765d2c35c9f2d0ed3c3fa20ff9652f 100644 (file)
@@ -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);
                                }
                        }
                }
index 9b5460bd995f8f9a1139fff110930147eba46663..b30d51788a8ac9be0b974002bb58aeedc08bbcfa 100644 (file)
@@ -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<ExecutionFlowRuntime> stack = new Stack<ExecutionFlowRuntime>();
 
        public synchronized void enterFlow(ExecutionFlow executionFlow) {
                ExecutionFlowRuntime runtime = new ExecutionFlowRuntime(executionFlow);
                stack.push(runtime);
 
-               Map<String, ExecutionSpecAttribute> specAttrs = executionFlow
-                               .getExecutionSpec().getAttributes();
+               Map<String, ExecutionSpecAttribute> 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);
        }
 
index c5be2cf0a983b6031c3125bf2c340382e99deb6f..7e0e7377852f67dc0da0514c9019c84de85d400f 100644 (file)
@@ -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);
index ecac292e23210707d9f7cdf7c047f400b03bfc1a..2e29438e360901bc5079d67a3913c5144da140e0 100644 (file)
@@ -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<Stack<ExecutionFlow>> flowStack = new ThreadLocal<Stack<ExecutionFlow>>();
 
        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;
index 97f83e2f947bfb7ffa4a38ba3848a6fb6813a5c1..33ea9f07ae6f0ddfe26b29127df8986e9dbe4828 100644 (file)
@@ -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();
index 778550aa23cfbcf09926ed687591e58abf087366..6695313a34c3cbb9bb33d62fe18d1a6cf0b2ab45 100644 (file)
@@ -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) {
index 5b9f9dce050f5a682495ab3fd6d6d6fb5f025a3c..d421cd2f214e196bbd66900cfb52a1eca5108cf5 100644 (file)
@@ -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<String, String> 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;
index f3ce14a3de3cf53abe2f428ac9605200807c3019..96ce5e1bfb8fbfcc7d247470fa1bc3bef78f8ce7 100644 (file)
@@ -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<String, Object> expectedValuesCommon = new TreeMap<String, Object>(
-                               parent.getExpectedValues());
+               Map<String, Object> expectedValuesCommon = new TreeMap<String, Object>(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<String, Object> expectedValuesCommon) {
+       private static void synchronize(ParentContextAware parent, Map<String, Object> 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<String, Object> expectedValuesCommonChild = new TreeMap<String, Object>(
-                                       expectedValuesCommon);
-                       putNotContained(expectedValuesCommonChild,
-                                       child.getExpectedValues());
+                       Map<String, Object> expectedValuesCommonChild = new TreeMap<String, Object>(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<String, Object> commonMap,
-                       Map<String, Object> childMap) {
+       public static void putNotContained(Map<String, Object> commonMap, Map<String, Object> 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<String, Object> commonMap,
-                       Map<String, Object> childMap) {
+       public static void overrideContained(Map<String, Object> commonMap, Map<String, Object> childMap) {
                for (String key : childMap.keySet()) {
                        if (commonMap.containsKey(key)) {
                                childMap.put(key, commonMap.get(key));
index 436903317847d17fd88d408cd9471215e5137a17..88f0a24b0b2b2e181f0138b74540bbfbfe9c6401 100644 (file)
@@ -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 (file)
index 0000000..066812d
--- /dev/null
@@ -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<Path> {
+       // 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<Path> 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<Path> 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 (file)
index 0000000..4321778
--- /dev/null
@@ -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<Path>() {
+                               @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 (file)
index 0000000..22a2f56
--- /dev/null
@@ -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<T> {
+       private final Set<T> added = new TreeSet<>();
+       private final Set<T> modified = new TreeSet<>();
+       private final Set<T> deleted = new TreeSet<>();
+       private final Set<Error> errors = new TreeSet<>();
+
+       public Set<T> getAdded() {
+               return added;
+       }
+
+       public Set<T> getModified() {
+               return modified;
+       }
+
+       public Set<T> getDeleted() {
+               return deleted;
+       }
+
+       public Set<Error> 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<Error> {
+               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();
+               }
+
+       }
+}
index 698dbf6eddbe9c8f7f640b4bf21235dce1f13621..0fa851b8e288870964b49f60ee87de72df817f3d 100644 (file)
@@ -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
index b3a28caf50c49cede072a4e11adba9d33e4bcd1d..54f802f9faa5fc5f0139a71a9c00c322548a6f2b 100644 (file)
@@ -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