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