mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-06 16:45:58 +00:00
Merged revisions 23666-23667,23670,23711,23782-23785,23788,23809-23810,23817,23859,23870,23879,23899-23904,23906-23907,23909,23913 via svnmerge from
https://www.dev.java.net/svn/hudson/branches/managed-startup ........ r23666 | kohsuke | 2009-11-11 15:56:46 -0800 (Wed, 11 Nov 2009) | 1 line Reworked the initialization logic ........ r23667 | kohsuke | 2009-11-11 16:01:56 -0800 (Wed, 11 Nov 2009) | 1 line fixed a failing test ........ r23670 | kohsuke | 2009-11-11 16:42:32 -0800 (Wed, 11 Nov 2009) | 1 line use a non-snapshot ........ r23711 | kohsuke | 2009-11-13 14:27:28 -0800 (Fri, 13 Nov 2009) | 1 line Session -> Reactor ........ r23782 | kohsuke | 2009-11-17 10:35:14 -0800 (Tue, 17 Nov 2009) | 1 line Poorman's clone of JDK6 ServiceLoader. ........ r23783 | kohsuke | 2009-11-17 10:56:25 -0800 (Tue, 17 Nov 2009) | 1 line Integrated the fatal support so that all errors are sent to the listener. ........ r23784 | kohsuke | 2009-11-17 10:58:07 -0800 (Tue, 17 Nov 2009) | 1 line with the new reactor initialization, things are getting too verbose ........ r23785 | kohsuke | 2009-11-17 11:18:45 -0800 (Tue, 17 Nov 2009) | 1 line failing to load jobs are non-fatal, too ........ r23788 | kohsuke | 2009-11-17 13:18:59 -0800 (Tue, 17 Nov 2009) | 1 line logging the exception properly ........ r23809 | kohsuke | 2009-11-17 18:45:47 -0800 (Tue, 17 Nov 2009) | 1 line releasing 1.1 as milestone ........ r23810 | kohsuke | 2009-11-17 18:47:37 -0800 (Tue, 17 Nov 2009) | 2 lines - use a better idiom for building a sequence - use null display name to signify internal book-keeping tasks that's not worth displaying ........ r23817 | kohsuke | 2009-11-18 07:54:17 -0800 (Wed, 18 Nov 2009) | 1 line removed accidental JDK6 dependency. ........ r23859 | kohsuke | 2009-11-19 11:03:28 -0800 (Thu, 19 Nov 2009) | 1 line introducing the strategy pattern to hook into key decision making points during the start up ........ r23870 | kohsuke | 2009-11-19 14:33:07 -0800 (Thu, 19 Nov 2009) | 1 line bug fix. These steps add additional tasks to the reactor, so they need to also prevent future milestones from getting attained. ........ r23879 | kohsuke | 2009-11-19 16:54:59 -0800 (Thu, 19 Nov 2009) | 1 line moved the logic to the strategy. ........ r23899 | kohsuke | 2009-11-20 09:32:13 -0800 (Fri, 20 Nov 2009) | 1 line handle duplicate plugins better ........ r23900 | kohsuke | 2009-11-20 10:06:42 -0800 (Fri, 20 Nov 2009) | 1 line support pre-exploded plugin ........ r23901 | kohsuke | 2009-11-20 10:49:04 -0800 (Fri, 20 Nov 2009) | 1 line making it a subtype ........ r23902 | kohsuke | 2009-11-20 10:54:42 -0800 (Fri, 20 Nov 2009) | 1 line allow InitStrategy to skip some tasks ........ r23903 | kohsuke | 2009-11-20 11:03:38 -0800 (Fri, 20 Nov 2009) | 1 line doc improvement. ........ r23904 | kohsuke | 2009-11-20 11:03:49 -0800 (Fri, 20 Nov 2009) | 1 line no need to throw exceptions ........ r23906 | kohsuke | 2009-11-20 11:17:34 -0800 (Fri, 20 Nov 2009) | 1 line debug switch to disable the automatic launch of slaves ........ r23907 | kohsuke | 2009-11-20 11:25:15 -0800 (Fri, 20 Nov 2009) | 1 line added a magic object for cancelling all saves ........ r23909 | kohsuke | 2009-11-20 11:29:53 -0800 (Fri, 20 Nov 2009) | 1 line moving initialization up ........ r23913 | kohsuke | 2009-11-20 13:32:08 -0800 (Fri, 20 Nov 2009) | 1 line seeing heap space problem during tests ........ git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@23992 71c3de6d-444a-0410-be80-ed276b4c234a
This commit is contained in:
1 parent
ef7768a0a9
commit
fd13eee6d5
26 files changed
+1154
-325
No files matched your search
@@ -157,8 +157,18 @@ public class BulkChange {
|
||||
*/
|
||||
public static boolean contains(Saveable s) {
|
||||
for(BulkChange b=current(); b!=null; b=b.parent)
|
||||
if(b.saveable== s)
|
||||
if(b.saveable==s || b.saveable==ALL)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic {@link Saveable} instance that can make {@link BulkChange} veto
|
||||
* all the save operations by making the {@link #contains(Saveable)} method return
|
||||
* true for everything.
|
||||
*/
|
||||
public static final Saveable ALL = new Saveable() {
|
||||
public void save() {
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -72,16 +72,16 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
}
|
||||
|
||||
public PluginWrapper createPluginWrapper(File archive) throws IOException {
|
||||
LOGGER.info("Loading plugin: " + archive);
|
||||
LOGGER.info("Preparing plugin: " + archive);
|
||||
|
||||
final Manifest manifest;
|
||||
URL baseResourceURL;
|
||||
|
||||
boolean isLinked = archive.getName().endsWith(".hpl");
|
||||
|
||||
File expandDir = null;
|
||||
// if .hpi, this is the directory where war is expanded
|
||||
|
||||
boolean isLinked = archive.getName().endsWith(".hpl");
|
||||
if (isLinked) {
|
||||
// resolve the .hpl file to the location of the manifest file
|
||||
String firstLine = new BufferedReader(new FileReader(archive))
|
||||
@@ -102,21 +102,25 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
in.close();
|
||||
}
|
||||
} else {
|
||||
expandDir = new File(archive.getParentFile(), PluginWrapper.getBaseName(archive));
|
||||
explode(archive, expandDir);
|
||||
if (archive.isDirectory()) {// already expanded
|
||||
expandDir = archive;
|
||||
} else {
|
||||
expandDir = new File(archive.getParentFile(), PluginWrapper.getBaseName(archive));
|
||||
explode(archive, expandDir);
|
||||
}
|
||||
|
||||
File manifestFile = new File(expandDir, "META-INF/MANIFEST.MF");
|
||||
if (!manifestFile.exists()) {
|
||||
throw new IOException(
|
||||
"Plugin installation failed. No manifest at "
|
||||
+ manifestFile);
|
||||
}
|
||||
FileInputStream fin = new FileInputStream(manifestFile);
|
||||
try {
|
||||
manifest = new Manifest(fin);
|
||||
} finally {
|
||||
fin.close();
|
||||
}
|
||||
File manifestFile = new File(expandDir, "META-INF/MANIFEST.MF");
|
||||
if (!manifestFile.exists()) {
|
||||
throw new IOException(
|
||||
"Plugin installation failed. No manifest at "
|
||||
+ manifestFile);
|
||||
}
|
||||
FileInputStream fin = new FileInputStream(manifestFile);
|
||||
try {
|
||||
manifest = new Manifest(fin);
|
||||
} finally {
|
||||
fin.close();
|
||||
}
|
||||
}
|
||||
|
||||
final Attributes atts = manifest.getMainAttributes();
|
||||
@@ -327,8 +331,6 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
if(explodeTime.exists() && explodeTime.lastModified()==archive.lastModified())
|
||||
return; // no need to expand
|
||||
|
||||
LOGGER.info("Extracting "+archive);
|
||||
|
||||
// delete the contents so that old files won't interfere with new files
|
||||
Util.deleteContentsRecursive(destDir);
|
||||
|
||||
|
||||
@@ -23,17 +23,25 @@
|
||||
*/
|
||||
package hudson;
|
||||
|
||||
import static hudson.init.InitMilestone.PLUGINS_PREPARED;
|
||||
import static hudson.init.InitMilestone.PLUGINS_STARTED;
|
||||
import static hudson.init.InitMilestone.PLUGINS_LISTED;
|
||||
import hudson.init.InitStrategy;
|
||||
import hudson.model.AbstractModelObject;
|
||||
import hudson.model.Failure;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.UpdateSite;
|
||||
import hudson.model.UpdateCenter;
|
||||
import hudson.model.UpdateSite;
|
||||
import hudson.util.Service;
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jvnet.hudson.reactor.Executable;
|
||||
import org.jvnet.hudson.reactor.Reactor;
|
||||
import org.jvnet.hudson.reactor.TaskBuilder;
|
||||
import org.jvnet.hudson.reactor.TaskGraphBuilder;
|
||||
import org.kohsuke.stapler.HttpRedirect;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
import org.kohsuke.stapler.QueryParameter;
|
||||
@@ -44,17 +52,17 @@ import org.kohsuke.stapler.WebApp;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -102,7 +110,7 @@ public final class PluginManager extends AbstractModelObject {
|
||||
/**
|
||||
* Strategy for creating and initializing plugins
|
||||
*/
|
||||
private PluginStrategy strategy;
|
||||
private final PluginStrategy strategy;
|
||||
|
||||
public PluginManager(ServletContext context) {
|
||||
this.context = context;
|
||||
@@ -112,58 +120,8 @@ public final class PluginManager extends AbstractModelObject {
|
||||
rootDir = new File(Hudson.getInstance().getRootDir(),"plugins");
|
||||
if(!rootDir.exists())
|
||||
rootDir.mkdirs();
|
||||
|
||||
Collection<String> bundledPlugins = loadBundledPlugins();
|
||||
|
||||
File[] archives = rootDir.listFiles(new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".hpi") // plugin jar file
|
||||
|| name.endsWith(".hpl"); // linked plugin. for debugging.
|
||||
}
|
||||
});
|
||||
|
||||
if(archives==null) {
|
||||
LOGGER.severe("Hudson is unable to create "+rootDir+"\nPerhaps its security privilege is insufficient");
|
||||
return;
|
||||
}
|
||||
|
||||
strategy = createPluginStrategy();
|
||||
|
||||
// load plugins from a system property, for use in the "mvn hudson-dev:run"
|
||||
List<File> archivesList = new ArrayList<File>(Arrays.asList(archives));
|
||||
String hplProperty = System.getProperty("hudson.bundled.plugins");
|
||||
if (hplProperty != null) {
|
||||
for (String hplLocation: hplProperty.split(",")) {
|
||||
File hpl = new File(hplLocation.trim());
|
||||
if (hpl.exists())
|
||||
archivesList.add(hpl);
|
||||
else
|
||||
LOGGER.warning("bundled plugin " + hplLocation + " does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
for( File arc : archivesList ) {
|
||||
try {
|
||||
PluginWrapper p = strategy.createPluginWrapper(arc);
|
||||
p.isBundled = bundledPlugins.contains(arc.getName());
|
||||
plugins.add(p);
|
||||
if(p.isActive())
|
||||
activePlugins.add(p);
|
||||
} catch (IOException e) {
|
||||
failedPlugins.add(new FailedPlugin(arc.getName(),e));
|
||||
LOGGER.log(Level.SEVERE, "Failed to load a plug-in " + arc, e);
|
||||
}
|
||||
}
|
||||
|
||||
for (PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()]))
|
||||
try {
|
||||
strategy.load(p);
|
||||
} catch (IOException e) {
|
||||
failedPlugins.add(new FailedPlugin(p.getShortName(),e));
|
||||
LOGGER.log(Level.SEVERE, "Failed to load a plug-in " + p.getShortName(), e);
|
||||
activePlugins.remove(p);
|
||||
plugins.remove(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,18 +129,114 @@ public final class PluginManager extends AbstractModelObject {
|
||||
* This is a separate method so that code executed from here will see a valid value in
|
||||
* {@link Hudson#pluginManager}.
|
||||
*/
|
||||
public void initialize() {
|
||||
for (PluginWrapper p : activePlugins.toArray(new PluginWrapper[activePlugins.size()])) {
|
||||
strategy.initializeComponents(p);
|
||||
try {
|
||||
p.getPlugin().postInitialize();
|
||||
} catch (Exception e) {
|
||||
failedPlugins.add(new FailedPlugin(p.getShortName(), e));
|
||||
LOGGER.log(Level.SEVERE, "Failed to post-initialize a plug-in " + p.getShortName(), e);
|
||||
activePlugins.remove(p);
|
||||
plugins.remove(p);
|
||||
public TaskBuilder initTasks(final InitStrategy initStrategy) {
|
||||
return 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 {
|
||||
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 session) throws Exception {
|
||||
try {
|
||||
PluginWrapper p = strategy.createPluginWrapper(arc);
|
||||
if (isDuplicate(p)) return;
|
||||
|
||||
p.isBundled = bundledPlugins.contains(arc.getName());
|
||||
plugins.add(p);
|
||||
if(p.isActive())
|
||||
activePlugins.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 hpi files, like "cobertura-1.0.hpi" and "cobertura-1.1.hpi"
|
||||
*/
|
||||
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.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 {
|
||||
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 {
|
||||
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 {
|
||||
try {
|
||||
p.getPlugin().postInitialize();
|
||||
} catch (Exception e) {
|
||||
failedPlugins.add(new FailedPlugin(p.getShortName(), e));
|
||||
activePlugins.remove(p);
|
||||
plugins.remove(p);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// register them all
|
||||
session.addAll(g.discoverTasks(session));
|
||||
}
|
||||
});
|
||||
|
||||
// register them all
|
||||
session.addAll(g.discoverTasks(session));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,8 +41,7 @@ public interface PluginStrategy extends ExtensionPoint {
|
||||
/**
|
||||
* Creates a plugin wrapper, which provides a management interface for the plugin
|
||||
* @param archive
|
||||
* @return
|
||||
* @throws IOException
|
||||
* Either a directory that points to a pre-exploded plugin, or an hpi file, or an hpl file.
|
||||
*/
|
||||
public abstract PluginWrapper createPluginWrapper(File archive)
|
||||
throws IOException;
|
||||
|
||||
@@ -295,8 +295,7 @@ public final class PluginWrapper {
|
||||
try {
|
||||
plugin.stop();
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Failed to shut down "+shortName);
|
||||
System.err.println(t);
|
||||
LOGGER.log(WARNING, "Failed to shut down "+shortName, t);
|
||||
}
|
||||
// Work around a bug in commons-logging.
|
||||
// See http://www.szegedi.org/articles/memleak.html
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
|
||||
*
|
||||
* 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.init;
|
||||
|
||||
import org.jvnet.hudson.reactor.Executable;
|
||||
import org.jvnet.hudson.reactor.Milestone;
|
||||
import org.jvnet.hudson.reactor.TaskBuilder;
|
||||
import org.jvnet.hudson.reactor.TaskGraphBuilder;
|
||||
|
||||
/**
|
||||
* Various key milestone in the initialization process of Hudson.
|
||||
*
|
||||
* <p>
|
||||
* Plugins can use these milestones to execute their initialization at the right moment
|
||||
* (in addition to defining their own milestones by implementing {@link Milestone}.
|
||||
*
|
||||
* <p>
|
||||
* These milestones are achieve in this order.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public enum InitMilestone implements Milestone {
|
||||
/**
|
||||
* The very first milestone that gets achieved without doing anything.
|
||||
*
|
||||
* This is used in {@link Initializer#after()} since annotations cannot have null as the default value.
|
||||
*/
|
||||
STARTED,
|
||||
|
||||
/**
|
||||
* By this milestone, all plugins metadata are inspected and their dependencies figured out.
|
||||
*/
|
||||
PLUGINS_LISTED,
|
||||
|
||||
/**
|
||||
* By this milestone, all plugin metadata are loaded and its classloader set up.
|
||||
*/
|
||||
PLUGINS_PREPARED,
|
||||
/**
|
||||
* By this milestone, all plugins start executing.
|
||||
*
|
||||
* <p>
|
||||
* This is a separate milestone from {@link #PLUGINS_PREPARED} since the execution
|
||||
* of a plugin often involves finding extension point implementations, which in turn
|
||||
* require all the classes from all the plugins to be loadable.
|
||||
*/
|
||||
PLUGINS_STARTED,
|
||||
/**
|
||||
* By this milestone, all jobs and their build records are loaded from disk.
|
||||
*/
|
||||
JOB_LOADED,
|
||||
/**
|
||||
* The very last milestone
|
||||
*
|
||||
* This is used in {@link Initializer#before()} since annotations cannot have null as the default value.
|
||||
*/
|
||||
COMPLETED;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a set of dummy tasks to enforce ordering among {@link InitMilestone}s.
|
||||
*/
|
||||
public static TaskBuilder ordering() {
|
||||
TaskGraphBuilder b = new TaskGraphBuilder();
|
||||
InitMilestone[] v = values();
|
||||
for (int i=0; i<v.length-1; i++)
|
||||
b.add(null, Executable.NOOP).requires(v[i]).attains(v[i+1]);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package hudson.init;
|
||||
|
||||
import org.jvnet.hudson.reactor.ReactorListener;
|
||||
import org.kohsuke.MetaInfServices;
|
||||
import hudson.model.Hudson;
|
||||
|
||||
/**
|
||||
* {@link ReactorListener}s that get notified of the Hudson initialization process.
|
||||
*
|
||||
* <p>
|
||||
* Because the act of initializing plugins is a part of the Hudson 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
|
||||
* @see Hudson#buildReactorListener()
|
||||
*/
|
||||
public interface InitReactorListener extends ReactorListener {
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
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.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import hudson.PluginManager;
|
||||
import hudson.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Strategy pattern of the various key decision making during the Hudson initialization.
|
||||
*
|
||||
* <p>
|
||||
* Because the act of initializing plugins is a part of the Hudson 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 *.hpi and *.hpl to expand and load.
|
||||
*
|
||||
* <p>
|
||||
* Normally we look at {@code $HUDSON_HOME/plugins/*.hpi} and *.hpl.
|
||||
*
|
||||
* @return
|
||||
* never null but can be empty. The list can contain different versions of the same plugin,
|
||||
* and when that happens, Hudson will ignore all but the first one in the list.
|
||||
*/
|
||||
public List<File> listPluginArchives(PluginManager pm) throws IOException {
|
||||
File[] archives = pm.rootDir.listFiles(new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".hpi") // plugin jar file
|
||||
|| name.endsWith(".hpl"); // linked plugin. for debugging.
|
||||
}
|
||||
});
|
||||
if (archives == null)
|
||||
throw new IOException("Hudson is unable to create " + pm.rootDir + "\nPerhaps its security privilege is insufficient");
|
||||
|
||||
List<File> r = new ArrayList<File>();
|
||||
getBundledPluginsFromProperty(r);
|
||||
r.addAll(Arrays.asList(archives));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists up additional bundled plugins from the system property.
|
||||
*
|
||||
* 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(List<File> r) {
|
||||
String hplProperty = System.getProperty("hudson.bundled.plugins");
|
||||
if (hplProperty != null) {
|
||||
for (String hplLocation : hplProperty.split(",")) {
|
||||
File hpl = new File(hplLocation.trim());
|
||||
if (hpl.exists())
|
||||
r.add(hpl);
|
||||
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 = ServiceLoader.load(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());
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
|
||||
*
|
||||
* 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.init;
|
||||
|
||||
import org.jvnet.hudson.annotation_indexer.Indexed;
|
||||
import org.jvnet.hudson.reactor.Task;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import java.lang.annotation.Retention;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static hudson.init.InitMilestone.STARTED;
|
||||
import static hudson.init.InitMilestone.COMPLETED;
|
||||
|
||||
/**
|
||||
* Placed on static methods to indicate that this method is to be run during the Hudson start up to perform
|
||||
* some sort of initialization tasks.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@Indexed
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target(METHOD)
|
||||
public @interface Initializer {
|
||||
/**
|
||||
* Indicates that the specified milestone is necessary before executing this initializer.
|
||||
*
|
||||
* <p>
|
||||
* This has the identical purpose as {@link #requires()}, but it's separated to allow better type-safety
|
||||
* when using {@link InitMilestone} as a requirement (since enum member definitions need to be constant.)
|
||||
*/
|
||||
InitMilestone after() default STARTED;
|
||||
|
||||
/**
|
||||
* Indicates that this initializer is a necessary step before achieving the specified milestone.
|
||||
*
|
||||
* <p>
|
||||
* This has the identical purpose as {@link #attains()}. See {@link #after()} for why there are two things
|
||||
* to achieve the same goal.
|
||||
*/
|
||||
InitMilestone before() default COMPLETED;
|
||||
|
||||
/**
|
||||
* Indicates the milestones necessary before executing this initializer.
|
||||
*/
|
||||
String[] requires() default {};
|
||||
|
||||
/**
|
||||
* Indicates the milestones that this initializer contributes to.
|
||||
*
|
||||
* A milestone is considered attained if all the initializers that attains the given milestone
|
||||
* completes. So it works as a kind of join.
|
||||
*/
|
||||
String[] attains() default {};
|
||||
|
||||
/**
|
||||
* Key in <tt>Messages.properties</tt> that represents what this task is about. Used for rendering the progress.
|
||||
* Defaults to "${short class name}.${method Name}".
|
||||
*/
|
||||
String displayName() default "";
|
||||
|
||||
/**
|
||||
* Should the failure in this task prevent Hudson from starting up?
|
||||
*
|
||||
* @see Task#failureIsFatal()
|
||||
*/
|
||||
boolean fatal() default true;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
|
||||
*
|
||||
* 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.init;
|
||||
|
||||
import org.jvnet.hudson.annotation_indexer.Index;
|
||||
import org.jvnet.hudson.reactor.Milestone;
|
||||
import org.jvnet.hudson.reactor.Task;
|
||||
import org.jvnet.hudson.reactor.TaskBuilder;
|
||||
import org.jvnet.hudson.reactor.MilestoneImpl;
|
||||
import org.jvnet.hudson.reactor.Reactor;
|
||||
import org.jvnet.localizer.ResourceBundleHolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import hudson.model.Hudson;
|
||||
|
||||
/**
|
||||
* Discovers initialization tasks from {@link Initializer}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class InitializerFinder extends TaskBuilder {
|
||||
private final ClassLoader cl;
|
||||
|
||||
public InitializerFinder(ClassLoader cl) {
|
||||
this.cl = cl;
|
||||
}
|
||||
|
||||
public InitializerFinder() {
|
||||
this(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
public Collection<Task> discoverTasks(Reactor session) throws IOException {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (Method e : Index.list(Initializer.class,cl,Method.class)) {
|
||||
if (!Modifier.isStatic(e.getModifiers()))
|
||||
throw new IOException(e+" is not a static method");
|
||||
|
||||
Initializer i = e.getAnnotation(Initializer.class);
|
||||
if (i==null) continue; // stale index
|
||||
|
||||
result.add(new TaskImpl(i, e));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the display name of the given initialization task
|
||||
*/
|
||||
protected String getDisplayNameOf(Method e, Initializer i) {
|
||||
try {
|
||||
Class<?> c = e.getDeclaringClass();
|
||||
ResourceBundleHolder rb = ResourceBundleHolder.get(c.getClassLoader().loadClass(c.getPackage().getName() + ".Messages"));
|
||||
|
||||
String key = i.displayName();
|
||||
if (key.length()==0) return c.getSimpleName()+"."+e.getName();
|
||||
return rb.format(key);
|
||||
} catch (ClassNotFoundException x) {
|
||||
throw (Error)new NoClassDefFoundError(x.getMessage()+" for "+e.toString()).initCause(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given initialization method.
|
||||
*/
|
||||
protected void invoke(Method e) {
|
||||
try {
|
||||
Class<?>[] pt = e.getParameterTypes();
|
||||
Object[] args = new Object[pt.length];
|
||||
for (int i=0; i<args.length; i++)
|
||||
args[i] = lookUp(pt[i]);
|
||||
e.invoke(null,args);
|
||||
} catch (IllegalAccessException x) {
|
||||
throw (Error)new IllegalAccessError().initCause(x);
|
||||
} catch (InvocationTargetException x) {
|
||||
throw new Error(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the parameter injection of the initialization method.
|
||||
*/
|
||||
private Object lookUp(Class<?> type) {
|
||||
if (type== Hudson.class)
|
||||
return Hudson.getInstance();
|
||||
throw new IllegalArgumentException("Unable to inject "+type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Task implementation.
|
||||
*/
|
||||
public class TaskImpl implements Task {
|
||||
final Collection<Milestone> requires;
|
||||
final Collection<Milestone> attains;
|
||||
private final Initializer i;
|
||||
private final Method e;
|
||||
|
||||
private TaskImpl(Initializer i, Method e) {
|
||||
this.i = i;
|
||||
this.e = e;
|
||||
requires = toMilestones(i.requires(), i.after());
|
||||
attains = toMilestones(i.attains(), i.before());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Initializer} annotaion on the {@linkplain #getMethod() method}
|
||||
*/
|
||||
public Initializer getAnnotation() {
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method that runs the initialization, that this task wraps.
|
||||
*/
|
||||
public Method getMethod() {
|
||||
return e;
|
||||
}
|
||||
|
||||
public Collection<Milestone> requires() {
|
||||
return requires;
|
||||
}
|
||||
|
||||
public Collection<Milestone> attains() {
|
||||
return attains;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return getDisplayNameOf(e, i);
|
||||
}
|
||||
|
||||
public boolean failureIsFatal() {
|
||||
return i.fatal();
|
||||
}
|
||||
|
||||
public void run(Reactor session) {
|
||||
invoke(e);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return e.toString();
|
||||
}
|
||||
|
||||
private Collection<Milestone> toMilestones(String[] tokens, InitMilestone m) {
|
||||
List<Milestone> r = new ArrayList<Milestone>();
|
||||
for (String s : tokens) {
|
||||
try {
|
||||
r.add(InitMilestone.valueOf(s));
|
||||
} catch (IllegalArgumentException x) {
|
||||
r.add(new MilestoneImpl(s));
|
||||
}
|
||||
}
|
||||
r.add(m);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
|
||||
*
|
||||
* 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.init.impl;
|
||||
|
||||
import groovy.lang.GroovyShell;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import hudson.model.Hudson;
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import hudson.init.Initializer;
|
||||
|
||||
/**
|
||||
* Run the initialization script, if it exists.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class GroovyInitScript {
|
||||
@Initializer(after=JOB_LOADED)
|
||||
public static void init(Hudson h) throws IOException {
|
||||
|
||||
File initScript = new File(h.getRootDir(),"init.groovy");
|
||||
if(initScript.exists()) {
|
||||
LOGGER.info("Executing "+initScript);
|
||||
GroovyShell shell = new GroovyShell(h.getPluginManager().uberClassLoader);
|
||||
shell.evaluate(initScript);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(GroovyInitScript.class.getName());
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
|
||||
*
|
||||
* 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.init.impl;
|
||||
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import hudson.init.Initializer;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Messages;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Prepares userContent folder and put a readme if it doesn't exist.
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class InitialUserContent {
|
||||
@Initializer(after=JOB_LOADED)
|
||||
public static void init(Hudson h) throws IOException {
|
||||
File userContentDir = new File(h.getRootDir(), "userContent");
|
||||
if(!userContentDir.exists()) {
|
||||
userContentDir.mkdirs();
|
||||
FileUtils.writeStringToFile(new File(userContentDir,"readme.txt"), Messages.Hudson_USER_CONTENT_README());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Logic for Hudson startup.
|
||||
*
|
||||
* <p>
|
||||
* Hudson's start up is based on the same idea as the modern Unix init mechanism like initng/upstart/SMF.
|
||||
* It first builds a set of {@link Task}s that are units of the initialization work, and have them declare
|
||||
* dependencies among themselves. For example, jobs are only loaded after all the plugins are initialized,
|
||||
* and restoring the build queue requires all the jobs to be loaded.
|
||||
*
|
||||
* <p>
|
||||
* Such micro-scopic dependencies are organized into a bigger directed acyclic graph, which is then executed
|
||||
* via {@link Session}. During execution of the reactor, additional tasks can be discovred and added to
|
||||
* the DAG. We use this additional indirection to:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Perform initialization in parallel where possible.
|
||||
* <li>Provide progress report on where we are in the initialization.
|
||||
* <li>Collect status of the initialization and their failures.
|
||||
* </ol>
|
||||
*/
|
||||
package hudson.init;
|
||||
|
||||
import org.jvnet.hudson.reactor.Task;
|
||||
@@ -25,6 +25,7 @@ package hudson.logging;
|
||||
|
||||
import hudson.FeedAdapter;
|
||||
import hudson.Functions;
|
||||
import hudson.init.Initializer;
|
||||
import hudson.model.AbstractModelObject;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.RSS;
|
||||
@@ -177,4 +178,9 @@ public class LogRecorderManager extends AbstractModelObject {
|
||||
}
|
||||
},req,rsp);
|
||||
}
|
||||
|
||||
@Initializer
|
||||
public static void init(Hudson h) throws IOException {
|
||||
h.getLog().load();
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,12 @@
|
||||
package hudson.model;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import groovy.lang.GroovyShell;
|
||||
import hudson.BulkChange;
|
||||
import hudson.DescriptorExtensionList;
|
||||
import hudson.Extension;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionListView;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.FilePath;
|
||||
import hudson.Functions;
|
||||
import hudson.Launcher;
|
||||
@@ -36,35 +40,34 @@ import hudson.PluginWrapper;
|
||||
import hudson.ProxyConfiguration;
|
||||
import hudson.StructuredForm;
|
||||
import hudson.TcpSlaveAgentListener;
|
||||
import hudson.UDPBroadcastThread;
|
||||
import hudson.Util;
|
||||
import static hudson.Util.fixEmpty;
|
||||
import static hudson.Util.fixNull;
|
||||
import hudson.WebAppMain;
|
||||
import hudson.XmlFile;
|
||||
import hudson.UDPBroadcastThread;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.DescriptorExtensionList;
|
||||
import hudson.ExtensionListView;
|
||||
import hudson.Extension;
|
||||
import hudson.tools.ToolInstallation;
|
||||
import hudson.tools.ToolDescriptor;
|
||||
import hudson.cli.CliEntryPoint;
|
||||
import hudson.cli.CliManagerImpl;
|
||||
import hudson.cli.declarative.CLIMethod;
|
||||
import hudson.cli.declarative.CLIResolver;
|
||||
import hudson.logging.LogRecorderManager;
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import static hudson.init.InitMilestone.PLUGINS_STARTED;
|
||||
import hudson.init.InitializerFinder;
|
||||
import hudson.init.InitMilestone;
|
||||
import hudson.init.InitReactorListener;
|
||||
import hudson.init.InitStrategy;
|
||||
import hudson.lifecycle.Lifecycle;
|
||||
import hudson.logging.LogRecorderManager;
|
||||
import hudson.lifecycle.RestartNotSupportedException;
|
||||
import hudson.model.Descriptor.FormException;
|
||||
import hudson.model.listeners.ItemListener;
|
||||
import hudson.model.listeners.JobListener;
|
||||
import hudson.model.listeners.JobListener.JobListenerAdapter;
|
||||
import hudson.model.listeners.SaveableListener;
|
||||
import hudson.model.listeners.SCMListener;
|
||||
import hudson.model.listeners.SaveableListener;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.LocalChannel;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.scm.RepositoryBrowser;
|
||||
import hudson.scm.SCM;
|
||||
import hudson.scm.SCMDescriptor;
|
||||
@@ -82,70 +85,87 @@ import hudson.security.PermissionGroup;
|
||||
import hudson.security.SecurityMode;
|
||||
import hudson.security.SecurityRealm;
|
||||
import hudson.security.csrf.CrumbIssuer;
|
||||
import hudson.slaves.ComputerListener;
|
||||
import hudson.slaves.NodeProperty;
|
||||
import hudson.slaves.NodePropertyDescriptor;
|
||||
import hudson.slaves.RetentionStrategy;
|
||||
import hudson.slaves.NodeList;
|
||||
import hudson.slaves.Cloud;
|
||||
import hudson.slaves.ComputerListener;
|
||||
import hudson.slaves.DumbSlave;
|
||||
import hudson.slaves.NodeDescriptor;
|
||||
import hudson.slaves.NodeList;
|
||||
import hudson.slaves.NodeProperty;
|
||||
import hudson.slaves.NodePropertyDescriptor;
|
||||
import hudson.slaves.NodeProvisioner;
|
||||
import hudson.slaves.OfflineCause;
|
||||
import hudson.slaves.RetentionStrategy;
|
||||
import hudson.tasks.BuildWrapper;
|
||||
import hudson.tasks.Builder;
|
||||
import hudson.tasks.Mailer;
|
||||
import hudson.tasks.Publisher;
|
||||
import hudson.tools.ToolDescriptor;
|
||||
import hudson.tools.ToolInstallation;
|
||||
import hudson.triggers.Trigger;
|
||||
import hudson.triggers.TriggerDescriptor;
|
||||
import hudson.util.AdministrativeError;
|
||||
import hudson.util.CaseInsensitiveComparator;
|
||||
import hudson.util.ClockDifference;
|
||||
import hudson.util.CopyOnWriteList;
|
||||
import hudson.util.CopyOnWriteMap;
|
||||
import hudson.util.DaemonThreadFactory;
|
||||
import hudson.util.DescribableList;
|
||||
import hudson.util.FormValidation;
|
||||
import hudson.util.Futures;
|
||||
import hudson.util.HudsonIsLoading;
|
||||
import hudson.util.HudsonIsRestarting;
|
||||
import hudson.util.Iterators;
|
||||
import hudson.util.Memoizer;
|
||||
import hudson.util.MultipartFormDataParser;
|
||||
import hudson.util.RemotingDiagnostics;
|
||||
import hudson.util.TextFile;
|
||||
import hudson.util.XStream2;
|
||||
import hudson.util.HudsonIsRestarting;
|
||||
import hudson.util.DescribableList;
|
||||
import hudson.util.Futures;
|
||||
import hudson.util.Memoizer;
|
||||
import hudson.util.Iterators;
|
||||
import hudson.util.FormValidation;
|
||||
import hudson.util.VersionNumber;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import hudson.util.AdministrativeError;
|
||||
import hudson.util.TextFile;
|
||||
import hudson.util.VersionNumber;
|
||||
import hudson.util.XStream2;
|
||||
import hudson.util.ServiceLoader;
|
||||
import hudson.widgets.Widget;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.acegisecurity.*;
|
||||
import org.acegisecurity.AccessDeniedException;
|
||||
import org.acegisecurity.AcegiSecurityException;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
|
||||
import org.acegisecurity.ui.AbstractProcessingFilter;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.jelly.Script;
|
||||
import org.apache.commons.jelly.JellyException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.jelly.Script;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jvnet.hudson.reactor.Executable;
|
||||
import org.jvnet.hudson.reactor.ReactorException;
|
||||
import org.jvnet.hudson.reactor.Task;
|
||||
import org.jvnet.hudson.reactor.TaskBuilder;
|
||||
import org.jvnet.hudson.reactor.TaskGraphBuilder;
|
||||
import org.jvnet.hudson.reactor.Milestone;
|
||||
import org.jvnet.hudson.reactor.Reactor;
|
||||
import org.jvnet.hudson.reactor.ReactorListener;
|
||||
import org.jvnet.hudson.reactor.TaskGraphBuilder.Handle;
|
||||
import org.kohsuke.stapler.Ancestor;
|
||||
import org.kohsuke.stapler.HttpRedirect;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
import org.kohsuke.stapler.HttpResponses;
|
||||
import org.kohsuke.stapler.MetaClass;
|
||||
import org.kohsuke.stapler.QueryParameter;
|
||||
import org.kohsuke.stapler.Stapler;
|
||||
import org.kohsuke.stapler.StaplerFallback;
|
||||
import org.kohsuke.stapler.StaplerProxy;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
import org.kohsuke.stapler.StaplerFallback;
|
||||
import org.kohsuke.stapler.WebApp;
|
||||
import org.kohsuke.stapler.QueryParameter;
|
||||
import org.kohsuke.stapler.HttpRedirect;
|
||||
import org.kohsuke.stapler.HttpResponses;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
import org.kohsuke.stapler.jelly.JellyClassLoaderTearOff;
|
||||
import org.kohsuke.stapler.jelly.JellyRequestDispatcher;
|
||||
import org.kohsuke.stapler.framework.adjunct.AdjunctManager;
|
||||
import org.kohsuke.stapler.export.Exported;
|
||||
import org.kohsuke.stapler.export.ExportedBean;
|
||||
import org.kohsuke.stapler.framework.adjunct.AdjunctManager;
|
||||
import org.kohsuke.stapler.jelly.JellyClassLoaderTearOff;
|
||||
import org.kohsuke.stapler.jelly.JellyRequestDispatcher;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
@@ -157,14 +177,15 @@ import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.BindException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.Collator;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -176,32 +197,29 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Timer;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.logging.Level;
|
||||
import static java.util.logging.Level.SEVERE;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import static java.util.logging.Level.SEVERE;
|
||||
import java.util.regex.Pattern;
|
||||
import java.nio.charset.Charset;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
/**
|
||||
* Root object of the system.
|
||||
@@ -509,7 +527,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
*/
|
||||
private transient final LogRecorderManager log = new LogRecorderManager();
|
||||
|
||||
public Hudson(File root, ServletContext context) throws IOException, InterruptedException {
|
||||
public Hudson(File root, ServletContext context) throws IOException, InterruptedException, ReactorException {
|
||||
// As hudson is starting, grant this process full controll
|
||||
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);
|
||||
try {
|
||||
@@ -520,8 +538,9 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
throw new IllegalStateException("second instance");
|
||||
theInstance = this;
|
||||
|
||||
log.load();
|
||||
|
||||
// doing this early allows InitStrategy to set environment upfront
|
||||
final InitStrategy is = InitStrategy.get(Thread.currentThread().getContextClassLoader());
|
||||
|
||||
Trigger.timer = new Timer("Hudson cron thread");
|
||||
queue = new Queue(CONSISTENT_HASH?LoadBalancer.CONSISTENT_HASH:LoadBalancer.DEFAULT);
|
||||
|
||||
@@ -551,30 +570,18 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(SEVERE, "Failed to load proxy configuration", e);
|
||||
}
|
||||
|
||||
// load plugins.
|
||||
pluginManager = new PluginManager(context);
|
||||
pluginManager.initialize();
|
||||
|
||||
// if we are loading old data that doesn't have this field
|
||||
if(slaves==null) slaves = new NodeList();
|
||||
pluginManager = new PluginManager(context);
|
||||
|
||||
adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+VERSION_HASH);
|
||||
|
||||
load();
|
||||
|
||||
// try {
|
||||
// // fill up the cache
|
||||
// load();
|
||||
//
|
||||
// Controller c = new Controller();
|
||||
// c.startCPUProfiling(ProfilingModes.CPU_TRACING,""); // "java.*");
|
||||
// load();
|
||||
// c.stopCPUProfiling();
|
||||
// c.captureSnapshot(ProfilingModes.SNAPSHOT_WITHOUT_HEAP);
|
||||
// } catch (Exception e) {
|
||||
// throw new Error(e);
|
||||
// }
|
||||
// initialization consists of ...
|
||||
executeReactor( is,
|
||||
new InitializerFinder(), // misc. stuff
|
||||
pluginManager.initTasks(is), // loading and preparing plugins
|
||||
loadTasks(), // load jobs
|
||||
InitMilestone.ordering() // forced ordering among key milestones
|
||||
);
|
||||
|
||||
if(slaveAgentPort!=-1) {
|
||||
try {
|
||||
@@ -599,54 +606,89 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
cl.onOnline(c,new StreamTaskListener(System.out));
|
||||
}
|
||||
|
||||
getQueue().load();
|
||||
|
||||
for (ItemListener l : ItemListener.all())
|
||||
l.onLoaded();
|
||||
|
||||
// run the initialization script, if it exists.
|
||||
File initScript = new File(getRootDir(),"init.groovy");
|
||||
if(initScript.exists()) {
|
||||
LOGGER.info("Executing "+initScript);
|
||||
GroovyShell shell = new GroovyShell(pluginManager.uberClassLoader);
|
||||
try {
|
||||
shell.evaluate(initScript);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
File userContentDir = new File(getRootDir(), "userContent");
|
||||
if(!userContentDir.exists()) {
|
||||
userContentDir.mkdirs();
|
||||
FileUtils.writeStringToFile(new File(userContentDir,"readme.txt"),Messages.Hudson_USER_CONTENT_README());
|
||||
}
|
||||
|
||||
updateCenter.load(); // this has to wait until after all plugins load, to let custom UpdateCenterConfiguration take effect first.
|
||||
|
||||
Trigger.init();
|
||||
// pending SEZPOZ-8
|
||||
// // invoke post initialization methods
|
||||
// for ( IndexItem<PostInit,Void> i : Index.load(PostInit.class, Void.class, pluginManager.uberClassLoader)) {
|
||||
// try {
|
||||
// Method m = (Method)i.element();
|
||||
// if (Modifier.isStatic(m.getModifiers()))
|
||||
// m.invoke(null);
|
||||
// else
|
||||
// LOGGER.severe(m+" is annotated with @PostInit but it's not a static method");
|
||||
// } catch (InstantiationException e) {
|
||||
// LOGGER.log(SEVERE,"Failed to invoke @PostInit: "+i,e);
|
||||
// } catch (IllegalAccessException e) {
|
||||
// LOGGER.log(SEVERE,"Failed to invoke @PostInit: "+i,e);
|
||||
// } catch (InvocationTargetException e) {
|
||||
// LOGGER.log(SEVERE,"Failed to invoke @PostInit: "+i,e);
|
||||
// }
|
||||
// }
|
||||
} finally {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a reactor.
|
||||
*
|
||||
* @param is
|
||||
* If non-null, this can be consulted for ignoring some tasks. Only used during the initialization of Hudson.
|
||||
*/
|
||||
private void executeReactor(final InitStrategy is, TaskBuilder... builders) throws IOException, InterruptedException, ReactorException {
|
||||
Reactor reactor = new Reactor(builders) {
|
||||
/**
|
||||
* Sets the thread name to the task for better diagnostics.
|
||||
*/
|
||||
protected void runTask(Task task) throws Exception {
|
||||
if (is!=null && is.skipInitTask(task)) return;
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); // full access in the initialization thread
|
||||
String taskName = task.getDisplayName();
|
||||
|
||||
Thread t = Thread.currentThread();
|
||||
String name = t.getName();
|
||||
if (taskName !=null)
|
||||
t.setName(taskName);
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
super.runTask(task);
|
||||
if(LOG_STARTUP_PERFORMANCE)
|
||||
LOGGER.info(String.format("Took %dms for %s by %s",
|
||||
System.currentTimeMillis()-start, taskName, name));
|
||||
} finally {
|
||||
t.setName(name);
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ExecutorService es;
|
||||
if (PARALLEL_LOAD)
|
||||
es = new ThreadPoolExecutor(
|
||||
TWICE_CPU_NUM, TWICE_CPU_NUM, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadFactory());
|
||||
else
|
||||
es = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
|
||||
try {
|
||||
reactor.execute(es,buildReactorListener());
|
||||
} finally {
|
||||
es.shutdownNow(); // upon a successful return the executor queue should be empty. Upon an exception, we want to cancel all pending tasks
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates all the listeners into one and returns it.
|
||||
*
|
||||
* <p>
|
||||
* At this point plugins are not loaded yet, so we fall back to the META-INF/services look up to discover implementations.
|
||||
* As such there's no way for plugins to participate into this process.
|
||||
*/
|
||||
private ReactorListener buildReactorListener() throws IOException {
|
||||
List<ReactorListener> r = (List) ServiceLoader.load(Thread.currentThread().getContextClassLoader(), InitReactorListener.class);
|
||||
r.add(new ReactorListener() {
|
||||
public void onTaskStarted(Task t) {
|
||||
LOGGER.fine("Started "+t.getDisplayName());
|
||||
}
|
||||
|
||||
public void onTaskCompleted(Task t) {
|
||||
LOGGER.fine("Completed "+t.getDisplayName());
|
||||
}
|
||||
|
||||
public void onTaskFailed(Task t, Throwable err, boolean fatal) {
|
||||
LOGGER.log(Level.SEVERE, "Failed "+t.getDisplayName(),err);
|
||||
}
|
||||
|
||||
public void onAttained(Milestone milestone) {
|
||||
LOGGER.fine("Attained "+milestone.toString());
|
||||
}
|
||||
});
|
||||
return new ReactorListener.Aggregator(r);
|
||||
}
|
||||
|
||||
public TcpSlaveAgentListener getTcpSlaveAgentListener() {
|
||||
return tcpSlaveAgentListener;
|
||||
}
|
||||
@@ -1036,7 +1078,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
} else {
|
||||
if(n.getNumExecutors()>0) {
|
||||
computers.put(n,c=n.createComputer());
|
||||
if (!n.holdOffLaunchUntilSave) {
|
||||
if (!n.holdOffLaunchUntilSave && AUTOMATIC_SLAVE_LAUNCH) {
|
||||
RetentionStrategy retentionStrategy = c.getRetentionStrategy();
|
||||
if (retentionStrategy != null) {
|
||||
// if there is a retention strategy, it is responsible for deciding to start the computer
|
||||
@@ -1998,18 +2040,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
return new MasterComputer();
|
||||
}
|
||||
|
||||
private synchronized void load() throws IOException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
XmlFile cfg = getConfigFile();
|
||||
if(cfg.exists()) {
|
||||
// reset some data that may not exit in the disk file
|
||||
// so that we can take a proper compensation action later.
|
||||
primaryView = null;
|
||||
views.clear();
|
||||
cfg.unmarshal(this);
|
||||
}
|
||||
clouds.setOwner(this);
|
||||
|
||||
private synchronized TaskBuilder loadTasks() throws IOException {
|
||||
File projectsDir = new File(root,"jobs");
|
||||
if(!projectsDir.isDirectory() && !projectsDir.mkdirs()) {
|
||||
if(projectsDir.exists())
|
||||
@@ -2021,110 +2052,95 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
return child.isDirectory() && Items.getConfigFile(child).exists();
|
||||
}
|
||||
});
|
||||
items.clear();
|
||||
if(PARALLEL_LOAD) {
|
||||
// load jobs in parallel for better performance
|
||||
LOGGER.info("Loading in "+TWICE_CPU_NUM+" parallel threads");
|
||||
List<Future<TopLevelItem>> loaders = new ArrayList<Future<TopLevelItem>>();
|
||||
for (final File subdir : subdirs) {
|
||||
loaders.add(threadPoolForLoad.submit(new Callable<TopLevelItem>() {
|
||||
public TopLevelItem call() throws Exception {
|
||||
Thread t = Thread.currentThread();
|
||||
String name = t.getName();
|
||||
t.setName("Loading "+subdir);
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
TopLevelItem item = (TopLevelItem) Items.load(Hudson.this, subdir);
|
||||
if(LOG_STARTUP_PERFORMANCE)
|
||||
LOGGER.info("Loaded "+item.getName()+" in "+(System.currentTimeMillis()-start)+"ms by "+name);
|
||||
return item;
|
||||
} finally {
|
||||
t.setName(name);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (Future<TopLevelItem> loader : loaders) {
|
||||
try {
|
||||
TopLevelItem item = loader.get();
|
||||
items.put(item.getName(), item);
|
||||
} catch (ExecutionException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load a project",e.getCause());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace(); // this is probably not the right thing to do
|
||||
TaskGraphBuilder g = new TaskGraphBuilder();
|
||||
Handle loadHudson = g.requires(PLUGINS_STARTED).attains(JOB_LOADED).add("Loading global config", new Executable() {
|
||||
public void run(Reactor session) throws Exception {
|
||||
XmlFile cfg = getConfigFile();
|
||||
if (cfg.exists()) {
|
||||
// reset some data that may not exit in the disk file
|
||||
// so that we can take a proper compensation action later.
|
||||
primaryView = null;
|
||||
views.clear();
|
||||
|
||||
// load from disk
|
||||
cfg.unmarshal(Hudson.this);
|
||||
}
|
||||
|
||||
// if we are loading old data that doesn't have this field
|
||||
if (slaves == null) slaves = new NodeList();
|
||||
|
||||
clouds.setOwner(Hudson.this);
|
||||
items.clear();
|
||||
}
|
||||
} else {
|
||||
for (File subdir : subdirs) {
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
TopLevelItem item = (TopLevelItem)Items.load(this,subdir);
|
||||
if(LOG_STARTUP_PERFORMANCE)
|
||||
LOGGER.info("Loaded "+item.getName()+" in "+(System.currentTimeMillis()-start)+"ms");
|
||||
});
|
||||
|
||||
for (final File subdir : subdirs) {
|
||||
g.requires(loadHudson).attains(JOB_LOADED).notFatal().add("Loading job "+subdir.getName(),new Executable() {
|
||||
public void run(Reactor session) throws Exception {
|
||||
TopLevelItem item = (TopLevelItem) Items.load(Hudson.this, subdir);
|
||||
items.put(item.getName(), item);
|
||||
} catch (Error e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load "+subdir,e);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load "+subdir,e);
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load "+subdir,e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
g.requires(JOB_LOADED).add("Finalizing set up",new Executable() {
|
||||
public void run(Reactor session) throws Exception {
|
||||
rebuildDependencyGraph();
|
||||
|
||||
{// recompute label objects - populates the labels mapping.
|
||||
for (Node slave : slaves)
|
||||
// Note that not all labels are visible until the slaves have connected.
|
||||
slave.getAssignedLabels();
|
||||
getAssignedLabels();
|
||||
}
|
||||
|
||||
// initialize views by inserting the default view if necessary
|
||||
// this is both for clean Hudson and for backward compatibility.
|
||||
if(views.size()==0 || primaryView==null) {
|
||||
View v = new AllView(Messages.Hudson_ViewName());
|
||||
v.owner = Hudson.this;
|
||||
views.add(0,v);
|
||||
primaryView = v.getViewName();
|
||||
}
|
||||
|
||||
// read in old data that doesn't have the security field set
|
||||
if(authorizationStrategy==null) {
|
||||
if(useSecurity==null || !useSecurity)
|
||||
authorizationStrategy = AuthorizationStrategy.UNSECURED;
|
||||
else
|
||||
authorizationStrategy = new LegacyAuthorizationStrategy();
|
||||
}
|
||||
if(securityRealm==null) {
|
||||
if(useSecurity==null || !useSecurity)
|
||||
setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
|
||||
else
|
||||
setSecurityRealm(new LegacySecurityRealm());
|
||||
} else {
|
||||
// force the set to proxy
|
||||
setSecurityRealm(securityRealm);
|
||||
}
|
||||
|
||||
if(useSecurity!=null && !useSecurity) {
|
||||
// forced reset to the unsecure mode.
|
||||
// this works as an escape hatch for people who locked themselves out.
|
||||
authorizationStrategy = AuthorizationStrategy.UNSECURED;
|
||||
setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
|
||||
}
|
||||
|
||||
// Initialize the filter with the crumb issuer
|
||||
setCrumbIssuer(crumbIssuer);
|
||||
|
||||
// auto register root actions
|
||||
for (Action a : getExtensionList(RootAction.class))
|
||||
if (!actions.contains(a)) actions.add(a);
|
||||
|
||||
if(KILL_AFTER_LOAD)
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
rebuildDependencyGraph();
|
||||
});
|
||||
|
||||
{// recompute label objects - populates the labels mapping.
|
||||
for (Node slave : slaves)
|
||||
// Note that all labels are not visible until the slaves have
|
||||
// connected.
|
||||
slave.getAssignedLabels();
|
||||
getAssignedLabels();
|
||||
}
|
||||
|
||||
// initialize views by inserting the default view if necessary
|
||||
// this is both for clean Hudson and for backward compatibility.
|
||||
if(views.size()==0 || primaryView==null) {
|
||||
View v = new AllView(Messages.Hudson_ViewName());
|
||||
v.owner = this;
|
||||
views.add(0,v);
|
||||
primaryView = v.getViewName();
|
||||
}
|
||||
|
||||
// read in old data that doesn't have the security field set
|
||||
if(authorizationStrategy==null) {
|
||||
if(useSecurity==null || !useSecurity)
|
||||
authorizationStrategy = AuthorizationStrategy.UNSECURED;
|
||||
else
|
||||
authorizationStrategy = new LegacyAuthorizationStrategy();
|
||||
}
|
||||
if(securityRealm==null) {
|
||||
if(useSecurity==null || !useSecurity)
|
||||
setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
|
||||
else
|
||||
setSecurityRealm(new LegacySecurityRealm());
|
||||
} else {
|
||||
// force the set to proxy
|
||||
setSecurityRealm(securityRealm);
|
||||
}
|
||||
|
||||
if(useSecurity!=null && !useSecurity) {
|
||||
// forced reset to the unsecure mode.
|
||||
// this works as an escape hatch for people who locked themselves out.
|
||||
authorizationStrategy = AuthorizationStrategy.UNSECURED;
|
||||
setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
|
||||
}
|
||||
|
||||
// Initialize the filter with the crumb issuer
|
||||
setCrumbIssuer(crumbIssuer);
|
||||
|
||||
// auto register root actions
|
||||
for (Action a : getExtensionList(RootAction.class))
|
||||
if (!actions.contains(a)) actions.add(a);
|
||||
|
||||
LOGGER.info(String.format("Took %s ms to load",System.currentTimeMillis()-startTime));
|
||||
if(KILL_AFTER_LOAD)
|
||||
System.exit(0);
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2396,14 +2412,14 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
}
|
||||
|
||||
@CLIMethod(name="quiet-down")
|
||||
public synchronized HttpRedirect doQuietDown() throws IOException, ServletException {
|
||||
public synchronized HttpRedirect doQuietDown() {
|
||||
checkPermission(ADMINISTER);
|
||||
isQuietingDown = true;
|
||||
return new HttpRedirect(".");
|
||||
}
|
||||
|
||||
@CLIMethod(name="cancel-quiet-down")
|
||||
public synchronized HttpRedirect doCancelQuietDown() throws IOException, ServletException {
|
||||
public synchronized HttpRedirect doCancelQuietDown() {
|
||||
checkPermission(ADMINISTER);
|
||||
isQuietingDown = false;
|
||||
getQueue().scheduleMaintenance();
|
||||
@@ -2703,11 +2719,13 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
load();
|
||||
User.reload();
|
||||
servletContext.setAttribute("app",Hudson.this);
|
||||
reload();
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(SEVERE,"Failed to reload Hudson config",e);
|
||||
} catch (ReactorException e) {
|
||||
LOGGER.log(SEVERE,"Failed to reload Hudson config",e);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(SEVERE,"Failed to reload Hudson config",e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
@@ -2715,6 +2733,15 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
return HttpResponses.redirectViaContextPath("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the configuration synchronously.
|
||||
*/
|
||||
public void reload() throws IOException, InterruptedException, ReactorException {
|
||||
executeReactor(null,loadTasks());
|
||||
User.reload();
|
||||
servletContext.setAttribute("app", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a finger-print check.
|
||||
*/
|
||||
@@ -3546,6 +3573,11 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|
||||
*/
|
||||
public static boolean CONCURRENT_BUILD = true;
|
||||
|
||||
/**
|
||||
* Automatically try to launch a slave when Hudson is initialized or a new slave is created.
|
||||
*/
|
||||
public static boolean AUTOMATIC_SLAVE_LAUNCH = true;
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Hudson.class.getName());
|
||||
|
||||
private static final Pattern ICON_SIZE = Pattern.compile("\\d+x\\d+");
|
||||
|
||||
@@ -29,6 +29,8 @@ import hudson.ExtensionPoint;
|
||||
import hudson.Util;
|
||||
import hudson.XmlFile;
|
||||
import hudson.matrix.MatrixConfiguration;
|
||||
import hudson.init.Initializer;
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import hudson.cli.declarative.CLIMethod;
|
||||
import hudson.cli.declarative.CLIResolver;
|
||||
import hudson.remoting.AsyncFutureImpl;
|
||||
@@ -46,6 +48,7 @@ import hudson.util.OneShotEvent;
|
||||
import hudson.util.TimeUnit2;
|
||||
import hudson.util.XStream2;
|
||||
import hudson.util.ConsistentHash;
|
||||
import hudson.util.DoubleLaunchChecker;
|
||||
import hudson.util.ConsistentHash.Hash;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -1573,4 +1576,12 @@ public class Queue extends ResourceController implements Saveable {
|
||||
public static Queue getInstance() {
|
||||
return Hudson.getInstance().getQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the queue content during the start up.
|
||||
*/
|
||||
@Initializer(after=JOB_LOADED)
|
||||
public static void init(Hudson h) {
|
||||
h.getQueue().load();
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,11 @@ import hudson.PluginWrapper;
|
||||
import hudson.ProxyConfiguration;
|
||||
import hudson.Util;
|
||||
import hudson.XmlFile;
|
||||
import hudson.triggers.SafeTimerTask;
|
||||
import hudson.init.Initializer;
|
||||
import hudson.init.InitMilestone;
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import static hudson.init.InitMilestone.PLUGINS_STARTED;
|
||||
import hudson.lifecycle.Lifecycle;
|
||||
import hudson.model.UpdateSite.Data;
|
||||
import hudson.model.UpdateSite.Plugin;
|
||||
@@ -40,6 +45,7 @@ import hudson.util.DaemonThreadFactory;
|
||||
import hudson.util.IOException2;
|
||||
import hudson.util.PersistedList;
|
||||
import hudson.util.XStream2;
|
||||
import hudson.util.DoubleLaunchChecker;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
@@ -839,6 +845,16 @@ public class UpdateCenter extends AbstractModelObject implements Saveable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the update center.
|
||||
*
|
||||
* This has to wait until after all plugins load, to let custom UpdateCenterConfiguration take effect first.
|
||||
*/
|
||||
@Initializer(after=PLUGINS_STARTED)
|
||||
public static void init(Hudson h) throws IOException {
|
||||
h.getUpdateCenter().load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequence number generator.
|
||||
*/
|
||||
@@ -849,6 +865,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable {
|
||||
public static boolean neverUpdate = Boolean.getBoolean(UpdateCenter.class.getName()+".never");
|
||||
|
||||
public static final XStream2 XSTREAM = new XStream2();
|
||||
|
||||
static {
|
||||
XSTREAM.alias("site",UpdateSite.class);
|
||||
XSTREAM.alias("sites",PersistedList.class);
|
||||
|
||||
@@ -29,6 +29,9 @@ import hudson.DependencyRunner.ProjectRunnable;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.DescriptorExtensionList;
|
||||
import hudson.Extension;
|
||||
import hudson.init.Initializer;
|
||||
import hudson.init.InitMilestone;
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Action;
|
||||
import hudson.model.Build;
|
||||
@@ -250,6 +253,7 @@ public abstract class Trigger<J extends Item> implements Describable<Trigger<?>>
|
||||
*/
|
||||
public static Timer timer;
|
||||
|
||||
@Initializer(after=JOB_LOADED)
|
||||
public static void init() {
|
||||
new DoubleLaunchChecker().schedule();
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package hudson.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Poorman's clone of JDK6 ServiceLoader.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class ServiceLoader {
|
||||
public static <T> List<T> load(ClassLoader classLoader, Class<T> type) throws IOException {
|
||||
List<T> result = new ArrayList<T>();
|
||||
|
||||
final Enumeration<URL> e = classLoader.getResources("META-INF/services/"+type.getName());
|
||||
while (e.hasMoreElements()) {
|
||||
URL url = e.nextElement();
|
||||
BufferedReader configFile = new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8"));
|
||||
String line;
|
||||
while ((line = configFile.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.startsWith("#") || line.length()==0) continue;
|
||||
|
||||
try {
|
||||
Class<?> t = classLoader.loadClass(line);
|
||||
if (!type.isAssignableFrom(t)) continue; // invalid type
|
||||
|
||||
result.add(type.cast(t.newInstance()));
|
||||
} catch (ClassNotFoundException x) {
|
||||
LOGGER.log(WARNING,"Failed to load "+line,x);
|
||||
} catch (InstantiationException x) {
|
||||
LOGGER.log(WARNING,"Failed to load "+line,x);
|
||||
} catch (IllegalAccessException x) {
|
||||
LOGGER.log(WARNING,"Failed to load "+line,x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ServiceLoader.class.getName());
|
||||
}
|
||||
Reference in new issue
Block a user