diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/atomic/AtomicCounterEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/atomic/AtomicCounterEditor.java index a7f9343c3a..84ec55393d 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/atomic/AtomicCounterEditor.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/atomic/AtomicCounterEditor.java @@ -60,7 +60,7 @@ import org.slf4j.LoggerFactory; * property ({@link #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 {@link NodeTypeConstants#MIX_ATOMIC_COUNTER} mixin to a node it will turn it * into an atomic counter. Then in order to increment or decrement the {@code oak:counter} property @@ -68,59 +68,59 @@ import org.slf4j.LoggerFactory; * {@code oak:incremement} will never be saved, only the {@code 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

- * + * + *

Internal behavioural details

+ * *

* The related jira ticket is OAK-2472. * In a nutshell when you save an {@code 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: {@code oak:counter}. The * consolidation process can happen either synchronously or asynchronously. Refer to * {@link #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 @@ -132,22 +132,22 @@ public class AtomicCounterEditor extends DefaultEditor { * property to be set for incrementing/decrementing the counter */ public static final String PROP_INCREMENT = "oak:increment"; - + /** * property with the consolidated counter */ public static final String PROP_COUNTER = "oak:counter"; - + /** * prefix used internally for tracking the counting requests */ public static final String PREFIX_PROP_COUNTER = ":oak-counter-"; - + /** * prefix used internally for tracking the cluster node related revision numbers */ public static final String PREFIX_PROP_REVISION = ":rev-"; - + private static final Logger LOG = LoggerFactory.getLogger(AtomicCounterEditor.class); private final NodeBuilder builder; private final String path; @@ -155,22 +155,22 @@ public class AtomicCounterEditor extends DefaultEditor { private final ScheduledExecutorService executor; private final NodeStore store; private final Whiteboard board; - + /** * the current counter property name */ private final String counterName; - + /** * the current revision property name */ private final String revisionName; - + /** * instruct whether to update the node on leave. */ private boolean update; - + /** *

* Create an instance of the editor for atomic increments. It can works synchronously as well as @@ -182,14 +182,14 @@ public class AtomicCounterEditor extends DefaultEditor { * be found in the whiteboard, a {@link EmptyHook} will be provided to the {@link NodeStore} for * merging. *

- * + * * @param builder the build on which to work. Cannot be null. * @param instanceId the current Oak instance Id. If null editor will be synchronous. * @param executor the current Oak executor service. If null editor will be synchronous. * @param store the current Oak node store. If null the editor will be synchronous. * @param board the current Oak {@link Whiteboard}. */ - public AtomicCounterEditor(@NotNull final NodeBuilder builder, + public AtomicCounterEditor(@NotNull final NodeBuilder builder, @Nullable String instanceId, @Nullable ScheduledExecutorService executor, @Nullable NodeStore store, @@ -197,9 +197,9 @@ public class AtomicCounterEditor extends DefaultEditor { this("", checkNotNull(builder), instanceId, executor, store, board); } - private AtomicCounterEditor(final String path, - final NodeBuilder builder, - @Nullable String instanceId, + private AtomicCounterEditor(final String path, + final NodeBuilder builder, + @Nullable String instanceId, @Nullable ScheduledExecutorService executor, @Nullable NodeStore store, @Nullable Whiteboard board) { @@ -209,8 +209,8 @@ public class AtomicCounterEditor extends DefaultEditor { this.executor = executor; this.store = store; this.board = board; - - counterName = instanceId == null ? PREFIX_PROP_COUNTER : + + counterName = instanceId == null ? PREFIX_PROP_COUNTER : PREFIX_PROP_COUNTER + instanceId; revisionName = instanceId == null ? PREFIX_PROP_REVISION : PREFIX_PROP_REVISION + instanceId; @@ -234,20 +234,20 @@ public class AtomicCounterEditor extends DefaultEditor { } return process; } - + /** *

* consolidate the {@link #PREFIX_PROP_COUNTER} properties and sum them into the * {@link #PROP_COUNTER} *

- * + * *

* The passed in {@code NodeBuilder} must have * {@link org.apache.jackrabbit.JcrConstants#JCR_MIXINTYPES JCR_MIXINTYPES} with * {@link NodeTypeConstants#MIX_ATOMIC_COUNTER MIX_ATOMIC_COUNTER}. * If not it will be silently ignored. *

- * + * * @param builder the builder to work on. Cannot be null. */ public static void consolidateCount(@NotNull final NodeBuilder builder) { @@ -263,27 +263,27 @@ public class AtomicCounterEditor extends DefaultEditor { private void setUniqueCounter(final long value) { update = true; - + PropertyState counter = builder.getProperty(counterName); PropertyState revision = builder.getProperty(revisionName); - + long currentValue = 0; if (counter != null) { currentValue = counter.getValue(LONG); } - + long currentRevision = 0; if (revision != null) { currentRevision = revision.getValue(LONG); } - + currentValue += value; currentRevision += 1; builder.setProperty(counterName, currentValue, LONG); builder.setProperty(revisionName, currentRevision, LONG); } - + @Override public void propertyAdded(final PropertyState after) throws CommitFailedException { if (shallWeProcessProperty(after, path, builder)) { @@ -299,8 +299,8 @@ public class AtomicCounterEditor extends DefaultEditor { } @Override - public Editor childNodeChanged(final String name, - final NodeState before, + public Editor childNodeChanged(final String name, + final NodeState before, final NodeState after) throws CommitFailedException { return new AtomicCounterEditor(path + '/' + name, builder.getChildNode(name), instanceId, executor, store, board); @@ -314,7 +314,7 @@ public class AtomicCounterEditor extends DefaultEditor { "Executing synchronously. instanceId: {}, store: {}, executor: {}, board: {}", new Object[] { instanceId, store, executor, board }); consolidateCount(builder); - } else { + } else { CommitHook hook = WhiteboardUtils.getService(board, CommitHook.class); if (hook == null) { LOG.trace("CommitHook not registered with Whiteboard. Falling back to sync."); @@ -322,31 +322,31 @@ public class AtomicCounterEditor extends DefaultEditor { } else { long delay = 500; ConsolidatorTask t = new ConsolidatorTask( - path, - builder.getProperty(revisionName), - store, - executor, - delay, + path, + builder.getProperty(revisionName), + store, + executor, + delay, hook); - LOG.debug("[{}] Scheduling process by {}ms", t.getName(), delay); - executor.schedule(t, delay, TimeUnit.MILLISECONDS); + LOG.debug("[{}] Scheduling process by {}ms", t.getName(), delay); + executor.schedule(t, delay, TimeUnit.MILLISECONDS); } } } } - + public static class ConsolidatorTask implements Callable { /** * millis over which the task will timeout */ public static final long MAX_TIMEOUT = Long .getLong("oak.atomiccounter.task.timeout", 32000); - + /** - * millis below which the next delay will schedule at this amount. + * millis below which the next delay will schedule at this amount. */ public static final long MIN_TIMEOUT = 500; - + private final String name; private final String p; private final PropertyState rev; @@ -355,9 +355,9 @@ public class AtomicCounterEditor extends DefaultEditor { private final long delay; private final long start; private final CommitHook hook; - - public ConsolidatorTask(@NotNull String path, - @Nullable PropertyState revision, + + public ConsolidatorTask(@NotNull String path, + @Nullable PropertyState revision, @NotNull NodeStore store, @NotNull ScheduledExecutorService exec, long delay, @@ -383,23 +383,23 @@ public class AtomicCounterEditor extends DefaultEditor { this.name = task.name; this.start = task.start; } - + @Override - public Void call() throws Exception { + public Void call() throws Exception { try { LOG.debug("[{}] Async consolidation running: path: {}, revision: {}", name, p, rev); NodeBuilder root = s.getRoot().builder(); NodeBuilder b = builderFromPath(root, p); - + dumpNode(b, p); - + if (!b.exists()) { LOG.debug("[{}] Builder for '{}' from NodeStore not available. Rescheduling.", name, p); reschedule(); return null; } - + if (!checkRevision(b, rev)) { LOG.debug("[{}] Missing or not yet a valid revision for '{}'. Rescheduling.", name, p); @@ -423,12 +423,12 @@ public class AtomicCounterEditor extends DefaultEditor { reschedule(); return null; } - + LOG.debug("[{}] Consolidation for '{}', '{}' completed in {}ms", name, p, rev, System.currentTimeMillis() - start); return null; } - + private void dumpNode(@NotNull NodeBuilder b, String path) { if (LOG.isTraceEnabled()) { checkNotNull(b); @@ -439,7 +439,7 @@ public class AtomicCounterEditor extends DefaultEditor { LOG.trace("[{}] Node status for {}:\n{}", this.name, path, s); } } - + private void reschedule() { long d = nextDelay(delay); if (isTimedOut(d)) { @@ -447,12 +447,12 @@ public class AtomicCounterEditor extends DefaultEditor { name, p); return; } - + ConsolidatorTask task = new ConsolidatorTask(this, d); LOG.debug("[{}] Rescheduling '{}' by {}ms", task.getName(), p, d); exec.schedule(task, d, TimeUnit.MILLISECONDS); } - + public static long nextDelay(long currentDelay) { if (currentDelay < MIN_TIMEOUT) { return MIN_TIMEOUT; @@ -462,24 +462,24 @@ public class AtomicCounterEditor extends DefaultEditor { } return currentDelay * 2; } - + public static boolean isTimedOut(long delay) { return delay > MAX_TIMEOUT; } - + public String getName() { return name; } } - + /** * checks that the revision provided in the PropertyState is less or equal than the one within * the builder. - * + * * if {@code revision} is null it will always be {@code true}. - * + * * If {@code builder} does not contain the property it will always return false. - * + * * @param builder * @param revision * @return @@ -493,16 +493,16 @@ public class AtomicCounterEditor extends DefaultEditor { if (builderRev == null) { return false; } - + long brValue = builderRev.getValue(Type.LONG); long rValue = revision.getValue(Type.LONG); - + if (brValue >= rValue) { return true; } return false; } - + private static NodeBuilder builderFromPath(@NotNull NodeBuilder ancestor, @NotNull String path) { NodeBuilder b = checkNotNull(ancestor); for (String name : PathUtils.elements(checkNotNull(path))) { @@ -510,12 +510,12 @@ public class AtomicCounterEditor extends DefaultEditor { } return b; } - + /** * check whether the provided builder has to be consolidated or not. A node has to be * consolidate if the sum of all the hidden counter does not match the exposed one. It could * happen that some other nodes previously saw our change and already consolidated it. - * + * * @param b the builde to check. Canno be null. * @return true if the sum of the hidden counters does not match the exposed one. */ @@ -525,14 +525,14 @@ public class AtomicCounterEditor extends DefaultEditor { if (counter == null) { counter = LongPropertyState.createLongProperty(PROP_COUNTER, 0); } - + long hiddensum = 0; for (PropertyState p : b.getProperties()) { if (p.getName().startsWith(PREFIX_PROP_COUNTER)) { hiddensum += p.getValue(LONG); } } - + return counter.getValue(LONG) != hiddensum; } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/impl/ImmutableTree.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/impl/ImmutableTree.java index 75f8e3f729..5709be2b73 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/impl/ImmutableTree.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/tree/impl/ImmutableTree.java @@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable; * Immutable implementation of the {@code Tree} interface in order to provide * the much feature rich API functionality for a given {@code NodeState}. * - *

Tree hierarchy

+ *

Tree hierarchy

* Due to the nature of this {@code Tree} implementation creating a proper * hierarchical view of the tree structure is the responsibility of the caller. * It is recommended to start with the state of the @@ -49,7 +49,7 @@ import org.jetbrains.annotations.Nullable; * {@link #ImmutableTree(ImmutableTree.ParentProvider, String, org.apache.jackrabbit.oak.spi.state.NodeState)} * an specify an appropriate {@code ParentProvider} implementation. * - *

ParentProvider

+ *

ParentProvider

* Apart from create the tree hierarchy in traversal mode this tree implementation * allows to instantiate disconnected trees that depending on the use may * never or on demand retrieve hierarchy information. The following default @@ -64,13 +64,13 @@ import org.jetbrains.annotations.Nullable; * upon hierarchy related methods like {@link #getParent()}, {@link #getPath()} * * - *

Filtering 'hidden' items

+ *

Filtering 'hidden' items

* This {@code Tree} implementation reflects the item hierarchy as exposed by the * underlying {@code NodeState}. In contrast to the mutable implementations it * does not filter out 'hidden' items as identified by * {@code org.apache.jackrabbit.oak.spi.state.NodeStateUtils#isHidden(String)}. * - *

Equality and hash code

+ *

Equality and hash code

* In contrast to {@link org.apache.jackrabbit.oak.plugins.tree.impl.AbstractMutableTree} * the {@code ImmutableTree} implements * {@link Object#equals(Object)} and {@link Object#hashCode()}: Two {@code ImmutableTree}s @@ -111,11 +111,13 @@ public final class ImmutableTree extends AbstractTree implements TreeTypeAware, } //----------------------------------------------------------< TypeAware >--- + @Override @Nullable public TreeType getType() { return type; } + @Override public void setType(@NotNull TreeType type) { this.type = type; } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java index f95466f379..286ead35cb 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java @@ -132,7 +132,7 @@ public class AuthenticationConfigurationImpl extends ConfigurationBase implement * related validation is omitted * * - *

Configuration Options

+ *

Configuration Options

* - * - *

Table Layout

+ * + *

Table Layout

*

* Data for each of the DocumentStore's {@link Collection}s is stored in its own * database table (with a name matching the collection). @@ -203,7 +203,7 @@ import com.google.common.collect.Sets; * testing, as tables can also be dropped automatically when the store is * disposed (this only happens for those tables that have been created on * demand). - *

Versioning

+ *

Versioning

*

* The initial database layout used in OAK 1.0 through 1.6 is version 0. *

@@ -216,7 +216,7 @@ import com.google.common.collect.Sets; * The code deals with both version 0, version 1 and version 2 table layouts. By * default, it tries to create version 2 tables, and also tries to upgrade * existing version 0 and 1 tables to version 2. - *

DB-specific information

+ *

DB-specific information

*

* Databases need to be configured so that: *