rishabhdaim commented on code in PR #880:
URL: https://github.com/apache/jackrabbit-oak/pull/880#discussion_r1153464833
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/NodeStateCopier.java:
##########
@@ -145,29 +150,73 @@ public static boolean copyNodeStore(@NotNull final
NodeStore source, @NotNull fi
*
* @param source The NodeState to copy from.
* @param target The NodeBuilder to copy to.
+ * @param preserveOnTarget boolean to indicate no changes on target except
additions
+ * @param path current path
* @return Whether changes were made or not.
*/
- public static boolean copyProperties(NodeState source, NodeBuilder target)
{
+ public static boolean copyProperties(NodeState source, NodeBuilder target,
boolean preserveOnTarget, String path) {
boolean hasChanges = false;
// remove removed properties
- for (final PropertyState property : target.getProperties()) {
- final String name = property.getName();
- if (!source.hasProperty(name)) {
- target.removeProperty(name);
- hasChanges = true;
+ if (!preserveOnTarget) {
+ for (final PropertyState property : target.getProperties()) {
+ final String name = property.getName();
+ if (!source.hasProperty(name)) {
+ target.removeProperty(name);
+ hasChanges = true;
+ }
}
}
// add new properties and change changed properties
for (PropertyState property : source.getProperties()) {
if (!property.equals(target.getProperty(property.getName()))) {
- target.setProperty(property);
- hasChanges = true;
+ if (!isVersionPropertyEmpty(source, property,
preserveOnTarget, path)) {
+ target.setProperty(property);
+ hasChanges = true;
+ }
}
}
return hasChanges;
}
+
+ private static Set<String> getValues(NodeState nodeState, String prop) {
+ if (nodeState.hasProperty(prop)) {
+ Iterable<String> values =
nodeState.getProperty(prop).getValue(Type.STRINGS);
+ return StreamSupport.stream(values.spliterator(), false).filter(s
-> !s.isEmpty()).collect(Collectors.toSet());
+ }
+ return new HashSet<>();
Review Comment:
Implementation of this method only uses the size of `set` returned by this
method. So instead of creating an `set` of non empty properties, we should
simply return a boolean indicating whether it is empty or not.
```suggestion
private static boolean isVersionEmpty(NodeState nodeState, String prop) {
if (nodeState.hasProperty(prop)) {
Iterable<String> values =
nodeState.getProperty(prop).getValue(Type.STRINGS);
return StreamSupport.stream(values.spliterator(),
false).allMatch(String::isEmpty);
}
return true;
```
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/NodeStateCopier.java:
##########
@@ -145,29 +150,73 @@ public static boolean copyNodeStore(@NotNull final
NodeStore source, @NotNull fi
*
* @param source The NodeState to copy from.
* @param target The NodeBuilder to copy to.
+ * @param preserveOnTarget boolean to indicate no changes on target except
additions
+ * @param path current path
* @return Whether changes were made or not.
*/
- public static boolean copyProperties(NodeState source, NodeBuilder target)
{
+ public static boolean copyProperties(NodeState source, NodeBuilder target,
boolean preserveOnTarget, String path) {
boolean hasChanges = false;
// remove removed properties
- for (final PropertyState property : target.getProperties()) {
- final String name = property.getName();
- if (!source.hasProperty(name)) {
- target.removeProperty(name);
- hasChanges = true;
+ if (!preserveOnTarget) {
+ for (final PropertyState property : target.getProperties()) {
+ final String name = property.getName();
+ if (!source.hasProperty(name)) {
+ target.removeProperty(name);
+ hasChanges = true;
+ }
}
}
// add new properties and change changed properties
for (PropertyState property : source.getProperties()) {
if (!property.equals(target.getProperty(property.getName()))) {
- target.setProperty(property);
- hasChanges = true;
+ if (!isVersionPropertyEmpty(source, property,
preserveOnTarget, path)) {
+ target.setProperty(property);
+ hasChanges = true;
+ }
}
}
return hasChanges;
}
+
+ private static Set<String> getValues(NodeState nodeState, String prop) {
+ if (nodeState.hasProperty(prop)) {
+ Iterable<String> values =
nodeState.getProperty(prop).getValue(Type.STRINGS);
+ return StreamSupport.stream(values.spliterator(), false).filter(s
-> !s.isEmpty()).collect(Collectors.toSet());
+ }
+ return new HashSet<>();
+ }
+
+ private static boolean isVersionPropertyEmpty(NodeState source,
PropertyState property, boolean preserveOnTarget,
+ String path) {
+ if (preserveOnTarget) {
+ if (property.getName().equals(JcrConstants.JCR_UUID) ||
+ property.getName().equals(JcrConstants.JCR_SUCCESSORS) ||
+ property.getName().equals(JcrConstants.JCR_PREDECESSORS)) {
+ boolean versionPropertyEmpty = getValues(source,
property.getName()).isEmpty();
Review Comment:
```suggestion
boolean versionPropertyEmpty = isVersionEmpty(source,
property.getName());
```
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/version/VersionCopier.java:
##########
@@ -111,6 +125,72 @@ public boolean copyVersionHistory(String versionableUuid,
Calendar minDate, bool
return false;
}
+ private boolean hasNoConflicts(String versionHistoryPath, String
versionableUuid, boolean preserveOnTarget, NodeState sourceVersionHistory) {
+ // if preserveOnTarget is true then check no conflicts which means
version history has moved forward only
+ if (preserveOnTarget) {
Review Comment:
same as above, return if `preserveOnTarget` is false.
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/NodeStateCopier.java:
##########
@@ -145,29 +150,73 @@ public static boolean copyNodeStore(@NotNull final
NodeStore source, @NotNull fi
*
* @param source The NodeState to copy from.
* @param target The NodeBuilder to copy to.
+ * @param preserveOnTarget boolean to indicate no changes on target except
additions
+ * @param path current path
* @return Whether changes were made or not.
*/
- public static boolean copyProperties(NodeState source, NodeBuilder target)
{
+ public static boolean copyProperties(NodeState source, NodeBuilder target,
boolean preserveOnTarget, String path) {
boolean hasChanges = false;
// remove removed properties
- for (final PropertyState property : target.getProperties()) {
- final String name = property.getName();
- if (!source.hasProperty(name)) {
- target.removeProperty(name);
- hasChanges = true;
+ if (!preserveOnTarget) {
+ for (final PropertyState property : target.getProperties()) {
+ final String name = property.getName();
+ if (!source.hasProperty(name)) {
+ target.removeProperty(name);
+ hasChanges = true;
+ }
}
}
// add new properties and change changed properties
for (PropertyState property : source.getProperties()) {
if (!property.equals(target.getProperty(property.getName()))) {
- target.setProperty(property);
- hasChanges = true;
+ if (!isVersionPropertyEmpty(source, property,
preserveOnTarget, path)) {
+ target.setProperty(property);
+ hasChanges = true;
+ }
}
}
return hasChanges;
}
+
+ private static Set<String> getValues(NodeState nodeState, String prop) {
+ if (nodeState.hasProperty(prop)) {
+ Iterable<String> values =
nodeState.getProperty(prop).getValue(Type.STRINGS);
+ return StreamSupport.stream(values.spliterator(), false).filter(s
-> !s.isEmpty()).collect(Collectors.toSet());
+ }
+ return new HashSet<>();
+ }
+
+ private static boolean isVersionPropertyEmpty(NodeState source,
PropertyState property, boolean preserveOnTarget,
+ String path) {
+ if (preserveOnTarget) {
Review Comment:
I would simply return if `preserveOnTarget` is false.
```suggestion
if (!preserveOnTarget) {
return false;
```
then to go inside if condition.
As a general coding practice, I found:
`if not condition:
do little stuff
return
// now...
do lots of stuff.`
increases the code readability and reduce cyclomatic complexity.
##########
oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/migration/version/VersionCopierTest.java:
##########
@@ -44,18 +50,15 @@
import org.junit.Test;
import static java.util.Collections.singletonList;
-import static org.apache.jackrabbit.JcrConstants.JCR_FROZENNODE;
-import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
-import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
-import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
-import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
-import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.apache.jackrabbit.JcrConstants.*;
Review Comment:
Please import them individually.
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/version/VersionCopier.java:
##########
@@ -111,6 +125,72 @@ public boolean copyVersionHistory(String versionableUuid,
Calendar minDate, bool
return false;
}
+ private boolean hasNoConflicts(String versionHistoryPath, String
versionableUuid, boolean preserveOnTarget, NodeState sourceVersionHistory) {
+ // if preserveOnTarget is true then check no conflicts which means
version history has moved forward only
+ if (preserveOnTarget) {
+ NodeBuilder targetVersionHistory =
getVersionHistoryBuilder(targetVersionStorage, versionableUuid);
+ if (targetVersionHistory.exists()) {
+ VersionComparator versionComparator = new VersionComparator();
+
+ // version history id not equal
+ boolean conflictingVersionHistory =
!targetVersionHistory.getString(JCR_UUID).equals(sourceVersionHistory.getString(JCR_UUID));
+ if (conflictingVersionHistory) {
+ logger.info("Skipping version history for {}: Conflicting
version history found",
+ versionHistoryPath);
+ return false;
+ }
+
+ // Get the version names except jcr:rootVersion
+ List<String> targetVersions =
+
StreamSupport.stream(targetVersionHistory.getChildNodeNames().spliterator(),
false).filter(s -> !s.equals(JCR_ROOTVERSION) && !s.equals(JCR_VERSIONLABELS))
+
.sorted(versionComparator).collect(Collectors.toList());
+ List<String> sourceVersions =
+
StreamSupport.stream(sourceVersionHistory.getChildNodeNames().spliterator(),
false).filter(s -> !s.equals(JCR_ROOTVERSION) && !s.equals(JCR_VERSIONLABELS))
+
.sorted(versionComparator).collect(Collectors.toList());
+ // source version only has a rootVersion which means nothing
to update
+ boolean noUpdate = sourceVersions.isEmpty() ||
targetVersions.containsAll(sourceVersions);
+ if (noUpdate) {
+ logger.info("Skipping version history for {}: No update
required", versionHistoryPath);
+ return false;
+ }
+
+ // highest source version does not exist on target or
+ // all source versions already exist on target (diverged or no
diff)
+ boolean diverged =
!targetVersions.contains(sourceVersions.get(0));
+ if (diverged) {
+ logger.info("Skipping version history for {}: Versions
diverged", versionHistoryPath);
+ return false;
+ }
+
+ // highest source version UUID does not match the
corresponding version on target (diverged)
+ boolean conflictingHighestVersion =
+
!sourceVersionHistory.getChildNode(sourceVersions.get(0)).getString(JCR_UUID).equals(targetVersionHistory.getChildNode(sourceVersions.get(0)).getString(JCR_UUID));
Review Comment:
Please use `Objects.equal`
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/version/VersionCopier.java:
##########
@@ -111,6 +125,72 @@ public boolean copyVersionHistory(String versionableUuid,
Calendar minDate, bool
return false;
}
+ private boolean hasNoConflicts(String versionHistoryPath, String
versionableUuid, boolean preserveOnTarget, NodeState sourceVersionHistory) {
+ // if preserveOnTarget is true then check no conflicts which means
version history has moved forward only
+ if (preserveOnTarget) {
+ NodeBuilder targetVersionHistory =
getVersionHistoryBuilder(targetVersionStorage, versionableUuid);
+ if (targetVersionHistory.exists()) {
+ VersionComparator versionComparator = new VersionComparator();
+
+ // version history id not equal
+ boolean conflictingVersionHistory =
!targetVersionHistory.getString(JCR_UUID).equals(sourceVersionHistory.getString(JCR_UUID));
+ if (conflictingVersionHistory) {
+ logger.info("Skipping version history for {}: Conflicting
version history found",
+ versionHistoryPath);
+ return false;
+ }
+
+ // Get the version names except jcr:rootVersion
+ List<String> targetVersions =
+
StreamSupport.stream(targetVersionHistory.getChildNodeNames().spliterator(),
false).filter(s -> !s.equals(JCR_ROOTVERSION) && !s.equals(JCR_VERSIONLABELS))
+
.sorted(versionComparator).collect(Collectors.toList());
+ List<String> sourceVersions =
+
StreamSupport.stream(sourceVersionHistory.getChildNodeNames().spliterator(),
false).filter(s -> !s.equals(JCR_ROOTVERSION) && !s.equals(JCR_VERSIONLABELS))
+
.sorted(versionComparator).collect(Collectors.toList());
+ // source version only has a rootVersion which means nothing
to update
+ boolean noUpdate = sourceVersions.isEmpty() ||
targetVersions.containsAll(sourceVersions);
+ if (noUpdate) {
+ logger.info("Skipping version history for {}: No update
required", versionHistoryPath);
+ return false;
+ }
+
+ // highest source version does not exist on target or
+ // all source versions already exist on target (diverged or no
diff)
+ boolean diverged =
!targetVersions.contains(sourceVersions.get(0));
+ if (diverged) {
+ logger.info("Skipping version history for {}: Versions
diverged", versionHistoryPath);
+ return false;
+ }
+
+ // highest source version UUID does not match the
corresponding version on target (diverged)
+ boolean conflictingHighestVersion =
+
!sourceVersionHistory.getChildNode(sourceVersions.get(0)).getString(JCR_UUID).equals(targetVersionHistory.getChildNode(sourceVersions.get(0)).getString(JCR_UUID));
+ if (conflictingHighestVersion) {
+ logger.info("Skipping version history for {}: Old base
version id changed", versionHistoryPath);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ static class VersionComparator implements Comparator<String> {
Review Comment:
We are making a reverse comparison here.
Shouldn't we make a simple comparison and then where we are using we should
explicitly call `comparator.reversed()` to highlight that we are using a
reverse comparison?
##########
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/migration/version/VersionCopier.java:
##########
@@ -111,6 +125,72 @@ public boolean copyVersionHistory(String versionableUuid,
Calendar minDate, bool
return false;
}
+ private boolean hasNoConflicts(String versionHistoryPath, String
versionableUuid, boolean preserveOnTarget, NodeState sourceVersionHistory) {
+ // if preserveOnTarget is true then check no conflicts which means
version history has moved forward only
+ if (preserveOnTarget) {
+ NodeBuilder targetVersionHistory =
getVersionHistoryBuilder(targetVersionStorage, versionableUuid);
+ if (targetVersionHistory.exists()) {
+ VersionComparator versionComparator = new VersionComparator();
+
+ // version history id not equal
+ boolean conflictingVersionHistory =
!targetVersionHistory.getString(JCR_UUID).equals(sourceVersionHistory.getString(JCR_UUID));
Review Comment:
Since `NodeBuilder.getString()` can return null, I would compare them using
`Objects.equal`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]