Java Source Code: com.caucho.ejb.cfg.EjbSessionBean


   1: /*
   2:  * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
   3:  *
   4:  * This file is part of Resin(R) Open Source
   5:  *
   6:  * Each copy or derived work must preserve the copyright notice and this
   7:  * notice unmodified.
   8:  *
   9:  * Resin Open Source is free software; you can redistribute it and/or modify
  10:  * it under the terms of the GNU General Public License as published by
  11:  * the Free Software Foundation; either version 2 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * Resin Open Source is distributed in the hope that it will be useful,
  15:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
  17:  * of NON-INFRINGEMENT.  See the GNU General Public License for more
  18:  * details.
  19:  *
  20:  * You should have received a copy of the GNU General Public License
  21:  * along with Resin Open Source; if not, write to the
  22:  *
  23:  *   Free Software Foundation, Inc.
  24:  *   59 Temple Place, Suite 330
  25:  *   Boston, MA 02111-1307  USA
  26:  *
  27:  * @author Scott Ferguson
  28:  */
  29: 
  30: package com.caucho.ejb.cfg;
  31: 
  32: import com.caucho.ejb.cfg21.EjbHomeView;
  33: import com.caucho.config.program.ContainerProgram;
  34: import com.caucho.config.ConfigException;
  35: import com.caucho.config.LineConfigException;
  36: import com.caucho.config.types.EnvEntry;
  37: import com.caucho.ejb.AbstractServer;
  38: import com.caucho.ejb.gen.BeanGenerator;
  39: import com.caucho.ejb.gen21.BeanAssembler;
  40: import com.caucho.ejb.gen.SessionGenerator;
  41: import com.caucho.ejb.gen.StatefulGenerator;
  42: import com.caucho.ejb.gen.StatelessGenerator;
  43: import com.caucho.ejb.manager.EjbContainer;
  44: import com.caucho.ejb.session.StatefulServer;
  45: import com.caucho.ejb.session.StatelessServer;
  46: import com.caucho.java.gen.JavaClassGenerator;
  47: import com.caucho.util.L10N;
  48: 
  49: import javax.annotation.PostConstruct;
  50: import javax.ejb.*;
  51: import java.util.ArrayList;
  52: import java.lang.reflect.*;
  53: 
  54: /**
  55:  * Configuration for an ejb entity bean.
  56:  */
  57:	  public class EjbSessionBean extends EjbBean {
  58:  private static final L10N L = new L10N(EjbSessionBean.class);
  59:
  60:  private boolean _isStateless;
  61:
  62:  // Default is container managed transaction.
  63:  private boolean _isContainerTransaction = true;
  64:
  65:  private SessionGenerator _sessionBean;
  66:  
  67:  /**
  68:   * Creates a new session bean configuration.
  69:   */
  70:  public EjbSessionBean(EjbConfig ejbConfig, String ejbModuleName)
  71:	    {
  72:    super(ejbConfig, ejbModuleName);
  73:  }
  74:
  75:  /**
  76:   * Returns the kind of bean.
  77:   */
  78:  public String getEJBKind()
  79:	    {
  80:    return "session";
  81:  }
  82:
  83:  /**
  84:   * Sets the ejb implementation class.
  85:   */
  86:  @Override
  87:  public void setEJBClass(Class type)
  88:    throws ConfigException
  89:	    {
  90:    super.setEJBClass(type);
  91:
  92:    ApiClass ejbClass = getEJBClassWrapper();
  93:
  94:    if (ejbClass.isAbstract())
  95:      throw error(L.l("'{0}' must not be abstract.  Session bean implementations must be fully implemented.", ejbClass.getName()));
  96:
  97:	      if (type.isAnnotationPresent(Stateless.class)) {
  98:      Stateless stateless = (Stateless) type.getAnnotation(Stateless.class);
  99:
 100:      if (getEJBName() == null && ! "".equals(stateless.name()))
 101:    setEJBName(stateless.name());
 102:      
 103:      _isStateless = true;
 104:    }
 105:	      else if (ejbClass.isAnnotationPresent(Stateful.class)) {
 106:      Stateful stateful = (Stateful) type.getAnnotation(Stateful.class);
 107:
 108:      if (getEJBName() == null && ! "".equals(stateful.name()))
 109:    setEJBName(stateful.name());
 110:      
 111:      _isStateless = false;
 112:    }
 113:
 114:    if (getEJBName() == null)
 115:      setEJBName(ejbClass.getSimpleName());
 116:    
 117:    /*
 118:      if (! ejbClass.isAssignableTo(SessionBean.class)
 119:          && ! ejbClass.isAnnotationPresent(Stateless.class)
 120:          && ! ejbClass.isAnnotationPresent(Stateful.class))
 121:        throw error(L.l("'{0}' must implement SessionBean or @Stateless or @Stateful.  Session beans must implement javax.ejb.SessionBean.", ejbClass.getName()));
 122:    */
 123:
 124:    // introspectSession();
 125:  }
 126:
 127:  /**
 128:   * Returns true if it's a stateless session bean.
 129:   */
 130:  public boolean isStateless()
 131:	    {
 132:    return _isStateless;
 133:  }
 134:
 135:  /**
 136:   * Set true if it's a stateless session bean.
 137:   */
 138:  public void setSessionType(String type)
 139:    throws ConfigException
 140:	    {
 141:    if (type.equals("Stateful"))
 142:      _isStateless = false;
 143:    else if (type.equals("Stateless"))
 144:      _isStateless = true;
 145:    else
 146:      throw new ConfigException(L.l("'{0}' is an unknown session-type.  session-type must be 'Stateless' or 'Stateful'.", type));
 147:  }
 148:
 149:  /**
 150:   * Returns true if the container handles transactions.
 151:   */
 152:  public boolean isContainerTransaction()
 153:	    {
 154:    return _isContainerTransaction;
 155:  }
 156:
 157:  /**
 158:   * Set true if the container handles transactions.
 159:   */
 160:  public void setTransactionType(String type)
 161:    throws ConfigException
 162:	    {
 163:    if (type.equals("Container"))
 164:      _isContainerTransaction = true;
 165:    else if (type.equals("Bean"))
 166:      _isContainerTransaction = false;
 167:    else
 168:      throw new ConfigException(L.l("'{0}' is an unknown transaction-type.  transaction-type must be 'Container' or 'Bean'.", type));
 169:  }
 170:
 171:  /**
 172:   * Configure initialization.
 173:   */
 174:  @PostConstruct
 175:  public void init()
 176:    throws ConfigException
 177:	    {
 178:    super.init();
 179:
 180:	      try {
 181:	        if (getRemoteHome() != null) {
 182:        validateHome(getRemoteHome(), getRemoteList().get(0));
 183:      }
 184:
 185:	        if (getLocalHome() != null) {
 186:        validateHome(getLocalHome(), getLocalList().get(0));
 187:      }
 188:
 189:      for (ApiClass remoteApi : getRemoteList())
 190:        validateRemote(remoteApi);
 191:
 192:      for (ApiClass localApi : getLocalList())
 193:        validateRemote(localApi);
 194:
 195:	        if (getEJBClass() == null) {
 196:        throw error(L.l("'{0}' does not have a defined ejb-class.  Session beans must have an ejb-class.",
 197:                        getEJBName()));
 198:      }
 199:
 200:	        if (! SessionSynchronization.class.isAssignableFrom(getEJBClassWrapper().getJavaClass())) {
 201:      }
 202:	        else if (isStateless()) {
 203:        throw error(L.l("'{0}' must not implement SessionSynchronization.  Stateless session beans must not implement SessionSynchronization.",
 204:                        getEJBClass().getName()));
 205:      }
 206:	        else if (! _isContainerTransaction) {
 207:        throw error(L.l("'{0}' must not implement SessionSynchronization.  Session beans with Bean-managed transactions may not use SessionSynchronization.",
 208:                        getEJBClass().getName()));
 209:      }
 210:    } catch (LineConfigException e) {
 211:      throw e;
 212:    } catch (ConfigException e) {
 213:      throw new LineConfigException(getLocation() + e.getMessage(), e);
 214:    }
 215:
 216:    /*
 217:    if (isStateless())
 218:      J2EEManagedObject.register(new StatelessSessionBean(this));
 219:    else
 220:      J2EEManagedObject.register(new StatefulSessionBean(this));
 221:     */
 222:  }
 223:  
 224:  /**
 225:   * Creates the bean generator for the session bean.
 226:   */
 227:  @Override
 228:  protected BeanGenerator createBeanGenerator()
 229:	    {
 230:    if (_isStateless)
 231:      _sessionBean = new StatelessGenerator(getEJBName(), getEJBClassWrapper());
 232:    else
 233:      _sessionBean = new StatefulGenerator(getEJBName(), getEJBClassWrapper());
 234:    
 235:    return _sessionBean;
 236:  }
 237:
 238:  /**
 239:   * Obtain and apply initialization from annotations.
 240:   */
 241:  public void initIntrospect()
 242:    throws ConfigException
 243:	    {
 244:    super.initIntrospect();
 245:
 246:    ApiClass type = getEJBClassWrapper();
 247:
 248:    // XXX: ejb/0f78
 249:    if (type == null)
 250:      return;
 251:
 252:    // ejb/0j20
 253:    if (! type.isAnnotationPresent(Stateful.class)
 254:        && ! type.isAnnotationPresent(Stateless.class)
 255:    && ! isAllowPOJO())
 256:      return;
 257:
 258:    /* TCK: ejb/0f6d: bean with local and remote interfaces
 259:    if (_localHome != null || _localList.size() != 0
 260:        || _remoteHome != null || _remoteList.size() != 0)
 261:      return;
 262:    */
 263:
 264:    Class []ifs = type.getInterfaces();
 265:
 266:    ArrayList<ApiClass> interfaceList = new ArrayList<ApiClass>();
 267:
 268:	      for (int i = 0; i < ifs.length; i++) {
 269:      ApiClass localApi = new ApiClass(ifs[i]);
 270:
 271:      Local local = (Local) ifs[i].getAnnotation(Local.class);
 272:
 273:	        if (local != null) {
 274:        setLocalWrapper(localApi);
 275:        continue;
 276:      }
 277:
 278:      Remote remote
 279:    = (Remote) ifs[i].getAnnotation(javax.ejb.Remote.class);
 280:
 281:	        if (remote != null || java.rmi.Remote.class.isAssignableFrom(ifs[i])) {
 282:        setRemoteWrapper(localApi);
 283:        continue;
 284:      }
 285:
 286:      if (ifs[i].getName().equals("java.io.Serializable"))
 287:        continue;
 288:
 289:      if (ifs[i].getName().equals("java.io.Externalizable"))
 290:        continue;
 291:
 292:      if (ifs[i].getName().startsWith("javax.ejb"))
 293:        continue;
 294:
 295:      if (ifs[i].getName().equals("java.rmi.Remote"))
 296:        continue;
 297:
 298:      if (! interfaceList.contains(localApi))
 299:        interfaceList.add(localApi);
 300:    }
 301:
 302:	      // if (getLocalList().size() != 0 || getRemoteList().size() != 0) {
 303:    if (_localHome != null || _localList.size() != 0
 304:	          || _remoteHome != null || _remoteList.size() != 0) {
 305:    }
 306:    else if (interfaceList.size() == 0)
 307:      throw new ConfigException(L.l("'{0}' has no interfaces.  Can't currently generate.",
 308:                                    type.getName()));
 309:    else if (interfaceList.size() != 1)
 310:      throw new ConfigException(L.l("'{0}' has multiple interfaces, but none are marked as @Local or @Remote.\n{1}",
 311:                                    type.getName(),
 312:                                    interfaceList.toString()));
 313:	      else {
 314:      setLocalWrapper(interfaceList.get(0));
 315:    }
 316:
 317:    // XXX: Check ejb30/bb/session/stateless/migration/twothree/annotated
 318:    // There is a conflict between 2.1 and 3.0 interfaces.
 319:
 320:    // ejb/0f6f
 321:    // The session bean might have @RemoteHome for EJB 2.1 and
 322:    // the @Remote interface for EJB 3.0 (same with @LocalHome and @Local).
 323:    // TCK: ejb30/bb/session/stateful/sessioncontext/annotated
 324:
 325:    ApiClass ejbClass = getEJBClassWrapper();
 326:
 327:    LocalHome localHomeAnn = ejbClass.getAnnotation(LocalHome.class);
 328:
 329:    // ejb/0f6f
 330:	      if (localHomeAnn != null) {
 331:      Class localHome = localHomeAnn.value();
 332:      setLocalHome(localHome);
 333:    }
 334:
 335:    RemoteHome remoteHomeAnn = ejbClass.getAnnotation(RemoteHome.class);
 336:
 337:    // ejb/0f6f
 338:	      if (remoteHomeAnn != null) {
 339:      Class home = remoteHomeAnn.value();
 340:      setHome(home);
 341:    }
 342:  }
 343:
 344:  /**
 345:   * Creates the assembler for the bean.
 346:   */
 347:  protected BeanAssembler createAssembler(String fullClassName)
 348:	    {
 349:    throw new IllegalStateException(getClass().getName());
 350:  }
 351:
 352:  /**
 353:   * Adds the assemblers.
 354:   */
 355:  protected void addImports(BeanAssembler assembler)
 356:	    {
 357:    super.addImports(assembler);
 358:
 359:	      if (isStateless()) {
 360:      assembler.addImport("com.caucho.ejb.session.StatelessServer");
 361:      assembler.addImport("com.caucho.ejb.session.AbstractStatelessContext");
 362:      assembler.addImport("com.caucho.ejb.session.StatelessHome");
 363:      assembler.addImport("com.caucho.ejb.session.StatelessObject21");
 364:      assembler.addImport("com.caucho.ejb.session.StatelessObject");
 365:    }
 366:	      else {
 367:      assembler.addImport("com.caucho.ejb.session.SessionServer");
 368:      assembler.addImport("com.caucho.ejb.session.AbstractSessionContext");
 369:      assembler.addImport("com.caucho.ejb.session.SessionHome");
 370:      assembler.addImport("com.caucho.ejb.session.SessionObject21");
 371:      assembler.addImport("com.caucho.ejb.session.SessionObject");
 372:    }
 373:  }
 374:
 375:  /**
 376:   * Creates the views.
 377:   */
 378:  protected EjbHomeView createHomeView(ApiClass homeClass, String prefix)
 379:    throws ConfigException
 380:	    {
 381:    if (isStateless())
 382:      return new EjbStatelessHomeView(this, homeClass, prefix);
 383:    else
 384:      return new EjbSessionHomeView(this, homeClass, prefix);
 385:  }
 386:
 387:  /**
 388:   * Deploys the bean.
 389:   */
 390:  @Override
 391:  public AbstractServer deployServer(EjbContainer ejbContainer,
 392:                                     JavaClassGenerator javaGen)
 393:    throws ClassNotFoundException, ConfigException
 394:	    {
 395:    AbstractServer server;
 396:
 397:    if (isStateless())
 398:      server = new StatelessServer(ejbContainer);
 399:    else
 400:      server = new StatefulServer(ejbContainer);
 401:
 402:    server.setModuleName(getEJBModuleName());
 403:    server.setEJBName(getEJBName());
 404:    server.setMappedName(getMappedName());
 405:    server.setId(getEJBModuleName() + "#" + getEJBName());
 406:    server.setContainerTransaction(_isContainerTransaction);
 407:    
 408:    server.setEjbClass(loadClass(getEJBClass().getName()));
 409:
 410:    ApiClass remoteHome = getRemoteHome();
 411:    if (remoteHome != null)
 412:      server.setRemoteHomeClass(loadClass(remoteHome.getName()));
 413:
 414:    ArrayList<ApiClass> remoteList = _sessionBean.getRemoteApi();
 415:	      if (remoteList.size() > 0) {
 416:      ArrayList<Class> classList = new ArrayList<Class>();
 417:	        for (ApiClass apiClass : remoteList) {
 418:    classList.add(loadClass(apiClass.getName()));
 419:      }
 420:      
 421:      server.setRemoteApiList(classList);
 422:    }
 423:
 424:    /*
 425:    if (getRemote21() != null)
 426:      server.setRemote21(loadClass(getRemote21().getName()));
 427:     */
 428:
 429:    ApiClass localHome = getLocalHome();
 430:    if (localHome != null)
 431:      server.setLocalHomeClass(loadClass(localHome.getName()));
 432:
 433:    ArrayList<ApiClass> localList = _sessionBean.getLocalApi();
 434:	      if (localList.size() > 0) {
 435:      ArrayList<Class> classList = new ArrayList<Class>();
 436:	        for (ApiClass apiClass : localList) {
 437:    classList.add(loadClass(apiClass.getName()));
 438:      }
 439:      
 440:      server.setLocalApiList(classList);
 441:    }
 442:
 443:    /*
 444:    if (getLocal21() != null)
 445:      server.setLocal21(loadClass(getLocal21().getName()));
 446:     */
 447:    
 448:    Class contextImplClass = javaGen.loadClass(getSkeletonName());
 449:
 450:    server.setContextImplClass(contextImplClass);
 451:
 452:    Class beanClass = javaGen.loadClass(getEJBClass().getName());
 453:
 454:    Thread thread = Thread.currentThread();
 455:    ClassLoader oldLoader = thread.getContextClassLoader();
 456:
 457:	      try {
 458:      thread.setContextClassLoader(server.getClassLoader());
 459:
 460:      ContainerProgram initContainer = getInitProgram();
 461:
 462:      server.setInitProgram(initContainer);
 463:
 464:	        try {
 465:        if (getServerProgram() != null)
 466:          getServerProgram().configure(server);
 467:      } catch (RuntimeException e) {
 468:        throw e;
 469:      } catch (Exception e) {
 470:        throw ConfigException.create(e);
 471:      }
 472:
 473:    } finally {
 474:      thread.setContextClassLoader(oldLoader);
 475:    }
 476:
 477:    return server;
 478:  }
 479:
 480:  private void introspectSession()
 481:    throws ConfigException
 482:	    {
 483:    ApiClass ejbClass = getEJBClassWrapper();
 484:
 485:    if (ejbClass.isAnnotationPresent(Stateless.class))
 486:      introspectStateless(ejbClass);
 487:    else if (ejbClass.isAnnotationPresent(Stateful.class))
 488:      introspectStateful(ejbClass);
 489:  }
 490:
 491:  private void introspectStateless(ApiClass type)
 492:    throws ConfigException
 493:	    {
 494:    String className = type.getName();
 495:
 496:    Stateless stateless = type.getAnnotation(Stateless.class);
 497:
 498:    setAllowPOJO(true);
 499:
 500:    setSessionType("Stateless");
 501:
 502:    setTransactionType(type);
 503:
 504:    String name;
 505:    if (stateless != null)
 506:      name = stateless.name();
 507:    else
 508:      name = className;
 509:
 510:    introspectBean(type, name);
 511:  }
 512:
 513:  private void introspectStateful(ApiClass type)
 514:    throws ConfigException
 515:	    {
 516:    String className = type.getName();
 517:
 518:    Stateful stateful = type.getAnnotation(Stateful.class);
 519:
 520:    setAllowPOJO(true);
 521:
 522:    setSessionType("Stateful");
 523:
 524:    setTransactionType(type);
 525:
 526:    String name;
 527:    if (stateful != null)
 528:      name = stateful.name();
 529:    else
 530:      name = className;
 531:
 532:    introspectBean(type, name);
 533:  }
 534:
 535:  private void setTransactionType(ApiClass type)
 536:	    {
 537:    TransactionManagement transaction
 538:      = type.getAnnotation(TransactionManagement.class);
 539:    
 540:    if (transaction == null)
 541:      setTransactionType("Container");
 542:    else if (TransactionManagementType.BEAN.equals(transaction.value()))
 543:      setTransactionType("Bean");
 544:    else
 545:      setTransactionType("Container");
 546:  }
 547:
 548:  private void validateMethods()
 549:    throws ConfigException
 550:	    {
 551:  }
 552:
 553:  /**
 554:   * Validates the home interface.
 555:   */
 556:  private void validateHome(ApiClass homeClass, ApiClass objectClass)
 557:    throws ConfigException
 558:	    {
 559:    ApiClass beanClass = getEJBClassWrapper();
 560:    String beanName = beanClass.getName();
 561:
 562:    if (homeClass == null)
 563:      return;
 564:    String homeName = homeClass.getName();
 565:    String objectName = objectClass.getName();
 566:
 567:    boolean hasFindByPrimaryKey = false;
 568:
 569:    if (! homeClass.isPublic())
 570:      throw error(L.l("'{0}' must be public", homeName));
 571:
 572:    if (beanClass.isFinal())
 573:      throw error(L.l("'{0}' must not be final", beanName));
 574:
 575:    if (beanClass.isAbstract())
 576:      throw error(L.l("'{0}' must not be abstract", beanName));
 577:
 578:    if (! homeClass.isInterface())
 579:      throw error(L.l("'{0}' must be an interface", homeName));
 580:
 581:    boolean hasCreate = false;
 582:
 583:	      for (ApiMethod method : homeClass.getMethods()) {
 584:      String name = method.getName();
 585:      Class []param = method.getParameterTypes();
 586:      Class retType = method.getReturnType();
 587:
 588:      if (method.getDeclaringClass().isAssignableFrom(EJBHome.class)
 589:      || method.getDeclaringClass().isAssignableFrom(EJBLocalHome.class))
 590:        continue;
 591:
 592:      if (EJBHome.class.isAssignableFrom(homeClass.getJavaClass()))
 593:        validateException(method, java.rmi.RemoteException.class);
 594:
 595:	        if (name.startsWith("create")) {
 596:        hasCreate = true;
 597:
 598:        // TCK: ejb30/bb/session/stateless/migration/twothree/descriptor/callLocalSameTxContextTest
 599:        // XXX TCK, needs QA: if a stateless session bean has EJB 3.0 and 2.1 interfaces,
 600:        // it is not required to have a matching ejbCreate().
 601:        // It may have a @PostConstruct method, if any.
 602:        if (isStateless() && name.equals("create"))
 603:          continue;
 604:
 605:        if (isStateless() && (! name.equals("create")
 606:	                    || method.getParameterTypes().length != 0)) {
 607:          throw error(L.l("{0}: '{1}' forbidden in stateless session home.  The create() method for a stateless session bean must have zero arguments.",
 608:                          method.getFullName(),
 609:                          homeName));
 610:        }
 611:
 612:        if (! isAllowPOJO())
 613:          validateException(method, CreateException.class);
 614:
 615:        /* XXX: ejb/0f6f
 616:
 617:           The session bean might have @RemoteHome for EJB 2.1 and the @Remote interface for EJB 3.0
 618:           TCK: ejb30/bb/session/stateful/sessioncontext/annotated
 619:
 620:        if (! retType.equals(objectClass))
 621:          throw error(L.l("{0}: '{1}' must return {2}.  Create methods must return the local or remote interface.",
 622:                          homeName,
 623:                          method.getFullName(),
 624:                          objectClass.getName()));
 625:        */
 626:
 627:        String createName = "ejbC" + name.substring(1);
 628:        ApiMethod implMethod =
 629:          validateNonFinalMethod(createName, param,
 630:                                 method, homeClass, isAllowPOJO());
 631:
 632:	          if (implMethod != null) {
 633:          if (! implMethod.getReturnType().getName().equals("void"))
 634:            throw error(L.l("'{0}' must return {1} in {2}",
 635:                            getFullMethodName(createName, param),
 636:                            "void",
 637:                            beanName));
 638:
 639:          validateExceptions(method, implMethod.getExceptionTypes());
 640:        }
 641:      }
 642:	        else if (name.startsWith("ejb") || name.startsWith("remove")) {
 643:        throw error(L.l("'{0}' forbidden in {1}",
 644:                        method.getFullName(),
 645:                        homeClass.getName()));
 646:      }
 647:    }
 648:
 649:    if (! hasCreate)
 650:      throw error(L.l("'{0}' needs at least one create method.  Session beans need a create method.",
 651:                      homeClass.getName()));
 652:  }
 653:}