diff --git a/ch.eitchnet.utils b/ch.eitchnet.utils index cde6eb652..401052a5e 160000 --- a/ch.eitchnet.utils +++ b/ch.eitchnet.utils @@ -1 +1 @@ -Subproject commit cde6eb652ec2c12ce22c8cb21a16589d56f8a49f +Subproject commit 401052a5ea5844673615ba4c255b6faa96000adc diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/api/LockHandler.java b/li.strolch.agent/src/main/java/li/strolch/agent/api/LockHandler.java index 8136efd81..794daee1f 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/api/LockHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/api/LockHandler.java @@ -51,8 +51,10 @@ public interface LockHandler { * * @param element * the element for which a {@link Lock} on its {@link Locator} is to be created and/or locked + * + * @throws StrolchLockException */ - public void lock(StrolchRootElement element); + public void lock(StrolchRootElement element) throws StrolchLockException; /** *

@@ -67,8 +69,10 @@ public interface LockHandler { * * @param element * the element for which the current/last {@link Lock} is to be unlocked + * + * @throws StrolchLockException */ - public void unlock(StrolchRootElement element); + public void unlock(StrolchRootElement element) throws StrolchLockException; /** * Releases the lock on the given element, by unlocking all locks, i.e. after this method is called, no lock will be @@ -76,6 +80,8 @@ public interface LockHandler { * * @param element * the element for which the {@link Lock} on the {@link Locator} is to be released + * + * @throws StrolchLockException */ - public void releaseLock(StrolchRootElement element); + public void releaseLock(StrolchRootElement element) throws StrolchLockException; } diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/api/StrolchLockException.java b/li.strolch.agent/src/main/java/li/strolch/agent/api/StrolchLockException.java new file mode 100644 index 000000000..94d0ecade --- /dev/null +++ b/li.strolch.agent/src/main/java/li/strolch/agent/api/StrolchLockException.java @@ -0,0 +1,19 @@ +package li.strolch.agent.api; + +import li.strolch.exception.StrolchException; + +/** + * @author Robert von Burg + */ +public class StrolchLockException extends StrolchException { + + private static final long serialVersionUID = 1L; + + public StrolchLockException(String message, Throwable cause) { + super(message, cause); + } + + public StrolchLockException(String message) { + super(message); + } +} diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java index bd2458f51..0b25e83c5 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import li.strolch.agent.api.LockHandler; -import li.strolch.exception.StrolchException; +import li.strolch.agent.api.StrolchLockException; import li.strolch.model.Locator; import li.strolch.model.StrolchRootElement; @@ -60,7 +60,7 @@ public class DefaultLockHandler implements LockHandler { } @Override - public void lock(StrolchRootElement element) { + public void lock(StrolchRootElement element) throws StrolchLockException { Locator locator = element.getLocator(); ReentrantLock lock = this.lockMap.get(locator); if (lock == null) { @@ -72,7 +72,7 @@ public class DefaultLockHandler implements LockHandler { } @Override - public void unlock(StrolchRootElement element) { + public void unlock(StrolchRootElement element) throws StrolchLockException { Locator locator = element.getLocator(); ReentrantLock lock = this.lockMap.get(locator); if (lock == null || !lock.isHeldByCurrentThread()) { @@ -83,7 +83,7 @@ public class DefaultLockHandler implements LockHandler { } @Override - public void releaseLock(StrolchRootElement element) { + public void releaseLock(StrolchRootElement element) throws StrolchLockException { Locator locator = element.getLocator(); ReentrantLock lock = this.lockMap.get(locator); if (lock == null || !lock.isHeldByCurrentThread()) { @@ -96,28 +96,33 @@ public class DefaultLockHandler implements LockHandler { /** * @see java.util.concurrent.locks.ReentrantLock#tryLock(long, TimeUnit) */ - private void lock(TimeUnit timeUnit, long tryLockTime, ReentrantLock lock, StrolchRootElement element) { + private void lock(TimeUnit timeUnit, long tryLockTime, ReentrantLock lock, StrolchRootElement element) + throws StrolchLockException { try { if (!lock.tryLock(tryLockTime, timeUnit)) { String msg = "Failed to acquire lock after {0}s for {1}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, timeUnit.toSeconds(tryLockTime), element.getLocator()); - throw new StrolchException(msg); + throw new StrolchLockException(msg); } if (logger.isDebugEnabled()) logger.debug("locked " + toString()); //$NON-NLS-1$ } catch (InterruptedException e) { - throw new StrolchException("Thread interrupted: " + e.getMessage(), e); //$NON-NLS-1$ + throw new StrolchLockException(e.getMessage(), e); //$NON-NLS-1$ } } /** * @see java.util.concurrent.locks.ReentrantLock#unlock() */ - private void unlock(ReentrantLock lock) { - lock.unlock(); - if (logger.isDebugEnabled()) - logger.debug("unlocking " + toString()); //$NON-NLS-1$ + private void unlock(ReentrantLock lock) throws StrolchLockException { + try { + lock.unlock(); + if (logger.isDebugEnabled()) + logger.debug("unlocking " + toString()); //$NON-NLS-1$ + } catch (IllegalMonitorStateException e) { + throw new StrolchLockException(e.getMessage(), e); //$NON-NLS-1$ + } } /** diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java index 6436b3cb5..9ec9ae206 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java @@ -27,6 +27,7 @@ import li.strolch.agent.api.ObserverHandler; import li.strolch.agent.api.OrderMap; import li.strolch.agent.api.ResourceMap; import li.strolch.agent.api.StrolchAgent; +import li.strolch.agent.api.StrolchLockException; import li.strolch.agent.api.StrolchRealm; import li.strolch.agent.impl.AuditingAuditMapFacade; import li.strolch.agent.impl.AuditingOrderMap; @@ -60,6 +61,7 @@ import li.strolch.model.timevalue.IValue; import li.strolch.model.visitor.NoStrategyOrderVisitor; import li.strolch.model.visitor.NoStrategyResourceVisitor; import li.strolch.persistence.inmemory.InMemoryTransaction; +import li.strolch.runtime.StrolchConstants; import li.strolch.runtime.privilege.PrivilegeHandler; import li.strolch.service.api.Command; @@ -159,45 +161,35 @@ public abstract class AbstractTransaction implements StrolchTransaction { this.closeStrategy.close(this); } - /** - * @param suppressUpdates - * the suppressUpdates to set - */ + @Override public void setSuppressUpdates(boolean suppressUpdates) { this.suppressUpdates = suppressUpdates; } - /** - * @return the suppressUpdates - */ + @Override public boolean isSuppressUpdates() { return this.suppressUpdates; } - /** - * @param suppressAudits - * the suppressAudits to set - */ + @Override public void setSuppressAudits(boolean suppressAudits) { this.suppressAudits = suppressAudits; } - /** - * @return the suppressAudits - */ + @Override public boolean isSuppressAudits() { return this.suppressAudits; } @Override - public void lock(T element) { + public void lock(T element) throws StrolchLockException { this.realm.lock(element); this.lockedElements.add(element); } @Override - public void unlock(T element) { - this.realm.unlock(element); + public void releaseLock(T element) throws StrolchLockException { + this.realm.releaseLock(element); this.lockedElements.remove(element); } @@ -361,6 +353,26 @@ public abstract class AbstractTransaction implements StrolchTransaction { throw new StrolchException(MessageFormat.format(msg, locator, stateOrBag)); } + @Override + public Resource getResourceTemplate(String type) { + return getResourceBy(StrolchConstants.TEMPLATE, type); + } + + @Override + public Resource getResourceTemplate(String type, boolean assertExists) throws StrolchException { + return getResourceBy(StrolchConstants.TEMPLATE, type, assertExists); + } + + @Override + public Order getOrderTemplate(String type) { + return getOrderBy(StrolchConstants.TEMPLATE, type); + } + + @Override + public Order getOrderTemplate(String type, boolean assertExists) throws StrolchException { + return getOrderBy(StrolchConstants.TEMPLATE, type, assertExists); + } + @Override public Order getOrderBy(String type, String id) { return getOrderBy(type, id, false); diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java index 188d64103..1edd1aa80 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/StrolchTransaction.java @@ -20,6 +20,10 @@ import java.util.List; import li.strolch.agent.api.AuditTrail; import li.strolch.agent.api.OrderMap; import li.strolch.agent.api.ResourceMap; +import li.strolch.agent.api.StrolchComponent; +import li.strolch.agent.api.StrolchLockException; +import li.strolch.agent.api.StrolchRealm; +import li.strolch.agent.impl.DataStoreMode; import li.strolch.exception.StrolchException; import li.strolch.model.Locator; import li.strolch.model.Order; @@ -29,6 +33,7 @@ import li.strolch.model.Resource; import li.strolch.model.ResourceVisitor; import li.strolch.model.StrolchElement; import li.strolch.model.StrolchRootElement; +import li.strolch.model.Tags; import li.strolch.model.audit.AccessType; import li.strolch.model.audit.Audit; import li.strolch.model.audit.AuditQuery; @@ -41,55 +46,341 @@ import li.strolch.model.query.ResourceQuery; import li.strolch.runtime.StrolchConstants; import li.strolch.service.api.Command; +/** + *

+ * {@link StrolchTransaction} is the central element in Strolch. It gives the developer access to the Strolch model and + * performs all the required actions to keep the model consistent etc. + *

+ * + *

+ * A Strolch transaction is performed as follows as it is an {@link AutoCloseable} implementation + *

+ * + * StrolchAgent strolchAgent = getStrolchAgent(); + * StrolchRealm realm = strolchAgent.getContainer().getRealm(StrolchConstants.DEFAULT_REALM); + * try(StrolchTransaction tx = realm.openTx(certificate, getClass())){ + * // do work e.g. add commands + * } + * + * + *

+ * A {@link StrolchTransaction} is always opened for a specific realm, should no specific realms be configured, then the + * {@link StrolchConstants#DEFAULT_REALM} is automatically created. + *

+ * + *

+ * A {@link StrolchTransaction} takes care of the following: + *

+ * + * + * @see AbstractTransaction + * + * @author Robert von Burg + */ public interface StrolchTransaction extends AutoCloseable { + /** + * Returns the name of the {@link StrolchRealm} for which this transaction was opened + * + * @return the name of the {@link StrolchRealm} for which this transaction was opened + */ public String getRealmName(); + /** + * Returns a reference to the {@link AuditTrail} for the {@link StrolchRealm} for which this transaction was opened + * + * @return the {@link AuditTrail} + */ + public AuditTrail getAuditTrail(); + + /** + * Returns a reference to the {@link ResourceMap} for the {@link StrolchRealm} for which this transaction was opened + * + * @return the {@link ResourceMap} + */ + public ResourceMap getResourceMap(); + + /** + * Returns a reference to the {@link OrderMap} for the {@link StrolchRealm} for which this transaction was opened + * + * @return the {@link OrderMap} + */ + public OrderMap getOrderMap(); + + /** + * Returns the {@link PersistenceHandler}. If the {@link StrolchRealm} is not running in + * {@link DataStoreMode#TRANSIENT} mode, then the {@link PersistenceHandler} will be a {@link StrolchComponent}, + * otherwise it will be the internal in memory persistence handler + * + * @return the {@link PersistenceHandler} + */ + public PersistenceHandler getPersistenceHandler(); + + /** + * Sets the strategy to be used when closing the transaction when {@link #close()} is called. The default is + * {@link TransactionCloseStrategy#COMMIT}, but if the user of the transaction decides it wants to use a different + * strategy, then it may set it using this method + * + * @param closeStrategy + * the new {@link TransactionCloseStrategy} to use + */ public void setCloseStrategy(TransactionCloseStrategy closeStrategy); + /** + * If the currently set close strategy is {@link TransactionCloseStrategy#COMMIT}, then when the transaction is + * closed, this method is called and all registered {@link Command} are performed, locks on objects are released and + * any other resources are released + */ public void autoCloseableCommit(); + /** + * If the currently set close strategy is {@link TransactionCloseStrategy#ROLLBACK}, then when the transaction is + * closed, no further actions are performed and any {@link Command} which were performed have their + * {@link Command#undo()} method called and any DB connections are also rolled back + */ public void autoCloseableRollback(); + /** + *

+ * DO NOT CALL THIS METHOD. This interface implements {@link AutoCloseable} and transactions are expected to be used + * in a auto closing try block: + *

+ * + * + * StrolchAgent strolchAgent = getStrolchAgent(); + * StrolchRealm realm = strolchAgent.getContainer().getRealm("defaultRealm"); + * try(StrolchTransaction tx = realm.openTx(certificate, getClass())){ + * // do work + * } + * + * + * After the block is closed, the transaction is automatically closed and all allocated resources are released + */ @Override public void close() throws StrolchPersistenceException; + /** + * @return true if the transaction is still open, i.e. not being closed or rolling back, committing, etc. + */ public boolean isOpen(); + /** + * @return true if the transaction is in the process of rolling back all changes + */ public boolean isRollingBack(); + /** + * @return true if the transaction is in the process of committing the changes + */ public boolean isCommitting(); + /** + * @return if the transaction has committed all changes + */ public boolean isCommitted(); + /** + * @return if the transaction has rolled back all changes + */ public boolean isRolledBack(); - public AuditTrail getAuditTrail(); + /** + * If the given argument is true, then no observer updates are performed + * + * @param suppressUpdates + * true to suppress the updates, false to enable them + */ + public void setSuppressUpdates(boolean suppressUpdates); - public ResourceMap getResourceMap(); + /** + * Returns true if the observer updates are currently suppressed + * + * @return true if the observer updates are currently suppressed + */ + public boolean isSuppressUpdates(); - public OrderMap getOrderMap(); + /** + * If the given argument is true, then no {@link Audit Audits} are written + * + * @param suppressAudits + * true to suppress writing {@link Audit Audits}, false to enable them + */ + public void setSuppressAudits(boolean suppressAudits); - public PersistenceHandler getPersistenceHandler(); + /** + * Returns true if writing {@link Audit Audits} is currently suppressed + * + * @return true if writing {@link Audit Audits} is currently suppressed + */ + public boolean isSuppressAudits(); - public void lock(T element); + /** + * Locks the given element and registers it on the transaction so the lock is released when the transaction is + * closed + * + * @param element + * the element to lock + * + * @throws StrolchLockException + */ + public void lock(T element) throws StrolchLockException; - public void unlock(T element); + /** + * Releases the lock of the given element so that even though the transaction is still open, another + * thread/transaction can lock the element + * + * @param element + * the element for which the lock is to be released + * + * @throws StrolchLockException + */ + public void releaseLock(T element) throws StrolchLockException; + /** + * Adds the given {@link Command} to the transaction. Using this method guarantees that a {@link Command} is + * executed properly: + *
    + *
  • {@link Command#validate()}
  • + *
  • {@link Command#doCommand()}
  • + *
+ * + * and if an exception occurs: + *
    + *
  • {@link Command#undo()}
  • + *
+ * + * @param command + */ public void addCommand(Command command); + /** + * Helper method to create an {@link Audit} with the given arguments. The audit can then be saved by calling + * {@link AuditTrail#add(StrolchTransaction, Audit)} + * + * @param accessType + * the type of access + * @param elementType + * the element type, i.e. s {@link Tags#RESOURCE}, {@link Tags#ORDER} + * @param id + * the id of the element audited + * + * @return the new audit + */ public Audit auditFrom(AccessType accessType, String elementType, String id); + /** + *

+ * Performs the given {@link OrderQuery} returning the resulting list of {@link Order Orders}. + *

+ * + *

+ * Note: Should the result be mapped to different objects, then use + * {@link #doQuery(OrderQuery, OrderVisitor)} + *

+ * + * @param query + * the query to perform + * + * @return the result list, never null + */ public List doQuery(OrderQuery query); + /** + *

+ * Performs the given {@link OrderQuery} and each returned {@link Order} is passed through the {@link OrderVisitor} + * and the return value of the visitor is added to the return list + *

+ * + *

+ * This method is intended for situations where the query result should not be {@link Order} but some other object + * type. For instance in a restful API, the result might have to be mapped to a POJO, thus using this method can + * perform the mapping step for you + *

+ * + * @param query + * the query to perform + * + * @return the result list of elements as returned by the {@link OrderVisitor}, never null + */ public List doQuery(OrderQuery query, OrderVisitor orderVisitor); + /** + *

+ * Performs the given {@link ResourceQuery} returning the resulting list of {@link Resource Resources}. + *

+ * + *

+ * Note: Should the result be mapped to different objects, then use + * {@link #doQuery(ResourceQuery, ResourceVisitor)} + *

+ * + * @param query + * the query to perform + * + * @return the result list, never null + */ public List doQuery(ResourceQuery query); + /** + *

+ * Performs the given {@link ResourceQuery} and each returned {@link Resource} is passed through the + * {@link ResourceVisitor} and the return value of the visitor is added to the return list + *

+ * + *

+ * This method is intended for situations where the query result should not be {@link Resource} but some other + * object type. For instance in a restful API, the result might have to be mapped to a POJO, thus using this method + * can perform the mapping step for you + *

+ * + * @param query + * the query to perform + * + * @return the result list of elements as returned by the {@link ResourceVisitor}, never null + */ public List doQuery(ResourceQuery query, ResourceVisitor resourceVisitor); + /** + *

+ * Performs the given {@link AuditQuery} returning the resulting list of {@link Audit Audits}. + *

+ * + *

+ * Note: Should the result be mapped to different objects, then use + * {@link #doQuery(AuditQuery, AuditVisitor)} + *

+ * + * @param query + * the query to perform + * + * @return the result list, never null + */ public List doQuery(AuditQuery query); + /** + *

+ * Performs the given {@link AuditQuery} and each returned {@link Audit} is passed through the {@link AuditVisitor} + * and the return value of the visitor is added to the return list + *

+ * + *

+ * This method is intended for situations where the query result should not be {@link Audit} but some other object + * type. For instance in a restful API, the result might have to be mapped to a POJO, thus using this method can + * perform the mapping step for you + *

+ * + * @param query + * the query to perform + * + * @return the result list of elements as returned by the {@link AuditVisitor}, never null + */ public List doQuery(AuditQuery query, AuditVisitor auditVisitor); /** @@ -121,6 +412,96 @@ public interface StrolchTransaction extends AutoCloseable { */ public T findElement(Locator locator) throws StrolchException, ClassCastException; + /** + *

+ * Returns the {@link Resource} of Type {@link StrolchConstants#TEMPLATE} with the given type as id, or null if it + * does not exist + *

+ * + *

+ * Templates are {@link StrolchRootElement StrolchRootElements} which have the type + * {@link StrolchConstants#TEMPLATE} and their id is the type of element for which it is a template. For instance + * when creating a {@link Resource} of type Person then having a template with the id + * Person helps creating new Person resources; get the resource and then create a clone: + * {@link Resource#getClone()} + *

+ * + * @param type + * the id of the {@link Resource} template + * + * @return the {@link Resource} template with the given id, or null if it does not exist + */ + public Resource getResourceTemplate(String type); + + /** + *

+ * Returns the {@link Resource} of Type {@link StrolchConstants#TEMPLATE} with the given type as id. If + * assertExists is true, then an exception is thrown if the template does not exist does not exist + *

+ * + *

+ * Templates are {@link StrolchRootElement StrolchRootElements} which have the type + * {@link StrolchConstants#TEMPLATE} and their id is the type of element for which it is a template. For instance + * when creating a {@link Resource} of type Person then having a template with the id + * Person helps creating new Person resources; get the resource and then create a clone: + * {@link Resource#getClone()} + *

+ * + * @param type + * the id of the {@link Resource} template + * + * @return the {@link Resource} template with the given id, or if assertExists is true, then an + * exception is thrown if the resource does not exist + * + * @throws StrolchException + */ + public Resource getResourceTemplate(String type, boolean assertExists) throws StrolchException; + + /** + *

+ * Returns the {@link Order} of Type {@link StrolchConstants#TEMPLATE} with the given type as id, or null if it does + * not exist + *

+ * + *

+ * Templates are {@link StrolchRootElement StrolchRootElements} which have the type + * {@link StrolchConstants#TEMPLATE} and their id is the type of element for which it is a template. For instance + * when creating an {@link Order} of type PurchaseOrder then having a template with the id + * PurchaseOrder helps creating new PurchaseOrder orders; get the order and then create a clone: + * {@link Order#getClone()} + *

+ * + * @param type + * the id of the {@link Order} template + * + * @return the {@link Order} template with the given id, or null if it does not exist + */ + public Order getOrderTemplate(String type); + + /** + *

+ * Returns the {@link Order} of Type {@link StrolchConstants#TEMPLATE} with the given type as id. If + * assertExists is true, then an exception is thrown if the template does not exist does not exist + *

+ * + *

+ * Templates are {@link StrolchRootElement StrolchRootElements} which have the type + * {@link StrolchConstants#TEMPLATE} and their id is the type of element for which it is a template. For instance + * when creating an {@link Order} of type PurchaseOrder then having a template with the id + * PurchaseOrder helps creating new PurchaseOrder orders; get the order and then create a clone: + * {@link Order#getClone()} + *

+ * + * @param type + * the id of the {@link Order} template + * + * @return the {@link Order} template with the given id, or if assertExists is true, then an exception + * is thrown if the order does not exist + * + * @throws StrolchException + */ + public Order getOrderTemplate(String type, boolean assertExists) throws StrolchException; + /** * Returns the {@link Resource} with the given type and id, or null if it does not exist *