1 /** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one or more 4 * contributor license agreements. See the NOTICE file distributed with 5 * this work for additional information regarding copyright ownership. 6 * The ASF licenses this file to You under the Apache License, Version 2.0 7 * (the "License"); you may not use this file except in compliance with 8 * the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.openejb.core.cmp; 19 20 import java.lang.reflect.Field; 21 import java.lang.reflect.InvocationTargetException; 22 import java.lang.reflect.Method; 23 import java.rmi.NoSuchObjectException; 24 import java.rmi.RemoteException; 25 import java.util.Collection; 26 import java.util.Enumeration; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.ArrayList; 31 import java.util.LinkedHashSet; 32 import java.util.Set; 33 import javax.ejb.EJBException; 34 import javax.ejb.EJBHome; 35 import javax.ejb.EJBLocalHome; 36 import javax.ejb.EJBLocalObject; 37 import javax.ejb.EJBObject; 38 import javax.ejb.EntityBean; 39 import javax.ejb.ObjectNotFoundException; 40 import javax.ejb.RemoveException; 41 import javax.ejb.Timer; 42 import javax.ejb.FinderException; 43 import javax.ejb.EJBAccessException; 44 import javax.transaction.TransactionManager; 45 import javax.transaction.TransactionSynchronizationRegistry; 46 import javax.transaction.Synchronization; 47 48 import org.apache.openejb.ApplicationException; 49 import org.apache.openejb.DeploymentInfo; 50 import org.apache.openejb.OpenEJBException; 51 import org.apache.openejb.ProxyInfo; 52 import org.apache.openejb.RpcContainer; 53 import org.apache.openejb.ContainerType; 54 import org.apache.openejb.InterfaceType; 55 import org.apache.openejb.loader.SystemInstance; 56 import org.apache.openejb.core.CoreDeploymentInfo; 57 import org.apache.openejb.core.Operation; 58 import org.apache.openejb.core.ThreadContext; 59 import org.apache.openejb.core.ExceptionType; 60 import org.apache.openejb.core.timer.EjbTimerService; 61 import org.apache.openejb.core.timer.EjbTimerServiceImpl; 62 import org.apache.openejb.core.entity.EntityContext; 63 import org.apache.openejb.core.entity.EntrancyTracker; 64 import org.apache.openejb.core.transaction.TransactionPolicy; 65 import static org.apache.openejb.core.transaction.EjbTransactionUtil.handleApplicationException; 66 import static org.apache.openejb.core.transaction.EjbTransactionUtil.handleSystemException; 67 import static org.apache.openejb.core.transaction.EjbTransactionUtil.afterInvoke; 68 import static org.apache.openejb.core.transaction.EjbTransactionUtil.createTransactionPolicy; 69 import org.apache.openejb.spi.SecurityService; 70 import org.apache.openejb.util.Enumerator; 71 72 /** 73 * @org.apache.xbean.XBean element="cmpContainer" 74 */ 75 public class CmpContainer implements RpcContainer { 76 protected final Object containerID; 77 protected final SecurityService securityService; 78 79 /** 80 * Index used for getDeployments() and getDeploymentInfo(deploymentId). 81 */ 82 protected final Map<Object, DeploymentInfo> deploymentsById = new HashMap<Object, DeploymentInfo>(); 83 84 /** 85 * When events are fired from the CMP engine only an entity bean instance is returned. The type of the bean is used 86 * to find the deployment info. This means that when the same type is used multiple ejb deployments a random deployment 87 * will be selected to handle the ejb callback. 88 */ 89 protected final Map<Class, DeploymentInfo> deploymentsByClass = new HashMap<Class, DeploymentInfo>(); 90 91 /** 92 * The CmpEngine which performs the actual persistence operations 93 */ 94 protected final CmpEngine cmpEngine; 95 96 /** 97 * Tracks entity instances that have been "entered" so we can throw reentrancy exceptions. 98 */ 99 protected EntrancyTracker entrancyTracker; 100 protected TransactionSynchronizationRegistry synchronizationRegistry; 101 private static final Object ENTITIES_TO_STORE = new Object() { 102 public String toString() { 103 return "EntitiesToStore"; 104 } 105 }; 106 107 public CmpContainer(Object id, TransactionManager transactionManager, SecurityService securityService, String cmpEngineFactory) throws OpenEJBException { 108 this.containerID = id; 109 this.securityService = securityService; 110 synchronizationRegistry = SystemInstance.get().getComponent(TransactionSynchronizationRegistry.class); 111 entrancyTracker = new EntrancyTracker(synchronizationRegistry); 112 113 // create the cmp engine instance 114 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 115 if (classLoader == null) classLoader = getClass().getClassLoader(); 116 117 CmpEngineFactory factory; 118 try { 119 Class<?> cmpEngineFactoryClass = classLoader.loadClass(cmpEngineFactory); 120 factory = (CmpEngineFactory) cmpEngineFactoryClass.newInstance(); 121 } catch (Exception e) { 122 throw new OpenEJBException("Unable to create cmp engine factory " + cmpEngineFactory, e); 123 } 124 factory.setTransactionManager(transactionManager); 125 factory.setTransactionSynchronizationRegistry(synchronizationRegistry); 126 factory.setCmpCallback(new ContainerCmpCallback()); 127 cmpEngine = factory.create(); 128 } 129 130 public Object getContainerID() { 131 return containerID; 132 } 133 134 public ContainerType getContainerType() { 135 return ContainerType.CMP_ENTITY; 136 } 137 138 public synchronized DeploymentInfo[] deployments() { 139 return deploymentsById.values().toArray(new DeploymentInfo[deploymentsById.size()]); 140 } 141 142 public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) { 143 return deploymentsById.get(deploymentID); 144 } 145 146 private DeploymentInfo getDeploymentInfoByClass(Class type) { 147 DeploymentInfo deploymentInfo = null; 148 while (type != null && deploymentInfo == null) { 149 deploymentInfo = deploymentsByClass.get(type); 150 type = type.getSuperclass(); 151 } 152 153 return deploymentInfo; 154 } 155 156 public void deploy(DeploymentInfo deploymentInfo) throws OpenEJBException { 157 deploy((CoreDeploymentInfo) deploymentInfo); 158 } 159 160 public void deploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException { 161 synchronized (this) { 162 Object deploymentId = deploymentInfo.getDeploymentID(); 163 164 cmpEngine.deploy(deploymentInfo); 165 deploymentInfo.setContainerData(cmpEngine); 166 167 // try to set deploymentInfo static field on bean implementation class 168 try { 169 Field field = deploymentInfo.getCmpImplClass().getField("deploymentInfo"); 170 field.set(null, deploymentInfo); 171 } catch (Exception e) { 172 // ignore 173 } 174 175 // add to indexes 176 deploymentsById.put(deploymentId, deploymentInfo); 177 deploymentsByClass.put(deploymentInfo.getCmpImplClass(), deploymentInfo); 178 deploymentInfo.setContainer(this); 179 } 180 181 EjbTimerService timerService = deploymentInfo.getEjbTimerService(); 182 if (timerService != null) { 183 timerService.start(); 184 } 185 } 186 187 public void undeploy(DeploymentInfo deploymentInfo) throws OpenEJBException { 188 EjbTimerService timerService = deploymentInfo.getEjbTimerService(); 189 if (timerService != null) { 190 timerService.stop(); 191 } 192 undeploy((CoreDeploymentInfo)deploymentInfo); 193 } 194 195 public void undeploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException { 196 synchronized (this) { 197 deploymentsById.remove(deploymentInfo.getDeploymentID()); 198 deploymentsByClass.remove(deploymentInfo.getCmpImplClass()); 199 200 try { 201 Field field = deploymentInfo.getCmpImplClass().getField("deploymentInfo"); 202 field.set(null, null); 203 } catch (Exception e) { 204 // ignore 205 } 206 207 deploymentInfo.setContainer(null); 208 deploymentInfo.setContainerData(null); 209 } 210 } 211 212 public Object getEjbInstance(CoreDeploymentInfo deployInfo, Object primaryKey) { 213 ThreadContext callContext = new ThreadContext(deployInfo, primaryKey); 214 215 ThreadContext oldCallContext = ThreadContext.enter(callContext); 216 try { 217 Object bean = cmpEngine.loadBean(callContext, primaryKey); 218 return bean; 219 } finally { 220 ThreadContext.exit(oldCallContext); 221 } 222 } 223 224 /** 225 * @deprecated use invoke signature without 'securityIdentity' argument. 226 */ 227 public Object invoke(Object deployID, Method callMethod, Object[] args, Object primKey, Object securityIdentity) throws OpenEJBException { 228 return invoke(deployID, null, callMethod.getDeclaringClass(), callMethod, args, primKey); 229 } 230 231 public Object invoke(Object deployID, Class callInterface, Method callMethod, Object[] args, Object primKey) throws OpenEJBException { 232 return invoke(deployID, null, callInterface, callMethod, args, primKey); 233 } 234 235 public Object invoke(Object deployID, InterfaceType type, Class callInterface, Method callMethod, Object[] args, Object primKey) throws OpenEJBException { 236 CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) this.getDeploymentInfo(deployID); 237 238 if (deployInfo == null) throw new OpenEJBException("Deployment does not exist in this container. Deployment(id='"+deployID+"'), Container(id='"+containerID+"')"); 239 240 // Use the backup way to determine call type if null was supplied. 241 if (type == null) type = deployInfo.getInterfaceType(callInterface); 242 243 ThreadContext callContext = new ThreadContext(deployInfo, primKey); 244 245 ThreadContext oldCallContext = ThreadContext.enter(callContext); 246 try { 247 248 boolean authorized = securityService.isCallerAuthorized(callMethod, type); 249 if (!authorized) { 250 throw new ApplicationException(new EJBAccessException("Unauthorized Access by Principal Denied")); 251 } 252 253 Class declaringClass = callMethod.getDeclaringClass(); 254 String methodName = callMethod.getName(); 255 256 if (EJBHome.class.isAssignableFrom(declaringClass) || EJBLocalHome.class.isAssignableFrom(declaringClass)) { 257 if (declaringClass != EJBHome.class && declaringClass != EJBLocalHome.class) { 258 if (methodName.startsWith("create")) { 259 return createEJBObject(callMethod, args, callContext); 260 } else if (methodName.equals("findByPrimaryKey")) { 261 return findByPrimaryKey(callMethod, args, callContext); 262 } else if (methodName.startsWith("find")) { 263 return findEJBObject(callMethod, args, callContext); 264 } else { 265 return homeMethod(callMethod, args, callContext); 266 } 267 } else if (methodName.equals("remove")) { 268 removeEJBObject(callMethod, callContext); 269 return null; 270 } 271 } else if ((EJBObject.class == declaringClass || EJBLocalObject.class == declaringClass) && methodName.equals("remove")) { 272 removeEJBObject(callMethod, callContext); 273 return null; 274 } 275 276 // business method 277 callContext.setCurrentOperation(Operation.BUSINESS); 278 callContext.setCurrentAllowedStates(EntityContext.getStates()); 279 Method runMethod = deployInfo.getMatchingBeanMethod(callMethod); 280 281 callContext.set(Method.class, runMethod); 282 283 Object retValue = businessMethod(callMethod, runMethod, args, callContext); 284 285 return retValue; 286 } finally { 287 ThreadContext.exit(oldCallContext); 288 } 289 } 290 291 private EntityBean createNewInstance(ThreadContext callContext) { 292 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 293 try { 294 EntityBean bean = (EntityBean) deploymentInfo.getCmpImplClass().newInstance(); 295 return bean; 296 } catch (Exception e) { 297 throw new EJBException("Unable to create new entity bean instance " + deploymentInfo.getCmpImplClass(), e); 298 } 299 } 300 301 private ThreadContext createThreadContext(EntityBean entityBean) { 302 if (entityBean == null) throw new NullPointerException("entityBean is null"); 303 304 CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) getDeploymentInfoByClass(entityBean.getClass()); 305 KeyGenerator keyGenerator = deployInfo.getKeyGenerator(); 306 Object primaryKey = keyGenerator.getPrimaryKey(entityBean); 307 308 ThreadContext callContext = new ThreadContext(deployInfo, primaryKey); 309 return callContext; 310 } 311 312 private void setEntityContext(EntityBean entityBean) { 313 if (entityBean == null) throw new NullPointerException("entityBean is null"); 314 315 // activating entity doen't have a primary key 316 CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) getDeploymentInfoByClass(entityBean.getClass()); 317 318 ThreadContext callContext = new ThreadContext(deployInfo, null); 319 callContext.setCurrentOperation(Operation.SET_CONTEXT); 320 callContext.setCurrentAllowedStates(EntityContext.getStates()); 321 322 ThreadContext oldCallContext = ThreadContext.enter(callContext); 323 try { 324 entityBean.setEntityContext(new EntityContext(securityService)); 325 } catch (RemoteException e) { 326 throw new EJBException(e); 327 } finally { 328 ThreadContext.exit(oldCallContext); 329 } 330 } 331 332 private void unsetEntityContext(EntityBean entityBean) { 333 if (entityBean == null) throw new NullPointerException("entityBean is null"); 334 335 ThreadContext callContext = createThreadContext(entityBean); 336 callContext.setCurrentOperation(Operation.UNSET_CONTEXT); 337 callContext.setCurrentAllowedStates(EntityContext.getStates()); 338 339 ThreadContext oldCallContext = ThreadContext.enter(callContext); 340 try { 341 entityBean.unsetEntityContext(); 342 } catch (RemoteException e) { 343 throw new EJBException(e); 344 } finally { 345 ThreadContext.exit(oldCallContext); 346 } 347 } 348 349 private void ejbLoad(EntityBean entityBean) { 350 if (entityBean == null) throw new NullPointerException("entityBean is null"); 351 352 ThreadContext callContext = createThreadContext(entityBean); 353 callContext.setCurrentOperation(Operation.LOAD); 354 callContext.setCurrentAllowedStates(EntityContext.getStates()); 355 356 ThreadContext oldCallContext = ThreadContext.enter(callContext); 357 try { 358 entityBean.ejbLoad(); 359 } catch (RemoteException e) { 360 throw new EJBException(e); 361 } finally { 362 ThreadContext.exit(oldCallContext); 363 } 364 365 // if we call load we must call store 366 try { 367 //noinspection unchecked 368 Set<EntityBean> registeredEntities = (LinkedHashSet<EntityBean>) synchronizationRegistry.getResource(ENTITIES_TO_STORE); 369 if (registeredEntities == null) { 370 registeredEntities = new LinkedHashSet<EntityBean>(); 371 synchronizationRegistry.putResource(ENTITIES_TO_STORE, registeredEntities); 372 synchronizationRegistry.registerInterposedSynchronization(new Synchronization() { 373 public void beforeCompletion() { 374 //noinspection unchecked 375 Set<EntityBean> registeredEntities = (LinkedHashSet<EntityBean>) synchronizationRegistry.getResource(ENTITIES_TO_STORE); 376 if (registeredEntities == null) { 377 return; 378 } 379 for (EntityBean entityBean : registeredEntities) { 380 ejbStore(entityBean); 381 } 382 } 383 public void afterCompletion(int i) { 384 } 385 }); 386 } 387 registeredEntities.add(entityBean); 388 } catch (Exception e) { 389 } 390 } 391 392 private void ejbStore(EntityBean entityBean) { 393 if (entityBean == null) throw new NullPointerException("entityBean is null"); 394 395 ThreadContext callContext = createThreadContext(entityBean); 396 callContext.setCurrentOperation(Operation.STORE); 397 callContext.setCurrentAllowedStates(EntityContext.getStates()); 398 399 ThreadContext oldCallContext = ThreadContext.enter(callContext); 400 try { 401 entityBean.ejbStore(); 402 } catch (RemoteException e) { 403 throw new EJBException(e); 404 } finally { 405 ThreadContext.exit(oldCallContext); 406 } 407 } 408 409 private void ejbRemove(EntityBean entityBean) throws RemoveException { 410 if (entityBean == null) throw new NullPointerException("entityBean is null"); 411 if (isDeleted(entityBean)) return; 412 413 ThreadContext callContext = createThreadContext(entityBean); 414 callContext.setCurrentOperation(Operation.REMOVE); 415 callContext.setCurrentAllowedStates(EntityContext.getStates()); 416 417 ThreadContext oldCallContext = ThreadContext.enter(callContext); 418 try { 419 entityBean.ejbRemove(); 420 } catch (RemoteException e) { 421 throw new EJBException(e); 422 } finally { 423 // clear relationships 424 // todo replace with interface call when CmpEntityBean interface is added 425 try { 426 entityBean.getClass().getMethod("OpenEJB_deleted").invoke(entityBean); 427 } catch (Exception ignored) { 428 } 429 cancelTimers(callContext); 430 ThreadContext.exit(oldCallContext); 431 } 432 } 433 434 private boolean isDeleted(EntityBean entityBean) { 435 try { 436 return (Boolean)entityBean.getClass().getMethod("OpenEJB_isDeleted").invoke(entityBean); 437 } catch (NoSuchMethodException e) { 438 return false; 439 } catch (Exception e) { 440 throw new EJBException(e); 441 } 442 } 443 444 private void ejbActivate(EntityBean entityBean) { 445 if (entityBean == null) throw new NullPointerException("entityBean is null"); 446 447 ThreadContext callContext = createThreadContext(entityBean); 448 callContext.setCurrentOperation(Operation.ACTIVATE); 449 callContext.setCurrentAllowedStates(EntityContext.getStates()); 450 451 ThreadContext oldCallContext = ThreadContext.enter(callContext); 452 try { 453 entityBean.ejbActivate(); 454 } catch (RemoteException e) { 455 throw new EJBException(e); 456 } finally { 457 ThreadContext.exit(oldCallContext); 458 } 459 } 460 461 private void ejbPassivate(EntityBean entityBean) { 462 if (entityBean == null) throw new NullPointerException("entityBean is null"); 463 464 ThreadContext callContext = createThreadContext(entityBean); 465 callContext.setCurrentOperation(Operation.PASSIVATE); 466 callContext.setCurrentAllowedStates(EntityContext.getStates()); 467 468 ThreadContext oldCallContext = ThreadContext.enter(callContext); 469 try { 470 entityBean.ejbPassivate(); 471 } catch (RemoteException e) { 472 throw new EJBException(e); 473 } finally { 474 ThreadContext.exit(oldCallContext); 475 } 476 } 477 478 private Object businessMethod(Method callMethod, Method runMethod, Object[] args, ThreadContext callContext) throws OpenEJBException { 479 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 480 481 TransactionPolicy txPolicy = createTransactionPolicy(deploymentInfo.getTransactionType(callMethod), callContext); 482 483 EntityBean bean; 484 Object returnValue = null; 485 486 entrancyTracker.enter(deploymentInfo, callContext.getPrimaryKey()); 487 try { 488 bean = (EntityBean) cmpEngine.loadBean(callContext, callContext.getPrimaryKey()); 489 if (bean == null) { 490 throw new NoSuchObjectException(deploymentInfo.getDeploymentID() + " : " + callContext.getPrimaryKey()); 491 } 492 493 returnValue = runMethod.invoke(bean, args); 494 495 // when there is not transaction, merge the data from the bean back into the cmp engine 496 cmpEngine.storeBeanIfNoTx(callContext, bean); 497 } catch (NoSuchObjectException e) { 498 handleApplicationException(txPolicy, e, false); 499 } catch (Throwable e) { 500 if (e instanceof InvocationTargetException) { 501 e = ((InvocationTargetException) e).getTargetException(); 502 } 503 504 ExceptionType type = callContext.getDeploymentInfo().getExceptionType(e); 505 if (type == ExceptionType.SYSTEM) { 506 /* System Exception ****************************/ 507 handleSystemException(txPolicy, e, callContext); 508 } else { 509 /* Application Exception ***********************/ 510 handleApplicationException(txPolicy, e, type == ExceptionType.APPLICATION_ROLLBACK); 511 } 512 } finally { 513 entrancyTracker.exit(deploymentInfo, callContext.getPrimaryKey()); 514 afterInvoke(txPolicy, callContext); 515 } 516 517 return returnValue; 518 } 519 520 private Object homeMethod(Method callMethod, Object[] args, ThreadContext callContext) throws OpenEJBException { 521 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 522 523 TransactionPolicy txPolicy = createTransactionPolicy(deploymentInfo.getTransactionType(callMethod), callContext); 524 525 EntityBean bean; 526 Object returnValue = null; 527 try { 528 /* 529 Obtain a bean instance from the method ready pool 530 */ 531 bean = createNewInstance(callContext); 532 533 // set the entity context 534 setEntityContext(bean); 535 536 try { 537 callContext.setCurrentOperation(Operation.HOME); 538 callContext.setCurrentAllowedStates(EntityContext.getStates()); 539 540 Method runMethod = deploymentInfo.getMatchingBeanMethod(callMethod); 541 542 try { 543 returnValue = runMethod.invoke(bean, args); 544 } catch (IllegalArgumentException e) { 545 System.out.println("********************************************************"); 546 System.out.println("callMethod = " + callMethod); 547 System.out.println("runMethod = " + runMethod); 548 System.out.println("bean = " + bean.getClass().getName()); 549 550 throw e; 551 } 552 } finally { 553 unsetEntityContext(bean); 554 } 555 } catch (Throwable e) { 556 if (e instanceof InvocationTargetException) { 557 e = ((InvocationTargetException) e).getTargetException(); 558 } 559 560 ExceptionType type = callContext.getDeploymentInfo().getExceptionType(e); 561 if (type == ExceptionType.SYSTEM) { 562 /* System Exception ****************************/ 563 handleSystemException(txPolicy, e, callContext); 564 565 } else { 566 /* Application Exception ***********************/ 567 handleApplicationException(txPolicy, e, type == ExceptionType.APPLICATION_ROLLBACK); 568 } 569 } finally { 570 afterInvoke(txPolicy, callContext); 571 } 572 573 return returnValue; 574 } 575 576 private ProxyInfo createEJBObject(Method callMethod, Object[] args, ThreadContext callContext) throws OpenEJBException { 577 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 578 579 TransactionPolicy txPolicy = createTransactionPolicy(deploymentInfo.getTransactionType(callMethod), callContext); 580 581 EntityBean bean; 582 Object primaryKey = null; 583 584 try { 585 // Obtain a bean instance from the method ready pool 586 bean = createNewInstance(callContext); 587 588 // set the entity context 589 setEntityContext(bean); 590 591 // Obtain the proper ejbCreate() method 592 Method ejbCreateMethod = deploymentInfo.getMatchingBeanMethod(callMethod); 593 594 // Set current operation for allowed operations 595 callContext.setCurrentOperation(Operation.CREATE); 596 callContext.setCurrentAllowedStates(EntityContext.getStates()); 597 598 // Invoke the proper ejbCreate() method on the instance 599 ejbCreateMethod.invoke(bean, args); 600 601 // create the new bean 602 primaryKey = cmpEngine.createBean(bean, callContext); 603 604 // determine post create callback method 605 Method ejbPostCreateMethod = deploymentInfo.getMatchingPostCreateMethod(ejbCreateMethod); 606 607 // create a new context containing the pk for the post create call 608 ThreadContext postCreateContext = new ThreadContext(deploymentInfo, primaryKey); 609 postCreateContext.setCurrentOperation(Operation.POST_CREATE); 610 postCreateContext.setCurrentAllowedStates(EntityContext.getStates()); 611 612 ThreadContext oldContext = ThreadContext.enter(postCreateContext); 613 try { 614 // Invoke the ejbPostCreate method on the bean instance 615 ejbPostCreateMethod.invoke(bean, args); 616 617 // According to section 9.1.5.1 of the EJB 1.1 specification, the "ejbPostCreate(...) 618 // method executes in the same transaction context as the previous ejbCreate(...) method." 619 // 620 // The bean is first insterted using db.create( ) and then after ejbPostCreate( ) its 621 // updated using db.update(). This protocol allows for visablity of the bean after ejbCreate 622 // within the current trasnaction. 623 } finally { 624 ThreadContext.exit(oldContext); 625 } 626 627 // when there is not transaction, merge the data from the bean back into the cmp engine 628 cmpEngine.storeBeanIfNoTx(callContext, bean); 629 } catch (Throwable e) { 630 if (e instanceof InvocationTargetException) { 631 e = ((InvocationTargetException) e).getTargetException(); 632 } 633 634 ExceptionType type = callContext.getDeploymentInfo().getExceptionType(e); 635 if (type == ExceptionType.SYSTEM) { 636 /* System Exception ****************************/ 637 handleSystemException(txPolicy, e, callContext); 638 } else { 639 /* Application Exception ***********************/ 640 handleApplicationException(txPolicy, e, type == ExceptionType.APPLICATION_ROLLBACK); 641 } 642 } finally { 643 afterInvoke(txPolicy, callContext); 644 } 645 646 return new ProxyInfo(deploymentInfo, primaryKey); 647 } 648 649 private Object findByPrimaryKey(Method callMethod, Object[] args, ThreadContext callContext) throws OpenEJBException { 650 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 651 652 TransactionPolicy txPolicy = createTransactionPolicy(deploymentInfo.getTransactionType(callMethod), callContext); 653 654 try { 655 EntityBean bean = (EntityBean) cmpEngine.loadBean(callContext, args[0]); 656 if (bean == null) { 657 throw new ObjectNotFoundException(deploymentInfo.getDeploymentID() + " : " + args[0]); 658 } 659 660 // rebuild the primary key 661 KeyGenerator kg = deploymentInfo.getKeyGenerator(); 662 Object primaryKey = kg.getPrimaryKey(bean); 663 664 // create a new ProxyInfo based on the deployment info and primary key 665 return new ProxyInfo(deploymentInfo, primaryKey); 666 } catch (javax.ejb.FinderException fe) { 667 handleApplicationException(txPolicy, fe, false); 668 } catch (Throwable e) {// handle reflection exception 669 handleSystemException(txPolicy, e, callContext); 670 } finally { 671 afterInvoke(txPolicy, callContext); 672 } 673 throw new AssertionError("Should not get here"); 674 } 675 676 private Object findEJBObject(Method callMethod, Object[] args, ThreadContext callContext) throws OpenEJBException { 677 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 678 679 TransactionPolicy txPolicy = createTransactionPolicy(deploymentInfo.getTransactionType(callMethod), callContext); 680 681 try { 682 List<Object> results = cmpEngine.queryBeans(callContext, callMethod, args); 683 684 KeyGenerator kg = deploymentInfo.getKeyGenerator(); 685 686 // The following block of code is responsible for returning ProxyInfo object(s) for each 687 // matching entity bean found by the query. If its a multi-value find operation a Vector 688 // of ProxyInfo objects will be returned. If its a single-value find operation then a 689 // single ProxyInfo object is returned. 690 if (callMethod.getReturnType() == Collection.class || callMethod.getReturnType() == Enumeration.class) { 691 List<ProxyInfo> proxies = new ArrayList<ProxyInfo>(); 692 for (Object value : results) { 693 EntityBean bean = (EntityBean) value; 694 695 if (value == null) { 696 proxies.add(null); 697 } else { 698 // get the primary key 699 Object primaryKey = kg.getPrimaryKey(bean); 700 701 // create a new ProxyInfo based on the deployment info and primary key and add it to the vector 702 proxies.add(new ProxyInfo(deploymentInfo, primaryKey)); 703 } 704 } 705 if (callMethod.getReturnType() == Enumeration.class) { 706 return new Enumerator(proxies); 707 } else { 708 return proxies; 709 } 710 } else { 711 if (results.size() != 1) { 712 throw new ObjectNotFoundException("A Enteprise bean with deployment_id = " + deploymentInfo.getDeploymentID() + " and primarykey = " + args[0] + " Does not exist"); 713 } 714 715 // create a new ProxyInfo based on the deployment info and primary key 716 EntityBean bean = (EntityBean) results.get(0); 717 if (bean == null) { 718 return null; 719 } else { 720 Object primaryKey = kg.getPrimaryKey(bean); 721 return new ProxyInfo(deploymentInfo, primaryKey); 722 } 723 } 724 } catch (javax.ejb.FinderException fe) { 725 handleApplicationException(txPolicy, fe, false); 726 } catch (Throwable e) {// handle reflection exception 727 handleSystemException(txPolicy, e, callContext); 728 } finally { 729 afterInvoke(txPolicy, callContext); 730 } 731 throw new AssertionError("Should not get here"); 732 } 733 734 public Object select(DeploymentInfo di, String methodSignature, String returnType, Object... args) throws FinderException { 735 CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) di; 736 String signature = deploymentInfo.getAbstractSchemaName() + "." + methodSignature; 737 738 try { 739 // execute the select query 740 Collection<Object> results = cmpEngine.queryBeans(deploymentInfo, signature, args); 741 742 // 743 // process the results 744 // 745 746 // If we need to return a set... 747 Collection<Object> proxies; 748 if (returnType.equals("java.util.Set")) { 749 // we collect values into a LinkedHashSet to preserve ordering 750 proxies = new LinkedHashSet<Object>(); 751 } else { 752 // otherwise use a simple array list 753 proxies = new ArrayList<Object>(); 754 } 755 756 boolean isSingleValued = !returnType.equals("java.util.Collection") && !returnType.equals("java.util.Set"); 757 ProxyFactory proxyFactory = null; 758 for (Object value : results) { 759 // if this is a single valued query and we already have results, throw FinderException 760 if (isSingleValued && !proxies.isEmpty()) { 761 throw new FinderException("The single valued query " + methodSignature + "returned more than one item"); 762 } 763 764 // if we have an EntityBean, we need to proxy it 765 if (value instanceof EntityBean) { 766 EntityBean entityBean = (EntityBean) value; 767 if (proxyFactory == null) { 768 CoreDeploymentInfo resultInfo = (CoreDeploymentInfo) getDeploymentInfoByClass(entityBean.getClass()); 769 if (resultInfo != null) { 770 proxyFactory = new ProxyFactory(resultInfo); 771 } 772 } 773 774 if (proxyFactory != null) { 775 if (deploymentInfo.isRemoteQueryResults(methodSignature)) { 776 value = proxyFactory.createRemoteProxy(entityBean, this); 777 } else { 778 value = proxyFactory.createLocalProxy(entityBean, this); 779 } 780 } 781 } 782 proxies.add(value); 783 } 784 785 // if not single valued, return the set 786 if (!isSingleValued) { 787 return proxies; 788 } 789 790 // single valued query that returned no rows, is an exception 791 if (proxies.isEmpty()) { 792 throw new ObjectNotFoundException(); 793 } 794 795 // return the single item.... multiple return values was handled in for loop above 796 Object returnValue = proxies.iterator().next(); 797 return returnValue; 798 } catch (RuntimeException e) { 799 throw new EJBException(e); 800 } 801 } 802 803 public int update(DeploymentInfo di, String methodSignature, Object... args) throws FinderException { 804 CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) di; 805 String signature = deploymentInfo.getAbstractSchemaName() + "." + methodSignature; 806 807 // exectue the update query 808 int result = cmpEngine.executeUpdateQuery(deploymentInfo, signature, args); 809 return result; 810 } 811 812 private void removeEJBObject(Method callMethod, ThreadContext callContext) throws OpenEJBException { 813 CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo(); 814 815 TransactionPolicy txPolicy = createTransactionPolicy(deploymentInfo.getTransactionType(callMethod), callContext); 816 817 try { 818 EntityBean entityBean = (EntityBean) cmpEngine.loadBean(callContext, callContext.getPrimaryKey()); 819 if (entityBean == null) { 820 throw new NoSuchObjectException(callContext.getDeploymentInfo().getDeploymentID() + " " + callContext.getPrimaryKey()); 821 } 822 ejbRemove(entityBean); 823 cmpEngine.removeBean(callContext); 824 } catch (NoSuchObjectException e) { 825 handleApplicationException(txPolicy, e, false); 826 } catch (Throwable e) {// handle reflection exception 827 handleSystemException(txPolicy, e, callContext); 828 } finally { 829 afterInvoke(txPolicy, callContext); 830 } 831 } 832 833 private void cancelTimers(ThreadContext threadContext) { 834 CoreDeploymentInfo deploymentInfo = threadContext.getDeploymentInfo(); 835 Object primaryKey = threadContext.getPrimaryKey(); 836 837 // stop timers 838 if (primaryKey != null && deploymentInfo.getEjbTimerService() != null) { 839 EjbTimerService timerService = deploymentInfo.getEjbTimerService(); 840 if (timerService != null && timerService instanceof EjbTimerServiceImpl) { 841 for (Timer timer : deploymentInfo.getEjbTimerService().getTimers(primaryKey)) { 842 timer.cancel(); 843 } 844 } 845 } 846 } 847 848 private class ContainerCmpCallback implements CmpCallback { 849 public void setEntityContext(EntityBean entity) { 850 CmpContainer.this.setEntityContext(entity); 851 } 852 853 public void unsetEntityContext(EntityBean entity) { 854 CmpContainer.this.unsetEntityContext(entity); 855 } 856 857 public void ejbActivate(EntityBean entity) { 858 CmpContainer.this.ejbActivate(entity); 859 } 860 861 public void ejbPassivate(EntityBean entity) { 862 CmpContainer.this.ejbPassivate(entity); 863 } 864 865 public void ejbLoad(EntityBean entity) { 866 CmpContainer.this.ejbLoad(entity); 867 } 868 869 public void ejbStore(EntityBean entity) { 870 CmpContainer.this.ejbStore(entity); 871 } 872 873 public void ejbRemove(EntityBean entity) throws RemoveException { 874 CmpContainer.this.ejbRemove(entity); 875 } 876 } 877 }