[Major] Refactored LockHandler to use Locator

This is required because in certain instances it is important to lock
the object before retrieving it, otherwise if, for instance in
execution, we would need to fetch, lock and re-fetch to be sure we have
the latest object.

In moste of the Update*Commands this is not important as we perform a
list commit wins, where the client wants their version to be persisted.
Later a optimistic locking can be performed in that the given version
must be the latest, otherwise an exception would be thrown
This commit is contained in:
Robert von Burg 2017-02-22 12:14:23 +01:00
parent c62bbe987e
commit 89739717f2
12 changed files with 240 additions and 114 deletions

View File

@ -45,43 +45,45 @@ import li.strolch.model.StrolchRootElement;
public interface LockHandler { public interface LockHandler {
/** /**
* Locks the given element by using the element's {@link Locator} and creating a lock on it. Calling lock multiple * Locks the element with the given {@link Locator} and creating a lock on it. Calling lock multiple times from the
* times from the same thread will not lock, it is up to the concrete implementation to define if a lock counter is * same thread will not lock, it is up to the concrete implementation to define if a lock counter is used
* used
* *
* @param element * @param locator
* the element for which a {@link Lock} on its {@link Locator} is to be created and/or locked * the {@link Locator} of the element for which a {@link Lock} is to be created and/or locked
* *
* @throws StrolchLockException * @throws StrolchLockException
* if the lock could not be acquired
*/ */
public void lock(StrolchRootElement element) throws StrolchLockException; public void lock(Locator locator) throws StrolchLockException;
/** /**
* <p> * <p>
* Unlocks the given element by finding the element's lock by its {@link Locator}. It is up to the concrete * Unlocks the element with the given locator. It is up to the concrete implementation to define if unlocking an
* implementation to define if unlocking an unlocked element will fail or not. This method might not completely * unlocked element will fail or not. This method might not completely unlock the element if a lock counter is used
* unlock the element if a lock counter is used and the object was locked multiple times. * and the object was locked multiple times.
* </p> * </p>
* *
* <p> * <p>
* If the lock must be completely released, then use {@link #releaseLock(StrolchRootElement)} * If the lock must be completely released, then use {@link #releaseLock(Locator)}
* </p> * </p>
* *
* @param element * @param locator
* the element for which the current/last {@link Lock} is to be unlocked * the {@link Locator} of the element for which the current/last {@link Lock} is to be unlocked
* *
* @throws StrolchLockException * @throws StrolchLockException
* if the unlock failed
*/ */
public void unlock(StrolchRootElement element) throws StrolchLockException; public void unlock(Locator locator) 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 element with the given {@link Locator}, by unlocking all locks, i.e. after this method
* held anymore by the current thread * is called, no lock will be held anymore by the current thread
* *
* @param element * @param locator
* the element for which the {@link Lock} on the {@link Locator} is to be released * the {@link Locator} of the element for which the {@link Lock} is to be released
* *
* @throws StrolchLockException * @throws StrolchLockException
* if the lock could not be released
*/ */
public void releaseLock(StrolchRootElement element) throws StrolchLockException; public void releaseLock(Locator locator) throws StrolchLockException;
} }

View File

