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

View File

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

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

View File

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

View File

@ -79,7 +79,7 @@ import li.strolch.service.api.Command;
* </p>
* <ul>
* <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>exception handling</li>
* <li>auditing</li>
@ -333,6 +333,17 @@ public interface StrolchTransaction extends AutoCloseable {
*/
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
* closed
@ -345,8 +356,8 @@ public interface StrolchTransaction extends AutoCloseable {
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
* thread/transaction can lock the element
* Releases the lock of the 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
@ -355,6 +366,17 @@ public interface StrolchTransaction extends AutoCloseable {
*/
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
* executed properly:

View File

@ -186,6 +186,12 @@ public class Locator {
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
* values

View File

@ -6,6 +6,7 @@ import li.strolch.execution.command.SetActionToErrorCommand;
import li.strolch.execution.command.SetActionToExecutedCommand;
import li.strolch.execution.command.SetActionToStoppedCommand;
import li.strolch.execution.command.SetActionToWarningCommand;
import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.model.Locator;
import li.strolch.model.activity.Action;
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.privilege.model.Certificate;
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 {
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)) {
IActivityElement activityElement = tx.findElement(locator);
Activity activity = activityElement.getRootElement();
Locator rootElemLoc = activityLoc.trim(3);
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);
command.setActivity(activity);
command.setActivity((Activity) elem);
tx.addCommand(command);
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)) {
Action action = tx.findElement(locator);
activityLoc = action.getRootElement().getLocator();
tx.lock(activityLoc);
IActivityElement elem = tx.findElement(actionLoc);
DBC.INTERIM.assertEquals("toExecuted only for Action!", Action.class, elem.getClass());
SetActionToExecutedCommand command = new SetActionToExecutedCommand(getContainer(), tx);
command.setAction(action);
command.setAction((Action) elem);
tx.addCommand(command);
tx.commitOnClose();
@ -107,36 +120,48 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
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)) {
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);
command.setAction(action);
command.setAction((Action) elem);
tx.addCommand(command);
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)) {
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);
command.setAction(action);
command.setAction((Action) elem);
tx.addCommand(command);
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)) {
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);
command.setAction(action);
command.setAction((Action) elem);
tx.addCommand(command);
tx.commitOnClose();

View File

@ -2,23 +2,92 @@ package li.strolch.execution;
import li.strolch.agent.api.ComponentContainer;
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.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 ExecutionHandler(ComponentContainer container, String 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 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 "
+ 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);
if (action.getType().equals(TYPE_RESERVE))
return !reservedP.getValue();

View File

@ -20,7 +20,6 @@ import java.util.List;
import java.util.Map.Entry;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.exception.StrolchException;
import li.strolch.model.Locator;
import li.strolch.model.Resource;
import li.strolch.model.State;
@ -56,16 +55,12 @@ public abstract class AbstractPlanCommand extends Command {
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void plan(Action action) {
Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId())
.build();
Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId()).build();
tx().lock(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();
for (IValueChange<?> change : changes) {
StrolchTimedState timedState = resource.getTimedState(change.getStateId());
@ -80,7 +75,7 @@ public abstract class AbstractPlanCommand extends Command {
* {@link IActivityElement} class.
*/
protected void plan(Activity activity) {
// TODO Martin: Use a visitor pattern so we don't start with instanceof again...
Iterator<Entry<String, IActivityElement>> elementIterator = activity.elementIterator();
@ -97,8 +92,7 @@ public abstract class AbstractPlanCommand extends Command {
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void unplan(Action action) {
Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId())
.build();
Locator locator = Locator.newBuilder(Tags.RESOURCE, action.getResourceType(), action.getResourceId()).build();
Resource resource = tx().findElement(locator);
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;
/**
* Command to plan an {@link Activity} to a {@link Resource}. This
* {@link Command} assumes that the {@link IValueChange} objects of the action
* are already constructed and {@link Action#resourceId} is set.
* Command to plan an {@link Activity} to a {@link Resource}. This {@link Command} assumes that the {@link IValueChange}
* objects of the action are already constructed and {@link Action#resourceId} is set.
* <p/>
* It iterates the {@link IValueChange} operators and registers the resulting
* changes on the {@link StrolchTimedState} objects assigned to the
* {@link Resource}.
* It iterates the {@link IValueChange} operators and registers the resulting changes on the {@link StrolchTimedState}
* objects assigned to the {@link Resource}.
*
* @author Martin Smock <martin.smock@bluewin.ch>
*/
@ -76,14 +74,13 @@ public class PlanActivityCommand extends AbstractPlanCommand {
@Override
public void doCommand() {
validate();
tx().lock(activity);
validate();
plan(activity);
}
@Override
public void undo() {
tx().lock(activity);
unplan(activity);
}