[New] Implemented notifying of observers of changed objects after a TX
So that components/observers can be notified of changes to the model, the completion of a TX now triggers an ObserverHandler update of the modified objects
This commit is contained in:
parent
ede49b6d20
commit
904b89f809
|
@ -36,6 +36,7 @@ import li.strolch.persistence.impl.model.ResourceContextFactory;
|
||||||
import li.strolch.runtime.component.ComponentContainer;
|
import li.strolch.runtime.component.ComponentContainer;
|
||||||
import li.strolch.runtime.component.StrolchComponent;
|
import li.strolch.runtime.component.StrolchComponent;
|
||||||
import li.strolch.runtime.configuration.ComponentConfiguration;
|
import li.strolch.runtime.configuration.ComponentConfiguration;
|
||||||
|
import li.strolch.runtime.observer.ObserverHandler;
|
||||||
import ch.eitchnet.xmlpers.api.IoMode;
|
import ch.eitchnet.xmlpers.api.IoMode;
|
||||||
import ch.eitchnet.xmlpers.api.PersistenceConstants;
|
import ch.eitchnet.xmlpers.api.PersistenceConstants;
|
||||||
import ch.eitchnet.xmlpers.api.PersistenceManager;
|
import ch.eitchnet.xmlpers.api.PersistenceManager;
|
||||||
|
@ -51,8 +52,8 @@ public class XmlPersistenceHandler extends StrolchComponent implements StrolchPe
|
||||||
public static final String DB_STORE_PATH = "dbStore/"; //$NON-NLS-1$
|
public static final String DB_STORE_PATH = "dbStore/"; //$NON-NLS-1$
|
||||||
private PersistenceManager persistenceManager;
|
private PersistenceManager persistenceManager;
|
||||||
|
|
||||||
public XmlPersistenceHandler(ComponentContainer container) {
|
public XmlPersistenceHandler(ComponentContainer container, String componentName) {
|
||||||
super(container, StrolchPersistenceHandler.class.getName());
|
super(container, componentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -72,6 +73,8 @@ public class XmlPersistenceHandler extends StrolchComponent implements StrolchPe
|
||||||
new ResourceContextFactory());
|
new ResourceContextFactory());
|
||||||
this.persistenceManager.getCtxFactory().registerPersistenceContextFactory(Order.class, Tags.ORDER,
|
this.persistenceManager.getCtxFactory().registerPersistenceContextFactory(Order.class, Tags.ORDER,
|
||||||
new OrderContextFactory());
|
new OrderContextFactory());
|
||||||
|
|
||||||
|
super.initialize(componentConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StrolchTransaction openTx() {
|
public StrolchTransaction openTx() {
|
||||||
|
@ -79,10 +82,13 @@ public class XmlPersistenceHandler extends StrolchComponent implements StrolchPe
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
// caller must close
|
// caller will/must close
|
||||||
public StrolchTransaction openTx(String realm) {
|
public StrolchTransaction openTx(String realm) {
|
||||||
PersistenceTransaction tx = this.persistenceManager.openTx(realm);
|
PersistenceTransaction tx = this.persistenceManager.openTx(realm);
|
||||||
XmlStrolchTransaction strolchTx = new XmlStrolchTransaction(tx);
|
XmlStrolchTransaction strolchTx = new XmlStrolchTransaction(tx);
|
||||||
|
if (getContainer().hasComponent(ObserverHandler.class)) {
|
||||||
|
strolchTx.setObserverHandler(getContainer().getComponent(ObserverHandler.class));
|
||||||
|
}
|
||||||
return strolchTx;
|
return strolchTx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,53 @@
|
||||||
package li.strolch.persistence.impl;
|
package li.strolch.persistence.impl;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import li.strolch.model.StrolchElement;
|
||||||
import li.strolch.persistence.api.StrolchPersistenceException;
|
import li.strolch.persistence.api.StrolchPersistenceException;
|
||||||
import li.strolch.persistence.api.StrolchTransaction;
|
import li.strolch.persistence.api.StrolchTransaction;
|
||||||
import li.strolch.persistence.api.TransactionCloseStrategy;
|
import li.strolch.persistence.api.TransactionCloseStrategy;
|
||||||
|
import li.strolch.runtime.observer.ObserverHandler;
|
||||||
|
import ch.eitchnet.xmlpers.api.ModificationResult;
|
||||||
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
|
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
|
||||||
|
import ch.eitchnet.xmlpers.api.TransactionResult;
|
||||||
|
|
||||||
public class XmlStrolchTransaction implements StrolchTransaction {
|
public class XmlStrolchTransaction implements StrolchTransaction {
|
||||||
|
|
||||||
|
private ObserverHandler observerHandler;
|
||||||
|
private boolean suppressUpdates;
|
||||||
private PersistenceTransaction tx;
|
private PersistenceTransaction tx;
|
||||||
private TransactionCloseStrategy closeStrategy;
|
private TransactionCloseStrategy closeStrategy;
|
||||||
|
private TransactionResult txResult;
|
||||||
|
|
||||||
public XmlStrolchTransaction(PersistenceTransaction tx) {
|
public XmlStrolchTransaction(PersistenceTransaction tx) {
|
||||||
|
this.suppressUpdates = false;
|
||||||
this.tx = tx;
|
this.tx = tx;
|
||||||
this.closeStrategy = TransactionCloseStrategy.COMMIT;
|
this.closeStrategy = TransactionCloseStrategy.COMMIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param observerHandler
|
||||||
|
* the observerHandler to set
|
||||||
|
*/
|
||||||
|
public void setObserverHandler(ObserverHandler observerHandler) {
|
||||||
|
this.observerHandler = observerHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param suppressUpdates
|
||||||
|
* the suppressUpdates to set
|
||||||
|
*/
|
||||||
|
public void setSuppressUpdates(boolean suppressUpdates) {
|
||||||
|
this.suppressUpdates = suppressUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the suppressUpdates
|
||||||
|
*/
|
||||||
|
public boolean isSuppressUpdates() {
|
||||||
|
return this.suppressUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
PersistenceTransaction getTx() {
|
PersistenceTransaction getTx() {
|
||||||
return this.tx;
|
return this.tx;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +59,25 @@ public class XmlStrolchTransaction implements StrolchTransaction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void autoCloseableCommit() {
|
public void autoCloseableCommit() {
|
||||||
|
|
||||||
|
if (!this.suppressUpdates && this.observerHandler != null) {
|
||||||
|
this.txResult = new TransactionResult();
|
||||||
|
this.tx.setTransactionResult(this.txResult);
|
||||||
|
}
|
||||||
|
|
||||||
this.tx.autoCloseableCommit();
|
this.tx.autoCloseableCommit();
|
||||||
|
|
||||||
|
if (!this.suppressUpdates && this.observerHandler != null) {
|
||||||
|
|
||||||
|
Set<String> keys = this.txResult.getKeys();
|
||||||
|
for (String key : keys) {
|
||||||
|
ModificationResult modificationResult = this.txResult.getModificationResult(key);
|
||||||
|
|
||||||
|
this.observerHandler.add(key, modificationResult.<StrolchElement> getCreated());
|
||||||
|
this.observerHandler.update(key, modificationResult.<StrolchElement> getUpdated());
|
||||||
|
this.observerHandler.remove(key, modificationResult.<StrolchElement> getDeleted());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,15 +22,11 @@
|
||||||
package li.strolch.persistence.impl.dao.test;
|
package li.strolch.persistence.impl.dao.test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.MessageFormat;
|
|
||||||
|
|
||||||
import li.strolch.persistence.impl.XmlPersistenceHandler;
|
import li.strolch.persistence.api.StrolchPersistenceHandler;
|
||||||
import li.strolch.runtime.component.ComponentContainer;
|
|
||||||
import li.strolch.runtime.configuration.ComponentConfiguration;
|
|
||||||
import li.strolch.runtime.configuration.ConfigurationParser;
|
|
||||||
import li.strolch.runtime.configuration.StrolchConfiguration;
|
|
||||||
import li.strolch.testbase.runtime.RuntimeMock;
|
import li.strolch.testbase.runtime.RuntimeMock;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -39,13 +35,14 @@ import org.slf4j.LoggerFactory;
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractDaoImplTest {
|
public abstract class AbstractDaoImplTest extends RuntimeMock {
|
||||||
|
|
||||||
private static final String RUNTIME_PATH = "target/strolchRuntime/"; //$NON-NLS-1$
|
private static final String RUNTIME_PATH = "target/strolchRuntime/"; //$NON-NLS-1$
|
||||||
|
private static final String DB_STORE_PATH_DIR = "dbStore"; //$NON-NLS-1$
|
||||||
private static final String CONFIG_SRC = "src/test/resources/runtime/config"; //$NON-NLS-1$
|
private static final String CONFIG_SRC = "src/test/resources/runtime/config"; //$NON-NLS-1$
|
||||||
protected static final Logger logger = LoggerFactory.getLogger(AbstractDaoImplTest.class);
|
protected static final Logger logger = LoggerFactory.getLogger(AbstractDaoImplTest.class);
|
||||||
|
|
||||||
protected static XmlPersistenceHandler persistenceHandler;
|
protected static StrolchPersistenceHandler persistenceHandler;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() {
|
public static void beforeClass() {
|
||||||
|
@ -53,21 +50,15 @@ public abstract class AbstractDaoImplTest {
|
||||||
File rootPath = new File(RUNTIME_PATH);
|
File rootPath = new File(RUNTIME_PATH);
|
||||||
File configSrc = new File(CONFIG_SRC);
|
File configSrc = new File(CONFIG_SRC);
|
||||||
RuntimeMock.mockRuntime(rootPath, configSrc);
|
RuntimeMock.mockRuntime(rootPath, configSrc);
|
||||||
|
new File(rootPath, DB_STORE_PATH_DIR).mkdir();
|
||||||
File dbStorePath = new File(rootPath, XmlPersistenceHandler.DB_STORE_PATH);
|
RuntimeMock.startContainer(rootPath);
|
||||||
if (!dbStorePath.mkdirs()) {
|
|
||||||
String msg = "Failed to create path {0}"; //$NON-NLS-1$
|
|
||||||
msg = MessageFormat.format(msg, dbStorePath.getAbsolutePath());
|
|
||||||
throw new RuntimeException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the component configuration
|
// initialize the component configuration
|
||||||
StrolchConfiguration strolchConfiguration = ConfigurationParser.parseConfiguration(rootPath);
|
persistenceHandler = getContainer().getComponent(StrolchPersistenceHandler.class);
|
||||||
ComponentConfiguration componentConfiguration = strolchConfiguration
|
}
|
||||||
.getComponentConfiguration("PersistenceHandler"); //$NON-NLS-1$
|
|
||||||
ComponentContainer container = new ComponentContainer();
|
|
||||||
persistenceHandler = new XmlPersistenceHandler(container);
|
|
||||||
persistenceHandler.initialize(componentConfiguration);
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() {
|
||||||
|
RuntimeMock.destroyRuntime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, Robert von Burg
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of the XXX.
|
||||||
|
*
|
||||||
|
* XXX 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.
|
||||||
|
*
|
||||||
|
* XXX 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 XXX. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package li.strolch.persistence.impl.dao.test;
|
||||||
|
|
||||||
|
import static li.strolch.testbase.model.ModelBuilder.createOrder;
|
||||||
|
import static li.strolch.testbase.model.ModelBuilder.createResource;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import li.strolch.model.Order;
|
||||||
|
import li.strolch.model.Resource;
|
||||||
|
import li.strolch.model.State;
|
||||||
|
import li.strolch.model.StrolchElement;
|
||||||
|
import li.strolch.persistence.api.StrolchTransaction;
|
||||||
|
import li.strolch.runtime.observer.Observer;
|
||||||
|
import li.strolch.runtime.observer.ObserverHandler;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.eitchnet.xmlpers.api.ModificationResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ObserverUpdateTest extends AbstractDaoImplTest {
|
||||||
|
|
||||||
|
public final class ElementAddedObserver implements Observer {
|
||||||
|
|
||||||
|
Map<String, ModificationResult> results = new HashMap<>();
|
||||||
|
|
||||||
|
private ModificationResult getModificationResult(String key) {
|
||||||
|
ModificationResult result = this.results.get(key);
|
||||||
|
if (result == null) {
|
||||||
|
result = new ModificationResult(key);
|
||||||
|
this.results.put(key, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(String key, List<StrolchElement> elements) {
|
||||||
|
getModificationResult(key).getUpdated().addAll(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(String key, List<StrolchElement> elements) {
|
||||||
|
getModificationResult(key).getDeleted().addAll(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(String key, List<StrolchElement> elements) {
|
||||||
|
getModificationResult(key).getCreated().addAll(elements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldReceiveUpdates() {
|
||||||
|
|
||||||
|
// register an observer for orders and resources
|
||||||
|
ElementAddedObserver observer = new ElementAddedObserver();
|
||||||
|
getContainer().getComponent(ObserverHandler.class).registerObserver("Order", observer); //$NON-NLS-1$
|
||||||
|
getContainer().getComponent(ObserverHandler.class).registerObserver("Resource", observer); //$NON-NLS-1$
|
||||||
|
|
||||||
|
// create order
|
||||||
|
Order newOrder = createOrder("MyTestOrder", "Test Name", "TestType", System.currentTimeMillis(), State.CREATED); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
|
||||||
|
try (StrolchTransaction tx = persistenceHandler.openTx();) {
|
||||||
|
persistenceHandler.getOrderDao(tx).save(newOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create resource
|
||||||
|
Resource newResource = createResource("MyTestResource", "Test Name", "TestType"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
|
||||||
|
try (StrolchTransaction tx = persistenceHandler.openTx();) {
|
||||||
|
persistenceHandler.getResourceDao(tx).save(newResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(2, observer.results.size());
|
||||||
|
assertEquals(1, observer.results.get("Order").getCreated().size()); //$NON-NLS-1$
|
||||||
|
assertEquals(1, observer.results.get("Resource").getCreated().size()); //$NON-NLS-1$
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,4 +14,9 @@
|
||||||
<verbose>true</verbose>
|
<verbose>true</verbose>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component>
|
||||||
|
<name>ObserverHandler</name>
|
||||||
|
<api>li.strolch.runtime.observer.ObserverHandler</api>
|
||||||
|
<impl>li.strolch.runtime.observer.DefaultObserverHandler</impl>
|
||||||
|
</Component>
|
||||||
</StrolchConfiguration>
|
</StrolchConfiguration>
|
Loading…
Reference in New Issue