All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.maxifier.mxcache.impl.resource.DependencyTracker Maven / Gradle / Ivy

Go to download

Constains all classes necessary for launching a MxCache-instrumentated application

There is a newer version: 2.6.9
Show newest version
/*
 * Copyright (c) 2008-2014 Maxifier Ltd. All Rights Reserved.
 */
package com.maxifier.mxcache.impl.resource;

import com.maxifier.mxcache.InternalProbeFailedError;
import com.maxifier.mxcache.caches.CleaningNode;
import com.maxifier.mxcache.resource.MxResource;
import com.maxifier.mxcache.util.HashWeakReference;
import com.maxifier.mxcache.util.TIdentityHashSet;
import gnu.trove.set.hash.THashSet;

import javax.annotation.Nonnull;

import javax.annotation.Nullable;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;

/**
 * @author Alexander Kochurov ([email protected])
 */
public final class DependencyTracker {
    private static final ThreadLocal NODE = new ThreadLocal();
    private static final ThreadLocal> RESOURCE_VIEW_NODES = new ThreadLocal>();

    /** ???? ???? ????????????, ???? ?? ????? ???????????????? ???????????? */
    public static final DependencyNode DUMMY_NODE = new DummyDependencyNode("");

    public static final DependencyNode PROBE_NODE = new DummyDependencyNode("");
    public static final DependencyNode NOCACHE_NODE = new DummyDependencyNode("");
    public static final DependencyNode HIDDEN_CALLER_NODE = new HiddenCallerDependencyNode();

    private DependencyTracker() {
    }

    /**
     * This method marks current top of stack dependency node as dependant of given node; it doesn't push given node to
     * stack. It is equivalent to exit(track(node)) sequence, but works much faster.
     *
     * @param node dependency node
     */
    public static void mark(DependencyNode node) {
        if (node != DUMMY_NODE) {
            track(node, NODE.get());
        }
    }

    /**
     * This method pushes given node to top of stack, and marks current on-stack node as dependant of given node.
     * This method returns current stack top; later it should be passed as an argument to
     * {@link DependencyTracker#exit(DependencyNode)}.
     *
     * @param node node to push
     * @return previous on-stack dependency node
     */
    public static DependencyNode track(DependencyNode node) {
        DependencyNode oldNode = NODE.get();
        assert oldNode == null || node != null : "Could not reassign node to null";
        if (oldNode == PROBE_NODE) {
            throw new InternalProbeFailedError();
        }
        if (node != DUMMY_NODE) {
            track(node, oldNode);
            NODE.set(node);
        } else if (oldNode == null) {
            // ???? ???? ???? ??????, ?? ???-???? ???????? ??? ????-?????????
            NODE.set(node);
        }
        return oldNode;
    }

    private static void track(DependencyNode node, DependencyNode oldNode) {
        if (oldNode != null && oldNode != DUMMY_NODE && node != oldNode) {
            node.trackDependency(oldNode);
        }
    }

    public static void exit(@Nullable DependencyNode callerNode) {
        NODE.set(callerNode);
    }

    public static DependencyNode get() {
        return NODE.get();
    }

    public static boolean hasUnderlyingNode() {
        return NODE.get() != null;
    }

    public static void addExplicitDependency(DependencyNode node, MxResource resource) {
        if (resource instanceof MxResourceImpl) {
            ((MxResourceImpl) resource).trackDependency(node);
        } else {
            throw new UnsupportedOperationException("Current implementation of dependency tracker cannot deal with resource class " + resource.getClass());
        }
    }

    public static TIdentityHashSet saveResourceViewNodes(DependencyNode node) {
        TIdentityHashSet oldNodes = RESOURCE_VIEW_NODES.get();
        RESOURCE_VIEW_NODES.set(DependencyTracker.getResourceViewDependentNodes(node));
        return oldNodes;
    }

    public static boolean isDependentResourceView(CleaningNode node) {
        TIdentityHashSet cleaningNodes = RESOURCE_VIEW_NODES.get();
        return cleaningNodes != null && cleaningNodes.contains(node);
    }

