001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.tide.cdi;
022
023 import java.lang.annotation.Annotation;
024 import java.lang.reflect.Method;
025 import java.lang.reflect.Type;
026 import java.math.BigDecimal;
027 import java.math.BigInteger;
028 import java.util.ArrayList;
029 import java.util.Arrays;
030 import java.util.HashSet;
031 import java.util.List;
032 import java.util.Map;
033 import java.util.Set;
034
035 import javax.annotation.PreDestroy;
036 import javax.enterprise.context.ContextNotActiveException;
037 import javax.enterprise.context.ConversationScoped;
038 import javax.enterprise.context.RequestScoped;
039 import javax.enterprise.context.SessionScoped;
040 import javax.enterprise.context.spi.CreationalContext;
041 import javax.enterprise.inject.Any;
042 import javax.enterprise.inject.Default;
043 import javax.enterprise.inject.IllegalProductException;
044 import javax.enterprise.inject.spi.Bean;
045 import javax.enterprise.inject.spi.BeanManager;
046 import javax.enterprise.util.AnnotationLiteral;
047 import javax.inject.Inject;
048 import javax.persistence.Entity;
049 import javax.servlet.http.HttpSession;
050
051 import org.granite.config.GraniteConfig;
052 import org.granite.context.GraniteContext;
053 import org.granite.logging.Logger;
054 import org.granite.messaging.amf.io.util.ClassGetter;
055 import org.granite.messaging.service.ServiceException;
056 import org.granite.messaging.service.ServiceInvocationContext;
057 import org.granite.messaging.webapp.HttpGraniteContext;
058 import org.granite.tide.IInvocationCall;
059 import org.granite.tide.IInvocationResult;
060 import org.granite.tide.TidePersistenceManager;
061 import org.granite.tide.TideServiceContext;
062 import org.granite.tide.annotations.BypassTideMerge;
063 import org.granite.tide.async.AsyncPublisher;
064 import org.granite.tide.cdi.lazy.CDIInitializer;
065 import org.granite.tide.data.DataContext;
066 import org.granite.tide.data.DataUpdatePostprocessor;
067 import org.granite.tide.invocation.ContextEvent;
068 import org.granite.tide.invocation.ContextResult;
069 import org.granite.tide.invocation.ContextUpdate;
070 import org.granite.tide.invocation.InvocationCall;
071 import org.granite.tide.invocation.InvocationResult;
072 import org.granite.util.TypeUtil;
073 import org.granite.util.Reflections;
074 import org.jboss.interceptor.util.proxy.TargetInstanceProxy;
075
076
077 /**
078 * @author William DRAI
079 */
080 @SessionScoped
081 public class CDIServiceContext extends TideServiceContext {
082
083 private static final long serialVersionUID = 1L;
084
085 private static final Logger log = Logger.getLogger(CDIServiceContext.class);
086
087 @SuppressWarnings("serial")
088 private static final AnnotationLiteral<Any> ANY_LITERAL = new AnnotationLiteral<Any>() {};
089 @SuppressWarnings("serial")
090 private static final AnnotationLiteral<Default> DEFAULT_LITERAL = new AnnotationLiteral<Default>() {};
091
092 private @Inject BeanManager manager;
093
094 private @Inject TideInstrumentedBeans instrumentedBeans;
095
096 private UserEvents userEvents;
097 private @Inject TideUserEvents tideUserEvents;
098 private boolean isAsynchronousContext = true;
099 private boolean isFirstCall = false;
100 private boolean isLogin = false;
101 private @Inject CDIInitializer tideEntityInitializer;
102
103
104 /**
105 * Determines the current sessionId for web invocations
106 */
107 @Override
108 public void initCall() {
109 super.initCall();
110
111 if (userEvents != null)
112 return;
113
114 if (getSessionId() != null)
115 userEvents = tideUserEvents.getUserEvents(getSessionId());
116 else {
117 GraniteContext graniteContext = GraniteContext.getCurrentInstance();
118 if (graniteContext instanceof HttpGraniteContext) {
119 HttpSession session = ((HttpGraniteContext)graniteContext).getSession(false);
120 if (session != null)
121 setSessionId(session.getId());
122 isAsynchronousContext = false;
123 }
124 }
125 }
126
127 /**
128 * Initialize current sessionId and event listeners for this context
129 *
130 * @param sessionId current sessionId
131 */
132 @Override
133 public void setSessionId(String sessionId) {
134 super.setSessionId(sessionId);
135 userEvents = tideUserEvents.getUserEvents(sessionId);
136 }
137
138 /**
139 * Clear current session from user events registry
140 */
141 @PreDestroy
142 public void endSession() {
143 if (!isAsynchronousContext && getSessionId() != null)
144 tideUserEvents.unregisterSession(getSessionId());
145 }
146
147 public void setLogin(boolean isLogin) {
148 this.isLogin = isLogin;
149 }
150
151
152 @Inject
153 private ResultsEval resultsEval;
154
155 public Map<ContextResult, Boolean> getResultsEval() {
156 return resultsEval.getResultsEval();
157 }
158
159
160 // /**
161 // * Constructs an asynchronous context object
162 // * @return current context
163 // */
164 // public AsyncContext getAsyncContext() {
165 // List<ContextResult> resultsEval = new ArrayList<ContextResult>();
166 // for (ScopeType evalScopeType : EVAL_SCOPE_TYPES)
167 // resultsEval.addAll(getResultsEval(evalScopeType).keySet());
168 //
169 // return new AsyncContext(getSessionId(), resultsEval);
170 // }
171 //
172 // /**
173 // * Restores an asynchronous context
174 // * @param asyncContext saved context
175 // */
176 // public void setAsyncContext(AsyncContext asyncContext) {
177 // AsyncPublisher asyncPublisher = getAsyncPublisher();
178 // if (asyncPublisher != null)
179 // asyncPublisher.initThread();
180 //
181 // Contexts.getSessionContext().set("org.jboss.seam.security.identity", asyncContext.getIdentity());
182 // setSessionId(asyncContext.getSessionId());
183 // for (ContextResult resultEval : asyncContext.getResults()) {
184 // if (resultEval instanceof ScopedContextResult)
185 // getResultsEval(((ScopedContextResult)resultEval).getScope()).put(resultEval, Boolean.FALSE);
186 // else
187 // getResultsEval(ScopeType.UNSPECIFIED).put(resultEval, Boolean.FALSE);
188 // }
189 // }
190
191
192 /**
193 * Implementation of component lookup for CDI service
194 *
195 * @param componentName component name
196 */
197 @Override
198 public Object findComponent(String componentName, Class<?> componentClass) {
199 Bean<?> bean = findBean(componentName, componentClass);
200 if (bean == null)
201 return null;
202 CreationalContext<?> cc = manager.createCreationalContext(bean);
203 return manager.getReference(bean, Object.class, cc);
204 }
205
206 /**
207 * Implementation of component lookup for CDI service
208 *
209 * @param componentName component name
210 */
211 @Override
212 public Set<Class<?>> findComponentClasses(String componentName, Class<?> componentClass) {
213 Bean<?> bean = findBean(componentName, componentClass);
214 return beanClasses(bean);
215 }
216
217 private Bean<?> findBean(String componentName, Class<?> componentClass) {
218 if (componentClass != null) {
219 Set<Bean<?>> beans = manager.getBeans(componentClass, ANY_LITERAL);
220 // If only one match, return match
221 if (beans.size() == 1)
222 return beans.iterator().next();
223 }
224
225 if (componentName != null && !("".equals(componentName))) {
226 // If no previous match, return by name
227 Set<Bean<?>> beans = manager.getBeans(componentName);
228 if (!beans.isEmpty())
229 return beans.iterator().next();
230 }
231
232 if (componentClass != null) {
233 // If more than one match and no named bean, return the Default one
234 Set<Bean<?>> beans = manager.getBeans(componentClass, DEFAULT_LITERAL);
235 if (beans.size() == 1)
236 return beans.iterator().next();
237 }
238
239 return null;
240 }
241
242 private Set<Class<?>> beanClasses(Object bean) {
243 if (bean instanceof Bean<?>) {
244 Set<Class<?>> classes = new HashSet<Class<?>>();
245 for (Type type : ((Bean<?>)bean).getTypes()) {
246 if (type instanceof Class<?>)
247 classes.add((Class<?>)type);
248 }
249 return classes;
250 }
251
252 Set<Class<?>> classes = new HashSet<Class<?>>(1);
253 classes.add(bean.getClass());
254 return classes;
255 }
256
257
258 // public void observeBeginConversation(@Observes org.jboss.webbeans.conversation.) {
259 // Contexts.getEventContext().set("org.granite.tide.conversation.wasCreated", true);
260 // }
261
262
263 /**
264 * Add an event in the current context
265 *
266 * @param event the event
267 */
268 public void processEvent(Object event) {
269 // Add the event to the current invocation
270 TideInvocation tideInvocation = TideInvocation.get();
271 if (tideInvocation == null)
272 return;
273
274 if (userEvents != null) {
275 String sessionId = getSessionId();
276 if (sessionId != null && userEvents.hasEventType(event.getClass()))
277 tideInvocation.addEvent(new ContextEvent(event.getClass().getName(),
278 new Object[] { event, null }));
279 }
280 // else if (Contexts.getSessionContext().isSet("org.granite.seam.login")) {
281 // // Force send of all events raised during login
282 // tideInvocation.addEvent(new ContextEvent(type, params));
283 // }
284 }
285
286
287 /**
288 * Factory for Seam async publisher
289 *
290 * @return servlet context of the current application
291 */
292 @Override
293 protected AsyncPublisher getAsyncPublisher() {
294 return null;
295 }
296
297
298 @Inject
299 private ConversationState conversation;
300
301 /**
302 * Synchronizes server context with data provided by the client
303 *
304 * @param context invocation context
305 * @param c client call
306 * @param componentName name of the component which will be invoked
307 */
308 @Override
309 public void prepareCall(ServiceInvocationContext context, IInvocationCall c, String componentName, Class<?> componentClass) {
310 InvocationCall call = (InvocationCall)c;
311 List<String> listeners = call.getListeners();
312 List<ContextUpdate> updates = call.getUpdates();
313 Object[] results = call.getResults();
314
315 try {
316 if (manager.getContext(RequestScoped.class).isActive() && manager.getContext(SessionScoped.class).isActive() && isFirstCall && isLogin) {
317 // Login tried : force evaluation of existing session context
318 for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
319 if (me.getKey().getExpression() == null
320 && findBean(me.getKey().getComponentName(), me.getKey().getComponentClass()).getScope().equals(SessionScoped.class))
321 me.setValue(Boolean.TRUE);
322 }
323 isLogin = false;
324 isFirstCall = false;
325 }
326 }
327 catch (ContextNotActiveException e) {
328 // isActive() is not enough !!
329 }
330 try {
331 if (manager.getContext(RequestScoped.class).isActive() && manager.getContext(ConversationScoped.class).isActive()
332 && conversation.isFirstCall()) {
333 // Join conversation : force evaluation of existing conversation context
334 for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
335 if (me.getKey().getExpression() == null
336 && findBean(me.getKey().getComponentName(), me.getKey().getComponentClass()).getScope().equals(ConversationScoped.class))
337 me.setValue(Boolean.TRUE);
338 }
339 conversation.setFirstCall(false);
340 }
341 }
342 catch (ContextNotActiveException e) {
343 // isActive() is not enough !!
344 }
345
346 String sessionId = getSessionId();
347 if (sessionId != null && listeners != null) {
348 // Registers new event listeners
349 for (String listener : listeners) {
350 try {
351 Class<?> listenerClass = TypeUtil.forName(listener);
352 tideUserEvents.registerEventType(sessionId, listenerClass);
353 }
354 catch (ClassNotFoundException e) {
355 log.error("Could not register event " + listener, e);
356 }
357 }
358
359 if (userEvents == null)
360 userEvents = tideUserEvents.getUserEvents(getSessionId());
361 }
362
363 boolean instrumented = false;
364 Bean<?> bean = findBean(componentName, componentClass);
365 if (bean != null)
366 instrumented = instrumentedBeans.getBean(bean.getBeanClass()) != null;
367
368 if (results != null) {
369 Map<ContextResult, Boolean> resultsEval = getResultsEval();
370 for (Object result : results) {
371 ContextResult cr = (ContextResult)result;
372 resultsEval.put(cr, Boolean.TRUE);
373 }
374 }
375
376 try {
377 tideEntityInitializer.restoreLoadedEntities();
378 }
379 catch (ContextNotActiveException e) {
380 // Not in a conversation
381 }
382
383 // Initialize an empty data context
384 DataContext.init();
385
386 DataUpdatePostprocessor dataUpdatePostprocessor = (DataUpdatePostprocessor)findComponent(null, DataUpdatePostprocessor.class);
387 if (dataUpdatePostprocessor != null)
388 DataContext.get().setDataUpdatePostprocessor(dataUpdatePostprocessor);
389
390 TideInvocation tideInvocation = TideInvocation.init();
391 tideInvocation.update(updates);
392
393 if (!instrumented) {
394 // If no interception enabled, force the update of the context for the current component
395 // In other cases it will be done by the interceptor
396 restoreContext(updates, null);
397 tideInvocation.updated();
398 }
399 }
400
401 /**
402 * Builds the result object for the invocation
403 *
404 * @param context invocation context
405 * @param result result of the method invocation
406 * @param componentName name of the invoked component
407 * @return result object
408 */
409 @Override
410 public IInvocationResult postCall(ServiceInvocationContext context, Object result, String componentName, Class<?> componentClass) {
411 TideInvocation tideInvocation = TideInvocation.get();
412 int scope = 3;
413 boolean restrict = false;
414
415 List<ContextUpdate> results = null;
416 if (!tideInvocation.isEvaluated()) {
417 // Do evaluation now if the interceptor has not been called
418 results = evaluateResults(null, false);
419 }
420 else
421 results = tideInvocation.getResults();
422
423 DataContext dataContext = DataContext.get();
424 Object[][] updates = dataContext != null ? dataContext.getUpdates() : null;
425
426 Bean<?> bean = null;
427 if (componentName != null || componentClass != null) {
428 // Determines scope of component
429 bean = findBean(componentName, componentClass);
430 if (bean.getScope() == RequestScoped.class)
431 scope = 3;
432 else if (bean.getScope() == ConversationScoped.class)
433 scope = 2;
434 else if (bean.getScope() == SessionScoped.class)
435 scope = 1;
436
437 try {
438 if (manager.getContext(RequestScoped.class).get(bean) != null)
439 scope = 3;
440 else if (manager.getContext(ConversationScoped.class).get(bean) != null)
441 scope = 2;
442 else if (manager.getContext(SessionScoped.class).get(bean) != null)
443 scope = 1;
444 }
445 catch (ContextNotActiveException e) {
446 scope = 3;
447 }
448 }
449
450 InvocationResult ires = new InvocationResult(result, results);
451 ires.setScope(scope);
452 ires.setRestrict(restrict);
453 Set<Class<?>> componentClasses = findComponentClasses(componentName, componentClass);
454 if (isBeanAnnotationPresent(componentClasses, context.getMethod().getName(), context.getMethod().getParameterTypes(), BypassTideMerge.class))
455 ires.setMerge(false);
456
457 ires.setUpdates(updates);
458
459 // Adds events in result object
460 ires.setEvents(tideInvocation.getEvents());
461
462 try {
463 // Save current set of entities loaded in a conversation scoped component to handle case of extended PM
464 tideEntityInitializer.saveLoadedEntities();
465 }
466 catch (ContextNotActiveException e) {
467 // Not in a conversation
468 }
469
470 // Clean thread
471 TideInvocation.remove();
472
473 return ires;
474 }
475
476 /**
477 * Intercepts a fault on the invocation
478 *
479 * @param context invocation context
480 * @param t exception thrown
481 * @param componentName name of the invoked component
482 */
483 @Override
484 public void postCallFault(ServiceInvocationContext context, Throwable t, String componentName, Class<?> componentClass) {
485 // Clean thread: very important to avoid phantom evaluations after exceptions
486 TideInvocation.remove();
487 }
488
489
490 public void addResultEval(ContextResult result) {
491 getResultsEval().put(result, Boolean.TRUE);
492 }
493
494
495 private static Object unproxy(Object obj) {
496 // TODO: Works only with Weld !!
497 if (obj instanceof TargetInstanceProxy<?>) {
498 try {
499 return ((TargetInstanceProxy<?>)obj).getTargetInstance();
500 }
501 catch (IllegalProductException e) {
502 return null;
503 }
504 }
505 return obj;
506 }
507
508 /**
509 * Evaluate updates in current server context
510 *
511 * @param updates list of updates
512 * @param target the target instance
513 */
514 public void restoreContext(List<ContextUpdate> updates, Object target) {
515 if (updates == null)
516 return;
517
518 try {
519 TideInvocation.get().lock();
520
521 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
522
523 // Restore context
524 for (ContextUpdate update : updates) {
525
526 log.debug("Before invocation: evaluating expression #0(#1).#2", update.getComponentName(), update.getComponentClassName(), update.getExpression());
527
528 Class<?> componentClass = update.getComponentClass();
529 Bean<?> sourceBean = findBean(update.getComponentName(), componentClass);
530
531 Object previous = null;
532 if (update.getExpression() != null) {
533 String[] path = update.getExpression().split("\\.");
534 Object instance = manager.getReference(sourceBean, Object.class, manager.createCreationalContext(sourceBean));
535 boolean disabled = instance != null
536 && config.isComponentTideDisabled(sourceBean.getName(), beanClasses(sourceBean), instance);
537 if (!disabled) {
538 instance = unproxy(instance);
539
540 Object bean = instance;
541 Object value = instance;
542
543 if (update.getValue() != null) {
544 boolean getPrevious = true;
545 if (update.getValue().getClass().isPrimitive() || isImmutable(update.getValue())) {
546 getPrevious = false;
547 }
548 else if (update.getValue().getClass().getAnnotation(Entity.class) != null) {
549 org.granite.util.Entity entity = new org.granite.util.Entity(update.getValue());
550 if (entity.getIdentifier() == null)
551 getPrevious = false;
552 }
553 if (getPrevious) {
554 try {
555 for (int i = 0; i < path.length; i++) {
556 if (value == null)
557 break;
558 // Use modified Reflections for getter because of a bug in Seam 2.0.0
559 Method getter = org.granite.util.Reflections.getGetterMethod(value.getClass(), path[i]);
560 value = Reflections.invoke(getter, value);
561 if (i < path.length-1)
562 bean = value;
563 }
564 }
565 catch (IllegalArgumentException e) {
566 // No getter found to retrieve current value
567 log.warn(e, "Partial merge only");
568 value = null;
569 }
570 catch (Exception e) {
571 throw new ServiceException("Could not get property: " + update.toString(), e);
572 }
573 previous = value;
574 }
575 }
576
577 // Set new value
578 try {
579 if (bean != null) {
580 Method setter = Reflections.getSetterMethod(bean.getClass(), path[path.length-1]);
581 Type type = setter.getParameterTypes()[0];
582 value = GraniteContext.getCurrentInstance().getGraniteConfig().getConverters().convert(update.getValue(), type);
583 // Merge entities into current persistent context if needed
584 value = mergeExternal(value, previous);
585 Reflections.invoke(setter, bean, value);
586 }
587 }
588 catch (Exception e) {
589 throw new ServiceException("Could not restore property: " + update.toString(), e);
590 }
591 }
592 }
593 else {
594 previous = manager.getReference(sourceBean, Object.class, manager.createCreationalContext(sourceBean));
595 boolean disabled = previous != null
596 && config.isComponentTideDisabled(sourceBean.getName(), beanClasses(sourceBean), previous);
597 if (!disabled) {
598 previous = unproxy(previous);
599
600 // Merge context variable
601 mergeExternal(update.getValue(), previous);
602 }
603 }
604 }
605 }
606 finally {
607 TideInvocation.get().unlock();
608 }
609 }
610
611
612 /**
613 * Evaluate results from context
614 *
615 * @param target the target instance
616 * @param nothing used by initializer to avoid interactions with context sync
617 *
618 * @return list of updates to send back to the client
619 */
620 public List<ContextUpdate> evaluateResults(Object target, boolean nothing) {
621
622 List<ContextUpdate> resultsMap = new ArrayList<ContextUpdate>();
623
624 if (nothing)
625 return resultsMap;
626
627 try {
628 TideInvocation.get().lock();
629
630 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
631 ClassGetter classGetter = GraniteContext.getCurrentInstance().getGraniteConfig().getClassGetter();
632
633 for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
634 if (!me.getValue())
635 continue;
636
637 ContextResult res = me.getKey();
638
639 Class<?> componentClass = res.getComponentClass();
640 Bean<?> targetBean = findBean(res.getComponentName(), componentClass);
641 if (targetBean == null) {
642 log.warn("Target bean " + res.getComponentName() + " of class " + componentClass + " not found");
643 continue;
644 }
645 String targetComponentName = targetBean.getName();
646
647 boolean add = true;
648 Class<? extends Annotation> scopeType = targetBean.getScope();
649 Boolean restrict = res.getRestrict();
650
651 Object value = res instanceof ScopedContextResult
652 ? ((ScopedContextResult)res).getValue()
653 : manager.getReference(targetBean, Object.class, manager.createCreationalContext(targetBean));
654
655 if (value != null && config.isComponentTideDisabled(targetComponentName, beanClasses(targetBean), value))
656 add = false;
657
658 if (add) {
659 getResultsEval().put(res, false);
660
661 String[] path = res.getExpression() != null ? res.getExpression().split("\\.") : new String[0];
662
663 value = unproxy(value);
664
665 if (value != null) {
666 try {
667 for (int i = 0; i < path.length; i++) {
668 if (value == null)
669 break;
670 try {
671 Method getter = Reflections.getGetterMethod(value.getClass(), path[i]);
672 value = Reflections.invoke(getter, value);
673 }
674 catch (IllegalArgumentException e) {
675 // GDS-566
676 add = false;
677 }
678 }
679 }
680 catch (Exception e) {
681 throw new ServiceException("Could not evaluate expression " + res.toString(), e);
682 }
683 }
684
685 if (add && value != null && classGetter != null) {
686 classGetter.initialize(null, null, value);
687
688 int scope = 3;
689 if (scopeType == ConversationScoped.class)
690 scope = 2;
691 else if (scopeType == SessionScoped.class)
692 scope = 1;
693
694 ContextUpdate cu = new ContextUpdate(res.getComponentName(), res.getExpression(), value, scope, Boolean.TRUE.equals(restrict));
695 cu.setComponentClassName(res.getComponentClassName());
696 resultsMap.add(cu);
697 add = false;
698 }
699 }
700
701 me.setValue(Boolean.FALSE);
702 }
703
704 return resultsMap;
705 }
706 finally {
707 TideInvocation.get().unlock();
708 }
709 }
710
711 @Inject
712 private CDIInitializer initializer;
713
714 @Override
715 protected TidePersistenceManager getTidePersistenceManager(boolean create) {
716 return initializer.getPersistenceManager();
717 }
718
719
720 @Override
721 protected boolean equals(Object obj1, Object obj2) {
722 if (super.equals(obj1, obj2))
723 return true;
724
725 return (obj1 != null && obj2 != null
726 && (obj1.getClass().isAnnotationPresent(TideBean.class) || obj2.getClass().isAnnotationPresent(TideBean.class)));
727 }
728
729
730 @SuppressWarnings("unchecked")
731 private static final Set<Class<?>> KNOWN_IMMUTABLES = new HashSet<Class<?>>(Arrays.asList(
732 String.class, Byte.class, Short.class, Integer.class, Long.class,
733 Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class
734 ));
735
736 public static boolean isImmutable(Object o) {
737 return KNOWN_IMMUTABLES.contains(o.getClass()) || Enum.class.isInstance(o);
738 }
739
740 // /**
741 // * Implementations of intercepted asynchronous calls
742 // * Send asynchronous event to client
743 // * @param asyncContext current context (session id)
744 // * @param targetComponentName target component name
745 // * @param methodName method name
746 // * @param paramTypes method argument types
747 // * @param params argument values
748 // * @return result
749 // */
750 // public Object invokeAsynchronous(AsyncContext asyncContext, String targetComponentName, String methodName, Class<?>[] paramTypes, Object[] params) {
751 // setAsyncContext(asyncContext);
752 //
753 // // Just another ugly hack: the Seam interceptor has set this variable and we don't want it
754 // Contexts.getEventContext().remove("org.jboss.seam.async.AsynchronousIntercepter.REENTRANT");
755 //
756 // Component component = TideInit.lookupComponent(targetComponentName);
757 //
758 // // Forces evaluation of all results if they are related to the called component
759 // for (Map.Entry<ContextResult, Boolean> me : getResultsEval(component.getScope()).entrySet()) {
760 // if (me.getKey().getComponentName().equals(targetComponentName))
761 // me.setValue(Boolean.TRUE);
762 // }
763 //
764 // Object target = Component.getInstance(targetComponentName);
765 //
766 // Method method;
767 // try {
768 // method = target.getClass().getMethod(methodName, paramTypes);
769 // }
770 // catch (NoSuchMethodException nsme) {
771 // throw new IllegalStateException(nsme);
772 // }
773 //
774 // Object result = Reflections.invokeAndWrap(method, target, params);
775 //
776 // sendEvent(targetComponentName);
777 //
778 // return result;
779 // }
780 //
781 //
782 // /**
783 // * Search for a named attribute in all contexts, in the
784 // * following order: method, event, page, conversation,
785 // * session, business process, application.
786 // *
787 // * @return the first component found, or null
788 // */
789 // public static Object[] lookupInStatefulContexts(String name, ScopeType scope) {
790 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.METHOD.equals(scope)) && Contexts.isMethodContextActive()) {
791 // Object result = Contexts.getMethodContext().get(name);
792 // if (result != null)
793 // return new Object[] { result, Contexts.getMethodContext().getType() };
794 // }
795 //
796 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.EVENT.equals(scope)) && Contexts.isEventContextActive()) {
797 // Object result = Contexts.getEventContext().get(name);
798 // if (result != null)
799 // return new Object[] { result, Contexts.getEventContext().getType() };
800 // }
801 //
802 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.PAGE.equals(scope)) && Contexts.isPageContextActive()) {
803 // Object result = Contexts.getPageContext().get(name);
804 // if (result != null)
805 // return new Object[] { result, Contexts.getPageContext().getType() };
806 // }
807 //
808 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.CONVERSATION.equals(scope)) && Contexts.isConversationContextActive()) {
809 // Object result = Contexts.getConversationContext().get(name);
810 // if (result != null)
811 // return new Object[] { result, Contexts.getConversationContext().getType() };
812 // }
813 //
814 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.SESSION.equals(scope)) && Contexts.isSessionContextActive()) {
815 // Object result = Contexts.getSessionContext().get(name);
816 // if (result != null)
817 // return new Object[] { result, Contexts.getSessionContext().getType() };
818 // }
819 //
820 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.BUSINESS_PROCESS.equals(scope)) && Contexts.isBusinessProcessContextActive()) {
821 // Object result = Contexts.getBusinessProcessContext().get(name);
822 // if (result != null)
823 // return new Object[] { result, Contexts.getBusinessProcessContext().getType() };
824 // }
825 //
826 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.APPLICATION.equals(scope)) && Contexts.isApplicationContextActive()) {
827 // Object result = Contexts.getApplicationContext().get(name);
828 // if (result != null)
829 // return new Object[] { result, Contexts.getApplicationContext().getType() };
830 // }
831 //
832 // return ScopeType.UNSPECIFIED.equals(scope) ? null : new Object[] { null, scope };
833 // }
834 }