File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\ExtensionList.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson;
25
26 import com.google.common.collect.Lists;
27 import hudson.init.InitMilestone;
28 import hudson.model.Hudson;
29 import jenkins.ExtensionComponentSet;
30 import jenkins.model.Jenkins;
31 import hudson.util.AdaptedIterator;
32 import hudson.util.DescriptorList;
33 import hudson.util.Memoizer;
34 import hudson.util.Iterators;
35 import hudson.ExtensionPoint.LegacyInstancesAreScopedToHudson;
36
37 import java.util.AbstractList;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Vector;
44 import java.util.concurrent.CopyOnWriteArrayList;
45 import java.util.logging.Level;
46 import java.util.logging.Logger;
47
48 /**
49 * Retains the known extension instances for the given type 'T'.
50 *
51 * <p>
52 * Extensions are loaded lazily on demand and automatically by using {@link ExtensionFinder}, but this
53 * class also provides a mechanism to provide compatibility with the older {@link DescriptorList}-based
54 * manual registration,
55 *
56 * <p>
57 * All {@link ExtensionList} instances should be owned by {@link jenkins.model.Jenkins}, even though
58 * extension points can be defined by anyone on any type. Use {@link jenkins.model.Jenkins#getExtensionList(Class)}
59 * and {@link jenkins.model.Jenkins#getDescriptorList(Class)} to obtain the instances.
60 *
61 * @param <T>
62 * Type of the extension point. This class holds instances of the subtypes of 'T'.
63 *
64 * @author Kohsuke Kawaguchi
65 * @since 1.286
66 * @see jenkins.model.Jenkins#getExtensionList(Class)
67 * @see jenkins.model.Jenkins#getDescriptorList(Class)
68 */
69 public class ExtensionList<T> extends AbstractList<T> {
70 /**
71 * @deprecated as of 1.417
72 * Use {@link #jenkins}
73 */
74 public final Hudson hudson;
75 public final Jenkins jenkins;
76 public final Class<T> extensionType;
77
78 /**
79 * Once discovered, extensions are retained here.
80 */
81 @CopyOnWrite
82 private volatile List<ExtensionComponent<T>> extensions;
83
84 /**
85 * Place to store manually registered instances with the per-Hudson scope.
86 * {@link CopyOnWriteArrayList} is used here to support concurrent iterations and mutation.
87 */
88 private final CopyOnWriteArrayList<ExtensionComponent<T>> legacyInstances;
89
90 /**
91 * @deprecated as of 1.416
92 * Use {@link #ExtensionList(Jenkins, Class)}
93 */
94 protected ExtensionList(Hudson hudson, Class<T> extensionType) {
95 this((Jenkins)hudson,extensionType);
96 }
97
98 protected ExtensionList(Jenkins jenkins, Class<T> extensionType) {
99 this(jenkins,extensionType,new CopyOnWriteArrayList<ExtensionComponent<T>>());
100 }
101
102 /**
103 * @deprecated as of 1.416
104 * Use {@link #ExtensionList(Jenkins, Class, CopyOnWriteArrayList)}
105 */
106 protected ExtensionList(Hudson hudson, Class<T> extensionType, CopyOnWriteArrayList<ExtensionComponent<T>> legacyStore) {
107 this((Jenkins)hudson,extensionType,legacyStore);
108 }
109
110 /**
111 *
112 * @param legacyStore
113 * Place to store manually registered instances. The version of the constructor that
114 * omits this uses a new {@link Vector}, making the storage lifespan tied to the life of {@link ExtensionList}.
115 * If the manually registered instances are scoped to VM level, the caller should pass in a static list.
116 */
117 protected ExtensionList(Jenkins jenkins, Class<T> extensionType, CopyOnWriteArrayList<ExtensionComponent<T>> legacyStore) {
118 this.hudson = (Hudson)jenkins;
119 this.jenkins = jenkins;
120 this.extensionType = extensionType;
121 this.legacyInstances = legacyStore;
122 }
123
124 /**
125 * Looks for the extension instance of the given type (subclasses excluded),
126 * or return null.
127 */
128 public <U extends T> U get(Class<U> type) {
129 for (T ext : this)
130 if(ext.getClass()==type)
131 return type.cast(ext);
132 return null;
133 }
134
135 @Override
136 public Iterator<T> iterator() {
137 // we need to intercept mutation, so for now don't allow Iterator.remove
138 return new AdaptedIterator<ExtensionComponent<T>,T>(Iterators.readOnly(ensureLoaded().iterator())) {
139 protected T adapt(ExtensionComponent<T> item) {
140 return item.getInstance();
141 }
142 };
143 }
144
145 /**
146 * Gets the same thing as the 'this' list represents, except as {@link ExtensionComponent}s.
147 */
148 public List<ExtensionComponent<T>> getComponents() {
149 return Collections.unmodifiableList(ensureLoaded());
150 }
151
152 public T get(int index) {
153 return ensureLoaded().get(index).getInstance();
154 }
155
156 public int size() {
157 return ensureLoaded().size();
158 }
159
160 /**
161 * Gets the read-only view of this {@link ExtensionList} where components are reversed.
162 */
163 public List<T> reverseView() {
164 return new AbstractList<T>() {
165 @Override
166 public T get(int index) {
167 return ExtensionList.this.get(size()-index-1);
168 }
169
170 @Override
171 public int size() {
172 return ExtensionList.this.size();
173 }
174 };
175 }
176
177 @Override
178 public synchronized boolean remove(Object o) {
179 removeComponent(legacyInstances,o);
180 if(extensions!=null) {
181 List<ExtensionComponent<T>> r = new ArrayList<ExtensionComponent<T>>(extensions);
182 removeComponent(r,o);
183 extensions = sort(r);
184 }
185 return true;
186 }
187
188 private <T> void removeComponent(Collection<ExtensionComponent<T>> collection, Object t) {
189 for (Iterator<ExtensionComponent<T>> itr = collection.iterator(); itr.hasNext();) {
190 ExtensionComponent<T> c = itr.next();
191 if (c.getInstance().equals(t)) {
192 collection.remove(c);
193 return;
194 }
195 }
196 }
197
198 @Override
199 public final synchronized T remove(int index) {
200 T t = get(index);
201 remove(t);
202 return t;
203 }
204
205 /**
206 * Write access will put the instance into a legacy store.
207 *
208 * @deprecated since 2009-02-23.
209 * Prefer automatic registration.
210 */
211 @Override
212 public synchronized boolean add(T t) {
213 legacyInstances.add(new ExtensionComponent<T>(t));
214 // if we've already filled extensions, add it
215 if(extensions!=null) {
216 List<ExtensionComponent<T>> r = new ArrayList<ExtensionComponent<T>>(extensions);
217 r.add(new ExtensionComponent<T>(t));
218 extensions = sort(r);
219 }
220 return true;
221 }
222
223 @Override
224 public void add(int index, T element) {
225 add(element);
226 }
227
228 /**
229 * Used to bind extension to URLs by their class names.
230 *
231 * @since 1.349
232 */
233 public T getDynamic(String className) {
234 for (T t : this)
235 if (t.getClass().getName().equals(className))
236 return t;
237 return null;
238 }
239
240 private List<ExtensionComponent<T>> ensureLoaded() {
241 if(extensions!=null)
242 return extensions; // already loaded
243 if(Jenkins.getInstance().getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0)
244 return legacyInstances; // can't perform the auto discovery until all plugins are loaded, so just make the legacy instances visible
245
246 synchronized (getLoadLock()) {
247 if(extensions==null) {
248 List<ExtensionComponent<T>> r = load();
249 r.addAll(legacyInstances);
250 extensions = sort(r);
251 }
252 return extensions;
253 }
254 }
255
256 /**
257 * Chooses the object that locks the loading of the extension instances.
258 */
259 protected Object getLoadLock() {
260 return jenkins.lookup.setIfNull(Lock.class,new Lock());
261 }
262
263 /**
264 * Used during {@link Jenkins#refreshExtensions()} to add new components into existing {@link ExtensionList}s.
265 * Do not call from anywhere else.
266 */
267 public void refresh(ExtensionComponentSet delta) {
268 synchronized (getLoadLock()) {
269 if (extensions==null)
270 return; // not yet loaded. when we load it, we'll load everything visible by then, so no work needed
271
272 Collection<ExtensionComponent<T>> found = load(delta);
273 if (!found.isEmpty()) {
274 List<ExtensionComponent<T>> l = Lists.newArrayList(extensions);
275 l.addAll(found);
276 extensions = sort(l);
277 }
278 }
279 }
280
281 /**
282 * Loading an {@link ExtensionList} can result in a nested loading of another {@link ExtensionList}.
283 * What that means is that we need a single lock that spans across all the {@link ExtensionList}s,
284 * or else we can end up in a dead lock.
285 */
286 private static final class Lock {}
287
288 /**
289 * Loads all the extensions.
290 */
291 protected List<ExtensionComponent<T>> load() {
292 if (LOGGER.isLoggable(Level.FINE))
293 LOGGER.log(Level.FINE,"Loading ExtensionList: "+extensionType, new Throwable());
294
295 return jenkins.getPluginManager().getPluginStrategy().findComponents(extensionType, hudson);
296 }
297
298 /**
299 * Picks up extensions that we care from the given list.
300 */
301 protected Collection<ExtensionComponent<T>> load(ExtensionComponentSet delta) {
302 return delta.find(extensionType);
303 }
304
305
306 /**
307 * If the {@link ExtensionList} implementation requires sorting extensions,
308 * override this method to do so.
309 *
310 * <p>
311 * The implementation should copy a list, do a sort, and return the new instance.
312 */
313 protected List<ExtensionComponent<T>> sort(List<ExtensionComponent<T>> r) {
314 r = new ArrayList<ExtensionComponent<T>>(r);
315 Collections.sort(r);
316 return r;
317 }
318
319 /**
320 * @deprecated as of 1.416
321 * Use {@link #create(Jenkins, Class)}
322 */
323 public static <T> ExtensionList<T> create(Hudson hudson, Class<T> type) {
324 return create((Jenkins)hudson,type);
325 }
326
327 public static <T> ExtensionList<T> create(Jenkins jenkins, Class<T> type) {
328 if(type.getAnnotation(LegacyInstancesAreScopedToHudson.class)!=null)
329 return new ExtensionList<T>(jenkins,type);
330 else {
331 return new ExtensionList<T>(jenkins,type,staticLegacyInstances.get(type));
332 }
333 }
334
335 /**
336 * Places to store static-scope legacy instances.
337 */
338 private static final Memoizer<Class,CopyOnWriteArrayList> staticLegacyInstances = new Memoizer<Class,CopyOnWriteArrayList>() {
339 public CopyOnWriteArrayList compute(Class key) {
340 return new CopyOnWriteArrayList();
341 }
342 };
343
344 /**
345 * Exposed for the test harness to clear all legacy extension instances.
346 */
347 public static void clearLegacyInstances() {
348 staticLegacyInstances.clear();
349 }
350
351 private static final Logger LOGGER = Logger.getLogger(ExtensionList.class.getName());
352 }
353