@ -16,7 +16,7 @@
package li.strolch.agent.api; package li.strolch.agent.api;
import li.strolch.agent.impl.DataStoreMode; import li.strolch.agent.impl.DataStoreMode;
import li.strolch.model.StrolchRootElement; import li.strolch.model.Locator;
import li.strolch.persistence.api.StrolchTransaction; import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
@ -51,34 +51,34 @@ public interface StrolchRealm {
public String getRealm(); public String getRealm();
/** /**
* Locks the given element * Locks the element with the given {@link Locator}
* *
* @param element * @param element
* the element to lock * the locator of the element to lock
* *
* @see LockHandler#lock(StrolchRootElement) * @see LockHandler#lock(Locator)
*/ */
public void lock(StrolchRootElement element); public void lock(Locator locator);
/** /**
* Unlocks the given element (lock might still be held, if lock counter is used) * Unlocks the element with the given {@link Locator} (lock might still be held, if lock counter is used)
* *
* @param lockedElement * @param locator
* the element to unlock * the locator of the element to unlock
* *
* @see LockHandler#unlock(StrolchRootElement) * @see LockHandler#unlock(Locator)
*/ */
public void unlock(StrolchRootElement lockedElement); public void unlock(Locator locator);
/** /**
* Releases the lock for the given element * Releases the lock for the given element
* *
* @param lockedElement * @param locator
* the element for which to release the lock * the locator of the element for which to release the lock
* *
* @see LockHandler#releaseLock(StrolchRootElement) * @see LockHandler#releaseLock(Locator)
*/ */
public void releaseLock(StrolchRootElement lockedElement); public void releaseLock(Locator locator);
/** /**
* Returns the {@link DataStoreMode} * Returns the {@link DataStoreMode}

View File

@ -21,15 +21,14 @@ import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.agent.api.LockHandler; import li.strolch.agent.api.LockHandler;
import li.strolch.agent.api.StrolchLockException; import li.strolch.agent.api.StrolchLockException;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.StrolchRootElement;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -59,35 +58,36 @@ public class DefaultLockHandler implements LockHandler {
} }
@Override @Override
public void lock(StrolchRootElement element) throws StrolchLockException { public void lock(Locator locator) throws StrolchLockException {
Locator locator = element.getLocator();
ReentrantLock lock = this.lockMap.get(locator); ReentrantLock lock = this.lockMap.get(locator);
if (lock == null) { if (lock == null) {
lock = new ReentrantLock(true); lock = new ReentrantLock(true);
this.lockMap.put(locator, lock); this.lockMap.put(locator, lock);
} }
lock(this.tryLockTimeUnit, this.tryLockTime, lock, element); lock(this.tryLockTimeUnit, this.tryLockTime, lock, locator);
} }
@Override @Override
public void unlock(StrolchRootElement element) throws StrolchLockException { public void unlock(Locator locator) throws StrolchLockException {
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()) {
logger.error(MessageFormat.format("Trying to unlock not locked element {0}", locator)); //$NON-NLS-1$ logger.error(MessageFormat.format("Trying to unlock not locked element {0}", locator)); //$NON-NLS-1$
} else { } else {
if (logger.isDebugEnabled())
logger.debug("unlocking " + locator); //$NON-NLS-1$
unlock(lock); unlock(lock);
} }
} }
@Override @Override
public void releaseLock(StrolchRootElement element) throws StrolchLockException { public void releaseLock(Locator locator) throws StrolchLockException {
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()) {
logger.error(MessageFormat.format("Trying to unlock not locked element {0}", locator)); //$NON-NLS-1$ logger.error(MessageFormat.format("Trying to unlock not locked element {0}", locator)); //$NON-NLS-1$
} else { } else {
if (logger.isDebugEnabled())
logger.debug("releasing lock " + locator); //$NON-NLS-1$
releaseLock(lock); releaseLock(lock);
} }
} }
@ -95,17 +95,19 @@ 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, Locator locator)
throws StrolchLockException { 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), locator);
throw new StrolchLockException(msg); throw new StrolchLockException(msg);
} }
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("locked " + toString()); //$NON-NLS-1$ logger.debug("locked " + locator); //$NON-NLS-1$
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new StrolchLockException(e.getMessage(), e); //$NON-NLS-1$ throw new StrolchLockException(e.getMessage(), e); //$NON-NLS-1$
} }
@ -117,8 +119,6 @@ public class DefaultLockHandler implements LockHandler {
private void unlock(ReentrantLock lock) throws StrolchLockException { private void unlock(ReentrantLock lock) throws StrolchLockException {
try { try {
lock.unlock(); lock.unlock();
if (logger.isDebugEnabled())
logger.debug("unlocking " + toString()); //$NON-NLS-1$
} catch (IllegalMonitorStateException e) { } catch (IllegalMonitorStateException e) {
throw new StrolchLockException(e.getMessage(), e); //$NON-NLS-1$ throw new StrolchLockException(e.getMessage(), e); //$NON-NLS-1$
} }

View File

@ -34,7 +34,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.StrolchRealm; import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.StrolchRootElement; import li.strolch.model.Locator;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.runtime.StrolchConstants; import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.configuration.ComponentConfiguration;
@ -67,19 +67,19 @@ public abstract class InternalStrolchRealm implements StrolchRealm {
} }
@Override @Override
public void lock(StrolchRootElement element) { public void lock(Locator locator) {
DBC.PRE.assertNotNull("Can not lock a null pointer =)", element); //$NON-NLS-1$ DBC.PRE.assertNotNull("Can not lock a null pointer =)", locator); //$NON-NLS-1$
this.lockHandler.lock(element); this.lockHandler.lock(locator);
} }
@Override @Override
public void unlock(StrolchRootElement lockedElement) { public void unlock(Locator locator) {
this.lockHandler.unlock(lockedElement); this.lockHandler.unlock(locator);
} }
@Override @Override
public void releaseLock(StrolchRootElement lockedElement) { public void releaseLock(Locator locator) {
this.lockHandler.releaseLock(lockedElement); this.lockHandler.releaseLock(locator);
} }
public void initialize(ComponentContainer container, ComponentConfiguration configuration) { public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
@ -152,7 +152,7 @@ public abstract class InternalStrolchRealm implements StrolchRealm {
} }
@Override @Override
public ObserverHandler getObserverHandler() throws IllegalArgumentException{ public ObserverHandler getObserverHandler() throws IllegalArgumentException {
if (!this.updateObservers) if (!this.updateObservers)
throw new IllegalArgumentException("ObserverUpdates are not enabled!"); //$NON-NLS-1$ throw new IllegalArgumentException("ObserverUpdates are not enabled!"); //$NON-NLS-1$
return this.observerHandler; return this.observerHandler;

View File

@ -91,7 +91,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
private List<Command> commands; private List<Command> commands;
private List<Command> flushedCommands; private List<Command> flushedCommands;
private Set<StrolchRootElement> lockedElements; private Set<Locator> lockedElements;
private AuditingOrderMap orderMap; private AuditingOrderMap orderMap;
private AuditingResourceMap resourceMap; private AuditingResourceMap resourceMap;
@ -255,21 +255,35 @@ public abstract class AbstractTransaction implements StrolchTransaction {
return this.realm.isVersioningEnabled(); return this.realm.isVersioningEnabled();
} }
@Override
public <T extends StrolchRootElement> void lock(Locator locator) throws StrolchLockException {
this.realm.lock(locator);
this.lockedElements.add(locator);
}
@Override @Override
public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException { public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException {
this.realm.lock(element); Locator locator = element.getLocator();
this.lockedElements.add(element); this.realm.lock(locator);
this.lockedElements.add(locator);
} }
@Override @Override
public <T extends StrolchRootElement> void releaseLock(T element) throws StrolchLockException { public <T extends StrolchRootElement> void releaseLock(T element) throws StrolchLockException {
this.realm.releaseLock(element); Locator locator = element.getLocator();
this.lockedElements.remove(element); this.realm.releaseLock(locator);
this.lockedElements.remove(locator);
}
@Override
public <T extends StrolchRootElement> void releaseLock(Locator locator) throws StrolchLockException {
this.realm.releaseLock(locator);
this.lockedElements.remove(locator);
} }
private void releaseElementLocks() { private void releaseElementLocks() {
for (StrolchRootElement lockedElement : this.lockedElements) { for (Locator locator : this.lockedElements) {
this.realm.releaseLock(lockedElement); this.realm.releaseLock(locator);
} }
} }

View File

@ -79,7 +79,7 @@ import li.strolch.service.api.Command;
* </p> * </p>
* <ul> * <ul>
* <li>Opening and closing database connections</li> * <li>Opening and closing database connections</li>
* <li>Releasing locks to strolch elements, if {@link #lock(StrolchRootElement)} is used</li> * <li>Releasing locks to strolch elements, if {@link #lock(StrolchRootElement)} or {@link #lock(Locator)} is used</li>
* <li>Performing Commands correctly</li> * <li>Performing Commands correctly</li>
* <li>exception handling</li> * <li>exception handling</li>
* <li>auditing</li> * <li>auditing</li>
@ -333,6 +333,17 @@ public interface StrolchTransaction extends AutoCloseable {
*/ */
boolean isVersioningEnabled(); boolean isVersioningEnabled();
/**
* Locks the element with the given locator and registers it on the transaction so the lock is released when the
* transaction is closed
*
* @param locator
* the {@link Locator} of the element to lock
*
* @throws StrolchLockException
*/
public <T extends StrolchRootElement> void lock(Locator locator) throws StrolchLockException;
/** /**
* Locks the given element and registers it on the transaction so the lock is released when the transaction is * Locks the given element and registers it on the transaction so the lock is released when the transaction is
* closed * closed
@ -345,8 +356,8 @@ public interface StrolchTransaction extends AutoCloseable {
public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException; public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException;
/** /**
* Releases the lock of the given element so that even though the transaction is still open, another * Releases the lock of the element so that even though the transaction is still open, another thread/transaction
* thread/transaction can lock the element * can lock the element
* *
* @param element * @param element
* the element for which the lock is to be released * the element for which the lock is to be released
@ -355,6 +366,17 @@ public interface StrolchTransaction extends AutoCloseable {
*/ */
public <T extends StrolchRootElement> void releaseLock(T element) throws StrolchLockException; public <T extends StrolchRootElement> void releaseLock(T element) throws StrolchLockException;
/**
* Releases the lock of the element with the given {@link Locator} so that even though the transaction is still
* open, another thread/transaction can lock the element
*
* @param locator
* the {@link Locator} of the element for which the lock is to be released
*
* @throws StrolchLockException
*/
public <T extends StrolchRootElement> void releaseLock(Locator locator) throws StrolchLockException;
/** /**
* Adds the given {@link Command} to the transaction. Using this method guarantees that a {@link Command} is * Adds the given {@link Command} to the transaction. Using this method guarantees that a {@link Command} is
* executed properly: * executed properly:

View File

@ -186,6 +186,12 @@ public class Locator {
return new Locator(this.pathElements, element); return new Locator(this.pathElements, element);
} }
public Locator trim(int size) {
if (this.pathElements.size() == size)
return this;
return new Locator(this.pathElements.subList(0, size));
}
/** /**
* Returns the string representation of this {@link Locator} by using the {@link #PATH_SEPARATOR} to separate the * Returns the string representation of this {@link Locator} by using the {@link #PATH_SEPARATOR} to separate the
* values * values

View File

@ -6,6 +6,7 @@ import li.strolch.execution.command.SetActionToErrorCommand;
import li.strolch.execution.command.SetActionToExecutedCommand; import li.strolch.execution.command.SetActionToExecutedCommand;
import li.strolch.execution.command.SetActionToStoppedCommand; import li.strolch.execution.command.SetActionToStoppedCommand;
import li.strolch.execution.command.SetActionToWarningCommand; import li.strolch.execution.command.SetActionToWarningCommand;
import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.activity.Action; import li.strolch.model.activity.Action;
import li.strolch.model.activity.Activity; import li.strolch.model.activity.Activity;
@ -13,7 +14,14 @@ import li.strolch.model.activity.IActivityElement;
import li.strolch.persistence.api.StrolchTransaction; import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.utils.dbc.DBC;
/**
* The event based execution handler waits for events in that the {@link ExecutionPolicy} implementations must call the
* relevant methods when the work is complete. Afterwards the next {@link Action} in the procedure is executed
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class EventBasedExecutionHandler extends ExecutionHandler { public class EventBasedExecutionHandler extends ExecutionHandler {
private DelayedExecutionTimer delayedExecutionTimer; private DelayedExecutionTimer delayedExecutionTimer;
@ -76,29 +84,34 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
}); });
} }
private void toExecution(String realm, Locator locator, PrivilegeContext ctx) { private void toExecution(String realm, Locator activityLoc, PrivilegeContext ctx) {
try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), ExecuteActivityCommand.class)) { try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), ExecuteActivityCommand.class)) {
IActivityElement activityElement = tx.findElement(locator); Locator rootElemLoc = activityLoc.trim(3);
Activity activity = activityElement.getRootElement(); tx.lock(rootElemLoc);
IActivityElement elem = tx.findElement(rootElemLoc);
DBC.INTERIM.assertEquals("toExecution only for Activity!", Activity.class, elem.getClass());
ExecuteActivityCommand command = new ExecuteActivityCommand(getContainer(), tx); ExecuteActivityCommand command = new ExecuteActivityCommand(getContainer(), tx);
command.setActivity(activity); command.setActivity((Activity) elem);
tx.addCommand(command); tx.addCommand(command);
tx.commitOnClose(); tx.commitOnClose();
} }
} }
private void toExecuted(String realm, Locator locator, PrivilegeContext ctx) { private void toExecuted(String realm, Locator actionLoc, PrivilegeContext ctx) {
Locator activityLoc; Locator activityLoc = actionLoc.trim(3);
try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) { try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) {
Action action = tx.findElement(locator); tx.lock(activityLoc);
activityLoc = action.getRootElement().getLocator();
IActivityElement elem = tx.findElement(actionLoc);
DBC.INTERIM.assertEquals("toExecuted only for Action!", Action.class, elem.getClass());
SetActionToExecutedCommand command = new SetActionToExecutedCommand(getContainer(), tx); SetActionToExecutedCommand command = new SetActionToExecutedCommand(getContainer(), tx);
command.setAction(action); command.setAction((Action) elem);
tx.addCommand(command); tx.addCommand(command);
tx.commitOnClose(); tx.commitOnClose();
@ -107,36 +120,48 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
toExecution(realm, activityLoc, ctx); toExecution(realm, activityLoc, ctx);
} }
private void toWarning(String realm, Locator locator, PrivilegeContext ctx) { private void toWarning(String realm, Locator actionLoc, PrivilegeContext ctx) {
try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) { try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) {
Action action = tx.findElement(locator); Locator rootElemLoc = actionLoc.trim(3);
tx.lock(rootElemLoc);
IActivityElement elem = tx.findElement(actionLoc);
DBC.INTERIM.assertEquals("toWarning only for Action!", Action.class, elem.getClass());
SetActionToWarningCommand command = new SetActionToWarningCommand(getContainer(), tx); SetActionToWarningCommand command = new SetActionToWarningCommand(getContainer(), tx);
command.setAction(action); command.setAction((Action) elem);
tx.addCommand(command); tx.addCommand(command);
tx.commitOnClose(); tx.commitOnClose();
} }
} }
private void toError(String realm, Locator locator, PrivilegeContext ctx) { private void toError(String realm, Locator actionLoc, PrivilegeContext ctx) {
try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) { try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) {
Action action = tx.findElement(locator); Locator rootElemLoc = actionLoc.trim(3);
tx.lock(rootElemLoc);
IActivityElement elem = tx.findElement(actionLoc);
DBC.INTERIM.assertEquals("toError only for Action!", Action.class, elem.getClass());
SetActionToErrorCommand command = new SetActionToErrorCommand(getContainer(), tx); SetActionToErrorCommand command = new SetActionToErrorCommand(getContainer(), tx);
command.setAction(action); command.setAction((Action) elem);
tx.addCommand(command); tx.addCommand(command);
tx.commitOnClose(); tx.commitOnClose();
} }
} }
private void toStopped(String realm, Locator locator, PrivilegeContext ctx) { private void toStopped(String realm, Locator actionLoc, PrivilegeContext ctx) {
try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToStoppedCommand.class)) { try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToStoppedCommand.class)) {
Action action = tx.findElement(locator); Locator rootElemLoc = actionLoc.trim(3);
tx.lock(rootElemLoc);
IActivityElement elem = tx.findElement(actionLoc);
DBC.INTERIM.assertEquals("toStopped only for Action!", Action.class, elem.getClass());
SetActionToStoppedCommand command = new SetActionToStoppedCommand(getContainer(), tx); SetActionToStoppedCommand command = new SetActionToStoppedCommand(getContainer(), tx);
command.setAction(action); command.setAction((Action) elem);
tx.addCommand(command); tx.addCommand(command);
tx.commitOnClose(); tx.commitOnClose();

View File

@ -2,23 +2,92 @@ package li.strolch.execution;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchComponent; import li.strolch.agent.api.StrolchComponent;
import li.strolch.execution.policy.DurationExecution;
import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.State;
import li.strolch.model.activity.Action;
import li.strolch.model.activity.Activity;
/**
* <p>
* The ExecutionHandler enables the automated execution of {@link Activity} and {@link Action} elements.
* </p>
*
* <p>
* To start the execution of an {@link Activity} pass it to the {@link #toExecution(String, Locator)} method, and
* together with the {@link ExecutionPolicy} to execution of the element will be handled
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public abstract class ExecutionHandler extends StrolchComponent { public abstract class ExecutionHandler extends StrolchComponent {
public ExecutionHandler(ComponentContainer container, String componentName) { public ExecutionHandler(ComponentContainer container, String componentName) {
super(container, componentName); super(container, componentName);
} }
/**
* <p>
* Returns the {@link DelayedExecutionTimer}
* </p>
*
* <p>
* The {@link DelayedExecutionTimer} allows to delay the {@link #toExecuted(String, Locator)} call by a given time.
* See the {@link DurationExecution} policy
* </p>
*
* @return the {@link DelayedExecutionTimer}
*/
public abstract DelayedExecutionTimer getDelayedExecutionTimer(); public abstract DelayedExecutionTimer getDelayedExecutionTimer();
public abstract void toExecution(String realm, Locator locator); /**
* Starts the execution of the given {@link Activity} with the given {@link Locator}
*
* @param realm
* the realm where the {@link Activity} resides
* @param activityLoc
* the {@link Locator} of the {@link Activity}
*/
public abstract void toExecution(String realm, Locator activityLoc);
public abstract void toExecuted(String realm, Locator locator); /**
* Completes the execution of the given {@link Action} with the given {@link Locator}
*
* @param realm
* the realm where the {@link Action} resides
* @param actionLoc
* the {@link Locator} of the {@link Action}
*/
public abstract void toExecuted(String realm, Locator actionLoc);
public abstract void toStopped(String realm, Locator locator); /**
* Sets the state of the {@link Action} with the given {@link Locator} to {@link State#STOPPED}
*
* @param realm
* the realm where the {@link Action} resides
* @param actionLoc
* the {@link Locator} of the {@link Action}
*/
public abstract void toStopped(String realm, Locator actionLoc);
public abstract void toWarning(String realm, Locator locator); /**
* Sets the state of the {@link Action} with the given {@link Locator} to {@link State#WARNING}
*
* @param realm
* the realm where the {@link Action} resides
* @param actionLoc
* the {@link Locator} of the {@link Action}
*/
public abstract void toWarning(String realm, Locator actionLoc);
public abstract void toError(String realm, Locator locator); /**
* Sets the state of the {@link Action} with the given {@link Locator} to {@link State#ERROR}
*
* @param realm
* the realm where the {@link Action} resides
* @param actionLoc
* the {@link Locator} of the {@link Action}
*/
public abstract void toError(String realm, Locator actionLoc);
} }