    public static void exitDependentResourceView(TIdentityHashSet oldNodes) {
        RESOURCE_VIEW_NODES.set(oldNodes);
    }

    public static TIdentityHashSet getAllDependentNodes(Iterable src) {
        return getAllDependentNodes(src, Collections.emptySet());
    }

    public static TIdentityHashSet getAllDependentNodes(Iterable src, Collection initial) {
        Set nodes = new THashSet();
        Queue queue = new LinkedList();

        DependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(nodes, queue);

        return getDependentNodes(src, initial, visitor);
    }

    /**
     * Finds only dependent caches with ResourceView annotation.
     *
     * @param sourceNode initial dependency node to start searching dependent caches from
     * @return set of dependent caches
     */
    public static TIdentityHashSet getResourceViewDependentNodes(DependencyNode sourceNode) {
        Set resourceViewableNodes = new THashSet();
        Queue queue = new LinkedList();

        DependencyNodeVisitor visitor = new ResourceViewDependencyNodeVisitor(resourceViewableNodes, queue);

        return getDependentNodes(Collections.singleton(sourceNode), Collections.emptySet(), visitor);
    }

    /**
     * Finds dependent caches taking ResourceView annotation into account.
     *
     * @param sourceNode initial dependency node to start searching dependent caches from
     * @return set of dependent caches
     */
    public static TIdentityHashSet getChangedDependentNodes(DependencyNode sourceNode) {
        Set nodes = new THashSet();
        Queue queue = new LinkedList();

        DependencyNodeVisitor visitor = new CollectingChangedDependencyNodeVisitor(nodes, queue);

        return getDependentNodes(Collections.singleton(sourceNode), Collections.emptySet(), visitor);
    }

    private static TIdentityHashSet getDependentNodes(Iterable src, Collection initial, DependencyNodeVisitor visitor) {
        // we enqueue this but we don't add it cause we don't want to call it's appendElements(Set)
        for (DependencyNode node : src) {
            node.visitDependantNodes(visitor);
        }

        Queue queue = visitor.getQueue();
        while (!queue.isEmpty()) {
            queue.poll().visitDependantNodes(visitor);
        }
        Set nodes = visitor.getNodes();
        TIdentityHashSet result = new TIdentityHashSet(nodes.size() + initial.size());
        result.addAll(initial);
        for (DependencyNode node : nodes) {
            node.appendNodes(result);
        }
        return result;
    }

    public static boolean isDummyNode(DependencyNode node) {
        return node instanceof DummyDependencyNode;
    }

    public static boolean isBypassCaches() {
        return NOCACHE_NODE.equals(get());
    }

    private static final class DummyDependencyNode implements DependencyNode {
        private final Reference selfReference = new HashWeakReference(this);
        private final String name;

        private DummyDependencyNode(String name) {
            this.name = name;
        }

        @Override
        public void visitDependantNodes(DependencyNodeVisitor visitor) {
            // do nothing, we don't track dependencies for that cache
        }

        @Override
        public void appendNodes(TIdentityHashSet elements) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void trackDependency(DependencyNode node) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void addNode(@Nonnull CleaningNode cache) {
            // ?????? ?? ??????, ???? ?????? ?? ??????????? ?????, ??? ??? ???? ????????
        }

        @Override
        public String toString() {
            return name;
        }

        @Override
        public Reference getSelfReference() {
            return selfReference;
        }
    }

    private static class HiddenCallerDependencyNode implements DependencyNode {
        private final Reference thisReference = new WeakReference(this);

        @Override
        public Reference getSelfReference() {
            return thisReference;
        }

        @Override
        public void visitDependantNodes(DependencyNodeVisitor visitor) {
            // do nothing
        }

        @Override
        public void appendNodes(TIdentityHashSet elements) {
            // do nothing
        }

        @Override
        public void trackDependency(DependencyNode node) {
            // do nothing
        }

        @Override
        public void addNode(@Nonnull CleaningNode cache) {
            // do nothing
        }

        @Override
        public String toString() {
            return "HiddenCaller - see MxCache.hideCallerDependencies";
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy