SLING-8391 - Add support for execution modes

* made ExecutionContext and its handling more robust
diff --git a/pom.xml b/pom.xml
index 11c55ad..09766e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -172,6 +172,11 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.service.component.annotations</artifactId>
             <scope>provided</scope>
diff --git a/src/main/java/org/apache/sling/cli/impl/Command.java b/src/main/java/org/apache/sling/cli/impl/Command.java
index 102b5e6..70a6c45 100644
--- a/src/main/java/org/apache/sling/cli/impl/Command.java
+++ b/src/main/java/org/apache/sling/cli/impl/Command.java
@@ -16,11 +16,14 @@
  */
 package org.apache.sling.cli.impl;
 
+
+import org.jetbrains.annotations.NotNull;
+
 public interface Command {
     
     String PROPERTY_NAME_COMMAND = "command";
     String PROPERTY_NAME_SUBCOMMAND = "subcommand";
     String PROPERTY_NAME_SUMMARY = "summary";
 
-    void execute(ExecutionContext context) throws Exception;
+    void execute(@NotNull ExecutionContext context) throws Exception;
 }
diff --git a/src/main/java/org/apache/sling/cli/impl/CommandProcessor.java b/src/main/java/org/apache/sling/cli/impl/CommandProcessor.java
index cb3de3a..9029d4b 100644
--- a/src/main/java/org/apache/sling/cli/impl/CommandProcessor.java
+++ b/src/main/java/org/apache/sling/cli/impl/CommandProcessor.java
@@ -88,12 +88,12 @@
 
     private ExecutionContext defineContext(String[] arguments) {
         if (arguments.length < 3)
-            return null;
+            return ExecutionContext.DEFAULT;
         String target = arguments[2];
         if (arguments.length > 3) {
-            return new ExecutionContext(target, arguments[3]);
+            return new ExecutionContext(ExecutionContext.Mode.fromString(arguments[3]), target);
         } else {
-            return new ExecutionContext(target, null);
+            return new ExecutionContext(ExecutionContext.Mode.DRY_RUN, target);
         }
     }
     
diff --git a/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java b/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java
index 77eac71..6505508 100644
--- a/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java
+++ b/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java
@@ -18,6 +18,10 @@
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 package org.apache.sling.cli.impl;
 
+import java.util.Objects;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -28,8 +32,9 @@
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ExecutionContext.class);
 
-    private final String target;
     private final Mode mode;
+    private final String target;
+    public static final ExecutionContext DEFAULT = new ExecutionContext(Mode.DRY_RUN, null);
 
     /**
      * Creates an {@code ExecutionContext}.
@@ -37,13 +42,9 @@
      * @param target the command's target
      * @param mode   the execution mode
      */
