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

hudson.maven.agent.PluginManagerInterceptor Maven / Gradle / Ivy

Go to download

Plexus module that intercepts invocations of key Maven components so that Hudson can monitor what's going on in Maven.

There is a newer version: 2.2.2
Show newest version
/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package hudson.maven.agent;

import java.io.IOException;
import java.lang.reflect.Method;

import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.DefaultPluginManager;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginConfigurationException;
import org.apache.maven.plugin.PluginManagerException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
import org.apache.maven.reporting.MavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.classworlds.ClassRealm;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.ComponentConfigurator;
import org.codehaus.plexus.component.configurator.ConfigurationListener;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.configuration.PlexusConfiguration;

/**
 * Description in META-INF/plexus/components.xml makes it possible to use this instead of the default
 * plugin manager.
 *
 * @author Kohsuke Kawaguchi
 */
public class PluginManagerInterceptor extends DefaultPluginManager {
    /**
     * {@link PluginManagerListener} that receives events.
     * There's no way external code can connect to a running instance of
     * {@link PluginManagerInterceptor}, so this cannot be made instance fields. 
     */
    private static PluginManagerListener listener;

    /**
     * {@link ComponentConfigurator} filter to intercept the mojo configuration.
     */
    private ComponentConfiguratorFilter configuratorFilter;

    public static void setListener(PluginManagerListener _listener) {
        listener = _listener;
    }

    @Override
    public void initialize() {
        super.initialize();
        container = new ContainerFilter(container) {
            /**
             * {@link DefaultPluginManager} uses it to load plugins and their configurators.
             *
             * @param name
             *      groupId+':'+artifactId of the plugin.
             */
            public PlexusContainer getChildContainer(String name) {
                PlexusContainer child = super.getChildContainer(name);
                if(child==null) return null;
                return new ContainerFilter(child) {
                    public Object lookup(String componentKey) throws ComponentLookupException {
                        return wrap(super.lookup(componentKey), componentKey);
                    }

                    public Object lookup(String role, String roleHint) throws ComponentLookupException {
                        return wrap(super.lookup(role,roleHint), role);
                    }

                    public void release(Object component) throws ComponentLifecycleException {
                        if(component==configuratorFilter)
                            super.release(configuratorFilter.core);
                        else
                            super.release(component);
                    }

                    private Object wrap(Object c, String componentKey) {
                        if(configuratorFilter==null)
                            return c; // not activated
                        if(c!=null && componentKey.equals(ComponentConfigurator.ROLE)) {
                            if(configuratorFilter.core!=null)
                                throw new IllegalStateException("ComponentConfigurationFilter being reused. " +
                                    "This is a bug in Hudson. Please report that to the development team.");
                            configuratorFilter.core = (ComponentConfigurator)c;
                            c = configuratorFilter;
                        }
                        return c;
                    }
                };
            }
        };
    }

    /**
     * Intercepts the {@link Mojo} configuration and grabs some key Maven objects that are used for configuration,
     * then call {@link #pre(Object, PlexusConfiguration, ExpressionEvaluator)} to provide an opportunity
     * to alter the configuration.
     */
    private abstract class MojoIntercepter extends ComponentConfiguratorFilter {
        // these are the key objects involved in configuring a mojo
        PlexusConfiguration config;
        ExpressionEvaluator eval;
        Mojo mojo;

        MojoIntercepter() {
            super(null);
            // it is the caller's responsibility to set 'configuratorFilter' to null when the interception is over.
            configuratorFilter = this;
        }

        @Override
        public void configureComponent(Object component, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator, ClassRealm containerRealm, ConfigurationListener configListener) throws ComponentConfigurationException {
            try {
                this.config = configuration;
                this.eval = expressionEvaluator;
                this.mojo = (Mojo)component;
                pre(component, configuration, expressionEvaluator);
                super.configureComponent(component, configuration, expressionEvaluator, containerRealm, configListener);
            } catch (IOException e) {
                throw new ComponentConfigurationException(e);
            } catch (InterruptedException e) {
                // orderly abort
                throw new AbortException("Execution aborted",e);
            }
        }

        protected abstract void pre(Object component, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator) throws IOException, InterruptedException;
    }

    @Override
    public void executeMojo(final MavenProject project, final MojoExecution mojoExecution, MavenSession session) throws ArtifactResolutionException, MojoExecutionException, MojoFailureException, ArtifactNotFoundException, InvalidDependencyVersionException, PluginManagerException, PluginConfigurationException {
        class MojoIntercepterImpl extends MojoIntercepter {
            @Override
            protected void pre(Object component, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator) throws IOException, InterruptedException {
                if(listener!=null)
                    // this lets preExecute a chance to modify the mojo configuration
                    listener.preExecute(project,mojoExecution, (Mojo)component, configuration,expressionEvaluator);
            }

            void callPost(Exception exception) throws IOException, InterruptedException {
                if(listener!=null)
                    listener.postExecute(project,mojoExecution,mojo,config,eval,exception);
            }
        }

        // prepare interception of ComponentConfigurator, so that we can get the final PlexusConfiguration object
        // representing the configuration before Mojo object is filled with that.
        MojoIntercepterImpl interceptor = new MojoIntercepterImpl();

        try {
            try {
                // inside the executeMojo but before the mojo actually gets executed,
                // we should be able to trap the mojo configuration.
                super.executeMojo(project, mojoExecution, session);
                interceptor.callPost(null);
            } catch (MojoExecutionException e) {
                interceptor.callPost(e);
                throw e;
            } catch (MojoFailureException e) {
                interceptor.callPost(e);
                throw e;
            }
        } catch (InterruptedException e) {
            // orderly abort
            throw new AbortException("Execution aborted",e);
        } catch (IOException e) {
            // we can't use two-args constructor. see the comment from steffeng on Mon Sep 14 20:22:32 HUDSON-2373.
            throw (PluginManagerException)new PluginManagerException(e.getMessage()).initCause(e);
        } finally {
            configuratorFilter = null;
        }
    }

    /**
     * Intercepts the creation of {@link MavenReport}, to intercept
     * the execution of it. This is used to discover the execution
     * of certain reporting.
     */
    @Override
    public MavenReport getReport(MavenProject project, final MojoExecution mojoExecution, MavenSession session) throws ArtifactNotFoundException, PluginConfigurationException, PluginManagerException, ArtifactResolutionException {
        // intercept the MavenReport object creation. 
        final MojoIntercepter interceptor = new MojoIntercepter() {
            protected void pre(Object component, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator) throws IOException, InterruptedException {
            }
        };

        MavenReport r;
        try {
            r = super.getReport(project, mojoExecution, session);
        } finally {
            configuratorFilter = null;
        }
        if(r==null)     return null;
        
        r = new ComponentInterceptor() {
            /**
             * Intercepts the execution of methods on {@link MavenReport}.
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(method.getName().equals("generate")) {
                    Object r = super.invoke(proxy, method, args);
                    // on successul execution of the generate method, raise an event
                    try {
                        listener.onReportGenerated(delegate,mojoExecution,interceptor.config,interceptor.eval);
                    } catch (InterruptedException e) {
                        // orderly abort
                        throw new AbortException("Execution aborted",e);
                    } catch (IOException e) {
                        throw new MavenReportException(e.getMessage(),e);
                    }
                    return r;
                }

                // delegate by default
                return super.invoke(proxy, method, args);
            }
        }.wrap(r);
        return r;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy