You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2020/04/14 18:23:25 UTC

[sling-org-apache-sling-testing-sling-mock] branch master updated: SLING-9373 remove compile dependency to guava

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

sseifert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-sling-mock.git


The following commit(s) were added to refs/heads/master by this push:
     new 59b938d  SLING-9373 remove compile dependency to guava
59b938d is described below

commit 59b938d51e52ac23e57dc8a142abfa7da8e3fb6d
Author: sseifert <ss...@pro-vision.de>
AuthorDate: Tue Apr 14 20:20:35 2020 +0200

    SLING-9373 remove compile dependency to guava
---
 .../testing/mock/sling/builder/ContentBuilder.java |   6 +-
 .../mock/sling/builder/ImmutableValueMap.java      | 371 +++++++++++++++++++++
 .../testing/mock/sling/builder/package-info.java   |   2 +-
 .../mock/sling/context/SlingContextImpl.java       |  35 +-
 .../testing/mock/sling/context/UniqueRoot.java     |   5 +-
 .../testing/mock/sling/context/package-info.java   |   2 +-
 .../testing/mock/sling/loader/ContentLoader.java   |  55 +--
 .../sling/services/MockSlingSettingService.java    |   7 +-
 .../mock/sling/builder/ImmutableValueMapTest.java  | 179 ++++++++++
 .../testing/mock/sling/junit/SlingContextTest.java |  10 +-
 parent/pom.xml                                     |  11 +-
 11 files changed, 618 insertions(+), 65 deletions(-)

diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ContentBuilder.java b/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ContentBuilder.java
index a812270..567f95f 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ContentBuilder.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ContentBuilder.java
@@ -32,8 +32,6 @@ import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.testing.mock.osgi.MapUtil;
 import org.jetbrains.annotations.NotNull;
 
-import com.google.common.collect.ImmutableMap;
-
 /**
  * Helper class for building test content in the resource hierarchy with as less
  * boilerplate code as possible.
@@ -185,9 +183,7 @@ public class ContentBuilder {
         Resource parentResource = ensureResourceExists(parentPath);
         try {
             resource = resourceResolver.create(parentResource, name,
-                    ImmutableMap.<String, Object> builder()
-                            .put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED)
-                            .build());
+                    ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED));
             resourceResolver.commit();
             return resource;
         } catch (PersistenceException ex) {
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ImmutableValueMap.java b/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ImmutableValueMap.java
new file mode 100644
index 0000000..719c3a8
--- /dev/null
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/builder/ImmutableValueMap.java
@@ -0,0 +1,371 @@
+/*
+ * 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.sling.testing.mock.sling.builder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * {@link ValueMap} that does not support changing its content.
+ * <p>
+ * All methods that may change the content will throw a
+ * {@link UnsupportedOperationException}.
+ * </p>
+ * <p>
+ * Static convenience methods provide similar behavior as Guava ImmutableMap
+ * variants.
+ * </p>
+ */
+@ProviderType
+public final class ImmutableValueMap implements ValueMap {
+
+    private final ValueMap map;
+
+    /**
+     * @param map Value map
+     */
+    ImmutableValueMap(@NotNull ValueMap map) {
+        this.map = map;
+    }
+
+    /**
+     * @param map Map
+     */
+    ImmutableValueMap(@NotNull Map<String, Object> map) {
+        this.map = new ValueMapDecorator(map);
+    }
+
+    @Override
+    public @Nullable <T> T get(@NotNull String name, @NotNull Class<T> type) {
+        return this.map.get(name, type);
+    }
+
+    @Override
+    public <T> T get(@NotNull String name, T defaultValue) {
+        return this.map.get(name, defaultValue);
+    }
+
+    @Override
+    public int size() {
+        return this.map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return this.map.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return this.map.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return this.map.containsValue(value);
+    }
+
+    @Override
+    public Object get(Object key) {
+        return this.map.get(key);
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return this.map.keySet();
+    }
+
+    @Override
+    public Collection<Object> values() {
+        return this.map.values();
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        return Collections.unmodifiableSet(this.map.entrySet());
+    }
+
+    @Override
+    public int hashCode() {
+        return this.map.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ImmutableValueMap)) {
+            return false;
+        }
+        return this.map.entrySet().equals(((ImmutableValueMap) obj).map.entrySet());
+    }
+
+    @Override
+    public String toString() {
+        return map.toString();
+    }
+
+    // mutable operations not supported
+    /**
+     * @deprecated Unsupported operation
+     */
+    @Override
+    @Deprecated
+    public Object put(String key, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @deprecated Unsupported operation
+     */
+    @Override
+    @Deprecated
+    public Object remove(Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @deprecated Unsupported operation
+     */
+    @Override
+    @Deprecated
+    public void putAll(Map<? extends String, ? extends Object> m) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @deprecated Unsupported operation
+     */
+    @Override
+    @Deprecated
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the empty map. This map behaves and performs comparably to
+     * {@link Collections#emptyMap}, and is preferable mainly for consistency
+     * and maintainability of your code.
+     * @return ImmutableValueMap
+     */
+    public static @NotNull ImmutableValueMap of() {
+        return new ImmutableValueMap(ValueMap.EMPTY);
+    }
+
+    /**
+     * Returns an immutable map containing a single entry. This map behaves and
+     * performs comparably to {@link Collections#singletonMap} but will not
+     * accept a null key or value. It is preferable mainly for consistency and
+     * maintainability of your code.
+     * @param k1 Key 1
+     * @param v1 Value 1
+     * @return ImmutableValueMap
+     */
+    public static @NotNull ImmutableValueMap of(@NotNull String k1, @NotNull Object v1) {
+        return new ImmutableValueMap(Collections.singletonMap(k1, v1));
+    }
+
+    /**
+     * Returns an immutable map containing the given entries, in order.
+     * @param k1 Key 1
+     * @param v1 Value 1
+     * @param k2 Key 2
+     * @param v2 Value 2
+     * @return ImmutableValueMap
+     * @throws IllegalArgumentException if duplicate keys are provided
+     */
+    public static @NotNull ImmutableValueMap of(@NotNull String k1, @NotNull Object v1, @NotNull String k2,
+            @NotNull Object v2) {
+        Map<String, Object> map = new HashMap<>();
+        map.put(k1, v1);
+        map.put(k2, v2);
+        return new ImmutableValueMap(map);
+    }
+
+    /**
+     * Returns an immutable map containing the given entries, in order.
+     * @param k1 Key 1
+     * @param v1 Value 1
+     * @param k2 Key 2
+     * @param v2 Value 2
+     * @param k3 Key 3
+     * @param v3 Value 3
+     * @return ImmutableValueMap
+     * @throws IllegalArgumentException if duplicate keys are provided
+     */
+    public static @NotNull ImmutableValueMap of(@NotNull String k1, @NotNull Object v1, @NotNull String k2,
+            @NotNull Object v2, @NotNull String k3, @NotNull Object v3) {
+        Map<String, Object> map = new HashMap<>();
+        map.put(k1, v1);
+        map.put(k2, v2);
+        map.put(k3, v3);
+        return new ImmutableValueMap(map);
+    }
+
+    /**
+     * Returns an immutable map containing the given entries, in order.
+     * @param k1 Key 1
+     * @param v1 Value 1
+     * @param k2 Key 2
+     * @param v2 Value 2
+     * @param k3 Key 3
+     * @param v3 Value 3
+     * @param k4 Key 4
+     * @param v4 Value 4
+     * @return ImmutableValueMap
+     * @throws IllegalArgumentException if duplicate keys are provided
+     */
+    public static @NotNull ImmutableValueMap of( // NOPMD
+            @NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2, @NotNull String k3,
+            @NotNull Object v3, @NotNull String k4, @NotNull Object v4) {
+        Map<String, Object> map = new HashMap<>();
+        map.put(k1, v1);
+        map.put(k2, v2);
+        map.put(k3, v3);
+        map.put(k4, v4);
+        return new ImmutableValueMap(map);
+    }
+
+    /**
+     * Returns an immutable map containing the given entries, in order.
+     * @param k1 Key 1
+     * @param v1 Value 1
+     * @param k2 Key 2
+     * @param v2 Value 2
+     * @param k3 Key 3
+     * @param v3 Value 3
+     * @param k4 Key 4
+     * @param v4 Value 4
+     * @param k5 Key 5
+     * @param v5 Value 5
+     * @return ImmutableValueMap
+     * @throws IllegalArgumentException if duplicate keys are provided
+     */
+    public static ImmutableValueMap of( // NOPMD
+            @NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2, @NotNull String k3,
+            @NotNull Object v3, @NotNull String k4, @NotNull Object v4, @NotNull String k5, @NotNull Object v5) {
+        Map<String, Object> map = new HashMap<>();
+        map.put(k1, v1);
+        map.put(k2, v2);
+        map.put(k3, v3);
+        map.put(k4, v4);
+        map.put(k5, v5);
+        return new ImmutableValueMap(map);
+    }
+
+    // looking for of() with > 5 entries? Use the builder instead.
+
+    /**
+     * Returns a new builder. The generated builder is equivalent to the builder
+     * created by the {@link Builder} constructor.
+     * @return Builder
+     */
+    public static @NotNull Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns an immutable map containing the same entries as {@code map}. If
+     * {@code map} somehow contains entries with duplicate keys (for example, if
+     * it is a {@code SortedMap} whose comparator is not <i>consistent with
+     * equals</i>), the results of this method are undefined.
+     * <p>
+     * Despite the method name, this method attempts to avoid actually copying
+     * the data when it is safe to do so. The exact circumstances under which a
+     * copy will or will not be performed are undocumented and subject to
+     * change.
+     * </p>
+     * @param map Map
+     * @return ImmutableValueMap
+     * @throws NullPointerException if any key or value in {@code map} is null
+     */
+    public static @NotNull ImmutableValueMap copyOf(@NotNull Map<String, Object> map) {
+        if (map instanceof ValueMap) {
+            return new ImmutableValueMap((ValueMap) map);
+        } else {
+            return new ImmutableValueMap(map);
+        }
+    }
+
+    /**
+     * Builder interface for {@link ImmutableValueMap}.
+     */
+    public static final class Builder {
+
+        private final @NotNull Map<String, Object> map = new HashMap<>();
+
+        /**
+         * Associates {@code key} with {@code value} in the built map. Duplicate
+         * keys are not allowed, and will cause {@link #build} to fail.
+         * @param key Key
+         * @param value value
+         * @return this
+         */
+        public @NotNull Builder put(@NotNull String key, @NotNull Object value) {
+            map.put(key, value);
+            return this;
+        }
+
+        /**
+         * Adds the given {@code entry} to the map, making it immutable if
+         * necessary. Duplicate keys are not allowed, and will cause
+         * {@link #build} to fail.
+         * @param entry Entry
+         * @return this
+         */
+        public @NotNull Builder put(@NotNull Entry<String, Object> entry) {
+            return put(entry.getKey(), entry.getValue());
+        }
+
+        /**
+         * Associates all of the given map's keys and values in the built map.
+         * Duplicate keys are not allowed, and will cause {@link #build} to
+         * fail.
+         * @param value Value
+         * @return this
+         * @throws NullPointerException if any key or value in {@code map} is
+         *             null
+         */
+        public @NotNull Builder putAll(@NotNull Map<String, Object> value) {
+            map.putAll(value);
+            return this;
+        }
+
+        /**
+         * Returns a newly-created immutable map.
+         * @return ImmutableValueMap
+         * @throws IllegalArgumentException if duplicate keys were added
+         */
+        public @NotNull ImmutableValueMap build() {
+            if (map.isEmpty()) {
+                return ImmutableValueMap.of();
+            } else {
+                return new ImmutableValueMap(map);
+            }
+        }
+    }
+
+}
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/builder/package-info.java b/core/src/main/java/org/apache/sling/testing/mock/sling/builder/package-info.java
index efa0a0d..74b16e8 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/builder/package-info.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/builder/package-info.java
@@ -19,5 +19,5 @@
 /**
  * Content builder for creating test content.
  */
-@org.osgi.annotation.versioning.Version("1.2")
+@org.osgi.annotation.versioning.Version("1.3")
 package org.apache.sling.testing.mock.sling.builder;
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java b/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java
index a565015..b6e7ca3 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java
@@ -18,8 +18,13 @@
  */
 package org.apache.sling.testing.mock.sling.context;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -57,10 +62,6 @@ import org.osgi.annotation.versioning.ConsumerType;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Defines Sling context objects with lazy initialization.
  * Should not be used directly but via the SlingContext JUnit rule or extension.
@@ -69,7 +70,7 @@ import com.google.common.collect.ImmutableSet;
 public class SlingContextImpl extends OsgiContextImpl {
 
     // default to publish instance run mode
-    static final @NotNull Set<String> DEFAULT_RUN_MODES = ImmutableSet.<String> builder().add("publish").build();
+    static final @NotNull Set<String> DEFAULT_RUN_MODES = Collections.singleton("publish");
 
     private static final @NotNull String RESOURCERESOLVERFACTORYACTIVATOR_PID = "org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl";
     
@@ -459,7 +460,7 @@ public class SlingContextImpl extends OsgiContextImpl {
      * @param runModes Run mode(s).
      */
     public final void runMode(@NotNull String @NotNull ... runModes) {
-        Set<String> newRunModes = ImmutableSet.<String> builder().add(runModes).build();
+        Set<String> newRunModes = new HashSet<>(Arrays.asList(runModes));
         ServiceReference<SlingSettingsService> ref = bundleContext().getServiceReference(SlingSettingsService.class);
         if (ref != null) {
             MockSlingSettingService slingSettings = (MockSlingSettingService)bundleContext().getService(ref);
@@ -515,17 +516,17 @@ public class SlingContextImpl extends OsgiContextImpl {
                 return (AdapterType)adaptHandler.apply((T1)adaptable);
             }
         };
-        registerService(AdapterFactory.class, adapterFactory, ImmutableMap.<String, Object>builder()
-                .put(AdapterFactory.ADAPTABLE_CLASSES, new String[] {
-                    adaptableClass.getName()
-                })
-                .put(AdapterFactory.ADAPTER_CLASSES, new String[] {
-                    adapterClass.getName()
-                })
-                // make sure this overlay has higher ranking than other adapter factories
-                // normally we should use Integer.MAX_VALUE for this - but due to SLING-7194 prefers lowest-ranking services first
-                .put(Constants.SERVICE_RANKING, Integer.MIN_VALUE)
-                .build());
+        Map<String,Object> props = new HashMap<>();
+        props.put(AdapterFactory.ADAPTABLE_CLASSES, new String[] {
+                adaptableClass.getName()
+            });
+        props.put(AdapterFactory.ADAPTER_CLASSES, new String[] {
+                adapterClass.getName()
+            });
+        // make sure this overlay has higher ranking than other adapter factories
+        // normally we should use Integer.MAX_VALUE for this - but due to SLING-7194 prefers lowest-ranking services first
+        props.put(Constants.SERVICE_RANKING, Integer.MIN_VALUE);
+        registerService(AdapterFactory.class, adapterFactory, props);
     }
 
 }
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/context/UniqueRoot.java b/core/src/main/java/org/apache/sling/testing/mock/sling/context/UniqueRoot.java
index ce73f93..373d2eb 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/context/UniqueRoot.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/context/UniqueRoot.java
@@ -24,14 +24,13 @@ import org.apache.jackrabbit.JcrConstants;
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.testing.mock.sling.builder.ImmutableValueMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.osgi.annotation.versioning.ConsumerType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableMap;
-
 /**
  * Manages unique root paths in JCR repository.
  * This is important for resource resolver types like JCR_JACKRABBIT  
@@ -66,7 +65,7 @@ public class UniqueRoot {
     protected final Resource getOrCreateResource(@NotNull String path, @NotNull String primaryType) {
         try {
             return ResourceUtil.getOrCreateResource(context.resourceResolver(), path, 
-                    ImmutableMap.<String,Object>of(JcrConstants.JCR_PRIMARYTYPE, primaryType),
+                    ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, primaryType),
                     null, true);
         }
         catch (PersistenceException ex) {
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java b/core/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java
index ac777cf..d125953 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java
@@ -19,5 +19,5 @@
 /**
  * Sling context implementation for unit tests.
  */
-@org.osgi.annotation.versioning.Version("3.8.0")
+@org.osgi.annotation.versioning.Version("4.0.0")
 package org.apache.sling.testing.mock.sling.context;
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java b/core/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java
index 8f4c403..7974b0a 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java
@@ -24,6 +24,8 @@ import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.jackrabbit.JcrConstants;
@@ -38,14 +40,12 @@ import org.apache.sling.contentparser.json.JSONParserFeature;
 import org.apache.sling.contentparser.json.JSONParserOptions;
 import org.apache.sling.contentparser.json.internal.JSONContentParser;
 import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.builder.ImmutableValueMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Imports JSON data and binary data into Sling resource hierarchy.
  * After all import operations from json or binaries {@link ResourceResolver#commit()} is called (when autocommit mode is active).
@@ -55,28 +55,28 @@ public final class ContentLoader {
     private static final String CONTENTTYPE_OCTET_STREAM = "application/octet-stream";
 
     // set of resource or property names that are ignored for all resource resolver types
-    private static final Set<String> SHARED_IGNORED_NAMES = ImmutableSet.<String>builder()
-            .add(JcrConstants.JCR_BASEVERSION)
-            .add(JcrConstants.JCR_PREDECESSORS)
-            .add(JcrConstants.JCR_SUCCESSORS)
-            .add(JcrConstants.JCR_VERSIONHISTORY)
-            .add("jcr:checkedOut")
-            .add("jcr:isCheckedOut")
-            .add("rep:policy")
-            .build();
+    private static final Set<String> SHARED_IGNORED_NAMES = Stream.of(
+            JcrConstants.JCR_BASEVERSION,
+            JcrConstants.JCR_PREDECESSORS,
+            JcrConstants.JCR_SUCCESSORS,
+            JcrConstants.JCR_VERSIONHISTORY,
+            "jcr:checkedOut",
+            "jcr:isCheckedOut",
+            "rep:policy")
+            .collect(Collectors.toSet());
     
     // set of resource or property names that are ignored when other resource resolver types than JCR_OAK are used
-    private static final Set<String> MOCK_IGNORED_NAMES = ImmutableSet.<String>builder()
-            .addAll(SHARED_IGNORED_NAMES)
-            .add(JcrConstants.JCR_MIXINTYPES)
-            .build();
+    private static final Set<String> MOCK_IGNORED_NAMES = Stream.concat(
+            SHARED_IGNORED_NAMES.stream(), Stream.of(
+            JcrConstants.JCR_MIXINTYPES))
+            .collect(Collectors.toSet());
     
     // set of resource or property names that are ignored when JCR_OAK resource resolver type (= a real repo impl) is used
-    private static final Set<String> OAK_IGNORED_NAMES = ImmutableSet.<String>builder()
-            .addAll(SHARED_IGNORED_NAMES)
-            .add(JcrConstants.JCR_UUID)
-            .add(JcrConstants.JCR_CREATED)
-            .build();
+    private static final Set<String> OAK_IGNORED_NAMES = Stream.concat(
+            SHARED_IGNORED_NAMES.stream(), Stream.of(
+            JcrConstants.JCR_UUID,
+            JcrConstants.JCR_CREATED))
+            .collect(Collectors.toSet()); 
     
     private final ResourceResolver resourceResolver;
     private final BundleContext bundleContext;
@@ -360,11 +360,11 @@ public final class ContentLoader {
     public @NotNull Resource binaryFile(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String name, @NotNull String mimeType) {
         try {
             Resource file = resourceResolver.create(parentResource, name,
-                    ImmutableMap.<String, Object> builder().put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_FILE)
-                            .build());
+                    ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_FILE));
             resourceResolver.create(file, JcrConstants.JCR_CONTENT,
-                    ImmutableMap.<String, Object> builder().put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE)
-                            .put(JcrConstants.JCR_DATA, inputStream).put(JcrConstants.JCR_MIMETYPE, mimeType).build());
+                    ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE,
+                            JcrConstants.JCR_DATA, inputStream,
+                            JcrConstants.JCR_MIMETYPE, mimeType));
             if (autoCommit) {
                 resourceResolver.commit();
             }
@@ -484,8 +484,9 @@ public final class ContentLoader {
     public @NotNull Resource binaryResource(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String name, @NotNull String mimeType) {
         try {
             Resource resource = resourceResolver.create(parentResource, name,
-                    ImmutableMap.<String, Object> builder().put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE)
-                            .put(JcrConstants.JCR_DATA, inputStream).put(JcrConstants.JCR_MIMETYPE, mimeType).build());
+                    ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE,
+                            JcrConstants.JCR_DATA, inputStream,
+                            JcrConstants.JCR_MIMETYPE, mimeType));
             if (autoCommit) {
                 resourceResolver.commit();
             }
diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java b/core/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java
index bca2470..bfa3544 100644
--- a/core/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java
+++ b/core/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java
@@ -19,14 +19,13 @@
 package org.apache.sling.testing.mock.sling.services;
 
 import java.net.URL;
+import java.util.Collections;
 import java.util.Set;
 import java.util.UUID;
 
 import org.apache.sling.settings.SlingSettingsService;
 import org.jetbrains.annotations.NotNull;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Mock implementation of {@link SlingSettingsService}.
  */
@@ -39,7 +38,7 @@ public final class MockSlingSettingService implements SlingSettingsService {
      * Instantiate with no default run modes.
      */
     public MockSlingSettingService() {
-        this(ImmutableSet.<String> of());
+        this(Collections.emptySet());
     }
 
     /**
@@ -53,7 +52,7 @@ public final class MockSlingSettingService implements SlingSettingsService {
 
     @Override
     public Set<String> getRunModes() {
-        return ImmutableSet.copyOf(this.runModes);
+        return Collections.unmodifiableSet(this.runModes);
     }
 
     public void setRunModes(@NotNull Set<String> runModes) {
diff --git a/core/src/test/java/org/apache/sling/testing/mock/sling/builder/ImmutableValueMapTest.java b/core/src/test/java/org/apache/sling/testing/mock/sling/builder/ImmutableValueMapTest.java
new file mode 100644
index 0000000..5f8005e
--- /dev/null
+++ b/core/src/test/java/org/apache/sling/testing/mock/sling/builder/ImmutableValueMapTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.sling.testing.mock.sling.builder;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class ImmutableValueMapTest {
+
+    private static final Map<String, Object> SAMPLE_PROPS = ImmutableMap.<String, Object> builder()
+            .put("prop1", "value1").put("prop2", 55).build();
+
+    private ValueMap underTest;
+
+    @Before
+    public void setUp() {
+        underTest = ImmutableValueMap.copyOf(SAMPLE_PROPS);
+    }
+
+    @Test
+    public void testProperties() {
+        assertEquals("value1", underTest.get("prop1", String.class));
+        assertEquals((Integer) 55, underTest.get("prop2", Integer.class));
+    }
+
+    @Test
+    public void testDefault() {
+        assertEquals("def", underTest.get("prop3", "def"));
+    }
+
+    @Test
+    public void testMapAccessMethods() {
+        assertEquals(2, underTest.size());
+        assertFalse(underTest.isEmpty());
+        assertTrue(underTest.containsKey("prop1"));
+        assertTrue(underTest.containsValue("value1"));
+        assertEquals("value1", underTest.get("prop1"));
+        assertTrue(underTest.keySet().contains("prop1"));
+        assertTrue(underTest.values().contains("value1"));
+        assertEquals(2, underTest.entrySet().size());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testMapClear() {
+        underTest.clear();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testMapRemove() {
+        underTest.remove("prop2");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testMapPut() {
+        underTest.put("prop3", "value3");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testMapPutAll() {
+        underTest.putAll(ImmutableMap.<String, Object> builder().put("prop4", 25).put("prop5", 33).build());
+    }
+
+    @Test
+    public void testOf() {
+        ValueMap map = ImmutableValueMap.of();
+        assertTrue(map.isEmpty());
+    }
+
+    @Test
+    public void testOfx1() {
+        ValueMap map = ImmutableValueMap.of("p1", "v1");
+        assertEquals(1, map.size());
+        assertEquals("v1", map.get("p1"));
+    }
+
+    @Test
+    public void testOfx2() {
+        ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2");
+        assertEquals(2, map.size());
+        assertEquals("v1", map.get("p1"));
+        assertEquals("v2", map.get("p2"));
+    }
+
+    @Test
+    public void testOfx3() {
+        ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2", "p3", "v3");
+        assertEquals(3, map.size());
+        assertEquals("v1", map.get("p1"));
+        assertEquals("v2", map.get("p2"));
+        assertEquals("v3", map.get("p3"));
+    }
+
+    @Test
+    public void testOfx4() {
+        ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2", "p3", "v3", "p4", "v4");
+        assertEquals(4, map.size());
+        assertEquals("v1", map.get("p1"));
+        assertEquals("v2", map.get("p2"));
+        assertEquals("v3", map.get("p3"));
+        assertEquals("v4", map.get("p4"));
+    }
+
+    @Test
+    public void testOfx5() {
+        ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2", "p3", "v3", "p4", "v4", "p5", "v5");
+        assertEquals(5, map.size());
+        assertEquals("v1", map.get("p1"));
+        assertEquals("v2", map.get("p2"));
+        assertEquals("v3", map.get("p3"));
+        assertEquals("v4", map.get("p4"));
+        assertEquals("v5", map.get("p5"));
+    }
+
+    @Test
+    public void testBuilder() {
+        ValueMap map = ImmutableValueMap.builder().put("p1", "v1")
+                .putAll(ImmutableMap.<String, Object> of("p2", "v2", "p3", "v3"))
+                .put(ImmutableMap.<String, Object> of("p4", "v4").entrySet().iterator().next()).put("p5", "v5").build();
+        assertEquals(5, map.size());
+        assertEquals("v1", map.get("p1"));
+        assertEquals("v2", map.get("p2"));
+        assertEquals("v3", map.get("p3"));
+        assertEquals("v4", map.get("p4"));
+        assertEquals("v5", map.get("p5"));
+    }
+
+    @Test
+    public void testBuilderEmpty() {
+        ValueMap map = ImmutableValueMap.builder().build();
+        assertTrue(map.isEmpty());
+    }
+
+    @Test
+    public void testCopyOfValueMap() {
+        ValueMap valueMap = new ValueMapDecorator(SAMPLE_PROPS);
+        ValueMap map = ImmutableValueMap.copyOf(valueMap);
+        assertEquals(map.size(), SAMPLE_PROPS.size());
+    }
+
+    @Test
+    public void testEquals() {
+        ValueMap map1 = ImmutableValueMap.builder().put("prop1", "value1").put("prop2", 55).build();
+        ValueMap map2 = ImmutableValueMap.builder().put("prop1", "value1").put("prop2", 55).build();
+        ValueMap map3 = ImmutableValueMap.builder().put("prop1", "value2").put("prop2", 55).build();
+
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        assertNotEquals(map1, map3);
+        assertNotEquals(map2, map3);
+    }
+
+}
diff --git a/junit4/src/test/java/org/apache/sling/testing/mock/sling/junit/SlingContextTest.java b/junit4/src/test/java/org/apache/sling/testing/mock/sling/junit/SlingContextTest.java
index 358bfcc..6005625 100644
--- a/junit4/src/test/java/org/apache/sling/testing/mock/sling/junit/SlingContextTest.java
+++ b/junit4/src/test/java/org/apache/sling/testing/mock/sling/junit/SlingContextTest.java
@@ -24,11 +24,14 @@ import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import java.util.function.Function;
+
 import org.apache.sling.api.adapter.AdapterFactory;
 import org.apache.sling.api.adapter.SlingAdaptable;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.builder.ImmutableValueMap;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Before;
 import org.junit.Rule;
@@ -36,9 +39,6 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
-
 @RunWith(MockitoJUnitRunner.class)
 @SuppressWarnings("null")
 public class SlingContextTest {
@@ -55,7 +55,7 @@ public class SlingContextTest {
         .afterSetUp(contextAfterSetup)
         .beforeTearDown(contextBeforeTeardown)
         .afterTearDown(contextAfterTeardown)
-        .resourceResolverFactoryActivatorProps(ImmutableMap.<String, Object>of(
+        .resourceResolverFactoryActivatorProps(ImmutableValueMap.of(
                 "resource.resolver.searchpath", new String[] {"/apps","/libs","/testpath"},
                 "resource.resolver.mapping", new String[] {"/:/", "/content/test/</"}
                 ))
@@ -167,7 +167,7 @@ public class SlingContextTest {
                 return (AdapterType)(((TestAdaptable)adaptable).getMessage() + "-initial");
             }
         };
-        context.registerService(AdapterFactory.class, adapterFactory, ImmutableMap.<String, Object>builder()
+        context.registerService(AdapterFactory.class, adapterFactory, ImmutableValueMap.builder()
                 .put(AdapterFactory.ADAPTABLE_CLASSES, new String[] { TestAdaptable.class.getName() })
                 .put(AdapterFactory.ADAPTER_CLASSES, new String[] { String.class.getName() })
                 .build());
diff --git a/parent/pom.xml b/parent/pom.xml
index eeaec58..c14cf1c 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
-        <version>36</version>
+        <version>38</version>
         <relativePath />
     </parent>
 
@@ -37,7 +37,7 @@
     <properties>
 
         <osgi-mock.version>2.4.15-SNAPSHOT</osgi-mock.version>
-        <jcr-mock.version>1.4.4</jcr-mock.version>
+        <jcr-mock.version>1.4.5-SNAPSHOT</jcr-mock.version>
         <resourceresolver-mock.version>1.1.26</resourceresolver-mock.version>
         <logging-mock.version>2.0.0</logging-mock.version>
         <servlet-helpers.version>1.3.0</servlet-helpers.version>
@@ -60,6 +60,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>15.0</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
     
     <dependencyManagement>