-    public ExecutionContext(String target, String mode) {
-        this.target = target;
-        if (mode == null) {
-            this.mode = Mode.DRY_RUN;
-        } else {
-            this.mode = Mode.fromString(mode);
-        }
+    public ExecutionContext(@NotNull Mode mode, @Nullable String target) {
+        this.mode = mode;
+        this.target = Objects.requireNonNullElse(target, "");
     }
 
     /**
@@ -51,6 +52,7 @@
      *
      * @return the execution target
      */
+    @NotNull
     public String getTarget() {
         return target;
     }
@@ -60,6 +62,7 @@
      *
      * @return the execution mode
      */
+    @NotNull
     public Mode getMode() {
         return mode;
     }
diff --git a/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java b/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java
index a15c8a9..6605268 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java
@@ -28,6 +28,7 @@
 import org.apache.sling.cli.impl.jira.VersionClient;
 import org.apache.sling.cli.impl.nexus.StagingRepository;
 import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -49,7 +50,7 @@
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
     @Override
-    public void execute(ExecutionContext context) {
+    public void execute(@NotNull ExecutionContext context) {
         try {
             StagingRepository repo = repoFinder.find(Integer.parseInt(context.getTarget()));
             for (Release release : Release.fromString(repo.getDescription()) ) {
diff --git a/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java b/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java
index 39b9b8d..4a59b7f 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java
@@ -21,6 +21,7 @@
 import org.apache.sling.cli.impl.Command;
 import org.apache.sling.cli.impl.ExecutionContext;
 import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -38,7 +39,7 @@
     private StagingRepositoryFinder repoFinder;
 
     @Override
-    public void execute(ExecutionContext context) {
+    public void execute(@NotNull ExecutionContext context) {
         try {
             repoFinder.list().stream()
                 .forEach( r -> logger.info("{}\t{}", r.getRepositoryId(), r.getDescription()));
diff --git a/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java b/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
index 748f68f..aff45f0 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
@@ -33,6 +33,7 @@
 import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
 import org.apache.sling.cli.impl.people.Member;
 import org.apache.sling.cli.impl.people.MembersFinder;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -94,7 +95,7 @@
             "https://issues.apache.org/jira/browse/SLING/fixforversion/##VERSION_ID##";
 
     @Override
-    public void execute(ExecutionContext context) {
+    public void execute(@NotNull ExecutionContext context) {
         try {
             int repoId = Integer.parseInt(context.getTarget());
             StagingRepository repo = repoFinder.find(repoId);
diff --git a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java
index 131a221..8cf26f2 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java
@@ -38,6 +38,7 @@
 import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
 import org.apache.sling.cli.impl.people.Member;
 import org.apache.sling.cli.impl.people.MembersFinder;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -85,7 +86,7 @@
             "\n";
 
     @Override
-    public void execute(ExecutionContext context) {
+    public void execute(@NotNull ExecutionContext context) {
         try {
             
             StagingRepository repository = repoFinder.find(Integer.parseInt(context.getTarget()));
diff --git a/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java b/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java
index 66aef04..d9ad68b 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java
@@ -32,6 +32,7 @@
 import org.eclipse.jgit.api.ResetCommand.ResetType;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -52,7 +53,7 @@
     private final Logger logger = LoggerFactory.getLogger(getClass());
     
     @Override
-    public void execute(ExecutionContext context) {
+    public void execute(@NotNull ExecutionContext context) {
         try {
             ensureRepo();
             try ( Git git = Git.open(new File(GIT_CHECKOUT)) ) {
diff --git a/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java b/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java
index a53cfae..d613487 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java
@@ -38,6 +38,7 @@
 import org.apache.sling.cli.impl.http.HttpClientFactory;
 import org.apache.sling.cli.impl.nexus.StagingRepository;
 import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -61,7 +62,7 @@
     private HttpClientFactory httpClientFactory;
 
     @Override
-    public void execute(ExecutionContext context) {
+    public void execute(@NotNull ExecutionContext context) {
         try {
             StagingRepository repository = repoFinder.find(Integer.parseInt(context.getTarget()));
             List<Release> releases = Release.fromString(repository.getDescription());
diff --git a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
index 55ab275..d459306 100644
--- a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
+++ b/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
@@ -52,7 +52,7 @@
         ServiceReference<?> reference =
                 osgiContext.bundleContext().getServiceReference(Command.class.getName());
         Command command = (Command) osgiContext.bundleContext().getService(reference);
-        command.execute(new ExecutionContext("123", "--auto"));
+        command.execute(new ExecutionContext(ExecutionContext.Mode.AUTO, "123"));
         verify(mailer).send(
                 "From: John Doe <johndoe@apache.org>\n" +
                         "To: \"Sling Developers List\" <dev@sling.apache.org>\n" +
diff --git a/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java
index bb25d9f..6b8bece 100644
--- a/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java
+++ b/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java
@@ -85,7 +85,7 @@
         ServiceReference<?> reference =
                 osgiContext.bundleContext().getServiceReference(Command.class.getName());
         Command command = (Command) osgiContext.bundleContext().getService(reference);
-        command.execute(new ExecutionContext("123", null));
+        command.execute(new ExecutionContext(ExecutionContext.Mode.DRY_RUN, "123"));
         verify(logger).info(
                 "From: John Doe <johndoe@apache.org> \n" +
                 "To: \"Sling Developers List\" <dev@sling.apache.org>\n" +
@@ -124,7 +124,7 @@
         ServiceReference<?> reference =
                 osgiContext.bundleContext().getServiceReference(Command.class.getName());
         Command command = (Command) osgiContext.bundleContext().getService(reference);
-        command.execute(new ExecutionContext("123", null));
+        command.execute(new ExecutionContext(ExecutionContext.Mode.DRY_RUN, "123"));
         verify(logger).info(
                 "Release {} does not have at least 3 binding votes.",
                 "Apache Sling CLI Test 1.0.0"
@@ -148,7 +148,7 @@
         ServiceReference<?> reference =
                 osgiContext.bundleContext().getServiceReference(Command.class.getName());
         Command command = (Command) osgiContext.bundleContext().getService(reference);
-        command.execute(new ExecutionContext("123", "--auto"));
+        command.execute(new ExecutionContext(ExecutionContext.Mode.AUTO, "123"));
         verify(mailer).send(
                 "From: John Doe <johndoe@apache.org> \n" +
                         "To: \"Sling Developers List\" <dev@sling.apache.org>\n" +
diff --git a/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java
index 8af6c63..ac5f758 100644
--- a/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java
+++ b/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java
@@ -90,7 +90,7 @@
         Command updateReporter = osgiContext.getService(Command.class);
         assertTrue("Expected to retrieve the UpdateReporterCommand from the mocked OSGi environment.",
                 updateReporter instanceof UpdateReporterCommand);
-        updateReporter.execute(new ExecutionContext("42", "--dry-run"));
+        updateReporter.execute(new ExecutionContext(ExecutionContext.Mode.DRY_RUN, "42"));
         verify(logger).info("The following {} would be added to the Apache Reporter System:", "releases");
         verify(logger).info("  - {}", "Apache Sling CLI 1");
         verify(logger).info("  - {}", "Apache Sling CLI 2");
@@ -113,7 +113,7 @@
         when(response.getStatusLine()).thenReturn(statusLine);
         when(statusLine.getStatusCode()).thenReturn(200);
         when(client.execute(any())).thenReturn(response);
-        updateReporter.execute(new ExecutionContext("42", "--interactive"));
+        updateReporter.execute(new ExecutionContext(ExecutionContext.Mode.INTERACTIVE, "42"));
         verify(client, times(2)).execute(any());
     }
 
@@ -128,7 +128,7 @@
         when(response.getStatusLine()).thenReturn(statusLine);
         when(statusLine.getStatusCode()).thenReturn(200);
         when(client.execute(any())).thenReturn(response);
-        updateReporter.execute(new ExecutionContext("42", "--auto"));
+        updateReporter.execute(new ExecutionContext(ExecutionContext.Mode.AUTO, "42"));
         verify(client, times(2)).execute(any());
     }