View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.config.annotation;
20  
21  import java.lang.annotation.Annotation;
22  import java.lang.reflect.Field;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.FacesException;
30  import javax.faces.bean.ApplicationScoped;
31  import javax.faces.bean.CustomScoped;
32  import javax.faces.bean.ManagedBean;
33  import javax.faces.bean.NoneScoped;
34  import javax.faces.bean.RequestScoped;
35  import javax.faces.bean.SessionScoped;
36  import javax.faces.bean.ViewScoped;
37  import javax.faces.component.FacesComponent;
38  import javax.faces.component.behavior.FacesBehavior;
39  import javax.faces.context.ExternalContext;
40  import javax.faces.convert.FacesConverter;
41  import javax.faces.event.ComponentSystemEvent;
42  import javax.faces.event.NamedEvent;
43  import javax.faces.render.FacesBehaviorRenderer;
44  import javax.faces.render.FacesRenderer;
45  import javax.faces.render.RenderKitFactory;
46  import javax.faces.validator.FacesValidator;
47  
48  import org.apache.myfaces.config.impl.digester.elements.Application;
49  import org.apache.myfaces.config.impl.digester.elements.Behavior;
50  import org.apache.myfaces.config.impl.digester.elements.Converter;
51  import org.apache.myfaces.config.impl.digester.elements.FacesConfig;
52  import org.apache.myfaces.spi.AnnotationProvider;
53  import org.apache.myfaces.spi.AnnotationProviderFactory;
54  
55  /**
56   * Configure all annotations that needs to be defined at startup.
57   *
58   * <ul>
59   * <li>{@link javax.faces.component.FacesComponent}</li>
60   * <li>{@link javax.faces.convert.FacesConverter}</li>
61   * <li>{@link javax.faces.validator.FacesValidator}</li>
62   * <li>{@link javax.faces.render.FacesRenderer}</li>
63   * <li>{@link javax.faces.bean.ManagedBean}</li>
64   * <li>{@link javax.faces.bean.ManagedProperty}</li>
65   * <li>{@link javax.faces.render.FacesBehaviorRenderer}</li>
66   * </ul>
67   * <p>
68   * Some parts copied from org.apache.shale.tiger.view.faces.LifecycleListener2
69   * </p>
70   *
71   * @since 2.0
72   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
73   * @version $Revision: 1455392 $ $Date: 2013-03-11 21:17:31 -0500 (Mon, 11 Mar 2013) $
74   */
75  public class AnnotationConfigurator
76  {
77      //private static final Log log = LogFactory.getLog(AnnotationConfigurator.class);
78      private static final Logger log = Logger.getLogger(AnnotationConfigurator.class.getName());
79  
80      public AnnotationConfigurator()
81      {
82      }
83  
84      public FacesConfig createFacesConfig(ExternalContext externalcontext, boolean metadataComplete)
85      {
86          if (!metadataComplete)
87          {
88              AnnotationProvider provider = AnnotationProviderFactory.getAnnotationProviderFactory(externalcontext).
89                      getAnnotationProvider(externalcontext);
90              Map<Class<? extends Annotation>, Set<Class<?>>> map = provider.getAnnotatedClasses(externalcontext);
91              return createFacesConfig(map);
92          }
93          return null;
94      }
95  
96      /**
97       * TODO: Implement strategy pattern over this method.
98       * 
99       * @param map
100      * @return
101      */
102     protected FacesConfig createFacesConfig(Map<Class<? extends Annotation>, Set<Class<?>>> map)
103     {
104         FacesConfig facesConfig = new FacesConfig();
105 
106         Set<Class<?>> classes = map.get(FacesComponent.class);
107 
108         if (classes != null && !classes.isEmpty())
109         {
110             for (Class<?> clazz : classes)
111             {
112                 FacesComponent comp = (FacesComponent) clazz
113                         .getAnnotation(FacesComponent.class);
114                 if (comp != null)
115                 {
116                     if (log.isLoggable(Level.FINEST))
117                     {
118                         log.finest("addComponent(" + comp.value() + ","
119                                 + clazz.getName() + ")");
120                     }
121 
122                     facesConfig.addComponent(comp.value(), clazz.getName());
123                 }
124             }
125         }
126 
127         classes = map.get(FacesConverter.class);
128         if (classes != null && !classes.isEmpty())
129         {
130             for (Class<?> clazz : classes)
131             {
132                 FacesConverter conv = (FacesConverter) clazz
133                         .getAnnotation(FacesConverter.class);
134                 if (conv != null)
135                 {
136                     if (log.isLoggable(Level.FINEST))
137                     {
138                         log.finest("addConverter(" + conv.value() + ","
139                                 + clazz.getName() + ")");
140                     }
141                     //If there is a previous entry on Application Configuration Resources,
142                     //the entry there takes precedence
143                     boolean hasForClass = !Object.class.equals(conv.forClass());
144                     boolean hasValue = conv.value().length() > 0;
145                     if (hasForClass || hasValue)
146                     {
147                         Converter converter = new Converter();
148                         if (hasForClass)
149                         {
150                             converter.setForClass(conv.forClass().getName());
151                         }
152                         if (hasValue)
153                         {
154                             converter.setConverterId(conv.value());
155                         }
156                         converter.setConverterClass(clazz.getName());
157                         facesConfig.addConverter(converter);
158                     }
159                     else
160                     {
161                         // TODO MartinKoci MYFACES-3053
162                         throw new FacesException("@FacesConverter must have value, forClass or both. Check annotation "
163                                                  + "@FacesConverter on class: " + clazz.getName());
164                     }
165                 }
166             }
167         }
168 
169         classes = map.get(FacesValidator.class);
170         if (classes != null && !classes.isEmpty())
171         {
172             for (Class<?> clazz : classes)
173             {
174                 FacesValidator val = (FacesValidator) clazz
175                         .getAnnotation(FacesValidator.class);
176                 if (val != null)
177                 {
178                     if (log.isLoggable(Level.FINEST))
179                     {
180                         log.finest("addValidator(" + val.value() + "," + clazz.getName()
181                                 + ")");
182                     }
183                     facesConfig.addValidator(val.value(), clazz.getName());
184                     if (val.isDefault())
185                     {
186                         Application app = null;
187                         if (facesConfig.getApplications().isEmpty())
188                         {
189                             app = new Application();
190                         }
191                         else
192                         {
193                             app = (Application) facesConfig.getApplications().get(0);
194                         }
195                         app.addDefaultValidatorId(val.value());
196                     }
197                 }
198             }
199         }
200 
201         classes = map.get(FacesRenderer.class);
202         if (classes != null && !classes.isEmpty())
203         {
204             for (Class<?> clazz : classes)
205             {
206                 FacesRenderer rend = (FacesRenderer) clazz
207                         .getAnnotation(FacesRenderer.class);
208                 if (rend != null)
209                 {
210                     String renderKitId = rend.renderKitId();
211                     if (renderKitId == null)
212                     {
213                         renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
214                     }
215                     if (log.isLoggable(Level.FINEST))
216                     {
217                         log.finest("addRenderer(" + renderKitId + ", "
218                                 + rend.componentFamily() + ", " + rend.rendererType()
219                                 + ", " + clazz.getName() + ")");
220                     }
221 
222                     org.apache.myfaces.config.impl.digester.elements.RenderKit renderKit =
223                             (org.apache.myfaces.config.impl.digester.elements.RenderKit)
224                                     facesConfig.getRenderKit(renderKitId);
225                     if (renderKit == null)
226                     {
227                         renderKit = new org.apache.myfaces.config.impl.digester.elements.RenderKit();
228                         renderKit.setId(renderKitId);
229                         facesConfig.addRenderKit(renderKit);
230                     }
231 
232                     org.apache.myfaces.config.impl.digester.elements.Renderer renderer =
233                             new org.apache.myfaces.config.impl.digester.elements.Renderer();
234                     renderer.setComponentFamily(rend.componentFamily());
235                     renderer.setRendererClass(clazz.getName());
236                     renderer.setRendererType(rend.rendererType());
237                     renderKit.addRenderer(renderer);
238                 }
239             }
240         }
241 
242         classes = map.get(ManagedBean.class);
243         if (classes != null && !classes.isEmpty())
244         {
245             handleManagedBean(facesConfig, classes);
246         }
247 
248         classes = map.get(NamedEvent.class);
249         if (classes != null && !classes.isEmpty())
250         {
251             handleNamedEvent(facesConfig, classes);
252         }
253 
254         classes = map.get(FacesBehavior.class);
255         if (classes != null && !classes.isEmpty())
256         {
257             handleFacesBehavior(facesConfig, classes);
258         }
259 
260         classes = map.get(FacesBehaviorRenderer.class);
261         if (classes != null && !classes.isEmpty())
262         {
263             handleFacesBehaviorRenderer(facesConfig, classes);
264         }
265         return facesConfig;
266     }
267     
268     private void handleManagedBean(FacesConfig facesConfig, Set<Class<?>> classes)
269     {
270         for (Class<?> clazz : classes)
271         {
272             javax.faces.bean.ManagedBean bean =
273                     (javax.faces.bean.ManagedBean) clazz.getAnnotation(javax.faces.bean.ManagedBean.class);
274 
275             if (bean != null)
276             {
277                 if (log.isLoggable(Level.FINE))
278                 {
279                     log.fine("Class '" + clazz.getName() + "' has an @ManagedBean annotation");
280                 }
281 
282                 org.apache.myfaces.config.impl.digester.elements.ManagedBean mbc =
283                         new org.apache.myfaces.config.impl.digester.elements.ManagedBean();
284                 String beanName = bean.name();
285 
286                 if ((beanName == null) || beanName.equals(""))
287                 {
288                     int index;
289 
290                     // Missing name attribute algorithm: take the unqualified name and make the
291                     // first character lowercase.
292 
293                     beanName = clazz.getName();
294                     index = beanName.lastIndexOf(".");
295 
296                     if (index != -1)
297                     {
298                         beanName = beanName.substring(index + 1);
299                     }
300 
301                     beanName = Character.toLowerCase(beanName.charAt(0)) +
302                             beanName.substring(1);
303                 }
304 
305                 mbc.setName(beanName);
306                 mbc.setEager(Boolean.toString(bean.eager()));
307                 mbc.setBeanClass(clazz.getName());
308 
309                 ApplicationScoped appScoped = (ApplicationScoped) clazz.getAnnotation(ApplicationScoped.class);
310                 if (appScoped != null)
311                 {
312                     mbc.setScope("application");
313                 }
314 
315                 else
316                 {
317                     NoneScoped noneScoped = (NoneScoped) clazz.getAnnotation(NoneScoped.class);
318                     if (noneScoped != null)
319                     {
320                         mbc.setScope("none");
321                     }
322 
323                     else
324                     {
325                         RequestScoped requestScoped = (RequestScoped) clazz.getAnnotation(RequestScoped.class);
326                         if (requestScoped != null)
327                         {
328                             mbc.setScope("request");
329                         }
330 
331                         else
332                         {
333                             SessionScoped sessionScoped = (SessionScoped) clazz.getAnnotation(SessionScoped.class);
334                             if (sessionScoped != null)
335                             {
336                                 mbc.setScope("session");
337                             }
338 
339                             else
340                             {
341                                 ViewScoped viewScoped = (ViewScoped) clazz.getAnnotation(ViewScoped.class);
342                                 if (viewScoped != null)
343                                 {
344                                     mbc.setScope("view");
345                                 }
346 
347                                 else
348                                 {
349                                     CustomScoped customScoped
350                                             = (CustomScoped) clazz.getAnnotation(CustomScoped.class);
351                                     if (customScoped != null)
352                                     {
353                                         mbc.setScope(customScoped.value());
354                                     }
355 
356                                     else
357                                     {
358                                         // No scope annotation means default of "request".
359 
360                                         mbc.setScope("request");
361                                     }
362                                 }
363                             }
364                         }
365                     }
366                 }
367 
368                 Field[] fields = fields(clazz);
369                 for (Field field : fields)
370                 {
371                     if (log.isLoggable(Level.FINEST))
372                     {
373                         log.finest("  Scanning field '" + field.getName() + "'");
374                     }
375                     javax.faces.bean.ManagedProperty property = (javax.faces.bean.ManagedProperty) field
376                             .getAnnotation(javax.faces.bean.ManagedProperty.class);
377                     if (property != null)
378                     {
379                         if (log.isLoggable(Level.FINE))
380                         {
381                             log.fine("  Field '" + field.getName()
382                                     + "' has a @ManagedProperty annotation");
383                         }
384                         org.apache.myfaces.config.impl.digester.elements.ManagedProperty mpc =
385                                 new org.apache.myfaces.config.impl.digester.elements.ManagedProperty();
386                         String name = property.name();
387                         if ((name == null) || "".equals(name))
388                         {
389                             name = field.getName();
390                         }
391                         mpc.setPropertyName(name);
392                         mpc.setPropertyClass(field.getType().getName()); // FIXME - primitives, arrays, etc.
393                         mpc.setValue(property.value());
394                         mbc.addProperty(mpc);
395                         continue;
396                     }
397                 }
398                 facesConfig.addManagedBean(mbc);
399             }
400         }
401     }
402     
403     private void handleNamedEvent(FacesConfig facesConfig, Set<Class<?>> classes)
404     {
405         for (Class<?> clazz : classes)
406         {
407             NamedEvent namedEvent = (NamedEvent) clazz.getAnnotation(NamedEvent.class);
408 
409             if (namedEvent != null)
410             {
411                 // Can only apply @NamedEvent to ComponentSystemEvent subclasses.
412 
413                 if (!ComponentSystemEvent.class.isAssignableFrom(clazz))
414                 {
415                     // Just log this.  We'll catch it later in the runtime.
416 
417                     if (log.isLoggable(Level.WARNING))
418                     {
419                         log.warning(clazz.getName() + " is annotated with @javax.faces.event.NamedEvent, but does "
420                                     + "not extend javax.faces.event.ComponentSystemEvent");
421                     }
422                 }
423                 // Have to register @NamedEvent annotations with the NamedEventManager class since
424                 // we need to get access to this info later and can't from the dispenser (it's not a
425                 // singleton).
426                 org.apache.myfaces.config.impl.digester.elements.NamedEvent namedEventConfig =
427                         new org.apache.myfaces.config.impl.digester.elements.NamedEvent();
428                 namedEventConfig.setEventClass(clazz.getName());
429                 namedEventConfig.setShortName(namedEvent.shortName());
430                 facesConfig.addNamedEvent(namedEventConfig);
431             }
432         }
433     }
434 
435     private void handleFacesBehavior(FacesConfig facesConfig, Set<Class<?>> classes)
436     {
437         for (Class<?> clazz : classes)
438         {
439             FacesBehavior facesBehavior = (FacesBehavior) clazz.getAnnotation(FacesBehavior.class);
440 
441             if (facesBehavior != null)
442             {
443                 // Can only apply @FacesBehavior to Behavior implementors.
444 
445                 if (!javax.faces.component.behavior.Behavior.class.isAssignableFrom(clazz))
446                 {
447                     // Just log this.  We'll catch it later in the runtime.
448 
449                     if (log.isLoggable(Level.WARNING))
450                     {
451                         log.warning(clazz.getName()
452                                     + " is annotated with @javax.faces.component.behavior.FacesBehavior, "
453                                     + "but does not implement javax.faces.component.behavior.Behavior");
454                     }
455                 }
456 
457                 if (log.isLoggable(Level.FINEST))
458                 {
459                     log.finest("addBehavior(" + facesBehavior.value() + ", " + clazz.getName() + ")");
460                 }
461 
462                 Behavior behavior = new Behavior();
463                 behavior.setBehaviorId(facesBehavior.value());
464                 behavior.setBehaviorClass(clazz.getName());
465                 facesConfig.addBehavior(behavior);
466             }
467 
468         }
469     }
470     
471     private void handleFacesBehaviorRenderer(FacesConfig facesConfig, Set<Class<?>> classes)
472     {
473         for (Class<?> clazz : classes)
474         {
475             FacesBehaviorRenderer facesBehaviorRenderer
476                     = (FacesBehaviorRenderer) clazz.getAnnotation(FacesBehaviorRenderer.class);
477 
478             if (facesBehaviorRenderer != null)
479             {
480                 String renderKitId = facesBehaviorRenderer.renderKitId();
481                 //RenderKit renderKit;
482 
483                 if (log.isLoggable(Level.FINEST))
484                 {
485                     log.finest("addClientBehaviorRenderer(" + renderKitId + ", "
486                                + facesBehaviorRenderer.rendererType() + ", "
487                                + clazz.getName() + ")");
488                 }
489 
490                 org.apache.myfaces.config.impl.digester.elements.RenderKit renderKit =
491                         (org.apache.myfaces.config.impl.digester.elements.RenderKit)
492                                 facesConfig.getRenderKit(renderKitId);
493                 if (renderKit == null)
494                 {
495                     renderKit = new org.apache.myfaces.config.impl.digester.elements.RenderKit();
496                     renderKit.setId(renderKitId);
497                     facesConfig.addRenderKit(renderKit);
498                 }
499 
500                 org.apache.myfaces.config.impl.digester.elements.ClientBehaviorRenderer cbr =
501                         new org.apache.myfaces.config.impl.digester.elements.ClientBehaviorRenderer();
502                 cbr.setRendererType(facesBehaviorRenderer.rendererType());
503                 cbr.setRendererClass(clazz.getName());
504                 renderKit.addClientBehaviorRenderer(cbr);
505             }
506         }
507     }
508 
509     /**
510      * <p>Return an array of all <code>Field</code>s reflecting declared
511      * fields in this class, or in any superclass other than
512      * <code>java.lang.Object</code>.</p>
513      *
514      * @param clazz Class to be analyzed
515      */
516     private Field[] fields(Class<?> clazz)
517     {
518 
519         Map<String, Field> fields = new HashMap<String, Field>();
520         do
521         {
522             for (Field field : clazz.getDeclaredFields())
523             {
524                 if (!fields.containsKey(field.getName()))
525                 {
526                     fields.put(field.getName(), field);
527                 }
528             }
529             clazz = clazz.getSuperclass();
530         }
531         while (clazz != Object.class);
532 
533         return fields.values().toArray(new Field[fields.size()]);
534 
535     }
536 }