You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/01/29 09:11:35 UTC

[jackrabbit-filevault] branch master updated: JCRVLT-499 parse dates with timezone designator "Z" correctly (#118)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git


The following commit(s) were added to refs/heads/master by this push:
     new dad1034  JCRVLT-499 parse dates with timezone designator "Z" correctly (#118)
dad1034 is described below

commit dad1034ad11ec135a1e83de0affb0ce243fccfac
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Jan 29 10:11:27 2021 +0100

    JCRVLT-499 parse dates with timezone designator "Z" correctly (#118)
    
    rely on PackageProperties API for reading from package
    expose unwrapped package properties without type conversions
---
 .../vault/packaging/JcrPackageDefinition.java      |  89 +-------
 .../vault/packaging/PackageProperties.java         |  12 +
 .../packaging/impl/JcrPackageDefinitionImpl.java   | 244 +++++++++++----------
 .../impl/JcrPackageDefinitionMetaInf.java          |  55 +++++
 .../packaging/impl/PackagePropertiesImpl.java      |  19 ++
 .../jackrabbit/vault/packaging/package-info.java   |   2 +-
 .../impl/JcrPackageDefinitionImplTest.java         |  70 ++++++
 .../META-INF/vault/properties.xml                  |   8 +-
 8 files changed, 296 insertions(+), 203 deletions(-)

diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/JcrPackageDefinition.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/JcrPackageDefinition.java
index ea120b1..3c157e7 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/JcrPackageDefinition.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/JcrPackageDefinition.java
@@ -35,7 +35,7 @@ import org.osgi.annotation.versioning.ProviderType;
  * Specifies the interface of a package definition stored in the repository.
  */
 @ProviderType
-public interface JcrPackageDefinition {
+public interface JcrPackageDefinition extends PackageProperties {
 
     /**
      * Property name of the last unpacked date
@@ -189,13 +189,6 @@ public interface JcrPackageDefinition {
     Node getNode();
 
     /**
-     * Returns the package id
-     * @return the package id
-     */
-    @NotNull
-    PackageId getId();
-
-    /**
      * Writes the properties derived from the package id to the content
      * @param id the package id
      * @param autoSave if {@code true} the changes are saved automatically.
@@ -236,13 +229,6 @@ public interface JcrPackageDefinition {
     void dumpCoverage(@NotNull ProgressTrackerListener listener) throws RepositoryException;
 
     /**
-     * Returns the dependencies stored in this definition
-     * @return the dependencies
-     */
-    @NotNull
-    Dependency[] getDependencies();
-
-    /**
      * Sets the dependencies to this definition and stores it in a node representation.
      * @param dependencies the package dependencies
      * @param autoSave if {@code true} the modifications are saved automatically.
@@ -313,50 +299,6 @@ public interface JcrPackageDefinition {
     void setFilter(@Nullable WorkspaceFilter filter, boolean autoSave);
 
     /**
-     * Returns the last modified date
-     * @return the last modified date
-     */
-    @Nullable
-    Calendar getLastModified();
-
-    /**
-     * Returns the last modified user id
-     * @return the last modified user id
-     */
-    @Nullable
-    String getLastModifiedBy();
-
-    /**
-     * Returns the created date
-     * @return the created date
-     */
-    @Nullable
-    Calendar getCreated();
-
-    /**
-     * Returns the creator user id
-     * @return the creator
-     */
-    @Nullable
-    String getCreatedBy();
-
-    /**
-     * Returns the last wrapped date
-     * @return the last wrapped date
-     * @since 2.2.22
-     */
-    @Nullable
-    Calendar getLastWrapped();
-
-    /**
-     * Returns the wrapper user id
-     * @return the wrapper
-     * @since 2.2.22
-     */
-    @Nullable
-    String getLastWrappedBy();
-
-    /**
      * Returns the last unwrapped date
      * @return the last unwrapped date
      */
@@ -385,42 +327,17 @@ public interface JcrPackageDefinition {
     String getLastUnpackedBy();
 
     /**
-     * Returns {@code true} if this package needs a admin user to install it.
-     * @return the "requires root" flag
-     * @deprecated
-     */
-    @Deprecated
-    boolean requiresRoot();
-
-    /**
-     * Returns {@code true} if this package needs restart after installation.
-     * @return the "requires restart" flag.
-     */
-    boolean requiresRestart();
-
-    /**
      * Returns the access control handling defined in the definition, or {@code null}
      * if not defined.
      * @return the access control handling or {@code null}
      * @since 2.3.2
+     * @deprecated Use {@link PackageProperties#getACHandling} retrieved via {@link #getMetaInf()} and {@link MetaInf#getPackageProperties()}.
      */
     @Nullable
+    @Deprecated
     AccessControlHandling getAccessControlHandling();
 
     /**
-     * Returns the description of this package
-     * @return the description
-     */
-    @Nullable
-    String getDescription();
-
-    /**
-     * Returns the build count of this package
-     * @return the build count.
-     */
-    long getBuildCount();
-
-    /**
      * Returns the meta inf of this package
      * @return the meta inf
      * @throws RepositoryException if an error occurs.
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
index fa10e85..9c9c6d9 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
@@ -267,6 +267,12 @@ public interface PackageProperties {
     boolean requiresRoot();
 
     /**
+     * Returns {@code true} if this package requires a restart after installation.
+     * @return {@code true} if this package requires a restart after installation.
+     */
+    boolean requiresRestart();
+
+    /**
      * Returns an unmodifiable list of dependencies
      * @return list of dependencies
      */
@@ -317,4 +323,10 @@ public interface PackageProperties {
      * @return dependencies locations as map
      */
     @NotNull Map<PackageId, URI> getDependenciesLocations();
+
+    /**
+     * Returns the build count of this package
+     * @return the build count.
+     */
+    long getBuildCount();
 }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImpl.java
index ae05b63..48a6294 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImpl.java
@@ -18,12 +18,15 @@
 package org.apache.jackrabbit.vault.packaging.impl;
 
 import java.io.IOException;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 
 import javax.jcr.Node;
@@ -40,8 +43,6 @@ import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
 import org.apache.jackrabbit.vault.fs.api.RepositoryAddress;
 import org.apache.jackrabbit.vault.fs.api.VaultFileSystem;
 import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
-import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
-import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
 import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
 import org.apache.jackrabbit.vault.fs.config.MetaInf;
 import org.apache.jackrabbit.vault.fs.io.AbstractExporter;
@@ -52,6 +53,9 @@ import org.apache.jackrabbit.vault.packaging.Dependency;
 import org.apache.jackrabbit.vault.packaging.ExportPostProcessor;
 import org.apache.jackrabbit.vault.packaging.JcrPackageDefinition;
 import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.packaging.PackageType;
+import org.apache.jackrabbit.vault.packaging.SubPackageHandling;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
 import org.apache.jackrabbit.vault.util.Constants;
 import org.apache.jackrabbit.vault.util.Text;
@@ -99,6 +103,7 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
     /**
      * {@inheritDoc}
      */
+    @SuppressWarnings("deprecation")
     public PackageId getId() {
         String group = get(PN_GROUP);
         String name = get(PN_NAME);
@@ -198,9 +203,6 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
         return mod.after(uw);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     public void unwrap(VaultPackage pack, boolean force)
             throws RepositoryException, IOException {
         unwrap(pack, force, true);
@@ -222,9 +224,6 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
     public void unwrap(Archive archive, boolean autoSave)
             throws RepositoryException, IOException {
         if (archive != null) {
@@ -237,7 +236,7 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
                 JcrWorkspaceFilter.saveFilter(inf.getFilter(), defNode, false);
             }
             if (inf.getProperties() != null) {
-                writeProperties(inf.getProperties());
+                writeProperties(inf.getPackageProperties());
             }
         }
         defNode.setProperty("unwrapped", (Value) null);
@@ -354,69 +353,19 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
     }
 
     /**
-     * Load the given properties from the content
-     * @param props the properties to load
-     */
-    private void loadProperties(Properties props) {
-        PackageId id = getId();
-        setProperty(props, VaultPackage.NAME_VERSION, id.getVersionString());
-        setProperty(props, VaultPackage.NAME_NAME, id.getName());
-        setProperty(props, VaultPackage.NAME_GROUP, id.getGroup());
-        setProperty(props, VaultPackage.NAME_BUILD_COUNT, get(PN_BUILD_COUNT));
-        setProperty(props, VaultPackage.NAME_DESCRIPTION, get(PN_DESCRIPTION));
-        setProperty(props, VaultPackage.NAME_REQUIRES_ROOT, get(PN_REQUIRES_ROOT));
-        setProperty(props, VaultPackage.NAME_REQUIRES_RESTART, get(PN_REQUIRES_RESTART));
-        setProperty(props, VaultPackage.NAME_LAST_MODIFIED, getCalendar(PN_LASTMODIFIED));
-        setProperty(props, VaultPackage.NAME_LAST_MODIFIED_BY, get(PN_LASTMODIFIED_BY));
-        setProperty(props, VaultPackage.NAME_LAST_WRAPPED, getCalendar(PN_LAST_WRAPPED));
-        setProperty(props, VaultPackage.NAME_LAST_WRAPPED_BY, get(PN_LAST_WRAPPED_BY));
-        setProperty(props, VaultPackage.NAME_CREATED, getCalendar(PN_CREATED));
-        setProperty(props, VaultPackage.NAME_CREATED_BY, get(PN_CREATED_BY));
-        setProperty(props, VaultPackage.NAME_DEPENDENCIES, Dependency.toString(getDependencies()));
-        setProperty(props, VaultPackage.NAME_AC_HANDLING, get(PN_AC_HANDLING));
-        setProperty(props, VaultPackage.NAME_CND_PATTERN, get(PN_CND_PATTERN));
-    }
-
-    /**
-     * internal method that adds or removes a property
-     * @param props the properties
-     * @param name the name of the properties
-     * @param value the value
-     */
-    private static void setProperty(Properties props, String name, String value) {
-        if (value == null) {
-            props.remove(name);
-        } else {
-            props.put(name, value);
-        }
-    }
-
-    /**
-     * internal method that adds or removes a property
-     * @param props the properties
-     * @param name the name of the properties
-     * @param value the value
-     */
-    private static void setProperty(Properties props, String name, Calendar value) {
-        if (value == null) {
-            props.remove(name);
-        } else {
-            props.put(name, ISO8601.format(value));
-        }
-    }
-
-    /**
      * Writes the given properties to the content.
      * @param props the properties
      */
-    private void writeProperties(Properties props) {
+    private void writeProperties(PackageProperties props) {
         try {
             // sanitize lastModBy property due to former bug that used the
             // lastMod value
+            final String lastModifiedBy;
             if (props.getProperty(VaultPackage.NAME_LAST_MODIFIED) != null
                     && props.getProperty(VaultPackage.NAME_LAST_MODIFIED).equals(props.getProperty(VaultPackage.NAME_LAST_MODIFIED_BY))) {
-                props = new Properties(props);
-                props.setProperty(VaultPackage.NAME_LAST_MODIFIED_BY, "unknown");
+                lastModifiedBy = "unknown";
+            } else {
+                lastModifiedBy = props.getLastModifiedBy();
             }
 
             // Note that the 'path', 'group' and 'name' properties are usually
@@ -424,10 +373,10 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
             // however, if a definition is unwrapped at another location, eg
             // in package-share, it is convenient if the original properties
             // are available in the content.
-            defNode.setProperty(PN_VERSION, props.getProperty(VaultPackage.NAME_VERSION));
+            defNode.setProperty(PN_VERSION, props.getId().getVersionString());
             defNode.setProperty(PN_BUILD_COUNT, props.getProperty(VaultPackage.NAME_BUILD_COUNT));
-            defNode.setProperty(PN_NAME, props.getProperty(VaultPackage.NAME_NAME));
-            defNode.setProperty(PN_GROUP, props.getProperty(VaultPackage.NAME_GROUP));
+            defNode.setProperty(PN_NAME, props.getId().getName());
+            defNode.setProperty(PN_GROUP, props.getId().getGroup());
             String deps = props.getProperty(VaultPackage.NAME_DEPENDENCIES);
             if (defNode.hasProperty(PN_DEPENDENCIES)) {
                 defNode.getProperty(PN_DEPENDENCIES).remove();
@@ -441,15 +390,15 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
                 }
                 defNode.setProperty(PN_DEPENDENCIES, ds.toArray(new String[ds.size()]));
             }
-            defNode.setProperty(PN_DESCRIPTION, props.getProperty(VaultPackage.NAME_DESCRIPTION));
-            defNode.setProperty(PN_REQUIRES_ROOT, Boolean.valueOf(props.getProperty(VaultPackage.NAME_REQUIRES_ROOT, "false")));
-            defNode.setProperty(PN_REQUIRES_RESTART, Boolean.valueOf(props.getProperty(VaultPackage.NAME_REQUIRES_RESTART, "false")));
-            defNode.setProperty(PN_LASTMODIFIED, getDate(props.getProperty(VaultPackage.NAME_LAST_MODIFIED)));
-            defNode.setProperty(PN_LASTMODIFIED_BY, props.getProperty(VaultPackage.NAME_LAST_MODIFIED_BY));
-            defNode.setProperty(PN_CREATED, getDate(props.getProperty(VaultPackage.NAME_CREATED)));
-            defNode.setProperty(PN_CREATED_BY, props.getProperty(VaultPackage.NAME_CREATED_BY));
-            defNode.setProperty(PN_LAST_WRAPPED, getDate(props.getProperty(VaultPackage.NAME_LAST_WRAPPED)));
-            defNode.setProperty(PN_LAST_WRAPPED_BY, props.getProperty(VaultPackage.NAME_LAST_WRAPPED_BY));
+            defNode.setProperty(PN_DESCRIPTION, props.getDescription());
+            defNode.setProperty(PN_REQUIRES_ROOT, props.requiresRoot());
+            defNode.setProperty(PN_REQUIRES_RESTART, props.requiresRestart());
+            defNode.setProperty(PN_LASTMODIFIED, props.getLastModified());
+            defNode.setProperty(PN_LASTMODIFIED_BY, lastModifiedBy);
+            defNode.setProperty(PN_CREATED, props.getCreated());
+            defNode.setProperty(PN_CREATED_BY, props.getCreatedBy());
+            defNode.setProperty(PN_LAST_WRAPPED, props.getLastWrapped());
+            defNode.setProperty(PN_LAST_WRAPPED_BY,props.getLastWrappedBy());
             defNode.setProperty(PN_AC_HANDLING, props.getProperty(VaultPackage.NAME_AC_HANDLING));
             defNode.setProperty(PN_CND_PATTERN, props.getProperty(VaultPackage.NAME_CND_PATTERN));
             defNode.setProperty(PN_DISABLE_INTERMEDIATE_SAVE, props.getProperty(VaultPackage.NAME_DISABLE_INTERMEDIATE_SAVE));
@@ -458,25 +407,6 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
         }
     }
 
-    /**
-     * Internal method that converts a ISO date to a calendar.
-     * @param iso the iso8601 formatted date
-     * @return the calendar or {@code null}
-     */
-    private static Calendar getDate(String iso) {
-        if (iso == null) {
-            return null;
-        }
-        // check for missing : in timezone part
-        String tzd = iso.substring(iso.length() - 4);
-        if (tzd.indexOf(':') < 0) {
-            iso = iso.substring(0, iso.length() - 4);
-            iso += tzd.substring(0, 2);
-            iso += ":";
-            iso += tzd.substring(2);
-        }
-        return ISO8601.parse(iso);
-    }
 
     /**
      * {@inheritDoc}
@@ -777,15 +707,7 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
      * {@inheritDoc}
      */
     public AccessControlHandling getAccessControlHandling() {
-        String acHandling = get(PN_AC_HANDLING);
-        try {
-            return acHandling == null
-                    ? null
-                    : AccessControlHandling.valueOf(acHandling.toUpperCase());
-        } catch (IllegalArgumentException e) {
-            log.warn("invalid access control handling in definition: {} of {}", acHandling, getId());
-            return null;
-        }
+        return getACHandling();
     }
 
     /**
@@ -808,19 +730,66 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
         }
     }
 
+
     /**
-     * {@inheritDoc}
+     * Load the given properties from the content
+     * @param props the properties to load
      */
-    public MetaInf getMetaInf() throws RepositoryException {
-        DefaultMetaInf inf = new DefaultMetaInf();
-        inf.setFilter(JcrWorkspaceFilter.loadFilter(defNode));
-
-        // add properties
+    private Properties loadLegacyProperties() {
         Properties props = new Properties();
-        loadProperties(props);
-        inf.setProperties(props);
+        PackageId id = getId();
+        setProperty(props, VaultPackage.NAME_VERSION, id.getVersionString());
+        setProperty(props, VaultPackage.NAME_NAME, id.getName());
+        setProperty(props, VaultPackage.NAME_GROUP, id.getGroup());
+        setProperty(props, VaultPackage.NAME_BUILD_COUNT, get(PN_BUILD_COUNT));
+        setProperty(props, VaultPackage.NAME_DESCRIPTION, get(PN_DESCRIPTION));
+        setProperty(props, VaultPackage.NAME_REQUIRES_ROOT, get(PN_REQUIRES_ROOT));
+        setProperty(props, VaultPackage.NAME_REQUIRES_RESTART, get(PN_REQUIRES_RESTART));
+        setProperty(props, VaultPackage.NAME_LAST_MODIFIED, getCalendar(PN_LASTMODIFIED));
+        setProperty(props, VaultPackage.NAME_LAST_MODIFIED_BY, get(PN_LASTMODIFIED_BY));
+        setProperty(props, VaultPackage.NAME_LAST_WRAPPED, getCalendar(PN_LAST_WRAPPED));
+        setProperty(props, VaultPackage.NAME_LAST_WRAPPED_BY, get(PN_LAST_WRAPPED_BY));
+        setProperty(props, VaultPackage.NAME_CREATED, getCalendar(PN_CREATED));
+        setProperty(props, VaultPackage.NAME_CREATED_BY, get(PN_CREATED_BY));
+        setProperty(props, VaultPackage.NAME_DEPENDENCIES, Dependency.toString(getDependencies()));
+        setProperty(props, VaultPackage.NAME_AC_HANDLING, get(PN_AC_HANDLING));
+        setProperty(props, VaultPackage.NAME_CND_PATTERN, get(PN_CND_PATTERN));
+        return props;
+    }
 
-        return inf;
+    /**
+     * internal method that adds or removes a property
+     * @param props the properties
+     * @param name the name of the properties
+     * @param value the value
+     */
+    private static void setProperty(Properties props, String name, String value) {
+        if (value == null) {
+            props.remove(name);
+        } else {
+            props.put(name, value);
+        }
+    }
+
+    /**
+     * internal method that adds or removes a property
+     * @param props the properties
+     * @param name the name of the properties
+     * @param value the value
+     */
+    private static void setProperty(Properties props, String name, Calendar value) {
+        if (value == null) {
+            props.remove(name);
+        } else {
+            props.put(name, ISO8601.format(value));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaInf getMetaInf() throws RepositoryException {
+        return new JcrPackageDefinitionMetaInf(defNode, this, loadLegacyProperties());
     }
 
     /**
@@ -981,4 +950,53 @@ public class JcrPackageDefinitionImpl implements JcrPackageDefinition {
             }
         }
     }
+
+    @Override
+    public Map<String, String> getExternalHooks() {
+        // not stored
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public AccessControlHandling getACHandling() {
+        String acHandling = get(PN_AC_HANDLING);
+        try {
+            return acHandling == null
+                    ? null
+                    : AccessControlHandling.valueOf(acHandling.toUpperCase());
+        } catch (IllegalArgumentException e) {
+            log.warn("invalid access control handling in definition: {} of {}", acHandling, getId());
+            return null;
+        }
+    }
+
+    @Override
+    public SubPackageHandling getSubPackageHandling() {
+        // not stored
+        return null;
+    }
+
+    
+    @Override
+    public Calendar getDateProperty(String name) {
+        return getCalendar(name);
+    }
+
+    @Override
+    public String getProperty(String name) {
+        return get(name);
+    }
+
+    @Override
+    public @Nullable PackageType getPackageType() {
+        // not stored
+        return null;
+    }
+
+    @Override
+    public @NotNull Map<PackageId, URI> getDependenciesLocations() {
+        // not stored
+        return Collections.emptyMap();
+    }
+
 }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionMetaInf.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionMetaInf.java
new file mode 100644
index 0000000..c3b371b
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionMetaInf.java
@@ -0,0 +1,55 @@
+/*
+ * 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.impl;
+
+import java.util.Properties;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+
+/**
+ * Only limited meta information like package properties and filters are exposed which are available
+ * from the underlying package definition node.
+ * Raw properties are exposed on a best effort basis, because the storage format is different from
+ * the one in {@code properties.xml}.
+ * Therefore it is recommended to use the high-level API exposed via {@link PackageProperties}.
+ */
+public class JcrPackageDefinitionMetaInf extends DefaultMetaInf  {
+
+    private final PackageProperties packageProperties;
+    private final Properties legacyProperties;
+
+    public JcrPackageDefinitionMetaInf(Node node, PackageProperties packageProperties, Properties legacyProperties) throws RepositoryException {
+        setFilter(JcrWorkspaceFilter.loadFilter(node));
+        this.packageProperties = packageProperties;
+        this.legacyProperties = legacyProperties;
+    }
+
+    @Override
+    public PackageProperties getPackageProperties() {
+        return packageProperties;
+    }
+
+    @Override
+    public Properties getProperties() {
+        return legacyProperties;
+    }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java
index 59d92cc..33062c1 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java
@@ -161,6 +161,14 @@ public abstract class PackagePropertiesImpl implements PackageProperties {
      * {@inheritDoc}
      */
     @Override
+    public boolean requiresRestart() {
+        return "true".equals(getProperty(NAME_REQUIRES_RESTART));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public Dependency[] getDependencies() {
         String deps = getProperty(NAME_DEPENDENCIES);
         if (deps == null) {
@@ -210,6 +218,7 @@ public abstract class PackagePropertiesImpl implements PackageProperties {
     @Override
     public Calendar getDateProperty(String name) {
         try {
+            // TODO: add timezone if not there?
             String p = getProperty(name);
             return p == null
                     ? null
@@ -271,6 +280,16 @@ public abstract class PackagePropertiesImpl implements PackageProperties {
         return hookClasses;
     }
 
+    @Override
+    public long getBuildCount() {
+        try {
+            return Long.parseLong(getProperty(NAME_BUILD_COUNT));
+        } catch (NumberFormatException e) {
+            log.warn("Invalid buildcount property, must be an integer");
+            return 0;
+        }
+    }
+
     protected abstract Properties getPropertiesMap();
 
 
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
index ef85b43..68b4700 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-@Version("2.10.0")
+@Version("2.11.0")
 package org.apache.jackrabbit.vault.packaging;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImplTest.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImplTest.java
new file mode 100644
index 0000000..3826833
--- /dev/null
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageDefinitionImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.packaging.integration.IntegrationTestBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class JcrPackageDefinitionImplTest extends IntegrationTestBase {
+
+    @Test
+    public void testUnwrapAndComparePackageProperties() throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException, IOException {
+        // new node
+        Node node = admin.getRootNode().addNode("packagedefinitiontest");
+        JcrPackageDefinitionImpl packageDefinition = new JcrPackageDefinitionImpl(node);
+        try (Archive archive = getFileArchive("/test-packages/principalbased.zip")) {
+            archive.open(false);
+            packageDefinition.unwrap(archive, false);
+            // now compare the package properties
+            assertPackagePropertiesEquals(archive.getMetaInf().getPackageProperties(), packageDefinition);
+            
+            Assert.assertEquals("bar", archive.getMetaInf().getPackageProperties().getProperty("foo"));
+            // custom properties are not part of the unwrapped node
+            Assert.assertNull(packageDefinition.getProperty("foo"));
+        }
+    }
+
+    void assertPackagePropertiesEquals(PackageProperties expectedProperties, PackageProperties actualProperties) {
+        Assert.assertEquals("lastModified is different", expectedProperties.getLastModified(), actualProperties.getLastModified());
+        Assert.assertEquals("lastModifiedBy is different", expectedProperties.getLastModifiedBy(), actualProperties.getLastModifiedBy());
+        Assert.assertEquals("created is different", expectedProperties.getCreated(), actualProperties.getCreated());
+        Assert.assertEquals("createdBy is different", expectedProperties.getCreatedBy(), actualProperties.getCreatedBy());
+        Assert.assertEquals("lastWrapped is different", expectedProperties.getLastWrapped(), actualProperties.getLastWrapped());
+        Assert.assertEquals("lastWrappedBy is different", expectedProperties.getLastWrappedBy(), actualProperties.getLastWrappedBy());
+        Assert.assertEquals("description is different", expectedProperties.getDescription(), actualProperties.getDescription());
+        Assert.assertEquals("acHandling is different", expectedProperties.getACHandling(), actualProperties.getACHandling());
+        Assert.assertEquals("id (group, name or version) is different", expectedProperties.getId(), actualProperties.getId());
+        Assert.assertArrayEquals("dependencies are different", expectedProperties.getDependencies(), actualProperties.getDependencies());
+        Assert.assertEquals("requiresRestart is different", expectedProperties.requiresRestart(), actualProperties.requiresRestart());
+        Assert.assertEquals("requiresRoot is different", expectedProperties.requiresRoot(), actualProperties.requiresRoot());
+    }
+}
diff --git a/vault-core/src/test/resources/test-packages/principalbased.zip/META-INF/vault/properties.xml b/vault-core/src/test/resources/test-packages/principalbased.zip/META-INF/vault/properties.xml
index 7716d3a..d7aaea8 100644
--- a/vault-core/src/test/resources/test-packages/principalbased.zip/META-INF/vault/properties.xml
+++ b/vault-core/src/test/resources/test-packages/principalbased.zip/META-INF/vault/properties.xml
@@ -4,17 +4,19 @@
 <comment>FileVault Package Properties</comment>
 <entry key="createdBy">admin</entry>
 <entry key="name">principalbased_overwrite</entry>
-<entry key="lastModified">2019-07-23T10:20:20.089+02:00</entry>
+<entry key="lastModified">2019-07-23T10:20:20.089Z</entry>
 <entry key="lastModifiedBy">admin</entry>
 <entry key="created">2019-07-23T10:20:20.170+02:00</entry>
 <entry key="buildCount">1</entry>
-<entry key="version"/>
+<entry key="version">2</entry>
 <entry key="packageType">content</entry>
-<entry key="dependencies"/>
+<entry key="dependencies">depgroup:dep1:1,depgroup:dep2:2</entry>
 <entry key="packageFormatVersion">2</entry>
 <entry key="description"/>
 <entry key="lastWrapped">2019-07-23T10:20:20.089+02:00</entry>
 <entry key="group">integrationtesting</entry>
 <entry key="lastWrappedBy">admin</entry>
 <entry key="acHandling">OVERWRITE</entry>
+<entry key="requiresRoot">true</entry>
+<entry key="foo">bar</entry>
 </properties>