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 */ 022package org.granite.tide.cdi; 023 024import java.lang.annotation.Annotation; 025import java.lang.reflect.Method; 026import java.lang.reflect.Type; 027import java.math.BigDecimal; 028import java.math.BigInteger; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035 036import javax.annotation.PreDestroy; 037import javax.enterprise.context.ContextNotActiveException; 038import javax.enterprise.context.ConversationScoped; 039import javax.enterprise.context.RequestScoped; 040import javax.enterprise.context.SessionScoped; 041import javax.enterprise.context.spi.CreationalContext; 042import javax.enterprise.inject.Any; 043import javax.enterprise.inject.Default; 044import javax.enterprise.inject.IllegalProductException; 045import javax.enterprise.inject.spi.Bean; 046import javax.enterprise.inject.spi.BeanManager; 047import javax.enterprise.util.AnnotationLiteral; 048import javax.inject.Inject; 049import javax.persistence.Entity; 050import javax.servlet.http.HttpSession; 051 052import org.granite.config.GraniteConfig; 053import org.granite.context.GraniteContext; 054import org.granite.logging.Logger; 055import org.granite.messaging.amf.io.util.ClassGetter; 056import org.granite.messaging.service.ServiceException; 057import org.granite.messaging.service.ServiceInvocationContext; 058import org.granite.messaging.webapp.HttpGraniteContext; 059import org.granite.tide.IInvocationCall; 060import org.granite.tide.IInvocationResult; 061import org.granite.tide.TidePersistenceManager; 062import org.granite.tide.TideServiceContext; 063import org.granite.tide.annotations.BypassTideMerge; 064import org.granite.tide.async.AsyncPublisher; 065import org.granite.tide.cdi.lazy.CDIInitializer; 066import org.granite.tide.data.DataContext; 067import org.granite.tide.data.DataUpdatePostprocessor; 068import org.granite.tide.invocation.ContextEvent; 069import org.granite.tide.invocation.ContextResult; 070import org.granite.tide.invocation.ContextUpdate; 071import org.granite.tide.invocation.InvocationCall; 072import org.granite.tide.invocation.InvocationResult; 073import org.granite.util.TypeUtil; 074import org.granite.util.Reflections; 075import org.jboss.interceptor.util.proxy.TargetInstanceProxy; 076 077 078/** 079 * @author William DRAI 080 */ 081@SessionScoped 082public 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}