Skip to content

Commit 25e2ac4

Browse files
committed
fix list synchronisation problems shown by tests #16
1 parent 2f55194 commit 25e2ac4

File tree

8 files changed

+67
-24
lines changed

8 files changed

+67
-24
lines changed

synchronizefx-core/src/main/java/de/saxsys/synchronizefx/core/metamodel/CommandListCreator.java

+21-5
Original file line numberDiff line numberDiff line change
@@ -308,20 +308,24 @@ private void addToList(final UUID listId, final int position, final Object value
308308
final State state) {
309309
final boolean isObservableObject = createObservableObject(value, state);
310310

311+
final ListPropertyMetaData metaData = listMetaDataStore.getMetaDataOrFail(listId);
311312
ListVersionChange change;
312313
if (state.skipKnown) {
313314
// List is already known on other peers, update the version.
314-
change = increaseListVersion(listId);
315+
change = increaseListVersion(metaData);
315316
} else {
316-
// Initial walk through the whole list.
317-
change = new ListVersionChange(INITIAL_LIST_VERSION, INITIAL_LIST_VERSION);
317+
// Initial walk through the whole list, do not update the version.
318+
change = new ListVersionChange(metaData.getLocalVersion(), metaData.getLocalVersion());
318319
}
319320
final AddToList msg = new AddToList(listId, change, valueMapper.map(value, isObservableObject), position);
320321
state.commands.add(msg);
321322
}
322323

