2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shale.tiger.faces;
19
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import javax.faces.context.ExternalContext;
25 import javax.faces.context.FacesContext;
26 import javax.faces.el.EvaluationException;
27 import javax.faces.el.PropertyNotFoundException;
28 import javax.faces.el.ValueBinding;
29 import javax.faces.el.VariableResolver;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.shale.tiger.config.FacesConfigConfig;
33 import org.apache.shale.tiger.managed.Bean;
34 import org.apache.shale.tiger.managed.Value;
35 import org.apache.shale.tiger.managed.config.ListEntriesConfig;
36 import org.apache.shale.tiger.managed.config.ListEntryConfig;
37 import org.apache.shale.tiger.managed.config.ManagedBeanConfig;
38 import org.apache.shale.tiger.managed.config.ManagedPropertyConfig;
39 import org.apache.shale.tiger.managed.config.MapEntriesConfig;
40 import org.apache.shale.tiger.managed.config.MapEntryConfig;
41 import org.apache.shale.tiger.view.faces.LifecycleListener2;
42 import org.apache.shale.util.ConverterHelper;
43 import org.apache.shale.util.Messages;
44 import org.apache.shale.util.PropertyHelper;
45
46 /***
47 * <p>Implementation of <code>VariableResolver</code> that delegates
48 * to the original handler under these circumstances:</p>
49 * <ul>
50 * <li>There is bean already in existence with the specified name.</li>
51 * <li>There is no managed beans definition for the specified name.</li>
52 * </ul>
53 *
54 * <p>If control is not delegated, implement the standard functionality
55 * for creating managed beans (this is necessary because JSF does not
56 * expose any direct API to access these capabilities), with the following
57 * additions:</p>
58 * <ul>
59 * <li>If the specified managed bean class includes the
60 * {@link Bean} annotation, the type and scope properties
61 * will have been preconfigured from the corresponding
62 * attribute values. (These default settings can be
63 * overridden by specifying an actual managed bean element
64 * in a <code>faces-config.xml</code> resource.)</li>
65 * <li>If a field of the specified bean class includes the
66 * {@link Value} annotation, the property initialization
67 * will be preconfigured to the literal or expression
68 * specified by the <code>value</code> attribute of the
69 * annotation. (These default settings can be overridden
70 * by specifiying an actual managed bean element, and
71 * including a property definition that sets the value.</li>
72 * </ul>
73 *
74 * <p>FIXME - Incomplete implemetnation of standard managed beans
75 * functionality in the following areas:</p>
76 * <ul>
77 * <li>Partial support for list entries on managed beans and managed properties.
78 * It currently works for lists, but not for arrays.</li>
79 * </ul>
80 *
81 * <p><strong>IMPLEMENTATION NOTE</strong> - There is no <code>faces-config.xml</code>
82 * resource that registers this variable resolver, since we could end up with
83 * ordering issues with the standard Shale variable resolver. Therefore, the
84 * standard implementation will dynamically insert an instance of this
85 * resolver (below the standard instance, but above the implementation
86 * provided instance) in the standard decorator chain that is implemented
87 * as the JavaServer Faces runtime parses configuration resources.</p>
88 */
89 public class VariableResolverImpl extends VariableResolver {
90
91 /***
92 * <p>Create a new <code>VariableResolverImpl</code> wrapping the
93 * specified <code>VariableResolver</code>.</p>
94 *
95 * @param original Original <code>VariableResolver</code> to wrap
96 */
97 public VariableResolverImpl(VariableResolver original) {
98
99 this.original = original;
100
101 if (log().isInfoEnabled()) {
102 log().info(messages().getMessage("variable.resolver",
103 new Object[] { original }));
104 }
105
106 }
107
108
109
110
111 /***
112 * <p>Helper bean for performing conversions.</p>
113 */
114 private ConverterHelper convHelper = new ConverterHelper();
115
116
117 /***
118 * <p>Log instance for this class.</p>
119 */
120 private transient Log log = null;
121
122
123 /***
124 * <p><code>Messages</code> instance for this class.</p>
125 */
126 private transient Messages messages = null;
127
128
129 /***
130 * <p>The original <code>VariableResolver</code> to which we should
131 * delegate when necessary.</p>
132 */
133 private VariableResolver original = null;
134
135
136 /***
137 * <p>Helper bean for accessing properties.</p>
138 */
139 private PropertyHelper propHelper = new PropertyHelper();
140
141
142
143
144
145
146 /***
147 * <p>Resolve the specified variable name, creating and initializing
148 * a new managed bean if necessary.</p>
149 *
150 * @param context <code>FacesContext</code> used to resolve variables
151 * @param name Name of the variable to be resolved
152 *
153 * @exception EvaluationException if an evaluation error occurs
154 */
155 public Object resolveVariable(FacesContext context,
156 String name) throws EvaluationException {
157
158 if (log().isDebugEnabled()) {
159 log().debug("resolveVariable(" + name + ")");
160 }
161
162
163 Object value = context.getExternalContext().getRequestMap().get(name);
164 if (value == null) {
165 value = context.getExternalContext().getSessionMap().get(name);
166 }
167 if (value == null) {
168 value = context.getExternalContext().getApplicationMap().get(name);
169 }
170 if (value != null) {
171 if (log().isTraceEnabled()) {
172 log().trace("resolveVariable(" + name + ") --> existing bean, so delegate");
173 }
174 return original.resolveVariable(context, name);
175 }
176
177
178 FacesConfigConfig config = config(context);
179 if (config == null) {
180 if (log().isTraceEnabled()) {
181 log().trace("resolveVariable(" + name + ") --> no FacesConfigConfig, so delegate");
182 }
183 return original.resolveVariable(context, name);
184 }
185 ManagedBeanConfig mb = config.getManagedBean(name);
186 if (mb == null) {
187 if (log().isTraceEnabled()) {
188 log().trace("resolveVariable(" + name + ") --> no ManagedBeanConfig, so delegate");
189 }
190 return original.resolveVariable(context, name);
191 }
192
193
194 Object created = create(context, mb);
195 return created;
196
197 }
198
199
200
201
202
203 /***
204 * <p>The {@link FacesConfigConfig} entry containing our managed bean
205 * definitions, lazily instantiated.</p>
206 */
207 private FacesConfigConfig config = null;
208
209
210 /***
211 * <p>Return the {@link FacesConfigConfig} bean describing the
212 * configuration information for this applicaiton, if any.</p>
213 *
214 * @param context FacesContext for the current request
215 */
216 private FacesConfigConfig config(FacesContext context) {
217
218 if (config == null) {
219 config = (FacesConfigConfig)
220 context.getExternalContext().getApplicationMap().
221 get(LifecycleListener2.FACES_CONFIG_CONFIG);
222 }
223 return config;
224
225 }
226
227
228 /***
229 * <p>Create, configure, and return a new instance based on the
230 * specified managed bean, after storing it in the configured
231 * scope (if any).</p>
232 *
233 * @param context <code>FacesContext</code> for the current request
234 * @param mb ManagedBeanConfig describing the bean to be created
235 *
236 * @exception EvaluationException if an evaluation error occurs
237 */
238 private Object create(FacesContext context, ManagedBeanConfig mb)
239 throws EvaluationException {
240
241 if (log().isDebugEnabled()) {
242 log().debug("create(" + mb.getName() + ")");
243 }
244
245
246 Object instance = instance(context, mb);
247
248
249 for (ManagedPropertyConfig mp : mb.getProperties().values()) {
250 property(context, mb, mp, instance);
251 }
252
253
254 ListEntriesConfig listEntries = mb.getListEntries();
255 if (listEntries != null) {
256
257 if (!List.class.isAssignableFrom(instance.getClass())) {
258 throw new EvaluationException(messages().
259 getMessage("list.list",
260 context.getViewRoot().getLocale(),
261 new Object[] { instance.getClass().getName() }));
262 }
263 list(context, listEntries, (List) instance);
264 }
265
266
267 MapEntriesConfig mapEntries = mb.getMapEntries();
268 if (mapEntries != null) {
269 if (!Map.class.isAssignableFrom(instance.getClass())) {
270 throw new EvaluationException(messages().
271 getMessage("map.map",
272 context.getViewRoot().getLocale(),
273 new Object[] { instance.getClass().getName() }));
274 }
275 map(context, mapEntries, (Map) instance);
276 }
277
278
279 scope(context, mb, instance);
280
281 return instance;
282
283 }
284
285
286 /***
287 * <p>Create and return an instance of the class named by the
288 * specified managed bean configuration bean.
289 *
290 * @param context <code>FacesContext</code> for the current request
291 * @param mb {@link ManagedBeanConfig} defining the bean to create
292 *
293 * @exception EvaluationException if an evaluation error occurs
294 */
295 private Object instance(FacesContext context, ManagedBeanConfig mb)
296 throws EvaluationException {
297
298
299 if (mb.getType() == null) {
300 throw new EvaluationException(messages().
301 getMessage("variable.type",
302 context.getViewRoot().getLocale(),
303 new Object[] { mb.getName() }));
304 }
305
306
307 ClassLoader cl = Thread.currentThread().getContextClassLoader();
308 Class clazz = null;
309 Object instance = null;
310 try {
311 clazz = cl.loadClass(mb.getType());
312 instance = clazz.newInstance();
313 } catch (ClassNotFoundException e) {
314 throw new EvaluationException(messages().
315 getMessage("variable.class",
316 context.getViewRoot().getLocale(),
317 new Object[] { mb.getName(), mb.getType() }), e);
318 } catch (IllegalAccessException e) {
319 throw new EvaluationException(messages().
320 getMessage("variable.access",
321 context.getViewRoot().getLocale(),
322 new Object[] { mb.getName(), mb.getType() }), e);
323 } catch (InstantiationException e) {
324 throw new EvaluationException(messages().
325 getMessage("variable.instantiate",
326 context.getViewRoot().getLocale(),
327 new Object[] { mb.getName(), mb.getType() }), e);
328 }
329
330 return instance;
331
332 }
333
334
335 /***
336 * <p>Populate the contents of the specified <code>List</code> from the
337 * specified list entries configuration information.</p>
338 *
339 * @param context <code>FacesContext</code> for the current request
340 * @param config {@link ListEntriesConfig} describing this list
341 * @param list <code>List</code> instance to have entries appended
342 *
343 * @exception EvaluationException if an evaluation error occurs
344 */
345 private void list(FacesContext context, ListEntriesConfig config, List list) {
346
347
348 String valueType = config.getValueType();
349 ClassLoader cl = Thread.currentThread().getContextClassLoader();
350 Class type = null;
351 try {
352 if (valueType != null) {
353 type = cl.loadClass(valueType);
354 }
355 } catch (ClassNotFoundException e) {
356 throw new EvaluationException(messages().
357 getMessage("list.class",
358 context.getViewRoot().getLocale(),
359 new Object[] { valueType }), e);
360 }
361
362
363 for (ListEntryConfig entry : config.getEntries()) {
364 if (entry.isNullValue()) {
365 list.add(null);
366 } else if (entry.isExpression()) {
367
368 ValueBinding vb =
369 context.getApplication().createValueBinding(entry.getValue());
370 list.add(vb.getValue(context));
371 } else {
372 if (type != null) {
373 list.add(convHelper.asObject(context, type, entry.getValue()));
374 } else {
375 list.add(entry.getValue());
376 }
377 }
378 }
379
380 }
381
382
383 /***
384 * <p>Return the <code>Log</code> instance to be used for this class,
385 * instantiating a new one if necessary.</p>
386 */
387 private Log log() {
388
389 if (log == null) {
390 log = LogFactory.getLog(VariableResolverImpl.class);
391 }
392 return log;
393
394 }
395
396
397 /***
398 * <p>Populate the contents of the specified <code>Map</code> from the
399 * specified map entries configuration information.</p>
400 *
401 * @param context <code>FacesContext</code> for the current request
402 * @param config {@link MapEntriesConfig} describing this list
403 * @param map <code>Map</code> instance to have entries appended
404 *
405 * @exception EvaluationException if an evaluation error occurs
406 */
407 private void map(FacesContext context, MapEntriesConfig config, Map map) {
408
409 ClassLoader cl = Thread.currentThread().getContextClassLoader();
410
411
412 String keyType = config.getKeyType();
413 Class keyClass = null;
414 try {
415 if (keyType != null) {
416 keyClass = cl.loadClass(keyType);
417 }
418 } catch (ClassNotFoundException e) {
419 throw new EvaluationException(messages().
420 getMessage("map.keyClass",
421 context.getViewRoot().getLocale(),
422 new Object[] { keyType }), e);
423 }
424
425
426 String valueType = config.getValueType();
427 Class valueClass = null;
428 try {
429 if (valueType != null) {
430 valueClass = cl.loadClass(valueType);
431 }
432 } catch (ClassNotFoundException e) {
433 throw new EvaluationException(messages().
434 getMessage("map.valueClass",
435 context.getViewRoot().getLocale(),
436 new Object[] { valueType }), e);
437 }
438
439
440 for (MapEntryConfig entry : config.getEntries()) {
441 Object key = null;
442 if (keyClass != null) {
443 key = convHelper.asObject(context, keyClass, entry.getKey());
444 } else {
445 key = entry.getKey();
446 }
447 if (entry.isNullValue()) {
448 map.put(key, null);
449 } else if (entry.isExpression()) {
450
451 ValueBinding vb =
452 context.getApplication().createValueBinding(entry.getValue());
453 map.put(key, vb.getValue(context));
454 } else {
455 if (valueClass != null) {
456 map.put(key, convHelper.asObject(context, valueClass, entry.getValue()));
457 } else {
458 map.put(key, entry.getValue());
459 }
460 }
461 }
462
463 }
464
465
466 /***
467 * <p>Return the <code>Messages</code> instance to be used for this class,
468 * instantiating a new one if necessary.</p>
469 */
470 private Messages messages() {
471
472 if (messages == null) {
473 messages = new Messages("org.apache.shale.tiger.faces.Bundle",
474 Thread.currentThread().getContextClassLoader());
475 }
476 return messages;
477
478 }
479
480
481 /***
482 * <p>Configure the specified property of the specified bean instance,
483 * based on information from the specified managed property entry.</p>
484 *
485 * @param context <code>FacesContext</code> for the current request
486 * @param mb {@link ManagedBeanConfig} for the bean being scoped
487 * @param mp {@link ManagedPropertyConfig} for the property being configured
488 * @param instance Bean instance to be potentially placed in scope
489 *
490 * @exception EvaluationException if an evaluation error occurs
491 */
492 private void property(FacesContext context, ManagedBeanConfig mb,
493 ManagedPropertyConfig mp, Object instance)
494 throws EvaluationException {
495
496
497 ListEntriesConfig listEntries = mp.getListEntries();
498 if (listEntries != null) {
499
500
501
502 Object property = null;
503 try {
504 property = propHelper.getValue(instance, mp.getName());
505
506 } catch (PropertyNotFoundException e) {
507 ;
508 } catch (Exception e) {
509 throw new EvaluationException(messages().
510 getMessage("list.get",
511 context.getViewRoot().getLocale(),
512 new Object[] { mp.getName(), mb.getName()}), e);
513 }
514 if (property == null) {
515 property = new ArrayList();
516 }
517
518
519 if (!List.class.isAssignableFrom(property.getClass())) {
520 throw new EvaluationException(messages().
521 getMessage("list.listProperty",
522 context.getViewRoot().getLocale(),
523 new Object[] { mp.getName(), mb.getName(), property.getClass().getName() }));
524 }
525
526
527 list(context, listEntries, (List) property);
528
529
530 try {
531 propHelper.setValue(instance, mp.getName(), property);
532
533 } catch (Exception e) {
534 throw new EvaluationException(messages().
535 getMessage("list.set",
536 context.getViewRoot().getLocale(),
537 new Object[] { mp.getName(), mb.getName()}), e);
538 }
539
540
541 return;
542
543 }
544
545
546 MapEntriesConfig mapEntries = mp.getMapEntries();
547 if (mapEntries != null) {
548
549
550
551 Object property = null;
552 try {
553 property = propHelper.getValue(instance, mp.getName());
554
555 } catch (PropertyNotFoundException e) {
556 ;
557 } catch (Exception e) {
558 throw new EvaluationException(messages().
559 getMessage("map.get",
560 context.getViewRoot().getLocale(),
561 new Object[] { mp.getName(), mb.getName()}), e);
562 }
563 if (property == null) {
564 property = new HashMap();
565 }
566
567 if (!Map.class.isAssignableFrom(property.getClass())) {
568 throw new EvaluationException(messages().
569 getMessage("map.mapProperty",
570 context.getViewRoot().getLocale(),
571 new Object[] { mp.getName(), mb.getName(), property.getClass().getName() }));
572 }
573
574
575 map(context, mapEntries, (Map) property);
576
577
578 try {
579 propHelper.setValue(instance, mp.getName(), property);
580
581 } catch (Exception e) {
582 throw new EvaluationException(messages().
583 getMessage("map.set",
584 context.getViewRoot().getLocale(),
585 new Object[] { mp.getName(), mb.getName()}), e);
586 }
587
588
589 return;
590
591 }
592
593
594 if ((mp.getValue() == null) && !mp.isNullValue()) {
595 return;
596 }
597
598
599 Object value = null;
600 if (!mp.isNullValue()) {
601 if (mp.isExpression()) {
602
603 ValueBinding vb =
604 context.getApplication().createValueBinding(mp.getValue());
605 value = vb.getValue(context);
606 } else {
607
608 value = mp.getValue();
609 }
610 }
611
612
613 try {
614 Class type = propHelper.getType(instance, mp.getName());
615 if ((value != null) && (value instanceof String)) {
616 value = convHelper.asObject(context, type, (String) value);
617 }
618 propHelper.setValue(instance, mp.getName(), value);
619 } catch (Exception e) {
620 throw new EvaluationException(messages().
621 getMessage("variable.evaluate",
622 context.getViewRoot().getLocale(),
623 new Object[] { mb.getName(), mp.getName(), mp.getValue() }), e);
624 }
625
626 }
627
628
629 /***
630 * <p>Install the specified bean instance in the specified scope (if any).
631 * </p>
632 *
633 * @param context <code>FacesContext</code> for the current request
634 * @param mb {@link ManagedBeanConfig} for the bean being scoped
635 * @param instance Bean instance to be potentially placed in scope
636 *
637 * @exception EvaluationException if an evaluation error occurs
638 */
639 private void scope(FacesContext context, ManagedBeanConfig mb, Object instance)
640 throws EvaluationException {
641
642 if (log().isTraceEnabled()) {
643 log().trace("Store bean " + mb.getName() + " in scope " + mb.getScope());
644 }
645
646 ExternalContext econtext = context.getExternalContext();
647 String scope = mb.getScope();
648 if ("request".equalsIgnoreCase(scope)) {
649 econtext.getRequestMap().put(mb.getName(), instance);
650 } else if ("session".equalsIgnoreCase(scope)) {
651 econtext.getSessionMap().put(mb.getName(), instance);
652 } else if ("application".equalsIgnoreCase(scope)) {
653 econtext.getApplicationMap().put(mb.getName(), instance);
654 }
655
656 }
657
658
659 }