提交 9a178aa8 编写于 作者: K Kohsuke Kawaguchi

Added a mechanism to filter extension points as they are discovered.

I couldn't quite find one place to do this, so it's spread apart
somewhat.
上级 4e7a43c5
......@@ -55,6 +55,8 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=rfe>
Added a mechanism to filter extension points as they are discovered.
<li class=rfe>
Exposed the master's own node properties to be configured in <tt>/computer/(master)/configure</tt>
(whereas <tt>/configure</tt> controls global node properties that apply to all nodes.)
......
......@@ -23,6 +23,8 @@
*/
package hudson;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import hudson.Plugin.DummyImpl;
import hudson.PluginWrapper.Dependency;
import hudson.model.Hudson;
......@@ -33,6 +35,7 @@ import hudson.util.IOUtils;
import hudson.util.MaskingClassLoader;
import hudson.util.VersionNumber;
import jenkins.ClassLoaderReflectionToolkit;
import jenkins.ExtensionFilter;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
......@@ -54,6 +57,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.jar.Attributes;
......@@ -286,7 +290,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
finder.scout(type, hudson);
}
List<ExtensionComponent<T>> r = new ArrayList<ExtensionComponent<T>>();
List<ExtensionComponent<T>> r = Lists.newArrayList();
for (ExtensionFinder finder : finders) {
try {
r.addAll(finder._find(type, hudson));
......@@ -296,7 +300,14 @@ public class ClassicPluginStrategy implements PluginStrategy {
r.add(new ExtensionComponent<T>(t));
}
}
return r;
List<ExtensionComponent<T>> filtered = Lists.newArrayList();
for (ExtensionComponent<T> e : r) {
if (ExtensionFilter.isAllowed(type,e))
filtered.add(e);
}
return filtered;
}
public void load(PluginWrapper wrapper) throws IOException {
......
......@@ -24,7 +24,9 @@
package hudson;
import hudson.model.Describable;
import hudson.model.Descriptor;
import jenkins.ExtensionFilter;
/**
* Discovered {@link Extension} object with a bit of metadata for Hudson.
......@@ -32,6 +34,8 @@ import hudson.model.Descriptor;
*
* @author Kohsuke Kawaguchi
* @since 1.356
* @see ExtensionFinder
* @see ExtensionFilter
*/
public class ExtensionComponent<T> implements Comparable<ExtensionComponent<T>> {
private final T instance;
......@@ -66,6 +70,15 @@ public class ExtensionComponent<T> implements Comparable<ExtensionComponent<T>>
return instance;
}
/**
* Checks if this component is a {@link Descriptor} describing the given type
*
* For example, {@code component.isDescriptorOf(Builder.class)}
*/
public boolean isDescriptorOf(Class<? extends Describable> c) {
return instance instanceof Descriptor && ((Descriptor)instance).isSubTypeOf(c);
}
/**
* Sort {@link ExtensionComponent}s in the descending order of {@link #ordinal()}.
*/
......
......@@ -41,6 +41,7 @@ import hudson.init.InitMilestone;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import jenkins.ExtensionComponentSet;
import jenkins.ExtensionFilter;
import jenkins.ExtensionRefreshException;
import jenkins.ProxyInjector;
import jenkins.model.Jenkins;
......@@ -72,10 +73,11 @@ import java.lang.reflect.Method;
*
* <p>
* {@link ExtensionFinder} itself is an extension point, but to avoid infinite recursion,
* Hudson discovers {@link ExtensionFinder}s through {@link Sezpoz} and that alone.
* Jenkins discovers {@link ExtensionFinder}s through {@link Sezpoz} and that alone.
*
* @author Kohsuke Kawaguchi
* @since 1.286
* @see ExtensionFilter
*/
public abstract class ExtensionFinder implements ExtensionPoint {
/**
......@@ -126,7 +128,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* <p>
* This method should return all the known components at the time of the call, including
* those that are discovered later via {@link #refresh()}, even though those components
* are separately retruend in {@link ExtensionComponentSet}.
* are separately returned in {@link ExtensionComponentSet}.
*
* @param <T>
* The type of the extension points. This is not bound to {@link ExtensionPoint} because
......
......@@ -3,6 +3,7 @@ package hudson.model;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.scm.SCMDescriptor;
import jenkins.ExtensionFilter;
import jenkins.model.Jenkins;
import java.util.ArrayList;
......@@ -13,6 +14,7 @@ import java.util.List;
*
* @author Kohsuke Kawaguchi
* @since 1.393
* @see ExtensionFilter
*/
public abstract class DescriptorVisibilityFilter implements ExtensionPoint {
......
......@@ -59,6 +59,24 @@ public abstract class ExtensionComponentSet {
*/
public abstract <T> Collection<ExtensionComponent<T>> find(Class<T> type);
/**
* Apply {@link ExtensionFilter}s and returns a filtered set.
*/
public final ExtensionComponentSet filtered() {
final ExtensionComponentSet base = this;
return new ExtensionComponentSet() {
@Override
public <T> Collection<ExtensionComponent<T>> find(Class<T> type) {
List<ExtensionComponent<T>> a = Lists.newArrayList();
for (ExtensionComponent<T> c : base.find(type)) {
if (ExtensionFilter.isAllowed(type,c))
a.add(c);
}
return a;
}
};
}
/**
* Constant that has zero component in it.
*/
......
/*
* The MIT License
*
* Copyright (c) 2012, CloudBees, 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 jenkins;
import hudson.ExtensionComponent;
import hudson.ExtensionFinder;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.AdministrativeMonitor;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.DescriptorVisibilityFilter;
import jenkins.model.Jenkins;
/**
* Filters out {@link ExtensionComponent}s discovered by {@link ExtensionFinder}s,
* as if they were never discovered.
*
* <p>
* This is useful for those who are deploying restricted/simplified version of Jenkins
* by reducing the functionality.
*
* <p>
* Because of the way {@link ExtensionFinder} works, even when an extension component
* is rejected by a filter, its instance still gets created first.
*
* @author Kohsuke Kawaguchi
* @since 1.472
* @see DescriptorVisibilityFilter
* @see ExtensionComponentSet#filtered()
*/
public abstract class ExtensionFilter implements ExtensionPoint {
/**
* Checks if a newly discovered extension is allowed to participate into Jenkins.
*
* <p>
* To filter {@link Descriptor}s based on the {@link Describable} subtypes, do as follows:
*
* <pre>
* return !component.isDescriptorOf(Builder.class);
* </pre>
*
* @param type
* The type of the extension that we are discovering. This is not the actual instance
* type, but the contract type, such as {@link Descriptor}, {@link AdministrativeMonitor}, etc.
* @return
* true to let the component into Jenkins. false to drop it and pretend
* as if it didn't exist. When any one of {@link ExtensionFilter}s veto
* a component, it gets dropped.
*/
public abstract <T> boolean allows(Class<T> type, ExtensionComponent<T> component);
public static <T> boolean isAllowed(Class<T> type, ExtensionComponent<T> component) {
// to avoid infinite recursion, those extension points are handled differently.
if (type==ExtensionFilter.class || type==ExtensionFinder.class)
return true;
for (ExtensionFilter f : all())
if (!f.allows(type, component))
return false;
return true;
}
/**
* All registered {@link ExtensionFilter} instances.
*/
public static ExtensionList<ExtensionFilter> all() {
return Jenkins.getInstance().getExtensionList(ExtensionFilter.class);
}
}
......@@ -2055,14 +2055,14 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
for (ExtensionFinder ef : finders) {
fragments.add(ef.refresh());
}
ExtensionComponentSet delta = ExtensionComponentSet.union(fragments);
ExtensionComponentSet delta = ExtensionComponentSet.union(fragments).filtered();
// if we find a new ExtensionFinder, we need it to list up all the extension points as well
List<ExtensionComponent<ExtensionFinder>> newFinders = Lists.newArrayList(delta.find(ExtensionFinder.class));
while (!newFinders.isEmpty()) {
ExtensionFinder f = newFinders.remove(newFinders.size()-1).getInstance();
ExtensionComponentSet ecs = ExtensionComponentSet.allOf(f);
ExtensionComponentSet ecs = ExtensionComponentSet.allOf(f).filtered();
newFinders.addAll(ecs.find(ExtensionFinder.class));
delta = ExtensionComponentSet.union(delta, ecs);
}
......
package jenkins;
import hudson.ExtensionComponent;
import hudson.console.ConsoleAnnotatorFactory;
import hudson.model.PageDecorator;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.TestExtension;
/**
* @author Kohsuke Kawaguchi
*/
public class ExtensionFilterTest extends HudsonTestCase {
public void testFilter() {
assertTrue(PageDecorator.all().isEmpty());
assertTrue(ConsoleAnnotatorFactory.all().isEmpty());
}
@TestExtension("testFilter")
public static class Impl extends ExtensionFilter {
@Override
public <T> boolean allows(Class<T> type, ExtensionComponent<T> component) {
if (type==ConsoleAnnotatorFactory.class)
return false;
if (component.isDescriptorOf(PageDecorator.class))
return false;
return true;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册