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;
20  
21  import java.lang.reflect.Array;
22  import java.lang.reflect.InvocationTargetException;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.el.ELContext;
30  import javax.el.ELException;
31  import javax.el.ELResolver;
32  import javax.el.ExpressionFactory;
33  import javax.el.ValueExpression;
34  import javax.faces.FacesException;
35  import javax.faces.application.Application;
36  import javax.faces.context.ExternalContext;
37  import javax.faces.context.FacesContext;
38  import javax.naming.NamingException;
39  
40  import org.apache.commons.beanutils.PropertyUtils;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.apache.myfaces.config.annotation.LifecycleProvider;
44  import org.apache.myfaces.config.annotation.LifecycleProvider2;
45  import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
46  import org.apache.myfaces.config.element.ListEntries;
47  import org.apache.myfaces.config.element.ListEntry;
48  import org.apache.myfaces.config.element.ManagedBean;
49  import org.apache.myfaces.config.element.ManagedProperty;
50  import org.apache.myfaces.config.element.MapEntries;
51  import org.apache.myfaces.config.element.MapEntry;
52  import org.apache.myfaces.shared_impl.util.ClassUtils;
53  import org.apache.myfaces.util.ContainerUtils;
54  
55  
56  /**
57   * Create and initialize managed beans
58   *
59   * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller</a> (latest modification by $Author: lu4242 $)
60   * @author Anton Koinov
61   */
62  public class ManagedBeanBuilder
63  {
64      private static Log log = LogFactory.getLog(ManagedBeanBuilder.class);
65      private RuntimeConfig _runtimeConfig;
66      public final static String REQUEST = "request";
67      public final static String APPLICATION = "application";
68      public final static String SESSION = "session";
69      public final static String NONE = "none";
70  
71      public Object buildManagedBean(FacesContext facesContext, ManagedBean beanConfiguration) throws FacesException
72      {
73  
74          /*final AnnotatedManagedBeanHandler handler = new AnnotatedManagedBeanHandler(bean,
75                    beanConfiguration.getManagedBeanScope(), beanConfiguration.getManagedBeanName());
76  
77            final boolean threwUnchecked = handler.invokePostConstruct();
78  
79            if(threwUnchecked)
80                return null;*/
81          try
82          {
83              LifecycleProvider lifecycleProvider =
84                      LifecycleProviderFactory.getLifecycleProviderFactory().getLifecycleProvider(facesContext.getExternalContext());
85              final Object bean = lifecycleProvider.newInstance(beanConfiguration.getManagedBeanClassName());
86  
87              switch (beanConfiguration.getInitMode())
88              {
89                  case ManagedBean.INIT_MODE_PROPERTIES:
90                      try
91                      {
92                          initializeProperties(facesContext, beanConfiguration.getManagedProperties(),
93                                  beanConfiguration.getManagedBeanScope(), bean);
94                      }
95                      catch (IllegalArgumentException e)
96                      {
97                          throw new IllegalArgumentException(
98                                  e.getMessage()
99                                          + " for bean '"
100                                         + beanConfiguration.getManagedBeanName()
101                                         + "' check the configuration to make sure all properties correspond with get/set methods", e);
102                     }
103                     break;
104 
105                 case ManagedBean.INIT_MODE_MAP:
106                     if (!(bean instanceof Map))
107                     {
108                         throw new IllegalArgumentException("Class " + bean.getClass().getName()
109                                 + " of managed bean "
110                                 + beanConfiguration.getManagedBeanName()
111                                 + " is not a Map.");
112                     }
113                     initializeMap(facesContext, beanConfiguration.getMapEntries(), (Map) bean);
114                     break;
115 
116                 case ManagedBean.INIT_MODE_LIST:
117                     if (!(bean instanceof List))
118                     {
119                         throw new IllegalArgumentException("Class " + bean.getClass().getName()
120                                 + " of managed bean "
121                                 + beanConfiguration.getManagedBeanName()
122                                 + " is not a List.");
123                     }
124                     initializeList(facesContext, beanConfiguration.getListEntries(), (List) bean);
125                     break;
126 
127                 case ManagedBean.INIT_MODE_NO_INIT:
128                     // no init values
129                     break;
130 
131                 default:
132                     throw new IllegalStateException("Unknown managed bean type "
133                             + bean.getClass().getName() + " for managed bean "
134                             + beanConfiguration.getManagedBeanName() + '.');
135             }
136             
137             // MYFACES-1761 if implements LifecycleProvider,
138             //PostConstruct was already called, but if implements
139             //LifecycleProvider2, call it now.
140             if (lifecycleProvider instanceof LifecycleProvider2)
141             {
142                 ((LifecycleProvider2)lifecycleProvider).postConstruct(bean);
143             }
144             return bean;
145         }
146         catch (IllegalAccessException e)
147         {
148             throw new FacesException(e);
149         }
150         catch (InvocationTargetException e)
151         {
152             throw new FacesException(e);
153         }
154         catch (NamingException e)
155         {
156             throw new FacesException(e);
157         }
158         catch (ClassNotFoundException e)
159         {
160             throw new FacesException(e);
161         }
162         catch (InstantiationException e)
163         {
164             throw new FacesException(e);
165         }
166 
167     }
168 
169 
170     private void initializeProperties(FacesContext facesContext, Iterator managedProperties, String targetScope, Object bean)
171     {
172         ELResolver elResolver = facesContext.getApplication().getELResolver();
173         ELContext elContext = facesContext.getELContext();
174 
175         while (managedProperties.hasNext())
176         {
177             ManagedProperty property = (ManagedProperty) managedProperties.next();
178             Object value = null;
179 
180             switch (property.getType())
181             {
182                 case ManagedProperty.TYPE_LIST:
183 
184                     // JSF 1.1, 5.3.1.3
185                     // Call the property getter, if it exists.
186                     // If the getter returns null or doesn't exist, create a java.util.ArrayList,
187                     // otherwise use the returned Object ...
188                     if (PropertyUtils.isReadable(bean, property.getPropertyName()))
189                         value = elResolver.getValue(elContext, bean, property.getPropertyName());
190                     value = value == null ? new ArrayList() : value;
191 
192                     if (value instanceof List)
193                     {
194                         initializeList(facesContext, property.getListEntries(), (List) value);
195 
196                     }
197                     else if (value != null && value.getClass().isArray())
198                     {
199                         int length = Array.getLength(value);
200                         ArrayList temp = new ArrayList(length);
201                         for (int i = 0; i < length; i++)
202                         {
203                             temp.add(Array.get(value, i));
204                         }
205                         initializeList(facesContext, property.getListEntries(), temp);
206                         value = Array.newInstance(value.getClass().getComponentType(), temp.size());
207                         length = temp.size();
208 
209                         for (int i = 0; i < length; i++)
210                         {
211                             Array.set(value, i, temp.get(i));
212                         }
213                     }
214                     else
215                     {
216                         value = new ArrayList();
217                         initializeList(facesContext, property.getListEntries(), (List) value);
218                     }
219 
220                     break;
221                 case ManagedProperty.TYPE_MAP:
222 
223                     // JSF 1.1, 5.3.1.3
224                     // Call the property getter, if it exists.
225                     // If the getter returns null or doesn't exist, create a java.util.HashMap,
226                     // otherwise use the returned java.util.Map .
227                     if (PropertyUtils.isReadable(bean, property.getPropertyName()))
228                         value = elResolver.getValue(elContext, bean, property.getPropertyName());
229                     value = value == null ? new HashMap() : value;
230 
231                     if (!(value instanceof Map))
232                     {
233                         value = new HashMap();
234                     }
235 
236                     initializeMap(facesContext, property.getMapEntries(), (Map) value);
237                     break;
238                 case ManagedProperty.TYPE_NULL:
239                     value = null;
240                     break;
241                 case ManagedProperty.TYPE_VALUE:
242                     // check for correct scope of a referenced bean
243                     if (!isInValidScope(facesContext, property, targetScope))
244                     {
245                         throw new FacesException("Property " + property.getPropertyName() +
246                                 " references object in a scope with shorter lifetime than the target scope " + targetScope);
247                     }
248                     value = property.getRuntimeValue(facesContext);
249                     break;
250             }
251             Class propertyClass = null;
252 
253             if (property.getPropertyClass() == null)
254             {
255                 propertyClass = elResolver
256                         .getType(elContext, bean, property.getPropertyName());
257             }
258             else
259             {
260                 propertyClass = ClassUtils
261                         .simpleJavaTypeToClass(property.getPropertyClass());
262             }
263             if (null == propertyClass)
264             {
265                 throw new IllegalArgumentException("unable to find the type of property " + property.getPropertyName());
266             }
267             Object coercedValue = coerceToType(facesContext, value, propertyClass);
268             elResolver.setValue(
269                     elContext, bean, property.getPropertyName(), coercedValue);
270         }
271     }
272 
273     // We no longer use the convertToType from shared impl because we switched
274     // to unified EL in JSF 1.2
275     public static Object coerceToType(FacesContext facesContext, Object value, Class desiredClass)
276     {
277         if (value == null) return null;
278 
279         try
280         {
281             ExpressionFactory expFactory = facesContext.getApplication().getExpressionFactory();
282             // Use coersion implemented by JSP EL for consistency with EL
283             // expressions. Additionally, it caches some of the coersions.
284             return expFactory.coerceToType(value, desiredClass);
285         }
286         catch (ELException e)
287         {
288             String message = "Cannot coerce " + value.getClass().getName()
289                     + " to " + desiredClass.getName();
290             log.error(message, e);
291             throw new FacesException(message, e);
292         }
293     }
294 
295 
296     /**
297      * Check if the scope of the property value is valid for a bean to be stored in targetScope.
298      *
299      * @param facesContext
300      * @param property     the property to be checked
301      * @param targetScope  name of the target scope of the bean under construction
302      */
303     private boolean isInValidScope(FacesContext facesContext, ManagedProperty property, String targetScope)
304     {
305         if (!property.isValueReference())
306         {
307             // no value reference but a literal value -> nothing to check
308             return true;
309         }
310         String[] expressions = extractExpressions(property.getValueBinding(facesContext).getExpressionString());
311 
312         for (int i = 0; i < expressions.length; i++)
313         {
314             String expression = expressions[i];
315             if (expression == null)
316             {
317                 continue;
318             }
319 
320             String valueScope = getScope(facesContext, expression);
321 
322             // if the target scope is 'none' value scope has to be 'none', too
323             if (targetScope == null || targetScope.equalsIgnoreCase(NONE))
324             {
325                 if (valueScope != null && !(valueScope.equalsIgnoreCase(NONE)))
326                 {
327                     return false;
328                 }
329                 return true;
330             }
331 
332             // 'application' scope can reference 'application' and 'none'
333             if (targetScope.equalsIgnoreCase(APPLICATION))
334             {
335                 if (valueScope != null)
336                 {
337                     if (valueScope.equalsIgnoreCase(REQUEST) ||
338                             valueScope.equalsIgnoreCase(SESSION))
339                     {
340                         return false;
341                     }
342                 }
343                 return true;
344             }
345 
346             // 'session' scope can reference 'session', 'application', and 'none' but not 'request'
347             if (targetScope.equalsIgnoreCase(SESSION))
348             {
349                 if (valueScope != null)
350                 {
351                     if (valueScope.equalsIgnoreCase(REQUEST))
352                     {
353                         return false;
354                     }
355                 }
356                 return true;
357             }
358 
359             // 'request' scope can reference any value scope
360             if (targetScope.equalsIgnoreCase(REQUEST))
361             {
362                 return true;
363             }
364         }
365         return false;
366     }
367 
368 
369     private String getScope(FacesContext facesContext, String expression)
370     {
371         String beanName = getFirstSegment(expression);
372         ExternalContext externalContext = facesContext.getExternalContext();
373 
374         // check scope objects
375         if (beanName.equalsIgnoreCase("requestScope"))
376         {
377             return REQUEST;
378         }
379         if (beanName.equalsIgnoreCase("sessionScope"))
380         {
381             return SESSION;
382         }
383         if (beanName.equalsIgnoreCase("applicationScope"))
384         {
385             return APPLICATION;
386         }
387 
388         // check implicit objects
389         if (beanName.equalsIgnoreCase("cookie"))
390         {
391             return REQUEST;
392         }
393         if (beanName.equalsIgnoreCase("facesContext"))
394         {
395             return REQUEST;
396         }
397 
398         if (beanName.equalsIgnoreCase("header"))
399         {
400             return REQUEST;
401         }
402         if (beanName.equalsIgnoreCase("headerValues"))
403         {
404             return REQUEST;
405         }
406 
407         if (beanName.equalsIgnoreCase("initParam"))
408         {
409             return APPLICATION;
410         }
411         if (beanName.equalsIgnoreCase("param"))
412         {
413             return REQUEST;
414         }
415         if (beanName.equalsIgnoreCase("paramValues"))
416         {
417             return REQUEST;
418         }
419         if (beanName.equalsIgnoreCase("view"))
420         {
421             return REQUEST;
422         }
423 
424         // not found so far - check all scopes
425         if (externalContext.getRequestMap().get(beanName) != null)
426         {
427             return REQUEST;
428         }
429         if (externalContext.getSessionMap().get(beanName) != null)
430         {
431             return SESSION;
432         }
433         if (externalContext.getApplicationMap().get(beanName) != null)
434         {
435             return APPLICATION;
436         }
437 
438         //not found - check mangaged bean config
439 
440         ManagedBean mbc = getRuntimeConfig(facesContext).getManagedBean(beanName);
441 
442         if (mbc != null)
443         {
444             return mbc.getManagedBeanScope();
445         }
446 
447         return null;
448     }
449 
450     /**
451      * Extract the first expression segment, that is the substring up to the first '.' or '['
452      *
453      * @param expression
454      * @return first segment of the expression
455      */
456     private String getFirstSegment(String expression)
457     {
458         int indexDot = expression.indexOf('.');
459         int indexBracket = expression.indexOf('[');
460 
461         if (indexBracket < 0)
462         {
463 
464             return indexDot < 0 ? expression : expression.substring(0, indexDot);
465 
466         }
467 
468         if (indexDot < 0)
469         {
470             return expression.substring(0, indexBracket);
471         }
472 
473         return expression.substring(0, Math.min(indexDot, indexBracket));
474 
475     }
476 
477     private String[] extractExpressions(String expressionString)
478     {
479         String[] expressions = expressionString.split("\\#\\{");
480         for (int i = 0; i < expressions.length; i++)
481         {
482             String expression = expressions[i];
483             if (expression.trim().length() == 0)
484             {
485                 expressions[i] = null;
486             }
487             else
488             {
489                 int index = expression.indexOf('}');
490                 expressions[i] = expression.substring(0, index);
491             }
492         }
493         return expressions;
494     }
495 
496 
497     private void initializeMap(FacesContext facesContext, MapEntries mapEntries, Map map)
498     {
499         Application application = facesContext.getApplication();
500         Class keyClass = (mapEntries.getKeyClass() == null)
501                 ? String.class : ClassUtils.simpleJavaTypeToClass(mapEntries.getKeyClass());
502         Class valueClass = (mapEntries.getValueClass() == null)
503                 ? String.class : ClassUtils.simpleJavaTypeToClass(mapEntries.getValueClass());
504         ValueExpression valueExpression;
505         ExpressionFactory expFactory = application.getExpressionFactory();
506         ELContext elContext = facesContext.getELContext();
507 
508         for (Iterator iterator = mapEntries.getMapEntries(); iterator.hasNext();)
509         {
510             MapEntry entry = (MapEntry) iterator.next();
511             Object key = entry.getKey();
512 
513             if (ContainerUtils.isValueReference((String) key))
514             {
515                 valueExpression = expFactory.createValueExpression(elContext, (String) key, Object.class);
516                 key = valueExpression.getValue(elContext);
517             }
518 
519             if (entry.isNullValue())
520             {
521                 map.put(coerceToType(facesContext, key, keyClass), null);
522             }
523             else
524             {
525                 Object value = entry.getValue();
526                 if (ContainerUtils.isValueReference((String) value))
527                 {
528                     valueExpression = expFactory.createValueExpression(elContext, (String) value, Object.class);
529                     value = valueExpression.getValue(elContext);
530                 }
531                 map.put(coerceToType(facesContext, key, keyClass), coerceToType(facesContext, value, valueClass));
532             }
533         }
534     }
535 
536 
537     private void initializeList(FacesContext facesContext, ListEntries listEntries, List list)
538     {
539         Application application = facesContext.getApplication();
540         Class valueClass = listEntries.getValueClass() == null ? String.class : ClassUtils.simpleJavaTypeToClass(listEntries.getValueClass());
541         ExpressionFactory expFactory = application.getExpressionFactory();
542         ELContext elContext = facesContext.getELContext();
543 
544         for (Iterator iterator = listEntries.getListEntries(); iterator.hasNext();)
545         {
546             ListEntry entry = (ListEntry) iterator.next();
547             if (entry.isNullValue())
548             {
549                 list.add(null);
550             }
551             else
552             {
553                 Object value = entry.getValue();
554                 if (ContainerUtils.isValueReference((String) value))
555                 {
556                     ValueExpression valueExpression = expFactory.createValueExpression(elContext, (String) value, Object.class);
557                     value = valueExpression.getValue(elContext);
558                 }
559                 list.add(coerceToType(facesContext, value, valueClass));
560             }
561         }
562     }
563 
564     private RuntimeConfig getRuntimeConfig(FacesContext facesContext)
565     {
566         if (_runtimeConfig == null)
567         {
568             _runtimeConfig = RuntimeConfig.getCurrentInstance(facesContext.getExternalContext());
569         }
570         return _runtimeConfig;
571     }
572 }