[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 * @param element
* the element for which a {@link Lock} on its {@link Locator} is to be created and/or locked * 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> * <p>
@ -67,8 +69,10 @@ public interface LockHandler {
* *
* @param element * @param element
* the element for which the current/last {@link Lock} is to be unlocked * 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 * 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 * @param element
* the element for which the {@link Lock} on the {@link Locator} is to be released * 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 java.util.concurrent.locks.ReentrantLock;
import li.strolch.agent.api.LockHandler; 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.Locator;
import li.strolch.model.StrolchRootElement; import li.strolch.model.StrolchRootElement;
@ -60,7 +60,7 @@ public class DefaultLockHandler implements LockHandler {
} }
@Override @Override
public void lock(StrolchRootElement element) { public void lock(StrolchRootElement element) throws StrolchLockException {
Locator locator = element.getLocator(); Locator locator = element.getLocator();
ReentrantLock lock = this.lockMap.get(locator); ReentrantLock lock = this.lockMap.get(locator);
if (lock == null) { if (lock == null) {
@ -72,7 +72,7 @@ public class DefaultLockHandler implements LockHandler {
} }
@Override @Override
public void unlock(StrolchRootElement element) { public void unlock(StrolchRootElement element) throws StrolchLockException {
Locator locator = element.getLocator(); Locator locator = element.getLocator();
ReentrantLock lock = this.lockMap.get(locator); ReentrantLock lock = this.lockMap.get(locator);
if (lock == null || !lock.isHeldByCurrentThread()) { if (lock == null || !lock.isHeldByCurrentThread()) {
@ -83,7 +83,7 @@ public class DefaultLockHandler implements LockHandler {
} }
@Override @Override
public void releaseLock(StrolchRootElement element) { public void releaseLock(StrolchRootElement element) throws StrolchLockException {
Locator locator = element.getLocator(); Locator locator = element.getLocator();
ReentrantLock lock = this.lockMap.get(locator); ReentrantLock lock = this.lockMap.get(locator);
if (lock == null || !lock.isHeldByCurrentThread()) { if (lock == null || !lock.isHeldByCurrentThread()) {
@ -96,28 +96,33 @@ public class DefaultLockHandler implements LockHandler {
/** /**
* @see java.util.concurrent.locks.ReentrantLock#tryLock(long, TimeUnit) * @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 { try {
if (!lock.tryLock(tryLockTime, timeUnit)) { if (!lock.tryLock(tryLockTime, timeUnit)) {
String msg = "Failed to acquire lock after {0}s for {1}"; //$NON-NLS-1$ String msg = "Failed to acquire lock after {0}s for {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, timeUnit.toSeconds(tryLockTime), element.getLocator()); msg = MessageFormat.format(msg, timeUnit.toSeconds(tryLockTime), element.getLocator());
throw new StrolchException(msg); throw new StrolchLockException(msg);
} }
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("locked " + toString()); //$NON-NLS-1$ logger.debug("locked " + toString()); //$NON-NLS-1$
} catch (InterruptedException e) { } 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() * @see java.util.concurrent.locks.ReentrantLock#unlock()
*/ */
private void unlock(ReentrantLock lock) { private void unlock(ReentrantLock lock) throws StrolchLockException {
lock.unlock(); try {
if (logger.isDebugEnabled()) lock.unlock();
logger.debug("unlocking " + toString()); //$NON-NLS-1$ 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.OrderMap;
import li.strolch.agent.api.ResourceMap; import li.strolch.agent.api.ResourceMap;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchLockException;
import li.strolch.agent.api.StrolchRealm; import li.strolch.agent.api.StrolchRealm;
import li.strolch.agent.impl.AuditingAuditMapFacade; import li.strolch.agent.impl.AuditingAuditMapFacade;
import li.strolch.agent.impl.AuditingOrderMap; 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.NoStrategyOrderVisitor;
import li.strolch.model.visitor.NoStrategyResourceVisitor; import li.strolch.model.visitor.NoStrategyResourceVisitor;
import li.strolch.persistence.inmemory.InMemoryTransaction; import li.strolch.persistence.inmemory.InMemoryTransaction;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.privilege.PrivilegeHandler; import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.service.api.Command; import li.strolch.service.api.Command;
@ -159,45 +161,35 @@ public abstract class AbstractTransaction implements StrolchTransaction {
this.closeStrategy.close(this); this.closeStrategy.close(this);
} }
/** @Override
* @param suppressUpdates
* the suppressUpdates to set
*/
public void setSuppressUpdates(boolean suppressUpdates) { public void setSuppressUpdates(boolean suppressUpdates) {
this.suppressUpdates = suppressUpdates; this.suppressUpdates = suppressUpdates;
} }
/** @Override
* @return the suppressUpdates
*/
public boolean isSuppressUpdates() { public boolean isSuppressUpdates() {
return this.suppressUpdates; return this.suppressUpdates;
} }
/** @Override
* @param suppressAudits
* the suppressAudits to set
*/
public void setSuppressAudits(boolean suppressAudits) { public void setSuppressAudits(boolean suppressAudits) {
this.suppressAudits = suppressAudits; this.suppressAudits = suppressAudits;
} }
/** @Override
* @return the suppressAudits
*/
public boolean isSuppressAudits() { public boolean isSuppressAudits() {
return this.suppressAudits; return this.suppressAudits;
} }
@Override @Override
public <T extends StrolchRootElement> void lock(T element) { public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException {
this.realm.lock(element); this.realm.lock(element);
this.lockedElements.add(element); this.lockedElements.add(element);
} }
@Override @Override
public <T extends StrolchRootElement> void unlock(T element) { public <T extends StrolchRootElement> void releaseLock(T element) throws StrolchLockException {
this.realm.unlock(element); this.realm.releaseLock(element);
this.lockedElements.remove(element); this.lockedElements.remove(element);
} }
@ -361,6 +353,26 @@ public abstract class AbstractTransaction implements StrolchTransaction {
throw new StrolchException(MessageFormat.format(msg, locator, stateOrBag)); 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 @Override
public Order getOrderBy(String type, String id) { public Order getOrderBy(String type, String id) {
return getOrderBy(type, id, false); 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.AuditTrail;
import li.strolch.agent.api.OrderMap; import li.strolch.agent.api.OrderMap;
import li.strolch.agent.api.ResourceMap; 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.exception.StrolchException;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.Order; import li.strolch.model.Order;
@ -29,6 +33,7 @@ import li.strolch.model.Resource;
import li.strolch.model.ResourceVisitor; import li.strolch.model.ResourceVisitor;
import li.strolch.model.StrolchElement; import li.strolch.model.StrolchElement;
import li.strolch.model.StrolchRootElement; import li.strolch.model.StrolchRootElement;
import li.strolch.model.Tags;
import li.strolch.model.audit.AccessType; import li.strolch.model.audit.AccessType;
import li.strolch.model.audit.Audit; import li.strolch.model.audit.Audit;
import li.strolch.model.audit.AuditQuery; import li.strolch.model.audit.AuditQuery;
@ -41,55 +46,341 @@ import li.strolch.model.query.ResourceQuery;
import li.strolch.runtime.StrolchConstants; import li.strolch.runtime.StrolchConstants;
import li.strolch.service.api.Command; 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 { 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(); 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); 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(); 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(); 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 @Override
public void close() throws StrolchPersistenceException; 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(); public boolean isOpen();
/**
* @return true if the transaction is in the process of rolling back all changes
*/
public boolean isRollingBack(); public boolean isRollingBack();
/**
* @return true if the transaction is in the process of committing the changes
*/
public boolean isCommitting(); public boolean isCommitting();
/**
* @return if the transaction has committed all changes
*/
public boolean isCommitted(); public boolean isCommitted();
/**
* @return if the transaction has rolled back all changes
*/
public boolean isRolledBack(); 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); 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); 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); 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); 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); 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); 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); 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); 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; 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 * Returns the {@link Resource} with the given type and id, or null if it does not exist
* *