com.graphaware.tx.event.improved.data.lazy.LazyEntityTransactionData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tx-api Show documentation
Show all versions of tx-api Show documentation
Improved Transaction API with Graph Snapshotting
The newest version!
/*
* Copyright (c) 2013-2020 GraphAware
*
* This file is part of the GraphAware Framework.
*
* GraphAware Framework is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of
* the GNU General Public License along with this program. If not, see
* .
*/
package com.graphaware.tx.event.improved.data.lazy;
import com.graphaware.common.util.Change;
import com.graphaware.tx.event.improved.data.EntityTransactionData;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.event.PropertyEntry;
import org.neo4j.logging.Log;
import com.graphaware.common.log.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* {@link com.graphaware.tx.event.improved.data.EntityTransactionData} that lazily initializes its internal structures (indexed transaction data)
* as they are needed by callers to prevent unnecessary overheads.
*
* @param type of the entity.
*/
public abstract class LazyEntityTransactionData implements EntityTransactionData {
private static final Log LOG = LoggerFactory.getLogger(LazyEntityTransactionData.class);
private Map created = null;
private Map deleted = null;
private Map> changed = null;
/**
* >
*/
private Map> createdProperties = null;
/**
* >
*/
private Map> deletedProperties = null;
/**
* >
*/
private Map>> changedProperties = null;
/**
* > of properties of deleted entities
*/
private Map> deletedEntityProperties = null;
/**
* Create an old snapshot of an original entity.
*
* @param original to create a snapshot from.
* @return the snapshot.
*/
protected abstract T oldSnapshot(T original);
/**
* Create a new snapshot of an original entity.
*
* @param original to create a snapshot from.
* @return the snapshot.
*/
protected abstract T newSnapshot(T original);
@Override
public boolean hasBeenCreated(T entity) {
initializeCreated();
return created.containsKey(entity.getId());
}
@Override
public Collection getAllCreated() {
initializeCreated();
return Collections.unmodifiableCollection(created.values());
}
private void initializeCreated() {
if (created == null) {
created = new HashMap<>();
for (T created : created()) {
this.created.put(created.getId(), newSnapshot(created));
}
}
}
/**
* Get all entities created in the transaction from the Neo4j API.
*
* @return created entities.
*/
protected abstract Iterable created();
@Override
public boolean hasBeenDeleted(T entity) {
initializeDeleted();
return deleted.containsKey(entity.getId());
}
@Override
public T getDeleted(T entity) {
initializeDeleted();
if (!hasBeenDeleted(entity)) {
throw new IllegalArgumentException(entity + " has not been deleted!");
}
return deleted.get(entity.getId());
}
@Override
public Collection getAllDeleted() {
initializeDeleted();
return Collections.unmodifiableCollection(deleted.values());
}
private void initializeDeleted() {
if (deleted == null) {
deleted = new HashMap<>();
for (T deleted : deleted()) {
this.deleted.put(deleted.getId(), oldSnapshot(deleted));
}
}
}
/**
* Get all entities deleted in the transaction from the Neo4j API.
*
* @return created entities.
*/
protected abstract Iterable deleted();
@Override
public boolean hasBeenChanged(T entity) {
initializeChanged();
return changedContainsKey(entity);
}
@Override
public Change getChanged(T entity) {
initializeChanged();
if (!hasBeenChanged(entity)) {
throw new IllegalArgumentException(entity + " has not been changed!");
}
return changed.get(entity.getId());
}
@Override
public Collection> getAllChanged() {
initializeChanged();
return Collections.unmodifiableCollection(changed.values());
}
protected void initializeChanged() {
initializeCreated();
initializeDeleted();
if (changed == null) {
changed = new HashMap<>();
for (PropertyEntry propertyEntry : assignedProperties()) {
if (hasNotActuallyChanged(propertyEntry)) {
continue;
}
T candidate = propertyEntry.entity();
if (!hasBeenCreated(candidate)) {
registerChange(candidate);
}
}
for (PropertyEntry propertyEntry : removedProperties()) {
T candidate = propertyEntry.entity();
if (!hasBeenDeleted(candidate)) {
registerChange(candidate);
}
}
doInitializeChanged();
}
}
protected void doInitializeChanged() {
//for subclasses
}
protected void registerChange(T candidate) {
if (!changedContainsKey(candidate)) {
Change change = createChangeObject(candidate);
changed.put(candidate.getId(), change);
}
}
protected boolean changedContainsKey(T candidate) {
return changed.containsKey(candidate.getId());
}
protected Change createChangeObject(T candidate) {
return new Change<>(oldSnapshot(candidate), newSnapshot(candidate));
}
/**
* Get all assigned properties from the Neo4j API.
*
* @return assigned properties.
*/
protected abstract Iterable> assignedProperties();
/**
* Get all removed properties from the Neo4j API.
*
* @return removed properties.
*/
protected abstract Iterable> removedProperties();
@Override
public boolean hasPropertyBeenCreated(T entity, String key) {
initializeProperties();
if (!hasBeenChanged(entity)) {
LOG.warn(entity + " has not been changed but the caller thinks it should have created properties.");
return false;
}
if (!createdProperties.containsKey(entity.getId())) {
return false;
}
return createdProperties.get(entity.getId()).containsKey(key);
}
@Override
public Map createdProperties(T entity) {
initializeProperties();
if (!hasBeenChanged(entity)) {
LOG.warn(entity + " has not been changed but the caller thinks it should have created properties.");
return Collections.emptyMap();
}
if (!createdProperties.containsKey(entity.getId())) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(createdProperties.get(entity.getId()));
}
@Override
public boolean hasPropertyBeenDeleted(T entity, String key) {
initializeProperties();
if (!hasBeenChanged(entity)) {
LOG.warn(entity + " has not been changed but the caller thinks it should have deleted properties.");
return false;
}
if (!deletedProperties.containsKey(entity.getId())) {
return false;
}
return deletedProperties.get(entity.getId()).containsKey(key);
}
@Override
public Map deletedProperties(T entity) {
initializeProperties();
if (!hasBeenChanged(entity)) {
LOG.warn(entity + " has not been changed but the caller thinks it should have deleted properties.");
return Collections.emptyMap();
}
if (!deletedProperties.containsKey(entity.getId())) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(deletedProperties.get(entity.getId()));
}
@Override
public Map propertiesOfDeletedEntity(T entity) {
initializeProperties();
if (!hasBeenDeleted(entity)) {
LOG.error(entity + " has not been deleted but the caller thinks it has! This is a bug.");
throw new IllegalStateException(entity + " has not been deleted but the caller thinks it has! This is a bug.");
}
if (!deletedEntityProperties.containsKey(entity.getId())) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(deletedEntityProperties.get(entity.getId()));
}
@Override
public boolean hasPropertyBeenChanged(T entity, String key) {
initializeProperties();
if (!hasBeenChanged(entity)) {
LOG.warn(entity + " has not been changed but the caller thinks it should have changed properties.");
return false;
}
if (!changedProperties.containsKey(entity.getId())) {
return false;
}
return changedProperties.get(entity.getId()).containsKey(key);
}
@Override
public Map> changedProperties(T entity) {
initializeProperties();
if (!hasBeenChanged(entity)) {
LOG.warn(entity + " has not been changed but the caller thinks it should have changed properties.");
return Collections.emptyMap();
}
if (!changedProperties.containsKey(entity.getId())) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(changedProperties.get(entity.getId()));
}
private void initializeProperties() {
if (createdProperties != null) {
assert changedProperties != null;
assert deletedProperties != null;
assert deletedEntityProperties != null;
return;
}
//initializeCreated(); // - called by initializeChanged()
//initializeDeleted(); // - called by initializeChanged()
initializeChanged();
createdProperties = new HashMap<>();
deletedProperties = new HashMap<>();
changedProperties = new HashMap<>();
deletedEntityProperties = new HashMap<>();
for (PropertyEntry propertyEntry : assignedProperties()) {
T entity = propertyEntry.entity();
if (hasBeenCreated(entity)) {
continue;
}
if (hasNotActuallyChanged(propertyEntry)) {
continue;
}
if (propertyEntry.previouslyCommittedValue() == null) {
if (!createdProperties.containsKey(entity.getId())) {
createdProperties.put(entity.getId(), new HashMap());
}
createdProperties.get(entity.getId()).put(propertyEntry.key(), propertyEntry.value());
} else {
if (!changedProperties.containsKey(entity.getId())) {
changedProperties.put(entity.getId(), new HashMap>());
}
changedProperties.get(entity.getId()).put(propertyEntry.key(), new Change<>(propertyEntry.previouslyCommittedValue(), propertyEntry.value()));
}
}
for (PropertyEntry propertyEntry : removedProperties()) {
T entity = propertyEntry.entity();
if (deleted.containsKey(entity.getId())) {
if (!deletedEntityProperties.containsKey(entity.getId())) {
deletedEntityProperties.put(entity.getId(), new HashMap());
}
deletedEntityProperties.get(entity.getId()).put(propertyEntry.key(), propertyEntry.previouslyCommittedValue());
continue;
}
if (!changedContainsKey(entity)) {
throw new IllegalStateException(entity + " seems to have not been deleted or changed, this is a bug");
}
assert changedContainsKey(entity);
if (!deletedProperties.containsKey(entity.getId())) {
deletedProperties.put(entity.getId(), new HashMap());
}
deletedProperties.get(entity.getId()).put(propertyEntry.key(), propertyEntry.previouslyCommittedValue());
}
}
private boolean hasNotActuallyChanged(PropertyEntry propertyEntry) {
return propertyEntry.previouslyCommittedValue() != null && propertyEntry.previouslyCommittedValue().equals(propertyEntry.value());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy