Author: stefanegli
Date: Thu Jul 16 10:04:05 2020
New Revision: 1879938
URL: http://svn.apache.org/viewvc?rev=1879938&view=rev
Log:
OAK-9131 : introducing scanners for nt:frozenNode references
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRef.java
(with props)
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsByScanningCommand.java
(with props)
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsUsingIndexCommand.java
(with props)
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/FrozenNodeReferenceCreator.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-run/README.md
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
Modified: jackrabbit/oak/trunk/oak-run/README.md
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/README.md?rev=1879938&r1=1879937&r2=1879938&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/README.md (original)
+++ jackrabbit/oak/trunk/oak-run/README.md Thu Jul 16 10:04:05 2020
@@ -17,6 +17,8 @@ The following runmodes are currently ava
* debug : Print status information about an Oak repository
* explore : Starts a GUI browser based on java swing
* export : Export repository content as json
+ * frozennoderefsbyscanning : Scan for nt:frozenNode references via query
+ * frozennoderefsusingindex : Scan for nt:frozenNode references via
/oak:index
* garbage : Identifies blob garbage on a DocumentMK repository
* help : Print a list of available runmodes
* history : Trace the history of a node
@@ -136,6 +138,42 @@ browsing of an existing oak repository.
$ java -jar oak-run-*.jar explore /path/to/oak/repository [skip-size-check]
+frozennoderefsbyscanning
+------------------------
+
+This command executes a potentially expensive (!) traversing query searching
for
+all properties formatted as a UUID (incl String, Reference types) and verifies
+if they represent (potential) references to nt:frozenNode.
+
+Since this is a rather expensive command, consider using
frozennoderefsusingindex
+(at least first) instead.
+
+If this is used eg on a MongoDB, consider running the command against
+a secondary MongoDB node, such as to not overload the primary MongoDB node.
+
+This tool is part of the effort to change the default nt:frozenNode node type
+definition to no longer be a mix:referenceable (see OAK-9134). Even though
+existing definitions aren't modified, the tool can be used to verify if
+an existing repository would potentially be in violation of OAK-9134 - ie if
+there are existing use cases of nt:frozenNode being a mix:referenceable.
+
+
+frozennoderefsusingindex
+------------------------
+
+This command browses through /oak:index/references and verifies if they
+represent references to nt:frozenNode.
+
+If this is used eg on a MongoDB, consider running the command against
+a secondary MongoDB node, such as to not overload the primary MongoDB node.
+
+This tool is part of the effort to change the default nt:frozenNode node type
+definition to no longer be a mix:referenceable (see OAK-9134). Even though
+existing definitions aren't modified, the tool can be used to verify if
+an existing repository would potentially be in violation of OAK-9134 - ie if
+there are existing use cases of nt:frozenNode being a mix:referenceable.
+
+
History
-------
Modified:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java?rev=1879938&r1=1879937&r2=1879938&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
(original)
+++
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
Thu Jul 16 10:04:05 2020
@@ -42,6 +42,8 @@ public final class AvailableModes {
.put("debug", new DebugCommand())
.put("explore", new ExploreCommand())
.put(NodeStateExportCommand.NAME, new NodeStateExportCommand())
+ .put(FrozenNodeRefsByScanningCommand.NAME, new
FrozenNodeRefsByScanningCommand())
+ .put(FrozenNodeRefsUsingIndexCommand.NAME, new
FrozenNodeRefsUsingIndexCommand())
.put("garbage", new GarbageCommand())
.put("help", new HelpCommand())
.put("history", new HistoryCommand())
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRef.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRef.java?rev=1879938&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRef.java
(added)
+++
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRef.java
Thu Jul 16 10:04:05 2020
@@ -0,0 +1,72 @@
+/*
+ * 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.run;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Contains one particular reference to an nt:frozenNode.
+ */
+public class FrozenNodeRef {
+
+ static final String REFERENCE_TO_NT_FROZEN_NODE_FOUND_PREFIX = "Reference
to nt:frozenNode found : ";
+
+ private final String refPath;
+ private final String refName;
+ private final String refType;
+ private final String refValue;
+ private final String targetPath;
+
+ static boolean isFrozenNodeReferenceCandidate(String uuidRefPath) {
+ boolean isVersionStorage =
PathUtils.isAncestor("/jcr:system/jcr:versionStorage", uuidRefPath);
+ if (!isVersionStorage) {
+ // TODO: we could consider NOT continuing here, as an
nt:frozenNode could "in theory"
+ // also show up somewhere else - but it is slightly faster this
way..
+ return false;
+ }
+ int depth = PathUtils.getDepth(uuidRefPath);
+ if (depth <= 7) {
+ // TODO: we could consider NOT continuing here, as the structure
of /jcr:system/jcr:versionStorage
+ // could at some point change and this '7' no longer be correct -
but so far it is and it's faster this way
+ return false;
+ }
+ boolean containsJcrFrozenNodeName =
Iterables.contains(PathUtils.elements(uuidRefPath), "jcr:frozenNode");
+ if (!containsJcrFrozenNodeName) {
+ // TODO: we could consider NOT continuing here, as we might opt to
store frozenNodes
+ // under another parent than 'jcr:frozenNode' - but so far that's
how it is and it's faster
+ return false;
+ }
+ return true;
+ }
+
+ FrozenNodeRef(String refPropertyPath, String refPropertyName, String
refPropertyType, String refPropertyValue, String targetPath) {
+ this.refPath = refPropertyPath;
+ this.refName = refPropertyName;
+ this.refType = refPropertyType;
+ this.refValue = refPropertyValue;
+ this.targetPath = targetPath;
+ }
+
+ String toInfoString() {
+ return "Node " + refPath + " has a property '" + refName + "'" + " (of
type " + refType + ")" + " with a value '"
+ + refValue + "'" + " which points to an nt:frozenNode, namely
to: " + targetPath;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRef.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsByScanningCommand.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsByScanningCommand.java?rev=1879938&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsByScanningCommand.java
(added)
+++
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsByScanningCommand.java
Thu Jul 16 10:04:05 2020
@@ -0,0 +1,261 @@
+/*
+ * 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.run;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Collections;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
+import
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.DocumentQueue;
+import org.apache.jackrabbit.oak.run.cli.CommonOptions;
+import org.apache.jackrabbit.oak.run.cli.NodeStoreFixture;
+import org.apache.jackrabbit.oak.run.cli.NodeStoreFixtureProvider;
+import org.apache.jackrabbit.oak.run.cli.Options;
+import org.apache.jackrabbit.oak.run.commons.Command;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+
+import com.google.common.io.Closer;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * Scans and lists all references to nt:frozenNode and returns an exit code of
1 if any are found (0 otherwise).
+ * <p/>
+ * This variant does a *very expensive repository scan* for all properties
formatted as uuid
+ * ( LIKE \"________-____-____-____-____________\" )
+ * and checking if any reference points to an nt:frozenNode (under
/jcr:system/jcr:versionStorage
+ * at depth > 7).
+ * <p/>
+ * Note that any property with uuid that cannot be resolved will *not be
reported*, as that
+ * is a legitimate use case of uuid property use. Only uuids that resolve will
be analysed.
+ * <p/>
+ * Example:
+ * <pre>
+ * java -mx4g -jar oak-run-*.jar frozennoderefsbyscanning
mongodb://localhost/<dbname> -user=admin -password=admin
+ * </pre>
+ */
+public class FrozenNodeRefsByScanningCommand implements Command {
+
+ static {
+ // disable any query limits as our query is going to be a full scan,
and we are aware of it
+ System.setProperty("oak.queryLimitReads",
String.valueOf(Long.MAX_VALUE));
+
+ // // disable the WARN of the TraversingCursor
+ // LoggerContext c = (LoggerContext)
LoggerFactory.getILoggerFactory();
+ // Logger logger =
c.getLogger("org.apache.jackrabbit.oak.plugins.index.Cursors$TraversingCursor");
+ // logger.setLevel(Level.ERROR);
+ }
+
+ public static final String NAME = "frozennoderefsbyscanning";
+
+ private final String summary = "Scans repository and lists all references
to nt:frozenNode";
+
+ @Override
+ public void execute(String... args) throws Exception {
+ OptionParser parser = new OptionParser();
+
+ Options opts = new Options();
+ opts.setCommandName(NAME);
+ opts.setSummary(summary);
+ opts.setConnectionString(CommonOptions.DEFAULT_CONNECTION_STRING);
+
+ OptionSpec<String> userOption = parser.accepts("user", "User
name").withOptionalArg().defaultsTo("admin");
+ OptionSpec<String> passwordOption = parser.accepts("password",
"Password").withOptionalArg().defaultsTo("admin");
+
+ OptionSet options = opts.parseAndConfigure(parser, args);
+
+ System.out.println("Opening nodestore (readOnly=true)...");
+ // explicitly set readOnly mode (overwriting any possibly set
-read-write= option)
+ NodeStoreFixture nodeStoreFixture =
NodeStoreFixtureProvider.create(opts, true);
+ System.out.println("Nodestore opened.");
+
+ int count = uuidscan(userOption, passwordOption, options,
nodeStoreFixture);
+ if (count > 0) {
+ System.err.println("FAILURE: " + count + " Reference(s) (in any
uuid formatted property value) to nt:frozenNode found.");
+ System.exit(1);
+ } else {
+ System.out.println("SUCCESS: No references (in any uuid formatted
property value) to nt:frozenNode found.");
+ }
+ }
+
+ /**
+ * Scans the repository (via an expensive traversing query, ouch, hence it
needs username/password)
+ * and returns the number of references found. For this, any value
formatted like a UUID is considered,
+ * then verified if it points to an nt:frozenNode.
+ */
+ private int uuidscan(OptionSpec<String> userOption, OptionSpec<String>
passwordOption,
+ OptionSet options, NodeStoreFixture nodeStoreFixture) throws
IOException {
+ NodeStore nodeStore = nodeStoreFixture.getStore();
+ String user = userOption.value(options);
+ String password = passwordOption.value(options);
+
+ Closer closer = Utils.createCloserWithShutdownHook();
+ closer.register(nodeStoreFixture);
+
+ int count = 0;
+
+ try {
+
+ System.out.println("Logging in...");
+ Session session = openSession(nodeStore, "crx.default", user,
password);
+
+ System.out.println("Logged in, querying...");
+ QueryManager qm = session.getWorkspace().getQueryManager();
+
+ // query for only getting 'Reference' and 'WeakReference' would be
:
+ // SELECT * FROM [nt:base] AS p WHERE PROPERTY(*, 'Reference') IS
NOT NULL OR PROPERTY(*, 'WeakReference') IS NOT NULL
+ Query q = qm.createQuery("SELECT * FROM [nt:base] AS p WHERE
PROPERTY(*, '*') LIKE \"________-____-____-____-____________\"", "JCR-SQL2");
+ QueryResult qr = q.execute();
+ NodeIterator it = qr.getNodes();
+
+ while (it.hasNext()) {
+ Node n = it.nextNode();
+ PropertyIterator pit = n.getProperties();
+ while (pit.hasNext()) {
+ Property p = pit.nextProperty();
+ if ("jcr:uuid".equals(p.getName())) {
+ // jcr:uuid should be skipped as that's the identifier
of a node, not a reference
+ continue;
+ }
+ if (!p.isMultiple()) {
+ String propValue = p.getValue().getString();
+ if
(propValue.matches("........-....-....-....-............")) {
+ if (verify(session, n, p, propValue)) {
+ count++;
+ }
+ }
+ } else {
+ for (Value v : p.getValues()) {
+ String propValue = v.getString();
+ if
(propValue.matches("........-....-....-....-............")) {
+ if (verify(session, n, p, propValue)) {
+ count++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ System.out.println("logout...");
+ session.logout();
+ System.out.println("done.");
+
+ return count;
+ } catch (Throwable e) {
+ throw closer.rethrow(e);
+ } finally {
+ closer.close();
+ }
+ }
+
+ private boolean verify(Session session, Node n, Property p, String
propValue) throws RepositoryException {
+ try {
+ Node node = session.getNodeByIdentifier(propValue);
+ String path = node.getPath();
+ boolean candidate =
FrozenNodeRef.isFrozenNodeReferenceCandidate(path);
+ if (!candidate) {
+ return false;
+ }
+ Property primaryType = node.getProperty("jcr:primaryType");
+ String primaryTypeValue = primaryType.getString();
+ boolean isNtFrozenNode = "nt:frozenNode".equals(primaryTypeValue);
+ if (!isNtFrozenNode) {
+ // this is where we ultimately have to continue out in any
case - as only an nt:frozenNode
+ // is what we're interested in.
+ return false;
+ }
+
+ String uuid = propValue;
+ String referrerPath = n.getPath();
+ String referrerProperty = p.getName();
+ FrozenNodeRef ref = new FrozenNodeRef(referrerPath,
referrerProperty, PropertyType.nameFromValue(p.getType()), uuid, path);
+
+
System.out.println(FrozenNodeRef.REFERENCE_TO_NT_FROZEN_NODE_FOUND_PREFIX +
ref.toInfoString());
+
+ return true;
+ } catch (ItemNotFoundException notFound) {
+ // then ignore
+ return false;
+ }
+ }
+
+ // from JsonIndexCommand, slightly modified to be able to set the
workspace name
+ public static Session openSession(NodeStore nodeStore, String
workspaceName, String user, String password) throws RepositoryException {
+ if (nodeStore == null) {
+ return null;
+ }
+ StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
+ Oak oak = new
Oak(nodeStore).with(ManagementFactory.getPlatformMBeanServer());
+ oak.getWhiteboard().register(StatisticsProvider.class,
statisticsProvider, Collections.emptyMap());
+ LuceneIndexProvider provider =
/*JsonIndexCommand.*/createLuceneIndexProvider();
+ oak.with((QueryIndexProvider) provider).with((Observer)
provider).with(/*JsonIndexCommand.*/createLuceneIndexEditorProvider());
+ Jcr jcr = new Jcr(oak);
+ jcr.with(workspaceName);
+ Repository repository = jcr.createRepository();
+ return repository.login(new SimpleCredentials(user,
password.toCharArray()));
+ }
+
+ // from JsonIndexCommand, unmodified
+ private static LuceneIndexEditorProvider createLuceneIndexEditorProvider()
{
+ LuceneIndexEditorProvider ep = new LuceneIndexEditorProvider();
+ ScheduledExecutorService executorService = MoreExecutors
+
.getExitingScheduledExecutorService((ScheduledThreadPoolExecutor)
Executors.newScheduledThreadPool(5));
+ StatisticsProvider statsProvider = StatisticsProvider.NOOP;
+ int queueSize = Integer.getInteger("queueSize", 1000);
+ IndexTracker tracker = new IndexTracker();
+ DocumentQueue queue = new DocumentQueue(queueSize, tracker,
executorService, statsProvider);
+ ep.setIndexingQueue(queue);
+ return ep;
+ }
+
+ // from JsonIndexCommand, unmodified
+ private static LuceneIndexProvider createLuceneIndexProvider() {
+ return new LuceneIndexProvider();
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsByScanningCommand.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsUsingIndexCommand.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsUsingIndexCommand.java?rev=1879938&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsUsingIndexCommand.java
(added)
+++
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsUsingIndexCommand.java
Thu Jul 16 10:04:05 2020
@@ -0,0 +1,155 @@
+/*
+ * 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.run;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+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.run.Utils.NodeStoreOptions;
+import org.apache.jackrabbit.oak.run.commons.Command;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.io.Closer;
+
+/**
+ * Scans and lists all references to nt:frozenNode and returns an exit code of
1 if any are found (0 otherwise).
+ * <p/>
+ * This variant uses the /oak:index/reference by scanning through that list
(only)
+ * and checking if any reference points to an nt:frozenNode (under
/jcr:system/jcr:versionStorage
+ * at depth > 7).
+ * <p/>
+ * Example:
+ * <pre>
+ * java -mx4g -jar oak-run-*.jar frozennoderefsusingindex
mongodb://localhost/<dbname>
+ * </pre>
+ */
+public class FrozenNodeRefsUsingIndexCommand implements Command {
+
+ public static final String NAME = "frozennoderefsusingindex";
+
+ @Override
+ public void execute(String... args) throws Exception {
+ String help = NAME + " {<path>|<mongo-uri>|<jdbc-uri>} [options]";
+ Utils.NodeStoreOptions nopts = new
Utils.NodeStoreOptions(help).parse(args);
+ int count = countNtFrozenNodeReferences(nopts);
+ if (count > 0) {
+ System.err.println("FAILURE: " + count + " Reference(s) (in
/oak:index/references) to nt:frozenNode found.");
+ System.exit(1);
+ } else {
+ System.out.println("SUCCESS: No references (in
/oak:index/references) to nt:frozenNode found.");
+ }
+ }
+
+ /**
+ * Browses through /oak:index/references and counts how many references
therein are
+ * to nt:frozenNode.
+ */
+ private static int countNtFrozenNodeReferences(NodeStoreOptions nopts)
throws IOException {
+ Closer closer = Utils.createCloserWithShutdownHook();
+ try {
+ // explicitly set readOnly mode
+ System.out.println("Opening NodeStore in read-only mode.");
+ NodeStore store = Utils.bootstrapNodeStore(nopts, closer, true);
+ NodeState root = store.getRoot();
+
+ NodeState oakIndex = root.getChildNode("oak:index");
+ NodeState refIndex = oakIndex.getChildNode("reference");
+ NodeState uuidIndex = oakIndex.getChildNode("uuid");
+ NodeState uuids = uuidIndex.getChildNode(":index");
+ System.out.println("Scanning ... refindex = " + refIndex);
+ NodeState references = refIndex.getChildNode(":references");
+
+ int count = 0;
+
+ for (ChildNodeEntry child : references.getChildNodeEntries()) {
+ String uuid = child.getName();
+ NodeState uuidRef = uuids.getChildNode(uuid);
+
+ PropertyState entry = uuidRef.getProperty("entry");
+ Iterable<String> uuidRefPaths = entry.getValue(Type.STRINGS);
+ for (String uuidRefPath : uuidRefPaths) {
+ if
(!FrozenNodeRef.isFrozenNodeReferenceCandidate(uuidRefPath)) {
+ continue;
+ }
+ NodeState p = getNodeByPath(root, uuidRefPath);
+ PropertyState primaryType =
p.getProperty("jcr:primaryType");
+ boolean isNtFrozenNode =
"nt:frozenNode".equals(primaryType.getValue(Type.NAME));
+ if (!isNtFrozenNode) {
+ // this is where we ultimately have to continue out in
any case - as only an nt:frozenNode
+ // is what we're interested in.
+ continue;
+ }
+ count += countFrozenNodeReferences(root, child,
uuidRefPath, p);
+ }
+ }
+ System.out.println("Scanning done.");
+
+ return count;
+ } catch (Throwable e) {
+ throw closer.rethrow(e);
+ } finally {
+ closer.close();
+ }
+ }
+
+ private static int countFrozenNodeReferences(NodeState root,
ChildNodeEntry referenceChild, String uuidRefPath,
+ NodeState resolvedNodeState) {
+ String uuid = referenceChild.getName();
+ List<String> paths = new LinkedList<String>();
+ scanReferrerPaths(paths, "/", referenceChild.getNodeState());
+ int count = 0;
+ for (String referrerPropertyPath : paths) {
+ String referrerPath =
PathUtils.getAncestorPath(referrerPropertyPath, 1);
+ String referrerProperty = PathUtils.getName(referrerPropertyPath);
+ FrozenNodeRef ref = new FrozenNodeRef(referrerPath,
referrerProperty, "Reference", uuid, uuidRefPath);
+
System.out.println(FrozenNodeRef.REFERENCE_TO_NT_FROZEN_NODE_FOUND_PREFIX +
ref.toInfoString());
+ count++;
+ }
+ return count;
+ }
+
+ private static void scanReferrerPaths(List<String> paths, String
parentPath, NodeState referenceChildNodeState) {
+ for (ChildNodeEntry e : referenceChildNodeState.getChildNodeEntries())
{
+ String childName = e.getName();
+ NodeState childNode = e.getNodeState();
+ String childPath = PathUtils.concat(parentPath, childName);
+ PropertyState p = childNode.getProperty("match");
+ if (p != null && p.getValue(Type.BOOLEAN)) {
+ paths.add(childPath);
+ }
+ scanReferrerPaths(paths, childPath, childNode);
+ }
+ }
+
+ private static NodeState getNodeByPath(NodeState base, String path) {
+ Iterable<String> elems = PathUtils.elements(path);
+ NodeState n = base;
+ for (String elem : elems) {
+ n = n.getChildNode(elem);
+ }
+ return n;
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FrozenNodeRefsUsingIndexCommand.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java?rev=1879938&r1=1879937&r2=1879938&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
(original)
+++
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
Thu Jul 16 10:04:05 2020
@@ -57,6 +57,7 @@ import org.apache.jackrabbit.oak.run.cli
import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders;
import org.apache.jackrabbit.oak.segment.file.FileStore;
import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.file.ReadOnlyFileStore;
import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.jetbrains.annotations.Nullable;
@@ -167,6 +168,10 @@ class Utils {
}
public static NodeStore bootstrapNodeStore(NodeStoreOptions options,
Closer closer) throws IOException, InvalidFileStoreVersionException {
+ return bootstrapNodeStore(options, closer, false);
+ }
+
+ public static NodeStore bootstrapNodeStore(NodeStoreOptions options,
Closer closer, boolean readOnlyMode) throws IOException,
InvalidFileStoreVersionException {
String src = options.getStoreArg();
if (src == null || src.length() == 0) {
options.printHelpOn(System.err);
@@ -176,12 +181,23 @@ class Utils {
if (src.startsWith(MongoURI.MONGODB_PREFIX) || src.startsWith("jdbc"))
{
DocumentNodeStoreBuilder<?> builder =
createDocumentMKBuilder(options, closer);
if (builder != null) {
+ if (readOnlyMode) {
+ builder.setReadOnlyMode();
+ } // otherwise default is read-write
DocumentNodeStore store = builder.build();
closer.register(asCloseable(store));
return store;
}
}
+ if (readOnlyMode) {
+ ReadOnlyFileStore fileStore = fileStoreBuilder(new File(src))
+ .withStrictVersionCheck(true)
+ .buildReadOnly();
+ closer.register(fileStore);
+ return SegmentNodeStoreBuilders.builder(fileStore).build();
+ }
+
FileStore fileStore = fileStoreBuilder(new File(src))
.withStrictVersionCheck(true)
.build();
Added:
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/FrozenNodeReferenceCreator.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/FrozenNodeReferenceCreator.java?rev=1879938&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/FrozenNodeReferenceCreator.java
(added)
+++
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/FrozenNodeReferenceCreator.java
Thu Jul 16 10:04:05 2020
@@ -0,0 +1,115 @@
+/*
+ * 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.run;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.oak.run.cli.CommonOptions;
+import org.apache.jackrabbit.oak.run.cli.NodeStoreFixture;
+import org.apache.jackrabbit.oak.run.cli.NodeStoreFixtureProvider;
+import org.apache.jackrabbit.oak.run.cli.Options;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.io.Closer;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * Test class that simplifies creation of a property of type Reference, as
that's not
+ * easily achievable without a tool.
+ * <p/>
+ * The idea is that this might help testing the corresponding
FrozenNodeRefsByScanningCommand
+ * and FrozenNodeRefsUsingIndexCommand commands.
+ * <p/>
+ * Example:
+ * <pre>
+ * java -mx4g -cp oak-run-*-tests.jar
org.apache.jackrabbit.oak.run.FrozenNodeReferenceCreator
mongodb://localhost/<dbname> -user=admin -password=admin
-testCreateRefPath=<mypath> -testCreateRefProp=<mypropertyname>
-testCreateRefUuid=<myuuid> -read-write=true
+ * </pre>
+ */
+public class FrozenNodeReferenceCreator {
+
+ public static void main(String[] args) throws Exception {
+ OptionParser parser = new OptionParser();
+
+ Options opts = new Options();
+ opts.setConnectionString(CommonOptions.DEFAULT_CONNECTION_STRING);
+
+ OptionSpec<String> userOption = parser.accepts("user", "User
name").withOptionalArg().defaultsTo("admin");
+ OptionSpec<String> passwordOption = parser.accepts("password",
"Password").withOptionalArg().defaultsTo("admin");
+
+ OptionSpec<String> testCreateRefPathOption =
parser.accepts("testCreateRefPath", "FOR TESTING ONLY: path where to create a
reference from")
+ .withRequiredArg();
+ OptionSpec<String> testCreateRefPropOption =
parser.accepts("testCreateRefProp", "FOR TESTING ONLY: property name for create
a reference")
+ .withRequiredArg();
+ OptionSpec<String> testCreateRefTypeOption = parser
+ .accepts("testCreateRefType", "FOR TESTING ONLY: property
type: 'reference' or anything else for a plain String").withOptionalArg();
+ OptionSpec<String> testCreateRefUuidOption =
parser.accepts("testCreateRefUuid", "FOR TESTING ONLY: uuid to use as the
reference")
+ .withRequiredArg();
+
+ OptionSet options = opts.parseAndConfigure(parser, args);
+
+ System.out.println("Opening nodestore...");
+ NodeStoreFixture nodeStoreFixture =
NodeStoreFixtureProvider.create(opts);
+ System.out.println("Nodestore opened.");
+
+ NodeStore nodeStore = nodeStoreFixture.getStore();
+ String user = userOption.value(options);
+ String password = passwordOption.value(options);
+
+ String createRefPath = testCreateRefPathOption.value(options);
+ String createRefProp = testCreateRefPropOption.value(options);
+ String createRefType = testCreateRefTypeOption.value(options);
+ String createRefUuid = testCreateRefUuidOption.value(options);
+
+ Closer closer = Utils.createCloserWithShutdownHook();
+ closer.register(nodeStoreFixture);
+ try {
+
+ System.out.println("Logging in...");
+ Session session =
FrozenNodeRefsByScanningCommand.openSession(nodeStore, "crx.default", user,
password);
+
+ System.out.println(
+ "Logged in, creating test reference: " + "path=" +
createRefPath + ", property=" + createRefProp + ", uuid=" + createRefUuid);
+ Node n = session.getNode(createRefPath);
+ Value v;
+ if ("reference".equals(createRefType)) {
+ v = session.getValueFactory().createValue(createRefUuid,
PropertyType.REFERENCE);
+ System.out.println("Creating property of type REFERENCE");
+ } else {
+ v = session.getValueFactory().createValue(createRefUuid,
PropertyType.STRING);
+ System.out.println("Creating property of type STRING");
+ }
+ n.setProperty(createRefProp, v);
+ session.save();
+ System.out.println("Created. Done.");
+ session.logout();
+ closer.close();
+
+ } catch (Throwable e) {
+ throw closer.rethrow(e);
+ } finally {
+ closer.close();
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/FrozenNodeReferenceCreator.java
------------------------------------------------------------------------------
svn:eol-style = native