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