mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-07 09:06:03 +00:00
[JENKINS-55681] - Make JAXB a detached plugin only on Java 11 (#3865)
* Move detached plugins logic into a dedicated class ClassicPluginStrategy is already long enough, so trying to make things a bit more focused before adding Java 11 logic. (and even if not, the change still splits a class that is almost 1k lines). * Install jaxb as detached only on Java 11+ * Remove duplicate @Restricted Given the enclosing class already has it. * Use 1.5 release * Add more test coverage * getMinJavaVersion => getMinimumJavaVersion * Rename DetachedPluginsManager as DetachedPluginsUtil as requested by Oleg and Jesse. Mainly because of the misleading relationship (there's no inheritance) with the existing `PluginManager` class. * 🤦 adjust to 1.5 release method renaming * Clarify minimum Java version column Co-Authored-By: batmat <bmathus@gmail.com> * Update to 2.3.0 version of the jaxb plugin * Bump minimum core version to next expected release version * Add preventive cycle breaks for jaxb while waiting for JENKINS-28942 As requested by Oleg. * Add test to fail fast on wrong format for split-plugins.txt
This commit is contained in:
1 parent
8ab5ab57e2
commit
a78312ea0f
11 files changed
+369
-202
No files matched your search
@@ -23,29 +23,21 @@
|
||||
*/
|
||||
package hudson;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import jenkins.util.AntWithFindResourceClassLoader;
|
||||
import jenkins.util.SystemProperties;
|
||||
import com.google.common.collect.Lists;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Plugin.DummyImpl;
|
||||
import hudson.PluginWrapper.Dependency;
|
||||
import hudson.model.Hudson;
|
||||
import jenkins.util.AntClassLoader;
|
||||
import hudson.util.CyclicGraphDetector;
|
||||
import hudson.util.CyclicGraphDetector.CycleDetectedException;
|
||||
import hudson.util.IOUtils;
|
||||
import hudson.util.MaskingClassLoader;
|
||||
import hudson.util.VersionNumber;
|
||||
import jenkins.ClassLoaderReflectionToolkit;
|
||||
import jenkins.ExtensionFilter;
|
||||
import jenkins.plugins.DetachedPluginsUtil;
|
||||
import jenkins.util.AntClassLoader;
|
||||
import jenkins.util.AntWithFindResourceClassLoader;
|
||||
import jenkins.util.SystemProperties;
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
@@ -60,37 +52,36 @@ import org.apache.tools.ant.util.GlobPatternMapper;
|
||||
import org.apache.tools.zip.ZipEntry;
|
||||
import org.apache.tools.zip.ZipExtraField;
|
||||
import org.apache.tools.zip.ZipOutputStream;
|
||||
import org.jenkinsci.bytecode.Transformer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.jenkinsci.bytecode.Transformer;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static org.apache.commons.io.FilenameUtils.getBaseName;
|
||||
|
||||
public class ClassicPluginStrategy implements PluginStrategy {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ClassicPluginStrategy.class.getName());
|
||||
|
||||
/**
|
||||
* Filter for jar files.
|
||||
*/
|
||||
@@ -266,32 +257,16 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
if (jenkinsVersion==null)
|
||||
jenkinsVersion = atts.getValue("Hudson-Version");
|
||||
|
||||
optionalDependencies.addAll(getImpliedDependencies(pluginName, jenkinsVersion));
|
||||
optionalDependencies.addAll(DetachedPluginsUtil.getImpliedDependencies(pluginName, jenkinsVersion));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all the plugin dependencies that are implicit based on a particular Jenkins version
|
||||
* @since 2.0
|
||||
* @see DetachedPluginsUtil#getImpliedDependencies(String, String)
|
||||
*/
|
||||
@Deprecated // since TODO
|
||||
@Nonnull
|
||||
public static List<PluginWrapper.Dependency> getImpliedDependencies(String pluginName, String jenkinsVersion) {
|
||||
List<PluginWrapper.Dependency> out = new ArrayList<>();
|
||||
for (DetachedPlugin detached : DETACHED_LIST) {
|
||||
// don't fix the dependency for itself, or else we'll have a cycle
|
||||
if (detached.shortName.equals(pluginName)) {
|
||||
continue;
|
||||
}
|
||||
if (BREAK_CYCLES.contains(pluginName + ' ' + detached.shortName)) {
|
||||
LOGGER.log(Level.FINE, "skipping implicit dependency {0} → {1}", new Object[] {pluginName, detached.shortName});
|
||||
continue;
|
||||
}
|
||||
// some earlier versions of maven-hpi-plugin apparently puts "null" as a literal in Hudson-Version. watch out for them.
|
||||
if (jenkinsVersion == null || jenkinsVersion.equals("null") || new VersionNumber(jenkinsVersion).compareTo(detached.splitWhen) <= 0) {
|
||||
out.add(new PluginWrapper.Dependency(detached.shortName + ':' + detached.requiredVersion));
|
||||
LOGGER.log(Level.FINE, "adding implicit dependency {0} → {1} because of {2}", new Object[] {pluginName, detached.shortName, jenkinsVersion});
|
||||
}
|
||||
}
|
||||
return out;
|
||||
return DetachedPluginsUtil.getImpliedDependencies(pluginName, jenkinsVersion);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -319,140 +294,6 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all plugins that have ever been {@link DetachedPlugin detached} from Jenkins core.
|
||||
* @return A {@link List} of {@link DetachedPlugin}s.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static @Nonnull List<DetachedPlugin> getDetachedPlugins() {
|
||||
return DETACHED_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of plugins that have been detached since a specific Jenkins release version.
|
||||
* @param since The Jenkins version.
|
||||
* @return A {@link List} of {@link DetachedPlugin}s.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static @Nonnull List<DetachedPlugin> getDetachedPlugins(@Nonnull VersionNumber since) {
|
||||
List<DetachedPlugin> detachedPlugins = new ArrayList<>();
|
||||
|
||||
for (DetachedPlugin detachedPlugin : DETACHED_LIST) {
|
||||
if (!detachedPlugin.getSplitWhen().isOlderThan(since)) {
|
||||
detachedPlugins.add(detachedPlugin);
|
||||
}
|
||||
}
|
||||
|
||||
return detachedPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the named plugin a plugin that was detached from Jenkins at some point in the past.
|
||||
* @param pluginId The plugin ID.
|
||||
* @return {@code true} if the plugin is a plugin that was detached from Jenkins at some
|
||||
* point in the past, otherwise {@code false}.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static boolean isDetachedPlugin(@Nonnull String pluginId) {
|
||||
for (DetachedPlugin detachedPlugin : DETACHED_LIST) {
|
||||
if (detachedPlugin.getShortName().equals(pluginId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about plugins that were originally in the core.
|
||||
* <p>
|
||||
* A detached plugin is one that has any of the following characteristics:
|
||||
* <ul>
|
||||
* <li>
|
||||
* Was an existing plugin that at some time previously bundled with the Jenkins war file.
|
||||
* </li>
|
||||
* <li>
|
||||
* Was previous code in jenkins core that was split to a separate-plugin (but may not have
|
||||
* ever been bundled in a jenkins war file - i.e. it gets split after this 2.0 update).
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static final class DetachedPlugin {
|
||||
private final String shortName;
|
||||
/**
|
||||
* Plugins built for this Jenkins version (and earlier) will automatically be assumed to have
|
||||
* this plugin in its dependency.
|
||||
*
|
||||
* When core/pom.xml version is 1.123-SNAPSHOT when the code is removed, then this value should
|
||||
* be "1.123.*" (because 1.124 will be the first version that doesn't include the removed code.)
|
||||
*/
|
||||
private final VersionNumber splitWhen;
|
||||
private final String requiredVersion;
|
||||
|
||||
private DetachedPlugin(String shortName, String splitWhen, String requiredVersion) {
|
||||
this.shortName = shortName;
|
||||
this.splitWhen = new VersionNumber(splitWhen);
|
||||
this.requiredVersion = requiredVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the short name of the plugin.
|
||||
* @return The short name of the plugin.
|
||||
*/
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Jenkins version from which the plugin was detached.
|
||||
* @return The Jenkins version from which the plugin was detached.
|
||||
*/
|
||||
public VersionNumber getSplitWhen() {
|
||||
return splitWhen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum required version for the current version of Jenkins.
|
||||
*
|
||||
* @return the minimum required version for the current version of Jenkins.
|
||||
* @since 2.16
|
||||
*/
|
||||
public VersionNumber getRequiredVersion() {
|
||||
return new VersionNumber(requiredVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return shortName + " " + splitWhen.toString().replace(".*", "") + " " + requiredVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/** Record of which plugins which removed from core and when. */
|
||||
private static final List<DetachedPlugin> DETACHED_LIST;
|
||||
|
||||
/** Implicit dependencies that are known to be unnecessary and which must be cut out to prevent a dependency cycle among bundled plugins. */
|
||||
private static final Set<String> BREAK_CYCLES;
|
||||
|
||||
static {
|
||||
try (InputStream is = ClassicPluginStrategy.class.getResourceAsStream("/jenkins/split-plugins.txt")) {
|
||||
DETACHED_LIST = ImmutableList.copyOf(configLines(is).map(line -> {
|
||||
String[] pieces = line.split(" ");
|
||||
return new DetachedPlugin(pieces[0], pieces[1] + ".*", pieces[2]);
|
||||
}).collect(Collectors.toList()));
|
||||
} catch (IOException x) {
|
||||
throw new ExceptionInInitializerError(x);
|
||||
}
|
||||
try (InputStream is = ClassicPluginStrategy.class.getResourceAsStream("/jenkins/split-plugin-cycles.txt")) {
|
||||
BREAK_CYCLES = ImmutableSet.copyOf(configLines(is).collect(Collectors.toSet()));
|
||||
} catch (IOException x) {
|
||||
throw new ExceptionInInitializerError(x);
|
||||
}
|
||||
}
|
||||
private static Stream<String> configLines(InputStream is) throws IOException {
|
||||
return org.apache.commons.io.IOUtils.readLines(is, StandardCharsets.UTF_8).stream().filter(line -> !line.matches("#.*|\\s*"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the classloader that takes the class masking into account.
|
||||
*
|
||||
@@ -483,7 +324,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
* See {@link ExtensionFinder#scout(Class, Hudson)} for the dead lock issue and what this does.
|
||||
*/
|
||||
if (LOGGER.isLoggable(Level.FINER))
|
||||
LOGGER.log(Level.FINER,"Scout-loading ExtensionList: "+type, new Throwable());
|
||||
LOGGER.log(Level.FINER, "Scout-loading ExtensionList: "+type, new Throwable());
|
||||
for (ExtensionFinder finder : finders) {
|
||||
finder.scout(type, hudson);
|
||||
}
|
||||
@@ -864,6 +705,5 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
}
|
||||
|
||||
public static boolean useAntClassLoader = SystemProperties.getBoolean(ClassicPluginStrategy.class.getName()+".useAntClassLoader");
|
||||
private static final Logger LOGGER = Logger.getLogger(ClassicPluginStrategy.class.getName());
|
||||
public static boolean DISABLE_TRANSFORMER = SystemProperties.getBoolean(ClassicPluginStrategy.class.getName()+".noBytecodeTransformer");
|
||||
}
|
||||
Reference in new issue
Block a user