org.springframework.cloud.stream.app.plugin.SpringCloudStreamAppMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.stream.app.plugin.SpringCloudStreamAppMojo.java

Source

/*
 * Copyright 2015-2016 the original author or authors.
 *
 * 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.springframework.cloud.stream.app.plugin;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.spring.initializr.generator.ProjectRequest;
import io.spring.initializr.metadata.Dependency;
import io.spring.initializr.metadata.InitializrMetadata;
import org.apache.commons.io.FileUtils;
import org.apache.maven.model.Model;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import org.springframework.cloud.stream.app.plugin.utils.MavenModelUtils;
import org.springframework.cloud.stream.app.plugin.utils.SpringCloudStreamPluginUtils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.CollectionUtils;

import static java.util.stream.Collectors.toList;

/**
 * @author Soby Chacko
 */
@Mojo(name = "generate-app")
public class SpringCloudStreamAppMojo extends AbstractMojo {

    private static final String SPRING_CLOUD_STREAM_BINDER_GROUP_ID = "org.springframework.cloud";

    @Parameter
    private Map<String, GeneratableApp> generatedApps;

    @Parameter
    private Bom bom;

    @Parameter
    private String generatedProjectHome;

    @Parameter
    private String javaVersion;

    @Parameter
    private String bootVersion;

    @Parameter
    private String generatedProjectVersion;

    @Parameter
    private String applicationType;

    @Parameter
    private List<Repository> extraRepositories;

    @Parameter
    List<Dependency> additionalGlobalDependencies = new ArrayList<>();

    @Parameter
    private Map<String, String> binders = new HashMap<>();

    @Parameter
    private String bomsWithHigherPrecedence;

    private ScsProjectGenerator projectGenerator = new ScsProjectGenerator();

    public void execute() throws MojoExecutionException, MojoFailureException {

        projectGenerator.setDockerHubOrg("springcloud" + applicationType);
        projectGenerator.setBomsWithHigherPrecedence(bomsWithHigherPrecedence);

        final InitializrDelegate initializrDelegate = new InitializrDelegate();

        initializrDelegate.prepareProjectGenerator();

        List<String> appTypes = getAppTypes();
        try {
            for (Map.Entry<String, GeneratableApp> entry : generatedApps.entrySet()) {
                for (String appType : appTypes) {
                    generateApp(initializrDelegate, entry, appType);
                }
            }
        } catch (IOException | XmlPullParserException e) {
            throw new IllegalStateException(e);
        }
    }

    private void generateApp(InitializrDelegate initializrDelegate, Map.Entry<String, GeneratableApp> entry,
            String appType) throws IOException, XmlPullParserException {

        GeneratableApp value = entry.getValue();
        final String generatedAppGroupId = getApplicationGroupId(applicationType);

        List<Dependency> deps = new ArrayList<>();
        List<String> artifactIds = new ArrayList<>();

        String appArtifactId = !appType.equals("default") ? entry.getKey() + "-" + appType : entry.getKey();
        String starterArtifactId = getStarterArtifactId(entry.getKey());
        Dependency starterDep = getDependency(starterArtifactId, generatedAppGroupId);
        deps.add(starterDep);
        artifactIds.add(starterArtifactId);
        if (!appType.equals("default")) {
            String binderArtifactId = constructBinderArtifact(appType);
            Dependency binderDep = getDependency(binderArtifactId, SPRING_CLOUD_STREAM_BINDER_GROUP_ID);
            deps.add(binderDep);
            artifactIds.add(binderArtifactId);
        }
        if (StringUtils.isNotEmpty(value.getExtraTestConfigClass())) {
            deps.add(getDependency("app-starters-test-support", generatedAppGroupId));
            artifactIds.add("app-starters-test-support");
        }

        for (Dependency globalDep : additionalGlobalDependencies) {
            Dependency dep = getDependency(globalDep.getArtifactId(), globalDep.getGroupId());
            deps.add(dep);
            artifactIds.add(dep.getArtifactId());
        }

        //Force a dependency independent of BOM
        for (Dependency forceDep : value.getForceDependencies()) {
            Dependency dependency = new Dependency();
            dependency.setId(forceDep.getArtifactId());
            dependency.setGroupId(forceDep.getGroupId());
            dependency.setArtifactId(forceDep.getArtifactId());
            if (StringUtils.isNotEmpty(forceDep.getVersion())) {
                dependency.setVersion(forceDep.getVersion());
            } else {
                dependency.setBom(bom.getName());
            }
            deps.add(dependency);

            artifactIds.add(dependency.getArtifactId());
        }

        Dependency[] depArray = deps.toArray(new Dependency[deps.size()]);
        String[] artifactNames = artifactIds.toArray(new String[artifactIds.size()]);
        List<Repository> extraReposToAdd = new ArrayList<>();
        Set<String> extraRepoIds = value.getExtraRepositories().keySet();
        if (!CollectionUtils.isEmpty(extraRepositories) && !CollectionUtils.isEmpty(extraRepoIds)) {
            extraReposToAdd = extraRepositories.stream().filter(e -> extraRepoIds.contains(e.getId()))
                    .collect(Collectors.toList());
        }
        String[] repoIds = new String[] {};
        if (!CollectionUtils.isEmpty(extraRepoIds)) {
            repoIds = new String[extraRepoIds.size()];
            repoIds = extraRepoIds.toArray(repoIds);
        }
        InitializrMetadata metadata = SpringCloudStreamAppMetadataBuilder.withDefaults()
                .addRepositories(extraReposToAdd)
                .addBom(bom.getName(), bom.getGroupId(), bom.getArtifactId(), bom.getVersion(), repoIds)
                .addJavaVersion(javaVersion).addBootVersion(bootVersion).addDependencyGroup(appArtifactId, depArray)
                .build();
        initializrDelegate.applyMetadata(metadata);
        ProjectRequest projectRequest = initializrDelegate.getProjectRequest(appArtifactId,
                getApplicationGroupId(applicationType), getDescription(appArtifactId),
                getPackageName(appArtifactId), generatedProjectVersion, artifactNames);
        File project = projectGenerator.doGenerateProjectStructure(projectRequest);

        File generatedProjectHome = StringUtils.isNotEmpty(this.generatedProjectHome)
                ? new File(this.generatedProjectHome)
                : StringUtils.isNotEmpty(value.getGeneratedProjectHome()) ? new File(value.generatedProjectHome)
                        : null;

        postProcessGeneratedProject(value, appArtifactId, project, generatedProjectHome, entry.getKey(),
                generatedProjectVersion);
    }