View File

@ -52,9 +52,6 @@ public class ReservationExection extends DurationExecution {
throw new StrolchModelException("Parameter " + PARAM_RESERVED + " on bag " + BAG_PARAMETERS + " missing on " throw new StrolchModelException("Parameter " + PARAM_RESERVED + " on bag " + BAG_PARAMETERS + " missing on "
+ resource.getLocator()); + resource.getLocator());
// immediately lock, so that when toExecution is called, we don't have a race condition
tx().lock(resource);
BooleanParameter reservedP = resource.getParameter(BAG_PARAMETERS, PARAM_RESERVED); BooleanParameter reservedP = resource.getParameter(BAG_PARAMETERS, PARAM_RESERVED);
if (action.getType().equals(TYPE_RESERVE)) if (action.getType().equals(TYPE_RESERVE))
return !reservedP.getValue(); return !reservedP.getValue();

View File

@ -20,7 +20,6 @@ import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.exception.StrolchException;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.Resource; import li.strolch.model.Resource;
import li.strolch.model.State; import li.strolch.model.State;
@ -56,16 +55,12 @@ public abstract class AbstractPlanCommand extends Command {
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
protected void plan(Action action) { protected void plan(Action action) {
Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId()) Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId()).build();
.build();
tx().lock(locator);
Resource resource = tx().findElement(locator); Resource resource = tx().findElement(locator);
if (resource == null)
throw new StrolchException("Resource with " + locator + " referenced by " + action.getLocator()
+ " cannot be null!");
tx().lock(resource);
List<IValueChange<? extends IValue<?>>> changes = action.getChanges(); List<IValueChange<? extends IValue<?>>> changes = action.getChanges();
for (IValueChange<?> change : changes) { for (IValueChange<?> change : changes) {
StrolchTimedState timedState = resource.getTimedState(change.getStateId()); StrolchTimedState timedState = resource.getTimedState(change.getStateId());
@ -80,7 +75,7 @@ public abstract class AbstractPlanCommand extends Command {
* {@link IActivityElement} class. * {@link IActivityElement} class.
*/ */
protected void plan(Activity activity) { protected void plan(Activity activity) {
// TODO Martin: Use a visitor pattern so we don't start with instanceof again... // TODO Martin: Use a visitor pattern so we don't start with instanceof again...
Iterator<Entry<String, IActivityElement>> elementIterator = activity.elementIterator(); Iterator<Entry<String, IActivityElement>> elementIterator = activity.elementIterator();
@ -97,8 +92,7 @@ public abstract class AbstractPlanCommand extends Command {
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
protected void unplan(Action action) { protected void unplan(Action action) {
Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId()) Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId()).build();
.build();
Resource resource = tx().findElement(locator); Resource resource = tx().findElement(locator);
List<IValueChange<? extends IValue<?>>> changes = action.getChanges(); List<IValueChange<? extends IValue<?>>> changes = action.getChanges();

View File

@ -30,13 +30,11 @@ import li.strolch.service.api.Command;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
/** /**
* Command to plan an {@link Activity} to a {@link Resource}. This * Command to plan an {@link Activity} to a {@link Resource}. This {@link Command} assumes that the {@link IValueChange}
* {@link Command} assumes that the {@link IValueChange} objects of the action * objects of the action are already constructed and {@link Action#resourceId} is set.
* are already constructed and {@link Action#resourceId} is set.
* <p/> * <p/>
* It iterates the {@link IValueChange} operators and registers the resulting * It iterates the {@link IValueChange} operators and registers the resulting changes on the {@link StrolchTimedState}
* changes on the {@link StrolchTimedState} objects assigned to the * objects assigned to the {@link Resource}.
* {@link Resource}.
* *
* @author Martin Smock <martin.smock@bluewin.ch> * @author Martin Smock <martin.smock@bluewin.ch>
*/ */
@ -76,14 +74,13 @@ public class PlanActivityCommand extends AbstractPlanCommand {
@Override @Override
public void doCommand() { public void doCommand() {
validate();
tx().lock(activity); tx().lock(activity);
validate();
plan(activity); plan(activity);
} }
@Override @Override
public void undo() { public void undo() {
tx().lock(activity);
unplan(activity); unplan(activity);
} }