package hudson.init; import org.kohsuke.MetaInfServices; import org.jvnet.hudson.reactor.Task; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; import hudson.PluginManager; import jenkins.util.SystemProperties; import hudson.util.DirScanner; import hudson.util.FileVisitor; import hudson.util.Service; /** * Strategy pattern of the various key decision making during the Jenkins initialization. * * <p> * Because the act of initializing plugins is a part of the Jenkins initialization, * this extension point cannot be implemented in a plugin. You need to place your jar * inside {@code WEB-INF/lib} instead. * * <p> * To register, put {@link MetaInfServices} on your implementation. * * @author Kohsuke Kawaguchi */ public class InitStrategy { /** * Returns the list of *.jpi, *.hpi and *.hpl to expand and load. * * <p> * Normally we look at {@code $JENKINS_HOME/plugins/*.jpi} and *.hpi and *.hpl. * * @return * never null but can be empty. The list can contain different versions of the same plugin, * and when that happens, Jenkins will ignore all but the first one in the list. */ public List<File> listPluginArchives(PluginManager pm) throws IOException { List<File> r = new ArrayList<File>(); // the ordering makes sure that during the debugging we get proper precedence among duplicates. // for example, while doing "mvn jpi:run" or "mvn hpi:run" on a plugin that's bundled with Jenkins, we want to the // *.jpl file to override the bundled jpi/hpi file. getBundledPluginsFromProperty(r); // similarly, we prefer *.jpi over *.hpi listPluginFiles(pm, ".jpl", r); // linked plugin. for debugging. listPluginFiles(pm, ".hpl", r); // linked plugin. for debugging. (for backward compatibility) listPluginFiles(pm, ".jpi", r); // plugin jar file listPluginFiles(pm, ".hpi", r); // plugin jar file (for backward compatibility) return r; } private void listPluginFiles(PluginManager pm, String extension, Collection<File> all) throws IOException { File[] files = pm.rootDir.listFiles(new FilterByExtension(extension)); if (files==null) throw new IOException("Jenkins is unable to create " + pm.rootDir + "\nPerhaps its security privilege is insufficient"); all.addAll(Arrays.asList(files)); } /** * Lists up additional bundled plugins from the system property {@code hudson.bundled.plugins}. * Since 1.480 glob syntax is supported. * For use in the "mvn hudson-dev:run". * TODO: maven-hpi-plugin should inject its own InitStrategy instead of having this in the core. */ protected void getBundledPluginsFromProperty(final List<File> r) { String hplProperty = SystemProperties.getString("hudson.bundled.plugins"); if (hplProperty != null) { for (String hplLocation : hplProperty.split(",")) { File hpl = new File(hplLocation.trim()); if (hpl.exists()) { r.add(hpl); } else if (hpl.getName().contains("*")) { try { new DirScanner.Glob(hpl.getName(), null).scan(hpl.getParentFile(), new FileVisitor() { @Override public void visit(File f, String relativePath) throws IOException { r.add(f); } }); } catch (IOException x) { LOGGER.log(Level.WARNING, "could not expand " + hplLocation, x); } } else { LOGGER.warning("bundled plugin " + hplLocation + " does not exist"); } } } } /** * Selectively skip some of the initialization tasks. * * @return * true to skip the execution. */ public boolean skipInitTask(Task task) { return false; } /** * Obtains the instance to be used. */ public static InitStrategy get(ClassLoader cl) throws IOException { List<InitStrategy> r = Service.loadInstances(cl, InitStrategy.class); if (r.isEmpty()) return new InitStrategy(); // default InitStrategy s = r.get(0); LOGGER.fine("Using "+s+" as InitStrategy"); return s; } private static final Logger LOGGER = Logger.getLogger(InitStrategy.class.getName()); private static class FilterByExtension implements FilenameFilter { private final List<String> extensions; public FilterByExtension(String... extensions) { this.extensions = Arrays.asList(extensions); } public boolean accept(File dir, String name) { for (String extension : extensions) { if (name.endsWith(extension)) return true; } return false; } } }