    private List<String> getAppTypes() {
        List<String> appTypes = new ArrayList<>();

        if (!CollectionUtils.isEmpty(binders)) {
            appTypes.addAll(binders.keySet());
        } else {
            appTypes.add("default");
        }
        return appTypes;
    }

    private void postProcessGeneratedProject(GeneratableApp value, String appArtifactId, File project,
            File generatedProjectHome, String origKey, String generatedProjectVersion)
            throws IOException, XmlPullParserException {
        if (generatedProjectHome != null && project != null) {
            String generatedAppHome = moveProjectWithMavenModelsUpdated(appArtifactId, project,
                    generatedProjectHome, value.isTestsIgnored(), generatedProjectVersion);

            if (StringUtils.isNotEmpty(value.getExtraTestConfigClass())) {
                String s = StringUtils.removeAndHump(appArtifactId, "-");
                String s1 = StringUtils.capitalizeFirstLetter(s);
                String clazzInfo = "classes = {\n" + "\t\t" + value.getExtraTestConfigClass() + ",\n" + "\t\t" + s1
                        + "Application.class" + " }";
                SpringCloudStreamPluginUtils.addExtraTestConfig(generatedAppHome, clazzInfo);
            }
            String s = StringUtils.removeAndHump(origKey, "-");
            String s1 = StringUtils.capitalizeFirstLetter(s);

            String[] tokens = origKey.split("-");
            List<String> orderedStarterArtifactTokens = new LinkedList<>();
            int toLimit = applicationType.equals("task") ? tokens.length - 1 : tokens.length;
            orderedStarterArtifactTokens.addAll(Stream.of(tokens).limit(toLimit).collect(toList()));
            String subPackage = orderedStarterArtifactTokens.stream().collect(Collectors.joining("."));

            String toBeImported = StringUtils.isEmpty(value.getAutoConfigClass())
                    ? String.format("%s.%s.%s.%s.%sConfiguration.class", "org.springframework.cloud",
                            applicationType, "app", subPackage, s1)
                    : value.getAutoConfigClass();
            SpringCloudStreamPluginUtils.addAutoConfigImport(generatedAppHome, toBeImported);
            addCopyrightToJavaFiles(generatedAppHome);
        } else if (project != null) {
            //no user provided generated project home, fall back to the default used by the Initializr
            getLog().info("Project is generated at " + project.toString());
        }
    }

    private void addCopyrightToJavaFiles(String generatedAppHome) throws IOException {
        Stream<Path> javaStream = Files.find(Paths.get(generatedAppHome), 20,
                (path, attr) -> String.valueOf(path).endsWith(".java"));

        javaStream.forEach(p -> {
            try {
                SpringCloudStreamPluginUtils.addCopyRight(p);
            } catch (IOException e) {
                getLog().warn("Issues adding copyright", e);
            }
        });
    }

    private String getStarterArtifactId(String key) {
        //time-source -> source-time
        //groovy-filter-processor -> processor-groovy-filter
        //scriptable-transform-processor -> processor-scriptable-transform
        //timestamp-task -> task-timestamp

        String[] tokens = key.split("-");
        List<String> orderedStarterArtifactTokens = new LinkedList<>();
        if (!applicationType.equals("task")) {
            orderedStarterArtifactTokens.add(tokens[tokens.length - 1]);
        }
        orderedStarterArtifactTokens.addAll(Stream.of(tokens).limit(tokens.length - 1).collect(toList()));
        String collect = orderedStarterArtifactTokens.stream().collect(Collectors.joining("-"));
        return String.format("%s-%s-%s", "spring-cloud-starter", applicationType, collect);
    }

    private static String getApplicationGroupId(String applicationType) {
        return String.format("%s.%s.%s", "org.springframework.cloud", applicationType, "app");
    }

    private String getPackageName(String artifactId) {
        String[] strings = applicationType.equals("stream")
                ? Stream.of(artifactId.split("-")).toArray(String[]::new)
                : Stream.of(artifactId.split("-")).limit(StringUtils.countMatches(artifactId, "-"))
                        .toArray(String[]::new);

        String join = StringUtils.join(strings, ".");
        return String.format("%s.%s", getApplicationGroupId(applicationType), join);
    }

    private String getDescription(String artifactId) {
        String[] strings = Stream.of(artifactId.split("-")).map(StringUtils::capitalizeFirstLetter)
                .toArray(String[]::new);
        String join = StringUtils.join(strings, " ");
        String appSuffix = applicationType.equals("stream") ? "Binder Application" : "Application";
        return String.format("%s %s %s %s", "Spring Cloud", StringUtils.capitalizeFirstLetter(applicationType),
                join, appSuffix);
    }

    private Dependency getDependency(String s, String groupId) {
        Dependency dependency = new Dependency();
        dependency.setId(s);
        dependency.setGroupId(groupId);
        dependency.setArtifactId(s);
        dependency.setBom(bom.getName());
        return dependency;
    }

    private String constructBinderArtifact(String binder) {
        return String.format("%s-%s", "spring-cloud-starter-stream", binder);
    }

    private String moveProjectWithMavenModelsUpdated(String key, File project, File generatedProjectHome,
            boolean testIgnored, String generatedProjectVersion) throws IOException, XmlPullParserException {

        Model model = isNewDir(generatedProjectHome)
                ? MavenModelUtils.populateModel(generatedProjectHome.getName(),
                        getApplicationGroupId(applicationType), generatedProjectVersion)
                : MavenModelUtils.getModelFromContainerPom(generatedProjectHome,
                        getApplicationGroupId(applicationType), generatedProjectVersion);

        if (model != null && MavenModelUtils.addModuleIntoModel(model, key)) {
            MavenModelUtils.writeModelToFile(model,
                    new FileOutputStream(new File(generatedProjectHome, "pom.xml")));
        }

        try {
            File generatedAppHome = new File(generatedProjectHome, key);
            removeExistingContent(generatedAppHome.toPath());

            FileUtils.copyDirectory(new File(project, key), generatedAppHome);

            File mvnw = new File(generatedAppHome, "mvnw");
            if (mvnw.exists()) {
                mvnw.setExecutable(true);
            }

            if (testIgnored) {
                SpringCloudStreamPluginUtils
                        .ignoreUnitTestGeneratedByInitializer(generatedAppHome.getAbsolutePath());
            }
            //MavenModelUtils.addModuleInfoToContainerPom(generatedProjectHome);
            return generatedAppHome.getAbsolutePath();
        } catch (IOException e) {
            getLog().error("Error during plugin execution", e);
            throw new IllegalStateException(e);
        }
    }

    private boolean isNewDir(File genProjecthome) {
        return (!genProjecthome.exists() && genProjecthome.mkdir());
    }

    private void removeExistingContent(Path path) {
        if (path.toFile().exists()) {
            try {
                SpringCloudStreamPluginUtils.cleanupGenProjHome(path.toFile());
            } catch (IOException e) {
                getLog().error("Error", e);
                throw new IllegalStateException(e);
            }
        }
    }

    private class InitializrDelegate {

        private void applyMetadata(final InitializrMetadata metadata) {
            projectGenerator.setMetadataProvider(() -> metadata);
        }

        private ProjectRequest getProjectRequest(String generatedArtifactId, String generatedAppGroupId,
                String description, String packageName, String version, String... artifactNames) {
            ProjectRequest projectRequest = createProjectRequest(artifactNames);
            projectRequest.setBaseDir(generatedArtifactId);

            projectRequest.setGroupId(generatedAppGroupId);
            projectRequest.setArtifactId(generatedArtifactId);
            projectRequest.setName(generatedArtifactId);
            projectRequest.setDescription(description);
            projectRequest.setPackageName(packageName);
            projectRequest.setVersion(version);
            return projectRequest;
        }

        private ProjectRequest createProjectRequest(String... styles) {
            ProjectRequest request = new ProjectRequest();
            request.initialize(projectGenerator.getMetadataProvider().get());
            request.getStyle().addAll(Arrays.asList(styles));
            return request;
        }

        private void prepareProjectGenerator() {
            String tmpdir = System.getProperty("java.io.tmpdir");
            projectGenerator.setTmpdir(tmpdir);
            projectGenerator.setEventPublisher(new ApplicationEventPublisher() {
                public void publishEvent(ApplicationEvent applicationEvent) {
                    getLog().debug("Generated project : " + applicationEvent.toString());
                }

                public void publishEvent(Object o) {
                    getLog().debug("Generated project : " + o.toString());
                }
            });
        }
    }
}