private void populateNodeRelEvent(PrimitiveElement element, TransactionDataImpl result) { for (long nodeId : element.nodes.keySet()) { CowNodeElement nodeElement = element.nodes.get(nodeId); NodeProxy node = nodeManager.newNodeProxyById(nodeId); NodeImpl nodeImpl = nodeManager.getNodeForProxy(nodeId, null); if (nodeElement.isDeleted()) { if (primitiveElement.createdNodes.contains(nodeId)) { continue; } result.deleted(node); } if (nodeElement.relationshipAddMap != null && !nodeElement.isDeleted()) { for (Integer type : nodeElement.relationshipAddMap.keySet()) { RelIdArray createdRels = nodeElement.relationshipAddMap.get(type); populateNodeRelEvent(element, result, nodeId, createdRels); } } if (nodeElement.relationshipRemoveMap != null) { for (Integer type : nodeElement.relationshipRemoveMap.keySet()) { Collection<Long> deletedRels = nodeElement.relationshipRemoveMap.get(type); for (long relId : deletedRels) { if (primitiveElement.createdRelationships.contains(relId)) { continue; } RelationshipProxy rel = nodeManager.newRelationshipProxyById(relId); if (rel.getStartNode().getId() == nodeId) { result.deleted(nodeManager.newRelationshipProxyById(relId)); } } } } if (nodeElement.propertyAddMap != null && !nodeElement.isDeleted()) { for (DefinedProperty data : nodeElement.propertyAddMap.values()) { String key = nodeManager.getKeyForProperty(data); Object oldValue = nodeImpl.getCommittedPropertyValue(nodeManager, key); Object newValue = data.value(); result.assignedProperty(node, key, newValue, oldValue); } } if (nodeElement.propertyRemoveMap != null) { for (DefinedProperty data : nodeElement.propertyRemoveMap.values()) { String key = nodeManager.getKeyForProperty(data); Object oldValue = data.value(); if (oldValue == null && !nodeElement.isDeleted()) { nodeImpl.getCommittedPropertyValue(nodeManager, key); } result.removedProperty(node, key, oldValue); } } } }
private void populateNodeRelEvent( PrimitiveElement element, TransactionDataImpl result, long nodeId, RelIdArray createdRels) { for (RelIdIterator iterator = createdRels.iterator(DirectionWrapper.BOTH); iterator.hasNext(); ) { long relId = iterator.next(); CowRelElement relElement = element.relationships.get(relId); if (relElement != null && relElement.isDeleted()) { continue; } RelationshipProxy rel = nodeManager.newRelationshipProxyById(relId); if (rel.getStartNode().getId() == nodeId) { result.created(nodeManager.newRelationshipProxyById(relId)); } } }
private void populateRelationshipPropertyEvents( PrimitiveElement element, TransactionDataImpl result) { for (long relId : element.relationships.keySet()) { CowRelElement relElement = element.relationships.get(relId); RelationshipProxy rel = nodeManager.newRelationshipProxyById(relId); RelationshipImpl relImpl = nodeManager.getRelationshipForProxy(relId); if (relElement.isDeleted()) { if (primitiveElement.createdRelationships.contains(relId)) { continue; } // note: this is done in node populate data // result.deleted( rel ); } if (relElement.propertyAddMap != null && !relElement.isDeleted()) { for (DefinedProperty data : relElement.propertyAddMap.values()) { String key = nodeManager.getKeyForProperty(data); Object oldValue = relImpl.getCommittedPropertyValue(nodeManager, key); Object newValue = data.value(); result.assignedProperty(rel, key, newValue, oldValue); } } if (relElement.propertyRemoveMap != null) { for (DefinedProperty data : relElement.propertyRemoveMap.values()) { String key = nodeManager.getKeyForProperty(data); Object oldValue = data.value(); if (oldValue != null && !relElement.isDeleted()) { relImpl.getCommittedPropertyValue(nodeManager, key); } result.removedProperty(rel, key, oldValue); } } } }
@Test public void arrayIndexOutOfBoundsInRelTypeArrayWhenCreatingRelationshipsConcurrently() throws Exception { // GIVEN // -- a node manager capable of serving light-weight RelationshipProxy capable of answering // getId() NodeManager nodeManager = mock(NodeManager.class); when(nodeManager.newRelationshipProxyById(anyLong())).thenAnswer(relationshipProxyWithId()); // -- a node that says it cannot load any more relationships NodeImpl node = mock(NodeImpl.class); when(node.getMoreRelationships(nodeManager)).thenReturn(LoadStatus.NOTHING); // -- a type iterator that at this point contains one relationship (0) ControlledRelIdIterator typeIterator = new ControlledRelIdIterator(0L); RelationshipIterator iterator = new RelationshipIterator( new RelIdIterator[] {typeIterator}, node, OUTGOING, nodeManager, false, false); // -- go forth one step in the iterator iterator.next(); // WHEN // -- one relationship has been returned, and we're in the middle of the next call to next() // typeIterator will get one more relationship in it. To mimic this we control the outcome of // RelIdIterator#hasNext() so that we get to the correct branch in the RelationshipIterator // code typeIterator.queueHasNextAnswers(false, false, true); long otherRelationship = 1, thirdRelationship = 2; typeIterator.add(otherRelationship, thirdRelationship); // -- go one more step, getting us into the state where the type index in RelationshipIterator // was incremented by mistake. Although this particular call to next() succeeds iterator.next(); // -- call next() again, where the first thing happening is to get the RelIdIterator with the // now invalid type index, causing ArrayIndexOutOfBoundsException Relationship returnedThirdRelationship = iterator.next(); // THEN assertEquals(thirdRelationship, returnedThirdRelationship.getId()); }