io.cloudslang.lang.compiler.scorecompiler.ScoreCompilerImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.lang.compiler.scorecompiler.ScoreCompilerImpl.java

Source

/*******************************************************************************
 * (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0 which accompany this distribution.
 *
 * The Apache License is available at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *******************************************************************************/
package io.cloudslang.lang.compiler.scorecompiler;

import ch.lambdaj.function.convert.Converter;
import io.cloudslang.lang.compiler.SlangTextualKeys;
import io.cloudslang.lang.compiler.modeller.DependenciesHelper;
import io.cloudslang.lang.compiler.modeller.model.Decision;
import io.cloudslang.lang.compiler.modeller.model.Executable;
import io.cloudslang.lang.compiler.modeller.model.Flow;
import io.cloudslang.lang.compiler.modeller.model.Operation;
import io.cloudslang.lang.compiler.modeller.model.Step;
import io.cloudslang.lang.compiler.modeller.result.CompilationModellingResult;
import io.cloudslang.lang.compiler.validator.CompileValidator;
import io.cloudslang.lang.entities.CompilationArtifact;
import io.cloudslang.lang.entities.ScoreLangConstants;
import io.cloudslang.lang.entities.bindings.Result;
import io.cloudslang.score.api.ExecutionPlan;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.Validate;

import static ch.lambdaj.Lambda.convertMap;

public class ScoreCompilerImpl implements ScoreCompiler {

    private ExecutionPlanBuilder executionPlanBuilder;

    private DependenciesHelper dependenciesHelper;

    private CompileValidator compileValidator;

    @Override
    public CompilationArtifact compile(Executable source, Set<Executable> path) {
        CompilationModellingResult compilationModellingResult = compileSource(source, path);
        List<RuntimeException> errors = compilationModellingResult.getErrors();
        if (CollectionUtils.isNotEmpty(errors)) {
            throw errors.get(0);
        }
        return compilationModellingResult.getCompilationArtifact();
    }

    @Override
    public CompilationModellingResult compileSource(Executable executable, Set<Executable> path) {
        List<RuntimeException> exceptions = new ArrayList<>();
        Map<String, Executable> filteredDependencies = new HashMap<>();
        //we handle dependencies only if the file has imports
        boolean hasDependencies = CollectionUtils.isNotEmpty(executable.getExecutableDependencies())
                && executable.getType().equals(SlangTextualKeys.FLOW_TYPE);
        if (hasDependencies) {
            try {
                Validate.notEmpty(path, "Source " + executable.getName()
                        + " has dependencies but no path was given to the compiler");
                Validate.noNullElements(path, "Source " + executable.getName() + " has empty dependencies");
            } catch (RuntimeException ex) {
                exceptions.add(ex);
            }

            //we add the current executable since a dependency can require it
            List<Executable> availableExecutables = new ArrayList<>(path);
            availableExecutables.add(executable);

            try {
                //than we match the references to the actual dependencies
                filteredDependencies = dependenciesHelper.matchReferences(executable, availableExecutables);

                handleOnFailureCustomResults(executable, filteredDependencies);

                List<RuntimeException> errors = compileValidator.validateModelWithDependencies(executable,
                        filteredDependencies);
                exceptions.addAll(errors);
            } catch (RuntimeException ex) {
                exceptions.add(ex);
            }

        }

        try {
            //next we create an execution plan for the required executable
            ExecutionPlan executionPlan = compileToExecutionPlan(executable);

            //and also create execution plans for all other dependencies
            Converter<Executable, ExecutionPlan> converter = new Converter<Executable, ExecutionPlan>() {
                @Override
                public ExecutionPlan convert(Executable compiledExecutable) {
                    return compileToExecutionPlan(compiledExecutable);
                }
            };
            Map<String, ExecutionPlan> dependencies = convertMap(filteredDependencies, converter);
            Collection<Executable> executables = new ArrayList<>(filteredDependencies.values());
            executables.add(executable);

            HashSet<String> subflowsUuids = new HashSet<>(dependencies.keySet());
            subflowsUuids.addAll(executable.getExternalExecutableDependencies());
            executionPlan.setSubflowsUUIDs(subflowsUuids);
            CompilationArtifact compilationArtifact = new CompilationArtifact(executionPlan, dependencies,
                    executable.getInputs(), getSystemPropertiesFromExecutables(executables));
            return new CompilationModellingResult(compilationArtifact, exceptions);
        } catch (RuntimeException ex) {
            exceptions.add(ex);
        }
        return new CompilationModellingResult(null, exceptions);
    }

