001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.processor.saga;
018
019import java.util.concurrent.CompletableFuture;
020
021import org.apache.camel.AsyncCallback;
022import org.apache.camel.CamelContext;
023import org.apache.camel.Exchange;
024import org.apache.camel.Processor;
025import org.apache.camel.model.SagaCompletionMode;
026import org.apache.camel.processor.DelegateAsyncProcessor;
027import org.apache.camel.saga.CamelSagaCoordinator;
028import org.apache.camel.saga.CamelSagaService;
029import org.apache.camel.saga.CamelSagaStep;
030import org.apache.camel.util.ObjectHelper;
031
032/**
033 * Processor for handling sagas.
034 */
035public abstract class SagaProcessor extends DelegateAsyncProcessor {
036
037    protected CamelContext camelContext;
038
039    protected CamelSagaService sagaService;
040
041    protected CamelSagaStep step;
042
043    protected SagaCompletionMode completionMode;
044
045    public SagaProcessor(CamelContext camelContext, Processor childProcessor, CamelSagaService sagaService, SagaCompletionMode completionMode, CamelSagaStep step) {
046        super(ObjectHelper.notNull(childProcessor, "childProcessor"));
047        this.camelContext = ObjectHelper.notNull(camelContext, "camelContext");
048        this.sagaService = ObjectHelper.notNull(sagaService, "sagaService");
049        this.completionMode = ObjectHelper.notNull(completionMode, "completionMode");
050        this.step = ObjectHelper.notNull(step, "step");
051    }
052
053    protected CompletableFuture<CamelSagaCoordinator> getCurrentSagaCoordinator(Exchange exchange) {
054        String currentSaga = exchange.getIn().getHeader(Exchange.SAGA_LONG_RUNNING_ACTION, String.class);
055        if (currentSaga != null) {
056            return sagaService.getSaga(currentSaga);
057        }
058
059        return CompletableFuture.completedFuture(null);
060    }
061
062    protected void setCurrentSagaCoordinator(Exchange exchange, CamelSagaCoordinator coordinator) {
063        if (coordinator != null) {
064            exchange.getIn().setHeader(Exchange.SAGA_LONG_RUNNING_ACTION, coordinator.getId());
065        } else {
066            exchange.getIn().removeHeader(Exchange.SAGA_LONG_RUNNING_ACTION);
067        }
068    }
069
070    protected void handleSagaCompletion(Exchange exchange, CamelSagaCoordinator coordinator, CamelSagaCoordinator previousCoordinator, AsyncCallback callback) {
071        if (this.completionMode == SagaCompletionMode.AUTO) {
072            if (exchange.getException() != null) {
073                coordinator.compensate().whenComplete((done, ex) -> ifNotException(ex, exchange, callback, () -> {
074                    setCurrentSagaCoordinator(exchange, previousCoordinator);
075                    callback.done(false);
076                }));
077            } else {
078                coordinator.complete().whenComplete((done, ex) -> ifNotException(ex, exchange, callback, () -> {
079                    setCurrentSagaCoordinator(exchange, previousCoordinator);
080                    callback.done(false);
081                }));
082            }
083        } else if (this.completionMode == SagaCompletionMode.MANUAL) {
084            // Completion will be handled manually by the user
085            callback.done(false);
086        } else {
087            throw new IllegalStateException("Unsupported completion mode: " + this.completionMode);
088        }
089    }
090
091    public CamelSagaService getSagaService() {
092        return sagaService;
093    }
094
095    @Override
096    public String toString() {
097        return "saga";
098    }
099
100    protected void ifNotException(Throwable ex, Exchange exchange, AsyncCallback callback, Runnable code) {
101        if (ex != null) {
102            exchange.setException(ex);
103            callback.done(false);
104        } else {
105            code.run();
106        }
107    }
108
109}