/*******************************************************************************
*
* Copyright (c) 2004-2009, Oracle Corporation
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
*
*
*
*******************************************************************************/
package hudson;
import hudson.model.Descriptor;
import hudson.model.Describable;
import hudson.model.Hudson;
import hudson.model.ViewDescriptor;
import hudson.model.Descriptor.FormException;
import hudson.util.AdaptedIterator;
import hudson.util.Memoizer;
import hudson.util.Iterators.FlattenIterator;
import hudson.slaves.NodeDescriptor;
import hudson.tasks.Publisher;
import hudson.tasks.Publisher.DescriptorExtensionListImpl;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Logger;
import java.util.concurrent.CopyOnWriteArrayList;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import org.jvnet.tiger_types.Types;
import org.kohsuke.stapler.Stapler;
import net.sf.json.JSONObject;
/**
* {@link ExtensionList} for holding a set of {@link Descriptor}s, which is a
* group of descriptors for the same extension point.
*
* Use {@link Hudson#getDescriptorList(Class)} to obtain instances.
*
* @param <D> Represents the descriptor type. This is {@code Descriptor<T>}
* normally but often there are subtypes of descriptors, like
* {@link ViewDescriptor}, {@link NodeDescriptor}, etc, and this parameter
* points to those for better type safety of users.
*
* The actual value of 'D' is not necessary for the operation of this code, so
* it's purely for convenience of the users of this class.
*
* @since 1.286
*/
public class DescriptorExtensionList<T extends Describable<T>, D extends Descriptor<T>> extends ExtensionList<D> {
/**
* Creates a new instance.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <T extends Describable<T>, D extends Descriptor<T>> DescriptorExtensionList<T, D> createDescriptorList(Hudson hudson, Class<T> describableType) {
if (describableType == (Class) Publisher.class) {
return (DescriptorExtensionList) new DescriptorExtensionListImpl(hudson);
}
return new DescriptorExtensionList<T, D>(hudson, describableType);
}
/**
* Type of the {@link Describable} that this extension list retains.
*/
private final Class<T> describableType;
protected DescriptorExtensionList(Hudson hudson, Class<T> describableType) {
super(hudson, (Class) Descriptor.class, (CopyOnWriteArrayList) getLegacyDescriptors(describableType));
this.describableType = describableType;
}
/**
* Finds the descriptor that has the matching fully-qualified class name.
*
* @param fqcn Fully qualified name of the descriptor, not the describable.
*/
public D find(String fqcn) {
return Descriptor.find(this, fqcn);
}
/**
* Finds the descriptor that describes the given type. That is, if this
* method returns d, {@code d.clazz==type}
*/
public D find(Class<? extends T> type) {
for (D d : this) {
if (d.clazz == type) {
return d;
}
}
return null;
}
/**
* Creates a new instance of a {@link Describable} from the structured form
* submission data posted by a radio button group.
*/
public T newInstanceFromRadioList(JSONObject config) throws FormException {
if (config.isNullObject()) {
return null; // none was selected
}
int idx = config.getInt("value");
return get(idx).newInstance(Stapler.getCurrentRequest(), config);
}
public T newInstanceFromRadioList(JSONObject parent, String name) throws FormException {
return newInstanceFromRadioList(parent.getJSONObject(name));
}
/**
* Finds a descriptor by their {@link Descriptor#getId()}.
*
* If none is found, null is returned.
*/
public Descriptor<T> findByName(String id) {
for (Descriptor<T> d : this) {
if (d.getId().equals(id)) {
return d;
}
}
return null;
}
/**
* {@link #load()} in the descriptor is not a real load activity, so locking
* against "this" is enough.
*/
@Override
protected Object getLoadLock() {
return this;
}
/**
* Loading the descriptors in this case means filtering the descriptor from
* the master {@link ExtensionList}.
*/
@Override
protected List<ExtensionComponent<D>> load() {
List<ExtensionComponent<D>> r = new ArrayList<ExtensionComponent<D>>();
for (ExtensionComponent<Descriptor> c : hudson.getExtensionList(Descriptor.class).getComponents()) {
Descriptor d = c.getInstance();
Type subTyping = Types.getBaseClass(d.getClass(), Descriptor.class);
if (!(subTyping instanceof ParameterizedType)) {
LOGGER.severe(d.getClass() + " doesn't extend Descriptor with a type parameter");
continue; // skip this one
}
if (Types.erasure(Types.getTypeArgument(subTyping, 0)) == (Class) describableType) {
r.add((ExtensionComponent) c);
}
}
return r;
}
/**
* Stores manually registered Descriptor instances. Keyed by the
* {@link Describable} type.
*/
private static final Memoizer<Class, CopyOnWriteArrayList<ExtensionComponent<Descriptor>>> legacyDescriptors = new Memoizer<Class, CopyOnWriteArrayList<ExtensionComponent<Descriptor>>>() {
public CopyOnWriteArrayList compute(Class key) {
return new CopyOnWriteArrayList();
}
};
private static <T extends Describable<T>> CopyOnWriteArrayList<ExtensionComponent<Descriptor<T>>> getLegacyDescriptors(Class<T> type) {
return (CopyOnWriteArrayList) legacyDescriptors.get(type);
}
/**
* List up all the legacy instances currently in use.
*/
public static Iterable<Descriptor> listLegacyInstances() {
return new Iterable<Descriptor>() {
public Iterator<Descriptor> iterator() {
return new AdaptedIterator<ExtensionComponent<Descriptor>, Descriptor>(
new FlattenIterator<ExtensionComponent<Descriptor>, CopyOnWriteArrayList<ExtensionComponent<Descriptor>>>(legacyDescriptors.values()) {
protected Iterator<ExtensionComponent<Descriptor>> expand(CopyOnWriteArrayList<ExtensionComponent<Descriptor>> v) {
return v.iterator();
}
}) {
protected Descriptor adapt(ExtensionComponent<Descriptor> item) {
return item.getInstance();
}
};
}
};
}
/**
* Exposed just for the test harness. Clear legacy instances.
*/
public static void clearLegacyInstances() {
legacyDescriptors.clear();
}
private static final Logger LOGGER = Logger.getLogger(DescriptorExtensionList.class.getName());
}