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 res = new InvocationResult(result, results);
451 res.setScope(scope);
452 res.setRestrict(restrict);
453 if (bean != null) {
454 if (bean.getBeanClass().isAnnotationPresent(BypassTideMerge.class))
455 res.setMerge(false);
456 else {
457 try {
458 if (context != null) {
459 Method m = bean.getBeanClass().getMethod(context.getMethod().getName(), context.getMethod().getParameterTypes());
460 if (m.isAnnotationPresent(BypassTideMerge.class))
461 res.setMerge(false);
462 }
463 }
464 catch (Exception e) {
465 log.warn("Could not find bean method", e);
466 }
467 }
468 }
469
470 res.setUpdates(updates);
471
472 // Adds events in result object
473 res.setEvents(tideInvocation.getEvents());
474
475 try {
476 // Save current set of entities loaded in a conversation scoped component to handle case of extended PM
477 tideEntityInitializer.saveLoadedEntities();
478 }
479 catch (ContextNotActiveException e) {
480 // Not in a conversation
481 }
482
483 // Clean thread
484 TideInvocation.remove();
485
486 return res;
487 }
488
489 /**
490 * Intercepts a fault on the invocation
491 *
492 * @param context invocation context
493 * @param t exception thrown
494 * @param componentName name of the invoked component
495 */
496 @Override
497 public void postCallFault(ServiceInvocationContext context, Throwable t, String componentName, Class<?> componentClass) {
498 // Clean thread: very important to avoid phantom evaluations after exceptions
499 TideInvocation.remove();
500 }
501
502
503 public void addResultEval(ContextResult result) {
504 getResultsEval().put(result, Boolean.TRUE);
505 }
506
507
508 private static Object unproxy(Object obj) {
509 // TODO: Works only with Weld !!
510 if (obj instanceof TargetInstanceProxy<?>) {
511 try {
512 return ((TargetInstanceProxy<?>)obj).getTargetInstance();
513 }
514 catch (IllegalProductException e) {
515 return null;
516 }
517 }
518 return obj;
519 }
520
521 /**
522 * Evaluate updates in current server context
523 *
524 * @param updates list of updates
525 * @param target the target instance
526 */
527 public void restoreContext(List<ContextUpdate> updates, Object target) {
528 if (updates == null)
529 return;
530
531 try {
532 TideInvocation.get().lock();
533
534 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
535
536 // Restore context
537 for (ContextUpdate update : updates) {
538
539 log.debug("Before invocation: evaluating expression #0(#1).#2", update.getComponentName(), update.getComponentClassName(), update.getExpression());
540
541 Class<?> componentClass = update.getComponentClass();
542 Bean<?> sourceBean = findBean(update.getComponentName(), componentClass);
543
544 Object previous = null;
545 if (update.getExpression() != null) {
546 String[] path = update.getExpression().split("\\.");
547 Object instance = manager.getReference(sourceBean, Object.class, manager.createCreationalContext(sourceBean));
548 boolean disabled = instance != null
549 && config.isComponentTideDisabled(sourceBean.getName(), beanClasses(sourceBean), instance);
550 if (!disabled) {
551 instance = unproxy(instance);
552
553 Object bean = instance;
554 Object value = instance;
555
556 if (update.getValue() != null) {
557 boolean getPrevious = true;
558 if (update.getValue().getClass().isPrimitive() || isImmutable(update.getValue())) {
559 getPrevious = false;
560 }
561 else if (update.getValue().getClass().getAnnotation(Entity.class) != null) {
562 org.granite.util.Entity entity = new org.granite.util.Entity(update.getValue());
563 if (entity.getIdentifier() == null)
564 getPrevious = false;
565 }
566 if (getPrevious) {
567 try {
568 for (int i = 0; i < path.length; i++) {
569 if (value == null)
570 break;
571 // Use modified Reflections for getter because of a bug in Seam 2.0.0
572 Method getter = org.granite.util.Reflections.getGetterMethod(value.getClass(), path[i]);
573 value = Reflections.invoke(getter, value);
574 if (i < path.length-1)
575 bean = value;
576 }
577 }
578 catch (IllegalArgumentException e) {
579 // No getter found to retrieve current value
580 log.warn(e, "Partial merge only");
581 value = null;
582 }
583 catch (Exception e) {
584 throw new ServiceException("Could not get property: " + update.toString(), e);
585 }
586 previous = value;
587 }
588 }
589
590 // Set new value
591 try {
592 if (bean != null) {
593 Method setter = Reflections.getSetterMethod(bean.getClass(), path[path.length-1]);
594 Type type = setter.getParameterTypes()[0];
595 value = GraniteContext.getCurrentInstance().getGraniteConfig().getConverters().convert(update.getValue(), type);
596 // Merge entities into current persistent context if needed
597 value = mergeExternal(value, previous);
598 Reflections.invoke(setter, bean, value);
599 }
600 }
601 catch (Exception e) {
602 throw new ServiceException("Could not restore property: " + update.toString(), e);
603 }
604 }
605 }
606 else {
607 previous = manager.getReference(sourceBean, Object.class, manager.createCreationalContext(sourceBean));
608 boolean disabled = previous != null
609 && config.isComponentTideDisabled(sourceBean.getName(), beanClasses(sourceBean), previous);
610 if (!disabled) {
611 previous = unproxy(previous);
612
613 // Merge context variable
614 mergeExternal(update.getValue(), previous);
615 }
616 }
617 }
618 }
619 finally {
620 TideInvocation.get().unlock();
621 }
622 }
623
624
625 /**
626 * Evaluate results from context
627 *
628 * @param target the target instance
629 * @param nothing used by initializer to avoid interactions with context sync
630 *
631 * @return list of updates to send back to the client
632 */
633 public List<ContextUpdate> evaluateResults(Object target, boolean nothing) {
634
635 List<ContextUpdate> resultsMap = new ArrayList<ContextUpdate>();
636
637 if (nothing)
638 return resultsMap;
639
640 try {
641 TideInvocation.get().lock();
642
643 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
644 ClassGetter classGetter = GraniteContext.getCurrentInstance().getGraniteConfig().getClassGetter();
645
646 for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
647 if (!me.getValue())
648 continue;
649
650 ContextResult res = me.getKey();
651
652 Class<?> componentClass = res.getComponentClass();
653 Bean<?> targetBean = findBean(res.getComponentName(), componentClass);
654 if (targetBean == null) {
655 log.warn("Target bean " + res.getComponentName() + " of class " + componentClass + " not found");
656 continue;
657 }
658 String targetComponentName = targetBean.getName();
659
660 boolean add = true;
661 Class<? extends Annotation> scopeType = targetBean.getScope();
662 Boolean restrict = res.getRestrict();
663
664 Object value = res instanceof ScopedContextResult
665 ? ((ScopedContextResult)res).getValue()
666 : manager.getReference(targetBean, Object.class, manager.createCreationalContext(targetBean));
667
668 if (value != null && config.isComponentTideDisabled(targetComponentName, beanClasses(targetBean), value))
669 add = false;
670
671 if (add) {
672 getResultsEval().put(res, false);
673
674 String[] path = res.getExpression() != null ? res.getExpression().split("\\.") : new String[0];
675
676 value = unproxy(value);
677
678 if (value != null) {
679 try {
680 for (int i = 0; i < path.length; i++) {
681 if (value == null)
682 break;
683 try {
684 Method getter = Reflections.getGetterMethod(value.getClass(), path[i]);
685 value = Reflections.invoke(getter, value);
686 }
687 catch (IllegalArgumentException e) {
688 // GDS-566
689 add = false;
690 }
691 }
692 }
693 catch (Exception e) {
694 throw new ServiceException("Could not evaluate expression " + res.toString(), e);
695 }
696 }
697
698 if (add && value != null && classGetter != null) {
699 classGetter.initialize(null, null, value);
700
701 int scope = 3;
702 if (scopeType == ConversationScoped.class)
703 scope = 2;
704 else if (scopeType == SessionScoped.class)
705 scope = 1;
706
707 ContextUpdate cu = new ContextUpdate(res.getComponentName(), res.getExpression(), value, scope, Boolean.TRUE.equals(restrict));
708 cu.setComponentClassName(res.getComponentClassName());
709 resultsMap.add(cu);
710 add = false;
711 }
712 }
713
714 me.setValue(Boolean.FALSE);
715 }
716
717 return resultsMap;
718 }
719 finally {
720 TideInvocation.get().unlock();
721 }
722 }
723
724 @Inject
725 private CDIInitializer initializer;
726
727 @Override
728 protected TidePersistenceManager getTidePersistenceManager(boolean create) {
729 return initializer.getPersistenceManager();
730 }
731
732
733 @Override
734 protected boolean equals(Object obj1, Object obj2) {
735 if (super.equals(obj1, obj2))
736 return true;
737
738 return (obj1 != null && obj2 != null
739 && (obj1.getClass().isAnnotationPresent(TideBean.class) || obj2.getClass().isAnnotationPresent(TideBean.class)));
740 }
741
742
743 @SuppressWarnings("unchecked")
744 private static final Set<Class<?>> KNOWN_IMMUTABLES = new HashSet<Class<?>>(Arrays.asList(
745 String.class, Byte.class, Short.class, Integer.class, Long.class,
746 Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class
747 ));
748
749 public static boolean isImmutable(Object o) {
750 return KNOWN_IMMUTABLES.contains(o.getClass()) || Enum.class.isInstance(o);
751 }
752
753 // /**
754 // * Implementations of intercepted asynchronous calls
755 // * Send asynchronous event to client
756 // * @param asyncContext current context (session id)
757 // * @param targetComponentName target component name
758 // * @param methodName method name
759 // * @param paramTypes method argument types
760 // * @param params argument values
761 // * @return result
762 // */
763 // public Object invokeAsynchronous(AsyncContext asyncContext, String targetComponentName, String methodName, Class<?>[] paramTypes, Object[] params) {
764 // setAsyncContext(asyncContext);
765 //
766 // // Just another ugly hack: the Seam interceptor has set this variable and we don't want it
767 // Contexts.getEventContext().remove("org.jboss.seam.async.AsynchronousIntercepter.REENTRANT");
768 //
769 // Component component = TideInit.lookupComponent(targetComponentName);
770 //
771 // // Forces evaluation of all results if they are related to the called component
772 // for (Map.Entry<ContextResult, Boolean> me : getResultsEval(component.getScope()).entrySet()) {
773 // if (me.getKey().getComponentName().equals(targetComponentName))
774 // me.setValue(Boolean.TRUE);
775 // }
776 //
777 // Object target = Component.getInstance(targetComponentName);
778 //
779 // Method method;
780 // try {
781 // method = target.getClass().getMethod(methodName, paramTypes);
782 // }
783 // catch (NoSuchMethodException nsme) {
784 // throw new IllegalStateException(nsme);
785 // }
786 //
787 // Object result = Reflections.invokeAndWrap(method, target, params);
788 //
789 // sendEvent(targetComponentName);
790 //
791 // return result;
792 // }
793 //
794 //
795 // /**
796 // * Search for a named attribute in all contexts, in the
797 // * following order: method, event, page, conversation,
798 // * session, business process, application.
799 // *
800 // * @return the first component found, or null
801 // */
802 // public static Object[] lookupInStatefulContexts(String name, ScopeType scope) {
803 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.METHOD.equals(scope)) && Contexts.isMethodContextActive()) {
804 // Object result = Contexts.getMethodContext().get(name);
805 // if (result != null)
806 // return new Object[] { result, Contexts.getMethodContext().getType() };
807 // }
808 //
809 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.EVENT.equals(scope)) && Contexts.isEventContextActive()) {
810 // Object result = Contexts.getEventContext().get(name);
811 // if (result != null)
812 // return new Object[] { result, Contexts.getEventContext().getType() };
813 // }
814 //
815 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.PAGE.equals(scope)) && Contexts.isPageContextActive()) {
816 // Object result = Contexts.getPageContext().get(name);
817 // if (result != null)
818 // return new Object[] { result, Contexts.getPageContext().getType() };
819 // }
820 //
821 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.CONVERSATION.equals(scope)) && Contexts.isConversationContextActive()) {
822 // Object result = Contexts.getConversationContext().get(name);
823 // if (result != null)
824 // return new Object[] { result, Contexts.getConversationContext().getType() };
825 // }
826 //
827 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.SESSION.equals(scope)) && Contexts.isSessionContextActive()) {
828 // Object result = Contexts.getSessionContext().get(name);
829 // if (result != null)
830 // return new Object[] { result, Contexts.getSessionContext().getType() };
831 // }
832 //
833 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.BUSINESS_PROCESS.equals(scope)) && Contexts.isBusinessProcessContextActive()) {
834 // Object result = Contexts.getBusinessProcessContext().get(name);
835 // if (result != null)
836 // return new Object[] { result, Contexts.getBusinessProcessContext().getType() };
837 // }
838 //
839 // if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.APPLICATION.equals(scope)) && Contexts.isApplicationContextActive()) {
840 // Object result = Contexts.getApplicationContext().get(name);
841 // if (result != null)
842 // return new Object[] { result, Contexts.getApplicationContext().getType() };
843 // }
844 //
845 // return ScopeType.UNSPECIFIED.equals(scope) ? null : new Object[] { null, scope };
846 // }
847 }