0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.ejb.cfg21;
0031:
0032: import com.caucho.ejb.cfg.*;
0033: import com.caucho.amber.manager.AmberPersistenceUnit;
0034: import com.caucho.amber.type.EntityType;
0035: import com.caucho.bytecode.JClassWrapper;
0036: import com.caucho.config.ConfigException;
0037: import com.caucho.config.LineConfigException;
0038: import com.caucho.config.types.Period;
0039: import com.caucho.ejb.AbstractServer;
0040: import com.caucho.ejb.amber.AmberConfig;
0041: import com.caucho.ejb.entity.EntityServer;
0042: import com.caucho.ejb.gen.*;
0043: import com.caucho.ejb.gen21.AmberAssembler;
0044: import com.caucho.ejb.gen21.BeanAssembler;
0045: import com.caucho.ejb.gen21.EntityAssembler;
0046: import com.caucho.ejb.gen21.EntityCreateMethod;
0047: import com.caucho.ejb.gen21.EntityFindMethod;
0048: import com.caucho.ejb.gen21.EntityHomePoolChain;
0049: import com.caucho.ejb.manager.EjbContainer;
0050: import com.caucho.java.JavaWriter;
0051: import com.caucho.java.gen.BaseClass;
0052: import com.caucho.java.gen.BaseMethod;
0053: import com.caucho.java.gen.CallChain;
0054: import com.caucho.java.gen.JavaClassGenerator;
0055: import com.caucho.java.gen.MethodCallChain;
0056: import com.caucho.management.j2ee.J2EEManagedObject;
0057: import com.caucho.util.L10N;
0058:
0059: import javax.annotation.PostConstruct;
0060: import javax.ejb.*;
0061: import javax.sql.DataSource;
0062: import java.io.IOException;
0063: import java.lang.reflect.*;
0064: import java.util.ArrayList;
0065: import java.util.Collection;
0066: import java.util.Enumeration;
0067: import java.util.Map;
0068:
0069: /**
0070: * Configuration for an ejb entity bean.
0071: */
0072: public class EjbEntityBean extends Ejb21Bean {
0073: private final static L10N L = new L10N(EjbEntityBean.class);
0074:
0075: private ApiClass _primKeyClass;
0076: private String _primKeyField;
0077:
0078: private ApiClass _compositeKeyClass;
0079:
0080: private String _cmpVersion = "2.x";
0081: private boolean _isCMP = false;
0082:
0083: private DataSource _dataSource;
0084:
0085: private EntityGenerator _entityBean;
0086:
0087: private String _abstractSchemaName;
0088: private String _sqlTable;
0089:
0090: private long _cacheTimeout = 2000L;
0091: private int _cacheSize = -666;
0092:
0093: private boolean _loadLazyOnTransaction = true;
0094:
0095: private boolean _isReadOnly = false;
0096: private boolean _isReentrant = true;
0097:
0098: private ArrayList<CmpField> _fields = new ArrayList<CmpField>();
0099:
0100: private ArrayList<CmrRelation> _relations = new ArrayList<CmrRelation>();
0101: private ArrayList<ApiMethod> _stubMethods = new ArrayList<ApiMethod>();
0102:
0103: // EJB 2.1
0104: private EjbHomeView _remoteHomeView;
0105: private EjbObjectView _remoteView;
0106:
0107: private EjbHomeView _localHomeView;
0108: private EjbObjectView _localView;
0109:
0110: /**
0111: * Creates a new entity bean configuration.
0112: */
0113: public EjbEntityBean(EjbConfig ejbConfig, String ejbModuleName) {
0114: super (ejbConfig, ejbModuleName);
0115:
0116: /*
0117: EjbServerManager ejbManager = ejbConfig.getEJBManager();
0118:
0119: if (ejbManager != null) {
0120: _cacheTimeout = ejbManager.getCacheTimeout();
0121: _loadLazyOnTransaction = ejbManager.isEntityLoadLazyOnTransaction();
0122: }
0123: */
0124: }
0125:
0126: /**
0127: * Returns the kind of bean.
0128: */
0129: public String getEJBKind() {
0130: return "entity";
0131: }
0132:
0133: /**
0134: * Sets the ejb implementation class.
0135: */
0136: @Override
0137: public void setEJBClass(Class ejbClass) throws ConfigException {
0138: super .setEJBClass(ejbClass);
0139:
0140: if (!EntityBean.class.isAssignableFrom(ejbClass)
0141: && !isAllowPOJO())
0142: throw error(L
0143: .l(
0144: "'{0}' must implement EntityBean. Entity beans must implement javax.ejb.EntityBean.",
0145: ejbClass.getName()));
0146:
0147: validateNonFinalMethod("setEntityContext",
0148: new Class[] { EntityContext.class }, isAllowPOJO());
0149: validateNonFinalMethod("unsetEntityContext", new Class[0],
0150: isAllowPOJO());
0151: validateNonFinalMethod("ejbActivate", new Class[0],
0152: isAllowPOJO());
0153: validateNonFinalMethod("ejbPassivate", new Class[0],
0154: isAllowPOJO());
0155:
0156: validateNonFinalMethod("ejbRemove", new Class[0], isAllowPOJO());
0157: // XXX: spec doesn't enforce this
0158: // validator.validateException(implMethod, RemoveException.class);
0159: validateNonFinalMethod("ejbLoad", new Class[0], isAllowPOJO());
0160: validateNonFinalMethod("ejbStore", new Class[0], isAllowPOJO());
0161: }
0162:
0163: /**
0164: * Returns the amber entity-type.
0165: */
0166: public EntityType getEntityType() {
0167: AmberPersistenceUnit amberPersistenceUnit = getEjbContainer()
0168: .createEjbPersistenceUnit();
0169:
0170: EntityType type = amberPersistenceUnit.createEntity(
0171: getAbstractSchemaName(), JClassWrapper
0172: .create(getEJBClassWrapper().getJavaClass()));
0173:
0174: // XXX: why is this if statement required?
0175: if (getLocalList().size() > 0)
0176: type.setProxyClass(JClassWrapper.create(getLocalList().get(
0177: 0).getJavaClass()));
0178:
0179: return type;
0180: }
0181:
0182: /**
0183: * Returns the primary key class.
0184: */
0185: public ApiClass getPrimKeyClass() {
0186: return _primKeyClass;
0187: }
0188:
0189: /**
0190: * Sets the primary key class.
0191: */
0192: public void setPrimKeyClass(Class cl) {
0193: _primKeyClass = new ApiClass(cl);
0194: }
0195:
0196: /**
0197: * Returns the primary key class.
0198: */
0199: public ApiClass getCompositeKeyClass() {
0200: return _compositeKeyClass;
0201: }
0202:
0203: /**
0204: * Returns the primary key field.
0205: */
0206: public String getPrimKeyField() {
0207: return _primKeyField;
0208: }
0209:
0210: /**
0211: * Sets the primary key field.
0212: */
0213: public void setPrimKeyField(String field) {
0214: _primKeyField = field;
0215: }
0216:
0217: /**
0218: * Sets the persistence type.
0219: */
0220: public void setPersistenceType(String type) throws ConfigException {
0221: if ("Bean".equals(type))
0222: _isCMP = false;
0223: else if ("Container".equals(type)) {
0224: _isCMP = true;
0225:
0226: /* ejb/0880
0227: if (getConfig().getEJBManager().getDataSource() == null) {
0228: throw new ConfigException(L.l("No DataSource found. The EJB server can't find a configured database."));
0229: }
0230: */
0231: } else
0232: throw new ConfigException(
0233: L
0234: .l(
0235: "'{0}' is an known persistence-type. <persistence-type> must either be 'Bean' or 'Container'.",
0236: type));
0237: }
0238:
0239: /**
0240: * Returns true if the entity bean is a CMP.
0241: */
0242: public boolean isCMP() {
0243: return _isCMP && "2.x".equals(_cmpVersion);
0244: }
0245:
0246: /**
0247: * Sets true if the entity bean is CMP.
0248: */
0249: public void setCMP(boolean isCMP) {
0250: _isCMP = isCMP;
0251: }
0252:
0253: /**
0254: * Returns true if the entity bean is a CMP.
0255: */
0256: public boolean isCMP1() {
0257: return _isCMP && "1.x".equals(_cmpVersion);
0258: }
0259:
0260: /**
0261: * Gets the implementation class name.
0262: */
0263: public String getFullImplName() {
0264: if (isCMP()) {
0265: String name = "_ejb." + getEJBName() + "."
0266: + getEJBClassName() + "__Amber";
0267: return JavaClassGenerator.cleanClassName(name);
0268: } else
0269: return super .getFullImplName();
0270: }
0271:
0272: public ApiClass getLocal() {
0273: return getLocalList().get(0);
0274: }
0275:
0276: /**
0277: * Sets the CMP version.
0278: */
0279: public void setCmpVersion(String version) throws ConfigException {
0280: _cmpVersion = version;
0281:
0282: if (!version.equals("1.x") && !version.equals("2.x"))
0283: throw error(L
0284: .l(
0285: "CMP version '{0}' is not currently supported. Only CMP version 1.x and 2.x are supported.",
0286: version));
0287: }
0288:
0289: /**
0290: * Sets true if the bean is reentrant.
0291: */
0292: public void setReentrant(boolean reentrant) throws ConfigException {
0293: _isReentrant = reentrant;
0294: }
0295:
0296: /**
0297: * Returns the JNDI name for the data-source
0298: */
0299: public DataSource getDataSource() {
0300: return _dataSource;
0301: }
0302:
0303: /**
0304: * Sets the data-source for the bean.
0305: */
0306: public void setDataSource(DataSource dataSource) {
0307: _dataSource = dataSource;
0308: }
0309:
0310: /**
0311: * Returns the abstract-schema-name for the bean.
0312: */
0313: public String getAbstractSchemaName() {
0314: if (_abstractSchemaName != null)
0315: return _abstractSchemaName;
0316: else {
0317: String name = getEJBName();
0318:
0319: int p = name.lastIndexOf('/');
0320: if (p < 0)
0321: return name;
0322: else
0323: return name.substring(p + 1);
0324: }
0325: }
0326:
0327: /**
0328: * Sets the abstract-schema for the bean.
0329: */
0330: public void setAbstractSchemaName(String abstractSchema)
0331: throws ConfigException {
0332: _abstractSchemaName = abstractSchema;
0333:
0334: EjbEntityBean bean = getConfig().findEntityBySchema(
0335: abstractSchema);
0336:
0337: if (bean != null && this != bean)
0338: throw new ConfigException(
0339: L
0340: .l(
0341: "Entity bean '{0}' already has abstract schema '{1}'. abstract-schema-name values must be distinct.",
0342: bean.getEJBName(), abstractSchema));
0343: }
0344:
0345: /**
0346: * Returns the sql-table for the bean.
0347: */
0348: public String getSQLTable() {
0349: return _sqlTable;
0350: }
0351:
0352: /**
0353: * Sets the sql-table for the bean.
0354: */
0355: public void setSQLTable(String sqlTable) {
0356: _sqlTable = sqlTable;
0357: }
0358:
0359: /**
0360: * Returns the number of items in the entity cache.
0361: */
0362: public int getCacheSize() {
0363: return _cacheSize;
0364: }
0365:
0366: /**
0367: * Sets the number of items in the entity cache.
0368: */
0369: public void setCacheSize(int cacheSize) {
0370: _cacheSize = cacheSize;
0371: }
0372:
0373: /**
0374: * Sets the timeout for items in the entity cache.
0375: */
0376: public void setCacheTimeout(Period cacheTimeout) {
0377: _cacheTimeout = cacheTimeout.getPeriod();
0378: }
0379:
0380: /**
0381: * Gets the timeout for items in the entity cache.
0382: */
0383: public long getCacheTimeout() {
0384: return _cacheTimeout;
0385: }
0386:
0387: /**
0388: * Returns true if the entity bean is read-only
0389: */
0390: public boolean isReadOnly() {
0391: return _isReadOnly;
0392: }
0393:
0394: /**
0395: * Sets true if the entity bean is read-only
0396: */
0397: public void setReadOnly(boolean isReadOnly) {
0398: _isReadOnly = isReadOnly;
0399: }
0400:
0401: /**
0402: * Adds a new cmp-field.
0403: */
0404: public CmpFieldProxy createCmpField() {
0405: return new CmpFieldProxy(this );
0406: }
0407:
0408: /**
0409: * Gets all the cmp-fields
0410: */
0411: public ArrayList<CmpField> getCmpFields() {
0412: return _fields;
0413: }
0414:
0415: /**
0416: * Gets the matching cmp-field.
0417: */
0418: public CmpField getCmpField(String fieldName) {
0419: for (int i = 0; i < _fields.size(); i++) {
0420: CmpField field = _fields.get(i);
0421:
0422: if (field.getName().equals(fieldName))
0423: return field;
0424: }
0425:
0426: return null;
0427: }
0428:
0429: /**
0430: * Add a cmp-field.
0431: */
0432: public CmpField addField(String fieldName) throws ConfigException {
0433: CmpField field = getCmpField(fieldName);
0434:
0435: if (field == null) {
0436: field = new CmpField(this );
0437: field.setFieldName(fieldName);
0438:
0439: field.init();
0440:
0441: _fields.add(field);
0442: }
0443:
0444: return field;
0445: }
0446:
0447: /**
0448: * Returns the field getter method.
0449: */
0450: public ApiMethod getFieldGetter(String fieldName) {
0451: if (fieldName == null)
0452: return null;
0453:
0454: String methodName = ("get"
0455: + Character.toUpperCase(fieldName.charAt(0)) + fieldName
0456: .substring(1));
0457:
0458: try {
0459: return getMethod(getEJBClassWrapper(), methodName,
0460: new Class[0]);
0461: } catch (Throwable e) {
0462: return null;
0463: }
0464: }
0465:
0466: /**
0467: * Adds a relation role.
0468: */
0469: public void addRelation(CmrRelation relation)
0470: throws ConfigException {
0471: String fieldName = relation.getName();
0472:
0473: ApiMethod method = getMethodField(fieldName);
0474:
0475: if (method == null && !(relation instanceof CmrMap))
0476: throw error(L.l("'{0}' is a missing method", fieldName));
0477:
0478: // relation.setJavaType(method.getReturnType());
0479:
0480: _relations.add(relation);
0481: }
0482:
0483: /**
0484: * Gets the matching cmp-relation.
0485: */
0486: public CmrRelation getRelation(String relationName) {
0487: for (int i = 0; i < _relations.size(); i++) {
0488: CmrRelation relation = _relations.get(i);
0489:
0490: if (relationName.equals(relation.getName()))
0491: return relation;
0492: }
0493:
0494: return null;
0495: }
0496:
0497: /**
0498: * Returns the cmp-relations
0499: */
0500: public ArrayList<CmrRelation> getRelations() {
0501: return _relations;
0502: }
0503:
0504: /**
0505: * Returns the stub methods
0506: */
0507: public ArrayList<ApiMethod> getStubMethods() {
0508: return _stubMethods;
0509: }
0510:
0511: /**
0512: * Adds a stub method
0513: */
0514: public void addStubMethod(ApiMethod method) {
0515: if (!_stubMethods.contains(method))
0516: _stubMethods.add(method);
0517: }
0518:
0519: /**
0520: * Gets the matching cmp-field.
0521: */
0522: public Class getFieldType(String fieldName) {
0523: ApiMethod method = getMethodField(fieldName);
0524:
0525: if (method != null)
0526: return method.getReturnType();
0527: else
0528: return null;
0529: }
0530:
0531: /**
0532: * Creates a query object for addition.
0533: */
0534: public Query createQuery() {
0535: return new Query(this );
0536: }
0537:
0538: /**
0539: * Adds a query.
0540: */
0541: public void addQuery(Query query) {
0542: MethodSignature sig = query.getSignature();
0543:
0544: EjbMethodPattern method = getMethod(sig);
0545:
0546: if (method == null) {
0547: method = new EjbMethodPattern(this , sig);
0548: method.setLocation(query.getConfigLocation());
0549: _methodList.add(method);
0550: }
0551:
0552: method.setQueryLocation(query.getConfigLocation());
0553: method.setQuery(query.getEjbQl());
0554: }
0555:
0556: public EjbMethodPattern getMethod(MethodSignature sig) {
0557: for (int i = 0; i < _methodList.size(); i++) {
0558: EjbMethodPattern method = _methodList.get(i);
0559:
0560: if (method.getSignature().equals(sig))
0561: return method;
0562: }
0563:
0564: return null;
0565: }
0566:
0567: /**
0568: * Gets the best method.
0569: */
0570: public EjbMethodPattern getQuery(ApiMethod method, String intf) {
0571: return getMethodPattern(method, intf);
0572: }
0573:
0574: /**
0575: * returns the method list.
0576: */
0577: public ArrayList<EjbMethodPattern> getQueryList() {
0578: return _methodList;
0579: }
0580:
0581: /**
0582: * Creates the bean generator for the session bean.
0583: */
0584: @Override
0585: protected BeanGenerator createBeanGenerator() {
0586: _entityBean = new EntityGenerator(this );
0587:
0588: return _entityBean;
0589: }
0590:
0591: /**
0592: * Configure initialization.
0593: */
0594: @PostConstruct
0595: public void init() throws ConfigException {
0596: try {
0597: if (!isCMP() && getEJBClassWrapper().isAbstract())
0598: throw error(L
0599: .l(
0600: "'{0}' must not be abstract. BMP entity beans may not be abstract.",
0601: getEJBClass().getName()));
0602:
0603: super .init();
0604:
0605: if (_primKeyClass == null
0606: && !isAllowPOJO()
0607: && (getRemoteList().size() > 0 || getLocalList()
0608: .size() > 0))
0609: throw new ConfigException(
0610: L
0611: .l(
0612: "{0}: <entity> has no primary key class. Entity beans must define a prim-key-class.",
0613: getEJBClass().getName()));
0614:
0615: if (getRemoteHome() != null)
0616: validateHome(getRemoteHome());
0617: if (getLocalHome() != null)
0618: validateHome(getLocalHome());
0619: for (ApiClass remote : getRemoteList())
0620: validateRemote(remote);
0621: for (ApiClass local : getLocalList())
0622: validateRemote(local);
0623:
0624: validateMethods();
0625: } catch (ConfigException e) {
0626: throw ConfigException.create(getLocation(), e);
0627: }
0628:
0629: //J2EEManagedObject.register(new com.caucho.management.j2ee.EntityBean(this));
0630: }
0631:
0632: /**
0633: * Creates the views.
0634: */
0635: protected void createViews21() throws ConfigException {
0636: if (_remoteHome != null) {
0637: _remoteHomeView = createHomeView(_remoteHome, "RemoteHome");
0638: _remoteHomeView.introspect();
0639: }
0640:
0641: if (_remoteList.size() > 0) {
0642: ArrayList<ApiClass> list = new ArrayList<ApiClass>();
0643: list.addAll(_remoteList);
0644:
0645: _remoteView = createRemoteObjectView(list, "Remote", "21");
0646: _remoteView.introspect();
0647: }
0648:
0649: /*
0650: if (_remote21 != null) {
0651: ArrayList<ApiClass> list = new ArrayList<ApiClass>();
0652: list.add(_remote21);
0653:
0654: _remoteView21 = createRemoteObjectView(list, "Remote", "21");
0655: _remoteView21.introspect();
0656: }
0657:
0658: else if (_remoteList.size() > 0) {
0659: ArrayList<ApiClass> list = new ArrayList<ApiClass>();
0660: list.addAll(_remoteList);
0661: list.remove(_remote21);
0662:
0663: if (list.size() > 0) {
0664: _remoteView = createRemoteObjectView(list, "Remote", "");
0665: _remoteView.introspect();
0666: }
0667: }
0668: */
0669:
0670: if (_localHome != null) {
0671: _localHomeView = createHomeView(_localHome, "LocalHome");
0672: _localHomeView.introspect();
0673: }
0674:
0675: /*
0676: if (_local21 != null) {
0677: ArrayList<ApiClass> list = new ArrayList<ApiClass>();
0678: list.add(_local21);
0679:
0680: _localView21 = createObjectView(list, "Local", "21");
0681: _localView21.introspect();
0682: }
0683: */
0684:
0685: if (_localList.size() > 0) {
0686: ArrayList<ApiClass> list = new ArrayList<ApiClass>();
0687: list.addAll(_localList);
0688: //list.remove(_local21);
0689:
0690: if (list.size() > 0) {
0691: _localView = createObjectView(list, "Local", "");
0692: _localView.introspect();
0693: }
0694: }
0695: }
0696:
0697: /**
0698: * Assembles the generator methods.
0699: */
0700: protected void assembleViews(BeanAssembler assembler,
0701: String fullClassName) throws ConfigException {
0702: if (_remoteHomeView != null)
0703: _remoteHomeView.assembleView(assembler, fullClassName);
0704:
0705: if (_remoteView != null)
0706: _remoteView.assembleView(assembler, fullClassName);
0707:
0708: /*
0709: if (_remoteView21 != null)
0710: _remoteView21.assembleView(assembler, fullClassName);
0711: */
0712:
0713: if (_localHomeView != null)
0714: _localHomeView.assembleView(assembler, fullClassName);
0715:
0716: /*
0717: if (_localView21 != null)
0718: _localView21.assembleView(assembler, fullClassName);
0719: */
0720:
0721: if (_localView != null)
0722: _localView.assembleView(assembler, fullClassName);
0723: }
0724:
0725: /**
0726: * Configure initialization.
0727: */
0728: @Override
0729: public void initIntrospect() throws ConfigException {
0730: if (isCMP()) {
0731: introspectCMPFields();
0732:
0733: introspectCMPId();
0734:
0735: validateCMPFields();
0736: }
0737: }
0738:
0739: /**
0740: * Introspects the missing CMP fields.
0741: */
0742: protected void introspectCMPFields() throws ConfigException {
0743: for (ApiMethod method : getEJBClassWrapper().getMethods()) {
0744: if (!method.isAbstract())
0745: continue;
0746:
0747: String name = method.getName();
0748:
0749: if (name.startsWith("get") && name.length() > 3
0750: && method.getParameterTypes().length == 0) {
0751: String fieldName = getterToFieldName(name);
0752:
0753: CmrRelation rel = getRelation(fieldName);
0754:
0755: if (rel != null) {
0756: continue;
0757: }
0758:
0759: Class type = method.getReturnType();
0760:
0761: if (Collection.class.isAssignableFrom(type)) {
0762: throw error(L.l("'{0}' needs to be a relation",
0763: fieldName));
0764: } else if (Map.class.isAssignableFrom(type)) {
0765: throw error(L.l("'{0}' needs to be a relation",
0766: fieldName));
0767: } else if (EJBLocalObject.class.isAssignableFrom(type)) {
0768: throw error(L
0769: .l(
0770: "{0}: '{1}' needs to be defined in an ejb-relation for getter method {2}.",
0771: getEJBClass().getName(), fieldName,
0772: method.getFullName()));
0773: } else if (EJBObject.class.isAssignableFrom(type)) {
0774: throw error(L.l("'{0}' needs to be a relation",
0775: fieldName));
0776: } else {
0777: CmpField cmpField = addField(fieldName);
0778:
0779: cmpField.setJavaType(method.getReturnType());
0780: }
0781: }
0782: }
0783: }
0784:
0785: /**
0786: * Introspects the missing CMP fields.
0787: */
0788: protected void validateCMPFields() throws ConfigException {
0789: for (ApiMethod method : getEJBClassWrapper().getMethods()) {
0790: if (!method.isAbstract())
0791: continue;
0792:
0793: String name = method.getName();
0794:
0795: if (name.startsWith("ejb")) {
0796: continue;
0797: } else if (name.startsWith("get") && name.length() > 3
0798: && method.getParameterTypes().length == 0) {
0799: String fieldName = getterToFieldName(name);
0800:
0801: if (getCmpField(fieldName) != null
0802: || getRelation(fieldName) != null)
0803: continue;
0804: } else if (name.startsWith("get") && name.length() > 3
0805: && method.getParameterTypes().length == 1) {
0806: String fieldName = getterToFieldName(name);
0807:
0808: CmrRelation rel = getRelation(fieldName);
0809:
0810: if (rel instanceof CmrMap) {
0811: CmrMap map = (CmrMap) rel;
0812:
0813: if (method.equals(map.getMapMethod()))
0814: continue;
0815: }
0816: } else if (name.startsWith("set") && name.length() > 3
0817: && method.getParameterTypes().length == 1) {
0818: String fieldName = getterToFieldName(name);
0819:
0820: CmpField field = getCmpField(fieldName);
0821:
0822: if (field == null) {
0823: } else if (method.isMatch(field.getSetter()))
0824: continue;
0825: else
0826: throw new ConfigException(
0827: L
0828: .l(
0829: "{0}: '{1}' does not match the corresponding cmp-field getter '{2}'.",
0830: getEJBClass().getName(),
0831: method.getFullName(), field
0832: .getGetter()
0833: .getFullName()));
0834:
0835: CmrRelation rel = getRelation(fieldName);
0836:
0837: if (rel == null) {
0838: } else if (method.equals(rel.getSetter()))
0839: continue;
0840: else
0841: throw new ConfigException(
0842: L
0843: .l(
0844: "{0}: '{1}' does not match the corresponding cmp-field getter '{2}'.",
0845: getEJBClass().getName(),
0846: method.getFullName(), rel
0847: .getGetter()
0848: .getFullName()));
0849: }
0850:
0851: throw error(L
0852: .l(
0853: "{0}: '{1}' must not be abstract. Business methods must be implemented.",
0854: getEJBClass().getName(), method
0855: .getFullName()));
0856: }
0857: }
0858:
0859: /**
0860: * Introspects the CMP id.
0861: */
0862: protected void introspectCMPId() throws ConfigException {
0863:
0864: if (_primKeyClass != null
0865: && !_primKeyClass.isPrimitive()
0866: && !_primKeyClass.getName().startsWith("java.lang.")
0867: && !_primKeyClass.getName().equals("java.util.Date")
0868: && !_primKeyClass.getName().equals("java.sql.Date")
0869: && !_primKeyClass.getName().equals("java.sql.Time")
0870: && !_primKeyClass.getName()
0871: .equals("java.sql.Timestamp")
0872: && !EJBLocalObject.class.isAssignableFrom(_primKeyClass
0873: .getJavaClass())) {
0874:
0875: if (_primKeyField != null)
0876: throw error(L
0877: .l(
0878: "{0}: 'primkey-field' must not be defined for a composite primkey-class.",
0879: getEJBClass().getName()));
0880:
0881: _compositeKeyClass = _primKeyClass;
0882: introspectCMPCompositeId();
0883: return;
0884: }
0885:
0886: String id = _primKeyField;
0887: if (id == null)
0888: id = "id";
0889:
0890: CmpProperty property = getCmpField(id);
0891:
0892: if (property == null)
0893: property = getRelation(id);
0894:
0895: if (property == null)
0896: throw error(L
0897: .l(
0898: "{0}: primary key field '{1}' is an unknown cmp-field",
0899: getEJBClass().getName(), id));
0900:
0901: property.setId(true);
0902: }
0903:
0904: /**
0905: * Introspects the CMP id.
0906: */
0907: protected void introspectCMPCompositeId() throws ConfigException {
0908: try {
0909: ApiMethod equals = _primKeyClass.getMethod("equals",
0910: new Class[] { Object.class });
0911:
0912: if (equals.getDeclaringClass().equals(Object.class))
0913: throw error(L
0914: .l(
0915: "{0}: primary key class '{1}' must override the 'equals' method.",
0916: getEJBClass().getName(), _primKeyClass
0917: .getName()));
0918: } catch (ConfigException e) {
0919: throw e;
0920: } catch (Throwable e) {
0921: }
0922:
0923: try {
0924: ApiMethod hashCode = _primKeyClass.getMethod("hashCode",
0925: new Class[] {});
0926:
0927: if (hashCode.getDeclaringClass().getName().equals(
0928: Object.class.getName()))
0929: throw error(L
0930: .l(
0931: "{0}: primary key class '{1}' must override the 'hashCode' method.",
0932: getEJBClass().getName(), _primKeyClass
0933: .getName()));
0934: } catch (ConfigException e) {
0935: throw e;
0936: } catch (Throwable e) {
0937: }
0938:
0939: if (_primKeyClass.getFields().length == 0)
0940: throw error(L
0941: .l(
0942: "{0}: compound key '{1}' has no public accessible fields. Compound key fields must be public.",
0943: getEJBClass().getName(), _primKeyClass
0944: .getName()));
0945:
0946: for (Field field : _primKeyClass.getFields()) {
0947: CmpProperty cmpProperty = getCmpField(field.getName());
0948:
0949: if (cmpProperty == null)
0950: cmpProperty = getRelation(field.getName());
0951:
0952: if (cmpProperty == null)
0953: throw error(L
0954: .l(
0955: "{0}: primary key field '{1}' is an unknown field.",
0956: getEJBClass().getName(), field
0957: .getName()));
0958:
0959: cmpProperty.setId(true);
0960: }
0961: }
0962:
0963: private static String getterToFieldName(String name) {
0964: String fieldName = name.substring(3);
0965: char ch = fieldName.charAt(0);
0966:
0967: if (Character.isUpperCase(ch)
0968: && (fieldName.length() == 1 || Character
0969: .isLowerCase(fieldName.charAt(1)))) {
0970: fieldName = Character.toLowerCase(ch)
0971: + fieldName.substring(1);
0972: }
0973:
0974: return fieldName;
0975: }
0976:
0977: /**
0978: * Configure for amber.
0979: */
0980: public void configureAmber(AmberConfig config)
0981: throws ConfigException {
0982: if (isCMP()) {
0983: try {
0984: config.addBean(this );
0985: } catch (Exception e) {
0986: throw ConfigException.create(getLocation(), e);
0987: }
0988: }
0989: }
0990:
0991: /**
0992: * Creates the assembler for the bean.
0993: */
0994: protected BeanAssembler createAssembler(String fullClassName) {
0995: if (isCMP())
0996: return new AmberAssembler(this , fullClassName);
0997: else
0998: return new EntityAssembler(this , fullClassName);
0999: }
1000:
1001: /**
1002: * Adds the assemblers.
1003: */
1004: protected void addImports(BeanAssembler assembler) {
1005: super .addImports(assembler);
1006:
1007: assembler.addImport("com.caucho.ejb.FinderExceptionWrapper");
1008:
1009: assembler.addImport("com.caucho.ejb.entity.EntityServer");
1010: assembler.addImport("com.caucho.ejb.entity.QEntityContext");
1011: assembler.addImport("com.caucho.ejb.entity.EntityHome");
1012: assembler.addImport("com.caucho.ejb.entity.EntityLocalHome");
1013: assembler.addImport("com.caucho.ejb.entity.EntityRemoteHome");
1014: assembler.addImport("com.caucho.ejb.entity.EntityObject");
1015: assembler.addImport("com.caucho.ejb.entity.QEntity");
1016: }
1017:
1018: /**
1019: * Introspects an ejb method.
1020: */
1021: protected EjbBaseMethod introspectEJBMethod(ApiMethod method)
1022: throws ConfigException {
1023: String methodName = method.getName();
1024: Class[] paramTypes = method.getParameterTypes();
1025:
1026: if (methodName.startsWith("ejbSelect") && method.isAbstract()) {
1027: validateSelectMethod(method);
1028:
1029: EjbMethodPattern pattern = getMethodPattern(method, "Local");
1030:
1031: if (pattern == null)
1032: throw error(L
1033: .l(
1034: "{0}: '{1}' expects an ejb-ql query. ejbSelect methods must have an ejb-ql query in the deployment descriptor.",
1035: getEJBClass().getName(), method
1036: .getFullName()));
1037:
1038: String query = pattern.getQuery();
1039:
1040: EjbAmberSelectMethod select;
1041: select = new EjbAmberSelectMethod(this , method, query,
1042: pattern.getQueryLocation());
1043:
1044: select.setQueryLoadsBean(pattern.getQueryLoadsBean());
1045:
1046: return select;
1047: }
1048:
1049: return null;
1050: }
1051:
1052: /**
1053: * Introspects an ejb method.
1054: */
1055: protected void validateImplMethod(ApiMethod method)
1056: throws ConfigException {
1057: String methodName = method.getName();
1058: Class[] paramTypes = method.getParameterTypes();
1059:
1060: if (method.isAbstract() && methodName.startsWith("get")
1061: && paramTypes.length == 0) {
1062: } else if (method.isAbstract() && methodName.startsWith("get")
1063: && paramTypes.length == 1) {
1064: } else if (method.isAbstract() && methodName.startsWith("set")
1065: && paramTypes.length == 1) {
1066: } else if (methodName.startsWith("ejb")) {
1067: } else if (method.isAbstract()) {
1068: throw error(L
1069: .l(
1070: "{0}: '{1}' must not be abstract. The bean must implement its business methods.",
1071: getEJBClass().getName(), method
1072: .getFullName()));
1073: }
1074: }
1075:
1076: /**
1077: * Check that a method is public.
1078: *
1079: * @return the matching method
1080: */
1081: private void validateSelectMethod(ApiMethod method)
1082: throws ConfigException {
1083: if (!method.isPublic()) {
1084: throw error(L
1085: .l(
1086: "{0}: '{1}' must be public. ejbSelect methods must be public.",
1087: getEJBClass().getName(), method
1088: .getFullName()));
1089: } else if (method.isStatic()) {
1090: throw error(L
1091: .l(
1092: "{0}: '{1}' must not be static. ejbSelect methods must not be static.",
1093: getEJBClass().getName(), method
1094: .getFullName()));
1095: } else if (!method.isAbstract()) {
1096: throw error(L
1097: .l(
1098: "{0}: '{1}' must be abstract. ejbSelect methods must be abstract.",
1099: getEJBClass().getName(), method
1100: .getFullName()));
1101: }
1102: }
1103:
1104: /**
1105: * Creates the views.
1106: */
1107: @Override
1108: protected EjbHomeView createHomeView(ApiClass homeClass,
1109: String prefix) throws ConfigException {
1110: return new EjbEntityHomeView(this , homeClass, prefix);
1111: }
1112:
1113: /**
1114: * Creates the views.
1115: */
1116: @Override
1117: protected EjbObjectView createObjectView(
1118: ArrayList<ApiClass> apiList, String prefix, String suffix)
1119: throws ConfigException {
1120: if (isCMP())
1121: return new EjbCmpView(this , apiList, prefix);
1122: else
1123: return new EjbEntityView(this , apiList, prefix);
1124: }
1125:
1126: /**
1127: * Deploys the bean.
1128: */
1129: @Override
1130: public AbstractServer deployServer(EjbContainer ejbManager,
1131: JavaClassGenerator javaGen) throws ClassNotFoundException {
1132: EntityServer server = new EntityServer(ejbManager);
1133:
1134: server.setModuleName(getEJBModuleName());
1135: server.setEJBName(getEJBName());
1136: server.setMappedName(getMappedName());
1137: server.setId(getEJBModuleName() + "#" + getEJBName());
1138:
1139: server.setRemoteHomeClass(getRemoteHomeClass());
1140: server.setRemoteObjectClass(getRemoteClass());
1141:
1142: Class contextImplClass = javaGen.loadClass(getSkeletonName());
1143:
1144: server.setContextImplClass(contextImplClass);
1145:
1146: server.setCMP(isCMP());
1147:
1148: if (_primKeyClass != null)
1149: server.setPrimaryKeyClass(_primKeyClass.getJavaClass());
1150:
1151: server.setLoadLazyOnTransaction(_loadLazyOnTransaction);
1152:
1153: server.setInitProgram(getInitProgram());
1154:
1155: if (isCMP()) {
1156: server.setAmberEntityHome(getEntityType().getHome());
1157: }
1158:
1159: Thread thread = Thread.currentThread();
1160: ClassLoader oldLoader = thread.getContextClassLoader();
1161:
1162: try {
1163: thread.setContextClassLoader(server.getClassLoader());
1164:
1165: try {
1166: // ejb/086d
1167: if (getServerProgram() != null)
1168: getServerProgram().configure(server);
1169: } catch (ConfigException e) {
1170: throw e;
1171: } catch (Exception e) {
1172: throw ConfigException.create(e);
1173: }
1174: } finally {
1175: thread.setContextClassLoader(oldLoader);
1176: }
1177:
1178: return server;
1179: }
1180:
1181: private void validateMethods() throws ConfigException {
1182: ApiClass primKeyClass = getPrimKeyClass();
1183:
1184: for (ApiMethod method : getEJBClassWrapper().getMethods()) {
1185: String name = method.getName();
1186:
1187: try {
1188: // XXX: ???
1189: ApiMethod cleanMethod = getEJBClassWrapper().getMethod(
1190: name, method.getParameterTypes());
1191: if (cleanMethod != null)
1192: method = cleanMethod;
1193: } catch (Exception e) {
1194: }
1195:
1196: if (EntityBean.class.isAssignableFrom(method
1197: .getReturnType()))
1198: throw error(L
1199: .l(
1200: "{0}: '{1}' must not return entity bean '{2}'. Entity bean methods must always return local or remote interfaces.",
1201: getEJBClass().getName(), method
1202: .getFullName(), method
1203: .getReturnType()
1204: .getSimpleName()));
1205:
1206: if (name.startsWith("ejbFind")) {
1207: if (!isCMP()) {
1208: validateNonFinalMethod(method.getName(), method
1209: .getParameterTypes());
1210: } else if (true) {
1211: // allow overriding
1212: validateNonFinalMethod(method.getName(), method
1213: .getParameterTypes());
1214: } else {
1215: throw error(L
1216: .l(
1217: "{0}: '{1}' forbidden. CMP entity beans must not implement ejbFind methods.",
1218: method.getDeclaringClass()
1219: .getName(), method
1220: .getFullName()));
1221: }
1222: } else if (name.startsWith("ejbSelect")) {
1223: if (!method.isAbstract())
1224: throw error(L
1225: .l(
1226: "{0}: '{1}' must be abstract. ejbSelect methods must be abstract.",
1227: method.getDeclaringClass()
1228: .getName(), method
1229: .getFullName()));
1230: if (!method.isPublic())
1231: throw error(L.l("{0}: '{1}' must be public.",
1232: method.getDeclaringClass().getName(),
1233: method.getFullName()));
1234: validateException(method, FinderException.class);
1235: } else if (name.startsWith("ejbCreate")) {
1236: validateNonFinalMethod(method.getName(), method
1237: .getParameterTypes());
1238: if (!isPrimaryKeyClass(method.getReturnType()))
1239: throw error(L
1240: .l(
1241: "{0}: '{1}' must return '{2}'. ejbCreate methods must return the primary key.",
1242: method.getDeclaringClass()
1243: .getName(), method
1244: .getFullName(),
1245: primKeyClass.getName()));
1246: if (isCMP())
1247: validateException(method, CreateException.class);
1248: } else if (name.startsWith("ejbPostCreate")) {
1249: validateNonFinalMethod(method.getName(), method
1250: .getParameterTypes());
1251:
1252: if (!method.getReturnType().getName().equals("void"))
1253: throw error(L
1254: .l(
1255: "{0}: '{1}' must return void. ejbPostCreate methods must return void.",
1256: method.getDeclaringClass()
1257: .getName(), method
1258: .getFullName()));
1259: } else if (name.startsWith("create") && method.isAbstract()) {
1260: // validated in dependent phase.
1261: } else if (name.startsWith("ejbHome")) {
1262: validateNonFinalMethod(method.getName(), method
1263: .getParameterTypes());
1264: } else if (name.equals("ejbRemove")) {
1265: if (method.getParameterTypes().length != 0)
1266: throw error(L.l(
1267: "{0}: '{1}' must have no arguments.",
1268: method.getDeclaringClass().getName(),
1269: method.getFullName()));
1270: } else if (name.equals("ejbTimeout")) {
1271: Class[] types = method.getParameterTypes();
1272:
1273: if (types.length != 1
1274: || !types[0].getName()
1275: .equals("javax.ejb.Timer"))
1276: throw error(L
1277: .l(
1278: "{0}: '{1}' must have one javax.ejb.Timer argument.",
1279: method.getDeclaringClass()
1280: .getName(), method
1281: .getFullName()));
1282: } else if (ApiClass.ENTITY_BEAN.getMethod(method) != null) {
1283: } else if (name.equals("finalize")
1284: && method.getParameterTypes().length == 0
1285: && !method.getDeclaringClass().getName().equals(
1286: "java.lang.Object"))
1287: throw error(L
1288: .l(
1289: "{0}: Entity beans must not define 'finalize'.",
1290: getEJBClass().getClass()));
1291: else if (name.startsWith("ejb")) {
1292: throw error(L
1293: .l(
1294: "{0}: '{1}' must not start with 'ejb'. ejbXXX methods are reserved by the EJB spec.",
1295: method.getDeclaringClass().getName(),
1296: method.getFullName()));
1297: } else {
1298: boolean isAbstract = method.isAbstract();
1299: ApiMethod persist = null;
1300:
1301: if (!isAbstract || !isCMP()) {
1302: } else if (method.getName().startsWith("get")) {
1303: //validateGetImpl(method); -- in relations
1304: } else if (method.getName().startsWith("set")) {
1305: //validateSetImpl(method); -- in relations
1306: } else if (method.getName().startsWith("ejbSelect")) {
1307: } else {
1308: throw error(L
1309: .l(
1310: "{0}: '{1}' must not be abstract. Only CMP methods may be abstract.",
1311: method.getDeclaringClass()
1312: .getName(), method
1313: .getFullName()));
1314: }
1315:
1316: /* XXX: should look for matching cmp/cmr-field */
1317: /*
1318: if (! getBeanManagedPersistence()) {
1319: persist = validator.findPersistentMethod(method.getName(),
1320: method.getParameterTypes());
1321:
1322: if (persist != null && ! isAbstract)
1323: throw error(L.l("'{0}' must be abstract in {1}",
1324: method.getFullName(),
1325: method.getDeclaringClass().getName()));
1326: }
1327:
1328: if (persist == null && isAbstract)
1329: throw error(L.l("'{0}' must not be abstract in {1}",
1330: method.getFullName(),
1331: method.getDeclaringClass().getName()));
1332: */
1333: }
1334: }
1335: }
1336:
1337: /**
1338: * Validates the home interface.
1339: */
1340: private void validateHome(ApiClass homeClass)
1341: throws ConfigException {
1342: ApiClass beanClass = getEJBClassWrapper();
1343: String beanName = beanClass.getName();
1344:
1345: String homeName = homeClass.getName();
1346:
1347: ApiClass objectClass;
1348:
1349: if (EJBHome.class.isAssignableFrom(homeClass.getJavaClass()))
1350: objectClass = getRemoteList().get(0);
1351: else
1352: objectClass = getLocal();
1353:
1354: String objectName = objectClass != null ? objectClass.getName()
1355: : null;
1356:
1357: boolean hasFindByPrimaryKey = false;
1358:
1359: ApiClass primKeyClass = getPrimKeyClass();
1360:
1361: if (!homeClass.isPublic())
1362: throw error(L
1363: .l(
1364: "'{0}' must be public. Entity beans must be public.",
1365: homeName));
1366:
1367: if (beanClass.isFinal())
1368: throw error(L
1369: .l(
1370: "'{0}' must not be final. Entity beans must not be final.",
1371: beanName));
1372:
1373: if (!isCMP() && beanClass.isAbstract())
1374: throw error(L
1375: .l(
1376: "'{0}' must not be abstract. BMP entity beans must not be abstract.",
1377: beanName));
1378:
1379: if (!homeClass.isInterface())
1380: throw error(L.l("'{0}' must be an interface.", homeName));
1381:
1382: for (ApiMethod method : homeClass.getMethods()) {
1383: String name = method.getName();
1384: Class[] param = method.getParameterTypes();
1385: Class retType = method.getReturnType();
1386:
1387: if (method.getDeclaringClass().isAssignableFrom(
1388: EJBHome.class))
1389: continue;
1390:
1391: if (method.getDeclaringClass().isAssignableFrom(
1392: EJBLocalHome.class))
1393: continue;
1394:
1395: if (EJBHome.class
1396: .isAssignableFrom(homeClass.getJavaClass()))
1397: validateException(method,
1398: java.rmi.RemoteException.class);
1399:
1400: if (name.startsWith("create")) {
1401: validateException(method, CreateException.class);
1402:
1403: if (!retType.equals(objectClass.getJavaClass()))
1404: throw error(L
1405: .l(
1406: "{0}: '{1}' must return {2}. Create methods must return the local or remote interface.",
1407: homeName, method.getFullName(),
1408: objectName));
1409:
1410: String createName = "ejbC" + name.substring(1);
1411: ApiMethod implMethod = validateNonFinalMethod(
1412: createName, param, method, homeClass);
1413:
1414: if (!isPrimaryKeyClass(implMethod.getReturnType()))
1415: throw error(L
1416: .l(
1417: "{0}: '{1}' must return '{2}'. ejbCreate methods must return the primary key.",
1418: beanName, getFullMethodName(
1419: createName, param),
1420: primKeyClass.getName()));
1421:
1422: if (!hasException(implMethod, CreateException.class)) {
1423: throw error(L
1424: .l(
1425: "{0}: '{1}' must throw {2}. ejbCreate methods must throw CreateException.",
1426: implMethod.getDeclaringClass()
1427: .getName(), implMethod
1428: .getFullName(),
1429: "CreateException"));
1430:
1431: }
1432:
1433: validateExceptions(method, implMethod);
1434:
1435: createName = "ejbPostC" + name.substring(1);
1436: implMethod = validateNonFinalMethod(createName, param,
1437: method, homeClass);
1438:
1439: if (!implMethod.getReturnType().getName()
1440: .equals("void"))
1441: throw error(L
1442: .l(
1443: "{0}: '{1}' must return {2}. ejbPostCreate methods must return void.",
1444: beanName, getFullMethodName(
1445: createName, param), "void"));
1446:
1447: validateExceptions(method, implMethod);
1448: } else if (name.startsWith("find")) {
1449: if (name.equals("findByPrimaryKey")) {
1450: hasFindByPrimaryKey = true;
1451:
1452: /*
1453: if (param.length != 1 || ! param[0].equals(primKeyClass))
1454: throw error(L.l("'{0}' expected as only argument of {1} in {2}. findByPrimaryKey must take the primary key as its only argument.",
1455: getClassName(primKeyClass),
1456: name, homeName));
1457: */
1458:
1459: if (!objectClass.getJavaClass().equals(
1460: method.getReturnType()))
1461: throw error(L
1462: .l(
1463: "{0}: '{1}' must return '{2}'. Find methods must return the remote or local interface.",
1464: homeName, method.getFullName(),
1465: objectName));
1466: }
1467:
1468: String findName = "ejbF" + name.substring(1);
1469: if (!isCMP()
1470: && !isCMP1()
1471: || getMethod(beanClass, findName, param) != null) {
1472: ApiMethod impl = validateNonFinalMethod(findName,
1473: param, isAllowPOJO());
1474:
1475: if (impl != null)
1476: validateExceptions(method, impl);
1477:
1478: if (objectClass.getJavaClass().equals(
1479: method.getReturnType())) {
1480: if (impl != null
1481: && !isPrimaryKeyClass(impl
1482: .getReturnType()))
1483: throw error(L
1484: .l(
1485: "{0}: '{1}' must return primary key '{2}'. ejbFind methods must return the primary key",
1486: beanName, impl
1487: .getFullName(),
1488: primKeyClass.getName()));
1489: } else if (Collection.class.isAssignableFrom(method
1490: .getReturnType())) {
1491: if (impl != null
1492: && !Collection.class
1493: .isAssignableFrom(impl
1494: .getReturnType()))
1495: throw error(L
1496: .l(
1497: "{0}: '{1}' must return collection.",
1498: beanName, impl
1499: .getFullName()));
1500: } else if (Enumeration.class
1501: .isAssignableFrom(method.getReturnType())) {
1502: if (!Enumeration.class.isAssignableFrom(impl
1503: .getReturnType()))
1504: throw error(L
1505: .l(
1506: "{0}: '{1}' must return enumeration.",
1507: beanName, impl
1508: .getFullName()));
1509: } else
1510: throw error(L
1511: .l(
1512: "{0}: '{1}' must return '{2}' or a collection. ejbFind methods must return the primary key or a collection.",
1513: homeName, method.getFullName(),
1514: objectName));
1515: } else if (isCMP() && !name.equals("findByPrimaryKey")) {
1516: String query = findQuery(method);
1517:
1518: if (query == null) {
1519: throw error(L
1520: .l(
1521: "{0}: '{1}' expects an ejb-ql query. All find methods need queries defined in the EJB deployment descriptor.",
1522: homeName, method.getFullName()));
1523: }
1524: }
1525:
1526: validateException(method, FinderException.class);
1527:
1528: if (!retType.equals(objectClass.getJavaClass())
1529: && (!Collection.class.isAssignableFrom(retType)
1530: && !Enumeration.class.equals(retType) || name
1531: .equals("findByPrimaryKey"))) {
1532: throw error(L
1533: .l(
1534: "{0}: '{1}' must return {2} or a collection. ejbFind methods must return the primary key or a collection.",
1535: homeName, method.getFullName(),
1536: objectName));
1537: }
1538: } else if (name.startsWith("ejbSelect")) {
1539: throw error(L
1540: .l(
1541: "{0}: '{1}' forbidden. ejbSelect methods may not be exposed in the remote or local interface.",
1542: homeName, method.getFullName()));
1543: } else if (name.startsWith("ejb")) {
1544: throw error(L
1545: .l(
1546: "{0}: '{1}' forbidden. Only ejbXXX methods defined by the spec are allowed.",
1547: homeName, method.getFullName()));
1548: } else if (name.startsWith("remove")) {
1549: throw error(L
1550: .l(
1551: "{0}: '{1}' forbidden. removeXXX methods are reserved by the spec.",
1552: homeName, method.getFullName()));
1553: } else {
1554: retType = method.getReturnType();
1555:
1556: if (EJBHome.class.isAssignableFrom(homeClass
1557: .getJavaClass())
1558: && (EJBLocalObject.class
1559: .isAssignableFrom(retType) || EJBLocalHome.class
1560: .isAssignableFrom(retType)))
1561: throw error(L
1562: .l(
1563: "{1}: '{0}' must not return local interface.",
1564: homeClass.getName(), method
1565: .getFullName()));
1566:
1567: String homeMethodName = ("ejbHome"
1568: + Character.toUpperCase(name.charAt(0)) + name
1569: .substring(1));
1570: ApiMethod implMethod = validateMethod(homeMethodName,
1571: param, method, homeClass);
1572:
1573: if (!retType.equals(implMethod.getReturnType()))
1574: throw error(L.l("{0}: '{1}' must return {2}.",
1575: beanName, implMethod.getFullName(), method
1576: .getReturnType().getName()));
1577:
1578: validateExceptions(method, implMethod
1579: .getExceptionTypes());
1580: }
1581: }
1582:
1583: // ejb/0588
1584: if (!hasFindByPrimaryKey && !isAllowPOJO()
1585: && objectClass != null)
1586: throw error(L
1587: .l(
1588: "{0}: expected '{1}'. All entity homes must define findByPrimaryKey.",
1589: homeName, getFullMethodName(
1590: "findByPrimaryKey",
1591: new Class[] { primKeyClass
1592: .getJavaClass() })));
1593: }
1594:
1595: protected void assembleHomeMethods(BeanAssembler assembler,
1596: BaseClass baseClass, String contextClassName,
1597: ApiClass homeClass, String prefix)
1598: throws NoSuchMethodException {
1599: for (ApiMethod method : homeClass.getMethods()) {
1600: String className = method.getDeclaringClass().getName();
1601: String methodName = method.getName();
1602:
1603: if (className.startsWith("javax.ejb.")) {
1604: }
1605: /*
1606: else if (isOld(methods, method, i)) {
1607: }
1608: */
1609: else if (methodName.startsWith("create")) {
1610: assembleCreateMethod(method, baseClass,
1611: contextClassName, prefix);
1612: } else if (methodName.startsWith("find")) {
1613: ApiMethod beanMethod = null;
1614:
1615: String name = ("ejb"
1616: + Character.toUpperCase(methodName.charAt(0)) + methodName
1617: .substring(1));
1618:
1619: try {
1620: beanMethod = getEJBClassWrapper().getMethod(name,
1621: method.getParameterTypes());
1622: } catch (Throwable e) {
1623: }
1624:
1625: if (beanMethod != null) {
1626: EntityFindMethod findMethod;
1627: findMethod = new EntityFindMethod(method,
1628: beanMethod, contextClassName, prefix);
1629:
1630: CallChain call = findMethod.getCall();
1631: call = new EntityHomePoolChain(call);
1632: // call = getTransactionChain(call, methods[i], prefix);
1633: findMethod.setCall(call);
1634:
1635: baseClass.addMethod(findMethod);
1636: }
1637: } else {
1638: ApiMethod beanMethod = null;
1639:
1640: String name = ("ejbHome"
1641: + Character.toUpperCase(methodName.charAt(0)) + methodName
1642: .substring(1));
1643:
1644: try {
1645: beanMethod = getEJBClassWrapper().getMethod(name,
1646: method.getParameterTypes());
1647: } catch (Exception e) {
1648: throw new NoSuchMethodException(
1649: "can't find method " + name);
1650: }
1651:
1652: CallChain call = new MethodCallChain(beanMethod
1653: .getMethod());
1654: // call = assembler.createPoolChain(call);
1655: call = getTransactionChain(call, beanMethod, method,
1656: prefix);
1657:
1658: baseClass.addMethod(new BaseMethod(method.getMethod(),
1659: call));
1660: }
1661: }
1662: }
1663:
1664: protected void assembleCreateMethod(ApiMethod method,
1665: BaseClass baseClass, String contextClassName, String prefix) {
1666: String methodName = method.getName();
1667:
1668: ApiMethod beanCreateMethod = null;
1669: ApiMethod beanPostCreateMethod = null;
1670:
1671: String name = ("ejb"
1672: + Character.toUpperCase(methodName.charAt(0)) + methodName
1673: .substring(1));
1674:
1675: try {
1676: beanCreateMethod = getEJBClassWrapper().getMethod(name,
1677: method.getParameterTypes());
1678: } catch (Throwable e) {
1679: }
1680:
1681: if (beanCreateMethod == null)
1682: throw new IllegalStateException(name);
1683:
1684: name = ("ejbPost" + Character.toUpperCase(methodName.charAt(0)) + methodName
1685: .substring(1));
1686:
1687: try {
1688: beanPostCreateMethod = getEJBClassWrapper().getMethod(name,
1689: method.getParameterTypes());
1690: } catch (Throwable e) {
1691: }
1692:
1693: EntityCreateMethod createMethod;
1694:
1695: createMethod = new EntityCreateMethod(this , method,
1696: beanCreateMethod, beanPostCreateMethod,
1697: contextClassName);
1698:
1699: createMethod.setCall(getTransactionChain(
1700: createMethod.getCall(), method, method, prefix));
1701:
1702: baseClass.addMethod(createMethod);
1703: }
1704:
1705: /**
1706: * Return true if the type matches the primary key class.
1707: */
1708: private boolean isPrimaryKeyClass(Class type) {
1709: return type.equals(getPrimKeyClass().getJavaClass());
1710: }
1711:
1712: private ApiMethod getMethodField(String fieldName) {
1713: String getter = "get"
1714: + Character.toUpperCase(fieldName.charAt(0))
1715: + fieldName.substring(1);
1716:
1717: return getEJBClassWrapper().getMethod(getter, new Class[0]);
1718: }
1719:
1720: /**
1721: * Finds the matching query.
1722: */
1723: private String findQuery(ApiMethod method) throws ConfigException {
1724: EjbMethodPattern ejbMethod = getMethodPattern(method, null);
1725:
1726: if (ejbMethod != null && ejbMethod.getQuery() != null)
1727: return ejbMethod.getQuery();
1728:
1729: EjbMethodPattern ejbQuery = getQuery(method, null);
1730: if (ejbQuery != null)
1731: return ejbQuery.getQuery();
1732: else
1733: return null;
1734: }
1735:
1736: /**
1737: * Compares dependencies for identifying relations.
1738: */
1739: public boolean dependsOn(EjbEntityBean target) {
1740: for (CmrRelation rel : _relations) {
1741: if (rel.isId()) {
1742: EjbEntityBean targetBean = rel.getTargetBean();
1743:
1744: if (target == targetBean || targetBean != null
1745: && targetBean.dependsOn(target))
1746: return true;
1747: }
1748: }
1749:
1750: return false;
1751: }
1752:
1753: /**
1754: * Generates the bean prologue.
1755: */
1756: public void generateBeanPrologue(JavaWriter out) throws IOException {
1757: }
1758:
1759: /**
1760: * Generates the after commit method.
1761: */
1762: public void generateAfterCommit(JavaWriter out) throws IOException {
1763: for (CmrRelation rel : _relations) {
1764: rel.generateAfterCommit(out);
1765: }
1766: }
1767:
1768: /**
1769: * Generates the destroy method.
1770: */
1771: public void generateDestroy(JavaWriter out) throws IOException {
1772: for (CmrRelation rel : _relations) {
1773: rel.generateDestroy(out);
1774: }
1775: }
1776:
1777: public String toString() {
1778: return "EjbEntityBean[" + getEJBName() + "]";
1779: }
1780: }
|