323324
private ListVersionChange increaseListVersion(final UUID listId) {
324-
final ListPropertyMetaData metaData = listMetaDataStore.getMetaDataOrFail(listId);
325+
return increaseListVersion(listMetaDataStore.getMetaDataOrFail(listId));
326+
}
327+
328+
private ListVersionChange increaseListVersion(final ListPropertyMetaData metaData) {
325329
final ListVersionChange change = new ListVersionChange(metaData.getLocalVersion(), UUID.randomUUID());
326330
metaData.setLocalVersion(change.getToVersion());
327331
return change;
@@ -396,11 +400,23 @@ protected boolean visitSingleValueProperty(final Property<?> fieldValue) {
396400
protected boolean visitCollectionProperty(final ListProperty<?> fieldValue) {
397401
final boolean listWasKnown = objectRegistry.getId(fieldValue).isPresent();
398402
final UUID fieldId = registerPropertyAndParent(getCurrentField(), fieldValue);
399-
if (!state.skipKnown && !listWasKnown) {
403+
if (!listWasKnown) {
404+
// initial walk through the meta model
400405
listMetaDataStore.storeMetaDataOrFail(fieldValue, new ListPropertyMetaData(
401406
INITIAL_LIST_VERSION, INITIAL_LIST_VERSION));
402407
}
403408
final ListIterator<?> it = fieldValue.listIterator();
409+
if (!it.hasNext()) {
410+
final ListPropertyMetaData metaData = listMetaDataStore.getMetaDataOrFail(fieldValue);
411+
if (metaData.getLocalVersion() != INITIAL_LIST_VERSION) {
412+
// This can happen when there where elements added after the list was created which where
413+
// all removed afterwards so that the list is now empty. This means that no AddToList
414+
// command will be created which would update the list version. Therefore the following
415+
// command will do this.
416+
state.commands.add(new RemoveFromList(fieldId, new ListVersionChange(INITIAL_LIST_VERSION,
417+
metaData.getLocalVersion()), 0, 0));
418+
}
419+
}
404420
int index = 0;
405421
while (it.hasNext()) {
406422
final Object o = it.next();

synchronizefx-core/src/main/java/de/saxsys/synchronizefx/core/metamodel/CommandListExecutor.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.lang.reflect.Field;
2323
import java.util.IdentityHashMap;
24+
import java.util.List;
2425
import java.util.Map;
2526
import java.util.Map.Entry;
2627
import java.util.Set;
@@ -190,8 +191,11 @@ private void execute(final CreateObservableObject command) {
190191
private void registerInMetaModel(final Object object, final UUID id) {
191192
objectRegistry.registerObject(object, id);
192193
if (object instanceof ListProperty) {
193-
listPropertyMetaDataStore.storeMetaDataOrFail((ListProperty<?>) object, new ListPropertyMetaData(
194-
CommandListCreator.INITIAL_LIST_VERSION, CommandListCreator.INITIAL_LIST_VERSION));
194+
final List<?> list = (List<?>) object;
195+
if (!listPropertyMetaDataStore.hasMetaDataFor(list)) {
196+
listPropertyMetaDataStore.storeMetaDataOrFail(list, new ListPropertyMetaData(
197+
CommandListCreator.INITIAL_LIST_VERSION, CommandListCreator.INITIAL_LIST_VERSION));
198+
}
195199
}
196200
}
197201

synchronizefx-core/src/main/java/de/saxsys/synchronizefx/core/metamodel/ListPropertyMetaDataStore.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public ListPropertyMetaData getMetaDataOrFail(final List<?> list) {
8181
*
8282
* <p>
8383
* <em>Performance hint:</em> If a reference to the list property itself is available, the method
84-
* {@link #getMetaDataOrFail(Object)} should be used as this saves a lookup in a map.
84+
* {@link #getMetaDataOrFail(List)} should be used as this saves a lookup in a map.
8585
* </p>
8686
*
8787
* @param listId
@@ -106,14 +106,24 @@ public ListPropertyMetaData getMetaDataOrFail(final UUID listId) throws ObjectTo
106106
* <code>listId</code>
107107
*/
108108
public void storeMetaDataOrFail(final List<?> list, final ListPropertyMetaData metaData)
109-
throws ObjectToIdMappingException {
109+
throws ObjectToIdMappingException {
110110
if (listToData.get(list) != null) {
111111
throw new ObjectToIdMappingException("Meta data for a known property should be registered twice. "
112112
+ "The clients may no longer be synchron.");
113113
}
114114
listToData.put(list, metaData);
115115
}
116116

117+
/**
118+
* Checks if this meta data store does already have meta data for a given list.
119+
*
120+
* @param list The list that should be checked for already having meta data.
121+
* @return <code>true</code> if meta data is known and <code>false</code> if not.
122+
*/
123+
public boolean hasMetaDataFor(final List<?> list) {
124+
return listToData.containsKey(list);
125+
}
126+
117127
/**
118128
* The meta data about a {@link List}.
119129
*

synchronizefx-core/src/main/java/de/saxsys/synchronizefx/core/metamodel/executors/lists/ListCommandVersionRepairer.java

-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ public class ListCommandVersionRepairer {
5353
*/
5454
public void repairLocalCommandsVersion(final Queue<ListCommand> localCommands,
5555
final ListCommand originalRemoteCommand) {
56-
if (localCommands.isEmpty()) {
57-
return;
58-
}
5956

6057
localCommands.add(repairCommand(localCommands.poll(), originalRemoteCommand.getListVersionChange()
6158
.getToVersion(), randomUUID()));

synchronizefx-core/src/main/java/de/saxsys/synchronizefx/core/metamodel/executors/lists/ReparingListPropertyCommandExecutor.java

+14-9
Original file line numberDiff line numberDiff line change
@@ -133,19 +133,24 @@ private void execute(final ListCommand command) {
133133
updateVersion(command);
134134
log.remove();
135135
} else {
136-
// change local list
137-
final List<? extends ListCommand> indexRepairedCommands = indexRepairer.repairCommands(
136+
// repair indices
137+
List<? extends ListCommand> repairedCommands = indexRepairer.repairCommands(
138138
metaData.getUnapprovedCommands(), command);
139-
versionRepairer.repairLocalCommandsVersion(metaData.getUnapprovedCommands(), command);
140-
final List<? extends ListCommand> versionRepairedCommands = versionRepairer.repairRemoteCommandVersion(
141-
indexRepairedCommands, metaData.getUnapprovedCommandsAsList());
142-
for (final ListCommand repaired : versionRepairedCommands) {
139+
140+
if (metaData.getUnapprovedCommands().size() > 0) {
141+
// repair versions if local commands are left after repairing indices.
142+
versionRepairer.repairLocalCommandsVersion(metaData.getUnapprovedCommands(), command);
143+
repairedCommands = versionRepairer.repairRemoteCommandVersion(repairedCommands,
144+
metaData.getUnapprovedCommandsAsList());
145+
// re-send repaired local changes
146+
topologyLayerCallback.sendCommands((List) metaData.getUnapprovedCommandsAsList());
147+
}
148+
149+
// execute repaired commands
150+
for (final ListCommand repaired : repairedCommands) {
143151
executeCommand(repaired);
144152
}
145153
updateVersion(command);
146-
147-
// re-send repaired local changes
148-
topologyLayerCallback.sendCommands((List) metaData.getUnapprovedCommandsAsList());
149154
}
150155
}
151156

synchronizefx-core/src/main/java/de/saxsys/synchronizefx/core/metamodel/executors/lists/ReplaceInListRepairer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
*
3333
* @author Raik Bieniek
3434
*/
35-
public final class ReplaceInListRepairer {
35+
public class ReplaceInListRepairer {
3636

3737
/**
3838
* Repairs a {@link ReplaceInList} in relation to an {@link AddToList} command.

synchronizefx-core/src/test/java/de/saxsys/synchronizefx/core/metamodel/ListPropertyMetaDataStoreTest.java

+11
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ public void canRetrieveMetaDataByListId() {
9090

9191
assertThat(cut.getMetaDataOrFail(list1Id)).isSameAs(exampleMetaData1);
9292
}
93+
94+
/**
95+
* The cut can check if meta data is already known for a list.
96+
*/
97+
@Test
98+
public void canCheckIfMetaDataIsStoredForList() {
99+
cut.storeMetaDataOrFail(list1, exampleMetaData1);
100+
101+
assertThat(cut.hasMetaDataFor(list1)).isTrue();
102+
assertThat(cut.hasMetaDataFor(list2)).isFalse();
103+
}
93104

94105
/**
95106
* When {@link ListPropertyMetaData} should be stored for a property that already has meta data, the cut should

synchronizefx-core/src/test/java/de/saxsys/synchronizefx/core/testutils/SaveParameterCallback.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import de.saxsys.synchronizefx.core.metamodel.TopologyLayerCallback;
2626
import de.saxsys.synchronizefx.core.metamodel.commands.Command;
2727

28-
import static org.junit.Assert.fail;
28+
import static org.assertj.core.api.Assertions.fail;
2929

3030
/**
3131
* An implementation that saves the parameters of the callback function so that they can be evaluated in tests.
@@ -41,7 +41,7 @@ public void sendCommands(final List<Command> commands) {
4141

4242
@Override
4343
public void onError(final SynchronizeFXException error) {
44-
fail("exception occured: " + error.getMessage());
44+
fail("exception occured: ", error);
4545
}
4646

4747
@Override

0 commit comments

Comments
 (0)