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

hudson.ivy.IvyBuildTrigger Maven / Gradle / Ivy

The newest version!
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 hudson.ivy;

import hudson.CopyOnWrite;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractProject;
import hudson.model.Build;
import hudson.model.BuildListener;
import hudson.model.DependecyDeclarer;
import hudson.model.DependencyGraph;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Project;
import hudson.tasks.Publisher;
import hudson.util.FormFieldValidator;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import org.apache.ivy.Ivy;
import org.apache.ivy.Ivy.IvyCallback;
import org.apache.ivy.core.IvyContext;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
import org.apache.ivy.util.Message;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
 * Trigger the build of other project based on the Ivy dependency managment
 * system.
 */
public class IvyBuildTrigger extends Publisher implements DependecyDeclarer {

    private static final Logger LOGGER = Logger.getLogger(IvyBuildTrigger.class.getName());

    private String ivyFile = "ivy.xml";

    private long lastmodified = 0;

    /**
     * Identifies {@link IvyConfiguration} to be used.
     */
    private final String ivyConfName;

    private transient ModuleDescriptor moduleDescriptor;

    /**
     * Set the Message Implementation for ivy to avoid logging to err
     */
    static {
        Message.setDefaultLogger(new IvyMessageImpl());
    }

    /**
     * Contructor
     *
     * @param ivyFile
     *            the ivy.xml file path within the workspace
     * @param ivyConfName
     *            the Ivy configuration name to use
     */
    public IvyBuildTrigger(final String ivyFile, final String ivyConfName) {
        this.ivyFile = ivyFile;
        this.ivyConfName = ivyConfName;
    }

    /**
     *
     * @return the ivy.xml file path within the workspace
     */
    public String getIvyFile() {
        return ivyFile;
    }

    /**
     *
     * @return the Ivy configuration name used
     */
    public String getIvyConfName() {
        return ivyConfName;
    }

    /**
     *
     * @return the {@link IvyConfiguration} from the {@link #ivyConfName}
     */
    public IvyConfiguration getIvyConfiguration() {
        for (IvyConfiguration i : DESCRIPTOR.getConfigurations()) {
            if (ivyConfName != null && i.getName().equals(ivyConfName)) {
                return i;
            }
        }
        return null;
    }

    /**
     *
     * @return the Ivy instance based on the {@link #ivyConfName}
     *
     * @throws ParseException
     * @throws IOException
     */
    public Ivy getIvy() {
        Message.setDefaultLogger(new IvyMessageImpl());
        Ivy ivy = Ivy.newInstance();
        IvyConfiguration ivyConf = getIvyConfiguration();
        if (ivyConf != null) {
            File conf = new File(ivyConf.getIvyConfPath());
            try {
                LOGGER.info("Configure Ivy for Configuration: " + ivyConf.name);
                ivy.configure(conf);
            } catch (ParseException e) {
                LOGGER.log(Level.WARNING, "Parsing error while reading the ivy configuration " + ivyConf.getName()
                        + " at " + ivyConf.getIvyConfPath(), e);
                return null;
            } catch (IOException e) {
                LOGGER.log(Level.WARNING, "I/O error while reading the ivy configuration " + ivyConf.getName() + " at "
                        + ivyConf.getIvyConfPath(), e);
                return null;
            }
        }
        return ivy;
    }

    /**
     * Get the last computed Ivy module descriptior created from the ivy.xml of
     * this trigger
     *
     * @param workspace
     *            the path to the root of the workspace
     * @return the Ivy module descriptior
     * @throws ParseException
     * @throws IOException
     */
    public ModuleDescriptor getModuleDescriptor(FilePath workspace) {
        if (moduleDescriptor == null) {
            recomputeModuleDescriptor(workspace);
        }
        return moduleDescriptor;
    }

    /**
     * Force the creation of the module descriptor from the ivy.xml file
     *
     * @throws ParseException
     * @throws IOException
     */
    public void recomputeModuleDescriptor(FilePath workspace) {
        Ivy ivy = getIvy();
        if (ivy == null) {
            moduleDescriptor = null;
        } else {
            final FilePath ivyF = workspace.child(ivyFile);
            moduleDescriptor = (ModuleDescriptor)ivy.execute(new IvyCallback(){
                @Override
                public Object doInIvyContext(Ivy ivy, IvyContext context) {
                    try {
                        return  ModuleDescriptorParserRegistry.getInstance().parseDescriptor(ivy.getSettings(),
                                ivyF.toURI().toURL(), ivy.getSettings().doValidate());
                    } catch (InterruptedException e) {
                        LOGGER.log(Level.WARNING, "The Ivy module descriptor parsing of " + ivyF + " has been interrupted", e);
                        return null;
                    } catch (MalformedURLException e) {
                        LOGGER.log(Level.WARNING, "The URL is malformed : " + ivyF, e);
                        return null;
                    } catch (ParseException e) {
                        LOGGER.log(Level.WARNING, "Parsing error while reading the ivy file " + ivyF, e);
                        return null;
                    } catch (IOException e) {
                        LOGGER.log(Level.WARNING, "I/O error while reading the ivy file " + ivyF, e);
                        return null;
                    }
                }
            } );

            try {
                lastmodified = ivyF.lastModified();
            } catch (InterruptedException e) {
                LOGGER.log(Level.WARNING, "Could not save the last Modified Date of the ivy file:"+ivyF,e);
            }
            catch (IOException e1) {
                LOGGER.log(Level.WARNING,"Could not save the last Modified Date of the ivy file:"+ivyF,e1);
            }
        }
    }

    /**
     * Container the configuration of Ivy
     */
    public static class IvyConfiguration {

