/* * Copyright (C) 2016 Red Hat, Inc. and/or its affiliates. * * 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 org.guvnor.ala.pipeline.execution; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; import org.guvnor.ala.pipeline.BiFunctionConfigExecutor; import org.guvnor.ala.pipeline.ConfigExecutor; import org.guvnor.ala.pipeline.ContextAware; import org.guvnor.ala.pipeline.FunctionConfigExecutor; import org.guvnor.ala.pipeline.Input; import org.guvnor.ala.pipeline.Pipeline; import org.guvnor.ala.pipeline.Stage; import org.guvnor.ala.pipeline.events.AfterPipelineExecutionEvent; import org.guvnor.ala.pipeline.events.AfterStageExecutionEvent; import org.guvnor.ala.pipeline.events.BeforePipelineExecutionEvent; import org.guvnor.ala.pipeline.events.BeforeStageExecutionEvent; import org.guvnor.ala.pipeline.events.OnErrorPipelineExecutionEvent; import org.guvnor.ala.pipeline.events.OnErrorStageExecutionEvent; import org.guvnor.ala.pipeline.events.PipelineEventListener; import static org.guvnor.ala.util.VariableInterpolation.*; /* * Represent the Pipeline Executor which will be in charge of executing a pipeline instance * by using the Input data provided. After executing the pipeline a Consumer callback will be executed. */ public class PipelineExecutor { private final Map<Class, ConfigExecutor> configExecutors = new HashMap<>(); public PipelineExecutor() { } public void init( final Collection<ConfigExecutor> configExecutors ) { for ( final ConfigExecutor configExecutor : configExecutors ) { this.configExecutors.put( configExecutor.executeFor(), configExecutor ); } } public PipelineExecutor( final Collection<ConfigExecutor> configExecutors ) { init( configExecutors ); } public <T> void execute( final Input input, final Pipeline pipeline, final Consumer<T> callback, final PipelineEventListener... eventListeners ) { final PipelineContext context = new PipelineContext( pipeline ); context.start( input ); context.pushCallback( callback ); propagateEvent( new BeforePipelineExecutionEvent( pipeline ), eventListeners ); continuePipeline( context, eventListeners ); propagateEvent( new AfterPipelineExecutionEvent( pipeline ), eventListeners ); } private void continuePipeline( final PipelineContext context, final PipelineEventListener... eventListeners ) { while ( !context.isFinished() ) { final Stage<Object, ?> stage = getCurrentStage( context ); final Object newInput = pollOutput( context ); try { propagateEvent( new BeforeStageExecutionEvent( context.getPipeline(), stage ), eventListeners ); stage.execute( newInput, output -> { final ConfigExecutor executor = resolve( output.getClass() ); if ( output instanceof ContextAware ) { ( (ContextAware) output ).setContext( Collections.unmodifiableMap( context.getValues() ) ); } final Object newOutput = interpolate( context.getValues(), output ); if(executor == null){ throw new RuntimeException("Fail to resolve ConfigExecutor for: " + output.getClass()); } context.getValues().put( executor.inputId(), newOutput ); if ( executor instanceof BiFunctionConfigExecutor ) { final Optional result = (Optional) ( (BiFunctionConfigExecutor) executor ).apply( newInput, newOutput ); context.pushOutput( executor.outputId(), result.get() ); } else if ( executor instanceof FunctionConfigExecutor ) { final Optional result = (Optional) ( (FunctionConfigExecutor) executor ).apply( newOutput ); context.pushOutput( executor.outputId(), result.get() ); } propagateEvent( new AfterStageExecutionEvent( context.getPipeline(), stage ), eventListeners ); } ); } catch ( final Throwable t ) { t.printStackTrace(); final RuntimeException exception = new RuntimeException( "An error occurred while executing the " + ( stage == null ? "null" : stage.getName() ) + " stage.", t ); propagateEvent( new OnErrorStageExecutionEvent( context.getPipeline(), stage, exception ), eventListeners ); propagateEvent( new OnErrorPipelineExecutionEvent( context.getPipeline(), stage, exception ), eventListeners ); throw exception; } } final Object output = pollOutput( context ); while ( context.hasCallbacks( ) ) { context.applyCallbackAndPop( output ); } } private ConfigExecutor resolve( final Class<?> clazz ) { final ConfigExecutor result = configExecutors.get( clazz ); if ( result != null ) { return result; } for ( final Map.Entry<Class, ConfigExecutor> entry : configExecutors.entrySet() ) { if ( entry.getKey().isAssignableFrom( clazz ) ) { return entry.getValue(); } } return null; } private static Object pollOutput( final PipelineContext context ) { return context.pollOutput() .orElseThrow( () -> new IllegalStateException( "The " + PipelineContext.class.getSimpleName() + " was polled with no previous output." ) ); } private static Stage<Object, ?> getCurrentStage( final PipelineContext context ) { return context .getCurrentStage() .orElseThrow( () -> new IllegalStateException( "There was not current stage even though the process has not finished." ) ); } private void propagateEvent( final BeforePipelineExecutionEvent beforePipelineExecutionEvent, final PipelineEventListener... eventListeners ) { for ( final PipelineEventListener eventListener : eventListeners ) { eventListener.beforePipelineExecution( beforePipelineExecutionEvent ); } } private void propagateEvent( final BeforeStageExecutionEvent beforeStageExecutionEvent, final PipelineEventListener... eventListeners ) { for ( final PipelineEventListener eventListener : eventListeners ) { eventListener.beforeStageExecution( beforeStageExecutionEvent ); } } private void propagateEvent( final AfterStageExecutionEvent afterStageExecutionEvent, final PipelineEventListener... eventListeners ) { for ( final PipelineEventListener eventListener : eventListeners ) { eventListener.afterStageExecution( afterStageExecutionEvent ); } } private void propagateEvent( final OnErrorStageExecutionEvent onErrorStageExecutionEvent, final PipelineEventListener... eventListeners ) { for ( final PipelineEventListener eventListener : eventListeners ) { eventListener.onStageError( onErrorStageExecutionEvent ); } } private void propagateEvent( final OnErrorPipelineExecutionEvent onErrorPipelineExecutionEvent, final PipelineEventListener... eventListeners ) { for ( final PipelineEventListener eventListener : eventListeners ) { eventListener.onPipelineError( onErrorPipelineExecutionEvent ); } } private void propagateEvent( final AfterPipelineExecutionEvent afterPipelineExecutionEvent, final PipelineEventListener... eventListeners ) { for ( final PipelineEventListener eventListener : eventListeners ) { eventListener.afterPipelineExecution( afterPipelineExecutionEvent ); } } }