Skip to content

Commit

Permalink
Reworked special handling of tables into hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
hylkevds committed May 19, 2024
1 parent 4d29885 commit 3f6706a
Show file tree
Hide file tree
Showing 10 changed files with 624 additions and 298 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 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.plugin.coremodel;

import de.fraunhofer.iosb.ilt.frostserver.model.core.PkValue;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.JooqPersistenceManager;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.HookPostDelete;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.tables.TableCollection;
import de.fraunhofer.iosb.ilt.frostserver.util.exception.NoSuchEntityException;
import org.jooq.TableField;
import org.jooq.impl.DSL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* @author hylke
*/
class HookPostDeleteLocation implements HookPostDelete {

private static final Logger LOGGER = LoggerFactory.getLogger(HookPostDeleteLocation.class.getName());

@Override
public void postDelete(JooqPersistenceManager pm, PkValue entityId) throws NoSuchEntityException {
final TableCollection tables = pm.getTableCollection();
// Also postDelete all historicalLocations that no longer reference any location
TableImpHistLocations thl = tables.getTableForClass(TableImpHistLocations.class);
TableImpLocationsHistLocations tlhl = tables.getTableForClass(TableImpLocationsHistLocations.class);
int count = pm.getDslContext().delete(thl).where(((TableField) thl.getId()).in(DSL.select(thl.getId()).from(thl).leftJoin(tlhl).on(((TableField) thl.getId()).eq(tlhl.getHistLocationId())).where(tlhl.getLocationId().isNull()))).execute();
LOGGER.debug("Deleted {} HistoricalLocations", count);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2023 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.plugin.coremodel;

import de.fraunhofer.iosb.ilt.frostserver.model.EntityType;
import de.fraunhofer.iosb.ilt.frostserver.model.core.Entity;
import de.fraunhofer.iosb.ilt.frostserver.model.ext.TimeInstant;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.JooqPersistenceManager;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.EntityFactories;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.HookPostInsert;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.tables.TableCollection;
import de.fraunhofer.iosb.ilt.frostserver.property.EntityPropertyMain;
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain.NavigationPropertyEntity;
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain.NavigationPropertyEntitySet;
import de.fraunhofer.iosb.ilt.frostserver.util.exception.IncompleteEntityException;
import de.fraunhofer.iosb.ilt.frostserver.util.exception.NoSuchEntityException;
import java.util.Collections;
import java.util.Map;
import net.time4j.Moment;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.TableField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* @author hylke
*/
class HookPostInsertHistLoc implements HookPostInsert {

private static final Logger LOGGER = LoggerFactory.getLogger(HookPostInsertHistLoc.class.getName());

@Override
public boolean postInsertIntoDatabase(JooqPersistenceManager pm, Entity histLoc, Map<Field, Object> insertFields) throws NoSuchEntityException, IncompleteEntityException {
final EntityFactories ef = pm.getEntityFactories();
final TableCollection tc = pm.getTableCollection();
final EntityType etHistLoc = histLoc.getEntityType();
final NavigationPropertyEntity npThing = (NavigationPropertyEntity) etHistLoc.getNavigationProperty("Thing");
final NavigationPropertyEntitySet npLocations = (NavigationPropertyEntitySet) etHistLoc.getNavigationProperty("Locations");
final EntityPropertyMain<TimeInstant> epTime = etHistLoc.getEntityProperty("time");
Entity thing = histLoc.getProperty(npThing);
Object thingId = thing.getPrimaryKeyValues().get(0);
DSLContext dslContext = pm.getDslContext();
TableImpHistLocations thl = tc.getTableForClass(TableImpHistLocations.class);
final TimeInstant hlTime = histLoc.getProperty(epTime);
Moment newTime = hlTime.getDateTime();
// https://github.com/opengeospatial/sensorthings/issues/30
// Check the time of the latest HistoricalLocation of our thing.
// If this time is earlier than our time, set the Locations of our Thing to our Locations.
Record lastHistLocation = dslContext.select(Collections.emptyList()).from(thl).where(((TableField) thl.getThingId()).eq(thingId).and(thl.time.gt(newTime))).orderBy(thl.time.desc()).limit(1).fetchOne();
if (lastHistLocation == null) {
// We are the newest.
// Unlink old Locations from Thing.
TableImpThingsLocations qtl = tc.getTableForClass(TableImpThingsLocations.class);
long count = dslContext.delete(qtl).where(((TableField) qtl.getThingId()).eq(thingId)).execute();
LOGGER.debug(EntityFactories.UNLINKED_L_FROM_T, count, thingId);
// Link new locations to Thing.
for (Entity l : histLoc.getProperty(npLocations)) {
if (!l.getPrimaryKeyValues().isFullySet() || !ef.entityExists(pm, l, true)) {
throw new NoSuchEntityException("Location with no id.");
}
Object locationId = l.getPrimaryKeyValues().get(0);
dslContext.insertInto(qtl).set((TableField) qtl.getThingId(), thingId).set(qtl.getLocationId(), locationId).execute();
LOGGER.debug(EntityFactories.LINKED_L_TO_T, locationId, thingId);
}
}
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* Copyright (C) 2023 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.plugin.coremodel;

import de.fraunhofer.iosb.ilt.configurable.annotations.ConfigurableField;
import de.fraunhofer.iosb.ilt.configurable.editor.EditorString;
import de.fraunhofer.iosb.ilt.frostserver.model.EntityChangedMessage;
import de.fraunhofer.iosb.ilt.frostserver.model.EntityType;
import de.fraunhofer.iosb.ilt.frostserver.model.ModelRegistry;
import de.fraunhofer.iosb.ilt.frostserver.model.core.Entity;
import de.fraunhofer.iosb.ilt.frostserver.model.core.EntitySet;
import de.fraunhofer.iosb.ilt.frostserver.model.core.PkValue;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.JooqPersistenceManager;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.EntityFactories;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.HookPostInsert;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.HookPostUpdate;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.HookPreInsert;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.factories.HookPreUpdate;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.tables.StaTable;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.tables.TableCollection;
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain.NavigationPropertyEntitySet;
import de.fraunhofer.iosb.ilt.frostserver.service.UpdateMode;
import de.fraunhofer.iosb.ilt.frostserver.util.exception.IncompleteEntityException;
import de.fraunhofer.iosb.ilt.frostserver.util.exception.NoSuchEntityException;
import java.util.Map;
import net.time4j.Moment;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.TableField;
import org.jooq.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* @author hylke
*/
public class HookPrePostInsertUpdateLocation implements HookPreInsert, HookPostInsert, HookPreUpdate, HookPostUpdate {

private static final Logger LOGGER = LoggerFactory.getLogger(HookPrePostInsertUpdateLocation.class);

@ConfigurableField(editor = EditorString.class,
label = "Things Locations LinkTable")
@EditorString.EdOptsString(dflt = "THINGS_LOCATIONS")
private String ttlName = "THINGS_LOCATIONS";

@ConfigurableField(editor = EditorString.class,
label = "LinkTable ThingId")
@EditorString.EdOptsString(dflt = "THING_ID")
private String ttlThingIdName = "THING_ID";

@ConfigurableField(editor = EditorString.class,
label = "LinkTable LocationId")
@EditorString.EdOptsString(dflt = "LOCATION_ID")
private String ttlLocationIdName = "LOCATION_ID";

@ConfigurableField(editor = EditorString.class,
label = "Locations-HistLocations LinkTable")
@EditorString.EdOptsString(dflt = "LOCATIONS_HIST_LOCATIONS")
private String tlhlName = "LOCATIONS_HIST_LOCATIONS";

@ConfigurableField(editor = EditorString.class,
label = "LinkTable HistLocationId")
@EditorString.EdOptsString(dflt = "HIST_LOCATION_ID")
private String tlhlHistLocationIdName = "HIST_LOCATION_ID";

@ConfigurableField(editor = EditorString.class,
label = "Time Field")
@EditorString.EdOptsString(dflt = "TIME")
private String thlTimeName = "TIME";

@Override
public boolean preInsertIntoDatabase(Phase fase, JooqPersistenceManager pm, Entity entity, Map<Field, Object> insertFields) throws NoSuchEntityException, IncompleteEntityException {
unlinkExitingLocationsFromLinkedThings(pm, entity);
return true;
}

@Override
public boolean postInsertIntoDatabase(JooqPersistenceManager pm, Entity entity, Map<Field, Object> insertFields) throws NoSuchEntityException, IncompleteEntityException {
PkValue entityId = entity.getPrimaryKeyValues();
createHistLocationLinkLocations(pm, entity, entityId);
return true;
}

@Override
public void preUpdateInDatabase(JooqPersistenceManager pm, Entity entity, PkValue entityId, UpdateMode updateMode) throws NoSuchEntityException, IncompleteEntityException {
unlinkExitingLocationsFromLinkedThings(pm, entity);
}

@Override
public void postUpdateInDatabase(JooqPersistenceManager pm, Entity entity, PkValue entityId, UpdateMode updateMode) throws NoSuchEntityException, IncompleteEntityException {
createHistLocationLinkLocations(pm, entity, entityId);
}

private void unlinkExitingLocationsFromLinkedThings(JooqPersistenceManager pm, Entity entity) {
TableCollection tables = pm.getTableCollection();
EntityType et = entity.getEntityType();
NavigationPropertyEntitySet npLocationThings = et.getNavigationPropertyEntitySet("Things");
if (npLocationThings == null) {
LOGGER.error("EntityType {} has no navigationPropertySet Things", et);
return;
}
NavigationPropertyEntitySet npLocationHistLocs = et.getNavigationPropertyEntitySet("HistoricalLocations");
if (npLocationHistLocs == null) {
LOGGER.error("EntityType {} has no navigationPropertySet HistoricalLocations", et);
return;
}
EntitySet things = entity.getProperty(npLocationThings);
// Maybe create a Historical Location for things linked to this Location.
if (things != null && !things.isEmpty()) {
for (Entity thing : things) {
Object thingId = thing.getPrimaryKeyValues().get(0);
if (thingId != null) {
DSLContext dslContext = pm.getDslContext();
StaTable<?> ttl = tables.getTableForName(ttlName);
// Unlink old Locations from Thing.
long count = dslContext.delete(ttl).where(((TableField) ttl.field(ttlThingIdName)).eq(thingId)).execute();
LOGGER.debug(EntityFactories.UNLINKED_L_FROM_T, count, thingId);
}
}
}
}

public void createHistLocationLinkLocations(JooqPersistenceManager pm, Entity entity, PkValue entityId) throws DataAccessException {
TableCollection tables = pm.getTableCollection();
EntityType et = entity.getEntityType();
NavigationPropertyEntitySet npLocationThings = et.getNavigationPropertyEntitySet("Things");
if (npLocationThings == null) {
LOGGER.error("EntityType {} has no navigationPropertySet Things", et);
return;
}
NavigationPropertyEntitySet npLocationHistLocs = et.getNavigationPropertyEntitySet("HistoricalLocations");
if (npLocationHistLocs == null) {
LOGGER.error("EntityType {} has no navigationPropertySet HistoricalLocations", et);
return;
}
EntityType etHistLoc = npLocationHistLocs.getEntityType();
EntitySet things = entity.getProperty(npLocationThings);
DSLContext dslContext = pm.getDslContext();
Object locationId = entityId.get(0);
// Maybe create a Historical Location for things linked to this Location.
if (things != null && !things.isEmpty()) {
final ModelRegistry modelRegistry = pm.getCoreSettings().getModelRegistry();
for (Entity thing : things) {
Object thingId = thing.getPrimaryKeyValues().get(0);

// Create HistoricalLocation for Thing
TableImpHistLocations qhl = tables.getTableForClass(TableImpHistLocations.class);
Object histLocationId = dslContext.insertInto(qhl)
.set((TableField) qhl.getThingId(), thingId)
.set(qhl.time, Moment.nowInSystemTime())
.returningResult(qhl.getId())
.fetchOne(0);
LOGGER.debug(EntityFactories.CREATED_HL, histLocationId);

// Link Location to HistoricalLocation.
TableImpLocationsHistLocations qlhl = tables.getTableForClass(TableImpLocationsHistLocations.class);
dslContext.insertInto(qlhl)
.set((TableField) qlhl.getHistLocationId(), histLocationId)
.set(qlhl.getLocationId(), locationId)
.execute();
LOGGER.debug(EntityFactories.LINKED_L_TO_HL, locationId, histLocationId);

// Send a message about the creation of a new HL
Entity newHl = pm.get(etHistLoc, PkValue.of(histLocationId));
newHl.setQuery(modelRegistry.getMessageQueryGenerator().getQueryFor(newHl.getEntityType()));
pm.getEntityChangedMessages().add(
new EntityChangedMessage()
.setEventType(EntityChangedMessage.Type.CREATE)
.setEntity(newHl));

}
}
}

}

0 comments on commit 3f6706a

Please sign in to comment.