[New] Added tx.getTemplate*(), documented TX, added StrolchLockException

This commit is contained in:
Robert von Burg 2015-01-23 11:40:17 +01:00
parent 9711914056
commit 120ff0ace9
6 changed files with 461 additions and 38 deletions

@ -1 +1 @@
Subproject commit cde6eb652ec2c12ce22c8cb21a16589d56f8a49f
Subproject commit 401052a5ea5844673615ba4c255b6faa96000adc

View File

@ -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;
/**
* <p>
@ -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;
}

View File

@ -0,0 +1,19 @@
package li.strolch.agent.api;
import li.strolch.exception.StrolchException;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
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);
}
}

View File

@ -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$
}
}
/**

View File

@ -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 <T extends StrolchRootElement> void lock(T element) {
public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException {
this.realm.lock(element);
this.lockedElements.add(element);
}
@Override
public <T extends StrolchRootElement> void unlock(T element) {
this.realm.unlock(element);
public <T extends StrolchRootElement> 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);

View File

@ -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;
/**
* <p>
* {@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.
* </p>
*
* <p>
* A Strolch transaction is performed as follows as it is an {@link AutoCloseable} implementation
* </p>
* <code>
* StrolchAgent strolchAgent = getStrolchAgent();
* StrolchRealm realm = strolchAgent.getContainer().getRealm(StrolchConstants.DEFAULT_REALM);
* try(StrolchTransaction tx = realm.openTx(certificate, getClass())){
* // do work e.g. add commands
* }
* </code>
*
* <p>
* 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.
* <p>
*
* <p>
* A {@link StrolchTransaction} takes care of the following:
* </p>
* <ul>
* <li>Opening and closing database connections</li>
* <li>Releasing locks to strolch elements, if {@link #lock(StrolchRootElement)} is used</li>
* <li>Performing Commands correctly</li>
* <li>exception handling</li>
* <li>auditing</li>
* <li>updating observers</li>
* </ul>
*
* @see AbstractTransaction
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
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();
/**
* <p>
* DO NOT CALL THIS METHOD. This interface implements {@link AutoCloseable} and transactions are expected to be used
* in a auto closing try block:
* </p>
*
* <code>
* StrolchAgent strolchAgent = getStrolchAgent();
* StrolchRealm realm = strolchAgent.getContainer().getRealm("defaultRealm");
* try(StrolchTransaction tx = realm.openTx(certificate, getClass())){
* // do work
* }
* </code>
*
* 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 <T extends StrolchRootElement> 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 <T extends StrolchRootElement> void lock(T element) throws StrolchLockException;
public <T extends StrolchRootElement> 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 <T extends StrolchRootElement> 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:
* <ul>
* <li>{@link Command#validate()}</li>
* <li>{@link Command#doCommand()}</li>
* </ul>
*
* and if an exception occurs:
* <ul>
* <li>{@link Command#undo()}</li>
* </ul>
*
* @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);
/**
* <p>
* Performs the given {@link OrderQuery} returning the resulting list of {@link Order Orders}.
* </p>
*
* <p>
* <b>Note:</b> Should the result be mapped to different objects, then use
* {@link #doQuery(OrderQuery, OrderVisitor)}
* </p>
*
* @param query
* the query to perform
*
* @return the result list, never null
*/
public List<Order> doQuery(OrderQuery query);
/**
* <p>
* 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
* </p>
*
* <p>
* 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
* </p>
*
* @param query
* the query to perform
*
* @return the result list of elements as returned by the {@link OrderVisitor}, never null
*/
public <U> List<U> doQuery(OrderQuery query, OrderVisitor<U> orderVisitor);
/**
* <p>
* Performs the given {@link ResourceQuery} returning the resulting list of {@link Resource Resources}.
* </p>
*
* <p>
* <b>Note:</b> Should the result be mapped to different objects, then use
* {@link #doQuery(ResourceQuery, ResourceVisitor)}
* </p>
*
* @param query
* the query to perform
*
* @return the result list, never null
*/
public List<Resource> doQuery(ResourceQuery query);
/**
* <p>
* 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
* </p>
*
* <p>
* 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
* </p>
*
* @param query
* the query to perform
*
* @return the result list of elements as returned by the {@link ResourceVisitor}, never null
*/
public <U> List<U> doQuery(ResourceQuery query, ResourceVisitor<U> resourceVisitor);
/**
* <p>
* Performs the given {@link AuditQuery} returning the resulting list of {@link Audit Audits}.
* </p>
*
* <p>
* <b>Note:</b> Should the result be mapped to different objects, then use
* {@link #doQuery(AuditQuery, AuditVisitor)}
* </p>
*
* @param query
* the query to perform
*
* @return the result list, never null
*/
public List<Audit> doQuery(AuditQuery query);
/**
* <p>
* 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
* </p>
*
* <p>
* 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
* </p>
*
* @param query
* the query to perform
*
* @return the result list of elements as returned by the {@link AuditVisitor}, never null
*/
public <U> List<U> doQuery(AuditQuery query, AuditVisitor<U> auditVisitor);
/**
@ -121,6 +412,96 @@ public interface StrolchTransaction extends AutoCloseable {
*/
public <T extends StrolchElement> T findElement(Locator locator) throws StrolchException, ClassCastException;
/**
* <p>
* Returns the {@link Resource} of Type {@link StrolchConstants#TEMPLATE} with the given type as id, or null if it
* does not exist
* </p>
*
* <p>
* 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 <code>Person</code> then having a template with the id
* <code>Person</code> helps creating new Person resources; get the resource and then create a clone:
* {@link Resource#getClone()}
* </p>
*
* @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);
/**
* <p>
* Returns the {@link Resource} of Type {@link StrolchConstants#TEMPLATE} with the given type as id. If
* <code>assertExists</code> is true, then an exception is thrown if the template does not exist does not exist
* </p>
*
* <p>
* 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 <code>Person</code> then having a template with the id
* <code>Person</code> helps creating new Person resources; get the resource and then create a clone:
* {@link Resource#getClone()}
* </p>
*
* @param type
* the id of the {@link Resource} template
*
* @return the {@link Resource} template with the given id, or if <code>assertExists</code> is true, then an
* exception is thrown if the resource does not exist
*
* @throws StrolchException
*/
public Resource getResourceTemplate(String type, boolean assertExists) throws StrolchException;
/**
* <p>
* Returns the {@link Order} of Type {@link StrolchConstants#TEMPLATE} with the given type as id, or null if it does
* not exist
* </p>
*
* <p>
* 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 <code>PurchaseOrder</code> then having a template with the id
* <code>PurchaseOrder</code> helps creating new PurchaseOrder orders; get the order and then create a clone:
* {@link Order#getClone()}
* </p>
*
* @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);
/**
* <p>
* Returns the {@link Order} of Type {@link StrolchConstants#TEMPLATE} with the given type as id. If
* <code>assertExists</code> is true, then an exception is thrown if the template does not exist does not exist
* </p>
*
* <p>
* 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 <code>PurchaseOrder</code> then having a template with the id
* <code>PurchaseOrder</code> helps creating new PurchaseOrder orders; get the order and then create a clone:
* {@link Order#getClone()}
* </p>
*
* @param type
* the id of the {@link Order} template
*
* @return the {@link Order} template with the given id, or if <code>assertExists</code> 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
*