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