Author: tomekr
Date: Fri Aug 11 08:31:12 2017
New Revision: 1804757

URL: http://svn.apache.org/viewvc?rev=1804757&view=rev
Log:
OAK-6548: Composite node builder/state keeps references to all the ancestor 
states

Added:
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java
Removed:
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/util/Memoizer.java
Modified:
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java?rev=1804757&r1=1804756&r2=1804757&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
 Fri Aug 11 08:31:12 2017
@@ -22,41 +22,31 @@ import org.apache.jackrabbit.oak.api.Blo
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.composite.util.Memoizer;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
 import org.apache.jackrabbit.oak.spi.state.MoveDetector;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Maps.newHashMap;
 import static java.lang.Long.MAX_VALUE;
 import static java.util.Collections.singleton;
 import static 
org.apache.jackrabbit.oak.composite.CompositeNodeState.STOP_COUNTING_CHILDREN;
 import static 
org.apache.jackrabbit.oak.composite.CompositeNodeState.accumulateChildSizes;
-import static 
org.apache.jackrabbit.oak.composite.CompositeNodeState.wrapWithNullCheck;
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
 import static 
org.apache.jackrabbit.oak.spi.state.AbstractNodeState.checkValidName;
 
 class CompositeNodeBuilder implements NodeBuilder {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(CompositeNodeBuilder.class);
-
     private final String path;
 
     private final CompositionContext ctx;
 
-    private Function<MountedNodeStore, NodeBuilder> nodeBuilders;
+    private NodeMap<NodeBuilder> nodeBuilders;
 
     private final MountedNodeStore owningStore;
 
@@ -64,14 +54,14 @@ class CompositeNodeBuilder implements No
 
     private final CompositeNodeBuilder rootBuilder;
 
-    CompositeNodeBuilder(String path, Function<MountedNodeStore, NodeBuilder> 
nodeBuilders, CompositionContext ctx) {
+    CompositeNodeBuilder(String path, NodeMap<NodeBuilder> nodeBuilders, 
CompositionContext ctx) {
         this(path, nodeBuilders, ctx, null);
     }
 
-    private CompositeNodeBuilder(String path, Function<MountedNodeStore, 
NodeBuilder> nodeBuilders, CompositionContext ctx, CompositeNodeBuilder parent) 
{
+    private CompositeNodeBuilder(String path, NodeMap<NodeBuilder> 
nodeBuilders, CompositionContext ctx, CompositeNodeBuilder parent) {
         this.path = path;
         this.ctx = ctx;
-        this.nodeBuilders = wrapWithNullCheck(Memoizer.memoize(nodeBuilders), 
LOG, path);
+        this.nodeBuilders = nodeBuilders;
         this.owningStore = ctx.getOwningStore(path);
         this.parent = parent;
         if (parent == null) {
@@ -82,23 +72,17 @@ class CompositeNodeBuilder implements No
     }
 
     NodeBuilder getNodeBuilder(MountedNodeStore mns) {
-        return nodeBuilders.apply(mns);
+        return nodeBuilders.get(mns);
     }
 
     @Override
     public CompositeNodeState getNodeState() {
-        Map<MountedNodeStore, NodeState> states = 
ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(),
-                nodeBuilders
-                        .andThen(n -> n.exists() ? n.getNodeState() : 
MISSING_NODE)));
-        return new CompositeNodeState(path, states, ctx);
+        return new CompositeNodeState(path, nodeBuilders.getAndApply(n -> 
n.exists() ? n.getNodeState() : MISSING_NODE), ctx);
     }
 
     @Override
     public CompositeNodeState getBaseState() {
-        Map<MountedNodeStore, NodeState> states = 
ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(),
-                nodeBuilders
-                        .andThen(NodeBuilder::getBaseState)));
-        return new CompositeNodeState(path, states, ctx);
+        return new CompositeNodeState(path, 
nodeBuilders.getAndApply(NodeBuilder::getBaseState), ctx);
     }
 
     // node or property-related methods ; directly delegate to wrapped builder
