[JENKINS-34681] Allow custom plugin managers when using war (#2323)

* [JENKINS-34681] Allow custom default plugin managers

* [JENKINS-34681] Convert remaining properties to SystemProperties.
This commit is contained in:
Andres Rodriguez authored and Oleg Nenashev committed 2016-05-14 21:32:55 +02:00
1 parent dbe04b3522
commit 454642dafe
4 files changed
+259 -12

No files matched your search

+96 -6
View File
@@ -23,6 +23,7 @@
*/
package hudson;
import edu.umd.cs.findbugs.annotations.NonNull;
import jenkins.util.SystemProperties;
import hudson.PluginWrapper.Dependency;
import hudson.init.InitMilestone;
@@ -136,6 +137,7 @@ import static hudson.init.InitMilestone.*;
import hudson.model.DownloadService;
import hudson.util.FormValidation;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
@@ -145,10 +147,101 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Manages {@link PluginWrapper}s.
*
* <p>
* <b>Setting default Plugin Managers</b>. The default plugin manager in {@code Jenkins} can be replaced by defining a
* System Property (<code>hudson.PluginManager.className</code>). See {@link #createDefault(Jenkins)}.
* This className should be available on early startup, so it cannot come only from a library
* (e.g. Jenkins module or Extra library dependency in the WAR file project).
* Plugins cannot be used for such purpose.
* In order to be correctly instantiated, the class definition must have at least one constructor with the same
* signature as the following ones:
* <ol>
* <li>{@link LocalPluginManager#LocalPluginManager(Jenkins)} </li>
* <li>{@link LocalPluginManager#LocalPluginManager(ServletContext, File)} </li>
* <li>{@link LocalPluginManager#LocalPluginManager(File)} </li>
* </ol>
* Constructors are searched in the order provided above and only the first found suitable constructor is
* tried to build an instance. In the last two cases the {@link File} argument refers to the <i>Jenkins home directory</i>.
*
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public abstract class PluginManager extends AbstractModelObject implements OnMaster, StaplerOverridable {
/** Custom plugin manager system property or context param. */
public static final String CUSTOM_PLUGIN_MANAGER = PluginManager.class.getName() + ".className";
/** Accepted constructors for custom plugin manager, in the order they are tried. */
private enum PMConstructor {
JENKINS {
@Override
@NonNull PluginManager doCreate(@NonNull Class<? extends PluginManager> klass,
@NonNull Jenkins jenkins) throws ReflectiveOperationException {
return klass.getConstructor(Jenkins.class).newInstance(jenkins);
}
},
SC_FILE {
@Override
@NonNull PluginManager doCreate(@NonNull Class<? extends PluginManager> klass,
@NonNull Jenkins jenkins) throws ReflectiveOperationException {
return klass.getConstructor(ServletContext.class, File.class).newInstance(jenkins.servletContext, jenkins.getRootDir());
}
},
FILE {
@Override
@NonNull PluginManager doCreate(@NonNull Class<? extends PluginManager> klass,
@NonNull Jenkins jenkins) throws ReflectiveOperationException {
return klass.getConstructor(File.class).newInstance(jenkins.getRootDir());
}
};
final @CheckForNull PluginManager create(@NonNull Class<? extends PluginManager> klass,
@NonNull Jenkins jenkins) throws ReflectiveOperationException {
try {
return doCreate(klass, jenkins);
} catch(NoSuchMethodException e) {
// Constructor not found. Will try the remaining ones.
return null;
}
}
abstract @NonNull PluginManager doCreate(@NonNull Class<? extends PluginManager> klass,
@NonNull Jenkins jenkins) throws ReflectiveOperationException;
}
/**
* Creates the {@link PluginManager} to use if no one is provided to a {@link Jenkins} object.
* This method will be called after creation of {@link Jenkins} object, but before it is fully initialized.
* @param jenkins Jenkins Instance.
* @return Plugin manager to use. If no custom class is configured or in case of any error, the default
* {@link LocalPluginManager} is returned.
*/
public static @NonNull PluginManager createDefault(@NonNull Jenkins jenkins) {
String pmClassName = SystemProperties.getString(CUSTOM_PLUGIN_MANAGER);
if (!StringUtils.isBlank(pmClassName)) {
LOGGER.log(FINE, String.format("Use of custom plugin manager [%s] requested.", pmClassName));
try {
final Class<? extends PluginManager> klass = Class.forName(pmClassName).asSubclass(PluginManager.class);
// Iteration is in declaration order
for (PMConstructor c : PMConstructor.values()) {
PluginManager pm = c.create(klass, jenkins);
if (pm != null) {
return pm;
}
}
LOGGER.log(WARNING, String.format("Provided custom plugin manager [%s] does not provide any of the suitable constructors. Using default.", pmClassName));
} catch(NullPointerException e) {
// Class.forName and Class.getConstructor are supposed to never return null though a broken ClassLoader
// could break the contract. Just in case we introduce this specific catch to avoid polluting the logs with NPEs.
LOGGER.log(WARNING, String.format("Unable to instantiate custom plugin manager [%s]. Using default.", pmClassName));
} catch(ClassCastException e) {
LOGGER.log(WARNING, String.format("Provided class [%s] does not extend PluginManager. Using default.", pmClassName));
} catch(Exception e) {
LOGGER.log(WARNING, String.format("Unable to instantiate custom plugin manager [%s]. Using default.", pmClassName), e);
}
}
return new LocalPluginManager(jenkins);
}
/**
* All discovered plugins.
*/
@@ -218,11 +311,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
this.rootDir = rootDir;
if(!rootDir.exists())
rootDir.mkdirs();
String workDir = System.getProperty(PluginManager.class.getName()+".workDir");
if (workDir == null && context != null) {
workDir = context.getInitParameter(PluginManager.class.getName() + ".workDir");
}
this.workDir = workDir == null ? null : new File(workDir);
String workDir = SystemProperties.getString(PluginManager.class.getName()+".workDir");
this.workDir = StringUtils.isBlank(workDir) ? null : new File(workDir);
strategy = createPluginStrategy();
@@ -561,7 +651,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
if (dependencyURL != null) {
// And transitive deps...
addDependencies(dependencyURL, fromPath, dependencySet);
// And then add the current plugin
// And then add the current plugin
dependencySet.add(dependencyURL);
}
}