/* * Copyright (C) 2016 Fraunhofer Institut IOSB, Fraunhoferstr. 1, D 76131 * Karlsruhe, Germany. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.fraunhofer.iosb.ilt.frostserver.persistence; import com.github.fge.jsonpatch.JsonPatch; import de.fraunhofer.iosb.ilt.frostserver.model.EntityChangedMessage; import de.fraunhofer.iosb.ilt.frostserver.messagebus.MessageBus; import de.fraunhofer.iosb.ilt.frostserver.messagebus.MessageBusFactory; import de.fraunhofer.iosb.ilt.frostserver.model.core.Entity; import de.fraunhofer.iosb.ilt.frostserver.model.core.Id; import de.fraunhofer.iosb.ilt.frostserver.path.PathElementEntity; import de.fraunhofer.iosb.ilt.frostserver.path.PathElementEntitySet; import de.fraunhofer.iosb.ilt.frostserver.model.EntityType; import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain; import de.fraunhofer.iosb.ilt.frostserver.path.ResourcePath; import de.fraunhofer.iosb.ilt.frostserver.query.Query; import de.fraunhofer.iosb.ilt.frostserver.util.exception.IncompleteEntityException; import de.fraunhofer.iosb.ilt.frostserver.util.exception.NoSuchEntityException; import java.util.ArrayList; import java.util.List; /** * * @author jab * @author scf */ public abstract class AbstractPersistenceManager implements PersistenceManager { /** * The changed entity messages that need to be sent to the bus. */ private final List<EntityChangedMessage> changedEntities; protected AbstractPersistenceManager() { this.changedEntities = new ArrayList<>(); } @Override public List<EntityChangedMessage> getEntityChangedMessages() { return changedEntities; } private Entity fetchEntity(EntityType entityType, Id id) { Entity entity = get(entityType, id); for (NavigationPropertyMain property : entityType.getNavigationEntities()) { Object parentObject = entity.getProperty(property); if (parentObject instanceof Entity) { Entity parentEntity = (Entity) parentObject; parentEntity.setExportObject(true); } } return entity; } @Override public boolean insert(Entity entity) throws NoSuchEntityException, IncompleteEntityException { boolean result = doInsert(entity); if (result) { Entity newEntity = fetchEntity( entity.getEntityType(), entity.getId()); changedEntities.add( new EntityChangedMessage() .setEventType(EntityChangedMessage.Type.CREATE) .setEntity(newEntity) ); } return result; } public abstract boolean doInsert(Entity entity) throws NoSuchEntityException, IncompleteEntityException; @Override public boolean delete(PathElementEntity pathElement) throws NoSuchEntityException { Entity entity = getEntityByEntityPath(pathElement); boolean result = doDelete(pathElement); if (result) { changedEntities.add( new EntityChangedMessage() .setEventType(EntityChangedMessage.Type.DELETE) .setEntity(entity) ); } return result; } @Override public void delete(ResourcePath path, Query query) throws NoSuchEntityException { doDelete(path, query); } private Entity getEntityByEntityPath(PathElementEntity pathElement) { ResourcePath path = new ResourcePath(); path.addPathElement(new PathElementEntitySet(pathElement.getEntityType(), null), false, false); pathElement.setParent(path.getLastElement()); path.addPathElement(pathElement, true, true); return (Entity) get(path, null); } public abstract boolean doDelete(PathElementEntity pathElement) throws NoSuchEntityException; public abstract void doDelete(ResourcePath path, Query query); @Override public boolean update(PathElementEntity pathElement, Entity entity) throws NoSuchEntityException, IncompleteEntityException { EntityChangedMessage result = doUpdate(pathElement, entity); if (result != null) { result.setEventType(EntityChangedMessage.Type.UPDATE); Entity newEntity = fetchEntity( entity.getEntityType(), entity.getId()); result.setEntity(newEntity); changedEntities.add(result); } return result != null; } /** * Update the given entity and return a message with the fields that were * changed. The entity is added to the message by the * AbstractPersistenceManager. * * @param pathElement The path to the entity to update. * @param entity The updated entity. * @return A message with the fields that were changed. The entity is added * by the AbstractPersistenceManager. * @throws NoSuchEntityException If the entity does not exist. * @throws IncompleteEntityException If the entity does not have all the * required fields. */ public abstract EntityChangedMessage doUpdate(PathElementEntity pathElement, Entity entity) throws NoSuchEntityException, IncompleteEntityException; @Override public boolean update(PathElementEntity pathElement, JsonPatch patch) throws NoSuchEntityException, IncompleteEntityException { EntityChangedMessage result = doUpdate(pathElement, patch); if (result != null) { result.setEventType(EntityChangedMessage.Type.UPDATE); changedEntities.add(result); } return result != null; } /** * Update the given entity and return a message with the entity and fields * that were changed. * * @param pathElement The path to the entity to update. * @param patch The patch to apply to the entity. * @return A message with the entity and the fields that were changed. * @throws NoSuchEntityException If the entity does not exist. * @throws IncompleteEntityException If the entity does not have all the * required fields. */ public abstract EntityChangedMessage doUpdate(PathElementEntity pathElement, JsonPatch patch) throws NoSuchEntityException, IncompleteEntityException; /** * If there are changes to send, connect to bus and send them. */ private void fireEntityChangeEvents() { MessageBus messageBus = MessageBusFactory.getMessageBus(); changedEntities.forEach(messageBus::sendMessage); clearEntityChangedEvents(); } private void clearEntityChangedEvents() { changedEntities.clear(); } @Override public void commit() { if (doCommit()) { fireEntityChangeEvents(); } } protected abstract boolean doCommit(); @Override public void rollback() { if (doRollback()) { clearEntityChangedEvents(); } } protected abstract boolean doRollback(); @Override public void close() { if (doClose()) { clearEntityChangedEvents(); } } protected abstract boolean doClose(); }