@@ -208,7 +192,7 @@ class CompositeNodeBuilder implements No
             // Count the children in each contributing store.
             return accumulateChildSizes(FluentIterable.from(contributingStores)
                     .transformAndConcat(mns -> {
-                        NodeBuilder node = nodeBuilders.apply(mns);
+                        NodeBuilder node = nodeBuilders.get(mns);
                         if (node.getChildNodeCount(max) == MAX_VALUE) {
                             return singleton(STOP_COUNTING_CHILDREN);
                         } else {
@@ -222,7 +206,7 @@ class CompositeNodeBuilder implements No
     public Iterable<String> getChildNodeNames() {
         return FluentIterable.from(ctx.getContributingStoresForBuilders(path, 
nodeBuilders))
                 .transformAndConcat(mns -> FluentIterable
-                        .from(nodeBuilders.apply(mns).getChildNodeNames())
+                        .from(nodeBuilders.get(mns).getChildNodeNames())
                         .filter(e -> belongsToStore(mns, e)));
     }
 
@@ -230,7 +214,7 @@ class CompositeNodeBuilder implements No
     public boolean hasChildNode(String name) {
         String childPath = simpleConcat(path, name);
         MountedNodeStore mountedStore = ctx.getOwningStore(childPath);
-        return nodeBuilders.apply(mountedStore).hasChildNode(name);
+        return nodeBuilders.get(mountedStore).hasChildNode(name);
     }
 
     @Override
@@ -243,24 +227,22 @@ class CompositeNodeBuilder implements No
     }
 
     private void createAncestors(MountedNodeStore mountedNodeStore) {
-        NodeBuilder builder = rootBuilder.nodeBuilders.apply(mountedNodeStore);
+        NodeBuilder builder = rootBuilder.nodeBuilders.get(mountedNodeStore);
         for (String element : PathUtils.elements(path)) {
             builder = builder.child(element);
         }
-
-        // the nodeBuilders function should be updated, to return the new node 
builder
-        Map<MountedNodeStore, NodeBuilder> map = 
newHashMap(ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(),
 nodeBuilders)));
-        map.put(mountedNodeStore, builder);
-        nodeBuilders = wrapWithNullCheck(m -> map.get(m), LOG, path);
+        synchronized(this) {
+            nodeBuilders = nodeBuilders.replaceNode(mountedNodeStore, builder);
+        }
     }
 
     @Override
     public NodeBuilder getChildNode(final String name) {
         String childPath = simpleConcat(path, name);
         if (!ctx.shouldBeComposite(childPath)) {
-            return 
nodeBuilders.apply(ctx.getOwningStore(childPath)).getChildNode(name);
+            return 
nodeBuilders.get(ctx.getOwningStore(childPath)).getChildNode(name);
         }
-        return new CompositeNodeBuilder(childPath, nodeBuilders.andThen(b -> 
b.getChildNode(name)), ctx, this);
+        return new CompositeNodeBuilder(childPath, nodeBuilders.lazyApply(b -> 
b.getChildNode(name)), ctx, this);
     }
 
     @Override
@@ -273,15 +255,14 @@ class CompositeNodeBuilder implements No
         checkState(exists(), "This builder does not exist: " + 
PathUtils.getName(path));
         String childPath = simpleConcat(path, name);
         final MountedNodeStore childStore = ctx.getOwningStore(childPath);
-        if (childStore != owningStore && 
!nodeBuilders.apply(childStore).exists()) {
+        if (childStore != owningStore && 
!nodeBuilders.get(childStore).exists()) {
             createAncestors(childStore);
         }
-        final NodeBuilder childBuilder = 
nodeBuilders.apply(childStore).setChildNode(name, nodeState);
+        final NodeBuilder childBuilder = 
nodeBuilders.get(childStore).setChildNode(name, nodeState);
         if (!ctx.shouldBeComposite(childPath)) {
             return childBuilder;
         }
-
-        return new CompositeNodeBuilder(childPath, m -> m == childStore ? 
childBuilder : nodeBuilders.apply(m).getChildNode(name), ctx, this);
+        return new CompositeNodeBuilder(childPath, nodeBuilders.lazyApply(b -> 
b.getChildNode(name)).replaceNode(childStore, childBuilder), ctx, this);
     }
 
     @Override
@@ -314,7 +295,7 @@ class CompositeNodeBuilder implements No
     }
 
     private NodeBuilder getWrappedNodeBuilder() {
-        return nodeBuilders.apply (owningStore);
+        return nodeBuilders.get(owningStore);
     }
 
     private void annotateSourcePath() {

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java?rev=1804757&r1=1804756&r2=1804757&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
 Fri Aug 11 08:31:12 2017
@@ -21,19 +21,13 @@ package org.apache.jackrabbit.oak.compos
 import com.google.common.collect.FluentIterable;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.composite.util.Memoizer;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
 
 import static java.lang.Long.MAX_VALUE;
 import static java.util.Collections.singleton;
@@ -41,8 +35,6 @@ import static org.apache.jackrabbit.oak.
 
 class CompositeNodeState extends AbstractNodeState {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(CompositeNodeState.class);
-
     // A note on content held by node stores which is outside the mount 
boundaries
     //
     // As a matter of design, mounted stores will definitely hold information 
_above_
@@ -60,40 +52,21 @@ class CompositeNodeState extends Abstrac
 
     private final String path;
 
+    private final NodeMap<NodeState> nodeStates;
+
     private final CompositionContext ctx;
 
     private final MountedNodeStore owningStore;
 
-    private final Function<MountedNodeStore, NodeState> nodeStates;
-
-    CompositeNodeState(String path, Map<MountedNodeStore, NodeState> 
nodeStates, CompositionContext ctx) {
+    CompositeNodeState(String path, NodeMap<NodeState> nodeStates, 
CompositionContext ctx) {
         this.path = path;
         this.ctx = ctx;
-        this.nodeStates = wrapWithNullCheck(mns -> nodeStates.get(mns), LOG, 
path);
+        this.nodeStates = nodeStates;
         this.owningStore = ctx.getOwningStore(path);
     }
 
-    CompositeNodeState(String path, Function<MountedNodeStore, NodeState> 
nodeStates, CompositionContext ctx) {
-        this.path = path;
-        this.ctx = ctx;
-        this.nodeStates = wrapWithNullCheck(Memoizer.memoize(nodeStates), LOG, 
path);
-        this.owningStore = ctx.getOwningStore(path);
-    }
-
-    static <N> Function<MountedNodeStore, N> 
wrapWithNullCheck(Function<MountedNodeStore, N> f, Logger log, String path) {
-        return mns -> {
-            N nodeState = f.apply(mns);
-            if (nodeState == null) {
-                // this shouldn't happen, so we need to log some more debug 
info
-                log.warn("Can't find node state for path {} and mount {}.", 
path, mns);
-                throw new IllegalStateException("Can't find the node state for 
mount " + mns);
-            }
-            return nodeState;
-        };
-    }
-
     NodeState getNodeState(MountedNodeStore mns) {
-        return nodeStates.apply(mns);
+        return nodeStates.get(mns);
     }
 
     @Override
@@ -127,16 +100,16 @@ class CompositeNodeState extends Abstrac
     public boolean hasChildNode(String name) {
         String childPath = simpleConcat(path, name);
         MountedNodeStore mountedStore = ctx.getOwningStore(childPath);
-        return nodeStates.apply(mountedStore).hasChildNode(name);
+        return nodeStates.get(mountedStore).hasChildNode(name);
     }
 
     @Override
     public NodeState getChildNode(final String name) {
         String childPath = simpleConcat(path, name);
         if (!ctx.shouldBeComposite(childPath)) {
-            return 
nodeStates.apply(ctx.getOwningStore(childPath)).getChildNode(name);
+            return 
nodeStates.get(ctx.getOwningStore(childPath)).getChildNode(name);
         }
-        Function<MountedNodeStore, NodeState> newNodeStates = 
nodeStates.andThen(n -> n.getChildNode(name));
+        NodeMap<NodeState> newNodeStates = nodeStates.lazyApply(n -> 
n.getChildNode(name));
         return new CompositeNodeState(childPath, newNodeStates, ctx);
     }
 
@@ -151,7 +124,7 @@ class CompositeNodeState extends Abstrac
             // Count the children in each contributing store.
             return accumulateChildSizes(FluentIterable.from(contributingStores)
                     .transformAndConcat(mns -> {
-                        NodeState node = nodeStates.apply(mns);
+                        NodeState node = nodeStates.get(mns);
                         if (node.getChildNodeCount(max) == MAX_VALUE) {
                             return singleton(STOP_COUNTING_CHILDREN);
                         } else {
@@ -176,7 +149,7 @@ class CompositeNodeState extends Abstrac
     public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
         return FluentIterable.from(ctx.getContributingStoresForNodes(path, 
nodeStates))
                 .transformAndConcat(mns -> FluentIterable
-                        .from(nodeStates.apply(mns).getChildNodeNames())
+                        .from(nodeStates.get(mns).getChildNodeNames())
                         .filter(n -> belongsToStore(mns, n)))
                 .transform(n -> new MemoryChildNodeEntry(n, getChildNode(n)));
     }
@@ -192,8 +165,8 @@ class CompositeNodeState extends Abstrac
                     continue;
                 }
                 NodeStateDiff childrenDiffFilter = new 
ChildrenDiffFilter(wrappingDiff, mns, false);
-                NodeState contributing = nodeStates.apply(mns);
-                NodeState contributingBase = multiBase.nodeStates.apply(mns);
+                NodeState contributing = nodeStates.get(mns);
+                NodeState contributingBase = multiBase.nodeStates.get(mns);
                 full = full && 
contributing.compareAgainstBaseState(contributingBase, childrenDiffFilter);
             }
             return full;
@@ -205,11 +178,11 @@ class CompositeNodeState extends Abstrac
     // write operations
     @Override
     public CompositeNodeBuilder builder() {
-        return new CompositeNodeBuilder(path, 
nodeStates.andThen(NodeState::builder), ctx);
+        return new CompositeNodeBuilder(path, 
nodeStates.lazyApply(NodeState::builder), ctx);
     }
 
     private NodeState getWrappedNodeState() {
-        return nodeStates.apply(owningStore);
+        return nodeStates.get(owningStore);
     }
 
     private boolean belongsToStore(MountedNodeStore mns, String childName) {

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java?rev=1804757&r1=1804756&r2=1804757&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
 Fri Aug 11 08:31:12 2017
@@ -81,12 +81,12 @@ class CompositionContext {
         }
     }
 
-    List<MountedNodeStore> getContributingStoresForNodes(String path, final 
Function<MountedNodeStore, NodeState> nodeStates) {
-        return getContributingStores(path, mns -> 
nodeStates.apply(mns).getChildNodeNames());
+    List<MountedNodeStore> getContributingStoresForNodes(String path, final 
NodeMap<NodeState> nodeStates) {
+        return getContributingStores(path, mns -> 
nodeStates.get(mns).getChildNodeNames());
     }
 
-    List<MountedNodeStore> getContributingStoresForBuilders(String path, final 
Function<MountedNodeStore, NodeBuilder> nodeBuilders) {
-        return getContributingStores(path, mns -> 
nodeBuilders.apply(mns).getChildNodeNames());
+    List<MountedNodeStore> getContributingStoresForBuilders(String path, final 
NodeMap<NodeBuilder> nodeBuilders) {
+        return getContributingStores(path, mns -> 
nodeBuilders.get(mns).getChildNodeNames());
     }
 
     boolean shouldBeComposite(final String path) {
@@ -171,7 +171,7 @@ class CompositionContext {
         if (rootStates.size() != nonDefaultStores.size() + 1) {
             throw new IllegalArgumentException("Too many root states passed: " 
+ rootStates.size());
         }
-        return new CompositeNodeState("/", rootStates, this);
+        return new CompositeNodeState("/", NodeMap.create(rootStates), this);
     }
 
 }

Added: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java?rev=1804757&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java
 Fri Aug 11 08:31:12 2017
@@ -0,0 +1,109 @@
+/*
+ * 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.oak.composite;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class NodeMap<T> {
+
+    private final Map<MountedNodeStore, CacheableSupplier<T>> suppliers;
+
+    private NodeMap(Map<MountedNodeStore, CacheableSupplier<T>> suppliers) {
+        this.suppliers = suppliers;
+    }
+
+    public static <T> NodeMap<T> create(Map<MountedNodeStore, T> nodes) {
+        ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<T>> suppliers 
= ImmutableMap.builder();
+        nodes.forEach((mns, node) -> suppliers.put(mns, new 
CacheableSupplier<T>(node)));
+        return new NodeMap<>(suppliers.build());
+    }
+
+    public T get(MountedNodeStore nodeStore) {
+        if (!suppliers.containsKey(nodeStore)) {
+            Mount mount = nodeStore.getMount();
+            String mountName = mount.isDefault() ? "[default]" : 
mount.getName();
+            throw new IllegalStateException("Node is not available for the 
node store " + mountName);
+        }
+        return suppliers.get(nodeStore).get();
+    }
+
+    public <R> NodeMap<R> getAndApply(Function<T, R> function) {
+        ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<R>> 
newSuppliers = ImmutableMap.builder();
+        suppliers.forEach((mns, node) -> newSuppliers.put(mns, 
node.getAndApply(function)));
+        return new NodeMap<>(newSuppliers.build());
+    }
+
+    public <R> NodeMap<R> lazyApply(Function<T, R> function) {
+        ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<R>> 
newSuppliers = ImmutableMap.builder();
+        suppliers.forEach((mns, node) -> newSuppliers.put(mns, 
node.lazyApply(function)));
+        return new NodeMap<>(newSuppliers.build());
+    }
+
+    public NodeMap<T> replaceNode(MountedNodeStore nodeStore, T node) {
+        ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<T>> 
newSuppliers = ImmutableMap.builder();
+        suppliers.forEach((mns, n) -> {
+            if (mns != nodeStore) {
+                newSuppliers.put(mns, n);
+            }
+        });
+        newSuppliers.put(nodeStore, new CacheableSupplier<>(node));
+        return new NodeMap<>(newSuppliers.build());
+    }
+
+    private static class CacheableSupplier<T> implements Supplier<T> {
+
+        private Supplier<T> supplier;
+
+        private volatile T value;
+
+        public CacheableSupplier(Supplier<T> supplier) {
+            this.supplier = supplier;
+        }
+
+        public CacheableSupplier(T value) {
+            this.value = value;
+        }
+
+        @Override
+        public T get() {
+            if (value == null) {
+                synchronized (this) {
+                    if (value == null) {
+                        value = supplier.get();
+                        supplier = null;
+                    }
+                }
+            }
+            return value;
+        }
+
+        public <R> CacheableSupplier<R> getAndApply(Function<T, R> function) {
+            return new CacheableSupplier<>(function.apply(get()));
+        }
+
+        public <R> CacheableSupplier<R> lazyApply(Function<T, R> function) {
+            return new CacheableSupplier<>(() -> function.apply(get()));
+        }
+    }
+}


Reply via email to