/** * Pause this Unit of Work by unregistering it with the {@link CurrentUnitOfWork}. This will detach it from the * current thread. */ public void pause() { CurrentUnitOfWork.clear(this); }
@Override public Boolean resolveParameterValue(Message<?> message) { return CurrentUnitOfWork.map(unitOfWork -> !(unitOfWork instanceof BatchingUnitOfWork<?>) || ((BatchingUnitOfWork<?>) unitOfWork).isLastMessage(message)).orElse(true); }
@Override public void start() { if (logger.isDebugEnabled()) { logger.debug("Starting Unit Of Work"); } Assert.state(Phase.NOT_STARTED.equals(phase()), () -> "UnitOfWork is already started"); rolledBack = false; onRollback(u -> rolledBack = true); CurrentUnitOfWork.ifStarted(parent -> { // we're nesting. this.parentUnitOfWork = parent; root().onCleanup(r -> changePhase(Phase.CLEANUP, Phase.CLOSED)); }); changePhase(Phase.STARTED); CurrentUnitOfWork.set(this); }
/** * Commits the current UnitOfWork. If no UnitOfWork was started, an {@link IllegalStateException} is thrown. * * @throws IllegalStateException if no UnitOfWork is currently started. * @see UnitOfWork#commit() */ public static void commit() { get().commit(); }
/** * Clears the UnitOfWork currently bound to the current thread, if that UnitOfWork is the given * {@code unitOfWork}. * * @param unitOfWork The UnitOfWork expected to be bound to the current thread. * @throws IllegalStateException when the given UnitOfWork was not the current active UnitOfWork. This exception * indicates a potentially wrong nesting of Units Of Work. */ public static void clear(UnitOfWork<?> unitOfWork) { if (!isStarted()) { throw new IllegalStateException("Could not clear this UnitOfWork. There is no UnitOfWork active."); } if (CURRENT.get().peek() == unitOfWork) { CURRENT.get().pop(); if (CURRENT.get().isEmpty()) { CURRENT.remove(); } } else { throw new IllegalStateException("Could not clear this UnitOfWork. It is not the active one."); } }
/** * Resume a paused Unit of Work by registering it with the {@link CurrentUnitOfWork}. This will attach it to the * current thread again. */ public void resume() { CurrentUnitOfWork.set(this); }
/** * Constructs a Message for the given {@code payload} and {@code meta data}. The given {@code metaData} is * merged with the MetaData from the correlation data of the current unit of work, if present. * * @param declaredPayloadType The declared type of message payload * @param payload The payload for the message * @param metaData The meta data for the message */ public GenericMessage(Class<T> declaredPayloadType, T payload, Map<String, ?> metaData) { this(IdentifierFactory.getInstance().generateIdentifier(), declaredPayloadType, payload, CurrentUnitOfWork.correlationData().mergedWith(MetaData.from(metaData))); }
@Override public Aggregate<T> newInstance(Callable<T> factoryMethod) throws Exception { CurrentUnitOfWork.get().onRollback(u -> this.rolledBack = true); aggregate = delegate.newInstance(factoryMethod); return aggregate; }
@Override public void onResult(CommandMessage<? extends C> commandMessage, CommandResultMessage<? extends R> commandResultMessage) { if (commandResultMessage.isExceptional()) { Throwable cause = commandResultMessage.exceptionResult(); history.add(simplify(cause)); try { // We fail immediately when the exception is checked, // or when it is a Deadlock Exception and we have an active unit of work. if (!(cause instanceof RuntimeException) || (isCausedBy(cause, DeadlockException.class) && CurrentUnitOfWork.isStarted()) || !retryScheduler.scheduleRetry(commandMessage, (RuntimeException) cause, new ArrayList<>(history), new RetryDispatch(commandMessage))) { delegate.onResult(commandMessage, commandResultMessage); } } catch (Exception e) { delegate.onResult(commandMessage, asCommandResultMessage(e)); } } else { delegate.onResult(commandMessage, commandResultMessage); } }
@Override public void start() { if (logger.isDebugEnabled()) { logger.debug("Starting Unit Of Work"); } Assert.state(Phase.NOT_STARTED.equals(phase()), () -> "UnitOfWork is already started"); rolledBack = false; onRollback(u -> rolledBack = true); CurrentUnitOfWork.ifStarted(parent -> { // we're nesting. this.parentUnitOfWork = parent; root().onCleanup(r -> changePhase(Phase.CLEANUP, Phase.CLOSED)); }); changePhase(Phase.STARTED); CurrentUnitOfWork.set(this); }
/** * Resume a paused Unit of Work by registering it with the {@link CurrentUnitOfWork}. This will attach it to the * current thread again. */ public void resume() { CurrentUnitOfWork.set(this); }
/** * Constructs a Message for the given {@code payload} and {@code meta data}. The given {@code metaData} is * merged with the MetaData from the correlation data of the current unit of work, if present. * * @param declaredPayloadType The declared type of message payload * @param payload The payload for the message * @param metaData The meta data for the message */ public GenericMessage(Class<T> declaredPayloadType, T payload, Map<String, ?> metaData) { this(IdentifierFactory.getInstance().generateIdentifier(), declaredPayloadType, payload, CurrentUnitOfWork.correlationData().mergedWith(MetaData.from(metaData))); }
/** * Check if the Unit of Work is the 'currently' active Unit of Work returned by {@link CurrentUnitOfWork#get()}. * * @return {@code true} if the Unit of Work is the currently active Unit of Work */ default boolean isCurrent() { return CurrentUnitOfWork.isStarted() && CurrentUnitOfWork.get() == this; }
/** * Either runs the provided {@link Runnable} immediately or adds it to a {@link List} as a resource to the current * {@link UnitOfWork} if {@link SimpleQueryUpdateEmitter#inStartedPhaseOfUnitOfWork} returns {@code true}. This is * done to * ensure any emitter calls made from a message handling function are executed in the * {@link UnitOfWork.Phase#AFTER_COMMIT} phase. * <p> * The latter check requires the current UnitOfWork's phase to be {@link UnitOfWork.Phase#STARTED}. This is done * to allow users to circumvent their {@code queryUpdateTask} being handled in the AFTER_COMMIT phase. They can do * this by retrieving the current UnitOfWork and performing any of the {@link QueryUpdateEmitter} calls in a * different phase. * * @param queryUpdateTask a {@link Runnable} to be ran immediately or as a resource if {@link * SimpleQueryUpdateEmitter#inStartedPhaseOfUnitOfWork} returns {@code true} */ private void runOnAfterCommitOrNow(Runnable queryUpdateTask) { if (inStartedPhaseOfUnitOfWork()) { UnitOfWork<?> unitOfWork = CurrentUnitOfWork.get(); unitOfWork.getOrComputeResource( this.toString() + QUERY_UPDATE_TASKS_RESOURCE_KEY, resourceKey -> { List<Runnable> queryUpdateTasks = new ArrayList<>(); unitOfWork.afterCommit(uow -> queryUpdateTasks.forEach(Runnable::run)); return queryUpdateTasks; } ).add(queryUpdateTask); } else { queryUpdateTask.run(); } }
/** * Clears the UnitOfWork currently bound to the current thread, if that UnitOfWork is the given * {@code unitOfWork}. * * @param unitOfWork The UnitOfWork expected to be bound to the current thread. * @throws IllegalStateException when the given UnitOfWork was not the current active UnitOfWork. This exception * indicates a potentially wrong nesting of Units Of Work. */ public static void clear(UnitOfWork<?> unitOfWork) { if (!isStarted()) { throw new IllegalStateException("Could not clear this UnitOfWork. There is no UnitOfWork active."); } if (CURRENT.get().peek() == unitOfWork) { CURRENT.get().pop(); if (CURRENT.get().isEmpty()) { CURRENT.remove(); } } else { throw new IllegalStateException("Could not clear this UnitOfWork. It is not the active one."); } }
@Override public InterceptorChain resolveParameterValue(Message<?> message) { return CurrentUnitOfWork.map(uow -> (InterceptorChain) uow.getResource(INTERCEPTOR_CHAIN_EMITTER_KEY)) .orElseThrow(() -> new IllegalStateException( "InterceptorChain should have been injected")); }
@Override public void commit() { if (logger.isDebugEnabled()) { logger.debug("Committing Unit Of Work"); } Assert.state(phase() == Phase.STARTED, () -> String.format("The UnitOfWork is in an incompatible phase: %s", phase())); Assert.state(isCurrent(), () -> "The UnitOfWork is not the current Unit of Work"); try { if (isRoot()) { commitAsRoot(); } else { commitAsNested(); } } finally { CurrentUnitOfWork.clear(this); } }
@Override public void start() { if (logger.isDebugEnabled()) { logger.debug("Starting Unit Of Work"); } Assert.state(Phase.NOT_STARTED.equals(phase()), () -> "UnitOfWork is already started"); rolledBack = false; onRollback(u -> rolledBack = true); CurrentUnitOfWork.ifStarted(parent -> { // we're nesting. this.parentUnitOfWork = parent; root().onCleanup(r -> changePhase(Phase.CLEANUP, Phase.CLOSED)); }); changePhase(Phase.STARTED); CurrentUnitOfWork.set(this); }
/** * Resume a paused Unit of Work by registering it with the {@link CurrentUnitOfWork}. This will attach it to the * current thread again. */ public void resume() { CurrentUnitOfWork.set(this); }