/* * Copyright 2013 Robert von Burg <eitch@eitchnet.ch> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package li.strolch.persistence.api; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import li.strolch.agent.api.ActivityMap; import li.strolch.agent.api.AuditTrail; 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.AuditingActivityMap; import li.strolch.agent.impl.AuditingAuditMapFacade; import li.strolch.agent.impl.AuditingOrderMap; import li.strolch.agent.impl.AuditingResourceMap; import li.strolch.agent.impl.InternalStrolchRealm; import li.strolch.exception.StrolchAccessDeniedException; import li.strolch.exception.StrolchException; import li.strolch.model.GroupedParameterizedElement; import li.strolch.model.Locator; import li.strolch.model.Order; import li.strolch.model.ParameterBag; import li.strolch.model.Resource; import li.strolch.model.StrolchElement; import li.strolch.model.StrolchRootElement; import li.strolch.model.Tags; import li.strolch.model.activity.Activity; import li.strolch.model.audit.AccessType; import li.strolch.model.audit.Audit; import li.strolch.model.audit.AuditQuery; import li.strolch.model.parameter.Parameter; import li.strolch.model.parameter.StringListParameter; import li.strolch.model.parameter.StringParameter; import li.strolch.model.query.ActivityQuery; import li.strolch.model.query.OrderQuery; import li.strolch.model.query.ResourceQuery; import li.strolch.model.query.StrolchQuery; import li.strolch.model.timedstate.StrolchTimedState; import li.strolch.model.timevalue.IValue; import li.strolch.model.visitor.ElementTypeVisitor; import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.PrivilegeContext; import li.strolch.runtime.StrolchConstants; import li.strolch.runtime.privilege.PrivilegeHandler; import li.strolch.service.api.Command; import li.strolch.utils.dbc.DBC; import li.strolch.utils.helper.ExceptionHelper; import li.strolch.utils.helper.StringHelper; /** * @author Robert von Burg <eitch@eitchnet.ch> */ public abstract class AbstractTransaction implements StrolchTransaction { protected static final Logger logger = LoggerFactory.getLogger(AbstractTransaction.class); private InternalStrolchRealm realm; private TransactionCloseStrategy closeStrategy; private boolean suppressUpdates; private boolean suppressAudits; private boolean suppressDoNothingLogging; private TransactionResult txResult; private List<Command> commands; private List<Command> flushedCommands; private Set<StrolchRootElement> lockedElements; private AuditingOrderMap orderMap; private AuditingResourceMap resourceMap; private AuditingActivityMap activityMap; private AuditingAuditMapFacade auditTrail; private String action; private Certificate certificate; private PrivilegeHandler privilegeHandler; public AbstractTransaction(PrivilegeHandler privilegeHandler, StrolchRealm realm, Certificate certificate, String action) { DBC.PRE.assertNotNull("privilegeHandler must be set!", privilegeHandler); //$NON-NLS-1$ DBC.PRE.assertNotNull("realm must be set!", realm); //$NON-NLS-1$ DBC.PRE.assertNotNull("certificate must be set!", certificate); //$NON-NLS-1$ DBC.PRE.assertNotNull("action must be set!", action); //$NON-NLS-1$ this.privilegeHandler = privilegeHandler; this.realm = (InternalStrolchRealm) realm; this.action = action; this.certificate = certificate; this.commands = new ArrayList<>(); this.flushedCommands = new ArrayList<>(); this.lockedElements = new HashSet<>(); this.closeStrategy = TransactionCloseStrategy.DO_NOTHING; this.txResult = new TransactionResult(getRealmName(), System.nanoTime(), new Date()); this.txResult.setState(TransactionState.OPEN); } @Override public TransactionState getState() { return this.txResult.getState(); } @Override public boolean isRollingBack() { return this.txResult.getState().isRollingBack(); } @Override public boolean isCommitting() { return this.txResult.getState().isCommitting(); } @Override public boolean isClosing() { return this.txResult.getState().isClosing(); } @Override public String getRealmName() { return this.realm.getRealm(); } protected StrolchRealm getRealm() { return this.realm; } @Override public Certificate getCertificate() { return certificate; } @Override public TransactionCloseStrategy getCloseStrategy() { return this.closeStrategy; } private void setCloseStrategy(TransactionCloseStrategy closeStrategy) { this.closeStrategy = closeStrategy; } @Override public void close() throws StrolchTransactionException { this.closeStrategy.close(this); } @Override public void doNothingOnClose() { setCloseStrategy(TransactionCloseStrategy.DO_NOTHING); } @Override public void commitOnClose() { setCloseStrategy(TransactionCloseStrategy.COMMIT); } @Override public void rollbackOnClose() { setCloseStrategy(TransactionCloseStrategy.ROLLBACK); } @Override public StrolchTransactionException fail(String string) { rollbackOnClose(); return new StrolchTransactionException(string); } @Override public void setSuppressUpdates(boolean suppressUpdates) { this.suppressUpdates = suppressUpdates; } @Override public boolean isSuppressUpdates() { return this.suppressUpdates; } @Override public void setSuppressAudits(boolean suppressAudits) { this.suppressAudits = suppressAudits; } @Override public boolean isSuppressAudits() { return this.suppressAudits; } @Override public boolean isSuppressDoNothingLogging() { return suppressDoNothingLogging; } @Override public void setSuppressDoNothingLogging(boolean quietDoNothing) { this.suppressDoNothingLogging = quietDoNothing; } @Override public <T extends StrolchRootElement> void lock(T element) throws StrolchLockException { this.realm.lock(element); this.lockedElements.add(element); } @Override public <T extends StrolchRootElement> void releaseLock(T element) throws StrolchLockException { this.realm.releaseLock(element); this.lockedElements.remove(element); } private void releaseElementLocks() { for (StrolchRootElement lockedElement : this.lockedElements) { this.realm.releaseLock(lockedElement); } } @Override public void addCommand(Command command) { this.commands.add(command); } @Override public ResourceMap getResourceMap() { if (this.resourceMap == null) { this.resourceMap = new AuditingResourceMap(this.realm.getResourceMap(), this.realm.isAuditTrailEnabledForRead()); } return this.resourceMap; } @Override public OrderMap getOrderMap() { if (this.orderMap == null) { this.orderMap = new AuditingOrderMap(this.realm.getOrderMap(), this.realm.isAuditTrailEnabledForRead()); } return this.orderMap; } @Override public ActivityMap getActivityMap() { if (this.activityMap == null) { this.activityMap = new AuditingActivityMap(this.realm.getActivityMap(), this.realm.isAuditTrailEnabledForRead()); } return this.activityMap; } @Override public AuditTrail getAuditTrail() { if (this.auditTrail == null) { this.auditTrail = new AuditingAuditMapFacade(this.realm.getAuditTrail(), this.realm.isAuditTrailEnabledForRead()); } return this.auditTrail; } private void assertQueryAllowed(StrolchQuery query) { try { PrivilegeContext privilegeContext = this.privilegeHandler.getPrivilegeContext(this.certificate); privilegeContext.validateAction(query); } catch (PrivilegeException e) { throw new StrolchAccessDeniedException(this.certificate, query, ExceptionHelper.getExceptionMessage(e), e); } } @Override public <U> List<U> doQuery(OrderQuery<U> query) { assertQueryAllowed(query); DBC.PRE.assertNotNull("orderVisitor", query.getOrderVisitor()); return getOrderMap().doQuery(this, query); } @Override public <U> List<U> doQuery(ResourceQuery<U> query) { assertQueryAllowed(query); DBC.PRE.assertNotNull("resourceVisitor", query.getResourceVisitor()); return getResourceMap().doQuery(this, query); } @Override public <U> List<U> doQuery(ActivityQuery<U> query) { assertQueryAllowed(query); DBC.PRE.assertNotNull("activityVisitor", query.getActivityVisitor()); return getActivityMap().doQuery(this, query); } @Override public <U> List<U> doQuery(AuditQuery<U> query) { assertQueryAllowed(query); DBC.PRE.assertNotNull("auditVisitor", query.getAuditVisitor()); return getAuditTrail().doQuery(this, query); } @SuppressWarnings("unchecked") @Override public <T extends StrolchElement> T findElement(Locator locator) { // Resource/<type>/<id> // Resource/<type>/<id>/Bag/<id> // Resource/<type>/<id>/Bag/<id>/<param_id> // Resource/<type>/<id>/State/<id> // Order/<type>/<id> // Order/<type>/<id>/Bag/<id> // Order/<type>/<id>/Bag/<id>/<param_id> if (locator.getSize() < 3) { String msg = "The locator is invalid as it does not have at least three path elements (e.g. Resource/MyType/@id): {0}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, locator.toString()); throw new StrolchException(msg); } List<String> elements = locator.getPathElements(); GroupedParameterizedElement groupedParameterizedElement; String objectClassType = elements.get(0); String type = elements.get(1); String id = elements.get(2); switch (objectClassType) { case Tags.RESOURCE: groupedParameterizedElement = getResourceMap().getBy(this, type, id); break; case Tags.ORDER: groupedParameterizedElement = getOrderMap().getBy(this, type, id); break; default: throw new StrolchException(MessageFormat.format("Unknown object class {0}", objectClassType)); //$NON-NLS-1$ } if (groupedParameterizedElement == null) { String msg = "No top level object could be found with locator {0}"; //$NON-NLS-1$ throw new StrolchException(MessageFormat.format(msg, locator)); } if (elements.size() == 3) return (T) groupedParameterizedElement; // state or bag String stateOrBag = elements.get(3); if (stateOrBag.equals(Tags.BAG)) { String parameterBagId = elements.get(4); ParameterBag bag = groupedParameterizedElement.getParameterBag(parameterBagId); if (bag == null) { String msg = "Could not find ParameterBag for locator {0} on element {1}"; //$NON-NLS-1$ throw new StrolchException( MessageFormat.format(msg, locator, groupedParameterizedElement.getLocator())); } if (elements.size() == 5) return (T) bag; String parameterId = elements.get(5); Parameter<?> parameter = bag.getParameter(parameterId); return (T) parameter; } else if (stateOrBag.equals(Tags.STATE)) { if (elements.size() != 5) { String msg = "Missing state Id on locator {0}"; //$NON-NLS-1$ throw new StrolchException(MessageFormat.format(msg, locator)); } Resource resource = (Resource) groupedParameterizedElement; String stateId = elements.get(4); StrolchTimedState<IValue<?>> timedState = resource.getTimedState(stateId); return (T) timedState; } String msg = "Invalid locator {0} on with part {1}"; //$NON-NLS-1$ 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); } @Override public Order getOrderBy(String type, String id, boolean assertExists) throws StrolchException { Order order = getOrderMap().getBy(this, type, id); if (assertExists && order == null) { String msg = "No Order exists with the id {0} with type {1}"; throw new StrolchException(MessageFormat.format(msg, id, type)); } return order; } @Override public Order getOrderBy(StringParameter refP) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getOrderBy(refP, false); } @Override public Order getOrderBy(StringParameter refP, boolean assertExists) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getOrderMap().getBy(this, refP, assertExists); } @Override public List<Order> getOrdersBy(StringListParameter refP) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getOrderMap().getBy(this, refP, false); } @Override public List<Order> getOrdersBy(StringListParameter refP, boolean assertExists) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getOrderMap().getBy(this, refP, assertExists); } @Override public Resource getResourceBy(String type, String id) { return getResourceBy(type, id, false); } @Override public Resource getResourceBy(String type, String id, boolean assertExists) throws StrolchException { Resource resource = getResourceMap().getBy(this, type, id); if (assertExists && resource == null) { String msg = "No Resource exists with the id {0} with type {1}"; throw new StrolchException(MessageFormat.format(msg, id, type)); } return resource; } @Override public Resource getResourceBy(StringParameter refP) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getResourceBy(refP, false); } @Override public Resource getResourceBy(StringParameter refP, boolean assertExists) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getResourceMap().getBy(this, refP, assertExists); } @Override public List<Resource> getResourcesBy(StringListParameter refP) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getResourceMap().getBy(this, refP, false); } @Override public List<Resource> getResourcesBy(StringListParameter refP, boolean assertExists) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getResourceMap().getBy(this, refP, assertExists); } @Override public Activity getActivityBy(String type, String id) { return getActivityBy(type, id, false); } @Override public Activity getActivityBy(String type, String id, boolean assertExists) throws StrolchException { Activity activity = getActivityMap().getBy(this, type, id); if (assertExists && activity == null) { String msg = "No Activity exists with the id {0} with type {1}"; throw new StrolchException(MessageFormat.format(msg, id, type)); } return activity; } @Override public Activity getActivityBy(StringParameter refP) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getActivityBy(refP, false); } @Override public Activity getActivityBy(StringParameter refP, boolean assertExists) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getActivityMap().getBy(this, refP, assertExists); } @Override public List<Activity> getActivitiesBy(StringListParameter refP) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getActivityMap().getBy(this, refP, false); } @Override public List<Activity> getActivitiesBy(StringListParameter refP, boolean assertExists) throws StrolchException { DBC.PRE.assertNotNull("refP", refP); return getActivityMap().getBy(this, refP, assertExists); } @Override public void flush() { try { validateCommands(); doCommands(); writeChanges(this.txResult); } catch (Exception e) { this.closeStrategy = TransactionCloseStrategy.ROLLBACK; String msg = "Strolch Transaction for realm {0} failed due to {1}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, getRealmName(), ExceptionHelper.getExceptionMessage(e)); throw new StrolchTransactionException(msg, e); } } @Override public void autoCloseableCommit() { long start = System.nanoTime(); try { this.txResult.setState(TransactionState.COMMITTING); validateCommands(); doCommands(); writeChanges(this.txResult); long auditTrailDuration = writeAuditTrail(); long updateObserversDuration = updateObservers(); // commit and close the connection commit(); handleCommit(start, auditTrailDuration, updateObserversDuration); this.txResult.setState(TransactionState.COMMITTED); } catch (Exception e) { this.txResult.setState(TransactionState.ROLLING_BACK); try { undoCommands(); } catch (Exception e2) { try { rollback(this.txResult); handleRollback(start); } catch (Exception e1) { logger.error("Failed to roll back after failing to undo commands: " + e1.getMessage(), e1); //$NON-NLS-1$ } handleFailure(start, e); } try { rollback(this.txResult); handleRollback(start); } catch (Exception e1) { handleFailure(start, e); } this.txResult.setState(TransactionState.FAILED); String msg = "Strolch Transaction for realm {0} failed due to {1}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, getRealmName(), ExceptionHelper.getExceptionMessage(e)); throw new StrolchTransactionException(msg, e); } finally { releaseElementLocks(); } } @Override public void autoCloseableRollback() { long start = System.nanoTime(); logger.warn(MessageFormat.format("Rolling back TX for realm {0}...", getRealmName())); //$NON-NLS-1$ try { this.txResult.setState(TransactionState.ROLLING_BACK); undoCommands(); rollback(this.txResult); handleRollback(start); this.txResult.setState(TransactionState.ROLLED_BACK); } catch (Exception e) { handleFailure(start, e); this.txResult.setState(TransactionState.FAILED); } finally { releaseElementLocks(); } } @Override public void autoCloseableDoNothing() throws StrolchTransactionException { long start = System.nanoTime(); try { this.txResult.setState(TransactionState.CLOSING); // TODO re-think this. if (!this.commands.isEmpty()) { logger.error( "There are commands registered on a read-only transaction. Changing to rollback! Probably due to an exception!"); autoCloseableRollback(); return; } long auditTrailDuration = writeAuditTrail(); // rollback and release any resources rollback(this.txResult); handleDoNothing(start, auditTrailDuration); this.txResult.setState(TransactionState.CLOSED); } catch (Exception e) { handleFailure(start, e); this.txResult.setState(TransactionState.FAILED); } finally { releaseElementLocks(); } } protected abstract void writeChanges(TransactionResult txResult) throws Exception; protected abstract void rollback(TransactionResult txResult) throws Exception; protected abstract void commit() throws Exception; private void handleDoNothing(long start, long auditTrailDuration) { if (this.suppressDoNothingLogging) return; long end = System.nanoTime(); long txDuration = end - this.txResult.getStartNanos(); long closeDuration = end - start; this.txResult.setTxDuration(txDuration); this.txResult.setCloseDuration(closeDuration); StringBuilder sb = new StringBuilder(); sb.append("TX user="); sb.append(this.certificate.getUsername()); sb.append(", realm="); //$NON-NLS-1$ sb.append(getRealmName()); sb.append(", took="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(txDuration)); sb.append(", action="); sb.append(this.action); if (closeDuration >= 100000000L) { sb.append(", close="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(closeDuration)); } if (isAuditTrailEnabled() && auditTrailDuration >= 100000000L) { sb.append(", auditTrail="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(auditTrailDuration)); } logger.info(sb.toString()); } private void handleCommit(long start, long auditTrailDuration, long observerUpdateDuration) { long end = System.nanoTime(); long txDuration = end - this.txResult.getStartNanos(); long closeDuration = end - start; this.txResult.setTxDuration(txDuration); this.txResult.setCloseDuration(closeDuration); StringBuilder sb = new StringBuilder(); sb.append("TX user="); sb.append(this.certificate.getUsername()); sb.append(", realm="); //$NON-NLS-1$ sb.append(getRealmName()); sb.append(", took="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(txDuration)); sb.append(", action="); sb.append(this.action); if (closeDuration >= 100000000L) { sb.append(", close="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(closeDuration)); } if (isAuditTrailEnabled() && auditTrailDuration >= 100000000L) { sb.append(", auditTrail="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(auditTrailDuration)); } if (isObserverUpdatesEnabled() && observerUpdateDuration >= 100000000L) { sb.append(", updates="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(observerUpdateDuration)); } logger.info(sb.toString()); } private void handleRollback(long start) { long end = System.nanoTime(); long txDuration = end - this.txResult.getStartNanos(); long closeDuration = end - start; this.txResult.setTxDuration(txDuration); this.txResult.setCloseDuration(closeDuration); StringBuilder sb = new StringBuilder(); sb.append("TX ROLLBACK user="); sb.append(this.certificate.getUsername()); sb.append(", realm="); //$NON-NLS-1$ sb.append(getRealmName()); sb.append(" failed="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(txDuration)); sb.append(", action="); sb.append(this.action); if (closeDuration >= 100000000L) { sb.append(", close="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(closeDuration)); } logger.error(sb.toString()); } protected void handleFailure(long closeStartNanos, Exception e) { long end = System.nanoTime(); long txDuration = end - this.txResult.getStartNanos(); long closeDuration = end - closeStartNanos; this.txResult.setState(TransactionState.FAILED); this.txResult.setTxDuration(txDuration); this.txResult.setCloseDuration(closeDuration); StringBuilder sb = new StringBuilder(); sb.append("TX FAILED user="); sb.append(this.certificate.getUsername()); sb.append(", realm="); //$NON-NLS-1$ sb.append(getRealmName()); sb.append(" failed="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(txDuration)); sb.append(", action="); sb.append(this.action); if (closeDuration >= 100000000L) { sb.append(", close="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(closeDuration)); } String msg = "Strolch Transaction for realm {0} failed due to {1}\n{2}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, getRealmName(), ExceptionHelper.getExceptionMessage(e), sb.toString()); throw new StrolchTransactionException(msg, e); } private boolean isAuditTrailEnabled() { return getAuditTrail().isEnabled(); } private long updateObservers() { if (!isObserverUpdatesEnabled()) return 0L; long observerUpdateStart = System.nanoTime(); ObserverHandler observerHandler = this.realm.getObserverHandler(); if (this.orderMap != null) { observerHandler.add(Tags.ORDER, new ArrayList<StrolchRootElement>(this.orderMap.getCreated())); observerHandler.update(Tags.ORDER, new ArrayList<StrolchRootElement>(this.orderMap.getUpdated())); observerHandler.remove(Tags.ORDER, new ArrayList<StrolchRootElement>(this.orderMap.getDeleted())); } if (this.resourceMap != null) { observerHandler.add(Tags.RESOURCE, new ArrayList<StrolchRootElement>(this.resourceMap.getCreated())); observerHandler.update(Tags.RESOURCE, new ArrayList<StrolchRootElement>(this.resourceMap.getUpdated())); observerHandler.remove(Tags.RESOURCE, new ArrayList<StrolchRootElement>(this.resourceMap.getDeleted())); } long observerUpdateDuration = System.nanoTime() - observerUpdateStart; return observerUpdateDuration; } private boolean isObserverUpdatesEnabled() { return !this.suppressUpdates && this.realm.isUpdateObservers(); } private long writeAuditTrail() { if (!isAuditTrailEnabled()) return 0L; if (isSuppressAudits()) return 0L; long auditTrailStart = System.nanoTime(); List<Audit> audits = new ArrayList<>(); if (this.orderMap != null) { if (this.realm.isAuditTrailEnabledForRead()) auditsFor(audits, AccessType.READ, Tags.ORDER, this.orderMap.getRead()); auditsFor(audits, AccessType.CREATE, Tags.ORDER, this.orderMap.getCreated()); auditsFor(audits, AccessType.UPDATE, Tags.ORDER, this.orderMap.getUpdated()); auditsFor(audits, AccessType.DELETE, Tags.ORDER, this.orderMap.getDeleted()); } if (this.resourceMap != null) { if (this.realm.isAuditTrailEnabledForRead()) auditsFor(audits, AccessType.READ, Tags.RESOURCE, this.resourceMap.getRead()); auditsFor(audits, AccessType.CREATE, Tags.RESOURCE, this.resourceMap.getCreated()); auditsFor(audits, AccessType.UPDATE, Tags.RESOURCE, this.resourceMap.getUpdated()); auditsFor(audits, AccessType.DELETE, Tags.RESOURCE, this.resourceMap.getDeleted()); } if (this.auditTrail != null) { if (this.realm.isAuditTrailEnabledForRead()) auditsForAudits(audits, AccessType.READ, Tags.AUDIT, this.auditTrail.getRead()); auditsForAudits(audits, AccessType.CREATE, Tags.AUDIT, this.auditTrail.getCreated()); auditsForAudits(audits, AccessType.UPDATE, Tags.AUDIT, this.auditTrail.getUpdated()); auditsForAudits(audits, AccessType.DELETE, Tags.AUDIT, this.auditTrail.getDeleted()); } this.realm.getAuditTrail().addAll(this, audits); long auditTrailDuration = System.nanoTime() - auditTrailStart; return auditTrailDuration; } private <T extends StrolchRootElement> void auditsFor(List<Audit> audits, AccessType accessType, String elementType, Set<T> elements) { for (StrolchRootElement element : elements) { audits.add(auditFrom(accessType, element)); } } private <T extends StrolchRootElement> void auditsForAudits(List<Audit> audits, AccessType accessType, String elementType, Set<Audit> elements) { for (Audit element : elements) { audits.add(auditFrom(accessType, elementType, StringHelper.DASH, element.getId().toString())); } } @Override public Audit auditFrom(AccessType accessType, StrolchRootElement element) { String type = element.accept(new ElementTypeVisitor()); String subType = element.getType(); String id = element.getId(); return auditFrom(accessType, type, subType, id); } @Override public Audit auditFrom(AccessType accessType, String elementType, String elementSubType, String id) { Audit audit = new Audit(); audit.setId(StrolchAgent.getUniqueIdLong()); audit.setUsername(this.certificate.getUsername()); audit.setFirstname(this.certificate.getFirstname()); audit.setLastname(this.certificate.getLastname()); audit.setDate(new Date()); audit.setElementType(elementType); audit.setElementSubType(elementSubType); audit.setElementAccessed(id); // audit.setNewVersion(); audit.setAction(this.action); audit.setAccessType(accessType); return audit; } /** * Calls {@link Command#validate()} on all registered command. This is done before we perform any commands and thus * no rollback needs be done due to invalid input for a command */ private void validateCommands() { for (Command command : this.commands) { command.validate(); } } /** * Calls {@link Command#doCommand()} on all registered commands. This is done after the commands have been validated * so chance of a runtime exception should be small */ private void doCommands() { ListIterator<Command> iter = this.commands.listIterator(); while (iter.hasNext()) { Command command = iter.next(); command.doCommand(); this.flushedCommands.add(command); iter.remove(); } } /** * Calls {@link Command#undo()} on all registered commands. This is done when an exception is caught while * performing the commands */ private void undoCommands() { ListIterator<Command> iter = this.flushedCommands.listIterator(this.flushedCommands.size()); while (iter.hasPrevious()) { Command command = iter.previous(); command.undo(); iter.remove(); } } }