jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From k...@apache.org
Subject [jackrabbit-filevault] 01/01: JCRVLT-511 add package task options
Date Thu, 11 Mar 2021 11:53:22 GMT
This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/support-package-task-options
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit 0c062cd8395f21e033368200377e6f0a786b057c
Author: Konrad Windszus <kwin@apache.org>
AuthorDate: Thu Mar 11 12:53:08 2021 +0100

    JCRVLT-511 add package task options
---
 .../jackrabbit/vault/fs/io/ImportOptions.java      | 105 +++++++++++++++++++
 .../vault/packaging/registry/PackageTask.java      |   7 ++
 .../packaging/registry/PackageTaskBuilder.java     |  10 +-
 ...ageTaskBuilder.java => PackageTaskOptions.java} |  26 +----
 .../registry/impl/ExecutionPlanBuilderImpl.java    |  54 +++++++---
 .../packaging/registry/impl/PackageTaskImpl.java   |  73 ++++++++++++--
 .../impl/PackageTaskOptionsSerializer.java         | 111 +++++++++++++++++++++
 .../taskoption/ImportOptionsPackageTaskOption.java |  66 ++++++++++++
 .../impl/ExecutionPlanBuilderImplTest.java         |  71 +++++++++++++
 .../registry/impl/MockPackageRegistry.java         |   5 +-
 10 files changed, 477 insertions(+), 51 deletions(-)

diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
index bff88c1..5ab14ee 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
@@ -405,4 +405,109 @@ public class ImportOptions {
     public void setDependencyHandling(DependencyHandling dependencyHandling) {
         this.dependencyHandling = dependencyHandling;
     }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((acHandling == null) ? 0 : acHandling.hashCode());
+        result = prime * result + autoSave;
+        result = prime * result + ((cndPattern == null) ? 0 : cndPattern.hashCode());
+        result = prime * result + ((cugHandling == null) ? 0 : cugHandling.hashCode());
+        result = prime * result + ((dependencyHandling == null) ? 0 : dependencyHandling.hashCode());
+        result = prime * result + (dryRun ? 1231 : 1237);
+        result = prime * result + ((filter == null) ? 0 : filter.hashCode());
+        result = prime * result + ((hookClassLoader == null) ? 0 : hookClassLoader.hashCode());
+        result = prime * result + ((importMode == null) ? 0 : importMode.hashCode());
+        result = prime * result + ((listener == null) ? 0 : listener.hashCode());
+        result = prime * result + (nonRecursive ? 1231 : 1237);
+        result = prime * result + ((patchDirectory == null) ? 0 : patchDirectory.hashCode());
+        result = prime * result + (patchKeepInRepo ? 1231 : 1237);
+        result = prime * result + ((patchParentPath == null) ? 0 : patchParentPath.hashCode());
+        result = prime * result + ((pathMapping == null) ? 0 : pathMapping.hashCode());
+        result = prime * result + (strict ? 1231 : 1237);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ImportOptions other = (ImportOptions) obj;
+        if (acHandling != other.acHandling)
+            return false;
+        if (autoSave != other.autoSave)
+            return false;
+        if (cndPattern == null) {
+            if (other.cndPattern != null)
+                return false;
+        } else if (!cndPattern.pattern().equals(other.cndPattern.pattern()))
+            return false;
+        if (cugHandling != other.cugHandling)
+            return false;
+        if (dependencyHandling != other.dependencyHandling)
+            return false;
+        if (dryRun != other.dryRun)
+            return false;
+        if (filter == null) {
+            if (other.filter != null)
+                return false;
+        } else if (!filter.equals(other.filter))
+            return false;
+        if (hookClassLoader == null) {
+            if (other.hookClassLoader != null)
+                return false;
+        } else if (!hookClassLoader.equals(other.hookClassLoader))
+            return false;
+        if (importMode != other.importMode)
+            return false;
+        if (listener == null) {
+            if (other.listener != null)
+                return false;
+        } else if (!listener.equals(other.listener))
+            return false;
+        if (nonRecursive != other.nonRecursive)
+            return false;
+        if (patchDirectory == null) {
+            if (other.patchDirectory != null)
+                return false;
+        } else if (!patchDirectory.equals(other.patchDirectory))
+            return false;
+        if (patchKeepInRepo != other.patchKeepInRepo)
+            return false;
+        if (patchParentPath == null) {
+            if (other.patchParentPath != null)
+                return false;
+        } else if (!patchParentPath.equals(other.patchParentPath))
+            return false;
+        if (pathMapping == null) {
+            if (other.pathMapping != null)
+                return false;
+        } else if (!pathMapping.equals(other.pathMapping))
+            return false;
+        if (strict != other.strict)
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "ImportOptions [strict=" + strict + ", " + (listener != null ? "listener="
+ listener + ", " : "")
+                + (patchParentPath != null ? "patchParentPath=" + patchParentPath + ", "
: "")
+                + (patchDirectory != null ? "patchDirectory=" + patchDirectory + ", " : "")
+ "patchKeepInRepo=" + patchKeepInRepo
+                + ", nonRecursive=" + nonRecursive + ", dryRun=" + dryRun + ", autoSave="
+ autoSave + ", "
+                + (acHandling != null ? "acHandling=" + acHandling + ", " : "")
+                + (cugHandling != null ? "cugHandling=" + cugHandling + ", " : "")
+                + (importMode != null ? "importMode=" + importMode + ", " : "")
+                + (cndPattern != null ? "cndPattern=" + cndPattern + ", " : "") + (filter
!= null ? "filter=" + filter + ", " : "")
+                + (hookClassLoader != null ? "hookClassLoader=" + hookClassLoader + ", "
: "")
+                + (pathMapping != null ? "pathMapping=" + pathMapping + ", " : "")
+                + (dependencyHandling != null ? "dependencyHandling=" + dependencyHandling
: "") + "]";
+    }
+    
+    
 }
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTask.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTask.java
index 15c0b31..42396c4 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTask.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTask.java
@@ -90,6 +90,13 @@ public interface PackageTask {
     Type getType();
 
     /**
+     * Returns the task optional options.
+     * @return the task options (may be null).
+     */
+    @Nullable
+    PackageTaskOptions getOptions();
+
+    /**
      * Returns the task state
      * @return the task state
      */
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
index 29d1666..925b951 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
@@ -37,9 +37,17 @@ public interface PackageTaskBuilder {
     /**
      * Sets the type of this task
      * @param type the type
-     * @return this.
+     * @return the parent execution plan builder.
      */
     @NotNull
     ExecutionPlanBuilder with(@NotNull PackageTask.Type type);
+    
+    /**
+     * Set the optional options for the package task
+     * @param options the options
+     * @return this.
+     */
+    @NotNull
+    PackageTaskBuilder withOptions(@NotNull PackageTaskOptions options);
 
 }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskOptions.java
similarity index 59%
copy from vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
copy to vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskOptions.java
index 29d1666..0098d76 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskBuilder.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageTaskOptions.java
@@ -16,30 +16,6 @@
  */
 package org.apache.jackrabbit.vault.packaging.registry;
 
-import org.apache.jackrabbit.vault.packaging.PackageId;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ProviderType;
-
-/**
- * {@code ExecutionTaskBuilder}...
- */
-@ProviderType
-public interface PackageTaskBuilder {
-
-    /**
-     * Sets the package id of this task.
-     * @param id the package id
-     * @return this.
-     */
-    @NotNull
-    PackageTaskBuilder with(@NotNull PackageId id);
-
-    /**
-     * Sets the type of this task
-     * @param type the type
-     * @return this.
-     */
-    @NotNull
-    ExecutionPlanBuilder with(@NotNull PackageTask.Type type);
+public interface PackageTaskOptions {
 
 }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java
index 5fdca4e..2bce5fb 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java
@@ -48,11 +48,13 @@ import org.apache.jackrabbit.vault.packaging.registry.ExecutionPlanBuilder;
 import org.apache.jackrabbit.vault.packaging.registry.PackageRegistry;
 import org.apache.jackrabbit.vault.packaging.registry.PackageTask;
 import org.apache.jackrabbit.vault.packaging.registry.PackageTaskBuilder;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTaskOptions;
 import org.apache.jackrabbit.vault.packaging.registry.RegisteredPackage;
 import org.apache.jackrabbit.vault.util.RejectingEntityResolver;
 import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -71,7 +73,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder {
      */
     private static final Logger log = LoggerFactory.getLogger(ExecutionPlanBuilderImpl.class);
 
-    private final static String ATTR_VERSION = "version";
+    private static final String ATTR_VERSION = "version";
     private static final String TAG_EXECUTION_PLAN = "executionPlan";
     private static final String TAG_TASK = "task";
     private static final String ATTR_CMD = "cmd";
@@ -81,10 +83,12 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
 
     protected double version = SUPPORTED_VERSION;
 
-    private final List<TaskBuilder> tasks = new LinkedList<TaskBuilder>();
+    private final List<TaskBuilder> tasks = new LinkedList<>();
 
     private final PackageRegistry registry;
 
+    private final PackageTaskOptionsSerializer optionsSerializer;
+
     private Session session;
 
     private ProgressTrackerListener listener;
@@ -95,6 +99,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder {
 
     ExecutionPlanBuilderImpl(PackageRegistry registry) {
         this.registry = registry;
+        optionsSerializer = new PackageTaskOptionsSerializer();
     }
 
     @NotNull
@@ -109,6 +114,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
                 writer.writeStartElement(TAG_TASK);
                 writer.writeAttribute(ATTR_CMD, task.getType().name().toLowerCase());
                 writer.writeAttribute(ATTR_PACKAGE_ID, task.getPackageId().toString());
+                optionsSerializer.save(writer, task.getOptions());
                 writer.writeEndElement();
             }
             writer.writeEndElement();
@@ -124,6 +130,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
     public ExecutionPlanBuilder load(@NotNull InputStream in) throws IOException {
         tasks.clear();
         try {
+            // TODO: use Stax for loading as well!
             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
             factory.setNamespaceAware(true);
             DocumentBuilder builder = factory.newDocumentBuilder();
@@ -168,7 +175,13 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
     private void readTask(Element elem) throws IOException {
         PackageTask.Type type = PackageTask.Type.valueOf(elem.getAttribute(ATTR_CMD).toUpperCase());
         PackageId id = PackageId.fromString(elem.getAttribute(ATTR_PACKAGE_ID));
-        addTask().with(id).with(type);
+        
+        PackageTaskBuilder packageTaskBuilder = addTask().with(id);
+        PackageTaskOptions options = optionsSerializer.load(elem);
+        if (options != null) {
+            packageTaskBuilder.withOptions(options);
+        }
+        packageTaskBuilder.with(type);
     }
 
     @NotNull
@@ -197,10 +210,10 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
     @NotNull
     @Override
     public ExecutionPlanBuilder validate() throws IOException, PackageException {
-        Map<PackageId, PackageTask> installTasks = new HashMap<PackageId, PackageTask>();
-        Map<PackageId, PackageTask> uninstallTasks = new HashMap<PackageId, PackageTask>();
-        Map<PackageId, PackageTask> removeTasks = new HashMap<PackageId, PackageTask>();
-        List<PackageTask> packageTasks = new ArrayList<PackageTask>(tasks.size());
+        Map<PackageId, PackageTask> installTasks = new HashMap<>();
+        Map<PackageId, PackageTask> uninstallTasks = new HashMap<>();
+        Map<PackageId, PackageTask> removeTasks = new HashMap<>();
+        List<PackageTask> packageTasks = new ArrayList<>(tasks.size());
         for (TaskBuilder task: tasks) {
             if (task.id == null || task.type == null) {
                 throw new PackageException("task builder must have package id and type defined.");
@@ -208,7 +221,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
             if (!registry.contains(task.id)) {
                 throw new NoSuchPackageException("No such package: " + task.id);
             }
-            PackageTaskImpl pTask = new PackageTaskImpl(task.id, task.type);
+            PackageTaskImpl pTask = new PackageTaskImpl(task.id, task.type, task.option);
             // very simple task resolution: uninstall -> remove -> install/extract
             switch (task.type) {
                 case INSTALL:
@@ -225,14 +238,15 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
         }
 
         for (PackageId id: uninstallTasks.keySet().toArray(new PackageId[uninstallTasks.size()]))
{
-            resolveUninstall(id, packageTasks, uninstallTasks, new HashSet<PackageId>());
+            resolveUninstall(id, packageTasks, uninstallTasks, new HashSet<>(), uninstallTasks.get(id).getOptions());
         }
 
         // todo: validate remove
         packageTasks.addAll(removeTasks.values());
 
         for (PackageId id: installTasks.keySet().toArray(new PackageId[installTasks.size()]))
{
-            resolveInstall(id, packageTasks, installTasks, new HashSet<PackageId>(),
installTasks.get(id).getType());
+            PackageTask task = installTasks.get(id);
+            resolveInstall(id, packageTasks, installTasks, new HashSet<>(), task.getType(),
task.getOptions());
         }
 
         for (PackageTask task: packageTasks) {
@@ -243,7 +257,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
         return this;
     }
 
-    private void resolveInstall(PackageId id, List<PackageTask> packageTasks, Map<PackageId,
PackageTask> installTasks, Set<PackageId> resolved, PackageTask.Type type) throws
IOException, PackageException {
+    private void resolveInstall(PackageId id, List<PackageTask> packageTasks, Map<PackageId,
PackageTask> installTasks, Set<PackageId> resolved, PackageTask.Type type, @Nullable
PackageTaskOptions option) throws IOException, PackageException {
         if (resolved.contains(id)) {
             throw new CyclicDependencyException("Package has cyclic dependencies: " + id);
         }
@@ -263,7 +277,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
                     continue;
                 }
             }
-            resolveInstall(depId, packageTasks, installTasks, resolved, type);
+            resolveInstall(depId, packageTasks, installTasks, resolved, type, option);
         }
         PackageTask task = installTasks.get(id);
         if (task == PackageTaskImpl.MARKER) {
@@ -274,7 +288,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
             if (task == null) {
                 // package is not registered in plan, but need to be installed
                 // due to dependency
-                task = new PackageTaskImpl(id, type);
+                task = new PackageTaskImpl(id, type, option);
             }
             packageTasks.add(task);
         }
@@ -282,7 +296,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
         installTasks.put(id, PackageTaskImpl.MARKER);
     }
 
-    private void resolveUninstall(PackageId id, List<PackageTask> packageTasks, Map<PackageId,
PackageTask> uninstallTasks, Set<PackageId> resolved) throws IOException, PackageException
{
+    private void resolveUninstall(PackageId id, List<PackageTask> packageTasks, Map<PackageId,
PackageTask> uninstallTasks, Set<PackageId> resolved, @Nullable PackageTaskOptions
option) throws IOException, PackageException {
         if (resolved.contains(id)) {
             throw new CyclicDependencyException("Package has cyclic dependencies: " + id);
         }
@@ -298,7 +312,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
                     continue;
                 }
             }
-            resolveUninstall(depId, packageTasks, uninstallTasks, resolved);
+            resolveUninstall(depId, packageTasks, uninstallTasks, resolved, option);
         }
         PackageTask task = uninstallTasks.get(id);
         if (task == PackageTaskImpl.MARKER) {
@@ -307,7 +321,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
         }
         if (task == null) {
             // package is not registered in plan, but need to be installed due to dependency
-            task = new PackageTaskImpl(id, PackageTask.Type.UNINSTALL);
+            task = new PackageTaskImpl(id, PackageTask.Type.UNINSTALL, option);
         }
         packageTasks.add(task);
         // mark as processed
@@ -334,6 +348,7 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
     private class TaskBuilder implements PackageTaskBuilder {
         private PackageId id;
         private PackageTask.Type type;
+        private PackageTaskOptions option;
 
         public PackageTaskBuilder with(@NotNull PackageId id) {
             this.id = id;
@@ -346,6 +361,13 @@ public class ExecutionPlanBuilderImpl implements ExecutionPlanBuilder
{
             this.type = type;
             return ExecutionPlanBuilderImpl.this;
         }
+
+        @Override
+        @NotNull
+        public PackageTaskBuilder withOptions(@NotNull PackageTaskOptions option) {
+            this.option = option;
+            return this;
+        }
     }
 
     @Override
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskImpl.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskImpl.java
index ed1fdae..391910c 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskImpl.java
@@ -25,7 +25,9 @@ import org.apache.jackrabbit.vault.packaging.PackageException;
 import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.registry.PackageRegistry;
 import org.apache.jackrabbit.vault.packaging.registry.PackageTask;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTaskOptions;
 import org.apache.jackrabbit.vault.packaging.registry.RegisteredPackage;
+import org.apache.jackrabbit.vault.packaging.registry.taskoption.ImportOptionsPackageTaskOption;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -41,19 +43,22 @@ public class PackageTaskImpl implements PackageTask {
      */
     private static final Logger log = LoggerFactory.getLogger(PackageTaskImpl.class);
 
-    final static PackageTaskImpl MARKER = new PackageTaskImpl(new PackageId("", "" ,""),
Type.INSTALL);
+    final static PackageTaskImpl MARKER = new PackageTaskImpl(new PackageId("", "" ,""),
Type.INSTALL, null);
 
     private final PackageId id;
 
     private final Type type;
 
-    private State state = State.NEW;
+    private final PackageTaskOptions options;
+
+    State state = State.NEW;
 
     private Throwable error;
 
-    PackageTaskImpl(@NotNull PackageId id, @NotNull Type type) {
+    PackageTaskImpl(@NotNull PackageId id, @NotNull Type type, @Nullable PackageTaskOptions
options) {
         this.id = id;
         this.type = type;
+        this.options = options;
     }
 
     @NotNull
@@ -68,6 +73,12 @@ public class PackageTaskImpl implements PackageTask {
         return type;
     }
 
+    @Nullable
+    @Override
+    public PackageTaskOptions getOptions() {
+        return options;
+    }
+
     @NotNull
     @Override
     public State getState() {
@@ -82,10 +93,9 @@ public class PackageTaskImpl implements PackageTask {
 
     @Override
     public String toString() {
-        return "PackageTaskImpl{" + "id=" + id +
-                ", type=" + type +
-                ", state=" + state +
-                '}';
+        return "PackageTaskImpl [" + (id != null ? "id=" + id + ", " : "") + (type != null
? "type=" + type + ", " : "")
+                + (options != null ? "option=" + options + ", " : "") + (state != null ?
"state=" + state + ", " : "")
+                + (error != null ? "error=" + error : "") + "]";
     }
 
     void execute(ExecutionPlanImpl executionPlan) {
@@ -164,7 +174,12 @@ public class PackageTaskImpl implements PackageTask {
      * @throws PackageException if a package error occurs
      */
     private void doInstall(ExecutionPlanImpl plan, boolean extract) throws IOException, PackageException
{
-        ImportOptions opts = new ImportOptions();
+        final ImportOptions opts;
+        if (options instanceof ImportOptionsPackageTaskOption) {
+            opts = ((ImportOptionsPackageTaskOption) options).getImportOptions().copy();
+        } else {
+            opts = new ImportOptions();
+        }
         opts.setListener(plan.getListener());
         // execution plan resolution already has resolved all dependencies, so there is no
need to use best effort here.
         opts.setDependencyHandling(DependencyHandling.STRICT);
@@ -180,5 +195,47 @@ public class PackageTaskImpl implements PackageTask {
         }
     }
 
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((error == null) ? 0 : error.hashCode());
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
+        result = prime * result + ((options == null) ? 0 : options.hashCode());
+        result = prime * result + ((state == null) ? 0 : state.hashCode());
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        PackageTaskImpl other = (PackageTaskImpl) obj;
+        if (error == null) {
+            if (other.error != null)
+                return false;
+        } else if (!error.equals(other.error))
+            return false;
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
+            return false;
+        if (options == null) {
+            if (other.options != null)
+                return false;
+        } else if (!options.equals(other.options))
+            return false;
+        if (state != other.state)
+            return false;
+        if (type != other.type)
+            return false;
+        return true;
+    }
 
 }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskOptionsSerializer.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskOptionsSerializer.java
new file mode 100644
index 0000000..1826ce7
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/PackageTaskOptionsSerializer.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.vault.packaging.registry.impl;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTaskOptions;
+import org.apache.jackrabbit.vault.packaging.registry.taskoption.ImportOptionsPackageTaskOption;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class PackageTaskOptionsSerializer {
+
+    private static final String TAG_OPTIONS = "options";
+    private static final String ATTR_TYPE = "type";
+    private static final String TAG_ACHANDLING = "acHandling";
+    private static final String TAG_IS_STRICT = "isStrict";
+    
+    enum Type {
+        ImportOptions;
+
+        static Type fromClass(PackageTaskOptions options) {
+            if (options instanceof ImportOptionsPackageTaskOption) {
+                return ImportOptions;
+            } else {
+                throw new IllegalStateException("Unsupported task option class " + options.getClass());
+            }
+        }
+    }
+
+    public PackageTaskOptions load(Element element) {
+        Element childElement = getFirstElementByTagName(TAG_OPTIONS, element);
+        if (childElement == null) {
+            return null;
+        }
+        final PackageTaskOptions options;
+        switch (Type.valueOf(childElement.getAttribute(ATTR_TYPE))) {
+            case ImportOptions:
+                options = loadImportOptions(childElement);
+                break;
+            default:
+                throw new IllegalArgumentException("Wrong type used");
+        }
+        return options;
+    }
+
+    public void save(XMLStreamWriter writer, PackageTaskOptions options) throws XMLStreamException
{
+        if (options == null) {
+            return;
+        }
+        writer.writeStartElement(TAG_OPTIONS);
+        Type type = Type.fromClass(options);
+        writer.writeAttribute(ATTR_TYPE, type.name());
+        switch (type) {
+            case ImportOptions:
+                save(writer, (ImportOptionsPackageTaskOption) options);
+                break;
+        }
+        writer.writeEndElement();
+    }
+
+    public void save(XMLStreamWriter writer, ImportOptionsPackageTaskOption options) throws
XMLStreamException {
+        writer.writeStartElement(TAG_IS_STRICT);
+        writer.writeCharacters(Boolean.toString(options.getImportOptions().isStrict()));
+        writer.writeEndElement();
+        AccessControlHandling acHandling = options.getImportOptions().getAccessControlHandling();
+        if (acHandling != null) {
+            writer.writeStartElement(TAG_ACHANDLING);
+            writer.writeCharacters(acHandling.toString());
+            writer.writeEndElement();
+        }
+    }
+
+    public ImportOptionsPackageTaskOption loadImportOptions(Element element) {
+        ImportOptions options = new ImportOptions();
+        Element childElement = getFirstElementByTagName(TAG_IS_STRICT, element);
+        if (childElement != null) {
+            options.setStrict(Boolean.parseBoolean(childElement.getTextContent()));
+        }
+        childElement = getFirstElementByTagName(TAG_ACHANDLING, element);
+        if (childElement != null) {
+            options.setAccessControlHandling(AccessControlHandling.valueOf(childElement.getTextContent()));
+        }
+        return new ImportOptionsPackageTaskOption(options);
+    }
+
+    private static final Element getFirstElementByTagName(String name, Element element) {
+        NodeList nodeList = element.getElementsByTagName(name);
+        if (nodeList.getLength() == 0) {
+            return null;
+        }
+        return (Element)nodeList.item(0);
+    }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/taskoption/ImportOptionsPackageTaskOption.java
b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/taskoption/ImportOptionsPackageTaskOption.java
new file mode 100644
index 0000000..114d9bc
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/taskoption/ImportOptionsPackageTaskOption.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.vault.packaging.registry.taskoption;
+
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTaskOptions;
+
+public class ImportOptionsPackageTaskOption implements PackageTaskOptions {
+
+    private final ImportOptions importOptions;
+
+    public ImportOptionsPackageTaskOption(ImportOptions importOptions) {
+        super();
+        this.importOptions = importOptions;
+    }
+
+    public ImportOptions getImportOptions() {
+        return importOptions;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((importOptions == null) ? 0 : importOptions.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ImportOptionsPackageTaskOption other = (ImportOptionsPackageTaskOption) obj;
+        if (importOptions == null) {
+            if (other.importOptions != null)
+                return false;
+        } else if (!importOptions.equals(other.importOptions))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "ImportOptionsPackageTaskOption [" + (importOptions != null ? "importOptions="
+ importOptions : "") + "]";
+    }
+
+    
+}
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImplTest.java
b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImplTest.java
new file mode 100644
index 0000000..c9999d9
--- /dev/null
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImplTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.vault.packaging.registry.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.packaging.PackageException;
+import org.apache.jackrabbit.vault.packaging.registry.ExecutionPlan;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTask;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTask.State;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTask.Type;
+import org.apache.jackrabbit.vault.packaging.registry.PackageTaskOptions;
+import org.apache.jackrabbit.vault.packaging.registry.taskoption.ImportOptionsPackageTaskOption;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ExecutionPlanBuilderImplTest {
+
+    private ExecutionPlanBuilderImpl builder;
+
+    @Mock
+    private Session session;
+    @Before
+    public void setUp() {
+        builder = new ExecutionPlanBuilderImpl(new MockPackageRegistry(MockPackageRegistry.NEW_PACKAGE_ID));
+    }
+
+    @Test
+    public void testSaveAndLoad() throws IOException, PackageException {
+        ImportOptions importOptions = new ImportOptions();
+        importOptions.setStrict(true);
+        PackageTaskOptions options = new ImportOptionsPackageTaskOption(importOptions);
+        builder.addTask().with(MockPackageRegistry.NEW_PACKAGE_ID).withOptions(options).with(PackageTask.Type.INSTALL);
+        
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        builder.save(out);
+        builder = new ExecutionPlanBuilderImpl(new MockPackageRegistry(MockPackageRegistry.NEW_PACKAGE_ID));
+        builder.load(new ByteArrayInputStream(out.toByteArray()));
+        builder.with(session);
+        ExecutionPlan plan = builder.execute();
+        importOptions.setStrict(true);
+        PackageTaskImpl expectedTask = new PackageTaskImpl(MockPackageRegistry.NEW_PACKAGE_ID,
Type.INSTALL, options);
+        expectedTask.state = State.COMPLETED;
+        MatcherAssert.assertThat(plan.getTasks(), Matchers.contains(expectedTask));
+    }
+}
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/MockPackageRegistry.java
b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/MockPackageRegistry.java
index fa01eb1..9ac5795 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/MockPackageRegistry.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/MockPackageRegistry.java
@@ -118,7 +118,10 @@ public final class MockPackageRegistry implements PackageRegistry {
     public @NotNull DependencyReport analyzeDependencies(@NotNull PackageId id, boolean onlyInstalled)
             throws IOException, NoSuchPackageException {
         if (containedPackageIdsAndDependencies.containsKey(id)) {
-            return Mockito.mock(DependencyReport.class);
+            DependencyReport report = Mockito.mock(DependencyReport.class);
+            Mockito.when(report.getUnresolvedDependencies()).thenReturn(new Dependency[0]);
+            Mockito.when(report.getResolvedDependencies()).thenReturn(new PackageId[0]);
+            return report;
         } else {
             throw new NoSuchPackageException("Could not find package with id " + id);
         }


Mime
View raw message