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