SLING-11449: Use latest improvements of Oak 1.44.0 (#28)

diff --git a/pom.xml b/pom.xml
index fd803b0..f717752 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,9 +44,8 @@
     <properties>
         <site.jira.version.id>12314286</site.jira.version.id>
         <site.javadoc.exclude>**.internal.**</site.javadoc.exclude>
-        <!-- aok 1.10.0 was first version compatible with Jackrabbit API 2.18 (https://issues.apache.org/jira/browse/OAK-7943) -->
-        <!-- aok 1.42.0 first version to include JackrabbitSession.getParentOrNull in oak-jackrabbit-api (https://issues.apache.org/jira/browse/SLING-10011) -->
-        <oak.version>1.42.0</oak.version>
+        <!-- oak 1.44.0 first version to include JackrabbitNode.getPropertyOrNull in oak-jackrabbit-api (https://issues.apache.org/jira/browse/SLING-11449) -->
+        <oak.version>1.44.0</oak.version>
         <jackrabbit.version>2.18.0</jackrabbit.version><!-- required for direct binary access, https://issues.apache.org/jira/browse/JCR-4335 -->
         <project.build.outputTimestamp>1</project.build.outputTimestamp>
     </properties>
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java b/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
index 5ec28f3..87b2477 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
@@ -22,6 +22,7 @@
 import java.util.Map;
 
 import javax.jcr.Node;
+import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 
@@ -104,8 +105,9 @@
         final Object oldValue = this.valueCache.remove(key);
         try {
             final String name = escapeKeyName(key);
-            if (node.hasProperty(name)) {
-                node.getProperty(name).remove();
+            Property property = NodeUtil.getPropertyOrNull(node, name);
+            if (property != null) {
+                property.remove();
             }
         } catch (final RepositoryException re) {
             throw new IllegalArgumentException("Property '" + key + "' can't be removed from node '" + getPath() + "'.", re);
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/JcrValueMap.java b/src/main/java/org/apache/sling/jcr/resource/internal/JcrValueMap.java
index 4d5fc4d..ba9abc9 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/JcrValueMap.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/JcrValueMap.java
@@ -26,7 +26,6 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
-
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
@@ -279,8 +278,9 @@
             // encoding
             final String path = ISO9075.encodePath(name);
             try {
-                if (node.hasProperty(path)) {
-                    return new JcrPropertyMapCacheEntry(node.getProperty(path));
+                Property property = NodeUtil.getPropertyOrNull(node, path);
+                if (property != null) {
+                    return new JcrPropertyMapCacheEntry(property);
                 }
             } catch (final RepositoryException re) {
                 throw new IllegalArgumentException(re);
@@ -304,8 +304,9 @@
             }
             final String newPath = sb.toString();
             try {
-                if (node.hasProperty(newPath)) {
-                    return new JcrPropertyMapCacheEntry(node.getProperty(newPath));
+                Property property = NodeUtil.getPropertyOrNull(node,newPath);
+                if (property != null) {
+                    return new JcrPropertyMapCacheEntry(property);
                 }
             } catch (final RepositoryException re) {
                 throw new IllegalArgumentException(re);
@@ -322,9 +323,9 @@
 
         try {
             final String key = escapeKeyName(name);
-            if (node.hasProperty(key)) {
-                final Property prop = node.getProperty(key);
-                return cacheProperty(prop);
+            Property property = NodeUtil.getPropertyOrNull(node,key);
+            if (property != null) {
+                return cacheProperty(property);
             }
         } catch (final RepositoryException re) {
             throw new IllegalArgumentException(re);
@@ -334,9 +335,9 @@
             // for compatibility with older versions we use the (wrong) ISO9075 path
             // encoding
             final String oldKey = ISO9075.encodePath(name);
-            if (node.hasProperty(oldKey)) {
-                final Property prop = node.getProperty(oldKey);
-                return cacheProperty(prop);
+            Property property = NodeUtil.getPropertyOrNull(node,oldKey);
+            if (property != null) {
+                return cacheProperty(property);
             }
         } catch (final RepositoryException re) {
             // we ignore this
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java b/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java
index f315416..81eb7f0 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java
@@ -36,6 +36,7 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.NodeType;
 
+import org.apache.jackrabbit.api.JackrabbitNode;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -90,8 +91,9 @@
                 : node.isNodeType(NT_LINKED_FILE) ? node.getProperty(JCR_CONTENT).getNode() : node;
         Property data;
         // if the node has a jcr:data property, use that property
-        if (content.hasProperty(JCR_DATA)) {
-            data = content.getProperty(JCR_DATA);
+        final Property property = getPropertyOrNull(content,JCR_DATA);
+        if (property != null) {
+            data = property;
         } else {
             // otherwise try to follow default item trail
             Item item = content.getPrimaryItem();
@@ -102,4 +104,42 @@
         }
         return data;
     }
+
+    /**
+     * Return a named property from a node.
+     *
+     * In case a recent Oak JCR version is present, it switches to an optimized
+     * version which just does one lookup of the property. This is not possible
+     * with plain JCR.
+     *
+     * @param node the node
+     * @param propertyName the property to check for
+     * @return the property if it exists, null otherwise
+     * @throws RepositoryException in case of a problem
+     */
+    public static @Nullable Property getPropertyOrNull (@NotNull Node node, @NotNull String propertyName) throws RepositoryException {
+        if (node instanceof JackrabbitNode) {
+            JackrabbitNode jnode = (JackrabbitNode) node;
+            return jnode.getPropertyOrNull(propertyName);
+        } else {
+            if (node.hasProperty(propertyName)) {
+                return node.getProperty(propertyName);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public static @Nullable Node getNodeOrNull (@NotNull Node node, @NotNull String childNode) throws RepositoryException {
+        if (node instanceof JackrabbitNode) {
+            JackrabbitNode jnode = (JackrabbitNode) node;
+            return jnode.getNodeOrNull(childNode);
+        } else {
+            if (node.hasNode(childNode)) {
+                return node.getNode(childNode);
+            } else {
+                return null;
+            }
+        }
+    }
 }
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java
index d91ef82..d81cd8e 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java
@@ -33,6 +33,7 @@
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.jcr.resource.api.JcrResourceConstants;
+import org.apache.sling.jcr.resource.internal.NodeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -119,15 +120,17 @@
     protected String getResourceTypeForNode(final @NotNull Node node) throws RepositoryException {
         String result = null;
 
-        if (node.hasProperty(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY)) {
-            result = node.getProperty(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY).getString();
+        Property property = NodeUtil.getPropertyOrNull(node, JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY);
+        if (property != null) {
+            result = property.getString();
         }
 
         if (result == null || result.length() == 0) {
             // Do not load the relatively expensive NodeType object unless necessary. See OAK-2441 for the reason why it
             // cannot only use getProperty here.
-            if (node.hasProperty(Property.JCR_PRIMARY_TYPE)) {
-                result = node.getProperty(Property.JCR_PRIMARY_TYPE).getString();
+            property = NodeUtil.getPropertyOrNull(node, Property.JCR_PRIMARY_TYPE);
+            if (property != null) {
+                result = property.getString();
             } else {
                 result = node.getPrimaryNodeType().getName();
             }
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResourceFactory.java b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResourceFactory.java
index f77d644..b2d97f5 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResourceFactory.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResourceFactory.java
@@ -35,6 +35,7 @@
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.jcr.resource.internal.HelperData;
+import org.apache.sling.jcr.resource.internal.NodeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -142,15 +143,18 @@
 
     private static @Nullable Item getSubitem(@NotNull Node node, @NotNull String relPath) {
         try {
-            if (relPath.length() == 0) { // not using isEmpty() due to 1.5 compatibility
+            if (relPath.isEmpty()) {
                 return node;
-            } else if (node.hasNode(relPath)) {
-                return node.getNode(relPath);
-            } else if (node.hasProperty(relPath)) {
-                return node.getProperty(relPath);
-            } else {
-                return null;
             }
+            Node childNode = NodeUtil.getNodeOrNull(node, relPath);
+            if (childNode != null) {
+                return childNode;
+            }
+            Property property = NodeUtil.getPropertyOrNull(node, relPath);
+            if (property != null) {
+                return property;
+            }
+            return null;
         } catch (RepositoryException e) {
             log.debug("getSubitem: Can't get subitem {} of {}: {}", relPath, node, e.toString());
             return null;
@@ -162,11 +166,11 @@
         final VersionHistory history = versionManager.getVersionHistory(node.getPath());
         if (history.hasVersionLabel(versionSpecifier)) {
             return history.getVersionByLabel(versionSpecifier).getFrozenNode();
-        } else if (history.hasNode(versionSpecifier)) {
-            return history.getVersion(versionSpecifier).getFrozenNode();
-        } else {
-            return null;
         }
+        if (history.hasNode(versionSpecifier)) {
+            return history.getVersion(versionSpecifier).getFrozenNode();
+        }
+        return null;
     }
 
     private static boolean isVersionable(@NotNull Item item) throws RepositoryException {
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
index f5628bc..dba7f67 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
@@ -107,8 +107,9 @@
         // Yes, this isn't how you're supposed to compare Strings, but this is intentional.
         if (resourceSuperType == UNSET_RESOURCE_SUPER_TYPE) {
             try {
-                if (getNode().hasProperty(JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY)) {
-                    resourceSuperType = getNode().getProperty(JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY).getValue().getString();
+                Property property = NodeUtil.getPropertyOrNull(getNode(), JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY);
+                if (property != null) {
+                    resourceSuperType = property.getValue().getString();
                 }
             } catch (RepositoryException re) {
                 // we ignore this
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceMetadata.java b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceMetadata.java
index 7440ddd..0ad1e08 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceMetadata.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceMetadata.java
@@ -39,6 +39,7 @@
 import javax.jcr.ValueFormatException;
 
 import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.jcr.resource.internal.NodeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -108,8 +109,9 @@
             String contentType = null;
             final Node targetNode = promoteNode();
             try {
-                if (targetNode.hasProperty(JCR_MIMETYPE)) {
-                    contentType = targetNode.getProperty(JCR_MIMETYPE).getString();
+                Property property = NodeUtil.getPropertyOrNull(targetNode, JCR_MIMETYPE);
+                if (property != null) {
+                    contentType = property.getString();
                 }
             } catch (final RepositoryException re) {
                 report(re);
@@ -121,8 +123,9 @@
             String characterEncoding = null;
             final Node targetNode = promoteNode();
             try {
-                if (targetNode.hasProperty(JCR_ENCODING)) {
-                    characterEncoding = targetNode.getProperty(JCR_ENCODING).getString();
+                Property property = NodeUtil.getPropertyOrNull(targetNode, JCR_ENCODING);
+                if (property != null) {
+                    characterEncoding = property.getString();
                 }
             } catch (final RepositoryException re) {
                 report(re);
@@ -133,9 +136,9 @@
             long modificationTime = -1;
             final Node targetNode = promoteNode();
             try {
-                if (targetNode.hasProperty(JCR_LAST_MODIFIED)) {
+                Property prop = NodeUtil.getPropertyOrNull(targetNode, JCR_LAST_MODIFIED);
+                if (prop != null) {
                     // We don't check node type, so JCR_LASTMODIFIED might not be a long
-                    final Property prop = targetNode.getProperty(JCR_LAST_MODIFIED);
                     try {
                         modificationTime = prop.getLong();
                     } catch (final ValueFormatException vfe) {
@@ -153,8 +156,8 @@
             final Node targetNode = promoteNode();
             try {
                 // if the node has a jcr:data property, use that property
-                if (targetNode.hasProperty(JCR_DATA)) {
-                    final Property prop = targetNode.getProperty(JCR_DATA);
+                Property prop = NodeUtil.getPropertyOrNull(targetNode, JCR_DATA);
+                if (prop != null) {
                     contentLength = JcrItemResource.getContentLength(prop);
                 } else {
                     // otherwise try to follow default item trail
@@ -186,13 +189,15 @@
         if (name == null) {
             return null;
         }
-        if (node.hasProperty(name)) {
-            return node.getProperty(name);
-        } else if (node.hasNode(name)) {
-            return node.getNode(name);
-        } else {
-            return null;
+        Property property = NodeUtil.getPropertyOrNull(node, name);
+        if (property != null) {
+            return property;
         }
+        Node childNode = NodeUtil.getNodeOrNull(node, name);
+        if (childNode != null) {
+            return childNode;
+        }
+        return null;
     }
 
     private void populate() {