        private String name;

        private String ivyConfPath;

        /**
         * Contructor
         *
         * @param name
         *            the name of the configuration
         * @param ivyConfPath
         *            the full path to the ivy configuration file
         */
        public IvyConfiguration(String name, String ivyConfPath) {
            this.name = name;
            this.ivyConfPath = ivyConfPath;
        }

        /**
         *
         * @return the full path to the ivy configuration file
         */
        public String getIvyConfPath() {
            return ivyConfPath;
        }

        /**
         *
         * @return the name of the configuration
         */
        public String getName() {
            return name;
        }

        /**
         *
         * @return true if the configuration file exists
         */
        public boolean getExists() {
            return new File(ivyConfPath).exists();
        }
    }

    @Override
    public boolean perform(Build build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
        FilePath workspace = build.getProject().getWorkspace();
        FilePath f = workspace.child(ivyFile);
        try {
            if (lastmodified != f.lastModified()) {
                recomputeModuleDescriptor(build.getProject().getWorkspace());
                Hudson.getInstance().rebuildDependencyGraph();
            }
        } catch (IOException e) {
            e.printStackTrace(listener.error("Failed to read the ivy file " + f));
        } catch (InterruptedException e) {
            e.printStackTrace(listener.error("Interuption of the read the ivy file " + f));
        }
        return true;
    }

    public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
        ModuleDescriptor md = getModuleDescriptor(owner.getWorkspace());
        if (md == null) {
            return;
        }

        // Create a map of all known ModuleID'S to their Projects
        // TODO One might introduce some caching for this.
        List projects = Hudson.getInstance().getAllItems(Project.class);
        Map> projectMap = new HashMap>();
        for (Project p : projects) {
            if (p.isDisabled()) {
                continue;
            }
            IvyBuildTrigger t = (IvyBuildTrigger) p.getPublisher(DESCRIPTOR);
            if (t != null) {
                ModuleDescriptor m = t.getModuleDescriptor(p.getWorkspace());
                if (m != null) {
                    ModuleId id = m.getModuleRevisionId().getModuleId();
                    projectMap.put(id, p);
                }
            }
        }

       // Get All Dependencies from ivy.
       // Map them to corresponding  hudson projects

        DependencyDescriptor[] deps = md.getDependencies();
        List  dependencies = new ArrayList();
        for (DependencyDescriptor depDesc : deps) {
            ModuleId id = depDesc.getDependencyId();
            AbstractProject p = projectMap.get(id);
            // Such a project might not exist
            if (p != null) dependencies.add(p);
        }
        graph.addDependency(dependencies, owner);
    }


    public Descriptor getDescriptor() {
        return DESCRIPTOR;
    }

    /**
     * The descriptor of this trigger
     */
    public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

    /**
     * The descriptor implementation of this trigger
     */
    public static final class DescriptorImpl extends Descriptor {

        @CopyOnWrite
        private volatile IvyConfiguration[] configurations = new IvyConfiguration[0];

        DescriptorImpl() {
            super(IvyBuildTrigger.class);
            load();
        }

        /**
         *
         * @return every existing configuration
         */
        public IvyConfiguration[] getConfigurations() {
            return configurations;
        }

        @Override
        public String getDisplayName() {
            return "Trigger the build of other project based on the Ivy management system";
        }

        @Override
        protected void convert(Map oldPropertyBag) {
            if (oldPropertyBag.containsKey("configurations")) {
                configurations = (IvyConfiguration[]) oldPropertyBag.get("configurations");
            }
        }

        @Override
        public boolean configure(StaplerRequest req) {
            boolean r = true;

            int i;
            String[] names = req.getParameterValues("ivy_name");
            String[] paths = req.getParameterValues("ivy_conf_path");

            IvyConfiguration[] confs;

            if (names != null && paths != null) {
                int len = Math.min(names.length, paths.length);
                confs = new IvyConfiguration[len];
                for (i = 0; i < len; i++) {
                    if (Util.nullify(names[i]) == null) {
                        continue;
                    }
                    if (Util.nullify(paths[i]) == null) {
                        continue;
                    }
                    confs[i] = new IvyConfiguration(names[i], paths[i]);
                }
            } else {
                confs = new IvyConfiguration[0];
            }

            this.configurations = confs;

            save();

            return r;
        }

        /**
         * Check that the Ivy configuration file exist
         *
         * @param req
         *            the Stapler request
         * @param rsp
         *            the Stapler response
         * @throws IOException
         * @throws ServletException
         */
        public void doCheckIvyConf(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
            // this can be used to check the existence of a file on the server,
            // so needs to be protected
            new FormFieldValidator(req, rsp, true) {
                @Override
                public void check() throws IOException, ServletException {
                    File f = getFileParameter("value");
                    if (f.getPath().equals("")) {
                        error("The Ivy configuration path is required");
                        return;
                    }
                    if (!f.isFile()) {
                        error(f + " is not a file");
                        return;
                    }

                    // I couldn't come up with a simple logic to test for a
                    // maven installation
                    // there seems to be just too much difference between m1 and
                    // m2.

                    ok();
                }
            }.process();
        }

        /**
         * Check that the ivy.xml file exist
         *
         * @param req
         *            the Stapler request
         * @param rsp
         *            the Stapler response
         * @throws IOException
         * @throws ServletException
         */
        public void doCheckIvyFile(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
            new FormFieldValidator.WorkspaceFilePath(req, rsp, true, true).process();
        }

        @Override
        public Publisher newInstance(StaplerRequest req) {
            return new IvyBuildTrigger(req.getParameter("ivy_file"), req.getParameter("ivy_conf_name"));
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy