/** * Called immediately after the construction. This is a separate method so that code executed from * here will see a valid value in {@link jenkins.model.Jenkins#pluginManager}. */ public TaskBuilder initTasks(final InitStrategy initStrategy) { TaskBuilder builder; if (!pluginListed) { builder = new TaskGraphBuilder() { List<File> archives; Collection<String> bundledPlugins; { Handle loadBundledPlugins = add( "Loading bundled plugins", new Executable() { public void run(Reactor session) throws Exception { bundledPlugins = loadBundledPlugins(); } }); Handle listUpPlugins = requires(loadBundledPlugins) .add( "Listing up plugins", new Executable() { public void run(Reactor session) throws Exception { archives = initStrategy.listPluginArchives(PluginManager.this); } }); requires(listUpPlugins) .attains(PLUGINS_LISTED) .add( "Preparing plugins", new Executable() { public void run(Reactor session) throws Exception { // once we've listed plugins, we can fill in the reactor with // plugin-specific initialization tasks TaskGraphBuilder g = new TaskGraphBuilder(); final Map<String, File> inspectedShortNames = new HashMap<String, File>(); for (final File arc : archives) { g.followedBy() .notFatal() .attains(PLUGINS_LISTED) .add( "Inspecting plugin " + arc, new Executable() { public void run(Reactor session1) throws Exception { try { PluginWrapper p = strategy.createPluginWrapper(arc); if (isDuplicate(p)) return; p.isBundled = containsHpiJpi(bundledPlugins, arc.getName()); plugins.add(p); } catch (IOException e) { failedPlugins.add(new FailedPlugin(arc.getName(), e)); throw e; } } /** * Inspects duplication. this happens when you run hpi:run on * a bundled plugin, as well as putting numbered jpi files, * like "cobertura-1.0.jpi" and "cobertura-1.1.jpi" */ private boolean isDuplicate(PluginWrapper p) { String shortName = p.getShortName(); if (inspectedShortNames.containsKey(shortName)) { LOGGER.info( "Ignoring " + arc + " because " + inspectedShortNames.get(shortName) + " is already loaded"); return true; } inspectedShortNames.put(shortName, arc); return false; } }); } g.followedBy() .attains(PLUGINS_LISTED) .add( "Checking cyclic dependencies", new Executable() { /** Makes sure there's no cycle in dependencies. */ public void run(Reactor reactor) throws Exception { try { CyclicGraphDetector<PluginWrapper> cgd = new CyclicGraphDetector<PluginWrapper>() { @Override protected List<PluginWrapper> getEdges( PluginWrapper p) { List<PluginWrapper> next = new ArrayList<PluginWrapper>(); addTo(p.getDependencies(), next); addTo(p.getOptionalDependencies(), next); return next; } private void addTo( List<Dependency> dependencies, List<PluginWrapper> r) { for (Dependency d : dependencies) { PluginWrapper p = getPlugin(d.shortName); if (p != null) r.add(p); } } @Override protected void reactOnCycle( PluginWrapper q, List<PluginWrapper> cycle) throws hudson.util.CyclicGraphDetector .CycleDetectedException { LOGGER.log( Level.SEVERE, "found cycle in plugin dependencies: (root=" + q + ", deactivating all involved) " + Util.join(cycle, " -> ")); for (PluginWrapper pluginWrapper : cycle) { pluginWrapper.setHasCycleDependency(true); failedPlugins.add( new FailedPlugin( pluginWrapper.getShortName(), new CycleDetectedException(cycle))); } } }; cgd.run(getPlugins()); // obtain topologically sorted list and overwrite the list ListIterator<PluginWrapper> litr = plugins.listIterator(); for (PluginWrapper p : cgd.getSorted()) { litr.next(); litr.set(p); if (p.isActive()) activePlugins.add(p); } } catch (CycleDetectedException e) { stop(); // disable all plugins since classloading from them // can lead to StackOverflow throw e; // let Hudson fail } } }); // Let's see for a while until we open this functionality up to plugins // // g.followedBy().attains(PLUGINS_LISTED).add("Load compatibility rules", // new Executable() { // public void run(Reactor reactor) throws // Exception { // // compatibilityTransformer.loadRules(uberClassLoader); // } // }); session.addAll(g.discoverTasks(session)); pluginListed = true; // technically speaking this is still too early, as at this // point tasks are merely scheduled, not necessarily executed. } }); } }; } else { builder = TaskBuilder.EMPTY_BUILDER; } final InitializerFinder initializerFinder = new InitializerFinder(uberClassLoader); // misc. stuff // lists up initialization tasks about loading plugins. return TaskBuilder.union( initializerFinder, // this scans @Initializer in the core once builder, new TaskGraphBuilder() { { requires(PLUGINS_LISTED) .attains(PLUGINS_PREPARED) .add( "Loading plugins", new Executable() { /** Once the plugins are listed, schedule their initialization. */ public void run(Reactor session) throws Exception { Jenkins.getInstance() .lookup .set(PluginInstanceStore.class, new PluginInstanceStore()); TaskGraphBuilder g = new TaskGraphBuilder(); // schedule execution of loading plugins for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { g.followedBy() .notFatal() .attains(PLUGINS_PREPARED) .add( "Loading plugin " + p.getShortName(), new Executable() { public void run(Reactor session) throws Exception { try { p.resolvePluginDependencies(); strategy.load(p); } catch (IOException e) { failedPlugins.add(new FailedPlugin(p.getShortName(), e)); activePlugins.remove(p); plugins.remove(p); throw e; } } }); } // schedule execution of initializing plugins for (final PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) { g.followedBy() .notFatal() .attains(PLUGINS_STARTED) .add( "Initializing plugin " + p.getShortName(), new Executable() { public void run(Reactor session) throws Exception { if (!activePlugins.contains(p)) { return; } try { p.getPlugin().postInitialize(); } catch (Exception e) { failedPlugins.add(new FailedPlugin(p.getShortName(), e)); activePlugins.remove(p); plugins.remove(p); throw e; } } }); } g.followedBy() .attains(PLUGINS_STARTED) .add( "Discovering plugin initialization tasks", new Executable() { public void run(Reactor reactor) throws Exception { // rescan to find plugin-contributed @Initializer reactor.addAll(initializerFinder.discoverTasks(reactor)); } }); // register them all session.addAll(g.discoverTasks(session)); } }); } }); }