提交 fd13eee6 编写于 作者: K kohsuke

Merged revisions...

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
上级 ef7768a0
......@@ -362,6 +362,11 @@ THE SOFTWARE.
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>annotation-indexer</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>task-reactor</artifactId>
<version>1.1</version>
</dependency>
<dependency>
......
......@@ -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);
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();
}
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();
}
}
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
......
/*
* 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;
}
}
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 {
}
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());
}
/*
* 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;
}
/*
* 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;
}
}
}
/*
* 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());
}
/*
* 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());
}
}
}
/*
* 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;
\ No newline at end of file
......@@ -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();
}
}
......@@ -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();
......
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());
}
GroovyInitScript.init=Executing user-defined init script
InitialUserContent.init=Preparing initial user content
\ No newline at end of file
LogRecorderManager.init=Initialing log recorders
\ No newline at end of file
......@@ -147,6 +147,7 @@ Queue.NodeOffline={0} is offline
Queue.Unknown=???
Queue.WaitingForNextAvailableExecutor=Waiting for next available executor
Queue.WaitingForNextAvailableExecutorOn=Waiting for next available executor on {0}
Queue.init=Restoring the build queue
Run.BuildAborted=Build was aborted
Run.MarkedExplicitly=explicitly marked to keep the record
......@@ -198,6 +199,7 @@ UpdateCenter.Status.UnknownHostException=\
UpdateCenter.Status.ConnectionFailed=\
<span class=error>Failed to connect to {0}. \
Perhaps you need to <a href="../pluginManager/advanced">configure HTTP proxy?</a></span>
UpdateCenter.init=Initialing update center
Permalink.LastBuild=Last build
Permalink.LastStableBuild=Last stable build
......
......@@ -24,4 +24,5 @@ SCMTrigger.DisplayName=Poll SCM
SCMTrigger.getDisplayName={0} Polling Log
SCMTrigger.SCMTriggerCause.ShortDescription=Started by an SCM change
TimerTrigger.DisplayName=Build periodically
TimerTrigger.TimerTriggerCause.ShortDescription=Started by timer
\ No newline at end of file
TimerTrigger.TimerTriggerCause.ShortDescription=Started by timer
Trigger.init=Initializing timer for triggers
\ No newline at end of file
......@@ -58,7 +58,7 @@ THE SOFTWARE.
<configuration>
<fork>true</fork>
<concurrency>-1</concurrency> <!-- -1 means # of processors in the system -->
<argLine>-XX:MaxPermSize=128m -Dfile.encoding=UTF-8</argLine>
<argLine>-XX:MaxPermSize=192m -Dfile.encoding=UTF-8</argLine>
<systemProperties>
<property>
<!-- use AntClassLoader that supports predictable file handle release -->
......
......@@ -94,12 +94,8 @@ public class JUnitResultArchiverTest extends HudsonTestCase {
assertTestResults(build);
}
private void reloadHudson() throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
Method m = Hudson.class.getDeclaredMethod("load");
m.setAccessible(true);
m.invoke(hudson);
private void reloadHudson() throws Exception {
hudson.reload();
project = (FreeStyleProject) hudson.getItem("junit");
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册