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