1 29 30 package com.caucho.ejb.cfg; 31 32 import com.caucho.amber.manager.AmberPersistenceUnit; 33 import com.caucho.amber.type.EntityType; 34 import com.caucho.bytecode.JClass; 35 import com.caucho.bytecode.JClassLoader; 36 import com.caucho.bytecode.JClassWrapper; 37 import com.caucho.bytecode.JField; 38 import com.caucho.bytecode.JMethod; 39 import com.caucho.config.ConfigException; 40 import com.caucho.config.LineConfigException; 41 import com.caucho.config.types.Period; 42 import com.caucho.ejb.AbstractServer; 43 import com.caucho.ejb.EjbServerManager; 44 import com.caucho.ejb.amber.AmberConfig; 45 import com.caucho.ejb.entity.EntityServer; 46 import com.caucho.ejb.gen.AmberAssembler; 47 import com.caucho.ejb.gen.BeanAssembler; 48 import com.caucho.ejb.gen.EntityAssembler; 49 import com.caucho.ejb.gen.EntityCreateMethod; 50 import com.caucho.ejb.gen.EntityFindMethod; 51 import com.caucho.ejb.gen.EntityHomePoolChain; 52 import com.caucho.java.JavaWriter; 53 import com.caucho.java.gen.BaseClass; 54 import com.caucho.java.gen.BaseMethod; 55 import com.caucho.java.gen.CallChain; 56 import com.caucho.java.gen.JavaClassGenerator; 57 import com.caucho.java.gen.MethodCallChain; 58 import com.caucho.management.j2ee.J2EEManagedObject; 59 import com.caucho.util.L10N; 60 61 import javax.annotation.PostConstruct; 62 import javax.ejb.*; 63 import javax.sql.DataSource ; 64 import java.io.IOException ; 65 import java.util.ArrayList ; 66 import java.util.Collection ; 67 import java.util.Enumeration ; 68 import java.util.Map ; 69 70 73 public class EjbEntityBean extends EjbBean { 74 private final static L10N L = new L10N(EjbEntityBean.class); 75 76 private JClassWrapper _primKeyClass; 77 private String _primKeyField; 78 79 private JClass _compositeKeyClass; 80 81 private String _cmpVersion = "2.x"; 82 private boolean _isCMP = false; 83 84 private DataSource _dataSource; 85 86 private String _abstractSchemaName; 87 private String _sqlTable; 88 89 private long _cacheTimeout = 2000L; 90 private int _cacheSize = -666; 91 92 private boolean _loadLazyOnTransaction = true; 93 94 private boolean _isReadOnly = false; 95 private boolean _isReentrant = true; 96 97 private ArrayList <CmpField> _fields = new ArrayList <CmpField>(); 98 99 private ArrayList <CmrRelation> _relations = new ArrayList <CmrRelation>(); 100 private ArrayList <JMethod> _stubMethods = new ArrayList <JMethod>(); 101 102 105 public EjbEntityBean(EjbConfig ejbConfig, String ejbModuleName) 106 { 107 super(ejbConfig, ejbModuleName); 108 109 EjbServerManager ejbManager = ejbConfig.getEJBManager(); 110 111 if (ejbManager != null) { 112 _cacheTimeout = ejbManager.getCacheTimeout(); 113 _loadLazyOnTransaction = ejbManager.isEntityLoadLazyOnTransaction(); 114 } 115 } 116 117 120 public String getEJBKind() 121 { 122 return "entity"; 123 } 124 125 128 public void setEJBClass(Class ejbClass) 129 throws ConfigException 130 { 131 super.setEJBClass(ejbClass); 132 133 if (! EntityBean.class.isAssignableFrom(ejbClass) && ! isAllowPOJO()) 134 throw error(L.l("`{0}' must implement EntityBean. Entity beans must implement javax.ejb.EntityBean.", ejbClass.getName())); 135 136 137 validateNonFinalMethod("setEntityContext", 138 new JClass[] { JClassLoader.systemForName(EntityContext.class.getName()) }, isAllowPOJO()); 139 validateNonFinalMethod("unsetEntityContext", new JClass[0], isAllowPOJO()); 140 validateNonFinalMethod("ejbActivate", new JClass[0], isAllowPOJO()); 141 validateNonFinalMethod("ejbPassivate", new JClass[0], isAllowPOJO()); 142 143 validateNonFinalMethod("ejbRemove", new JClass[0], isAllowPOJO()); 144 validateNonFinalMethod("ejbLoad", new JClass[0], isAllowPOJO()); 147 validateNonFinalMethod("ejbStore", new JClass[0], isAllowPOJO()); 148 } 149 150 153 public EntityType getEntityType() 154 { 155 AmberPersistenceUnit amberPersistenceUnit = getConfig().getEJBManager().getAmberManager(); 156 157 EntityType type = amberPersistenceUnit.createEntity(getAbstractSchemaName(), 158 getEJBClassWrapper()); 159 160 type.setProxyClass(getLocal()); 161 162 return type; 163 } 164 165 168 public JClass getPrimKeyClass() 169 { 170 return _primKeyClass; 171 } 172 173 176 public void setPrimKeyClass(Class cl) 177 { 178 _primKeyClass = new JClassWrapper(cl, _jClassLoader); 179 } 180 181 184 public JClass getCompositeKeyClass() 185 { 186 return _compositeKeyClass; 187 } 188 189 192 public String getPrimKeyField() 193 { 194 return _primKeyField; 195 } 196 197 200 public void setPrimKeyField(String field) 201 { 202 _primKeyField = field; 203 } 204 205 208 public void setPersistenceType(String type) 209 throws ConfigException 210 { 211 if ("Bean".equals(type)) 212 _isCMP = false; 213 else if ("Container".equals(type)) { 214 _isCMP = true; 215 216 221 } 222 else 223 throw new ConfigException(L.l("`{0}' is an known persistence-type. <persistence-type> must either be `Bean' or `Container'.", type)); 224 } 225 226 229 public boolean isCMP() 230 { 231 return _isCMP && "2.x".equals(_cmpVersion); 232 } 233 234 237 public void setCMP(boolean isCMP) 238 { 239 _isCMP = isCMP; 240 } 241 242 245 public boolean isCMP1() 246 { 247 return _isCMP && "1.x".equals(_cmpVersion); 248 } 249 250 253 public String getFullImplName() 254 { 255 if (isCMP()) { 256 String name = "_ejb." + getEJBName() + "." + getEJBClassName() + "__Amber"; 257 return JavaClassGenerator.cleanClassName(name); 258 } 259 else 260 return super.getFullImplName(); 261 } 262 263 266 public void setCmpVersion(String version) 267 throws ConfigException 268 { 269 _cmpVersion = version; 270 271 if (! version.equals("1.x") && ! version.equals("2.x")) 272 throw error(L.l("CMP version `{0}' is not currently supported. Only CMP version 1.x and 2.x are supported.", version)); 273 } 274 275 278 public void setReentrant(boolean reentrant) 279 throws ConfigException 280 { 281 _isReentrant = reentrant; 282 } 283 284 287 public DataSource getDataSource() 288 { 289 return _dataSource; 290 } 291 292 295 public void setDataSource(DataSource dataSource) 296 { 297 _dataSource = dataSource; 298 } 299 300 303 public String getAbstractSchemaName() 304 { 305 if (_abstractSchemaName != null) 306 return _abstractSchemaName; 307 else { 308 String name = getEJBName(); 309 310 int p = name.lastIndexOf('/'); 311 if (p < 0) 312 return name; 313 else 314 return name.substring(p + 1); 315 } 316 } 317 318 321 public void setAbstractSchemaName(String abstractSchema) 322 throws ConfigException 323 { 324 _abstractSchemaName = abstractSchema; 325 326 EjbEntityBean bean = getConfig().findEntityBySchema(abstractSchema); 327 328 if (bean != null && this != bean) 329 throw new ConfigException(L.l("Entity bean '{0}' already has abstract schema '{1}'. abstract-schema-name values must be distinct.", 330 bean.getEJBName(), abstractSchema)); 331 } 332 333 336 public String getSQLTable() 337 { 338 return _sqlTable; 339 } 340 341 344 public void setSQLTable(String sqlTable) 345 { 346 _sqlTable = sqlTable; 347 } 348 349 352 public int getCacheSize() 353 { 354 return _cacheSize; 355 } 356 357 360 public void setCacheSize(int cacheSize) 361 { 362 _cacheSize = cacheSize; 363 } 364 365 368 public void setCacheTimeout(Period cacheTimeout) 369 { 370 _cacheTimeout = cacheTimeout.getPeriod(); 371 } 372 373 376 public long getCacheTimeout() 377 { 378 return _cacheTimeout; 379 } 380 381 384 public boolean isReadOnly() 385 { 386 return _isReadOnly; 387 } 388 389 392 public void setReadOnly(boolean isReadOnly) 393 { 394 _isReadOnly = isReadOnly; 395 } 396 397 400 public CmpFieldProxy createCmpField() 401 { 402 return new CmpFieldProxy(this); 403 } 404 405 408 public ArrayList <CmpField> getCmpFields() 409 { 410 return _fields; 411 } 412 413 416 public CmpField getCmpField(String fieldName) 417 { 418 for (int i = 0; i < _fields.size(); i++) { 419 CmpField field = _fields.get(i); 420 421 if (field.getName().equals(fieldName)) 422 return field; 423 } 424 425 return null; 426 } 427 428 431 public CmpField addField(String fieldName) 432 throws ConfigException 433 { 434 CmpField field = getCmpField(fieldName); 435 436 if (field == null) { 437 field = new CmpField(this); 438 field.setFieldName(fieldName); 439 440 field.init(); 441 442 _fields.add(field); 443 } 444 445 return field; 446 } 447 448 451 public JMethod getFieldGetter(String fieldName) 452 { 453 if (fieldName == null) 454 return null; 455 456 String methodName = ("get" + 457 Character.toUpperCase(fieldName.charAt(0)) + 458 fieldName.substring(1)); 459 460 try { 461 return getMethod(getEJBClassWrapper(), methodName, new JClass[0]); 462 } catch (Throwable e) { 463 return null; 464 } 465 } 466 467 470 public void addRelation(CmrRelation relation) 471 throws ConfigException 472 { 473 String fieldName = relation.getName(); 474 475 JMethod method = getMethodField(fieldName); 476 477 if (method == null && ! (relation instanceof CmrMap)) 478 throw error(L.l("'{0}' is a missing method", fieldName)); 479 480 482 _relations.add(relation); 483 } 484 485 488 public CmrRelation getRelation(String relationName) 489 { 490 for (int i = 0; i < _relations.size(); i++) { 491 CmrRelation relation = _relations.get(i); 492 493 if (relationName.equals(relation.getName())) 494 return relation; 495 } 496 497 return null; 498 } 499 500 503 public ArrayList <CmrRelation> getRelations() 504 { 505 return _relations; 506 } 507 508 511 public ArrayList <JMethod> getStubMethods() 512 { 513 return _stubMethods; 514 } 515 516 519 public void addStubMethod(JMethod method) 520 { 521 if (! _stubMethods.contains(method)) 522 _stubMethods.add(method); 523 } 524 525 528 public JClass getFieldType(String fieldName) 529 { 530 JMethod method = getMethodField(fieldName); 531 532 if (method != null) 533 return method.getReturnType(); 534 else 535 return null; 536 } 537 538 541 public Query createQuery() 542 { 543 return new Query(this); 544 } 545 546 549 public void addQuery(Query query) 550 { 551 MethodSignature sig = query.getSignature(); 552 553 EjbMethodPattern method = getMethod(sig); 554 555 if (method == null) { 556 method = new EjbMethodPattern(this, sig); 557 method.setLocation(query.getConfigLocation()); 558 _methodList.add(method); 559 } 560 561 method.setQueryLocation(query.getConfigLocation()); 562 method.setQuery(query.getEjbQl()); 563 } 564 565 public EjbMethodPattern getMethod(MethodSignature sig) 566 { 567 for (int i = 0; i < _methodList.size(); i++) { 568 EjbMethodPattern method = _methodList.get(i); 569 570 if (method.getSignature().equals(sig)) 571 return method; 572 } 573 574 return null; 575 } 576 577 580 public EjbMethodPattern getQuery(JMethod method, String intf) 581 { 582 return getMethodPattern(method, intf); 583 } 584 585 588 public ArrayList <EjbMethodPattern> getQueryList() 589 { 590 return _methodList; 591 } 592 593 596 @PostConstruct 597 public void init() 598 throws ConfigException 599 { 600 try { 601 if (! isCMP() && getEJBClassWrapper().isAbstract()) 602 throw error(L.l("'{0}' must not be abstract. BMP entity beans may not be abstract.", 603 getEJBClass().getName())); 604 605 super.init(); 606 607 if (_primKeyClass == null && ! isAllowPOJO() && 608 (getRemote() != null || getLocal() != null)) 609 throw new ConfigException(L.l("{0}: <entity> has no primary key class. Entity beans must define a prim-key-class.", 610 getEJBClass().getName())); 611 612 if (getRemoteHome() != null) 613 validateHome(getRemoteHome()); 614 if (getLocalHome() != null) 615 validateHome(getLocalHome()); 616 if (getRemote() != null) 617 validateRemote(getRemote()); 618 if (getLocal() != null) 619 validateRemote(getLocal()); 620 621 validateMethods(); 622 } catch (LineConfigException e) { 623 throw e; 624 } catch (ConfigException e) { 625 throw new LineConfigException(getLocation() + e.getMessage(), e); 626 } 627 628 J2EEManagedObject.register(new com.caucho.management.j2ee.EntityBean(this)); 629 } 630 631 634 public void introspect() 635 throws ConfigException 636 { 637 if (isCMP()) { 638 introspectCMPFields(); 639 640 introspectCMPId(); 641 642 validateCMPFields(); 643 } 644 } 645 646 649 protected void introspectCMPFields() 650 throws ConfigException 651 { 652 JMethod []methods = getMethods(getEJBClassWrapper()); 653 654 for (JMethod method : methods) { 655 if (! method.isAbstract()) 656 continue; 657 658 String name = method.getName(); 659 660 if (name.startsWith("get") && name.length() > 3 && 661 method.getParameterTypes().length == 0) { 662 String fieldName = getterToFieldName(name); 663 664 CmrRelation rel = getRelation(fieldName); 665 666 if (rel != null) { 667 continue; 668 } 669 670 JClass type = method.getReturnType(); 671 672 if (type.isAssignableTo(Collection .class)) { 673 throw error(L.l("'{0}' needs to be a relation", 674 fieldName)); 675 } 676 else if (type.isAssignableTo(Map .class)) { 677 throw error(L.l("'{0}' needs to be a relation", 678 fieldName)); 679 } 680 else if (type.isAssignableTo(EJBLocalObject.class)) { 681 throw error(L.l("{0}: '{1}' needs to be defined in an ejb-relation for getter method {2}.", 682 getEJBClass().getName(), 683 fieldName, 684 method.getFullName())); 685 } 686 else if (type.isAssignableTo(EJBObject.class)) { 687 throw error(L.l("'{0}' needs to be a relation", 688 fieldName)); 689 } 690 else { 691 CmpField cmpField = addField(fieldName); 692 693 cmpField.setJavaType(method.getReturnType()); 694 } 695 } 696 } 697 } 698 699 702 protected void validateCMPFields() 703 throws ConfigException 704 { 705 JMethod []methods = getMethods(getEJBClassWrapper()); 706 707 for (JMethod method : methods) { 708 if (! method.isAbstract()) 709 continue; 710 711 String name = method.getName(); 712 713 if (name.startsWith("ejb")) { 714 continue; 715 } 716 else if (name.startsWith("get") && name.length() > 3 && 717 method.getParameterTypes().length == 0) { 718 String fieldName = getterToFieldName(name); 719 720 if (getCmpField(fieldName) != null || 721 getRelation(fieldName) != null) 722 continue; 723 } 724 else if (name.startsWith("get") && name.length() > 3 && 725 method.getParameterTypes().length == 1) { 726 String fieldName = getterToFieldName(name); 727 728 CmrRelation rel = getRelation(fieldName); 729 730 if (rel instanceof CmrMap) { 731 CmrMap map = (CmrMap) rel; 732 733 if (method.equals(map.getMapMethod())) 734 continue; 735 } 736 } 737 else if (name.startsWith("set") && name.length() > 3 && 738 method.getParameterTypes().length == 1) { 739 String fieldName = getterToFieldName(name); 740 741 CmpField field = getCmpField(fieldName); 742 743 if (field == null) { 744 } 745 else if (isMatch(method, field.getSetter())) 746 continue; 747 else 748 throw new ConfigException(L.l("{0}: '{1}' does not match the corresponding cmp-field getter '{2}'.", 749 getEJBClass().getName(), 750 method.getFullName(), 751 field.getGetter().getFullName())); 752 753 CmrRelation rel = getRelation(fieldName); 754 755 if (rel == null) { 756 } 757 else if (method.equals(rel.getSetter())) 758 continue; 759 else 760 throw new ConfigException(L.l("{0}: '{1}' does not match the corresponding cmp-field getter '{2}'.", 761 getEJBClass().getName(), 762 method.getFullName(), 763 rel.getGetter().getFullName())); 764 } 765 766 throw error(L.l("{0}: '{1}' must not be abstract. Business methods must be implemented.", 767 getEJBClass().getName(), 768 method.getFullName())); 769 } 770 } 771 772 775 protected void introspectCMPId() 776 throws ConfigException 777 { 778 779 if (_primKeyClass != null && 780 ! _primKeyClass.isPrimitive() && 781 ! _primKeyClass.getName().startsWith("java.lang.") && 782 ! _primKeyClass.getName().equals("java.util.Date") && 783 ! _primKeyClass.getName().equals("java.sql.Date") && 784 ! _primKeyClass.getName().equals("java.sql.Time") && 785 ! _primKeyClass.getName().equals("java.sql.Timestamp") && 786 ! _primKeyClass.isAssignableTo(EJBLocalObject.class)) { 787 788 if (_primKeyField != null) 789 throw error(L.l("{0}: 'primkey-field' must not be defined for a composite primkey-class.", 790 getEJBClass().getName())); 791 792 _compositeKeyClass = _primKeyClass; 793 introspectCMPCompositeId(); 794 return; 795 } 796 797 String id = _primKeyField; 798 if (id == null) 799 id = "id"; 800 801 CmpProperty property = getCmpField(id); 802 803 if (property == null) 804 property = getRelation(id); 805 806 if (property == null) 807 throw error(L.l("{0}: primary key field '{1}' is an unknown cmp-field", 808 getEJBClass().getName(), id)); 809 810 property.setId(true); 811 } 812 813 816 protected void introspectCMPCompositeId() 817 throws ConfigException 818 { 819 try { 820 JMethod equals = _primKeyClass.getMethod("equals", new JClass[] { JClass.OBJECT }); 821 822 if (equals.getDeclaringClass().equals(JClass.OBJECT)) 823 throw error(L.l("{0}: primary key class '{1}' must override the 'equals' method.", 824 getEJBClass().getName(), 825 _primKeyClass.getName())); 826 } catch (ConfigException e) { 827 throw e; 828 } catch (Throwable e) { 829 } 830 831 try { 832 JMethod hashCode = _primKeyClass.getMethod("hashCode", new JClass[] { }); 833 834 if (hashCode.getDeclaringClass().getName().equals(Object .class.getName())) 835 throw error(L.l("{0}: primary key class '{1}' must override the 'hashCode' method.", 836 getEJBClass().getName(), 837 _primKeyClass.getName())); 838 } catch (ConfigException e) { 839 throw e; 840 } catch (Throwable e) { 841 } 842 843 if (_primKeyClass.getFields().length == 0) 844 throw error(L.l("{0}: compound key '{1}' has no public accessible fields. Compound key fields must be public.", 845 getEJBClass().getName(), 846 _primKeyClass.getName())); 847 848 for (JField field : _primKeyClass.getFields()) { 849 CmpProperty cmpProperty = getCmpField(field.getName()); 850 851 if (cmpProperty == null) 852 cmpProperty = getRelation(field.getName()); 853 854 if (cmpProperty == null) 855 throw error(L.l("{0}: primary key field '{1}' is an unknown field.", 856 getEJBClass().getName(), field.getName())); 857 858 cmpProperty.setId(true); 859 } 860 } 861 862 private static String getterToFieldName(String name) 863 { 864 String fieldName = name.substring(3); 865 char ch = fieldName.charAt(0); 866 867 if (Character.isUpperCase(ch) && 868 (fieldName.length() == 1 || 869 Character.isLowerCase(fieldName.charAt(1)))) { 870 fieldName = Character.toLowerCase(ch) + fieldName.substring(1); 871 } 872 873 return fieldName; 874 } 875 876 879 public void configureAmber(AmberConfig config) 880 throws ConfigException 881 { 882 if (isCMP()) { 883 try { 884 config.addBean(this); 885 } catch (LineConfigException e) { 886 throw e; 887 } catch (Exception e) { 888 throw new LineConfigException(getLocation() + e.getMessage(), e); 889 } 890 } 891 } 892 893 896 protected BeanAssembler createAssembler(String fullClassName) 897 { 898 if (isCMP()) 899 return new AmberAssembler(this, fullClassName); 900 else 901 return new EntityAssembler(this, fullClassName); 902 } 903 904 907 protected void addImports(BeanAssembler assembler) 908 { 909 super.addImports(assembler); 910 911 assembler.addImport("com.caucho.ejb.FinderExceptionWrapper"); 912 913 assembler.addImport("com.caucho.ejb.entity.EntityServer"); 914 assembler.addImport("com.caucho.ejb.entity.QEntityContext"); 915 assembler.addImport("com.caucho.ejb.entity.EntityHome"); 916 assembler.addImport("com.caucho.ejb.entity.EntityLocalHome"); 917 assembler.addImport("com.caucho.ejb.entity.EntityRemoteHome"); 918 assembler.addImport("com.caucho.ejb.entity.EntityObject"); 919 assembler.addImport("com.caucho.ejb.entity.QEntity"); 920 } 921 922 925 protected EjbBaseMethod introspectEJBMethod(JMethod method) 926 throws ConfigException 927 { 928 String methodName = method.getName(); 929 JClass []paramTypes = method.getParameterTypes(); 930 931 if (methodName.startsWith("ejbSelect") && method.isAbstract()) { 932 validateSelectMethod(method); 933 934 EjbMethodPattern pattern = getMethodPattern(method, "Local"); 935 936 if (pattern == null) 937 throw error(L.l("{0}: '{1}' expects an ejb-ql query. ejbSelect methods must have an ejb-ql query in the deployment descriptor.", 938 getEJBClass().getName(), 939 method.getFullName())); 940 941 String query = pattern.getQuery(); 942 943 EjbAmberSelectMethod select; 944 select = new EjbAmberSelectMethod(this, method, query, 945 pattern.getQueryLocation()); 946 947 select.setQueryLoadsBean(pattern.getQueryLoadsBean()); 948 949 return select; 950 } 951 952 return null; 953 } 954 955 958 protected void validateImplMethod(JMethod method) 959 throws ConfigException 960 { 961 String methodName = method.getName(); 962 JClass []paramTypes = method.getParameterTypes(); 963 964 if (method.isAbstract() && 965 methodName.startsWith("get") && 966 paramTypes.length == 0) { 967 } 968 else if (method.isAbstract() && 969 methodName.startsWith("get") && 970 paramTypes.length == 1) { 971 } 972 else if (method.isAbstract() && 973 methodName.startsWith("set") && 974 paramTypes.length == 1) { 975 } 976 else if (methodName.startsWith("ejb")) { 977 } 978 else if (method.isAbstract()) { 979 throw error(L.l("{0}: '{1}' must not be abstract. The bean must implement its business methods.", 980 getEJBClass().getName(), 981 method.getFullName())); 982 } 983 } 984 985 990 private void validateSelectMethod(JMethod method) 991 throws ConfigException 992 { 993 if (! method.isPublic()) { 994 throw error(L.l("{0}: '{1}' must be public. ejbSelect methods must be public.", 995 getEJBClass().getName(), 996 method.getFullName())); 997 } 998 else if (method.isStatic()) { 999 throw error(L.l("{0}: `{1}' must not be static. ejbSelect methods must not be static.", 1000 getEJBClass().getName(), 1001 method.getFullName())); 1002 } 1003 else if (! method.isAbstract()) { 1004 throw error(L.l("{0}: `{1}' must be abstract. ejbSelect methods must be abstract.", 1005 getEJBClass().getName(), 1006 method.getFullName())); 1007 } 1008 } 1009 1010 1013 protected EjbHomeView createHomeView(JClass homeClass, String prefix) 1014 throws ConfigException 1015 { 1016 return new EjbEntityHomeView(this, homeClass, prefix); 1017 } 1018 1019 1022 protected EjbObjectView createObjectView(JClass apiClass, String prefix) 1023 throws ConfigException 1024 { 1025 if (isCMP()) 1026 return new EjbCmpView(this, apiClass, prefix); 1027 else 1028 return new EjbEntityView(this, apiClass, prefix); 1029 } 1030 1031 1034 public AbstractServer deployServer(EjbServerManager ejbManager, 1035 JavaClassGenerator javaGen) 1036 throws ClassNotFoundException 1037 { 1038 EntityServer server = new EntityServer(ejbManager); 1039 1040 server.setEJBName(getEJBName()); 1041 server.setJndiName(getJndiName()); 1042 1043 server.setRemoteHomeClass(getRemoteHomeClass()); 1044 server.setRemoteObjectClass(getRemoteClass()); 1045 1046 Class contextImplClass = javaGen.loadClass(getSkeletonName()); 1047 1048 server.setContextImplClass(contextImplClass); 1049 1050 server.setCMP(isCMP()); 1051 1052 if (_primKeyClass != null) 1053 server.setPrimaryKeyClass(_primKeyClass.getWrappedClass()); 1054 1055 server.setLoadLazyOnTransaction(_loadLazyOnTransaction); 1056 1057 server.setInitProgram(getInitProgram()); 1058 1059 if (isCMP()) { 1060 server.setAmberEntityHome(getEntityType().getHome()); 1061 } 1062 1063 Thread thread = Thread.currentThread(); 1064 ClassLoader oldLoader = thread.getContextClassLoader(); 1065 1066 try { 1067 thread.setContextClassLoader(server.getClassLoader()); 1068 1069 try { 1070 if (getServerProgram() != null) 1072 getServerProgram().configure(server); 1073 } catch (ConfigException e) { 1074 throw e; 1075 } catch (Throwable e) { 1076 throw new ConfigException(e); 1077 } 1078 } finally { 1079 thread.setContextClassLoader(oldLoader); 1080 } 1081 1082 return server; 1083 } 1084 1085 private void validateMethods() 1086 throws ConfigException 1087 { 1088 JClass primKeyClass = getPrimKeyClass(); 1089 1090 JMethod []methods = getMethods(getEJBClassWrapper()); 1091 1092 for (JMethod method : methods) { 1093 String name = method.getName(); 1094 1095 try { 1096 JMethod cleanMethod = getEJBClassWrapper().getMethod(name, method.getParameterTypes()); 1098 if (cleanMethod != null) 1099 method = cleanMethod; 1100 } catch (Exception e) { 1101 } 1102 1103 if (method.getReturnType().isAssignableTo(EntityBean.class)) 1104 throw error(L.l("{0}: `{1}' must not return entity bean `{2}'. Entity bean methods must always return local or remote interfaces.", 1105 getEJBClass().getName(), 1106 method.getFullName(), 1107 method.getReturnType().getShortName())); 1108 1109 if (name.startsWith("ejbFind")) { 1110 if (! isCMP()) { 1111 validateNonFinalMethod(method.getName(), 1112 method.getParameterTypes()); 1113 } 1114 else if (true) { 1115 validateNonFinalMethod(method.getName(), method.getParameterTypes()); 1117 } 1118 else { 1119 throw error(L.l("{0}: `{1}' forbidden. CMP entity beans must not implement ejbFind methods.", 1120 method.getDeclaringClass().getName(), 1121 method.getFullName())); 1122 } 1123 } 1124 else if (name.startsWith("ejbSelect")) { 1125 if (! method.isAbstract()) 1126 throw error(L.l("{0}: `{1}' must be abstract. ejbSelect methods must be abstract.", 1127 method.getDeclaringClass().getName(), 1128 method.getFullName())); 1129 if (! method.isPublic()) 1130 throw error(L.l("{0}: `{1}' must be public.", 1131 method.getDeclaringClass().getName(), 1132 method.getFullName())); 1133 validateException(method, FinderException.class); 1134 } 1135 else if (name.startsWith("ejbCreate")) { 1136 validateNonFinalMethod(method.getName(), method.getParameterTypes()); 1137 if (! isPrimaryKeyClass(method.getReturnType())) 1138 throw error(L.l("{0}: `{1}' must return `{2}'. ejbCreate methods must return the primary key.", 1139 method.getDeclaringClass().getName(), 1140 method.getFullName(), 1141 getClassName(primKeyClass))); 1142 if (isCMP()) 1143 validateException(method, CreateException.class); 1144 } 1145 else if (name.startsWith("ejbPostCreate")) { 1146 validateNonFinalMethod(method.getName(), method.getParameterTypes()); 1147 1148 if (! method.getReturnType().getName().equals("void")) 1149 throw error(L.l("{0}: `{1}' must return void. ejbPostCreate methods must return void.", 1150 method.getDeclaringClass().getName(), 1151 method.getFullName())); 1152 } 1153 else if (name.startsWith("create") && 1154 method.isAbstract()) { 1155 } 1157 else if (name.startsWith("ejbHome")) { 1158 validateNonFinalMethod(method.getName(), method.getParameterTypes()); 1159 } 1160 else if (name.equals("ejbRemove")) { 1161 if (method.getParameterTypes().length != 0) 1162 throw error(L.l("{0}: `{1}' must have no arguments.", 1163 method.getDeclaringClass().getName(), 1164 method.getFullName())); 1165 } 1166 else if (name.equals("ejbTimeout")) { 1167 JClass []types = method.getParameterTypes(); 1168 1169 if (types.length != 1 || ! types[0].getName().equals("javax.ejb.Timer")) 1170 throw error(L.l("{0}: `{1}' must have one javax.ejb.Timer argument.", 1171 method.getDeclaringClass().getName(), 1172 method.getFullName())); 1173 } 1174 else if (classHasMethod(method, JClassLoader.systemForName(EntityBean.class.getName()))) { 1175 } 1176 else if (name.equals("finalize") && 1177 method.getParameterTypes().length == 0 && 1178 ! method.getDeclaringClass().getName().equals("java.lang.Object")) 1179 throw error(L.l("{0}: Entity beans must not define `finalize'.", 1180 getEJBClass().getClass())); 1181 else if (name.startsWith("ejb")) { 1182 throw error(L.l("{0}: `{1}' must not start with `ejb'. ejbXXX methods are reserved by the EJB spec.", 1183 method.getDeclaringClass().getName(), 1184 method.getFullName())); 1185 } 1186 else { 1187 boolean isAbstract = method.isAbstract(); 1188 JMethod persist = null; 1189 1190 if (! isAbstract || ! isCMP()) { 1191 } 1192 else if (method.getName().startsWith("get")) { 1193 } 1195 else if (method.getName().startsWith("set")) { 1196 } 1198 else if (method.getName().startsWith("ejbSelect")) { 1199 } 1200 else { 1201 throw error(L.l("{0}: `{1}' must not be abstract. Only CMP methods may be abstract.", 1202 method.getDeclaringClass().getName(), 1203 method.getFullName())); 1204 } 1205 1206 1207 1223 } 1224 } 1225 } 1226 1227 1230 private void validateHome(JClass homeClass) 1231 throws ConfigException 1232 { 1233 JClass beanClass = getEJBClassWrapper(); 1234 String beanName = beanClass.getName(); 1235 1236 String homeName = homeClass.getName(); 1237 1238 JClass objectClass; 1239 if (homeClass.isAssignableTo(EJBHome.class)) 1240 objectClass = getRemote(); 1241 else 1242 objectClass = getLocal(); 1243 String objectName = objectClass != null ? objectClass.getName() : null; 1244 1245 boolean hasFindByPrimaryKey = false; 1246 1247 JClass primKeyClass = getPrimKeyClass(); 1248 1249 if (! homeClass.isPublic()) 1250 throw error(L.l("`{0}' must be public. Entity beans must be public.", homeName)); 1251 1252 if (beanClass.isFinal()) 1253 throw error(L.l("`{0}' must not be final. Entity beans must not be final.", beanName)); 1254 1255 if (! isCMP() && beanClass.isAbstract()) 1256 throw error(L.l("`{0}' must not be abstract. BMP entity beans must not be abstract.", beanName)); 1257 1258 if (! homeClass.isInterface()) 1259 throw error(L.l("`{0}' must be an interface.", homeName)); 1260 1261 JMethod []methods = getMethods(homeClass); 1262 for (int i = 0; i < methods.length; i++) { 1263 JMethod method = methods[i]; 1264 String name = method.getName(); 1265 JClass []param = method.getParameterTypes(); 1266 JClass retType = method.getReturnType(); 1267 1268 if (method.getDeclaringClass().isAssignableFrom(EJBHome.class)) 1269 continue; 1270 1271 if (method.getDeclaringClass().isAssignableFrom(EJBLocalHome.class)) 1272 continue; 1273 1274 if (homeClass.isAssignableTo(EJBHome.class)) 1275 validateException(method, java.rmi.RemoteException .class); 1276 1277 if (name.startsWith("create")) { 1278 validateException(method, CreateException.class); 1279 1280 if (! retType.equals(objectClass)) 1281 throw error(L.l("{0}: `{1}' must return {2}. Create methods must return the local or remote interface.", 1282 homeName, 1283 method.getFullName(), 1284 objectName)); 1285 1286 String createName = "ejbC" + name.substring(1); 1287 JMethod implMethod = 1288 validateNonFinalMethod(createName, param, method, homeClass); 1289 1290 if (! isPrimaryKeyClass(implMethod.getReturnType())) 1291 throw error(L.l("{0}: `{1}' must return `{2}'. ejbCreate methods must return the primary key.", 1292 beanName, 1293 getFullMethodName(createName, param), 1294 getClassName(primKeyClass))); 1295 1296 if (! hasException(implMethod, CreateException.class)) { 1297 throw error(L.l("{0}: `{1}' must throw {2}. ejbCreate methods must throw CreateException.", 1298 implMethod.getDeclaringClass().getName(), 1299 implMethod.getFullName(), 1300 "CreateException")); 1301 1302 } 1303 1304 validateExceptions(method, implMethod); 1305 1306 createName = "ejbPostC" + name.substring(1); 1307 implMethod = validateNonFinalMethod(createName, param, 1308 method, homeClass); 1309 1310 if (! implMethod.getReturnType().getName().equals("void")) 1311 throw error(L.l("{0}: `{1}' must return {2}. ejbPostCreate methods must return void.", 1312 beanName, 1313 getFullMethodName(createName, param), 1314 "void")); 1315 1316 1317 validateExceptions(method, implMethod); 1318 } 1319 else if (name.startsWith("find")) { 1320 if (name.equals("findByPrimaryKey")) { 1321 hasFindByPrimaryKey = true; 1322 1323 1329 1330 if (! objectClass.equals(method.getReturnType())) 1331 throw error(L.l("{0}: `{1}' must return `{2}'. Find methods must return the remote or local interface.", 1332 homeName, 1333 method.getFullName(), 1334 objectName)); 1335 } 1336 1337 String findName = "ejbF" + name.substring(1); 1338 if (! isCMP() && ! isCMP1() 1339 || getMethod(beanClass, findName, param) != null) { 1340 JMethod impl = validateNonFinalMethod(findName, param, isAllowPOJO()); 1341 1342 if (impl != null) 1343 validateExceptions(method, impl); 1344 1345 if (objectClass.equals(method.getReturnType())) { 1346 if (impl != null && ! isPrimaryKeyClass(impl.getReturnType())) 1347 throw error(L.l("{0}: `{1}' must return primary key `{2}'. ejbFind methods must return the primary key", 1348 beanName, 1349 impl.getFullName(), 1350 getClassName(primKeyClass))); 1351 } 1352 else if (method.getReturnType().isAssignableTo(Collection .class)) { 1353 if (impl != null && ! impl.getReturnType().isAssignableTo(Collection .class)) 1354 throw error(L.l("{0}: `{1}' must return collection.", 1355 beanName, 1356 impl.getFullName())); 1357 } 1358 else if (method.getReturnType().isAssignableTo(Enumeration .class)) { 1359 if (! impl.getReturnType().isAssignableTo(Enumeration .class)) 1360 throw error(L.l("{0}: `{1}' must return enumeration.", 1361 beanName, 1362 impl.getFullName())); 1363 } 1364 else 1365 throw error(L.l("{0}: `{1}' must return `{2}' or a collection. ejbFind methods must return the primary key or a collection.", 1366 homeName, 1367 method.getFullName(), 1368 objectName)); 1369 } 1370 else if (isCMP() && ! name.equals("findByPrimaryKey")) { 1371 String query = findQuery(method); 1372 1373 if (query == null) { 1374 throw error(L.l("{0}: `{1}' expects an ejb-ql query. All find methods need queries defined in the EJB deployment descriptor.", 1375 homeName, 1376 method.getFullName())); 1377 } 1378 } 1379 1380 validateException(method, FinderException.class); 1381 1382 if (! retType.equals(objectClass) && 1383 (! retType.isAssignableTo(Collection .class) && 1384 ! Enumeration .class.getName().equals(retType.getName()) || 1385 name.equals("findByPrimaryKey"))) { 1386 throw error(L.l("{0}: `{1}' must return {2} or a collection. ejbFind methods must return the primary key or a collection.", 1387 homeName, 1388 method.getFullName(), 1389 objectName)); 1390 } 1391 } 1392 else if (name.startsWith("ejbSelect")) { 1393 throw error(L.l("{0}: `{1}' forbidden. ejbSelect methods may not be exposed in the remote or local interface.", 1394 homeName, 1395 method.getFullName())); 1396 } 1397 else if (name.startsWith("ejb")) { 1398 throw error(L.l("{0}: `{1}' forbidden. Only ejbXXX methods defined by the spec are allowed.", 1399 homeName, 1400 method.getFullName())); 1401 } 1402 else if (name.startsWith("remove")) { 1403 throw error(L.l("{0}: `{1}' forbidden. removeXXX methods are reserved by the spec.", 1404 homeName, 1405 method.getFullName())); 1406 } 1407 else { 1408 retType = method.getReturnType(); 1409 1410 if (homeClass.isAssignableTo(EJBHome.class) && 1411 (retType.isAssignableTo(EJBLocalObject.class) || 1412 retType.isAssignableTo(EJBLocalHome.class))) 1413 throw error(L.l("{1}: `{0}' must not return local interface.", 1414 homeClass.getName(), 1415 method.getFullName())); 1416 1417 String homeMethodName = ("ejbHome" + 1418 Character.toUpperCase(name.charAt(0)) + 1419 name.substring(1)); 1420 JMethod implMethod = validateMethod(homeMethodName, param, 1421 method, homeClass); 1422 1423 if (! retType.equals(implMethod.getReturnType())) 1424 throw error(L.l("{0}: `{1}' must return {2}.", 1425 beanName, 1426 implMethod.getFullName(), 1427 method.getReturnType().getName())); 1428 1429 validateExceptions(method, implMethod.getExceptionTypes()); 1430 } 1431 } 1432 1433 if (! hasFindByPrimaryKey && ! isAllowPOJO() && objectClass != null) 1435 throw error(L.l("{0}: expected `{1}'. All entity homes must define findByPrimaryKey.", 1436 homeName, 1437 getFullMethodName("findByPrimaryKey", 1438 new JClass[] { 1439 primKeyClass }))); 1440 } 1441 1442 protected void assembleHomeMethods(BeanAssembler assembler, 1443 BaseClass baseClass, 1444 String contextClassName, 1445 JClass homeClass, 1446 String prefix) 1447 throws NoSuchMethodException 1448 { 1449 JMethod []methods = getMethods(homeClass); 1450 1451 for (int i = 0; i < methods.length; i++) { 1452 String className = methods[i].getDeclaringClass().getName(); 1453 String methodName = methods[i].getName(); 1454 1455 if (className.startsWith("javax.ejb.")) { 1456 } 1457 else if (isOld(methods, methods[i], i)) { 1458 } 1459 else if (methodName.startsWith("create")) { 1460 assembleCreateMethod(methods[i], baseClass, contextClassName, prefix); 1461 } 1462 else if (methodName.startsWith("find")) { 1463 JMethod beanMethod = null; 1464 1465 String name = ("ejb" + Character.toUpperCase(methodName.charAt(0)) 1466 + methodName.substring(1)); 1467 1468 try { 1469 beanMethod = getEJBClassWrapper().getMethod(name, 1470 methods[i].getParameterTypes()); 1471 } catch (Throwable e) { 1472 } 1473 1474 if (beanMethod != null) { 1475 EntityFindMethod findMethod; 1476 findMethod = new EntityFindMethod(methods[i], 1477 beanMethod, 1478 contextClassName, 1479 prefix); 1480 1481 CallChain call = findMethod.getCall(); 1482 call = new EntityHomePoolChain(call); 1483 findMethod.setCall(call); 1485 1486 baseClass.addMethod(findMethod); 1487 } 1488 } 1489 else { 1490 JMethod beanMethod = null; 1491 1492 String name = ("ejbHome" + Character.toUpperCase(methodName.charAt(0)) 1493 + methodName.substring(1)); 1494 1495 try { 1496 beanMethod = getEJBClassWrapper().getMethod(name, 1497 methods[i].getParameterTypes()); 1498 } catch (Exception e) { 1499 throw new NoSuchMethodException ("can't find method " + name); 1500 } 1501 1502 CallChain call = new MethodCallChain(beanMethod); 1503 call = getTransactionChain(call, beanMethod, prefix); 1505 1506 baseClass.addMethod(new BaseMethod(methods[i], call)); 1507 } 1508 } 1509 } 1510 1511 protected void assembleCreateMethod(JMethod method, 1512 BaseClass baseClass, 1513 String contextClassName, 1514 String prefix) 1515 { 1516 String methodName = method.getName(); 1517 1518 JMethod beanCreateMethod = null; 1519 JMethod beanPostCreateMethod = null; 1520 1521 String name = ("ejb" + Character.toUpperCase(methodName.charAt(0)) 1522 + methodName.substring(1)); 1523 1524 try { 1525 beanCreateMethod = getEJBClassWrapper().getMethod(name, 1526 method.getParameterTypes()); 1527 } catch (Throwable e) { 1528 } 1529 1530 if (beanCreateMethod == null) 1531 throw new IllegalStateException (name); 1532 1533 name = ("ejbPost" + Character.toUpperCase(methodName.charAt(0)) 1534 + methodName.substring(1)); 1535 1536 try { 1537 beanPostCreateMethod = getEJBClassWrapper().getMethod(name, 1538 method.getParameterTypes()); 1539 } catch (Throwable e) { 1540 } 1541 1542 EntityCreateMethod createMethod; 1543 1544 createMethod = new EntityCreateMethod(this, method, 1545 beanCreateMethod, 1546 beanPostCreateMethod, 1547 contextClassName); 1548 1549 createMethod.setCall(getTransactionChain(createMethod.getCall(), 1550 method, 1551 prefix)); 1552 1553 baseClass.addMethod(createMethod); 1554 } 1555 1556 1559 private boolean isPrimaryKeyClass(JClass type) 1560 { 1561 return type.equals(getPrimKeyClass()); 1562 } 1563 1564 private JMethod getMethodField(String fieldName) 1565 { 1566 String getter = "get" + Character.toUpperCase(fieldName.charAt(0)) + 1567 fieldName.substring(1); 1568 1569 JMethod []methods = getMethods(getEJBClassWrapper()); 1570 1571 for (int i = 0; i < methods.length; i++) { 1572 if (getter.equals(methods[i].getName()) && 1573 methods[i].getParameterTypes().length == 0) 1574 return methods[i]; 1575 } 1576 1577 return null; 1578 } 1579 1580 1583 private String findQuery(JMethod method) 1584 throws ConfigException 1585 { 1586 EjbMethodPattern ejbMethod = getMethodPattern(method, null); 1587 1588 if (ejbMethod != null && ejbMethod.getQuery() != null) 1589 return ejbMethod.getQuery(); 1590 1591 EjbMethodPattern ejbQuery = getQuery(method, null); 1592 if (ejbQuery != null) 1593 return ejbQuery.getQuery(); 1594 else 1595 return null; 1596 } 1597 1598 1601 public boolean dependsOn(EjbEntityBean target) 1602 { 1603 for (CmrRelation rel : _relations) { 1604 if (rel.isId()) { 1605 EjbEntityBean targetBean = rel.getTargetBean(); 1606 1607 if (target == targetBean || 1608 targetBean != null && targetBean.dependsOn(target)) 1609 return true; 1610 } 1611 } 1612 1613 return false; 1614 } 1615 1616 1619 public void generateBeanPrologue(JavaWriter out) 1620 throws IOException 1621 { 1622 } 1623 1624 1627 public void generateAfterCommit(JavaWriter out) 1628 throws IOException 1629 { 1630 for (CmrRelation rel : _relations) { 1631 rel.generateAfterCommit(out); 1632 } 1633 } 1634 1635 1638 public void generateDestroy(JavaWriter out) 1639 throws IOException 1640 { 1641 for (CmrRelation rel : _relations) { 1642 rel.generateDestroy(out); 1643 } 1644 } 1645 1646 public String toString() 1647 { 1648 return "EjbEntityBean[" + getEJBName() + "]"; 1649 } 1650} 1651 | Popular Tags |