1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
5 *
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common Development
8 * and Distribution License("CDDL") (collectively, the "License"). You
9 * may not use this file except in compliance with the License. You can obtain
10 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
11 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
12 * language governing permissions and limitations under the License.
13 *
14 * When distributing the software, include this License Header Notice in each
15 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
16 * Sun designates this particular file as subject to the "Classpath" exception
17 * as provided by Sun in the GPL Version 2 section of the License file that
18 * accompanied this code. If applicable, add the following below the License
19 * Header, with the fields enclosed by brackets [] replaced by your own
20 * identifying information: "Portions Copyrighted [year]
21 * [name of copyright owner]"
22 *
23 * Contributor(s):
24 *
25 * If you wish your version of this file to be governed by only the CDDL or
26 * only the GPL Version 2, indicate your decision by adding "[Contributor]
27 * elects to include this software in this distribution under the [CDDL or GPL
28 * Version 2] license." If you don't indicate a single choice of license, a
29 * recipient has the option to distribute your version of this file under
30 * either the CDDL, the GPL Version 2 or to extend the choice of license to
31 * its licensees as provided above. However, if you add GPL Version 2 code
32 * and therefore, elected the GPL Version 2 license, then the option applies
33 * only if the new code is made subject to such option by the copyright
34 * holder.
35 */
36
37 package com.sun.faces.application.annotation;
38
39 import java.lang.annotation.Annotation;
40 import java.util.Collections;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.Collection;
44 import java.util.Set;
45 import java.util.concurrent.Callable;
46 import java.util.concurrent.CancellationException;
47 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.concurrent.ConcurrentMap;
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.Future;
51 import java.util.concurrent.FutureTask;
52 import java.util.logging.Level;
53 import java.util.logging.Logger;
54
55 import javax.faces.FacesException;
56 import javax.faces.component.UIComponent;
57 import javax.faces.component.behavior.Behavior;
58 import javax.faces.component.behavior.ClientBehaviorBase;
59 import javax.faces.context.FacesContext;
60 import javax.faces.convert.Converter;
61 import javax.faces.render.ClientBehaviorRenderer;
62 import javax.faces.render.RenderKit;
63 import javax.faces.render.Renderer;
64 import javax.faces.validator.Validator;
65
66 import com.sun.faces.util.FacesLogger;
67 import javax.faces.event.SystemEvent;
68
69 /**
70 * This class represents the central point for annotation handling within a
71 * web application.
72 */
73 public class AnnotationManager {
74
75 private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
76 private static final Scanner RESOURCE_DEPENDENCY_SCANNER = new ResourceDependencyScanner();
77 private static final Scanner LISTENER_FOR_SCANNER = new ListenerForScanner();
78
79 /**
80 * {@link Scanner} instances to be used against {@link Behavior} classes.
81 */
82 private static final Scanner[] BEHAVIOR_SCANNERS = {
83 RESOURCE_DEPENDENCY_SCANNER
84 };
85
86 /**
87 * {@link Scanner} instances to be used against {@link ClientBehaviorRenderer} classes.
88 */
89 private static final Scanner[] CLIENT_BEHAVIOR_RENDERER_SCANNERS = {
90 RESOURCE_DEPENDENCY_SCANNER
91 };
92
93 /**
94 * {@link Scanner} instances to be used against {@link UIComponent} classes.
95 */
96 private static final Scanner[] UICOMPONENT_SCANNERS = {
97 RESOURCE_DEPENDENCY_SCANNER,
98 LISTENER_FOR_SCANNER
99 };
100
101 /**
102 * {@link Scanner} instances to be used against {@link Validator} classes.
103 */
104 private static final Scanner[] VALIDATOR_SCANNERS = {
105 RESOURCE_DEPENDENCY_SCANNER
106 };
107
108 /**
109 * {@link Scanner} instances to be used against {@link Converter} classes.
110 */
111 private static final Scanner[] CONVERTER_SCANNERS = {
112 RESOURCE_DEPENDENCY_SCANNER
113 };
114
115 /**
116 * {@link Scanner} instances to be used against {@link Renderer} classes.
117 */
118 private static final Scanner[] RENDERER_SCANNERS = {
119 RESOURCE_DEPENDENCY_SCANNER,
120 LISTENER_FOR_SCANNER
121 };
122
123 private static final Scanner[] EVENTS_SCANNERS = {
124 RESOURCE_DEPENDENCY_SCANNER
125 };
126
127 /**
128 * Enum of the different processing targets and their associated
129 * {@link Scanner}s
130 */
131 private enum ProcessingTarget {
132 Behavior(BEHAVIOR_SCANNERS),
133 ClientBehaviorRenderer(CLIENT_BEHAVIOR_RENDERER_SCANNERS),
134 UIComponent(UICOMPONENT_SCANNERS),
135 Validator(VALIDATOR_SCANNERS),
136 Converter(CONVERTER_SCANNERS),
137 Renderer(RENDERER_SCANNERS),
138 SystemEvent(EVENTS_SCANNERS);
139
140
141 @SuppressWarnings({"NonSerializableFieldInSerializableClass"})
142 private Scanner[] scanners;
143 ProcessingTarget(Scanner[] scanners) {
144 this.scanners = scanners;
145 }
146
147 }
148
149 /**
150 * The backing cache for all annotation metadata.
151 */
152 private ConcurrentMap<Class<?>,Future<Map<Class<? extends Annotation>, RuntimeAnnotationHandler>>> cache;
153
154
155 // ------------------------------------------------------------ Constructors
156
157
158 /**
159 * Construct a new AnnotationManager instance.
160 */
161 public AnnotationManager() {
162
163 cache = new ConcurrentHashMap<Class<?>,Future<Map<Class<? extends Annotation>, RuntimeAnnotationHandler>>>(40, .75f, 32);
164
165 }
166
167
168
169 // ---------------------------------------------------------- Public Methods
170
171
172 /**
173 * <p>
174 * Apply the configuration metadata contained with in the <code>Collection</code>
175 * of annotated classes.
176 * </p>
177 *
178 * @param ctx FacesContext available during application initialization
179 * @param annotatedClasses <code>Collection</code> of class names known
180 * to contain one or more Faces configuration annotations
181 */
182 public void applyConfigAnntations(FacesContext ctx,
183 Class<? extends Annotation> annotationType,
184 Set<? extends Class> annotatedClasses) {
185
186 if (annotatedClasses != null && !annotatedClasses.isEmpty()) {
187 ConfigAnnotationHandler handler =
188 getConfigAnnotationHandlers().get(annotationType);
189 if (handler == null) {
190 throw new IllegalStateException("Internal Error: No ConfigAnnotationHandler for type: " + annotationType);
191 }
192 for (Class<?> clazz : annotatedClasses) {
193 handler.collect(clazz, clazz.getAnnotation(annotationType));
194 }
195 // metadata collected, now push the configuration to the system
196 handler.push(ctx);
197 }
198
199 }
200
201 /**
202 * Apply annotations relevant to {@link javax.faces.component.behavior.Behavior} instances.
203 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
204 * @param b the target <code>Behavior</code> to process
205 */
206 public void applyBehaviorAnnotations(FacesContext ctx, Behavior b) {
207
208 applyAnnotations(ctx, b.getClass(), ProcessingTarget.Behavior, b);
209 if (b instanceof ClientBehaviorBase) {
210 ClientBehaviorBase clientBehavior = (ClientBehaviorBase) b;
211 String rendererType = clientBehavior.getRendererType();
212 RenderKit renderKit = ctx.getRenderKit();
213 if( null != rendererType && null != renderKit){
214 ClientBehaviorRenderer behaviorRenderer = renderKit.getClientBehaviorRenderer(rendererType);
215 if(null != behaviorRenderer){
216 applyClientBehaviorRendererAnnotations(ctx, behaviorRenderer);
217 }
218 }
219 }
220
221 }
222
223 /**
224 * Apply annotations relevant to {@link javax.faces.render.ClientBehaviorRenderer} instances.
225 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
226 * @param b the target <code>ClientBehaviorRenderer</code> to process
227 */
228 public void applyClientBehaviorRendererAnnotations(FacesContext ctx, ClientBehaviorRenderer b) {
229
230 applyAnnotations(ctx, b.getClass(), ProcessingTarget.ClientBehaviorRenderer, b);
231
232 }
233
234 /**
235 * Apply annotations relevant to {@link javax.faces.component.UIComponent} instances.
236 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
237 * @param c the target <code>UIComponent</code> to process
238 */
239 public void applyComponentAnnotations(FacesContext ctx, UIComponent c) {
240
241 applyAnnotations(ctx, c.getClass(), ProcessingTarget.UIComponent, c);
242
243 }
244
245
246 /**
247 * Apply annotations relevant to {@link javax.faces.validator.Validator} instances.
248 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
249 * @param v the target <code>Validator</code> to process
250 */
251 public void applyValidatorAnnotations(FacesContext ctx, Validator v) {
252
253 applyAnnotations(ctx, v.getClass(), ProcessingTarget.Validator, v);
254
255 }
256
257
258 /**
259 * Apply annotations relevant to {@link javax.faces.convert.Converter} instances.
260 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
261 * @param c the target <code>Converter</code> to process
262 */
263 public void applyConverterAnnotations(FacesContext ctx, Converter c) {
264
265 applyAnnotations(ctx, c.getClass(), ProcessingTarget.Converter, c);
266
267 }
268
269
270 /**
271 * Apply annotations relevent to {@link javax.faces.render.Renderer} instances.
272 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
273 * @param r the <code>Renderer</code> to process
274 * @param c the <code>UIComponent</code> instances that is associated with this
275 * <code>Renderer</code>
276 */
277 public void applyRendererAnnotations(FacesContext ctx, Renderer r, UIComponent c) {
278
279 applyAnnotations(ctx, r.getClass(), ProcessingTarget.Renderer, r, c);
280
281 }
282
283 public void applySystemEventAnnotations(FacesContext ctx, SystemEvent e) {
284 applyAnnotations(ctx, e.getClass(), ProcessingTarget.SystemEvent, e);
285 }
286
287
288 // --------------------------------------------------------- Private Methods
289
290
291 /**
292 * @return a new <code>Map</code> which maps the types of annotations to
293 * a specific <code>ConfigAnnotationHandler</code>. Note that each invocation
294 * of this method constructs a new <code>Map</code> with new
295 * <code>ConfigAnnotationhandler</code> instances as they are not thread
296 * safe.
297 */
298 private Map<Class<? extends Annotation>,ConfigAnnotationHandler> getConfigAnnotationHandlers() {
299
300 ConfigAnnotationHandler[] handlers = {
301 new ComponentConfigHandler(),
302 new ConverterConfigHandler(),
303 new ValidatorConfigHandler(),
304 new BehaviorConfigHandler(),
305 new RenderKitConfigHandler(),
306 new ManagedBeanConfigHandler(),
307 new NamedEventConfigHandler()
308 };
309 Map<Class<? extends Annotation>,ConfigAnnotationHandler> handlerMap =
310 new HashMap<Class<? extends Annotation>,ConfigAnnotationHandler>();
311 for (ConfigAnnotationHandler handler : handlers) {
312 Collection<Class<? extends Annotation>> handledClasses = handler.getHandledAnnotations();
313 for (Class<? extends Annotation> handled : handledClasses) {
314 handlerMap.put(handled, handler);
315 }
316 }
317
318 return handlerMap;
319
320 }
321
322
323 /**
324 * Apply all annotations associated with <code>targetClass</code>
325 *
326 * @param ctx the {@link javax.faces.context.FacesContext} for the current request
327 * @param targetClass class of the <code>processingTarget</code>
328 * @param processingTarget the type of component that is being processed
329 * @param params one or more parameters to be passed to each {@link RuntimeAnnotationHandler}
330 */
331 private void applyAnnotations(FacesContext ctx,
332 Class<?> targetClass,
333 ProcessingTarget processingTarget,
334 Object... params) {
335
336 Map<Class<? extends Annotation>, RuntimeAnnotationHandler> map = getHandlerMap(targetClass, processingTarget);
337 if (map != null && !map.isEmpty()) {
338 for (RuntimeAnnotationHandler handler : map.values()) {
339 handler.apply(ctx, params);
340 }
341 }
342
343 }
344
345
346 /**
347 * Helper method to look up cached annotation metadata.
348 * @param targetClass class of the <code>processingTarget</code>
349 * @param processingTarget the type of component being processed
350 * @return a Map keyed by Annotation class with an AnnotationHandler as the
351 * value
352 */
353 private Map<Class<? extends Annotation>, RuntimeAnnotationHandler> getHandlerMap(Class<?> targetClass,
354 ProcessingTarget processingTarget) {
355
356 while (true) {
357 Future<Map<Class<? extends Annotation>, RuntimeAnnotationHandler>> f =
358 cache.get(targetClass);
359 if (f == null) {
360 ProcessAnnotationsTask t =
361 new ProcessAnnotationsTask(targetClass, processingTarget.scanners);
362 FutureTask<Map<Class<? extends Annotation>, RuntimeAnnotationHandler>> ft =
363 new FutureTask<Map<Class<? extends Annotation>, RuntimeAnnotationHandler>>(t);
364 f = cache.putIfAbsent(targetClass, ft);
365 if (f == null) {
366 f = ft;
367 ft.run();
368 }
369 }
370 try {
371 return f.get();
372 } catch (CancellationException ce) {
373 if (LOGGER.isLoggable(Level.FINEST)) {
374 LOGGER.log(Level.FINEST,
375 ce.toString(),
376 ce);
377 }
378 cache.remove(targetClass);
379 } catch (InterruptedException ie) {
380 if (LOGGER.isLoggable(Level.FINEST)) {
381 LOGGER.log(Level.FINEST,
382 ie.toString(),
383 ie);
384 }
385 cache.remove(targetClass);
386 } catch (ExecutionException ee) {
387 throw new FacesException(ee);
388 }
389 }
390
391 }
392
393
394 // ----------------------------------------------------------- Inner Classes
395
396
397 /**
398 * This <code>Callable</code> will leverage the provided <code>Scanner</code>s
399 * to build a mapping between a particular annotation type and an
400 * <code>AnnotationHandler</code> for that type.
401 */
402 private static final class ProcessAnnotationsTask
403 implements Callable<Map<Class<? extends Annotation>, RuntimeAnnotationHandler>> {
404
405 @SuppressWarnings({"unchecked"})
406 private static final Map<Class<? extends Annotation>, RuntimeAnnotationHandler> EMPTY =
407 Collections.EMPTY_MAP;
408 private Class<?> clazz;
409 private Scanner[] scanners;
410
411
412 // -------------------------------------------------------- Constructors
413
414
415
416 public ProcessAnnotationsTask(Class<?> clazz, Scanner[] scanners) {
417
418 this.clazz = clazz;
419 this.scanners = scanners;
420
421 }
422
423
424 // ------------------------------------------------------ Public Methods
425
426
427 public Map<Class<? extends Annotation>, RuntimeAnnotationHandler> call() throws Exception {
428
429 Map<Class<? extends Annotation>, RuntimeAnnotationHandler> map = null;
430 for (Scanner scanner : scanners) {
431 RuntimeAnnotationHandler handler = scanner.scan(clazz);
432 if (handler != null) {
433 if (map == null) {
434 map = new HashMap<Class<? extends Annotation>, RuntimeAnnotationHandler>(2, 1.0f);
435 }
436 map.put(scanner.getAnnotation(), handler);
437 }
438 }
439
440 return ((map != null) ? map : EMPTY);
441
442 }
443
444 } // END ProcessAnnotationsTask
445
446
447
448
449
450
451
452 }