[Major] Implemented opt-in versioning

Now all root elements have a version, and if the realm has versioning
enabled, then actions through the ElementMap lead to new versions being
created. There are also methods to revert/undo changes to an object.

Some tests are still failing, this will be fixed later
This commit is contained in:
Robert von Burg 2016-08-05 20:24:23 +02:00
parent 219eb26182
commit 9dc09515e9
94 changed files with 3634 additions and 606 deletions

View File

@ -23,33 +23,106 @@ import li.strolch.model.StrolchRootElement;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.parameter.StringListParameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.persistence.api.StrolchDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.runtime.StrolchConstants;
/**
* <p>
* The {@link ElementMap} is the entry point to the Strolch model. Any access to Strolch objects is done using the
* {@link ElementMap}. A concrete {@link ElementMap} instance uses the given {@link StrolchTransaction} to access the a
* {@link StrolchDao} and perform the relevant operation.
* </p>
*
* <p>
* <b>Note:</b>
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*
* @param <T>
* the object instance being managed
*/
public interface ElementMap<T extends StrolchRootElement> {
/**
* Returns true if the underlying persistence layer has elements with the given type
*
* @param tx
* the open {@link StrolchTransaction}
* @param type
* the type of element to check for
*
* @return true if the underlying persistence layer has elements with the given type
*/
public boolean hasType(StrolchTransaction tx, String type);
/**
* Returns true if the underlying persistence layer has an element with the given type and ID
*
* @param tx
* the open {@link StrolchTransaction}
* @param type
* the type of element to check for
* @param id
* the ID of the element to check for
*
* @return true if the underlying persistence layer has an element with the given type and ID
*/
public boolean hasElement(StrolchTransaction tx, String type, String id);
/**
* Returns the number of elements regardless of type in the underlying persistence layer
*
* @param tx
* the open {@link StrolchTransaction}
*
* @return the number of elements regardless of type in the underlying persistence layer
*/
public long querySize(StrolchTransaction tx);
/**
* Returns the number of elements of the given type in the underlying persistence layer
*
* @param tx
* the open {@link StrolchTransaction}
* @param type
* the type of element for which the number of elements is to be queried
*
* @return the number of elements of the given type in the underlying persistence layer
*/
public long querySize(StrolchTransaction tx, String type);
/**
* Returns the element with the type "Template" and the id = type
* Returns a copy of the element with the type "Template" and the id = type
*
* @param tx
* the open {@link StrolchTransaction}
* @param type
* The template id to return
*
* @return the template, or null if it does not exist
*/
public T getTemplate(StrolchTransaction tx, String type);
/**
* Returns a copy of the element with the type "Template" and the id = type
*
* @param tx
* the open {@link StrolchTransaction}
* @param type
* The template id to return
* @param assertExists
* if true, and element does not exist, then a {@link StrolchException} is thrown
*
* @return the template, or null if it does not exist
*
* @throws StrolchException
* if the template does not exist
*/
public T getTemplate(StrolchTransaction tx, String type, boolean assertExists) throws StrolchException;
/**
* Retrieves the element with the given type and id, or null if it does not exist
*
@ -64,6 +137,63 @@ public interface ElementMap<T extends StrolchRootElement> {
*/
public T getBy(StrolchTransaction tx, String type, String id);
/**
* Retrieves the element with the given type and id, or null if it does not exist
*
* @param tx
* the open transaction
* @param type
* the type of the element to retrieve
* @param id
* the id of the element to retrieve
* @param assertExists
* if true, and element does not exist, then a {@link StrolchException} is thrown
*
* @return the element with the type and id, or null if it does not exist
*
* @throws StrolchException
* if the element does not exist
*/
public T getBy(StrolchTransaction tx, String type, String id, boolean assertExists) throws StrolchException;
/**
* Retrieves the specific version of the element with the given type and id, or null if it does not exist
*
* @param tx
* the open transaction
* @param type
* the type of the element to retrieve
* @param id
* the id of the element to retrieve
* @param version
* the version to get
*
* @return the element with the type and id, or null if it does not exist
*/
public T getBy(StrolchTransaction tx, String type, String id, int version);
/**
* Retrieves the specific version of the element with the given type and id, or null if it does not exist
*
* @param tx
* the open transaction
* @param type
* the type of the element to retrieve
* @param id
* the id of the element to retrieve
* @param version
* the version to get
* @param assertExists
* if true, and element does not exist, then a {@link StrolchException} is thrown
*
* @return the element with the type and id, or null if it does not exist
*
* @throws StrolchException
* if the element does not exist
*/
public T getBy(StrolchTransaction tx, String type, String id, int version, boolean assertExists)
throws StrolchException;
/**
* Returns the element which is referenced by the given {@link StringParameter}. A reference {@link Parameter} must
* have its interpretation set to the element type being referenced e.g. s
@ -105,29 +235,264 @@ public interface ElementMap<T extends StrolchRootElement> {
*/
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists) throws StrolchException;
/**
* Queries and returns all the versions of the element with the given type and ID
*
* @param tx
* the {@link StrolchTransaction} instance
* @param type
* the type of the element to be queried
* @param id
* the id of the element to be queried
*
* @return all the versions of the element with the given type and ID
*/
public List<T> getVersionsFor(StrolchTransaction tx, String type, String id);
/**
* Returns all elements in the underlying persistence layer regardless of type
*
* @param tx
* the {@link StrolchTransaction} instance
*
* @return all elements in the underlying persistence layer regardless of type
*/
public List<T> getAllElements(StrolchTransaction tx);
/**
* Returns all elements in the underlying persistence layer of the given type
*
* @param tx
* the {@link StrolchTransaction} instance
*
* @param type
* the type of the elements to retrieve
*
* @return all elements in the underlying persistence layer of the given type
*/
public List<T> getElementsBy(StrolchTransaction tx, String type);
/**
* Returns all the types known in the underlying persistence layer
*
* @param tx
* the {@link StrolchTransaction} instance
*
* @return all the types known in the underlying persistence layer
*/
public Set<String> getTypes(StrolchTransaction tx);
/**
* Returns all keys/IDs of all elements in the underlying persistence layer, regardless of type
*
* @param tx
* the {@link StrolchTransaction} instance
*
* @return all keys/IDs of all elements in the underlying persistence layer, regardless of type
*/
public Set<String> getAllKeys(StrolchTransaction tx);
/**
* Returns all keys/IDs of all elements in the underlying persistence layer, of the given type
*
* @param tx
* the {@link StrolchTransaction} instance
* @param type
* the type of the element to retrieve the keys for
*
* @return all keys/IDs of all elements in the underlying persistence layer, of the given type
*/
public Set<String> getKeysBy(StrolchTransaction tx, String type);
public void add(StrolchTransaction tx, T element);
/**
* Adds the given element to the underlying persistence layer. The element may not already exist
*
* @param tx
* the {@link StrolchTransaction} instance
* @param element
* the element to add
*
* @throws StrolchPersistenceException
* if an element already exists with the same ID
*/
public void add(StrolchTransaction tx, T element) throws StrolchPersistenceException;
public void addAll(StrolchTransaction tx, List<T> elements);
/**
* Adds the given elements to the underlying persistence layer. None of the elements may already exist
*
* @param tx
* the {@link StrolchTransaction} instance
* @param elements
* the elements to add
*
* @throws StrolchPersistenceException
* if an element already exists with the same ID
*/
public void addAll(StrolchTransaction tx, List<T> elements) throws StrolchPersistenceException;
public T update(StrolchTransaction tx, T element);
/**
* Updates the existing element
*
* @param tx
* the {@link StrolchTransaction} instance
* @param element
* the element to update
*
* @return the replaced element
*
* @throws StrolchPersistenceException
* if the element does not exist
*/
public void update(StrolchTransaction tx, T element) throws StrolchPersistenceException;
public List<T> updateAll(StrolchTransaction tx, List<T> elements);
/**
* Updates all the existing elements
*
* @param tx
* the {@link StrolchTransaction} instance
* @param elements
* the elements to update
*
* @return the replaced elements
*
* @throws StrolchPersistenceException
* if any of the elements don't yet exist
*/
public void updateAll(StrolchTransaction tx, List<T> elements) throws StrolchPersistenceException;
public void remove(StrolchTransaction tx, T element);
/**
* Removes the given element
*
* @param tx
* the {@link StrolchTransaction} instance
* @param element
* the element to be removed
*
* @throws StrolchPersistenceException
* if the element does not exist
*/
public void remove(StrolchTransaction tx, T element) throws StrolchPersistenceException;
public void removeAll(StrolchTransaction tx, List<T> elements);
/**
* Removes the given elements
*
* @param tx
* the {@link StrolchTransaction} instance
* @param elements
* the elements to be removed
*
* @throws StrolchPersistenceException
* if any of the elements don't yet exist
*/
public void removeAll(StrolchTransaction tx, List<T> elements) throws StrolchPersistenceException;
/**
* <p>
* Removes all elements regardless of the type
* </p>
*
* <p>
* <b>Note:</b> This method ignores versioning. Do NOT call this method unless you want to clear the model!
* </p>
*
* @param tx
* the {@link StrolchTransaction} instance
*
* @return the number of elements removed
*/
public long removeAll(StrolchTransaction tx);
/**
* <p>
* Removes all elements of the given type
* </p>
*
* <p>
* <b>Note:</b> This method ignores versioning. Do NOT call this method unless you want to clear the model!
* </p>
*
* @param tx
* the {@link StrolchTransaction} instance
* @param type
* the type of elements to remove
*
* @return the number of elements removed
*/
public long removeAllBy(StrolchTransaction tx, String type);
/**
* <p>
* Undoes the given version by reverting to the previous version of the element given. If the element given is the
* first version, then the element is removed. If the previous version exists, then it is reverted to it. If the
* given element does not exist, nor it's previous version, then a {@link StrolchPersistenceException} is thrown
* </p>
*
* <p>
* <b>Note:</b> This method should only be used in the same transaction where the element was created to undo a
* change in the same transaction. If there is a requirement to revert to a previous version, then the
* {@link #revertToVersion(StrolchTransaction, StrolchRootElement)} method.
* </p>
*
* @param tx
* the {@link StrolchTransaction} instance
* @param element
* the element which is to be reverted to a previous version
*
* @throws StrolchException
* if the version does not exist, if this version is not the latest version, if the previous version is
* missing or other problems arise
*/
public void undoVersion(StrolchTransaction tx, T element) throws StrolchException;
/**
* <p>
* Reverts to the given version of the specified element. This method will retrieve the given version of the
* specified element, then set a new version on that element which is an increment of the current latest version and
* store this new version. The new element is then returned for convenience.
* </p>
*
* <p>
* <b>Note:</b> This is the method which should be called when a change should be reverted. This method gurantees
* that the history is not changed and that a new version is saved but with the version of the element specified.
* </p>
*
* @param tx
* the {@link StrolchTransaction} instance
* @param element
* the version of the element to revert to
*
* @return the new version of the element
*
* @throws StrolchException
* if the version does not exist
*/
public T revertToVersion(StrolchTransaction tx, T element) throws StrolchException;
/**
* <p>
* Reverts to the given version of the specified element. This method will retrieve the given version of the
* specified element, then set a new version on that element which is an increment of the current latest version and
* store this new version. The new element is then returned for convenience.
* </p>
*
* <p>
* <b>Note:</b> This is the method which should be called when a change should be reverted. This method gurantees
* that the history is not changed and that a new version is saved but with the version of the element specified.
* </p>
*
* @param tx
* the {@link StrolchTransaction} instance
* @param type
* the type of the element to revert to
* @param id
* the id of the element to revert to
* @param version
* the version of the specified element to revert to
*
* @return the new version of the element
*
* @throws StrolchException
* if the version does not exist
*/
public T revertToVersion(StrolchTransaction tx, String type, String id, int version) throws StrolchException;
}

View File

@ -45,5 +45,7 @@ public interface StrolchRealm {
public boolean isUpdateObservers();
public boolean isVersioningEnabled();
public ObserverHandler getObserverHandler();
}

View File

@ -137,7 +137,15 @@ public class AuditingElementMapFacade<T extends StrolchRootElement> implements E
@Override
public T getTemplate(StrolchTransaction tx, String type) {
T template = this.elementMap.getTemplate(tx, type);
if (this.observeAccessReads)
if (this.observeAccessReads && template != null)
this.read.add(template);
return template;
}
@Override
public T getTemplate(StrolchTransaction tx, String type, boolean assertExists) throws StrolchException {
T template = this.elementMap.getTemplate(tx, type, assertExists);
if (this.observeAccessReads && template != null)
this.read.add(template);
return template;
}
@ -145,7 +153,32 @@ public class AuditingElementMapFacade<T extends StrolchRootElement> implements E
@Override
public T getBy(StrolchTransaction tx, String type, String id) {
T element = this.elementMap.getBy(tx, type, id);
if (this.observeAccessReads)
if (this.observeAccessReads && element != null)
this.read.add(element);
return element;
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, boolean assertExists) throws StrolchException {
T element = this.elementMap.getBy(tx, type, id, assertExists);
if (this.observeAccessReads && element != null)
this.read.add(element);
return element;
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, int version) {
T element = this.elementMap.getBy(tx, type, id, version);
if (this.observeAccessReads && element != null)
this.read.add(element);
return element;
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, int version, boolean assertExists)
throws StrolchException {
T element = this.elementMap.getBy(tx, type, id, version, assertExists);
if (this.observeAccessReads && element != null)
this.read.add(element);
return element;
}
@ -153,23 +186,32 @@ public class AuditingElementMapFacade<T extends StrolchRootElement> implements E
@Override
public T getBy(StrolchTransaction tx, StringParameter refP, boolean assertExists) throws StrolchException {
T element = this.elementMap.getBy(tx, refP, assertExists);
if (this.observeAccessReads)
if (this.observeAccessReads && element != null)
this.read.add(element);
return element;
}
@Override
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists) throws StrolchException {
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists)
throws StrolchException {
List<T> elements = this.elementMap.getBy(tx, refP, assertExists);
if (this.observeAccessReads)
if (this.observeAccessReads && !elements.isEmpty())
this.read.addAll(elements);
return elements;
}
@Override
public List<T> getVersionsFor(StrolchTransaction tx, String type, String id) {
List<T> versions = this.elementMap.getVersionsFor(tx, type, id);
if (this.observeAccessReads && !versions.isEmpty())
this.read.add(versions.get(versions.size() - 1));
return versions;
}
@Override
public List<T> getAllElements(StrolchTransaction tx) {
List<T> elements = this.elementMap.getAllElements(tx);
if (this.observeAccessReads)
if (this.observeAccessReads && !elements.isEmpty())
this.read.addAll(elements);
return elements;
}
@ -177,7 +219,7 @@ public class AuditingElementMapFacade<T extends StrolchRootElement> implements E
@Override
public List<T> getElementsBy(StrolchTransaction tx, String type) {
List<T> elements = this.elementMap.getElementsBy(tx, type);
if (this.observeAccessReads)
if (this.observeAccessReads && !elements.isEmpty())
this.read.addAll(elements);
return elements;
}
@ -210,18 +252,15 @@ public class AuditingElementMapFacade<T extends StrolchRootElement> implements E
}
@Override
public T update(StrolchTransaction tx, T element) {
T replaced = this.elementMap.update(tx, element);
public void update(StrolchTransaction tx, T element) {
this.elementMap.update(tx, element);
this.updated.add(element);
return replaced;
}
@Override
public List<T> updateAll(StrolchTransaction tx, List<T> elements) {
List<T> replaced = this.elementMap.updateAll(tx, elements);
public void updateAll(StrolchTransaction tx, List<T> elements) {
this.elementMap.updateAll(tx, elements);
this.updated.addAll(elements);
return replaced;
}
@Override
@ -255,4 +294,27 @@ public class AuditingElementMapFacade<T extends StrolchRootElement> implements E
return removed;
}
@Override
public T revertToVersion(StrolchTransaction tx, String type, String id, int version) throws StrolchException {
T element = this.elementMap.revertToVersion(tx, type, id, version);
this.updated.add(element);
return element;
}
@Override
public T revertToVersion(StrolchTransaction tx, T element) throws StrolchException {
T revertedElement = this.elementMap.revertToVersion(tx, element);
this.updated.add(revertedElement);
return revertedElement;
}
@Override
public void undoVersion(StrolchTransaction tx, T element) throws StrolchException {
this.elementMap.undoVersion(tx, element);
if (element.getVersion().isFirstVersion())
this.deleted.add(element);
else
this.updated.add(element);
}
}

View File

@ -20,6 +20,7 @@ import static li.strolch.model.StrolchModelConstants.INTERPRETATION_ACTIVITY_REF
import java.util.List;
import li.strolch.agent.api.ActivityMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.activity.Activity;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.query.ActivityQuery;
@ -31,9 +32,10 @@ public class CachedActivityMap extends CachedElementMap<Activity> implements Act
private ActivityDao cachedDao;
public CachedActivityMap() {
super();
this.cachedDao = new InMemoryActivityDao();
public CachedActivityMap(StrolchRealm realm) {
super(realm);
// the cached DAO should not have versioning enabled
this.cachedDao = new InMemoryActivityDao(false);
}
@Override

View File

@ -16,23 +16,27 @@
package li.strolch.agent.impl;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.agent.api.ElementMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.exception.StrolchException;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.Version;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.parameter.StringListParameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.persistence.api.StrolchDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.runtime.StrolchConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -40,6 +44,16 @@ public abstract class CachedElementMap<T extends StrolchRootElement> implements
private static final Logger logger = LoggerFactory.getLogger(CachedElementMap.class);
private StrolchRealm realm;
public CachedElementMap(StrolchRealm realm) {
this.realm = realm;
}
protected StrolchRealm getRealm() {
return this.realm;
}
protected abstract StrolchDao<T> getCachedDao();
protected abstract StrolchDao<T> getDbDao(StrolchTransaction tx);
@ -66,62 +80,110 @@ public abstract class CachedElementMap<T extends StrolchRootElement> implements
@Override
public synchronized T getTemplate(StrolchTransaction tx, String type) {
return getBy(tx, StrolchConstants.TEMPLATE, type);
return getTemplate(tx, type, false);
}
@Override
public T getTemplate(StrolchTransaction tx, String type, boolean assertExists) {
T t = getCachedDao().queryBy(StrolchConstants.TEMPLATE, type);
if (assertExists && t == null) {
String msg = "The template for type {0} does not exist!"; //$NON-NLS-1$
throw new StrolchException(MessageFormat.format(msg, type));
}
if (t == null)
return null;
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}
@Override
public synchronized T getBy(StrolchTransaction tx, String type, String id) {
return getCachedDao().queryBy(type, id);
return getBy(tx, type, id, false);
}
protected abstract void assertIsRefParam(Parameter<?> refP);
@Override
public T getBy(StrolchTransaction tx, String type, String id, boolean assertExists) throws StrolchException {
T t = getCachedDao().queryBy(type, id);
if (assertExists && t == null) {
String msg = "The element for type {0} and id {1} does not exist!"; //$NON-NLS-1$
throw new StrolchException(MessageFormat.format(msg, type, id));
}
if (t == null)
return null;
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, int version) {
return getBy(tx, type, id, version, false);
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, int version, boolean assertExists)
throws StrolchException {
T t = getDbDao(tx).queryBy(type, id, version);
if (assertExists && t == null) {
String msg = "The element for type {0} and id {1} and version {2} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, type, id, version);
throw new StrolchException(msg);
}
return t;
}
@Override
public T getBy(StrolchTransaction tx, StringParameter refP, boolean assertExists) throws StrolchException {
assertIsRefParam(refP);
String type = refP.getUom();
String id = refP.getValue();
T element = getBy(tx, type, id);
if (assertExists && element == null) {
String msg = "The element for refP {0} with id {1} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, refP.getLocator(), id);
throw new StrolchException(msg);
}
return element;
return getBy(tx, type, id, assertExists);
}
@Override
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists) throws StrolchException {
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists)
throws StrolchException {
assertIsRefParam(refP);
List<T> elements = new ArrayList<>();
String type = refP.getUom();
List<String> ids = refP.getValue();
for (String id : ids) {
T element = getBy(tx, type, id);
if (element != null) {
elements.add(element);
} else if (assertExists) {
if (assertExists && element == null) {
String msg = "The element for refP {0} with id {1} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, refP.getLocator(), id);
throw new StrolchException(msg);
}
}
}
return ids.stream().map(id -> getBy(tx, type, id, assertExists)).filter(Objects::nonNull)
.collect(Collectors.toList());
}
return elements;
@Override
public List<T> getVersionsFor(StrolchTransaction tx, String type, String id) {
return getDbDao(tx).queryVersionsFor(type, id);
}
@Override
public synchronized List<T> getAllElements(StrolchTransaction tx) {
return getCachedDao().queryAll();
List<T> all = getCachedDao().queryAll();
return all.stream().map(t -> {
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}).collect(Collectors.toList());
}
@Override
public synchronized List<T> getElementsBy(StrolchTransaction tx, String type) {
return getCachedDao().queryAll(type);
List<T> all = getCachedDao().queryAll(type);
return all.stream().map(t -> {
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}).collect(Collectors.toList());
}
@Override
@ -139,14 +201,6 @@ public abstract class CachedElementMap<T extends StrolchRootElement> implements
return getCachedDao().queryKeySet(type);
}
@Override
public synchronized void add(StrolchTransaction tx, T element) {
// first perform cached change
getCachedDao().save(element);
// last is to perform DB changes
getDbDao(tx).save(element);
}
/**
* Special method used when starting the container to cache the values. Not to be used anywhere else but from the
* {@link CachedRealm}
@ -158,27 +212,28 @@ public abstract class CachedElementMap<T extends StrolchRootElement> implements
getCachedDao().save(element);
}
// TODO for update we should return the updated elements, or remove the return value
@Override
public synchronized T update(StrolchTransaction tx, T element) {
public synchronized void add(StrolchTransaction tx, T element) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
// first perform cached change
getCachedDao().update(element);
getCachedDao().save(element);
// last is to perform DB changes
getDbDao(tx).update(element);
return element;
}
@Override
public synchronized void remove(StrolchTransaction tx, T element) {
// first perform cached change
getCachedDao().remove(element);
getDbDao(tx).remove(element);
getDbDao(tx).save(element);
}
@Override
public synchronized void addAll(StrolchTransaction tx, List<T> elements) {
for (T element : elements) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
}
// first perform cached change
getCachedDao().saveAll(elements);
// last is to perform DB changes
@ -186,21 +241,79 @@ public abstract class CachedElementMap<T extends StrolchRootElement> implements
}
@Override
public synchronized List<T> updateAll(StrolchTransaction tx, List<T> elements) {
public synchronized void update(StrolchTransaction tx, T element) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
// first perform cached change
getCachedDao().update(element);
// last is to perform DB changes
getDbDao(tx).update(element);
}
@Override
public synchronized void updateAll(StrolchTransaction tx, List<T> elements) {
for (T t : elements) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(t, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(t, tx.getCertificate().getUsername());
}
// first perform cached change
getCachedDao().updateAll(elements);
// last is to perform DB changes
getDbDao(tx).updateAll(elements);
}
return elements;
@Override
public synchronized void remove(StrolchTransaction tx, T element) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), true);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
if (this.realm.isVersioningEnabled()) {
// first perform cached change
getCachedDao().update(element);
// last is to perform DB changes
getDbDao(tx).update(element);
} else {
// first perform cached change
getCachedDao().remove(element);
// last is to perform DB changes
getDbDao(tx).remove(element);
}
}
@Override
public synchronized void removeAll(StrolchTransaction tx, List<T> elements) {
// first perform cached change
getCachedDao().removeAll(elements);
// last is to perform DB changes
getDbDao(tx).removeAll(elements);
for (T t : elements) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(t, tx.getCertificate().getUsername(), true);
else
Version.setInitialVersionFor(t, tx.getCertificate().getUsername());
}
if (this.realm.isVersioningEnabled()) {
// first perform cached change
getCachedDao().updateAll(elements);
// last is to perform DB changes
getDbDao(tx).updateAll(elements);
} else {
// first perform cached change
getCachedDao().removeAll(elements);
// last is to perform DB changes
getDbDao(tx).removeAll(elements);
}
}
@Override
@ -232,4 +345,63 @@ public abstract class CachedElementMap<T extends StrolchRootElement> implements
return removed;
}
@Override
public T revertToVersion(StrolchTransaction tx, T element) throws StrolchException {
return revertToVersion(tx, element.getType(), element.getId(), element.getVersion().getVersion());
}
@Override
public T revertToVersion(StrolchTransaction tx, String type, String id, int version) throws StrolchException {
if (!this.realm.isVersioningEnabled()) {
throw new StrolchPersistenceException("Can not und a version if versioning is not enabled!");
}
// get the current and specified version
T current = getBy(tx, type, id, true);
T versionT = getBy(tx, type, id, version, true);
// create the new version
@SuppressWarnings("unchecked")
T clone = (T) versionT.getClone();
clone.setVersion(current.getVersion().next(tx.getCertificate().getUsername(), false));
// save the new version
getCachedDao().update(clone);
getDbDao(tx).update(clone);
// and return new version
return clone;
}
@Override
public void undoVersion(StrolchTransaction tx, T element) throws StrolchException {
if (!this.realm.isVersioningEnabled()) {
throw new StrolchPersistenceException("Can not und a version if versioning is not enabled!");
}
String type = element.getType();
String id = element.getId();
Version elementVersion = element.getVersion();
// make sure the given element is the latest version
T current = getBy(tx, type, id, true);
if (!current.getVersion().equals(elementVersion)) {
String msg = "Can not undo the version {0} as it is not the latest!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, elementVersion);
throw new StrolchException(msg);
}
if (elementVersion.isFirstVersion()) {
getCachedDao().remove(element);
getDbDao(tx).remove(element);
} else {
T previous = getBy(tx, type, id, elementVersion.getPreviousVersion(), false);
getCachedDao().update(previous);
getDbDao(tx).removeVersion(current);
}
}
protected abstract void assertIsRefParam(Parameter<?> refP);
}

View File

@ -20,6 +20,7 @@ import static li.strolch.model.StrolchModelConstants.INTERPRETATION_ORDER_REF;
import java.util.List;
import li.strolch.agent.api.OrderMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.Order;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.query.OrderQuery;
@ -31,9 +32,10 @@ public class CachedOrderMap extends CachedElementMap<Order> implements OrderMap
private OrderDao cachedDao;
public CachedOrderMap() {
super();
this.cachedDao = new InMemoryOrderDao();
public CachedOrderMap(StrolchRealm realm) {
super(realm);
// the cached DAO should not have versioning enabled
this.cachedDao = new InMemoryOrderDao(false);
}
@Override

View File

@ -95,17 +95,14 @@ public class CachedRealm extends InternalStrolchRealm {
super.initialize(container, configuration);
this.persistenceHandler = container.getComponent(PersistenceHandler.class);
this.resourceMap = new CachedResourceMap();
this.orderMap = new CachedOrderMap();
this.activityMap = new CachedActivityMap();
this.resourceMap = new CachedResourceMap(this);
this.orderMap = new CachedOrderMap(this);
this.activityMap = new CachedActivityMap(this);
if (isAuditTrailEnabled()) {
if (isAuditTrailEnabled())
this.auditTrail = new CachedAuditTrail();
logger.info("Enabling AuditTrail for realm " + getRealm()); //$NON-NLS-1$
} else {
else
this.auditTrail = new NoStrategyAuditTrail();
logger.info("AuditTrail is disabled for realm " + getRealm()); //$NON-NLS-1$
}
}
@Override

View File

@ -20,6 +20,7 @@ import static li.strolch.model.StrolchModelConstants.INTERPRETATION_RESOURCE_REF
import java.util.List;
import li.strolch.agent.api.ResourceMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.Resource;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.query.ResourceQuery;
@ -31,9 +32,10 @@ public class CachedResourceMap extends CachedElementMap<Resource> implements Res
private ResourceDao cachedDao;
public CachedResourceMap() {
super();
this.cachedDao = new InMemoryResourceDao();
public CachedResourceMap(StrolchRealm realm) {
super(realm);
// the cached DAO should not have versioning enabled
this.cachedDao = new InMemoryResourceDao(false);
}
@Override

View File

@ -39,6 +39,7 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle
public static final String PROP_ENABLE_AUDIT_TRAIL = "enableAuditTrail"; //$NON-NLS-1$
public static final String PROP_ENABLE_AUDIT_TRAIL_FOR_READ = "enableAuditTrailForRead"; //$NON-NLS-1$
public static final String PROP_ENABLE_OBSERVER_UPDATES = "enableObserverUpdates"; //$NON-NLS-1$
public static final String PROP_ENABLE_VERSIONING = "enableVersioning"; //$NON-NLS-1$
public static final String PREFIX_DATA_STORE_MODE = "dataStoreMode"; //$NON-NLS-1$
public static final String PREFIX_DATA_STORE_FILE = "dataStoreFile"; //$NON-NLS-1$
public static final String PROP_REALMS = "realms"; //$NON-NLS-1$
@ -76,9 +77,11 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle
this.realms = new HashMap<>();
String[] realms = configuration.getStringArray(PROP_REALMS, StrolchConstants.DEFAULT_REALM);
for (String realmName : realms) {
String dataStoreModeKey = StrolchConstants.makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
String realmMode = configuration.getString(dataStoreModeKey, null);
DataStoreMode dataStoreMode = DataStoreMode.parseDataStoreMode(realmMode);
InternalStrolchRealm realm = dataStoreMode.createRealm(realmName);
this.realms.put(realmName, realm);
}

View File

@ -27,7 +27,6 @@ import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.persistence.inmemory.InMemoryPersistence;
import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.utils.dbc.DBC;
@ -86,18 +85,15 @@ public class EmptyRealm extends InternalStrolchRealm {
@Override
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
super.initialize(container, configuration);
this.persistenceHandler = new InMemoryPersistence(container.getPrivilegeHandler());
this.resourceMap = new TransactionalResourceMap();
this.orderMap = new TransactionalOrderMap();
this.persistenceHandler = new InMemoryPersistence(container.getPrivilegeHandler(), isVersioningEnabled());
this.resourceMap = new TransactionalResourceMap(this);
this.orderMap = new TransactionalOrderMap(this);
this.activityMap = new TransactionalActivityMap(this);
String enableAuditKey = StrolchConstants.makeRealmKey(getRealm(), DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL);
if (configuration.getBoolean(enableAuditKey, Boolean.FALSE)) {
if (isAuditTrailEnabled())
this.auditTrail = new TransactionalAuditTrail();
logger.info("Enabling AuditTrail for realm " + getRealm()); //$NON-NLS-1$
} else {
else
this.auditTrail = new NoStrategyAuditTrail();
logger.info("AuditTrail is disabled for realm " + getRealm()); //$NON-NLS-1$
}
}
@Override

View File

@ -15,9 +15,17 @@
*/
package li.strolch.agent.impl;
import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL;
import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL_FOR_READ;
import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_OBSERVER_UPDATES;
import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_VERSIONING;
import java.text.MessageFormat;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.agent.api.ActivityMap;
import li.strolch.agent.api.AuditTrail;
import li.strolch.agent.api.ComponentContainer;
@ -32,9 +40,6 @@ import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.utils.dbc.DBC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -47,6 +52,7 @@ public abstract class InternalStrolchRealm implements StrolchRealm {
private LockHandler lockHandler;
private boolean auditTrailEnabled;
private boolean auditTrailEnabledForRead;
private boolean versioningEnabled;
private boolean updateObservers;
private ObserverHandler observerHandler;
@ -78,26 +84,51 @@ public abstract class InternalStrolchRealm implements StrolchRealm {
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
String propTryLockTimeUnit = StrolchConstants.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME_UNIT);
String propTryLockTime = StrolchConstants.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME);
logger.info("Initializing Realm " + getRealm() + "...");
String enableAuditKey = StrolchConstants.makeRealmKey(getRealm(), DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL);
// audits
String enableAuditKey = StrolchConstants.makeRealmKey(getRealm(), PROP_ENABLE_AUDIT_TRAIL);
this.auditTrailEnabled = configuration.getBoolean(enableAuditKey, Boolean.FALSE);
String enableAuditForReadKey = StrolchConstants.makeRealmKey(getRealm(),
DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL_FOR_READ);
// audits for read
String enableAuditForReadKey = StrolchConstants.makeRealmKey(getRealm(), PROP_ENABLE_AUDIT_TRAIL_FOR_READ);
this.auditTrailEnabledForRead = configuration.getBoolean(enableAuditForReadKey, Boolean.FALSE);
String updateObserversKey = StrolchConstants.makeRealmKey(getRealm(),
DefaultRealmHandler.PROP_ENABLE_OBSERVER_UPDATES);
// observer updates
String updateObserversKey = StrolchConstants.makeRealmKey(getRealm(), PROP_ENABLE_OBSERVER_UPDATES);
this.updateObservers = configuration.getBoolean(updateObserversKey, Boolean.FALSE);
if (this.updateObservers)
this.observerHandler = new DefaultObserverHandler();
// lock timeout
String propTryLockTimeUnit = StrolchConstants.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME_UNIT);
String propTryLockTime = StrolchConstants.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME);
TimeUnit timeUnit = TimeUnit.valueOf(configuration.getString(propTryLockTimeUnit, TimeUnit.SECONDS.name()));
long time = configuration.getLong(propTryLockTime, 10);
logger.info(MessageFormat.format("Using a locking try timeout of {0}s", timeUnit.toSeconds(time))); //$NON-NLS-1$
this.lockHandler = new DefaultLockHandler(this.realm, timeUnit, time);
// versioning
String enableVersioningKey = StrolchConstants.makeRealmKey(getRealm(), PROP_ENABLE_VERSIONING);
this.versioningEnabled = configuration.getBoolean(enableVersioningKey, Boolean.FALSE);
if (this.auditTrailEnabled)
logger.info("Enabling AuditTrail for realm " + getRealm()); //$NON-NLS-1$
else
logger.info("AuditTrail not enabled for realm " + getRealm()); //$NON-NLS-1$
if (this.auditTrailEnabledForRead)
logger.info("Enabling AuditTrail for read for realm " + getRealm()); //$NON-NLS-1$
else
logger.info("AuditTrail not enabled for read for realm " + getRealm()); //$NON-NLS-1$
if (this.updateObservers)
logger.info("Enabling Observer Updates for realm " + getRealm()); //$NON-NLS-1$
else
logger.info("Observer Updates not enabled for realm " + getRealm()); //$NON-NLS-1$
if (this.versioningEnabled)
logger.info("Enabling Versioning for realm " + getRealm()); //$NON-NLS-1$
else
logger.info("Versioning not enabled for realm " + getRealm()); //$NON-NLS-1$
logger.info(MessageFormat.format("Using a locking try timeout of {0}s", timeUnit.toSeconds(time))); //$NON-NLS-1$
}
@Override
@ -115,6 +146,11 @@ public abstract class InternalStrolchRealm implements StrolchRealm {
return this.updateObservers;
}
@Override
public boolean isVersioningEnabled() {
return this.versioningEnabled;
}
@Override
public ObserverHandler getObserverHandler() {
if (!this.updateObservers)

View File

@ -20,6 +20,7 @@ import static li.strolch.model.StrolchModelConstants.INTERPRETATION_ACTIVITY_REF
import java.util.List;
import li.strolch.agent.api.ActivityMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.activity.Activity;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.query.ActivityQuery;
@ -28,6 +29,10 @@ import li.strolch.persistence.api.StrolchTransaction;
public class TransactionalActivityMap extends TransactionalElementMap<Activity> implements ActivityMap {
public TransactionalActivityMap(StrolchRealm realm) {
super(realm);
}
@Override
protected void assertIsRefParam(Parameter<?> refP) {
ElementMapHelpers.assertIsRefParam(INTERPRETATION_ACTIVITY_REF, refP);

View File

@ -63,7 +63,7 @@ public class TransactionalAuditTrail implements AuditTrail {
public Audit getBy(StrolchTransaction tx, String type, Long id) {
return getDao(tx).queryBy(type, id);
}
@Override
public List<Audit> getAllElements(StrolchTransaction tx, String type, DateRange dateRange) {
return getDao(tx).queryAll(type, dateRange);

View File

@ -16,17 +16,21 @@
package li.strolch.agent.impl;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import li.strolch.agent.api.ElementMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.exception.StrolchException;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.Version;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.parameter.StringListParameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.persistence.api.StrolchDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.runtime.StrolchConstants;
@ -37,6 +41,16 @@ import li.strolch.runtime.StrolchConstants;
*/
public abstract class TransactionalElementMap<T extends StrolchRootElement> implements ElementMap<T> {
private StrolchRealm realm;
public TransactionalElementMap(StrolchRealm realm) {
this.realm = realm;
}
protected StrolchRealm getRealm() {
return this.realm;
}
protected abstract StrolchDao<T> getDao(StrolchTransaction tx);
@Override
@ -61,62 +75,160 @@ public abstract class TransactionalElementMap<T extends StrolchRootElement> impl
@Override
public T getTemplate(StrolchTransaction tx, String type) {
return getBy(tx, StrolchConstants.TEMPLATE, type);
return getTemplate(tx, type, false);
}
@Override
public T getTemplate(StrolchTransaction tx, String type, boolean assertExists) throws StrolchException {
T t = getBy(tx, StrolchConstants.TEMPLATE, type);
if (assertExists && t == null) {
String msg = "The template for type {0} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, type);
throw new StrolchException(msg);
}
if (t == null)
return null;
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}
@Override
public T getBy(StrolchTransaction tx, String type, String id) {
return getDao(tx).queryBy(type, id);
return getBy(tx, type, id, false);
}
protected abstract void assertIsRefParam(Parameter<?> refP);
@Override
public T getBy(StrolchTransaction tx, String type, String id, boolean assertExists) throws StrolchException {
T t = getDao(tx).queryBy(type, id);
if (assertExists && t == null) {
String msg = "The element for type {0} and id {1} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, type, id);
throw new StrolchException(msg);
}
if (t == null)
return null;
if (!this.realm.getMode().isTransient())
return t;
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, int version) {
return getBy(tx, type, id, version, false);
}
@Override
public T getBy(StrolchTransaction tx, String type, String id, int version, boolean assertExists)
throws StrolchException {
T t = getDao(tx).queryBy(type, id, version);
if (assertExists && t == null) {
String msg = "The element for type {0} and id {1} and version {2} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, type, id, version);
throw new StrolchException(msg);
}
return t;
}
@Override
public T getBy(StrolchTransaction tx, StringParameter refP, boolean assertExists) throws StrolchException {
assertIsRefParam(refP);
String type = refP.getUom();
String id = refP.getValue();
T element = getBy(tx, type, id);
if (assertExists && element == null) {
T t = getBy(tx, type, id);
if (assertExists && t == null) {
String msg = "The element for refP {0} with id {1} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, refP.getLocator(), id);
throw new StrolchException(msg);
}
return element;
if (t == null)
return null;
if (!this.realm.getMode().isTransient())
return t;
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}
@Override
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists) throws StrolchException {
public List<T> getBy(StrolchTransaction tx, StringListParameter refP, boolean assertExists)
throws StrolchException {
assertIsRefParam(refP);
List<T> elements = new ArrayList<>();
String type = refP.getUom();
List<String> ids = refP.getValue();
for (String id : ids) {
T element = getBy(tx, type, id);
if (element != null) {
elements.add(element);
} else if (assertExists) {
if (assertExists && element == null) {
String msg = "The element for refP {0} with id {1} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, refP.getLocator(), id);
throw new StrolchException(msg);
}
return ids.stream().map(id -> {
T t = getBy(tx, type, id);
if (assertExists && t == null) {
String msg = "The element for refP {0} with id {1} does not exist!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, refP.getLocator(), id);
throw new StrolchException(msg);
}
}
return t;
}).filter(Objects::nonNull).map(t -> {
if (!this.realm.getMode().isTransient()) {
return t;
} else {
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}
}).collect(Collectors.toList());
}
return elements;
@Override
public List<T> getVersionsFor(StrolchTransaction tx, String type, String id) {
return getDao(tx).queryVersionsFor(type, id).stream().map(t -> {
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}).collect(Collectors.toList());
}
@Override
public List<T> getAllElements(StrolchTransaction tx) {
return getDao(tx).queryAll();
List<T> all = getDao(tx).queryAll();
if (this.realm.getMode().isTransient())
return all;
return all.stream().map(t -> {
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}).collect(Collectors.toList());
}
@Override
public List<T> getElementsBy(StrolchTransaction tx, String type) {
return getDao(tx).queryAll(type);
List<T> all = getDao(tx).queryAll(type);
if (this.realm.getMode().isTransient())
return all;
return all.stream().map(t -> {
@SuppressWarnings("unchecked")
T clone = (T) t.getClone();
clone.setVersion(t.getVersion());
return clone;
}).collect(Collectors.toList());
}
@Override
@ -136,34 +248,73 @@ public abstract class TransactionalElementMap<T extends StrolchRootElement> impl
@Override
public void add(StrolchTransaction tx, T element) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
getDao(tx).save(element);
}
@Override
public T update(StrolchTransaction tx, T element) {
getDao(tx).update(element);
return element;
}
@Override
public void remove(StrolchTransaction tx, T element) {
getDao(tx).remove(element);
}
@Override
public void addAll(StrolchTransaction tx, List<T> elements) {
for (T element : elements) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
}
getDao(tx).saveAll(elements);
}
@Override
public List<T> updateAll(StrolchTransaction tx, List<T> elements) {
public void update(StrolchTransaction tx, T element) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
getDao(tx).update(element);
}
@Override
public void updateAll(StrolchTransaction tx, List<T> elements) {
for (T element : elements) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), false);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
}
getDao(tx).updateAll(elements);
return elements;
}
@Override
public void remove(StrolchTransaction tx, T element) {
if (this.realm.isVersioningEnabled()) {
Version.updateVersionFor(element, tx.getCertificate().getUsername(), true);
getDao(tx).update(element);
} else {
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
getDao(tx).remove(element);
}
}
@Override
public void removeAll(StrolchTransaction tx, List<T> elements) {
getDao(tx).removeAll(elements);
for (T element : elements) {
if (realm.isVersioningEnabled())
Version.updateVersionFor(element, tx.getCertificate().getUsername(), true);
else
Version.setInitialVersionFor(element, tx.getCertificate().getUsername());
}
if (this.realm.isVersioningEnabled()) {
getDao(tx).updateAll(elements);
} else {
getDao(tx).removeAll(elements);
}
}
@Override
@ -175,4 +326,50 @@ public abstract class TransactionalElementMap<T extends StrolchRootElement> impl
public long removeAllBy(StrolchTransaction tx, String type) {
return getDao(tx).removeAllBy(type);
}
@Override
public T revertToVersion(StrolchTransaction tx, T element) throws StrolchException {
return revertToVersion(tx, element.getType(), element.getId(), element.getVersion().getVersion());
}
@Override
public T revertToVersion(StrolchTransaction tx, String type, String id, int version) throws StrolchException {
if (!this.realm.isVersioningEnabled()) {
throw new StrolchPersistenceException("Can not revert to a version if versioning is not enabled!");
}
// get the current and specified version
T current = getBy(tx, type, id, true);
T versionT = getBy(tx, type, id, version, true);
// create the new version
@SuppressWarnings("unchecked")
T clone = (T) versionT.getClone();
clone.setVersion(current.getVersion().next(tx.getCertificate().getUsername(), false));
// save the new version
getDao(tx).update(clone);
// and return new version
return clone;
}
@Override
public void undoVersion(StrolchTransaction tx, T element) throws StrolchException {
if (!this.realm.isVersioningEnabled()) {
throw new StrolchPersistenceException("Can not undo a version if versioning is not enabled!");
}
// make sure the given element is the latest version
T current = getBy(tx, element.getType(), element.getId(), true);
if (!current.getVersion().equals(element.getVersion())) {
String msg = "Can not undo the version {0} as it is not the latest {1}!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, element.getVersion(), current.getVersion());
throw new StrolchException(msg);
}
getDao(tx).removeVersion(current);
}
protected abstract void assertIsRefParam(Parameter<?> refP);
}

View File

@ -20,6 +20,7 @@ import static li.strolch.model.StrolchModelConstants.INTERPRETATION_ORDER_REF;
import java.util.List;
import li.strolch.agent.api.OrderMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.Order;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.query.OrderQuery;
@ -28,6 +29,10 @@ import li.strolch.persistence.api.StrolchTransaction;
public class TransactionalOrderMap extends TransactionalElementMap<Order> implements OrderMap {
public TransactionalOrderMap(StrolchRealm realm) {
super(realm);
}
@Override
protected void assertIsRefParam(Parameter<?> refP) {
ElementMapHelpers.assertIsRefParam(INTERPRETATION_ORDER_REF, refP);

View File

@ -85,16 +85,14 @@ public class TransactionalRealm extends InternalStrolchRealm {
@Override
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
super.initialize(container, configuration);
this.resourceMap = new TransactionalResourceMap();
this.orderMap = new TransactionalOrderMap();
this.activityMap = new TransactionalActivityMap();
this.resourceMap = new TransactionalResourceMap(this);
this.orderMap = new TransactionalOrderMap(this);
this.activityMap = new TransactionalActivityMap(this);
if (isAuditTrailEnabled()) {
this.auditTrail = new TransactionalAuditTrail();
logger.info("Enabling AuditTrail for realm " + getRealm()); //$NON-NLS-1$
} else {
this.auditTrail = new NoStrategyAuditTrail();
logger.info("AuditTrail is disabled for realm " + getRealm()); //$NON-NLS-1$
}
this.persistenceHandler = container.getComponent(PersistenceHandler.class);
@ -122,8 +120,8 @@ public class TransactionalRealm extends InternalStrolchRealm {
long duration = System.nanoTime() - start;
String durationS = StringHelper.formatNanoDuration(duration);
logger.info(MessageFormat.format(
"Initialized Transactional Maps for realm {0} took {1}.", getRealm(), durationS)); //$NON-NLS-1$
logger.info(
MessageFormat.format("Initialized Transactional Maps for realm {0} took {1}.", getRealm(), durationS)); //$NON-NLS-1$
logger.info(MessageFormat.format("There are {0} Orders", nrOfOrders)); //$NON-NLS-1$
logger.info(MessageFormat.format("There are {0} Resources", nrOfResources)); //$NON-NLS-1$
logger.info(MessageFormat.format("There are {0} Activities", nrOfActivities)); //$NON-NLS-1$

View File

@ -20,6 +20,7 @@ import static li.strolch.model.StrolchModelConstants.INTERPRETATION_RESOURCE_REF
import java.util.List;
import li.strolch.agent.api.ResourceMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.Resource;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.query.ResourceQuery;
@ -28,6 +29,10 @@ import li.strolch.persistence.api.StrolchTransaction;
public class TransactionalResourceMap extends TransactionalElementMap<Resource> implements ResourceMap {
public TransactionalResourceMap(StrolchRealm realm) {
super(realm);
}
@Override
protected void assertIsRefParam(Parameter<?> refP) {
ElementMapHelpers.assertIsRefParam(INTERPRETATION_RESOURCE_REF, refP);

View File

@ -103,18 +103,15 @@ public class TransientRealm extends InternalStrolchRealm {
this.modelFile = configuration.getDataFile(key, null, configuration.getRuntimeConfiguration(), true);
this.persistenceHandler = new InMemoryPersistence(container.getPrivilegeHandler());
this.resourceMap = new TransactionalResourceMap();
this.orderMap = new TransactionalOrderMap();
this.activityMap = new TransactionalActivityMap();
this.persistenceHandler = new InMemoryPersistence(container.getPrivilegeHandler(), isVersioningEnabled());
this.resourceMap = new TransactionalResourceMap(this);
this.orderMap = new TransactionalOrderMap(this);
this.activityMap = new TransactionalActivityMap(this);
if (isAuditTrailEnabled()) {
if (isAuditTrailEnabled())
this.auditTrail = new TransactionalAuditTrail();
logger.info("Enabling AuditTrail for realm " + getRealm()); //$NON-NLS-1$
} else {
else
this.auditTrail = new NoStrategyAuditTrail();
logger.info("AuditTrail is disabled for realm " + getRealm()); //$NON-NLS-1$
}
}
@Override

View File

@ -49,7 +49,6 @@ import li.strolch.model.Resource;
import li.strolch.model.StrolchElement;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.Tags;
import li.strolch.model.Version;
import li.strolch.model.activity.Activity;
import li.strolch.model.audit.AccessType;
import li.strolch.model.audit.Audit;
@ -67,7 +66,6 @@ import li.strolch.model.visitor.ElementTypeVisitor;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.service.api.Command;
import li.strolch.utils.dbc.DBC;
@ -86,7 +84,6 @@ public abstract class AbstractTransaction implements StrolchTransaction {
private boolean suppressUpdates;
private boolean suppressAudits;
private boolean suppressDoNothingLogging;
private boolean versioningEnabled;
private TransactionResult txResult;
private List<Command> commands;
@ -147,7 +144,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
return this.realm.getRealm();
}
protected StrolchRealm getRealm() {
public StrolchRealm getRealm() {
return this.realm;
}
@ -211,16 +208,6 @@ public abstract class AbstractTransaction implements StrolchTransaction {
return this.suppressAudits;
}
@Override
public void setVersioningEnabled(boolean versioningEnabled) {
this.versioningEnabled = versioningEnabled;
}
@Override
public boolean isVersioningEnabled() {
return this.versioningEnabled;
}
@Override
public boolean isSuppressDoNothingLogging() {
return suppressDoNothingLogging;
@ -231,6 +218,11 @@ public abstract class AbstractTransaction implements StrolchTransaction {
this.suppressDoNothingLogging = quietDoNothing;
}
@Override
public boolean isVersioningEnabled() {
return this.realm.isVersioningEnabled();
}
@Override
public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException {
this.realm.lock(element);
@ -407,22 +399,22 @@ public abstract class AbstractTransaction implements StrolchTransaction {
@Override
public Resource getResourceTemplate(String type) {
return getResourceBy(StrolchConstants.TEMPLATE, type);
return getResourceMap().getTemplate(this, type);
}
@Override
public Resource getResourceTemplate(String type, boolean assertExists) throws StrolchException {
return getResourceBy(StrolchConstants.TEMPLATE, type, assertExists);
return getResourceMap().getTemplate(this, type, assertExists);
}
@Override
public Order getOrderTemplate(String type) {
return getOrderBy(StrolchConstants.TEMPLATE, type);
return getOrderMap().getTemplate(this, type);
}
@Override
public Order getOrderTemplate(String type, boolean assertExists) throws StrolchException {
return getOrderBy(StrolchConstants.TEMPLATE, type, assertExists);
return getOrderMap().getTemplate(this, type, assertExists);
}
@Override
@ -432,12 +424,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
@Override
public Order getOrderBy(String type, String id, boolean assertExists) throws StrolchException {
Order order = getOrderMap().getBy(this, type, id);
if (assertExists && order == null) {
String msg = "No Order exists with the id {0} with type {1}";
throw new StrolchException(MessageFormat.format(msg, id, type));
}
return order;
return getOrderMap().getBy(this, type, id, assertExists);
}
@Override
@ -580,21 +567,25 @@ public abstract class AbstractTransaction implements StrolchTransaction {
} catch (Exception e) {
this.txResult.setState(TransactionState.ROLLING_BACK);
try {
undoCommands();
} catch (Exception e2) {
} catch (Exception ex) {
logger.error("Failed to commit transaction and then undo commands due to " + ex.getMessage(), ex);
try {
rollback(this.txResult);
handleRollback(start);
} catch (Exception e1) {
logger.error("Failed to roll back after failing to undo commands: " + e1.getMessage(), e1); //$NON-NLS-1$
} catch (Exception exc) {
logger.error("Failed to roll back after failing to undo commands: " + exc.getMessage(), exc); //$NON-NLS-1$
}
handleFailure(start, e);
handleFailure(start, ex);
}
try {
rollback(this.txResult);
handleRollback(start);
} catch (Exception e1) {
} catch (Exception ex) {
logger.error("Failed to commit transaction and then rollback due to " + ex.getMessage(), ex);
handleFailure(start, e);
}
@ -843,6 +834,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
List<Audit> audits = new ArrayList<>();
// this is bad... doesn't account for a created and deleted in same TX...
if (this.orderMap != null) {
if (this.realm.isAuditTrailEnabledForRead())
auditsFor(audits, AccessType.READ, Tags.ORDER, this.orderMap.getRead());
@ -916,15 +909,6 @@ public abstract class AbstractTransaction implements StrolchTransaction {
return audit;
}
@Override
public void updateVersionFor(StrolchRootElement element, boolean deleted) {
if (this.versioningEnabled) {
int v = element.getVersion() == null ? 0 : element.getVersion().getVersion() + 1;
Version version = new Version(element.getLocator(), v, this.certificate.getUsername(), deleted);
element.setVersion(version);
}
}
/**
* Calls {@link Command#validate()} on all registered command. This is done before we perform any commands and thus
* no rollback needs be done due to invalid input for a command
@ -943,8 +927,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
ListIterator<Command> iter = this.commands.listIterator();
while (iter.hasNext()) {
Command command = iter.next();
command.doCommand();
this.flushedCommands.add(command);
command.doCommand();
iter.remove();
}
}

View File

@ -21,41 +21,263 @@ import java.util.Set;
import li.strolch.model.StrolchRootElement;
/**
* <p>
* This data access object is used to access {@link StrolchRootElement} from the underlying persistence layer. Objects
* in Strolch are always referenced by a type and an ID. The type is a categorisation/grouping of the objects, while the
* ID is a unique identifier of the object. The ID must be unique, even for multiple groups.
* </p>
*
* <p>
* The DAO must support versioning. i.e. if versioning is enabled, then if an object is updated or removed, the existing
* version is not modified, but a new version is persisted so that rollbacks can be done
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*
* @param <T>
* the object instance being queried from the underlying persistence layer
*/
public interface StrolchDao<T extends StrolchRootElement> {
/**
* Queries the set of IDs for all elements, regardless of types from the underlying persistence layer.
*
* @return the set of IDs for all elements
*/
public Set<String> queryKeySet();
/**
* Returns true if an element exists with the given type and id in the underlying persistence layer
*
* @param type
* the type of elements
* @param id
* the id of the element to return
*
* @return true if an element exists with the given type and id
*/
public boolean hasElement(String type, String id);
/**
* Returns the number of elements in the underlying persistence layer, regardless of type
*
* @return the number of elements in the underlying persistence layer
*/
public long querySize();
/**
* Returns the number of elements in the underlying persistence layer for the given type
*
* @return the number of elements in the underlying persistence layer for the given type
*/
public long querySize(String type);
/**
* Queries the set of IDs for the elements with the given type
*
* @return the set of IDs for the elements with the given type
*/
public Set<String> queryKeySet(String type);
/**
* Queries the current list of types from the underlying persistence layer
*
* @return the list of types
*/
public Set<String> queryTypes();
/**
* Queries the latest version of the element with the given type and ID
*
* @param type
* the type of the element to be queried
* @param id
* the id of the element to be queried
*
* @return the element with the given type and ID, or null if it does not exist
*/
public T queryBy(String type, String id);
/**
* <p>
* Queries a specific version of the element with the given type and ID.
* </p>
*
* <p>
* <b>Note:</b> If you want to query the latest version, then use the method with out the version parameter
* </p>
*
* @param type
* the type of the element to be queried
* @param id
* the id of the element to be queried
* @param version
* the version of the element to be returned
*
* @return the element with the given type and ID, or null if it does not exist
*/
public T queryBy(String type, String id, int version);
/**
* Queries and returns all the versions of the element with the given type and ID
*
* @param type
* the type of the element to be queried
* @param id
* the id of the element to be queried
*
* @return all the versions of the element with the given type and ID
*/
public List<T> queryVersionsFor(String type, String id);
/**
* Queries and returns all elements regardless of type
*
* @return all elements regardless of type
*/
public List<T> queryAll();
/**
* Queries and returns all elements of the given type
*
* @param type
* the type of element to return
*
* @return all elements of the given type
*/
public List<T> queryAll(String type);
public void save(T element);
/**
* Persists the given element. The element must not yet exist
*
* @param element
* the element to be persisted
*
* @throws StrolchPersistenceException
* if the element already exists
*/
public void save(T element) throws StrolchPersistenceException;
public void saveAll(List<T> elements);
/**
* Persists the given list of elements. None of the elements may already exists
*
* @param elements
* the list of elements to be persisted
*
* @throws StrolchPersistenceException
* if any of the elements already exist
*/
public void saveAll(List<T> elements) throws StrolchPersistenceException;
public void update(T element);
/**
* Updates the given element. The element must already exist
*
* @param element
* the element to be updated
*
* @throws StrolchPersistenceException
* if the element does not exist
*/
public void update(T element) throws StrolchPersistenceException;
public void updateAll(List<T> elements);
/**
* Updates the given list of elements. Each element must already exist
*
* @param elements
* the elements to be updated
*
* @throws StrolchPersistenceException
* if any of the elements do not exist
*/
public void updateAll(List<T> elements) throws StrolchPersistenceException;
public void remove(T element);
/**
* <p>
* Removes the given element from the underlying persistence layer
* </p>
*
* <p>
* <b>Note:</b> This method deletes the given object including its versions! Do not call this method if you want to
* use versioning!
* </p>
*
* @param element
* the element to be removed
*
* @throws StrolchPersistenceException
* if the element does not exist
*/
public void remove(T element) throws StrolchPersistenceException;
public void removeAll(List<T> elements);
/**
* <p>
* Removes the given elements from the underlying persistence layer
* </p>
*
* <p>
* <b>Note:</b> This method deletes the given objects including their versions! Do not call this method if you want
* to use versioning!
* </p>
*
*
* @param elements
* the elements to be removed
*
* @throws StrolchPersistenceException
* if any of the elements do not exist
*/
public void removeAll(List<T> elements) throws StrolchPersistenceException;
/**
* <p>
* Removes all elements regardless of type from the underlying persistence layer
* </p>
*
* <p>
* <b>Note:</b> This method does not support versioning. This method completely removes all objects regardless of
* type and their versions!
* </p>
*
* @return the number of elements removed
*
* @throws StrolchPersistenceException
*/
public long removeAll();
/**
* <p>
* Removes all elements of the given type from the underlying persistence layer
* </p>
*
* <p>
* <b>Note:</b> This method does not support versioning. This method completely removes all objects of the given
* type and their versions!
* </p>
*
* @param type
* the type of element to remove
*
* @return the number of elements removed
*/
public long removeAllBy(String type);
/**
* <p>
* Removes the given version of the given element from the underlying persistence layer. The version must be the
* latest version and thus always deletes the newest version. To delete multiple versions call this method multiple
* times. To remove it completely, call the {@link #remove(StrolchRootElement)} method.
* </p>
*
* <p>
* <b>Note:</b> This element given must be the current latest version!
* </p>
*
* @param element
* the latest version of the element to be removed
*
* @throws StrolchPersistenceException
* if the element/version does not exist
*/
public void removeVersion(T element) throws StrolchPersistenceException;
}

View File

@ -279,19 +279,6 @@ public interface StrolchTransaction extends AutoCloseable {
*/
public boolean isSuppressAudits();
/**
*
* @param versioningEnabled
*/
public void setVersioningEnabled(boolean versioningEnabled);
/**
* Returns true if versioning of objects is enabled
*
* @return true if versioning of objects is enabled
*/
public boolean isVersioningEnabled();
/**
* If the given argument is true, then logging of a {@link TransactionCloseStrategy#DO_NOTHING} will be suppressed
*
@ -307,6 +294,13 @@ public interface StrolchTransaction extends AutoCloseable {
*/
boolean isSuppressDoNothingLogging();
/**
* Returns true if versioning is enabled on the {@link StrolchRealm} for which this transaction has been opened
*
* @return true if versioning is enabled
*/
boolean isVersioningEnabled();
/**
* Locks the given element and registers it on the transaction so the lock is released when the transaction is
* closed
@ -376,21 +370,6 @@ public interface StrolchTransaction extends AutoCloseable {
*/
public Audit auditFrom(AccessType accessType, StrolchRootElement element);
/**
* Creates a new version for the given element. If the element has no version yet, then the result will be version
* 0, otherwise the version will be an increment to the current version
*
* @param element
* the element for which to create a new version
* @param deleted
* if true, then the version will be marked as deleted, i.e. this object was removed from the element
* maps
*
* @return the new version, which is either an increment of the existing version on the element, or it will be
* version 0
*/
public void updateVersionFor(StrolchRootElement element, boolean deleted);
/**
* <p>
* Performs the given {@link OrderQuery} and each returned {@link Order} is passed through the {@link OrderVisitor}

View File

@ -25,6 +25,10 @@ import li.strolch.runtime.query.inmemory.InMemoryQuery;
public class InMemoryActivityDao extends InMemoryDao<Activity> implements ActivityDao {
public InMemoryActivityDao(boolean versioningEnabled) {
super(versioningEnabled);
}
@Override
public <U> List<U> doQuery(ActivityQuery<U> activityQuery) {
InMemoryActivityQueryVisitor visitor = new InMemoryActivityQueryVisitor();

View File

@ -16,6 +16,7 @@
package li.strolch.persistence.inmemory;
import java.text.MessageFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -29,25 +30,32 @@ import li.strolch.persistence.api.StrolchPersistenceException;
public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T> {
private Map<String, Map<String, T>> elementMap;
private Map<String, Map<String, ArrayDeque<T>>> elementMap;
private boolean versioningEnabled;
public InMemoryDao() {
public InMemoryDao(boolean versioningEnabled) {
this.versioningEnabled = versioningEnabled;
this.elementMap = new HashMap<>();
}
@Override
public synchronized boolean hasElement(String type, String id) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return false;
return byType.containsKey(id);
ArrayDeque<T> list = byType.get(id);
if (list == null)
return false;
return !list.getLast().getVersion().isDeleted();
}
@Override
public synchronized long querySize() {
long size = 0;
for (String type : this.elementMap.keySet()) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
size += byType.size();
}
return size;
@ -55,7 +63,7 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized long querySize(String type) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return 0;
return byType.size();
@ -63,21 +71,19 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized Set<String> queryKeySet() {
Set<String> keySet = new HashSet<>();
for (String type : this.elementMap.keySet()) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
for (String id : byType.keySet()) {
keySet.add(id);
}
}
return keySet;
}
@Override
public synchronized Set<String> queryKeySet(String type) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return new HashSet<>(0);
return new HashSet<>(byType.keySet());
@ -90,19 +96,58 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized T queryBy(String type, String id) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return null;
return byType.get(id);
ArrayDeque<T> list = byType.get(id);
if (list == null)
return null;
T t = list.getLast();
if (t.getVersion() != null && t.getVersion().isDeleted())
return null;
return t;
}
@Override
public synchronized T queryBy(String type, String id, int version) {
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return null;
ArrayDeque<T> list = byType.get(id);
if (list == null)
return null;
for (T t : list) {
if (t.getVersion().getVersion() == version)
return t;
}
return null;
}
@Override
public synchronized List<T> queryVersionsFor(String type, String id) {
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return null;
ArrayDeque<T> list = byType.get(id);
if (list == null)
return new ArrayList<>();
return new ArrayList<>(list);
}
@Override
public synchronized List<T> queryAll() {
List<T> elements = new ArrayList<>();
for (String type : this.elementMap.keySet()) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
for (String id : byType.keySet()) {
elements.add(byType.get(id));
ArrayDeque<T> list = byType.get(id);
T last = list.getLast();
if (last.getVersion() == null || !last.getVersion().isDeleted())
elements.add(last);
}
}
@ -111,27 +156,43 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized List<T> queryAll(String type) {
Map<String, T> byType = this.elementMap.get(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.get(type);
if (byType == null)
return new ArrayList<>(0);
return new ArrayList<>(byType.values());
List<T> elements = new ArrayList<>();
for (ArrayDeque<T> list : byType.values()) {
T last = list.getLast();
if (last.getVersion() == null || !last.getVersion().isDeleted())
elements.add(last);
}
return elements;
}
@Override
public synchronized void save(T element) {
Map<String, T> byType = this.elementMap.get(element.getType());
Map<String, ArrayDeque<T>> byType = this.elementMap.get(element.getType());
if (byType == null) {
byType = new HashMap<>();
this.elementMap.put(element.getType(), byType);
}
if (byType.containsKey(element.getId())) {
ArrayDeque<T> list = byType.get(element.getId());
// only allow add for existing id, if the existing one is "deleted"
if (list != null && !list.getLast().getVersion().isDeleted()) {
String msg = "An element already exists with the id {0}. Elements of the same class must always have a unique id, regardless of their type!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, element.getId());
throw new StrolchPersistenceException(msg);
}
byType.put(element.getId(), element);
if (list == null) {
list = new ArrayDeque<>();
byType.put(element.getId(), list);
}
list.addLast(element);
}
@Override
@ -143,13 +204,24 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized void update(T element) {
Map<String, T> byType = this.elementMap.get(element.getType());
Map<String, ArrayDeque<T>> byType = this.elementMap.get(element.getType());
if (byType == null) {
byType = new HashMap<>();
this.elementMap.put(element.getType(), byType);
String msg = "The element does not yet exist with the type {0} and id {1}. Use add() for new objects!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, element.getType(), element.getId());
throw new StrolchPersistenceException(msg);
}
byType.put(element.getId(), element);
ArrayDeque<T> list = byType.get(element.getId());
if (list == null) {
String msg = "The element does not yet exist with the type {0} and id {1}. Use add() for new objects!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, element.getType(), element.getId());
throw new StrolchPersistenceException(msg);
}
if (!this.versioningEnabled)
list.clear();
list.addLast(element);
}
@Override
@ -161,7 +233,7 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized void remove(T element) {
Map<String, T> byType = this.elementMap.get(element.getType());
Map<String, ArrayDeque<T>> byType = this.elementMap.get(element.getType());
if (byType != null) {
byType.remove(element.getId());
@ -184,7 +256,7 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
Set<String> keySet = new HashSet<>(this.elementMap.keySet());
for (String type : keySet) {
Map<String, T> byType = this.elementMap.remove(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.remove(type);
removed += byType.size();
byType.clear();
}
@ -194,11 +266,42 @@ public class InMemoryDao<T extends StrolchRootElement> implements StrolchDao<T>
@Override
public synchronized long removeAllBy(String type) {
Map<String, T> byType = this.elementMap.remove(type);
Map<String, ArrayDeque<T>> byType = this.elementMap.remove(type);
if (byType == null)
return 0;
long removed = byType.size();
byType.clear();
return removed;
}
@Override
public synchronized void removeVersion(T element) throws StrolchPersistenceException {
Map<String, ArrayDeque<T>> byType = this.elementMap.get(element.getType());
if (byType == null) {
String msg = "The element does not yet exist with the type {0}!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, element.getType());
throw new StrolchPersistenceException(msg);
}
ArrayDeque<T> list = byType.get(element.getId());
if (list == null) {
String msg = "The element does not yet exist with the type {0} and id {1}!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, element.getType(), element.getId());
throw new StrolchPersistenceException(msg);
}
T last = list.getLast();
if (!last.getVersion().equals(element.getVersion())) {
String msg = "The current version {0} is not the same as the version to remove {1}!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, last.getVersion(), element.getVersion());
throw new StrolchPersistenceException(msg);
}
list.removeLast();
if (list.isEmpty()) {
byType.remove(element.getId());
}
}
}

View File

@ -25,6 +25,10 @@ import li.strolch.runtime.query.inmemory.InMemoryQuery;
public class InMemoryOrderDao extends InMemoryDao<Order> implements OrderDao {
public InMemoryOrderDao(boolean versioningEnabled) {
super(versioningEnabled);
}
@Override
public <U> List<U> doQuery(OrderQuery<U> orderQuery) {
InMemoryOrderQueryVisitor visitor = new InMemoryOrderQueryVisitor();

View File

@ -30,11 +30,13 @@ import li.strolch.runtime.privilege.PrivilegeHandler;
public class InMemoryPersistence implements PersistenceHandler {
private boolean versioningEnabled;
private Map<String, DaoCache> daoCache;
private PrivilegeHandler privilegeHandler;
public InMemoryPersistence(PrivilegeHandler privilegeHandler) {
public InMemoryPersistence(PrivilegeHandler privilegeHandler, boolean versioningEnabled) {
this.privilegeHandler = privilegeHandler;
this.versioningEnabled = versioningEnabled;
this.daoCache = new HashMap<>();
}
@ -75,7 +77,8 @@ public class InMemoryPersistence implements PersistenceHandler {
private synchronized DaoCache getDaoCache(StrolchTransaction tx) {
DaoCache daoCache = this.daoCache.get(tx.getRealmName());
if (daoCache == null) {
daoCache = new DaoCache(new InMemoryOrderDao(), new InMemoryResourceDao(), new InMemoryActivityDao(),
daoCache = new DaoCache(new InMemoryOrderDao(this.versioningEnabled),
new InMemoryResourceDao(this.versioningEnabled), new InMemoryActivityDao(this.versioningEnabled),
new InMemoryAuditDao());
this.daoCache.put(tx.getRealmName(), daoCache);
}

View File

@ -25,6 +25,10 @@ import li.strolch.runtime.query.inmemory.InMemoryResourceQueryVisitor;
public class InMemoryResourceDao extends InMemoryDao<Resource> implements ResourceDao {
public InMemoryResourceDao(boolean versioningEnabled) {
super(versioningEnabled);
}
@Override
public <U> List<U> doQuery(ResourceQuery<U> resourceQuery) {
InMemoryResourceQueryVisitor visitor = new InMemoryResourceQueryVisitor();

View File

@ -50,6 +50,7 @@ public class ComponentContainerTest {
public static final String PATH_CACHED_CONTAINER = "src/test/resources/cachedtest";
public static final String PATH_EMPTY_CONTAINER = "src/test/resources/emptytest";
public static final String PATH_MINIMAL_CONTAINER = "src/test/resources/minimaltest";
public static final String PATH_VERSIONING_CONTAINER = "src/test/resources/versioningtest";
public static final String PATH_REALM_RUNTIME = "target/realmtest/";
public static final String PATH_TRANSIENT_RUNTIME = "target/transienttest/";

View File

@ -0,0 +1,319 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.runtime.query.inmemory;
import static li.strolch.model.query.ParameterSelection.booleanSelection;
import static li.strolch.model.query.ParameterSelection.floatListSelection;
import static li.strolch.model.query.ParameterSelection.floatSelection;
import static li.strolch.model.query.ParameterSelection.integerListSelection;
import static li.strolch.model.query.ParameterSelection.longListSelection;
import static li.strolch.model.query.ParameterSelection.stringListSelection;
import static li.strolch.model.query.ParameterSelection.stringSelection;
import static li.strolch.utils.StringMatchMode.ci;
import static li.strolch.utils.StringMatchMode.es;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.junit.Test;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Order;
import li.strolch.model.ParameterBag;
import li.strolch.model.State;
import li.strolch.model.Version;
import li.strolch.model.parameter.BooleanParameter;
import li.strolch.model.parameter.FloatListParameter;
import li.strolch.model.parameter.FloatParameter;
import li.strolch.model.parameter.IntegerListParameter;
import li.strolch.model.parameter.LongListParameter;
import li.strolch.model.parameter.StringListParameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.model.query.IdSelection;
import li.strolch.model.query.NameSelection;
import li.strolch.model.query.OrderQuery;
import li.strolch.model.query.ParameterSelection;
import li.strolch.persistence.inmemory.InMemoryOrderDao;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@SuppressWarnings("nls")
public class InMemoryOrderQueryTest {
protected InMemoryOrderDao daoInstance() {
return new InMemoryOrderDao(false);
}
@Test
public void shouldQueryById() {
List<Order> orders = getOrders();
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> orderQuery = OrderQuery.query("MyType1");
orderQuery.with(new IdSelection("@1"));
List<Order> result = dao.doQuery(orderQuery);
assertEquals(1, result.size());
assertEquals("@1", result.get(0).getId());
}
@Test
public void shouldQueryByIdOr() {
List<Order> orders = getOrders();
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> orderQuery = OrderQuery.query("MyType2");
orderQuery.or().with(new IdSelection("@3"), new IdSelection("@4"));
List<Order> result = dao.doQuery(orderQuery);
assertEquals(2, result.size());
assertEquals("@3", result.get(0).getId());
assertEquals("@4", result.get(1).getId());
}
@Test
public void shouldQueryByIdAnd() {
List<Order> orders = getOrders();
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> orderQuery = OrderQuery.query("MyType2");
orderQuery.and().with(new IdSelection("@3"), new NameSelection("Res 3", es()));
List<Order> result = dao.doQuery(orderQuery);
assertEquals(1, result.size());
assertEquals("@3", result.get(0).getId());
}
@Test
public void shouldNotQueryByIdAnd() {
List<Order> orders = getOrders();
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> orderQuery = OrderQuery.query("MyType1");
orderQuery.and().with(new IdSelection("@3"), new NameSelection("@4", es()));
List<Order> result = dao.doQuery(orderQuery);
assertEquals(0, result.size());
}
@Test
public void shouldQueryByParameter() {
List<Order> orders = getOrders();
orders.add(getBallOrder());
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(
//
stringSelection("parameters", "color", "red", es()),
booleanSelection("parameters", "forChildren", true), floatSelection("parameters", "diameter", 22.0));
List<Order> result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
@Test
public void shouldQueryByListParameter() {
List<Order> orders = getOrders();
orders.add(getBallOrder());
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> ballQuery;
List<Order> result;
// string list
{
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(stringListSelection("parameters", "stringListValues", Arrays.asList("a", "z")));
result = dao.doQuery(ballQuery);
assertEquals(0, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(stringListSelection("parameters", "stringListValues", Arrays.asList("a")));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(stringListSelection("parameters", "stringListValues", Arrays.asList("c", "b", "a")));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
// integer list
{
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(integerListSelection("parameters", "intListValues", Arrays.asList(1, 5)));
result = dao.doQuery(ballQuery);
assertEquals(0, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(integerListSelection("parameters", "intListValues", Arrays.asList(1)));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(integerListSelection("parameters", "intListValues", Arrays.asList(3, 2, 1)));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
// float list
{
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(floatListSelection("parameters", "floatListValues", Arrays.asList(4.0, 8.0)));
result = dao.doQuery(ballQuery);
assertEquals(0, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(floatListSelection("parameters", "floatListValues", Arrays.asList(4.0)));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(floatListSelection("parameters", "floatListValues", Arrays.asList(6.2, 5.1, 4.0)));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
// long list
{
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(longListSelection("parameters", "longListValues", Arrays.asList(8L, 11L)));
result = dao.doQuery(ballQuery);
assertEquals(0, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(longListSelection("parameters", "longListValues", Arrays.asList(8L)));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
ballQuery = OrderQuery.query("Ball");
ballQuery.and().with(longListSelection("parameters", "longListValues", Arrays.asList(10L, 9L, 8L)));
result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
}
@Test
public void shouldQueryByNullParameter1() {
List<Order> orders = getOrders();
orders.add(getBallOrder());
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> ballQuery = OrderQuery.query("Ball");
ballQuery.and().with( //
ParameterSelection.nullSelection("parameters", "color"));
List<Order> result = dao.doQuery(ballQuery);
assertEquals(0, result.size());
}
@Test
public void shouldQueryByNullParameter2() {
List<Order> orders = getOrders();
orders.add(getBallOrder());
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> ballQuery = OrderQuery.query("Ball");
ballQuery.and().with( //
ParameterSelection.nullSelection("parameters", "weight"));
List<Order> result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
@Test
public void shouldQueryByNullParameter3() {
List<Order> orders = getOrders();
orders.add(getBallOrder());
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> ballQuery = OrderQuery.query("Ball");
ballQuery.and().with( //
ParameterSelection.nullSelection("parameters", "weight"));
List<Order> result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
@Test
public void shouldQueryByName() {
List<Order> orders = getOrders();
orders.add(getBallOrder());
InMemoryOrderDao dao = daoInstance();
dao.saveAll(orders);
OrderQuery<Order> ballQuery = OrderQuery.query("Ball");
ballQuery.with(new NameSelection("ball ", ci()));
List<Order> result = dao.doQuery(ballQuery);
assertEquals(1, result.size());
}
private Order getBallOrder() {
Order o1 = new Order("childrensBall", "Ball 1", "Ball");
o1.setVersion(new Version(o1.getLocator(), 0, "ModelGenerator", false));
ParameterBag bag = new ParameterBag("parameters", "Ball Details", "Parameters");
bag.addParameter(new StringParameter("color", "Color", "red"));
bag.addParameter(new BooleanParameter("forChildren", "Color", true));
bag.addParameter(new FloatParameter("diameter", "Color", 22.0));
bag.addParameter(
new StringListParameter("stringListValues", "List of String Values", Arrays.asList("a", "b", "c")));
bag.addParameter(new IntegerListParameter("intListValues", "List of Integer Values", Arrays.asList(1, 2, 3)));
bag.addParameter(
new FloatListParameter("floatListValues", "List of Float Values", Arrays.asList(4.0, 5.1, 6.2)));
bag.addParameter(new LongListParameter("longListValues", "List of Long Values", Arrays.asList(8L, 9L, 10L)));
o1.addParameterBag(bag);
return o1;
}
private List<Order> getOrders() {
Order res1 = ModelGenerator.createOrder("@1", "Res 1", "MyType1", new Date(), State.CREATED);
Order res2 = ModelGenerator.createOrder("@2", "Res 2", "MyType1", new Date(), State.CREATED);
Order res3 = ModelGenerator.createOrder("@3", "Res 3", "MyType2", new Date(), State.CREATED);
Order res4 = ModelGenerator.createOrder("@4", "Res 4", "MyType2", new Date(), State.CREATED);
Order res5 = ModelGenerator.createOrder("@5", "Res 5", "MyType3", new Date(), State.CREATED);
Order res6 = ModelGenerator.createOrder("@6", "Res 6", "MyType3", new Date(), State.CREATED);
List<Order> orders = new ArrayList<>();
orders.add(res1);
orders.add(res2);
orders.add(res3);
orders.add(res4);
orders.add(res5);
orders.add(res6);
return orders;
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.inmemory;
package li.strolch.runtime.query.inmemory;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchComponent;
@ -24,6 +24,7 @@ import li.strolch.persistence.api.OrderDao;
import li.strolch.persistence.api.PersistenceHandler;
import li.strolch.persistence.api.ResourceDao;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.persistence.inmemory.InMemoryPersistence;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.configuration.ComponentConfiguration;
@ -44,7 +45,7 @@ public class InMemoryPersistenceHandler extends StrolchComponent implements Pers
@Override
public void initialize(ComponentConfiguration configuration) throws Exception {
this.persistence = new InMemoryPersistence(getContainer().getPrivilegeHandler());
this.persistence = new InMemoryPersistence(getContainer().getPrivilegeHandler(), false);
super.initialize(configuration);
}

View File

@ -1,18 +1,3 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.runtime.query.inmemory;
import static li.strolch.model.query.ParameterSelection.booleanSelection;
@ -28,14 +13,14 @@ import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.junit.Test;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Order;
import li.strolch.model.ParameterBag;
import li.strolch.model.Resource;
import li.strolch.model.State;
import li.strolch.model.Version;
import li.strolch.model.parameter.BooleanParameter;
import li.strolch.model.parameter.FloatListParameter;
import li.strolch.model.parameter.FloatParameter;
@ -45,39 +30,21 @@ import li.strolch.model.parameter.StringListParameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.model.query.IdSelection;
import li.strolch.model.query.NameSelection;
import li.strolch.model.query.OrderQuery;
import li.strolch.model.query.ParameterSelection;
import li.strolch.model.query.ResourceQuery;
import li.strolch.persistence.inmemory.InMemoryOrderDao;
import li.strolch.persistence.inmemory.InMemoryResourceDao;
import org.junit.Test;
public class InMemoryResourceQueryTest {
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@SuppressWarnings("nls")
public class InMemoryQueryTest {
@Test
public void shouldQueryOrderById() {
List<Order> orders = getOrders();
InMemoryOrderDao dao = new InMemoryOrderDao();
dao.saveAll(orders);
OrderQuery<Order> orderQuery = OrderQuery.query("MyType1");
orderQuery.with(new IdSelection("@1"));
List<Order> result = dao.doQuery(orderQuery);
assertEquals(1, result.size());
assertEquals("@1", result.get(0).getId());
protected InMemoryResourceDao daoInstance() {
return new InMemoryResourceDao(false);
}
@Test
public void shouldQueryResourceById() {
public void shouldQueryById() {
List<Resource> resources = getResources();
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> resourceQuery = ResourceQuery.query("MyType1");
@ -89,10 +56,10 @@ public class InMemoryQueryTest {
}
@Test
public void shouldQueryResourceByIdOr() {
public void shouldQueryByIdOr() {
List<Resource> resources = getResources();
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> resourceQuery = ResourceQuery.query("MyType2");
@ -105,10 +72,10 @@ public class InMemoryQueryTest {
}
@Test
public void shouldQueryResourceByIdAnd() {
public void shouldQueryByIdAnd() {
List<Resource> resources = getResources();
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> resourceQuery = ResourceQuery.query("MyType2");
@ -120,10 +87,10 @@ public class InMemoryQueryTest {
}
@Test
public void shouldNotQueryResourceByIdAnd() {
public void shouldNotQueryByIdAnd() {
List<Resource> resources = getResources();
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> resourceQuery = ResourceQuery.query("MyType1");
@ -138,7 +105,7 @@ public class InMemoryQueryTest {
List<Resource> resources = getResources();
resources.add(getBallResource());
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> ballQuery = ResourceQuery.query("Ball");
@ -156,7 +123,7 @@ public class InMemoryQueryTest {
List<Resource> resources = getResources();
resources.add(getBallResource());
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> ballQuery;
@ -239,7 +206,7 @@ public class InMemoryQueryTest {
public void shouldQueryByNullParameter1() {
List<Resource> resources = getResources();
resources.add(getBallResource());
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> ballQuery = ResourceQuery.query("Ball");
@ -254,7 +221,7 @@ public class InMemoryQueryTest {
public void shouldQueryByNullParameter2() {
List<Resource> resources = getResources();
resources.add(getBallResource());
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> ballQuery = ResourceQuery.query("Ball");
@ -269,7 +236,7 @@ public class InMemoryQueryTest {
public void shouldQueryByNullParameter3() {
List<Resource> resources = getResources();
resources.add(getBallResource());
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> ballQuery = ResourceQuery.query("Ball");
@ -285,7 +252,7 @@ public class InMemoryQueryTest {
List<Resource> resources = getResources();
resources.add(getBallResource());
InMemoryResourceDao dao = new InMemoryResourceDao();
InMemoryResourceDao dao = daoInstance();
dao.saveAll(resources);
ResourceQuery<Resource> ballQuery = ResourceQuery.query("Ball");
@ -297,14 +264,16 @@ public class InMemoryQueryTest {
private Resource getBallResource() {
Resource res1 = new Resource("childrensBall", "Ball 1", "Ball");
res1.setVersion(new Version(res1.getLocator(), 0, "ModelGenerator", false));
ParameterBag bag = new ParameterBag("parameters", "Ball Details", "Parameters");
bag.addParameter(new StringParameter("color", "Color", "red"));
bag.addParameter(new BooleanParameter("forChildren", "Color", true));
bag.addParameter(new FloatParameter("diameter", "Color", 22.0));
bag.addParameter(new StringListParameter("stringListValues", "List of String Values", Arrays.asList("a", "b",
"c")));
bag.addParameter(
new StringListParameter("stringListValues", "List of String Values", Arrays.asList("a", "b", "c")));
bag.addParameter(new IntegerListParameter("intListValues", "List of Integer Values", Arrays.asList(1, 2, 3)));
bag.addParameter(new FloatListParameter("floatListValues", "List of Float Values", Arrays.asList(4.0, 5.1, 6.2)));
bag.addParameter(
new FloatListParameter("floatListValues", "List of Float Values", Arrays.asList(4.0, 5.1, 6.2)));
bag.addParameter(new LongListParameter("longListValues", "List of Long Values", Arrays.asList(8L, 9L, 10L)));
res1.addParameterBag(bag);
return res1;
@ -324,23 +293,11 @@ public class InMemoryQueryTest {
resources.add(res4);
resources.add(res5);
resources.add(res6);
for (Resource resource : resources) {
resource.setVersion(new Version(resource.getLocator(), 0, "Test", false));
}
return resources;
}
private List<Order> getOrders() {
Order res1 = ModelGenerator.createOrder("@1", "Res 1", "MyType1", new Date(), State.CREATED);
Order res2 = ModelGenerator.createOrder("@2", "Res 2", "MyType1", new Date(), State.CREATED);
Order res3 = ModelGenerator.createOrder("@3", "Res 3", "MyType2", new Date(), State.CREATED);
Order res4 = ModelGenerator.createOrder("@4", "Res 4", "MyType2", new Date(), State.CREATED);
Order res5 = ModelGenerator.createOrder("@5", "Res 5", "MyType3", new Date(), State.CREATED);
Order res6 = ModelGenerator.createOrder("@6", "Res 6", "MyType3", new Date(), State.CREATED);
List<Order> orders = new ArrayList<>();
orders.add(res1);
orders.add(res2);
orders.add(res3);
orders.add(res4);
orders.add(res5);
orders.add(res6);
return orders;
}
}

View File

@ -44,7 +44,7 @@
<Component>
<name>PersistenceHandler</name>
<api>li.strolch.persistence.api.PersistenceHandler</api>
<impl>li.strolch.persistence.inmemory.InMemoryPersistenceHandler</impl>
<impl>li.strolch.runtime.query.inmemory.InMemoryPersistenceHandler</impl>
</Component>
</env>
</StrolchConfiguration>

View File

@ -19,7 +19,7 @@
<Component>
<name>PersistenceHandler</name>
<api>li.strolch.persistence.api.PersistenceHandler</api>
<impl>li.strolch.persistence.inmemory.InMemoryPersistenceHandler</impl>
<impl>li.strolch.runtime.query.inmemory.InMemoryPersistenceHandler</impl>
</Component>
<Component>
<name>RealmHandler</name>

View File

@ -45,7 +45,7 @@
<Component>
<name>PersistenceHandler</name>
<api>li.strolch.persistence.api.PersistenceHandler</api>
<impl>li.strolch.persistence.inmemory.InMemoryPersistenceHandler</impl>
<impl>li.strolch.runtime.query.inmemory.InMemoryPersistenceHandler</impl>
</Component>
</env>
</StrolchConfiguration>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<Privilege>
<Container>
<Parameters>
<!-- parameters for the container itself -->
<Parameter name="autoPersistOnUserChangesData" value="true" />
</Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters>
<Parameter name="hashAlgorithm" value="SHA-256" />
</Parameters>
</EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler">
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
</Container>
<Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="RoleAccessPrivilege" class="li.strolch.privilege.policy.RoleAccessPrivilege" />
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
</Policies>
</Privilege>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<Roles>
<Role name="agent">
<Privilege name="li.strolch.privilege.handler.SystemUserAction" policy="DefaultPrivilege">
<Allow>li.strolch.agent.impl.StartRealms</Allow>
</Privilege>
</Role>
<Role name="AppUser">
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
</Role>
</Roles>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<Users>
<User userId="1" username="agent">
<State>SYSTEM</State>
<Roles>
<Role>agent</Role>
</Roles>
</User>
<User userId="2" username="test" password="9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08">
<Firstname>Application</Firstname>
<Lastname>Administrator</Lastname>
<State>ENABLED</State>
<Locale>en_GB</Locale>
<Roles>
<Role>AppUser</Role>
</Roles>
</User>
</Users>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<StrolchConfiguration>
<env id="dev">
<Runtime>
<applicationName>StrolchRuntimeTest</applicationName>
<Properties>
<verbose>true</verbose>
</Properties>
</Runtime>
<Component>
<name>PrivilegeHandler</name>
<api>li.strolch.runtime.privilege.PrivilegeHandler</api>
<impl>li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler</impl>
<Properties>
<privilegeConfigFile>PrivilegeConfig.xml</privilegeConfigFile>
</Properties>
</Component>
<Component>
<name>RealmHandler</name>
<api>li.strolch.agent.api.RealmHandler</api>
<impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
<depends>PrivilegeHandler</depends>
<Properties>
<enableVersioning>true</enableVersioning>
<dataStoreMode>TRANSIENT</dataStoreMode>
<dataStoreFile>StrolchModel.xml</dataStoreFile>
</Properties>
</Component>
<Component>
<name>ServiceHandler</name>
<api>li.strolch.runtime.configuration.model.ServiceHandlerTest</api>
<impl>li.strolch.runtime.configuration.model.ServiceHandlerTestImpl</impl>
<depends>RealmHandler</depends>
<Properties>
</Properties>
</Component>
<Component>
<name>PostInitializer</name>
<api>li.strolch.runtime.configuration.model.PostInitializerTest</api>
<impl>li.strolch.runtime.configuration.model.PostInitializerTestImpl</impl>
<depends>ServiceHandler</depends>
<Properties>
</Properties>
</Component>
</env>
</StrolchConfiguration>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<StrolchModel>
<Order Id="MyTestOrder" Name="Test Name" Type="TestType" Date="2013-11-20T07:42:57.699+01:00" State="CREATED">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
<Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
<Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
<Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
<Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
<Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
</ParameterBag>
</Order>
</StrolchModel>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<StrolchModel>
<Resource Id="MyTestResource" Name="Test Name" Type="TestType">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
<Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
<Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
<Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
<Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
<Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
</ParameterBag>
</Resource>
</StrolchModel>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<StrolchModel>
<Resource Id="TestType" Name="TestType Template" Type="Template">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
<Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
<Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
<Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
<Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
<Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
</ParameterBag>
</Resource>
<Order Id="MyTestOrder" Name="MyTestOrder Template" Type="Template">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
<Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
<Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
<Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
<Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
<Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
</ParameterBag>
</Order>
<IncludeFile file="Resources.xml" />
<IncludeFile file="Orders.xml" />
</StrolchModel>

View File

@ -22,6 +22,7 @@ import java.util.Iterator;
import java.util.List;
import li.strolch.exception.StrolchException;
import li.strolch.utils.dbc.DBC;
import li.strolch.utils.helper.StringHelper;
/**
@ -126,6 +127,11 @@ public class Locator {
this.pathElements = Collections.unmodifiableList(fullPath);
}
public String get(int part) {
DBC.PRE.assertTrue("Part outside of locator range: " + part, part < this.pathElements.size());
return this.pathElements.get(part);
}
/**
* Returns the immutable list of path elements making up this locator
*
@ -155,7 +161,7 @@ public class Locator {
public Locator append(List<String> subPathElements) {
return new Locator(this.pathElements, subPathElements);
}
/**
* Returns a new {@link Locator} where the given sub path is appended to the locator
*

View File

@ -346,6 +346,33 @@ public class ModelGenerator {
return orders;
}
/**
* Creates a list of {@link Activity Activities} with the given values and adds a {@link ParameterBag} by calling
* {@link #createParameterBag(String, String, String)}
*
* @param idStart
* id range start
* @param count
* the number of elements to create
* @param idPrefix
* the prefix to generate IDs for the {@link Activity Activities}
* @param name
* the name of the {@link Activity}
* @param type
* the type of the {@link Activity}
*
* @return the list of newly created {@link Activity Activities}
*/
public static List<Activity> createActivities(int idStart, int count, String idPrefix, String name, String type) {
List<Activity> activities = new ArrayList<>();
for (int i = 0; i < count; i++) {
String id = StringHelper.normalizeLength(String.valueOf((i + idStart)), 8, true, '0');
activities.add(createActivity(idPrefix + id, name + " " + i, type));
}
return activities;
}
public static Activity createActivity(String id, String name, String type) {
Activity rootActivity = new Activity(id, name, type);

View File

@ -15,6 +15,7 @@
*/
package li.strolch.model;
import java.text.MessageFormat;
import java.util.Date;
import li.strolch.exception.StrolchPolicyException;
@ -86,8 +87,11 @@ public class Order extends GroupedParameterizedElement implements StrolchRootEle
@Override
public void setVersion(Version version) throws IllegalArgumentException, IllegalStateException {
if (this.version != null)
this.version.validateIsNext(version);
if (version != null && !getLocator().equals(version.getLocator())) {
String msg = "Illegal version as locator is not same: Element: {0} Version: {1}";
throw new IllegalArgumentException(MessageFormat.format(msg, getLocator(), version));
}
this.version = version;
}

View File

@ -15,6 +15,7 @@
*/
package li.strolch.model;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -66,8 +67,11 @@ public class Resource extends GroupedParameterizedElement implements StrolchRoot
@Override
public void setVersion(Version version) throws IllegalArgumentException, IllegalStateException {
if (this.version != null)
this.version.validateIsNext(version);
if (version != null && !getLocator().equals(version.getLocator())) {
String msg = "Illegal version as locator is not same: Element: {0} Version: {1}";
throw new IllegalArgumentException(MessageFormat.format(msg, getLocator(), version));
}
this.version = version;
}
@ -140,6 +144,7 @@ public class Resource extends GroupedParameterizedElement implements StrolchRoot
@Override
public Resource getClone() {
Resource clone = new Resource();
super.fillClone(clone);
if (this.timedStateMap != null) {

View File

@ -34,23 +34,16 @@ public interface StrolchRootElement extends StrolchElement, PolicyContainer, Par
/**
* <p>
* Sets the version of this object.
* </p>
*
* <p>
* <b>Note:</b> If the version is set, then the new version must have the {@link Version#getVersion()} be an
* increment to the current version!
* Sets the version of this object
* </p>
*
* @param version
* the version to set
*
* @throws IllegalArgumentException
* if the given version's locator is not equal to the current version's locator
* @throws IllegalStateException
* if the given version is not the next version (an increment)
* if the given version's locator is not equal to the current element's locator
*/
public void setVersion(Version version) throws IllegalArgumentException, IllegalStateException;
public void setVersion(Version version) throws IllegalArgumentException;
/**
* Visitor pattern accept method. Takes a {@link StrolchRootElementVisitor} to visit this element

View File

@ -20,6 +20,10 @@ import li.strolch.utils.dbc.DBC;
* A version has a flag <code>delete</code> which, if true, designates that this version was removed
* </p>
*
* <p>
* A {@link Version} is immutable
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class Version {
@ -34,12 +38,12 @@ public class Version {
* Creates a new version instance with the given values. The creation date is now.
*
* @param version
* the integer version which must be > 0 and should be incremented for each new version of an object
* the integer version which must be >= 0 and should be incremented for each new version of an object
* @param createdBy
* the username of the creator of this object
*/
public Version(Locator locator, int version, String createdBy, boolean deleted) {
DBC.PRE.assertTrue("Version must by > 0", version > 0);
DBC.PRE.assertTrue("Version must by >= 0", version >= 0);
DBC.PRE.assertNotNull("locator must be set!", locator);
DBC.PRE.assertNotNull("createdBy must be set!", createdBy);
this.locator = locator;
@ -54,7 +58,7 @@ public class Version {
}
/**
* Returns the integer version, which is > 0
* Returns the integer version, which is >= 0
*
* @return the version
*/
@ -123,7 +127,7 @@ public class Version {
}
/**
* Validates that the given argument is an increment to this version
* Validates that the given argument is a newer version to this version
*
* @param other
* the other version to check
@ -133,14 +137,14 @@ public class Version {
* @throws IllegalStateException
* if the given argument is not the next version
*/
public void validateIsNext(Version other) throws IllegalArgumentException, IllegalStateException {
public void validateIsNewer(Version other) throws IllegalArgumentException, IllegalStateException {
if (!this.locator.equals(other.locator)) {
String msg = "Other version {0} is not for same object: {1}";
throw new IllegalArgumentException(MessageFormat.format(msg, other, this.version));
}
if (other.version != this.version + 1) {
String msg = "Other version: {0} is not an increment to this version: {1}";
if (this.version >= other.version) {
String msg = "Other version: {0} is a newer version of this version: {1}";
throw new IllegalArgumentException(MessageFormat.format(msg, other, this.version));
}
}
@ -161,4 +165,80 @@ public class Version {
builder.append("]");
return builder.toString();
}
/**
* Returns the next version, i.e. this version incremented by 1
*
* @param username
* the username to set
* @param deleted
* the deleted flag to set
*
* @return the next version
*/
public Version next(String username, boolean deleted) {
return new Version(this.locator, getNextVersion(), username, deleted);
}
/**
* Sets the initial version = 0 for the given element which is also set to not deleted
*
* @param element
* the element for which to create a new version
* @param username
* the username of the user who created this version of the object
*/
public static void setInitialVersionFor(StrolchRootElement element, String username) {
Version version = new Version(element.getLocator(), 0, username, false);
element.setVersion(version);
}
/**
* Sets a new version on the given element. If the element has no version yet, then the result will be version 0,
* otherwise the version will be an increment to the current version
*
* @param element
* the element for which to create a new version
* @param username
* the username of the user who created this version of the object
* @param deleted
* if true, then the version will be marked as deleted, i.e. this object was removed from the element
* maps
*/
public static void updateVersionFor(StrolchRootElement element, String username, boolean deleted) {
int v = element.getVersion() == null ? 0 : element.getVersion().getVersion() + 1;
Version version = new Version(element.getLocator(), v, username, deleted);
element.setVersion(version);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (this.deleted ? 1231 : 1237);
result = prime * result + ((this.locator == null) ? 0 : this.locator.hashCode());
result = prime * result + this.version;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Version other = (Version) obj;
if (this.deleted != other.deleted)
return false;
if (this.locator == null) {
if (other.locator != null)
return false;
} else if (!this.locator.equals(other.locator))
return false;
if (this.version != other.version)
return false;
return true;
}
}

View File

@ -15,6 +15,7 @@
*/
package li.strolch.model.activity;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -78,8 +79,12 @@ public class Activity extends GroupedParameterizedElement
public void setVersion(Version version) throws IllegalArgumentException, IllegalStateException {
if (!this.isRootElement())
throw new IllegalStateException("Can't set the version on non root of " + getLocator());
if (this.version != null)
this.version.validateIsNext(version);
if (version != null && !getLocator().equals(version.getLocator())) {
String msg = "Illegal version as locator is not same: Element: {0} Version: {1}";
throw new IllegalArgumentException(MessageFormat.format(msg, getLocator(), version));
}
this.version = version;
}
@ -260,6 +265,7 @@ public class Activity extends GroupedParameterizedElement
@Override
public Activity getClone() {
Activity clone = new Activity();
super.fillClone(clone);
if (this.elements == null)

View File

@ -18,6 +18,7 @@ package li.strolch.model.visitor;
import li.strolch.model.Order;
import li.strolch.model.Resource;
import li.strolch.model.Tags;
import li.strolch.model.activity.Activity;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
@ -33,4 +34,9 @@ public class ElementTypeVisitor implements StrolchRootElementVisitor<String> {
public String visitResource(Resource resource) {
return Tags.RESOURCE;
}
@Override
public String visitActivity(Activity activity) {
return Tags.ACTIVITY;
}
}

View File

@ -17,6 +17,7 @@ package li.strolch.model.visitor;
import li.strolch.model.Order;
import li.strolch.model.Resource;
import li.strolch.model.activity.Activity;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
@ -26,4 +27,6 @@ public interface StrolchRootElementVisitor<T> extends StrolchVisitor {
public T visitOrder(Order order);
public T visitResource(Resource resource);
public T visitActivity(Activity activity);
}

View File

@ -21,8 +21,10 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
@ -30,6 +32,9 @@ import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXResult;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import li.strolch.model.Tags;
import li.strolch.model.activity.Activity;
import li.strolch.model.query.ActivityQuery;
@ -39,9 +44,6 @@ import li.strolch.model.xml.XmlModelSaxReader;
import li.strolch.persistence.api.ActivityDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
@SuppressWarnings("nls")
public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements ActivityDao {
@ -68,22 +70,22 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(binaryStream, new XmlModelSaxReader(listener));
} catch (SQLException | IOException | SAXException | ParserConfigurationException e) {
throw new StrolchPersistenceException(MessageFormat.format(
"Failed to extract Activity from sqlxml value for {0} / {1}", id, type), e);
throw new StrolchPersistenceException(
MessageFormat.format("Failed to extract Activity from sqlxml value for {0} / {1}", id, type), e);
}
if (listener.getActivities().size() == 0)
throw new StrolchPersistenceException(MessageFormat.format(
"No Activity parsed from sqlxml value for {0} / {1}", id, type));
throw new StrolchPersistenceException(
MessageFormat.format("No Activity parsed from sqlxml value for {0} / {1}", id, type));
if (listener.getActivities().size() > 1)
throw new StrolchPersistenceException(MessageFormat.format(
"Multiple Activities parsed from sqlxml value for {0} / {1}", id, type));
throw new StrolchPersistenceException(
MessageFormat.format("Multiple Activities parsed from sqlxml value for {0} / {1}", id, type));
return listener.getActivities().get(0);
}
protected SQLXML createSqlXml(Activity activity, PreparedStatement preparedStatement) throws SQLException,
SAXException {
protected SQLXML createSqlXml(Activity activity, PreparedStatement preparedStatement)
throws SQLException, SAXException {
SQLXML sqlxml = tx().getConnection().createSQLXML();
SAXResult saxResult = sqlxml.setResult(SAXResult.class);
ContentHandler contentHandler = saxResult.getHandler();
@ -95,14 +97,29 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
@Override
protected void internalSave(final Activity activity) {
String sql = "insert into " + getTableName() + " (id, name, type, asxml) values (?, ?, ?, ?)";
String sql = "insert into " + getTableName()
+ " (id, version, created_by, created_at, deleted, latest, name, type, asxml) values (?, ?, ?, ?, ?, true, ?, ?, ?)";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
// id
preparedStatement.setString(1, activity.getId());
preparedStatement.setString(2, activity.getName());
preparedStatement.setString(3, activity.getType());
// version
preparedStatement.setInt(2, activity.getVersion().getVersion());
preparedStatement.setString(3, activity.getVersion().getCreatedBy());
preparedStatement.setTimestamp(4, new Timestamp(activity.getVersion().getCreatedAt().getTime()),
Calendar.getInstance());
preparedStatement.setBoolean(5, activity.getVersion().isDeleted());
// attributes
preparedStatement.setString(6, activity.getName());
preparedStatement.setString(7, activity.getType());
SQLXML sqlxml = createSqlXml(activity, preparedStatement);
preparedStatement.setSQLXML(4, sqlxml);
preparedStatement.setSQLXML(8, sqlxml);
try {
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
@ -118,19 +135,79 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
throw new StrolchPersistenceException(MessageFormat.format("Failed to insert Activity {0} due to {1}",
activity.getLocator(), e.getLocalizedMessage()), e);
}
if (activity.getVersion().isFirstVersion()) {
return;
}
// and set the previous version to not be latest anymore
sql = "update " + getTableName() + " SET latest = false WHERE id = ? AND version = ?";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
// primary key
preparedStatement.setString(1, activity.getId());
preparedStatement.setInt(2, activity.getVersion().getPreviousVersion());
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
String msg = "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
msg = MessageFormat.format(msg, activity.getId(), activity.getVersion().getPreviousVersion(), modCount);
throw new StrolchPersistenceException(msg);
}
} catch (SQLException e) {
throw new StrolchPersistenceException(
MessageFormat.format("Failed to update previous version of Activity {0} due to {1}",
activity.getVersion(), e.getLocalizedMessage()),
e);
}
}
@Override
protected void internalUpdate(final Activity activity) {
String sql = "update " + getTableName() + " set name = ?, type = ?, asxml = ? where id = ? ";
// with versioning we save a new object
if (tx().getRealm().isVersioningEnabled()) {
internalSave(activity);
return;
}
// make sure is first version when versioning is not enabled
if (!activity.getVersion().isFirstVersion()) {
throw new StrolchPersistenceException(MessageFormat.format(
"Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
activity.getVersion()));
}
// and also not marked as deleted!
if (activity.getVersion().isDeleted()) {
throw new StrolchPersistenceException(
MessageFormat.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
activity.getVersion()));
}
String sql = "update " + getTableName()
+ " set created_by = ?, created_at = ?, deleted = ?, latest = true, name = ?, type = ?, asxml = ? where id = ? and version = ?";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
preparedStatement.setString(1, activity.getName());
preparedStatement.setString(2, activity.getType());
preparedStatement.setString(4, activity.getId());
// version
preparedStatement.setString(1, activity.getVersion().getCreatedBy());
preparedStatement.setTimestamp(2, new Timestamp(activity.getVersion().getCreatedAt().getTime()),
Calendar.getInstance());
preparedStatement.setBoolean(3, activity.getVersion().isDeleted());
// attributes
preparedStatement.setString(4, activity.getName());
preparedStatement.setString(5, activity.getType());
SQLXML sqlxml = createSqlXml(activity, preparedStatement);
preparedStatement.setSQLXML(3, sqlxml);
preparedStatement.setSQLXML(6, sqlxml);
// primary key
preparedStatement.setString(7, activity.getId());
preparedStatement.setInt(8, activity.getVersion().getVersion());
try {
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {

View File

@ -45,7 +45,7 @@ import li.strolch.persistence.api.OrderDao;
import li.strolch.persistence.api.StrolchPersistenceException;
@SuppressWarnings("nls")
public class PostgreSqlOrderDao extends PostgresqlXmlDao<Order> implements OrderDao {
public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao {
public static final String ORDERS = "orders";
@ -96,17 +96,31 @@ public class PostgreSqlOrderDao extends PostgresqlXmlDao<Order> implements Order
@Override
protected void internalSave(final Order order) {
String sql = "insert into " + getTableName()
+ " (id, name, type, state, date, asxml) values (?, ?, ?, ?::order_state, ?, ?)";
+ " (id, version, created_by, created_at, deleted, latest, name, type, state, date, asxml) values (?, ?, ?, ?, ?, true, ?, ?, ?::order_state, ?, ?)";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
// id
preparedStatement.setString(1, order.getId());
preparedStatement.setString(2, order.getName());
preparedStatement.setString(3, order.getType());
preparedStatement.setString(4, order.getState().name());
preparedStatement.setTimestamp(5, new Timestamp(order.getDate().getTime()), Calendar.getInstance());
// version
preparedStatement.setInt(2, order.getVersion().getVersion());
preparedStatement.setString(3, order.getVersion().getCreatedBy());
preparedStatement.setTimestamp(4, new Timestamp(order.getVersion().getCreatedAt().getTime()),
Calendar.getInstance());
preparedStatement.setBoolean(5, order.getVersion().isDeleted());
// attributes
preparedStatement.setString(6, order.getName());
preparedStatement.setString(7, order.getType());
preparedStatement.setString(8, order.getState().name());
preparedStatement.setTimestamp(9, new Timestamp(order.getDate().getTime()), Calendar.getInstance());
SQLXML sqlxml = createSqlXml(order, preparedStatement);
preparedStatement.setSQLXML(6, sqlxml);
preparedStatement.setSQLXML(10, sqlxml);
try {
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
@ -120,29 +134,87 @@ public class PostgreSqlOrderDao extends PostgresqlXmlDao<Order> implements Order
} catch (SQLException | SAXException e) {
throw new StrolchPersistenceException(MessageFormat.format("Failed to insert Order {0} due to {1}",
order.getLocator(), e.getLocalizedMessage()), e);
order.getVersion(), e.getLocalizedMessage()), e);
}
if (order.getVersion().isFirstVersion()) {
return;
}
// and set the previous version to not be latest anymore
sql = "update " + getTableName() + " SET latest = false WHERE id = ? AND version = ?";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
// primary key
preparedStatement.setString(1, order.getId());
preparedStatement.setInt(2, order.getVersion().getPreviousVersion());
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
String msg = "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
msg = MessageFormat.format(msg, order.getId(), order.getVersion().getPreviousVersion(), modCount);
throw new StrolchPersistenceException(msg);
}
} catch (SQLException e) {
throw new StrolchPersistenceException(
MessageFormat.format("Failed to update previous version of Order {0} due to {1}",
order.getVersion(), e.getLocalizedMessage()),
e);
}
}
@Override
protected void internalUpdate(final Order order) {
// with versioning we save a new object
if (tx().getRealm().isVersioningEnabled()) {
internalSave(order);
return;
}
// make sure is first version when versioning is not enabled
if (!order.getVersion().isFirstVersion()) {
throw new StrolchPersistenceException(MessageFormat.format(
"Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
order.getVersion()));
}
// and also not marked as deleted!
if (order.getVersion().isDeleted()) {
throw new StrolchPersistenceException(MessageFormat.format(
"Versioning is not enabled, so version can not be marked as deleted for {0}", order.getVersion()));
}
// now we update the existing object
String sql = "update " + getTableName()
+ " set name = ?, type = ?, state = ?::order_state, date = ?, asxml = ? where id = ? ";
+ " set created_by = ?, created_at = ?, deleted = ?, latest = true, name = ?, type = ?, state = ?::order_state, date = ?, asxml = ? where id = ? and version = ?";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
preparedStatement.setString(1, order.getName());
preparedStatement.setString(2, order.getType());
preparedStatement.setString(3, order.getState().name());
preparedStatement.setTimestamp(4, new Timestamp(order.getDate().getTime()), Calendar.getInstance());
preparedStatement.setString(6, order.getId());
// version
preparedStatement.setString(1, order.getVersion().getCreatedBy());
preparedStatement.setTimestamp(2, new Timestamp(order.getVersion().getCreatedAt().getTime()),
Calendar.getInstance());
preparedStatement.setBoolean(3, order.getVersion().isDeleted());
// attributes
preparedStatement.setString(4, order.getName());
preparedStatement.setString(5, order.getType());
preparedStatement.setString(6, order.getState().name());
preparedStatement.setTimestamp(7, new Timestamp(order.getDate().getTime()), Calendar.getInstance());
SQLXML sqlxml = createSqlXml(order, preparedStatement);
preparedStatement.setSQLXML(5, sqlxml);
preparedStatement.setSQLXML(8, sqlxml);
// primary key
preparedStatement.setString(9, order.getId());
preparedStatement.setInt(10, order.getVersion().getVersion());
try {
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
String msg = "Expected to update 1 element with id {0} but SQL statement modified {1} elements!";
msg = MessageFormat.format(msg, order.getId(), modCount);
String msg = "Expected to update 1 element with id {0} and version {1} but SQL statement modified {2} elements!";
msg = MessageFormat.format(msg, order.getId(), order.getVersion().getVersion(), modCount);
throw new StrolchPersistenceException(msg);
}
} finally {

View File

@ -100,7 +100,7 @@ public abstract class PostgreSqlQueryVisitor
return this.sqlAsString;
}
this.sql.append("type = ? AND\n");
this.sql.append("type = ? AND latest = true AND\n");
this.sql.append(this.sb.toString());
appendOrdering();

View File

@ -21,8 +21,10 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
@ -30,6 +32,9 @@ import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXResult;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import li.strolch.model.Resource;
import li.strolch.model.Tags;
import li.strolch.model.query.ResourceQuery;
@ -39,9 +44,6 @@ import li.strolch.model.xml.XmlModelSaxReader;
import li.strolch.persistence.api.ResourceDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
@SuppressWarnings("nls")
public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements ResourceDao {
@ -68,16 +70,16 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(binaryStream, new XmlModelSaxReader(listener));
} catch (SQLException | IOException | SAXException | ParserConfigurationException e) {
throw new StrolchPersistenceException(MessageFormat.format(
"Failed to extract Resource from sqlxml value for {0} / {1}", id, type), e);
throw new StrolchPersistenceException(
MessageFormat.format("Failed to extract Resource from sqlxml value for {0} / {1}", id, type), e);
}
if (listener.getResources().size() == 0)
throw new StrolchPersistenceException(MessageFormat.format(
"No Resource parsed from sqlxml value for {0} / {1}", id, type));
throw new StrolchPersistenceException(
MessageFormat.format("No Resource parsed from sqlxml value for {0} / {1}", id, type));
if (listener.getResources().size() > 1)
throw new StrolchPersistenceException(MessageFormat.format(
"Multiple Resources parsed from sqlxml value for {0} / {1}", id, type));
throw new StrolchPersistenceException(
MessageFormat.format("Multiple Resources parsed from sqlxml value for {0} / {1}", id, type));
return listener.getResources().get(0);
}
@ -94,14 +96,26 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
@Override
protected void internalSave(final Resource res) {
String sql = "insert into " + getTableName() + " (id, name, type, asxml) values (?, ?, ?, ?)";
String sql = "insert into " + getTableName()
+ " (id, version, created_by, created_at, deleted, latest, name, type, asxml) values (?, ?, ?, ?, ?, true, ?, ?, ?)";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
// id
preparedStatement.setString(1, res.getId());
preparedStatement.setString(2, res.getName());
preparedStatement.setString(3, res.getType());
// version
preparedStatement.setInt(2, res.getVersion().getVersion());
preparedStatement.setString(3, res.getVersion().getCreatedBy());
preparedStatement.setTimestamp(4, new Timestamp(res.getVersion().getCreatedAt().getTime()),
Calendar.getInstance());
preparedStatement.setBoolean(5, res.getVersion().isDeleted());
// attributes
preparedStatement.setString(6, res.getName());
preparedStatement.setString(7, res.getType());
SQLXML sqlxml = createSqlXml(res, preparedStatement);
preparedStatement.setSQLXML(4, sqlxml);
preparedStatement.setSQLXML(8, sqlxml);
try {
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
@ -117,24 +131,82 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
throw new StrolchPersistenceException(MessageFormat.format("Failed to insert Resource {0} due to {1}",
res.getLocator(), e.getLocalizedMessage()), e);
}
if (res.getVersion().isFirstVersion()) {
return;
}
// and set the previous version to not be latest anymore
sql = "update " + getTableName() + " SET latest = false WHERE id = ? AND version = ?";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
// primary key
preparedStatement.setString(1, res.getId());
preparedStatement.setInt(2, res.getVersion().getPreviousVersion());
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
String msg = "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
msg = MessageFormat.format(msg, res.getId(), res.getVersion().getPreviousVersion(), modCount);
throw new StrolchPersistenceException(msg);
}
} catch (SQLException e) {
throw new StrolchPersistenceException(MessageFormat.format("Failed to insert Resource {0} due to {1}",
res.getLocator(), e.getLocalizedMessage()), e);
}
}
@Override
protected void internalUpdate(final Resource resource) {
String sql = "update " + getTableName() + " set name = ?, type = ?, asxml = ? where id = ? ";
// with versioning we save a new object
if (tx().getRealm().isVersioningEnabled()) {
internalSave(resource);
return;
}
// make sure is first version when versioning is not enabled
if (!resource.getVersion().isFirstVersion()) {
throw new StrolchPersistenceException(MessageFormat.format(
"Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
resource.getVersion()));
}
// and also not marked as deleted!
if (resource.getVersion().isDeleted()) {
throw new StrolchPersistenceException(
MessageFormat.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
resource.getVersion()));
}
// now we update the existing object
String sql = "update " + getTableName()
+ " set created_by = ?, created_at = ?, deleted = ?, latest = true, name = ?, type = ?, asxml = ? where id = ? and version = ?";
try (PreparedStatement preparedStatement = tx().getConnection().prepareStatement(sql)) {
preparedStatement.setString(1, resource.getName());
preparedStatement.setString(2, resource.getType());
preparedStatement.setString(4, resource.getId());
// version
preparedStatement.setString(1, resource.getVersion().getCreatedBy());
preparedStatement.setTimestamp(2, new Timestamp(resource.getVersion().getCreatedAt().getTime()),
Calendar.getInstance());
preparedStatement.setBoolean(3, resource.getVersion().isDeleted());
// attributes
preparedStatement.setString(4, resource.getName());
preparedStatement.setString(5, resource.getType());
SQLXML sqlxml = createSqlXml(resource, preparedStatement);
preparedStatement.setSQLXML(3, sqlxml);
preparedStatement.setSQLXML(6, sqlxml);
// primary key
preparedStatement.setString(7, resource.getId());
preparedStatement.setInt(8, resource.getVersion().getVersion());
try {
int modCount = preparedStatement.executeUpdate();
if (modCount != 1) {
String msg = "Expected to update 1 element with id {0} but SQL statement modified {1} elements!";
msg = MessageFormat.format(msg, resource.getId(), modCount);
String msg = "Expected to update 1 element with id {0} and version {1} but SQL statement modified {2} elements!";
msg = MessageFormat.format(msg, resource.getId(), resource.getVersion().getVersion(), modCount);
throw new StrolchPersistenceException(msg);
}
} finally {

View File

@ -17,6 +17,9 @@ package li.strolch.persistence.postgresql;
import java.sql.Connection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.persistence.api.AbstractTransaction;
import li.strolch.persistence.api.ActivityDao;
@ -28,9 +31,6 @@ import li.strolch.persistence.api.TransactionResult;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.privilege.PrivilegeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PostgreSqlStrolchTransaction extends AbstractTransaction {
private static final Logger logger = LoggerFactory.getLogger(PostgreSqlStrolchTransaction.class);
@ -56,6 +56,8 @@ public class PostgreSqlStrolchTransaction extends AbstractTransaction {
this.orderDao.commit(txResult);
if (this.resourceDao != null)
this.resourceDao.commit(txResult);
if (this.activityDao != null)
this.activityDao.commit(txResult);
// don't commit the connection, this is done in postCommit when we close the connection
}

View File

@ -33,7 +33,7 @@ import li.strolch.persistence.api.TransactionResult;
@SuppressWarnings("nls")
public abstract class PostgresqlDao<T extends StrolchRootElement> implements StrolchDao<T> {
protected PostgreSqlStrolchTransaction tx;
private PostgreSqlStrolchTransaction tx;
protected List<DaoCommand> commands;
public PostgresqlDao(PostgreSqlStrolchTransaction tx) {
@ -179,6 +179,18 @@ public abstract class PostgresqlDao<T extends StrolchRootElement> implements Str
}
}
@Override
public T queryBy(String type, String id, int version) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<T> queryVersionsFor(String type, String id) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<T> queryAll() {
@ -225,73 +237,55 @@ public abstract class PostgresqlDao<T extends StrolchRootElement> implements Str
@Override
public void save(final T res) {
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
internalSave(res);
txResult.incCreated(1);
}
this.commands.add(txResult -> {
internalSave(res);
txResult.incCreated(1);
});
}
@Override
public void saveAll(final List<T> elements) {
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
for (T element : elements) {
internalSave(element);
}
txResult.incCreated(elements.size());
this.commands.add(txResult -> {
for (T element : elements) {
internalSave(element);
}
txResult.incCreated(elements.size());
});
}
@Override
public void update(final T element) {
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
internalUpdate(element);
txResult.incUpdated(1);
}
this.commands.add(txResult -> {
internalUpdate(element);
txResult.incUpdated(1);
});
}
@Override
public void updateAll(final List<T> elements) {
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
for (T element : elements) {
internalUpdate(element);
}
txResult.incUpdated(elements.size());
this.commands.add(txResult -> {
for (T element : elements) {
internalUpdate(element);
}
txResult.incUpdated(elements.size());
});
}
@Override
public void remove(final T element) {
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
internalRemove(element);
txResult.incDeleted(1);
}
this.commands.add(txResult -> {
internalRemove(element);
txResult.incDeleted(1);
});
}
@Override
public void removeAll(final List<T> elements) {
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
for (T element : elements) {
internalRemove(element);
}
txResult.incDeleted(elements.size());
this.commands.add(txResult -> {
for (T element : elements) {
internalRemove(element);
}
txResult.incDeleted(elements.size());
});
}
@ -300,12 +294,9 @@ public abstract class PostgresqlDao<T extends StrolchRootElement> implements Str
final long toRemove = querySize();
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
internalRemoveAll(toRemove);
txResult.incDeleted(toRemove);
}
this.commands.add(txResult -> {
internalRemoveAll(toRemove);
txResult.incDeleted(toRemove);
});
return toRemove;
@ -316,17 +307,20 @@ public abstract class PostgresqlDao<T extends StrolchRootElement> implements Str
final long toRemove = querySize(type);
this.commands.add(new DaoCommand() {
@Override
public void doComand(TransactionResult txResult) {
internalRemoveAllBy(toRemove, type);
txResult.incDeleted(toRemove);
}
this.commands.add(txResult -> {
internalRemoveAllBy(toRemove, type);
txResult.incDeleted(toRemove);
});
return toRemove;
}
@Override
public void removeVersion(T element) throws StrolchPersistenceException {
// TODO Auto-generated method stub
}
/**
* @param element
*/
@ -366,8 +360,8 @@ public abstract class PostgresqlDao<T extends StrolchRootElement> implements Str
}
} catch (SQLException e) {
throw new StrolchPersistenceException(MessageFormat.format("Failed to remove all elements due to {0}",
e.getLocalizedMessage()), e);
throw new StrolchPersistenceException(
MessageFormat.format("Failed to remove all elements due to {0}", e.getLocalizedMessage()), e);
}
}
@ -383,8 +377,8 @@ public abstract class PostgresqlDao<T extends StrolchRootElement> implements Str
}
} catch (SQLException e) {
throw new StrolchPersistenceException(MessageFormat.format(
"Failed to remove all elements of type {0} due to {1}", type, e.getLocalizedMessage()), e);
throw new StrolchPersistenceException(MessageFormat
.format("Failed to remove all elements of type {0} due to {1}", type, e.getLocalizedMessage()), e);
}
}

View File

@ -1,14 +0,0 @@
package li.strolch.persistence.postgresql;
import java.sql.SQLXML;
import li.strolch.model.StrolchRootElement;
public abstract class PostgresqlXmlDao<T extends StrolchRootElement> extends PostgresqlDao<T> {
public PostgresqlXmlDao(PostgreSqlStrolchTransaction tx) {
super(tx);
}
protected abstract T parseFromXml(String id, String type, SQLXML xml);
}

View File

@ -0,0 +1,11 @@
DROP TABLE IF EXISTS resources;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS activities;
DROP TABLE IF EXISTS audits;
DROP TABLE IF EXISTS db_version;
DROP TYPE IF EXISTS order_state;
DROP TYPE IF EXISTS access_type;

View File

@ -0,0 +1,131 @@
-- DB_VERSION
CREATE TABLE IF NOT EXISTS db_version (
id serial primary key not null,
app varchar(255) not null,
version varchar(255) not null,
description varchar(255) not null,
created timestamp with time zone not null
);
-- RESOURCES
CREATE TABLE IF NOT EXISTS resources (
id varchar(255) not null,
version integer not null,
created_by varchar(255) not null,
created_at timestamp with time zone not null,
deleted boolean not null,
latest boolean not null,
name varchar(255) not null,
type varchar(255) not null,
asxml xml not null,
PRIMARY KEY (id, version)
);
-- ORDERS
CREATE TYPE order_state AS ENUM ('CREATED', 'OPEN', 'EXECUTION', 'CLOSED');
CREATE TABLE IF NOT EXISTS orders (
id varchar(255) not null,
version integer not null,
created_by varchar(255) not null,
created_at timestamp with time zone not null,
deleted boolean,
latest boolean not null,
name varchar(255),
type varchar(255),
state order_state,
date timestamp with time zone,
asxml xml not null,
PRIMARY KEY (id, version)
);
-- ACTIVITIES
CREATE TABLE IF NOT EXISTS activities (
id varchar(255) not null,
version integer not null,
created_by varchar(255) not null,
created_at timestamp with time zone not null,
deleted boolean not null,
latest boolean not null,
name varchar(255) not null,
type varchar(255) not null,
asxml xml not null,
PRIMARY KEY (id, version)
);
-- AUDITS
CREATE TYPE access_type AS ENUM ('READ', 'CREATE', 'UPDATE', 'DELETE');
CREATE TABLE IF NOT EXISTS audits (
id bigint PRIMARY KEY,
username varchar(255) NOT NULL,
firstname varchar(255) NOT NULL,
lastname varchar(255) NOT NULL,
date timestamp with time zone NOT NULL,
element_type varchar(255) NOT NULL,
element_sub_type varchar(255) NOT NULL,
element_accessed varchar(255) NOT NULL,
new_version timestamp with time zone,
action varchar(255) NOT NULL,
access_type access_type NOT NULL
);
-- set version
INSERT INTO db_version
(version, app, description, created)
values(
'0.1.0',
'strolch',
'Initial schema version',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.2.0',
'strolch',
'Added new table for audits',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.2.1',
'strolch',
'Added new column app to table table version',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.3.0',
'strolch',
'Added new column element_sub_type to table audits',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.4.0',
'strolch',
'Added new table activities',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.5.0',
'strolch',
'Added versioning to root elements',
CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,72 @@
-- add version columns
ALTER TABLE resources ADD COLUMN version integer;
ALTER TABLE resources ADD COLUMN created_by varchar(255);
ALTER TABLE resources ADD COLUMN created_at timestamp with time zone;
ALTER TABLE resources ADD COLUMN deleted boolean;
ALTER TABLE resources ADD COLUMN latest boolean;
ALTER TABLE orders ADD COLUMN version integer;
ALTER TABLE orders ADD COLUMN created_by varchar(255);
ALTER TABLE orders ADD COLUMN created_at timestamp with time zone;
ALTER TABLE orders ADD COLUMN deleted boolean;
ALTER TABLE orders ADD COLUMN latest boolean;
ALTER TABLE activities ADD COLUMN version integer;
ALTER TABLE activities ADD COLUMN created_by varchar(255);
ALTER TABLE activities ADD COLUMN created_at timestamp with time zone;
ALTER TABLE activities ADD COLUMN deleted boolean;
ALTER TABLE activities ADD COLUMN latest boolean;
-- set initial values for new columns
UPDATE resources SET version = 0 where version IS NULL;
UPDATE resources SET created_by = 'MIGRATION' where version IS NULL;
UPDATE resources SET created_at = CURRENT_TIMESTAMP where version IS NULL;
UPDATE resources SET deleted = false where version IS NULL;
UPDATE orders SET version = 0 where version IS NULL;
UPDATE orders SET created_by = 'MIGRATION' where version IS NULL;
UPDATE orders SET created_at = CURRENT_TIMESTAMP where version IS NULL;
UPDATE orders SET deleted = false where version IS NULL;
UPDATE activities SET version = 0 where version IS NULL;
UPDATE activities SET created_by = 'MIGRATION' where version IS NULL;
UPDATE activities SET created_at = CURRENT_TIMESTAMP where version IS NULL;
UPDATE activities SET deleted = false where version IS NULL;
-- make columns not null
ALTER TABLE resources ALTER COLUMN version SET NOT NULL;
ALTER TABLE resources ALTER COLUMN created_by SET NOT NULL;
ALTER TABLE resources ALTER COLUMN created_at SET NOT NULL;
ALTER TABLE resources ALTER COLUMN latest SET NOT NULL;
ALTER TABLE resources ALTER COLUMN deleted SET NOT NULL;
ALTER TABLE orders ALTER COLUMN version SET NOT NULL;
ALTER TABLE orders ALTER COLUMN created_by SET NOT NULL;
ALTER TABLE orders ALTER COLUMN created_at SET NOT NULL;
ALTER TABLE orders ALTER COLUMN latest SET NOT NULL;
ALTER TABLE orders ALTER COLUMN deleted SET NOT NULL;
ALTER TABLE activities ALTER COLUMN version SET NOT NULL;
ALTER TABLE activities ALTER COLUMN created_by SET NOT NULL;
ALTER TABLE activities ALTER COLUMN created_at SET NOT NULL;
ALTER TABLE activities ALTER COLUMN latest SET NOT NULL;
ALTER TABLE activities ALTER COLUMN deleted SET NOT NULL;
-- change primary key to id, version
ALTER TABLE resources DROP CONSTRAINT resources_pkey;
ALTER TABLE orders DROP CONSTRAINT orders_pkey;
ALTER TABLE activities DROP CONSTRAINT activities_pkey;
ALTER TABLE resources ADD CONSTRAINT resources_pkey PRIMARY KEY (id, version);
ALTER TABLE orders ADD CONSTRAINT orders_pkey PRIMARY KEY (id, version);
ALTER TABLE activities ADD CONSTRAINT activities_pkey PRIMARY KEY (id, version);
INSERT INTO db_version
(version, app, description, created)
values(
'0.5.0',
'strolch',
'Added versioning to root elements',
CURRENT_TIMESTAMP
);

View File

@ -1,2 +1,2 @@
# Property file defining what the currently expected version is supposed to be
db_version=0.4.0
db_version=0.5.0

View File

@ -22,7 +22,7 @@
<depends>PrivilegeHandler</depends>
<depends>PersistenceHandler</depends>
<Properties>
<dataStoreMode>TRANSACTIONAL</dataStoreMode>
<dataStoreMode>CACHED</dataStoreMode>
<enableAuditTrail>true</enableAuditTrail>
<enableObserverUpdates>true</enableObserverUpdates>
</Properties>

View File

@ -22,6 +22,7 @@ import java.util.Set;
import li.strolch.model.StrolchRootElement;
import li.strolch.persistence.api.StrolchDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.xmlpers.api.PersistenceTransaction;
import li.strolch.xmlpers.objref.IdOfSubTypeRef;
@ -104,6 +105,18 @@ public abstract class AbstractDao<T extends StrolchRootElement> implements Strol
return t;
}
@Override
public T queryBy(String type, String id, int version) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<T> queryVersionsFor(String type, String id) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<T> queryAll() {
List<T> objects = new ArrayList<>();
@ -163,4 +176,10 @@ public abstract class AbstractDao<T extends StrolchRootElement> implements Strol
SubTypeRef typeRef = getTypeRef(type);
return this.tx.getObjectDao().removeAllBy(typeRef);
}
@Override
public void removeVersion(T element) throws StrolchPersistenceException {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("not yet implemented!");
}
}

View File

@ -31,6 +31,7 @@ import li.strolch.utils.dbc.DBC;
public class RemoveOrderCommand extends Command {
private Order order;
private boolean removed;
/**
* @param tx
@ -65,14 +66,16 @@ public class RemoveOrderCommand extends Command {
}
orderMap.remove(tx(), this.order);
this.removed = true;
}
@Override
public void undo() {
if (this.order != null && tx().isRollingBack()) {
OrderMap orderMap = tx().getOrderMap();
if (!orderMap.hasElement(tx(), this.order.getType(), this.order.getId()))
orderMap.add(tx(), this.order);
if (this.order != null && tx().isRollingBack() && this.removed) {
if (tx().isVersioningEnabled())
tx().getOrderMap().undoVersion(tx(), this.order);
else
tx().getOrderMap().add(tx(), this.order);
}
}
}

View File

@ -31,6 +31,7 @@ import li.strolch.utils.dbc.DBC;
public class RemoveResourceCommand extends Command {
private Resource resource;
private boolean removed;
/**
* @param tx
@ -65,14 +66,16 @@ public class RemoveResourceCommand extends Command {
}
resourceMap.remove(tx(), this.resource);
this.removed = true;
}
@Override
public void undo() {
if (this.resource != null && tx().isRollingBack()) {
ResourceMap resourceMap = tx().getResourceMap();
if (!resourceMap.hasElement(tx(), this.resource.getType(), this.resource.getId()))
resourceMap.add(tx(), this.resource);
if (this.resource != null && tx().isRollingBack() && this.removed) {
if (tx().isVersioningEnabled())
tx().getResourceMap().undoVersion(tx(), this.resource);
else
tx().getResourceMap().add(tx(), this.resource);
}
}
}

View File

@ -16,6 +16,7 @@
package li.strolch.command;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import li.strolch.agent.api.ComponentContainer;
@ -32,7 +33,8 @@ import li.strolch.utils.dbc.DBC;
public class UpdateOrderCollectionCommand extends Command {
private List<Order> orders;
private List<Order> replacedElements;
private List<Order> replaced;
private boolean updated;
/**
* @param tx
@ -62,22 +64,32 @@ public class UpdateOrderCollectionCommand extends Command {
}
OrderMap orderMap = tx().getOrderMap();
this.replaced = new ArrayList<>();
for (Order order : this.orders) {
if (!orderMap.hasElement(tx(), order.getType(), order.getId())) {
Order o = orderMap.getBy(tx(), order.getType(), order.getId());
if (o == null) {
String msg = "The Order {0} can not be updated as it does not exist!";
msg = MessageFormat.format(msg, order.getLocator());
throw new StrolchException(msg);
}
this.replaced.add(o);
}
this.replacedElements = orderMap.updateAll(tx(), this.orders);
orderMap.updateAll(tx(), this.orders);
this.updated = true;
}
@Override
public void undo() {
if (this.replacedElements != null && tx().isRollingBack()) {
OrderMap orderMap = tx().getOrderMap();
orderMap.updateAll(tx(), this.replacedElements);
if (this.updated && tx().isRollingBack()) {
if (tx().isVersioningEnabled()) {
for (Order order : this.orders) {
tx().getOrderMap().undoVersion(tx(), order);
}
} else {
tx().getOrderMap().updateAll(tx(), this.replaced);
}
}
}
}

View File

@ -31,7 +31,8 @@ import li.strolch.utils.dbc.DBC;
public class UpdateOrderCommand extends Command {
private Order order;
private Order replacedElement;
private Order replaced;
private boolean updated;
/**
* @param tx
@ -59,19 +60,24 @@ public class UpdateOrderCommand extends Command {
tx().lock(this.order);
OrderMap orderMap = tx().getOrderMap();
if (!orderMap.hasElement(tx(), this.order.getType(), this.order.getId())) {
String msg = "The Order {0} can not be updated as it does not exist!";
this.replaced = orderMap.getBy(tx(), this.order.getType(), this.order.getId());
if (this.replaced == null) {
String msg = "The Order {0} can not be updated as it does not exist!!";
msg = MessageFormat.format(msg, this.order.getLocator());
throw new StrolchException(msg);
}
this.replacedElement = orderMap.update(tx(), this.order);
orderMap.update(tx(), this.order);
this.updated = true;
}
@Override
public void undo() {
if (this.replacedElement != null && tx().isRollingBack()) {
tx().getOrderMap().update(tx(), this.replacedElement);
if (this.updated && tx().isRollingBack()) {
if (tx().isVersioningEnabled())
tx().getOrderMap().undoVersion(tx(), this.order);
else
tx().getOrderMap().update(tx(), this.replaced);
}
}
}

View File

@ -16,6 +16,7 @@
package li.strolch.command;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import li.strolch.agent.api.ComponentContainer;
@ -32,7 +33,8 @@ import li.strolch.utils.dbc.DBC;
public class UpdateResourceCollectionCommand extends Command {
private List<Resource> resources;
private List<Resource> replacedElements;
private List<Resource> replaced;
private boolean updated;
/**
* @param tx
@ -62,22 +64,32 @@ public class UpdateResourceCollectionCommand extends Command {
}
ResourceMap resourceMap = tx().getResourceMap();
this.replaced = new ArrayList<>();
for (Resource resource : this.resources) {
if (!resourceMap.hasElement(tx(), resource.getType(), resource.getId())) {
Resource r = resourceMap.getBy(tx(), resource.getType(), resource.getId());
if (r == null) {
String msg = "The Resource {0} can not be updated as it does not exist!";
msg = MessageFormat.format(msg, resource.getLocator());
throw new StrolchException(msg);
}
this.replaced.add(r);
}
this.replacedElements = resourceMap.updateAll(tx(), this.resources);
resourceMap.updateAll(tx(), this.resources);
this.updated = true;
}
@Override
public void undo() {
if (this.replacedElements != null && tx().isRollingBack()) {
ResourceMap resourceMap = tx().getResourceMap();
resourceMap.updateAll(tx(), this.replacedElements);
if (this.updated && tx().isRollingBack()) {
if (tx().isVersioningEnabled()) {
for (Resource resource : this.resources) {
tx().getResourceMap().undoVersion(tx(), resource);
}
} else {
tx().getResourceMap().updateAll(tx(), this.replaced);
}
}
}
}

View File

@ -31,7 +31,8 @@ import li.strolch.utils.dbc.DBC;
public class UpdateResourceCommand extends Command {
private Resource resource;
private Resource replacedElement;
private Resource replaced;
private boolean updated;
/**
* @param tx
@ -59,19 +60,24 @@ public class UpdateResourceCommand extends Command {
tx().lock(this.resource);
ResourceMap resourceMap = tx().getResourceMap();
if (!resourceMap.hasElement(tx(), this.resource.getType(), this.resource.getId())) {
this.replaced = resourceMap.getBy(tx(), this.resource.getType(), this.resource.getId());
if (this.replaced == null) {
String msg = "The Resource {0} can not be updated as it does not exist!!";
msg = MessageFormat.format(msg, this.resource.getLocator());
throw new StrolchException(msg);
}
this.replacedElement = resourceMap.update(tx(), this.resource);
resourceMap.update(tx(), this.resource);
this.updated = true;
}
@Override
public void undo() {
if (this.replacedElement != null && tx().isRollingBack()) {
tx().getResourceMap().update(tx(), this.replacedElement);
if (this.updated && tx().isRollingBack()) {
if (tx().isVersioningEnabled())
tx().getResourceMap().undoVersion(tx(), this.resource);
else
tx().getResourceMap().update(tx(), this.replaced);
}
}
}

View File

@ -18,6 +18,7 @@ package li.strolch.command.parameter;
import java.text.MessageFormat;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.visitor.UndoUpdateElementVisitor;
import li.strolch.command.visitor.UpdateElementVisitor;
import li.strolch.model.ParameterizedElement;
import li.strolch.model.StrolchRootElement;
@ -34,7 +35,6 @@ public class AddParameterCommand extends Command {
private ParameterizedElement element;
private Parameter<?> parameter;
private StrolchRootElement replacedElement;
/**
* @param container
@ -79,7 +79,7 @@ public class AddParameterCommand extends Command {
tx().lock(rootElement);
this.element.addParameter(this.parameter);
this.replacedElement = new UpdateElementVisitor(tx()).update(rootElement);
new UpdateElementVisitor(tx()).update(rootElement);
}
@Override
@ -87,11 +87,8 @@ public class AddParameterCommand extends Command {
if (this.parameter != null) {
if (this.element.hasParameter(this.parameter.getId())) {
this.element.removeParameter(this.parameter.getId());
new UndoUpdateElementVisitor(tx()).undo(this.element.getRootElement());
}
}
if (this.replacedElement != null && this.element != this.replacedElement) {
new UpdateElementVisitor(tx()).update(this.replacedElement);
}
}
}

View File

@ -18,6 +18,7 @@ package li.strolch.command.parameter;
import java.text.MessageFormat;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.visitor.UndoUpdateElementVisitor;
import li.strolch.command.visitor.UpdateElementVisitor;
import li.strolch.model.ParameterizedElement;
import li.strolch.model.StrolchRootElement;
@ -36,7 +37,6 @@ public class RemoveParameterCommand extends Command {
private String parameterId;
private Parameter<?> removedParameter;
private StrolchRootElement replacedElement;
/**
* @param container
@ -81,17 +81,14 @@ public class RemoveParameterCommand extends Command {
tx().lock(rootElement);
this.removedParameter = this.element.removeParameter(this.parameterId);
this.replacedElement = new UpdateElementVisitor(tx()).update(rootElement);
new UpdateElementVisitor(tx()).update(rootElement);
}
@Override
public void undo() {
if (this.removedParameter != null) {
this.element.addParameter(this.removedParameter);
}
if (this.replacedElement != null && this.element != this.replacedElement) {
new UpdateElementVisitor(tx()).update(this.replacedElement);
new UndoUpdateElementVisitor(tx()).undo(this.removedParameter.getRootElement());
}
}
}

View File

@ -16,6 +16,7 @@
package li.strolch.command.parameter;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.visitor.UndoUpdateElementVisitor;
import li.strolch.command.visitor.UpdateElementVisitor;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.parameter.Parameter;
@ -46,7 +47,7 @@ public class SetParameterCommand extends Command {
private Integer oldIndex;
private String oldValueAsString;
private StrolchRootElement replacedElement;
private boolean updated;
/**
* @param container
@ -151,7 +152,8 @@ public class SetParameterCommand extends Command {
}
if (hasChanges()) {
this.replacedElement = new UpdateElementVisitor(tx()).update(rootElement);
new UpdateElementVisitor(tx()).update(rootElement);
this.updated = true;
}
}
@ -185,8 +187,9 @@ public class SetParameterCommand extends Command {
visitor.setValue(this.parameter, this.oldValueAsString);
}
if (hasChanges() && this.replacedElement != null && this.replacedElement != this.parameter.getRootElement()) {
new UpdateElementVisitor(tx()).update(this.replacedElement);
if (hasChanges() && this.updated) {
StrolchRootElement rootElement = this.parameter.getRootElement();
new UndoUpdateElementVisitor(tx()).undo(rootElement);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.command.visitor;
import li.strolch.model.Order;
import li.strolch.model.Resource;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.activity.Activity;
import li.strolch.model.visitor.StrolchRootElementVisitor;
import li.strolch.persistence.api.StrolchTransaction;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class UndoUpdateElementVisitor implements StrolchRootElementVisitor<Void> {
private StrolchTransaction tx;
public UndoUpdateElementVisitor(StrolchTransaction tx) {
this.tx = tx;
}
public Void undo(StrolchRootElement rootElement) {
return rootElement.accept(this);
}
@Override
public Void visitOrder(Order order) {
if (tx.isVersioningEnabled())
this.tx.getOrderMap().undoVersion(this.tx, order);
else
this.tx.getOrderMap().update(this.tx, order);
return null;
}
@Override
public Void visitResource(Resource resource) {
if (tx.isVersioningEnabled())
this.tx.getResourceMap().undoVersion(this.tx, resource);
else
this.tx.getResourceMap().update(this.tx, resource);
return null;
}
@Override
public Void visitActivity(Activity activity) {
if (tx.isVersioningEnabled())
this.tx.getActivityMap().undoVersion(this.tx, activity);
else
this.tx.getActivityMap().update(this.tx, activity);
return null;
}
}

View File

@ -18,13 +18,14 @@ package li.strolch.command.visitor;
import li.strolch.model.Order;
import li.strolch.model.Resource;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.activity.Activity;
import li.strolch.model.visitor.StrolchRootElementVisitor;
import li.strolch.persistence.api.StrolchTransaction;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class UpdateElementVisitor implements StrolchRootElementVisitor<StrolchRootElement> {
public class UpdateElementVisitor implements StrolchRootElementVisitor<Void> {
private StrolchTransaction tx;
@ -32,17 +33,25 @@ public class UpdateElementVisitor implements StrolchRootElementVisitor<StrolchRo
this.tx = tx;
}
public StrolchRootElement update(StrolchRootElement rootElement) {
public Void update(StrolchRootElement rootElement) {
return rootElement.accept(this);
}
@Override
public StrolchRootElement visitOrder(Order order) {
return this.tx.getOrderMap().update(this.tx, order);
public Void visitOrder(Order order) {
this.tx.getOrderMap().update(this.tx, order);
return null;
}
@Override
public StrolchRootElement visitResource(Resource resource) {
return this.tx.getResourceMap().update(this.tx, resource);
public Void visitResource(Resource resource) {
this.tx.getResourceMap().update(this.tx, resource);
return null;
}
@Override
public Void visitActivity(Activity activity) {
this.tx.getActivityMap().update(this.tx, activity);
return null;
}
}

View File

@ -22,9 +22,16 @@ import static li.strolch.service.test.AbstractRealmServiceTest.REALM_TRANSIENT;
import static li.strolch.service.test.AbstractRealmServiceTest.RUNTIME_PATH;
import static li.strolch.service.test.AbstractRealmServiceTest.dropSchema;
import static li.strolch.service.test.AbstractRealmServiceTest.importFromXml;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.persistence.api.StrolchTransaction;
@ -33,12 +40,6 @@ import li.strolch.service.api.Command;
import li.strolch.service.api.ServiceHandler;
import li.strolch.testbase.runtime.RuntimeMock;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -48,9 +49,6 @@ public abstract class AbstractRealmCommandTest {
protected static Certificate certificate;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@BeforeClass
public static void beforeClass() throws Exception {
@ -80,17 +78,27 @@ public abstract class AbstractRealmCommandTest {
protected abstract Command getCommandInstance(ComponentContainer container, StrolchTransaction tx);
protected void doCommandAsFail(String realmName) {
this.expectedException.expect(RuntimeException.class);
this.expectedException.expectMessage("Fail on purpose after do command!");
protected abstract void validateAfterCommand(ComponentContainer container, StrolchTransaction tx);
protected abstract void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx);
protected void doCommandAsFail(String realmName) {
StrolchRealm realm = runtimeMock.getContainer().getRealm(realmName);
boolean caught = false;
try (StrolchTransaction tx = realm.openTx(certificate, "test")) {
Command command = getCommandInstance(runtimeMock.getContainer(), tx);
FailCommandFacade commandFacade = new FailCommandFacade(runtimeMock.getContainer(), tx, command);
tx.addCommand(commandFacade);
tx.commitOnClose();
} catch (RuntimeException e) {
assertThat(e.getMessage(), containsString("Fail on purpose after do command!"));
caught = true;
}
assertTrue(caught);
try (StrolchTransaction tx = realm.openTx(certificate, "test")) {
validateAfterCommandFailed(runtimeMock.getContainer(), tx);
}
}
@ -101,21 +109,28 @@ public abstract class AbstractRealmCommandTest {
tx.addCommand(command);
tx.commitOnClose();
}
try (StrolchTransaction tx = realm.openTx(certificate, "test")) {
validateAfterCommand(runtimeMock.getContainer(), tx);
}
}
@Test
public void shouldFailCommandTransient() {
public void shouldDoCommandTransient() {
doCommandAsFail(REALM_TRANSIENT);
doCommand(REALM_TRANSIENT);
}
@Test
public void shouldFailCommandCached() {
public void shouldDoCommandCached() {
doCommandAsFail(REALM_CACHED);
doCommand(REALM_CACHED);
}
@Test
public void shouldFailCommandTransactional() {
public void shouldDoCommandTransactional() {
doCommandAsFail(REALM_TRANSACTIONAL);
doCommand(REALM_TRANSACTIONAL);
}
private class FailCommandFacade extends Command {

View File

@ -15,17 +15,20 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Order;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -48,4 +51,18 @@ public class AddOrderCollectionCommandTest extends AbstractRealmCommandTest {
command.setOrders(this.orders);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
for (Order order : orders) {
assertTrue(tx.getOrderMap().hasElement(tx, order.getType(), order.getId()));
}
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
for (Order order : orders) {
assertFalse(tx.getOrderMap().hasElement(tx, order.getType(), order.getId()));
}
}
}

View File

@ -15,14 +15,17 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Order;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -42,4 +45,14 @@ public class AddOrderCommandTest extends AbstractRealmCommandTest {
command.setOrder(this.order);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
assertTrue(tx.getOrderMap().hasElement(tx, this.order.getType(), this.order.getId()));
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
assertFalse(tx.getOrderMap().hasElement(tx, this.order.getType(), this.order.getId()));
}
}

View File

@ -15,17 +15,20 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Resource;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -48,4 +51,18 @@ public class AddResourceCollectionCommandTest extends AbstractRealmCommandTest {
command.setResources(this.resources);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
for (Resource resource : resources) {
assertTrue(tx.getResourceMap().hasElement(tx, resource.getType(), resource.getId()));
}
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
for (Resource resource : resources) {
assertFalse(tx.getResourceMap().hasElement(tx, resource.getType(), resource.getId()));
}
}
}

View File

@ -15,14 +15,17 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Resource;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -42,4 +45,14 @@ public class AddResourceCommandTest extends AbstractRealmCommandTest {
command.setResource(this.resource);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
assertTrue(tx.getResourceMap().hasElement(tx, this.resource.getType(), this.resource.getId()));
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
assertFalse(tx.getResourceMap().hasElement(tx, this.resource.getType(), this.resource.getId()));
}
}

View File

@ -15,9 +15,14 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.Locator;
import li.strolch.model.Order;
@ -25,8 +30,6 @@ import li.strolch.model.Tags;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -54,4 +57,18 @@ public class RemoveOrderCollectionCommandTest extends AbstractRealmCommandTest {
command.setOrders(orders);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
for (Locator locator : locators) {
assertFalse(tx.getOrderMap().hasElement(tx, locator.get(1), locator.get(2)));
}
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
for (Locator locator : locators) {
assertTrue(tx.getOrderMap().hasElement(tx, locator.get(1), locator.get(2)));
}
}
}

View File

@ -15,6 +15,11 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.Locator;
import li.strolch.model.Order;
@ -22,8 +27,6 @@ import li.strolch.model.Tags;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -45,4 +48,14 @@ public class RemoveOrderCommandTest extends AbstractRealmCommandTest {
command.setOrder(order);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
assertFalse(tx.getOrderMap().hasElement(tx, "TestType", "@3"));
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
assertTrue(tx.getOrderMap().hasElement(tx, "TestType", "@3"));
}
}

View File

@ -15,9 +15,14 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.Locator;
import li.strolch.model.Resource;
@ -25,8 +30,6 @@ import li.strolch.model.Tags;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -54,4 +57,18 @@ public class RemoveResourceCollectionCommandTest extends AbstractRealmCommandTes
command.setResources(resources);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
for (Locator locator : locators) {
assertFalse(tx.getResourceMap().hasElement(tx, locator.get(1), locator.get(2)));
}
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
for (Locator locator : locators) {
assertTrue(tx.getResourceMap().hasElement(tx, locator.get(1), locator.get(2)));
}
}
}

View File

@ -15,6 +15,11 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.Locator;
import li.strolch.model.Resource;
@ -22,8 +27,6 @@ import li.strolch.model.Tags;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -45,4 +48,14 @@ public class RemoveResourceCommandTest extends AbstractRealmCommandTest {
command.setResource(resource);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
assertFalse(tx.getResourceMap().hasElement(tx, "Enumeration", "sex"));
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
assertTrue(tx.getResourceMap().hasElement(tx, "Enumeration", "sex"));
}
}

View File

@ -15,17 +15,19 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Order;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -36,6 +38,7 @@ public class UpdateOrderCollectionCommandTest extends AbstractRealmCommandTest {
@Before
public void before() {
this.orders = new ArrayList<>();
// we create elements with the same id as already exists!
this.orders.add(ModelGenerator.createOrder("@1", "Modified Test Order", "TestType"));
this.orders.add(ModelGenerator.createOrder("@2", "Modified Test Order", "TestType"));
this.orders.add(ModelGenerator.createOrder("@3", "Modified Test Order", "TestType"));
@ -48,4 +51,20 @@ public class UpdateOrderCollectionCommandTest extends AbstractRealmCommandTest {
command.setOrders(this.orders);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
for (Order order : orders) {
Order o = tx.getOrderBy(order.getType(), order.getId());
assertEquals("Modified Test Order", o.getName());
}
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
for (Order order : orders) {
Order o = tx.getOrderBy(order.getType(), order.getId());
assertEquals("Test Name", o.getName());
}
}
}

View File

@ -42,4 +42,16 @@ public class UpdateOrderCommandTest extends AbstractRealmCommandTest {
command.setOrder(this.order);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
// TODO Auto-generated method stub
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
// TODO Auto-generated method stub
}
}

View File

@ -15,17 +15,19 @@
*/
package li.strolch.command;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Resource;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -48,4 +50,20 @@ public class UpdateResourceCollectionCommandTest extends AbstractRealmCommandTes
command.setResources(this.resources);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
for (Resource resource : this.resources) {
Resource r = tx.getResourceBy(resource.getType(), resource.getId());
assertEquals("Modified Enumeration", r.getName());
}
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
for (Resource resource : this.resources) {
Resource r = tx.getResourceBy(resource.getType(), resource.getId());
assertEquals(r.getId(), r.getName().toLowerCase());
}
}
}

View File

@ -42,4 +42,16 @@ public class UpdateResourceCommandTest extends AbstractRealmCommandTest {
command.setResource(this.resource);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
// TODO Auto-generated method stub
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
// TODO Auto-generated method stub
}
}

View File

@ -15,6 +15,11 @@
*/
package li.strolch.command.parameter;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.AbstractRealmCommandTest;
import li.strolch.model.Locator;
@ -24,8 +29,6 @@ import li.strolch.model.parameter.Parameter;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -50,4 +53,16 @@ public class AddParameterCommandTest extends AbstractRealmCommandTest {
command.setParameter(this.parameter);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
ParameterizedElement element = tx.findElement(this.locator);
assertTrue(element.hasParameter(this.parameter.getId()));
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
ParameterizedElement element = tx.findElement(this.locator);
assertFalse(element.hasParameter(this.parameter.getId()));
}
}

View File

@ -15,6 +15,11 @@
*/
package li.strolch.command.parameter;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.AbstractRealmCommandTest;
import li.strolch.model.Locator;
@ -22,8 +27,6 @@ import li.strolch.model.ParameterizedElement;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -48,4 +51,16 @@ public class RemoveParameterCommandTest extends AbstractRealmCommandTest {
command.setParameterId(this.parameterId);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
ParameterizedElement element = tx.findElement(this.locator);
assertFalse(element.hasParameter(this.parameterId));
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
ParameterizedElement element = tx.findElement(this.locator);
assertTrue(element.hasParameter(this.parameterId));
}
}

View File

@ -15,6 +15,10 @@
*/
package li.strolch.command.parameter;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.AbstractRealmCommandTest;
import li.strolch.model.Locator;
@ -22,8 +26,6 @@ import li.strolch.model.parameter.Parameter;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.service.api.Command;
import org.junit.Before;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -31,6 +33,7 @@ public class SetParameterCommandTest extends AbstractRealmCommandTest {
private Locator locator;
private String valueAsString;
private String originalValue;
@Before
public void before() {
@ -42,10 +45,23 @@ public class SetParameterCommandTest extends AbstractRealmCommandTest {
protected Command getCommandInstance(ComponentContainer container, StrolchTransaction tx) {
Parameter<?> parameter = tx.findElement(this.locator);
this.originalValue = parameter.getValueAsString();
SetParameterCommand command = new SetParameterCommand(container, tx);
command.setValueAsString(this.valueAsString);
command.setParameter(parameter);
return command;
}
@Override
protected void validateAfterCommand(ComponentContainer container, StrolchTransaction tx) {
Parameter<?> parameter = tx.findElement(this.locator);
assertEquals(this.valueAsString, parameter.getValueAsString());
}
@Override
protected void validateAfterCommandFailed(ComponentContainer container, StrolchTransaction tx) {
Parameter<?> parameter = tx.findElement(this.locator);
assertEquals(this.originalValue, parameter.getValueAsString());
}
}

View File

@ -15,15 +15,15 @@
*/
package li.strolch.testbase.runtime;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.privilege.PrivilegeHandler;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractModelTest {
protected static final Logger logger = LoggerFactory.getLogger(AbstractModelTest.class);
@ -97,9 +97,43 @@ public abstract class AbstractModelTest {
testRunner.runBulkOperationTests();
}
@Test
public void shouldCreateActivities() {
ActivityModelTestRunner testRunner = new ActivityModelTestRunner(getRuntimeMock(), this.realmName);
testRunner.runCreateActivityTest();
}
@Test
public void shouldQueryActivitySizes() {
ActivityModelTestRunner testRunner = new ActivityModelTestRunner(getRuntimeMock(), this.realmName);
testRunner.runQuerySizeTest();
}
@Test
public void shouldActivityCrud() {
ActivityModelTestRunner testRunner = new ActivityModelTestRunner(getRuntimeMock(), this.realmName);
testRunner.runCrudTests();
}
@Test
public void shouldActivityPerformBulkOperations() {
ActivityModelTestRunner testRunner = new ActivityModelTestRunner(getRuntimeMock(), this.realmName);
testRunner.runBulkOperationTests();
}
@Test
public void shouldTestAudits() {
AuditModelTestRunner testRunner = new AuditModelTestRunner(getRuntimeMock(), this.realmName);
testRunner.runTestForAudits();
}
@Test
public void shouldTestVersioning() {
VersioningTestRunner testRunner = new VersioningTestRunner(getRuntimeMock());
testRunner.runTestsForVersioning();
}
}

View File

@ -0,0 +1,276 @@
/*
* Copyright 2015 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.testbase.runtime;
import static li.strolch.model.ModelGenerator.BAG_ID;
import static li.strolch.model.ModelGenerator.PARAM_STRING_ID;
import static li.strolch.model.ModelGenerator.createActivities;
import static li.strolch.model.ModelGenerator.createActivity;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import li.strolch.agent.api.ActivityMap;
import li.strolch.agent.impl.DataStoreMode;
import li.strolch.model.activity.Activity;
import li.strolch.model.parameter.Parameter;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.privilege.PrivilegeHandler;
@SuppressWarnings("nls")
public class ActivityModelTestRunner {
private static final String ID = "@testActivity";
private static final String NAME = "Test Activity";
private static final String TYPE = "Produce";
private RuntimeMock runtimeMock;
private String realmName;
private Certificate certificate;
public ActivityModelTestRunner(RuntimeMock runtimeMock, String realmName) {
this.runtimeMock = runtimeMock;
this.realmName = realmName;
PrivilegeHandler privilegeHandler = runtimeMock.getContainer().getPrivilegeHandler();
this.certificate = privilegeHandler.authenticate("test", "test".getBytes());
}
public void runCreateActivityTest() {
// create
Activity newActivity = createActivity("MyTestActivity", "Test Name", "TestType"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
tx.getActivityMap().add(tx, newActivity);
tx.commitOnClose();
}
}
public void runQuerySizeTest() {
// remove all
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
tx.getActivityMap().removeAll(tx, tx.getActivityMap().getAllElements(tx));
tx.commitOnClose();
}
// create three activities
Activity activity1 = createActivity("myTestActivity1", "Test Name", "QTestType1"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
Activity activity2 = createActivity("myTestActivity2", "Test Name", "QTestType2"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
Activity activity3 = createActivity("myTestActivity3", "Test Name", "QTestType3"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
tx.getActivityMap().add(tx, activity1);
tx.getActivityMap().add(tx, activity2);
tx.getActivityMap().add(tx, activity3);
tx.commitOnClose();
}
// query size
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
long size = tx.getActivityMap().querySize(tx);
assertEquals("Should have three objects", 3, size);
size = tx.getActivityMap().querySize(tx, "QTestType1");
assertEquals("Should have only one object of type 'QTestType1'", 1, size);
size = tx.getActivityMap().querySize(tx, "QTestType2");
assertEquals("Should have only one object of type 'QTestType1'", 1, size);
size = tx.getActivityMap().querySize(tx, "QTestType3");
assertEquals("Should have only one object of type 'QTestType1'", 1, size);
size = tx.getActivityMap().querySize(tx, "NonExistingType");
assertEquals("Should have zero objects of type 'NonExistingType'", 0, size);
}
}
public void runCrudTests() {
// create
Activity newActivity = createActivity(ID, NAME, TYPE);
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
tx.getActivityMap().add(tx, newActivity);
tx.commitOnClose();
}
// read
Activity readActivity = null;
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
readActivity = tx.getActivityMap().getBy(tx, TYPE, ID);
}
assertNotNull("Should read Activity with id " + ID, readActivity);
// update
Parameter<String> sParam = readActivity.getParameter(BAG_ID, PARAM_STRING_ID);
String newStringValue = "Giddiya!";
sParam.setValue(newStringValue);
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
tx.getActivityMap().update(tx, readActivity);
tx.commitOnClose();
}
// read updated
Activity updatedActivity = null;
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
updatedActivity = tx.getActivityMap().getBy(tx, TYPE, ID);
}
assertNotNull("Should read Activity with id " + ID, updatedActivity);
if (this.runtimeMock.getRealm(this.realmName).getMode() != DataStoreMode.CACHED)
assertFalse("Objects can't be the same reference after re-reading!", readActivity == updatedActivity);
Parameter<String> updatedParam = readActivity.getParameter(BAG_ID, PARAM_STRING_ID);
assertEquals(newStringValue, updatedParam.getValue());
// delete
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
tx.getActivityMap().remove(tx, readActivity);
tx.commitOnClose();
}
// fail to re-read
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test");) {
Activity activity = tx.getActivityMap().getBy(tx, TYPE, ID);
assertNull("Should no read Activity with id " + ID, activity);
}
}
public void runBulkOperationTests() {
// create 15 activities
List<Activity> activities = new ArrayList<>();
activities.addAll(createActivities(activities.size(), 5, "@", "My Activity", "MyType1"));
activities.addAll(createActivities(activities.size(), 5, "@", "Other Activity", "MyType2"));
activities.addAll(createActivities(activities.size(), 5, "@", "Further Activity", "MyType3"));
// sort them so we know which activity our objects are
Comparator<Activity> comparator = new Comparator<Activity>() {
@Override
public int compare(Activity o1, Activity o2) {
return o1.getId().compareTo(o2.getId());
}
};
Collections.sort(activities, comparator);
// first clear the map, so that we have a clean state
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
ActivityMap activityMap = tx.getActivityMap();
activityMap.removeAll(tx, activityMap.getAllElements(tx));
tx.commitOnClose();
}
{
// make sure it is empty
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
ActivityMap activityMap = tx.getActivityMap();
assertEquals(0, activityMap.querySize(tx));
}
// now add some activities
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
tx.getActivityMap().addAll(tx, activities);
tx.commitOnClose();
}
// make sure we have our expected size
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
ActivityMap activityMap = tx.getActivityMap();
assertEquals(activities.size(), activityMap.querySize(tx));
assertEquals(5, activityMap.querySize(tx, "MyType3"));
}
// now use the remove all by type
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
tx.getActivityMap().removeAllBy(tx, "MyType3");
tx.commitOnClose();
}
// again make sure we have our expected size
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
ActivityMap activityMap = tx.getActivityMap();
assertEquals(activities.size() - 5, activityMap.querySize(tx));
assertEquals(0, activityMap.querySize(tx, "MyType3"));
}
// now use the remove all
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
long removed = tx.getActivityMap().removeAll(tx);
assertEquals(activities.size() - 5, removed);
tx.commitOnClose();
}
// again make sure we have our expected size
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
ActivityMap activityMap = tx.getActivityMap();
assertEquals(0, activityMap.querySize(tx));
}
}
// remove the version
activities.forEach(t -> t.setVersion(null));
// now add all again
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
tx.getActivityMap().addAll(tx, activities);
tx.commitOnClose();
}
Set<String> expectedTypes = new HashSet<>();
expectedTypes.add("MyType1");
expectedTypes.add("MyType2");
expectedTypes.add("MyType3");
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
List<Activity> allActivities = tx.getActivityMap().getAllElements(tx);
Collections.sort(allActivities, comparator);
assertEquals(activities, allActivities);
}
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
ActivityMap activityMap = tx.getActivityMap();
Set<String> types = activityMap.getTypes(tx);
assertEquals(expectedTypes, types);
Set<String> keySet = activityMap.getAllKeys(tx);
assertEquals(15, keySet.size());
for (String type : types) {
Set<String> idsByType = activityMap.getKeysBy(tx, type);
assertEquals(5, idsByType.size());
List<Activity> activitiesByType = activityMap.getElementsBy(tx, type);
assertEquals(5, activitiesByType.size());
}
}
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
Activity activity = tx.getActivityMap().getBy(tx, "MyType1", "@00000001");
assertNotNull(activity);
activity = tx.getActivityMap().getBy(tx, "MyType2", "@00000006");
assertNotNull(activity);
activity = tx.getActivityMap().getBy(tx, "MyType3", "@00000011");
assertNotNull(activity);
}
}
}

View File

@ -226,6 +226,9 @@ public class OrderModelTestRunner {
}
}
// remove the version
orders.forEach(t -> t.setVersion(null));
// now add all again
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
tx.getOrderMap().addAll(tx, orders);

View File

@ -226,6 +226,8 @@ public class ResourceModelTestRunner {
}
}
resources.forEach(t -> t.setVersion(null));
// now add all again
try (StrolchTransaction tx = this.runtimeMock.getRealm(this.realmName).openTx(this.certificate, "test")) {
tx.getResourceMap().addAll(tx, resources);

View File

@ -0,0 +1,195 @@
package li.strolch.testbase.runtime;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.ModelGenerator;
import li.strolch.model.Resource;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.privilege.PrivilegeHandler;
public class VersioningTestRunner {
private RuntimeMock runtimeMock;
private Certificate certificate;
public VersioningTestRunner(RuntimeMock runtimeMock) {
this.runtimeMock = runtimeMock;
PrivilegeHandler privilegeHandler = runtimeMock.getContainer().getPrivilegeHandler();
this.certificate = privilegeHandler.authenticate("test", "test".getBytes());
}
public void runTestsForVersioning() {
ComponentContainer container = runtimeMock.getContainer();
// initialize by adding a resource
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = ModelGenerator.createResource("MyTestResource", "Test Name", "TestType");
tx.getResourceMap().add(tx, res1);
// must be first version
assertEquals(0, res1.getVersion().getVersion());
tx.commitOnClose();
}
// first make sure that the we can't change anything without updating the model
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
// must be first version
assertEquals(0, res1.getVersion().getVersion());
res1.setName("Something");
tx.commitOnClose();
}
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals("Test Name", res1.getName());
tx.commitOnClose();
}
// now do a change
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
res1.setName("Something");
tx.getResourceMap().update(tx, res1);
tx.commitOnClose();
}
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals("Something", res1.getName());
// version must be incremented
assertEquals(1, res1.getVersion().getVersion());
tx.commitOnClose();
}
// now revert the change
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource revertedVersion = tx.getResourceMap().revertToVersion(tx, "TestType", "MyTestResource", 0);
assertEquals("Test Name", revertedVersion.getName());
// version must be incremented
assertEquals(2, revertedVersion.getVersion().getVersion());
tx.commitOnClose();
}
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals("Test Name", res1.getName());
// version must be incremented
assertEquals(2, res1.getVersion().getVersion());
tx.commitOnClose();
}
// undo a version in same TX
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
// create a new version:
res1.setName("Version 3");
tx.getResourceMap().update(tx, res1);
// version must be incremented
assertEquals(3, res1.getVersion().getVersion());
// now undo
tx.getResourceMap().undoVersion(tx, res1);
// and validate we have again version 2
res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals("Test Name", res1.getName());
assertEquals(2, res1.getVersion().getVersion());
tx.commitOnClose();
}
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals("Test Name", res1.getName());
// version must be incremented
assertEquals(2, res1.getVersion().getVersion());
tx.commitOnClose();
}
// undo all versions
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1;
// undo three times as we have version 0, 1, 2
res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals(2, res1.getVersion().getVersion());
tx.getResourceMap().undoVersion(tx, res1);
res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals(1, res1.getVersion().getVersion());
tx.getResourceMap().undoVersion(tx, res1);
res1 = tx.getResourceBy("TestType", "MyTestResource", true);
assertEquals(0, res1.getVersion().getVersion());
tx.getResourceMap().undoVersion(tx, res1);
// and validate all are deleted
assertFalse(tx.getResourceMap().hasElement(tx, "TestType", "MyTestResource"));
tx.commitOnClose();
}
// do a deletion
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = ModelGenerator.createResource("ball", "Red Ball", "Ball");
assertNull(res1.getVersion());
tx.getResourceMap().add(tx, res1);
assertEquals(0, res1.getVersion().getVersion());
tx.commitOnClose();
}
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("Ball", "ball", true);
assertEquals("Red Ball", res1.getName());
tx.getResourceMap().remove(tx, res1);
// version must be incremented
assertEquals(1, res1.getVersion().getVersion());
res1 = tx.getResourceBy("Ball", "ball");
assertNull(res1);
assertFalse(tx.getResourceMap().hasElement(tx, "Ball", "ball"));
tx.commitOnClose();
}
// restore a version manually
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("Ball", "ball");
assertNull(res1);
List<Resource> versions = tx.getResourceMap().getVersionsFor(tx, "Ball", "ball");
assertEquals(2, versions.size());
res1 = versions.get(versions.size() - 1);
assertTrue(res1.getVersion().isDeleted());
tx.getResourceMap().add(tx, res1);
assertEquals(2, res1.getVersion().getVersion());
tx.commitOnClose();
}
try (StrolchTransaction tx = container.getRealm(certificate).openTx(certificate, VersioningTestRunner.class)) {
Resource res1 = tx.getResourceBy("Ball", "ball");
assertNotNull(res1);
assertEquals(2, res1.getVersion().getVersion());
tx.commitOnClose();
}
}
}