001: /*
002: * Copyright 2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.myfaces.config;
017:
018: import java.lang.reflect.Array;
019: import java.lang.reflect.InvocationTargetException;
020: import java.util.ArrayList;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025:
026: import javax.el.ELContext;
027: import javax.el.ELException;
028: import javax.el.ELResolver;
029: import javax.el.ExpressionFactory;
030: import javax.el.ValueExpression;
031: import javax.faces.FacesException;
032: import javax.faces.application.Application;
033: import javax.faces.context.ExternalContext;
034: import javax.faces.context.FacesContext;
035: import javax.faces.webapp.UIComponentTag;
036: import javax.naming.NamingException;
037:
038: import org.apache.commons.beanutils.PropertyUtils;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041: import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
042: import org.apache.myfaces.config.annotation.LifecycleProvider;
043: import org.apache.myfaces.config.element.ListEntries;
044: import org.apache.myfaces.config.element.ListEntry;
045: import org.apache.myfaces.config.element.ManagedBean;
046: import org.apache.myfaces.config.element.ManagedProperty;
047: import org.apache.myfaces.config.element.MapEntries;
048: import org.apache.myfaces.config.element.MapEntry;
049: import org.apache.myfaces.shared_impl.util.ClassUtils;
050:
051: /**
052: * Create and initialize managed beans
053: *
054: * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller</a> (latest modification by $Author: bommel $)
055: * @author Anton Koinov
056: */
057: public class ManagedBeanBuilder {
058: private static Log log = LogFactory
059: .getLog(ManagedBeanBuilder.class);
060: private RuntimeConfig _runtimeConfig;
061: public final static String REQUEST = "request";
062: public final static String APPLICATION = "application";
063: public final static String SESSION = "session";
064: public final static String NONE = "none";
065:
066: public Object buildManagedBean(FacesContext facesContext,
067: ManagedBean beanConfiguration) throws FacesException {
068:
069: /*final AnnotatedManagedBeanHandler handler = new AnnotatedManagedBeanHandler(bean,
070: beanConfiguration.getManagedBeanScope(), beanConfiguration.getManagedBeanName());
071:
072: final boolean threwUnchecked = handler.invokePostConstruct();
073:
074: if(threwUnchecked)
075: return null;*/
076: try {
077: LifecycleProvider lifecycleProvider = LifecycleProviderFactory
078: .getLifecycleProviderFactory()
079: .getLifecycleProvider(
080: facesContext.getExternalContext());
081: final Object bean = lifecycleProvider
082: .newInstance(beanConfiguration
083: .getManagedBeanClassName());
084:
085: switch (beanConfiguration.getInitMode()) {
086: case ManagedBean.INIT_MODE_PROPERTIES:
087: try {
088: initializeProperties(facesContext,
089: beanConfiguration.getManagedProperties(),
090: beanConfiguration.getManagedBeanScope(),
091: bean);
092: } catch (IllegalArgumentException e) {
093: throw new IllegalArgumentException(
094: e.getMessage()
095: + " for bean '"
096: + beanConfiguration
097: .getManagedBeanName()
098: + "' check the configuration to make sure all properties correspond with get/set methods",
099: e);
100: }
101: break;
102:
103: case ManagedBean.INIT_MODE_MAP:
104: if (!(bean instanceof Map)) {
105: throw new IllegalArgumentException("Class "
106: + bean.getClass().getName()
107: + " of managed bean "
108: + beanConfiguration.getManagedBeanName()
109: + " is not a Map.");
110: }
111: initializeMap(facesContext, beanConfiguration
112: .getMapEntries(), (Map) bean);
113: break;
114:
115: case ManagedBean.INIT_MODE_LIST:
116: if (!(bean instanceof List)) {
117: throw new IllegalArgumentException("Class "
118: + bean.getClass().getName()
119: + " of managed bean "
120: + beanConfiguration.getManagedBeanName()
121: + " is not a List.");
122: }
123: initializeList(facesContext, beanConfiguration
124: .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(
133: "Unknown managed bean type "
134: + bean.getClass().getName()
135: + " for managed bean "
136: + beanConfiguration
137: .getManagedBeanName() + '.');
138: }
139: return bean;
140: } catch (IllegalAccessException e) {
141: throw new FacesException(e);
142: } catch (InvocationTargetException e) {
143: throw new FacesException(e);
144: } catch (NamingException e) {
145: throw new FacesException(e);
146: } catch (ClassNotFoundException e) {
147: throw new FacesException(e);
148: } catch (InstantiationException e) {
149: throw new FacesException(e);
150: }
151:
152: }
153:
154: private void initializeProperties(FacesContext facesContext,
155: Iterator managedProperties, String targetScope, Object bean) {
156: ELResolver elResolver = facesContext.getApplication()
157: .getELResolver();
158: ELContext elContext = facesContext.getELContext();
159:
160: while (managedProperties.hasNext()) {
161: ManagedProperty property = (ManagedProperty) managedProperties
162: .next();
163: Object value = null;
164:
165: switch (property.getType()) {
166: case ManagedProperty.TYPE_LIST:
167:
168: // JSF 1.1, 5.3.1.3
169: // Call the property getter, if it exists.
170: // If the getter returns null or doesn't exist, create a java.util.ArrayList,
171: // otherwise use the returned Object ...
172: if (PropertyUtils.isReadable(bean, property
173: .getPropertyName()))
174: value = elResolver.getValue(elContext, bean,
175: property.getPropertyName());
176: value = value == null ? new ArrayList() : value;
177:
178: if (value instanceof List) {
179: initializeList(facesContext, property
180: .getListEntries(), (List) value);
181:
182: } else if (value != null && value.getClass().isArray()) {
183: int length = Array.getLength(value);
184: ArrayList temp = new ArrayList(length);
185: for (int i = 0; i < length; i++) {
186: temp.add(Array.get(value, i));
187: }
188: initializeList(facesContext, property
189: .getListEntries(), temp);
190: value = Array.newInstance(value.getClass()
191: .getComponentType(), temp.size());
192: length = temp.size();
193:
194: for (int i = 0; i < length; i++) {
195: Array.set(value, i, temp.get(i));
196: }
197: } else {
198: value = new ArrayList();
199: initializeList(facesContext, property
200: .getListEntries(), (List) value);
201: }
202:
203: break;
204: case ManagedProperty.TYPE_MAP:
205:
206: // JSF 1.1, 5.3.1.3
207: // Call the property getter, if it exists.
208: // If the getter returns null or doesn't exist, create a java.util.HashMap,
209: // otherwise use the returned java.util.Map .
210: if (PropertyUtils.isReadable(bean, property
211: .getPropertyName()))
212: value = elResolver.getValue(elContext, bean,
213: property.getPropertyName());
214: value = value == null ? new HashMap() : value;
215:
216: if (!(value instanceof Map)) {
217: value = new HashMap();
218: }
219:
220: initializeMap(facesContext, property.getMapEntries(),
221: (Map) value);
222: break;
223: case ManagedProperty.TYPE_NULL:
224: value = null;
225: break;
226: case ManagedProperty.TYPE_VALUE:
227: // check for correct scope of a referenced bean
228: if (!isInValidScope(facesContext, property, targetScope)) {
229: throw new FacesException(
230: "Property "
231: + property.getPropertyName()
232: + " references object in a scope with shorter lifetime than the target scope "
233: + targetScope);
234: }
235: value = property.getRuntimeValue(facesContext);
236: break;
237: }
238: Class propertyClass = null;
239:
240: if (property.getPropertyClass() == null) {
241: propertyClass = elResolver.getType(elContext, bean,
242: property.getPropertyName());
243: } else {
244: propertyClass = ClassUtils
245: .simpleJavaTypeToClass(property
246: .getPropertyClass());
247: }
248: if (null == propertyClass) {
249: throw new IllegalArgumentException(
250: "unable to find the type of property "
251: + property.getPropertyName());
252: }
253: Object coercedValue = coerceToType(facesContext, value,
254: propertyClass);
255: elResolver.setValue(elContext, bean, property
256: .getPropertyName(), coercedValue);
257: }
258: }
259:
260: // We no longer use the convertToType from shared impl because we switched
261: // to unified EL in JSF 1.2
262: public static Object coerceToType(FacesContext facesContext,
263: Object value, Class desiredClass) {
264: if (value == null)
265: return null;
266:
267: try {
268: ExpressionFactory expFactory = facesContext
269: .getApplication().getExpressionFactory();
270: // Use coersion implemented by JSP EL for consistency with EL
271: // expressions. Additionally, it caches some of the coersions.
272: return expFactory.coerceToType(value, desiredClass);
273: } catch (ELException e) {
274: String message = "Cannot coerce "
275: + value.getClass().getName() + " to "
276: + desiredClass.getName();
277: log.error(message, e);
278: throw new FacesException(message, e);
279: }
280: }
281:
282: /**
283: * Check if the scope of the property value is valid for a bean to be stored in targetScope.
284: *
285: * @param facesContext
286: * @param property the property to be checked
287: * @param targetScope name of the target scope of the bean under construction
288: */
289: private boolean isInValidScope(FacesContext facesContext,
290: ManagedProperty property, String targetScope) {
291: if (!property.isValueReference()) {
292: // no value reference but a literal value -> nothing to check
293: return true;
294: }
295: String[] expressions = extractExpressions(property
296: .getValueBinding(facesContext).getExpressionString());
297:
298: for (int i = 0; i < expressions.length; i++) {
299: String expression = expressions[i];
300: if (expression == null) {
301: continue;
302: }
303:
304: String valueScope = getScope(facesContext, expression);
305:
306: // if the target scope is 'none' value scope has to be 'none', too
307: if (targetScope == null
308: || targetScope.equalsIgnoreCase(NONE)) {
309: if (valueScope != null
310: && !(valueScope.equalsIgnoreCase(NONE))) {
311: return false;
312: }
313: return true;
314: }
315:
316: // 'application' scope can reference 'application' and 'none'
317: if (targetScope.equalsIgnoreCase(APPLICATION)) {
318: if (valueScope != null) {
319: if (valueScope.equalsIgnoreCase(REQUEST)
320: || valueScope.equalsIgnoreCase(SESSION)) {
321: return false;
322: }
323: }
324: return true;
325: }
326:
327: // 'session' scope can reference 'session', 'application', and 'none' but not 'request'
328: if (targetScope.equalsIgnoreCase(SESSION)) {
329: if (valueScope != null) {
330: if (valueScope.equalsIgnoreCase(REQUEST)) {
331: return false;
332: }
333: }
334: return true;
335: }
336:
337: // 'request' scope can reference any value scope
338: if (targetScope.equalsIgnoreCase(REQUEST)) {
339: return true;
340: }
341: }
342: return false;
343: }
344:
345: private String getScope(FacesContext facesContext, String expression) {
346: String beanName = getFirstSegment(expression);
347: ExternalContext externalContext = facesContext
348: .getExternalContext();
349:
350: // check scope objects
351: if (beanName.equalsIgnoreCase("requestScope")) {
352: return REQUEST;
353: }
354: if (beanName.equalsIgnoreCase("sessionScope")) {
355: return SESSION;
356: }
357: if (beanName.equalsIgnoreCase("applicationScope")) {
358: return APPLICATION;
359: }
360:
361: // check implicit objects
362: if (beanName.equalsIgnoreCase("cookie")) {
363: return REQUEST;
364: }
365: if (beanName.equalsIgnoreCase("facesContext")) {
366: return REQUEST;
367: }
368:
369: if (beanName.equalsIgnoreCase("header")) {
370: return REQUEST;
371: }
372: if (beanName.equalsIgnoreCase("headerValues")) {
373: return REQUEST;
374: }
375:
376: if (beanName.equalsIgnoreCase("initParam")) {
377: return APPLICATION;
378: }
379: if (beanName.equalsIgnoreCase("param")) {
380: return REQUEST;
381: }
382: if (beanName.equalsIgnoreCase("paramValues")) {
383: return REQUEST;
384: }
385: if (beanName.equalsIgnoreCase("view")) {
386: return REQUEST;
387: }
388:
389: // not found so far - check all scopes
390: if (externalContext.getRequestMap().get(beanName) != null) {
391: return REQUEST;
392: }
393: if (externalContext.getSessionMap().get(beanName) != null) {
394: return SESSION;
395: }
396: if (externalContext.getApplicationMap().get(beanName) != null) {
397: return APPLICATION;
398: }
399:
400: //not found - check mangaged bean config
401:
402: ManagedBean mbc = getRuntimeConfig(facesContext)
403: .getManagedBean(beanName);
404:
405: if (mbc != null) {
406: return mbc.getManagedBeanScope();
407: }
408:
409: return null;
410: }
411:
412: /**
413: * Extract the first expression segment, that is the substring up to the first '.' or '['
414: *
415: * @param expression
416: * @return first segment of the expression
417: */
418: private String getFirstSegment(String expression) {
419: int indexDot = expression.indexOf('.');
420: int indexBracket = expression.indexOf('[');
421:
422: if (indexBracket < 0) {
423:
424: return indexDot < 0 ? expression : expression.substring(0,
425: indexDot);
426:
427: }
428:
429: if (indexDot < 0) {
430: return expression.substring(0, indexBracket);
431: }
432:
433: return expression
434: .substring(0, Math.min(indexDot, indexBracket));
435:
436: }
437:
438: private String[] extractExpressions(String expressionString) {
439: String[] expressions = expressionString.split("\\#\\{");
440: for (int i = 0; i < expressions.length; i++) {
441: String expression = expressions[i];
442: if (expression.trim().length() == 0) {
443: expressions[i] = null;
444: } else {
445: int index = expression.indexOf('}');
446: expressions[i] = expression.substring(0, index);
447: }
448: }
449: return expressions;
450: }
451:
452: private void initializeMap(FacesContext facesContext,
453: MapEntries mapEntries, Map map) {
454: Application application = facesContext.getApplication();
455: Class keyClass = (mapEntries.getKeyClass() == null) ? String.class
456: : ClassUtils.simpleJavaTypeToClass(mapEntries
457: .getKeyClass());
458: Class valueClass = (mapEntries.getValueClass() == null) ? String.class
459: : ClassUtils.simpleJavaTypeToClass(mapEntries
460: .getValueClass());
461: ValueExpression valueExpression;
462: ExpressionFactory expFactory = application
463: .getExpressionFactory();
464: ELContext elContext = facesContext.getELContext();
465:
466: for (Iterator iterator = mapEntries.getMapEntries(); iterator
467: .hasNext();) {
468: MapEntry entry = (MapEntry) iterator.next();
469: Object key = entry.getKey();
470:
471: if (UIComponentTag.isValueReference((String) key)) {
472: valueExpression = expFactory.createValueExpression(
473: elContext, (String) key, Object.class);
474: key = valueExpression.getValue(elContext);
475: }
476:
477: if (entry.isNullValue()) {
478: map
479: .put(coerceToType(facesContext, key, keyClass),
480: null);
481: } else {
482: Object value = entry.getValue();
483: if (UIComponentTag.isValueReference((String) value)) {
484: valueExpression = expFactory.createValueExpression(
485: elContext, (String) value, Object.class);
486: value = valueExpression.getValue(elContext);
487: }
488: map.put(coerceToType(facesContext, key, keyClass),
489: coerceToType(facesContext, value, valueClass));
490: }
491: }
492: }
493:
494: private void initializeList(FacesContext facesContext,
495: ListEntries listEntries, List list) {
496: Application application = facesContext.getApplication();
497: Class valueClass = listEntries.getValueClass() == null ? String.class
498: : ClassUtils.simpleJavaTypeToClass(listEntries
499: .getValueClass());
500: ExpressionFactory expFactory = application
501: .getExpressionFactory();
502: ELContext elContext = facesContext.getELContext();
503:
504: for (Iterator iterator = listEntries.getListEntries(); iterator
505: .hasNext();) {
506: ListEntry entry = (ListEntry) iterator.next();
507: if (entry.isNullValue()) {
508: list.add(null);
509: } else {
510: Object value = entry.getValue();
511: if (UIComponentTag.isValueReference((String) value)) {
512: ValueExpression valueExpression = expFactory
513: .createValueExpression(elContext,
514: (String) value, Object.class);
515: value = valueExpression.getValue(elContext);
516: }
517: list.add(coerceToType(facesContext, value, valueClass));
518: }
519: }
520: }
521:
522: private RuntimeConfig getRuntimeConfig(FacesContext facesContext) {
523: if (_runtimeConfig == null) {
524: _runtimeConfig = RuntimeConfig
525: .getCurrentInstance(facesContext
526: .getExternalContext());
527: }
528: return _runtimeConfig;
529: }
530: }
|