Package netflix.karyon.transport.interceptor

Source Code of netflix.karyon.transport.interceptor.InterceptorExecutor$ChainSubscriber

package netflix.karyon.transport.interceptor;

import io.reactivex.netty.channel.Handler;
import netflix.karyon.transport.RequestRouter;
import rx.Observable;
import rx.Subscriber;
import rx.subscriptions.SerialSubscription;

import java.util.ArrayList;
import java.util.List;

/**
* A utility class to execute a chain of interceptors defined by {@link InterceptorSupport}
*
* @author Nitesh Kant
*/
public class InterceptorExecutor<I, O, C extends KeyEvaluationContext> {

    private final List<InterceptorHolder<I, C, InboundInterceptor<I, O>>> allIn;
    private final List<InterceptorHolder<I, C, OutboundInterceptor<O>>> allOut;
    private final Handler<I, O> router;

    public InterceptorExecutor(AbstractInterceptorSupport<I, O, C, ?, ?> support, Handler<I, O> router) {
        this.router = router;
        allIn = support.getInboundInterceptors();
        allOut = support.getOutboundInterceptors();
    }

    /**
     * @deprecated Use {@link #InterceptorExecutor(AbstractInterceptorSupport, Handler)} instead.
     */
    @Deprecated
    public InterceptorExecutor(AbstractInterceptorSupport<I, O, C, ?, ?> support, final RequestRouter<I, O> router) {
        this.router = new Handler<I, O>() {
            @Override
            public Observable<Void> handle(I input, O output) {
                return router.route(input, output);
            }
        };
        allIn = support.getInboundInterceptors();
        allOut = support.getOutboundInterceptors();
    }

    /**
     * Executes the interceptor chain for the passed request and response.
     *
     * @param request Request to be executed.
     * @param response Response to be populated.
     * @param keyEvaluationContext The context for {@link InterceptorKey} evaluation.
     *
     * @return The final result of execution after executing all the inbound and outbound interceptors and the router.
     */
    public Observable<Void> execute(final I request, final O response, C keyEvaluationContext) {
        final ExecutionContext context = new ExecutionContext(request, keyEvaluationContext);
        InboundInterceptor<I, O> nextIn = context.nextIn(request);
        Observable<Void> startingPoint;

        if (null != nextIn) {
            startingPoint = nextIn.in(request, response);
        } else if (context.invokeRouter()){
            startingPoint = router.handle(request, response);
        } else {
            return Observable.error(new IllegalStateException("No router defined.")); // No router defined.
        }

        return startingPoint.lift(new Observable.Operator<Void, Void>() {
            @Override
            public Subscriber<? super Void> call(Subscriber<? super Void> child) {
                SerialSubscription subscription = new SerialSubscription();
                return new ChainSubscriber(subscription, context, request, response, child);
            }
        });
    }

    private enum NextExecutionState { NotStarted, NextInHolder, NextInInterceptor, Router, NextOutInterceptor, End}

    private class ExecutionContext {

        private final C keyEvaluationContext;
        private NextExecutionState nextExecutionState = NextExecutionState.NotStarted;
        /**
         * This list is eagerly created by evaluating keys of all out interceptors as we do not want to hold the
         * request (for key evaluation) till the outbound interceptor execution starts.
         */
        private List<OutboundInterceptor<O>> applicableOutInterceptors;

        private int currentHolderIndex;
        private int currentInterceptorIndex;

        public ExecutionContext(I request, C keyEvaluationContext) {
            this.keyEvaluationContext = keyEvaluationContext;
            applicableOutInterceptors = new ArrayList<OutboundInterceptor<O>>(); // Execution is not multi-threaded.

            for (InterceptorHolder<I, C, OutboundInterceptor<O>> holder : allOut) {
                switch (keyEvaluationContext.getEvaluationResult(holder.getKey())) { // Result is cached.
                    case Apply:
                        applicableOutInterceptors.addAll(holder.getInterceptors());
                        break;
                    case Skip:
                        break;
                    case NotExecuted:
                        boolean apply = holder.getKey().apply(request, keyEvaluationContext);
                        keyEvaluationContext.updateKeyEvaluationResult(holder.getKey(), apply);
                        if (apply) {
                            applicableOutInterceptors.addAll(holder.getInterceptors());
                        }
                        break;
                }
            }
        }

        public InboundInterceptor<I, O> nextIn(I request) {
            switch (nextExecutionState) {
                case NotStarted:
                    nextExecutionState = NextExecutionState.NextInInterceptor; // Index is 0, so we can skip the NextInHolder state.
                    return nextIn(request);
                case NextInHolder:
                    ++currentHolderIndex;
                    currentInterceptorIndex = 0;
                    nextExecutionState = NextExecutionState.NextInInterceptor;
                    return nextIn(request);
                case NextInInterceptor:
                    if (currentHolderIndex >= allIn.size()) {
                        nextExecutionState = NextExecutionState.Router;
                        return null;
                    } else {
                        InterceptorHolder<I, C, InboundInterceptor<I, O>> holder =
                                allIn.get( currentHolderIndex);
                        switch (keyEvaluationContext.getEvaluationResult(holder.getKey())) { // Result is cached.
                            case Apply:
                                return returnNextInterceptor(request, holder);
                            case Skip:
                                nextExecutionState = NextExecutionState.NextInHolder;
                                return nextIn(request);
                            case NotExecuted:
                                boolean apply = holder.getKey().apply(request, keyEvaluationContext);
                                keyEvaluationContext.updateKeyEvaluationResult(holder.getKey(), apply);
                                return nextIn(request);
                        }
                    }
            }

            return null;
        }

        public OutboundInterceptor<O> nextOut() {
            switch (nextExecutionState) {
                case NextOutInterceptor:
                    if (currentInterceptorIndex >= applicableOutInterceptors.size()) {
                        nextExecutionState = NextExecutionState.End;
                        return null;
                    } else {
                        return applicableOutInterceptors.get(currentInterceptorIndex++);
                    }
            }

            return null;
        }

        private InboundInterceptor<I, O> returnNextInterceptor(I request,
                                                               InterceptorHolder<I, C, InboundInterceptor<I, O>> holder) {
            List<InboundInterceptor<I, O>> interceptors = holder.getInterceptors();
            if (currentInterceptorIndex >= interceptors.size()) {
                nextExecutionState = NextExecutionState.NextInHolder;
                return nextIn(request);
            }
            return interceptors.get(currentInterceptorIndex++);
        }

        public boolean invokeRouter() {
            if (NextExecutionState.Router == nextExecutionState) {
                currentHolderIndex = 0;
                nextExecutionState = NextExecutionState.NextOutInterceptor;
                return true;
            } else {
                return false;
            }
        }
    }

    private class ChainSubscriber extends Subscriber<Void> {

        private final SerialSubscription subscription;
        private final ExecutionContext context;
        private final I request;
        private final O response;
        private final Subscriber<? super Void> child;

        public ChainSubscriber(SerialSubscription subscription, ExecutionContext context, I request, O response,
                               Subscriber<? super Void> child) {
            this.subscription = subscription;
            this.context = context;
            this.request = request;
            this.response = response;
            this.child = child;
        }

        @Override
        public void onCompleted() {
            InboundInterceptor<I, O> nextIn = context.nextIn(request);
            OutboundInterceptor<O> nextOut;
            if (null != nextIn) {
                Observable<Void> interceptorResult = nextIn.in(request, response);
                handleResult(interceptorResult);
            } else if (context.invokeRouter()) {
                handleResult(router.handle(request, response));
            } else if (null != (nextOut = context.nextOut())) {
                handleResult(nextOut.out(response));
            } else {
                child.onCompleted();
            }
        }

        private void handleResult(Observable<Void> aResult) {
            ChainSubscriber nextSubscriber = new ChainSubscriber(subscription, context, request, response,
                                                                 child);
            subscription.set(nextSubscriber);
            aResult.unsafeSubscribe(nextSubscriber);
        }

        @Override
        public void onError(Throwable e) {
            child.onError(e);
        }

        @Override
        public void onNext(Void aVoid) {
            child.onNext(aVoid);
        }
    }
}
TOP

Related Classes of netflix.karyon.transport.interceptor.InterceptorExecutor$ChainSubscriber

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.