Class AtomicCounterEditor

  • All Implemented Interfaces:
    Editor

    public class AtomicCounterEditor
    extends DefaultEditor

    Manages a node as Atomic Counter: a node which will handle at low level a protected property (PROP_COUNTER) in an atomic way. This will represent an increment or decrement of a counter in the case, for example, of Likes or Voting.

    Whenever you add a NodeTypeConstants.MIX_ATOMIC_COUNTER mixin to a node it will turn it into an atomic counter. Then in order to increment or decrement the oak:counter property you'll need to set the oak:increment one (PROP_INCREMENT). Please note that the oak:incremement will never be saved, only the oak:counter will be amended accordingly.

    So in order to deal with the counter from a JCR point of view you'll do something as follows

      Session session = ...
      
      // creating a counter node
      Node counter = session.getRootNode().addNode("mycounter");
      counter.addMixin("mix:atomicCounter"); // or use the NodeTypeConstants
      session.save();
      
      // Will output 0. the default value
      System.out.println("counter now: " + counter.getProperty("oak:counter").getLong());
      
      // incrementing by 5 the counter
      counter.setProperty("oak:increment", 5);
      session.save();
      
      // Will output 5
      System.out.println("counter now: " + counter.getProperty("oak:counter").getLong());
      
      // decreasing by 1
      counter.setProperty("oak:increment", -1);
      session.save();
      
      // Will output 4
      System.out.println("counter now: " + counter.getProperty("oak:counter").getLong());
      
      session.logout();
     

    Internal behavioural details

    The related jira ticket is OAK-2472. In a nutshell when you save an oak:increment behind the scene it takes its value and increment an internal counter. There will be an individual counter for each cluster node.

    Then it will consolidate all the internal counters into a single one: oak:counter. The consolidation process can happen either synchronously or asynchronously. Refer to AtomicCounterEditor(NodeBuilder, String, ScheduledExecutorService, NodeStore, Whiteboard) for details on when it consolidate one way or the other.

    synchronous. It means the consolidation, sum of all the internal counters, will happen in the same thread. During the lifecycle of the same commit.

    asynchronous. It means the internal counters will be set during the same commit; but it will eventually schedule a separate thread in which will retry some times to consolidate them.

    • Field Detail

      • PROP_INCREMENT

        public static final java.lang.String PROP_INCREMENT
        property to be set for incrementing/decrementing the counter
        See Also:
        Constant Field Values
      • PROP_COUNTER

        public static final java.lang.String PROP_COUNTER
        property with the consolidated counter
        See Also:
        Constant Field Values
      • PREFIX_PROP_COUNTER

        public static final java.lang.String PREFIX_PROP_COUNTER
        prefix used internally for tracking the counting requests
        See Also:
        Constant Field Values
      • PREFIX_PROP_REVISION

        public static final java.lang.String PREFIX_PROP_REVISION
        prefix used internally for tracking the cluster node related revision numbers
        See Also:
        Constant Field Values
    • Constructor Detail

      • AtomicCounterEditor

        public AtomicCounterEditor​(@NotNull
                                   @NotNull NodeBuilder builder,
                                   @Nullable
                                   @Nullable java.lang.String instanceId,
                                   @Nullable
                                   @Nullable java.util.concurrent.ScheduledExecutorService executor,
                                   @Nullable
                                   @Nullable NodeStore store,
                                   @Nullable
                                   @Nullable Whiteboard board)

        Create an instance of the editor for atomic increments. It can works synchronously as well as asynchronously. See class javadoc for details around it.

        If instanceId OR executor OR store OR board are null, the editor will switch to synchronous behaviour for consolidation. If no CommitHook will be found in the whiteboard, a EmptyHook will be provided to the NodeStore for merging.

        Parameters:
        builder - the build on which to work. Cannot be null.
        instanceId - the current Oak instance Id. If null editor will be synchronous.
        executor - the current Oak executor service. If null editor will be synchronous.
        store - the current Oak node store. If null the editor will be synchronous.
        board - the current Oak Whiteboard.
    • Method Detail

      • childNodeChanged

        public Editor childNodeChanged​(java.lang.String name,
                                       NodeState before,
                                       NodeState after)
                                throws CommitFailedException
        Description copied from interface: Editor
        Processes a changed child node. This method gets called for all child nodes that may contain changes between the before and after states.
        Specified by:
        childNodeChanged in interface Editor
        Overrides:
        childNodeChanged in class DefaultEditor
        Parameters:
        name - name of the changed node
        before - child node before the change
        after - child node after the change
        Returns:
        an editor for processing the subtree below the added node, or null if the subtree does not need processing
        Throws:
        CommitFailedException - if processing failed
      • leave

        public void leave​(NodeState before,
                          NodeState after)
                   throws CommitFailedException
        Description copied from interface: Editor
        Called after the given before and after states are compared. The implementation can use this method to post-process information collected during the content diff. For example an implementation that during the diff just recorded the fact that this node was modified in some way could then use this method to trigger an index update based on that modification flag.
        Specified by:
        leave in interface Editor
        Overrides:
        leave in class DefaultEditor
        Parameters:
        before - before state, non-existent if this node was added
        after - after state, non-existent if this node was removed
        Throws:
        CommitFailedException - if this commit should be rejected