    private void handleOnFailureCustomResults(Executable executable, Map<String, Executable> filteredDependencies) {
        handleOnFailureStepCustomResults((Flow) executable, filteredDependencies);
        for (Executable dependency : filteredDependencies.values()) {
            if (dependency.getType().equals(SlangTextualKeys.FLOW_TYPE)) {
                handleOnFailureStepCustomResults((Flow) dependency, filteredDependencies);
            }
        }
    }

    private void handleOnFailureStepCustomResults(Flow executable, Map<String, Executable> filteredDependencies) {
        Step onFailureStep = getOnFailureStep(executable);
        if (onFailureStep != null) {
            Executable onFailureDependency = filteredDependencies.get(onFailureStep.getRefId());
            for (Result result : onFailureDependency.getResults()) {
                Map<String, String> navigationString = new HashMap<>();
                navigationString.put(result.getName(), ScoreLangConstants.FAILURE_RESULT);
                onFailureStep.getNavigationStrings().add(navigationString);
            }
        }
    }

    private Step getOnFailureStep(Flow flow) {
        Deque<Step> stepDeque = flow.getWorkflow().getSteps();
        for (Step step : stepDeque) {
            if (step.isOnFailureStep()) {
                return step;
            }
        }
        return null;
    }

    @Override
    public List<RuntimeException> validateSlangModelWithDirectDependencies(Executable slangModel,
            Set<Executable> directDependenciesModels) {
        Map<String, Executable> dependenciesMap = new HashMap<>();
        for (Executable dependency : directDependenciesModels) {
            dependenciesMap.put(dependency.getId(), dependency);
        }
        return compileValidator.validateModelWithDirectDependencies(slangModel, dependenciesMap);
    }

    /**
     * Utility method that cast a {@link io.cloudslang.lang.compiler.modeller.model.Executable} to its subtype
     * and create an {@link io.cloudslang.score.api.ExecutionPlan} for it
     *
     * @param executable the executable to create an {@link io.cloudslang.score.api.ExecutionPlan} for
     * @return {@link io.cloudslang.score.api.ExecutionPlan} of the given
     * {@link io.cloudslang.lang.compiler.modeller.model.Executable}
     */
    private ExecutionPlan compileToExecutionPlan(Executable executable) {

        switch (executable.getType()) {
        case SlangTextualKeys.OPERATION_TYPE:
            return executionPlanBuilder.createOperationExecutionPlan((Operation) executable);
        case SlangTextualKeys.FLOW_TYPE:
            return executionPlanBuilder.createFlowExecutionPlan((Flow) executable);
        case SlangTextualKeys.DECISION_TYPE:
            return executionPlanBuilder.createDecisionExecutionPlan((Decision) executable);
        default:
            throw new RuntimeException("Executable: " + executable.getName()
                    + " cannot be compiled to an ExecutionPlan since it is not of type flow, operation or decision");
        }
    }

    private Set<String> getSystemPropertiesFromExecutables(Collection<Executable> executables) {
        Set<String> result = new HashSet<>();
        for (Executable executable : executables) {
            result.addAll(executable.getSystemPropertyDependencies());
        }
        return result;
    }

    public void setExecutionPlanBuilder(ExecutionPlanBuilder executionPlanBuilder) {
        this.executionPlanBuilder = executionPlanBuilder;
    }

    public void setDependenciesHelper(DependenciesHelper dependenciesHelper) {
        this.dependenciesHelper = dependenciesHelper;
    }

    public void setCompileValidator(CompileValidator compileValidator) {
        this.compileValidator = compileValidator;
    }
}