All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wisdom.maven.mojos.RunMojo Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/*
 * #%L
 * Wisdom-Framework
 * %%
 * Copyright (C) 2013 - 2014 Wisdom Framework
 * %%
 * 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.
 * #L%
 */
package org.wisdom.maven.mojos;

import com.google.common.collect.ImmutableList;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.project.*;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;

import java.io.File;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Mojo running a 'watched' instance of Wisdom. It deploys the applications and monitor for changes. On each change,
 * the Maven 'watch' pipeline is triggered to re-deploy the bundle, or update configurations and files.
 */
@Mojo(name = "run", threadSafe = false,
        // We need to use the TEST scope to let Surefire access its dependencies.
        requiresDependencyResolution = ResolutionScope.TEST,
        requiresProject = true
)
@Execute(phase = LifecyclePhase.PACKAGE)
public class RunMojo extends AbstractWisdomMojo implements Contextualizable {

    /**
     * Enables / disables the pom file monitoring.
     */
    @Parameter(defaultValue = "${pom.watching}")
    protected boolean pomFileMonitoring = true;

    /**
     * The last modification date of the project's pom.xml file. This field is required on Windows as the exit code
     * and not propagated correctly.
     */
    private long lastPomFileModification;

    /**
     * Component used to build the Maven project from the pom file.
     */
    @Component
    private ProjectBuilder projectBuilder;

    /**
     * The component used to execute the second Maven execution.
     */
    @Component
    private LifecycleExecutor lifecycleExecutor;

    /**
     * The plexus container.
     */
    private PlexusContainer container;

    /**
     * Sets to false to indicate that we are executing a nested 'build'.
     */
    private boolean initialBuild = true;

    @Override
    public void execute() throws MojoExecutionException {
        File pom = project.getFile();
        lastPomFileModification = pom.lastModified();

        MavenProject newProject = null;
        try {
            final ProjectBuildingResult result = loadMavenProject();
            newProject = result.getProject();
        } catch (ProjectBuildingException exception) {
            getLog().error("Error(s) detected in the pom file: " + exception.getMessage());
            if (initialBuild) {
                throw new MojoExecutionException("Invalid pom file, check log", exception);
            }
            if (pomFileMonitoring) {
                waitForModification();
                execute();
            }
        }

        // newProject is necessarily set here and valid.

        MavenExecutionRequest execRequest = getMavenExecutionRequest();
        MavenSession newSession = getMavenSession(newProject, execRequest); //NOSONAR
        // The session is going to be cleared, write the watcher list in the container.
//        container.getContext().put(Watchers.WATCHERS_KEY, Watchers.all(session));
        initialBuild = false;

        try {
            lifecycleExecutor.execute(newSession);
        } catch (RuntimeException e) {
            // Maven may throw internal error, they are fatal, stop everything.
            getLog().info("Maven has thrown a fatal error, stopping the watch mode: " + e.getMessage());
            return;
        }

        if (newSession.getResult().hasExceptions()) {
            getLog().error("Exception(s) detected while launching Maven : " + newSession.getResult().getExceptions());
            if (initialBuild) {
                throw new MojoExecutionException("Exception(s) detected while launching Maven, check log",
                        newSession.getResult().getExceptions().get(0));
            }
            if (pomFileMonitoring) {
                waitForModification();
                execute();
            }
            return;
        }

        if (pomFileMonitoring && pomFileModified()) {
            execute();
        }
    }

    /**
     * Waits for pom file modification. This method is used when a pom file has problems,
     * to reload the project when they are fixed.
     */
    private void waitForModification() {
        while( ! pomFileModified()) {
            try {
                Thread.sleep(Integer.getInteger("watch.period", 2) * 1000);
            } catch (InterruptedException e) {
                // Ignore it.
            }
        }
    }

    private MavenSession getMavenSession(final MavenProject project, MavenExecutionRequest request) {
        MavenSession newSession = new MavenSession(container,
                session.getRepositorySession(),
                request,
                session.getResult());
        newSession.setAllProjects(session.getAllProjects());
        newSession.setCurrentProject(project);
        newSession.setParallel(session.isParallel());
        // Update project map to update the current project
        Map projectMaps = new LinkedHashMap<>(session.getProjectMap());
        projectMaps.put(ArtifactUtils.key(project.getGroupId(), project.getArtifactId(),
                project.getVersion()), project);
        newSession.setProjectMap(projectMaps);

        /**
         * Fake implementation of the project dependency graph, as we don't support reactor.
         */
        ProjectDependencyGraph graph = new ProjectDependencyGraph() {

            @Override
            public List getSortedProjects() {
                return ImmutableList.of(project);
            }

            @Override
            public List getDownstreamProjects(MavenProject project, boolean transitive) {
                return Collections.emptyList();
            }

            @Override
            public List getUpstreamProjects(MavenProject project, boolean transitive) {
                return Collections.emptyList();
            }
        };
        newSession.setProjectDependencyGraph(graph);
        newSession.setProjects(ImmutableList.of(project));
        return newSession;
    }


    private MavenExecutionRequest getMavenExecutionRequest() {
        MavenExecutionRequest request = DefaultMavenExecutionRequest.copy(session.getRequest());
        request.setStartTime(session.getStartTime());
        request.setExecutionListener(null);
        if (! initialBuild  && session.getGoals().contains("clean")) {
            // Here the package phase is required to restore the runtime environment
            request.setGoals(ImmutableList.of("clean", "package", "wisdom:internal-run"));
        } else {
            // It is safer to re-execute the package phase to have the new classes...
            request.setGoals(ImmutableList.of("package", "wisdom:internal-run"));
        }
        return request;
    }

    private ProjectBuildingResult loadMavenProject() throws ProjectBuildingException {
        DefaultProjectBuildingRequest request = new DefaultProjectBuildingRequest();
        request.setRepositorySession(repoSession);
        request.setUserProperties(session.getUserProperties());
        request.setSystemProperties(session.getSystemProperties());
        request.setProfiles(session.getRequest().getProfiles());
        request.setActiveProfileIds(session.getRequest().getActiveProfiles());
        request.setRemoteRepositories(session.getRequest().getRemoteRepositories());
        request.setBuildStartTime(session.getRequest().getStartTime());
        request.setInactiveProfileIds(session.getRequest().getInactiveProfiles());
        request.setPluginArtifactRepositories(session.getRequest().getPluginArtifactRepositories());
        request.setLocalRepository(session.getRequest().getLocalRepository());

        return projectBuilder.build(project.getFile(), request);

    }

    /**
     * On windows we can't play with the exit code, so check for the pom file modification dates.
     *
     * @return {@code true} if the pom file was modified. {@code false} otherwise.
     */
    private boolean pomFileModified() {
        File pom = project.getFile();
        return pom.lastModified() > lastPomFileModification;
    }

    /**
     * Retrieves the Plexus container.
     * @param context the context
     * @throws ContextException if the container cannot be retrieved.
     */
    @Override
    public void contextualize(Context context) throws ContextException {
        container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy