0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jboss.ejb.plugins.cmp.jdbc.metadata;
0023:
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.Collections;
0027: import java.util.HashMap;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Map;
0031: import java.lang.reflect.Method;
0032:
0033: import org.jboss.deployment.DeploymentException;
0034: import org.jboss.metadata.EntityMetaData;
0035: import org.jboss.metadata.MetaData;
0036: import org.jboss.metadata.QueryMetaData;
0037: import org.jboss.mx.util.MBeanServerLocator;
0038: import org.w3c.dom.Element;
0039:
0040: import javax.management.ObjectName;
0041: import javax.management.MalformedObjectNameException;
0042: import javax.management.MBeanServer;
0043:
0044: /**
0045: * This immutable class contains information about an entity
0046: *
0047: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
0048: * @author <a href="sebastien.alborini@m4x.org">Sebastien Alborini</a>
0049: * @author <a href="mailto:dirk@jboss.de">Dirk Zimmermann</a>
0050: * @author <a href="mailto:loubyansky@hotmail.com">Alex Loubyansky</a>
0051: * @author <a href="mailto:heiko.rupp@cellent.de">Heiko W. Rupp</a>
0052: * @version $Revision: 57209 $
0053: */
0054: public final class JDBCEntityMetaData {
0055: /**
0056: * application metadata in which this entity is defined
0057: */
0058: private final JDBCApplicationMetaData jdbcApplication;
0059:
0060: /**
0061: * data source name in jndi
0062: */
0063: private final String dataSourceName;
0064:
0065: /**
0066: * type mapping name as specified in the deployment descriptor
0067: */
0068: private final String datasourceMappingName;
0069:
0070: /**
0071: * type mapping used for this entity
0072: */
0073: private final JDBCTypeMappingMetaData datasourceMapping;
0074:
0075: /**
0076: * the name of this entity
0077: */
0078: private final String entityName;
0079:
0080: /**
0081: * the abstract schema name of this entity
0082: */
0083: private final String abstractSchemaName;
0084:
0085: /**
0086: * the implementation class of this entity
0087: */
0088: private final Class entityClass;
0089:
0090: /**
0091: * the home class of this entity
0092: */
0093: private final Class homeClass;
0094:
0095: /**
0096: * the remote class of this entity
0097: */
0098: private final Class remoteClass;
0099:
0100: /**
0101: * the local home class of this entity
0102: */
0103: private final Class localHomeClass;
0104:
0105: /**
0106: * the local class of this entity
0107: */
0108: private final Class localClass;
0109:
0110: /**
0111: * Does this entity use cmp 1.x?
0112: */
0113: private final boolean isCMP1x;
0114:
0115: /**
0116: * the name of the table to which this entity is persisted
0117: */
0118: private final String tableName;
0119:
0120: /**
0121: * Should we try and create the table when deployed?
0122: */
0123: private final boolean createTable;
0124:
0125: /**
0126: * Should we drop the table when undeployed?
0127: */
0128: private final boolean removeTable;
0129:
0130: /**
0131: * Should we alter the table when deployed?
0132: */
0133: private final boolean alterTable;
0134:
0135: /**
0136: * What command should be issued directly after creation
0137: * of a table?
0138: */
0139: private final ArrayList tablePostCreateCmd;
0140:
0141: /**
0142: * Should we use 'SELECT ... FOR UPDATE' syntax when loading?
0143: */
0144: private final boolean rowLocking;
0145:
0146: /**
0147: * Is this entity read-only?
0148: */
0149: private final boolean readOnly;
0150:
0151: /**
0152: * how long is a read valid
0153: */
0154: private final int readTimeOut;
0155:
0156: /**
0157: * Should the table have a primary key constraint?
0158: */
0159: private final boolean primaryKeyConstraint;
0160:
0161: /**
0162: * the java class of the primary key
0163: */
0164: private final Class primaryKeyClass;
0165:
0166: /**
0167: * the name of the primary key field or null if the primary key field
0168: * is multivalued
0169: */
0170: private final String primaryKeyFieldName;
0171:
0172: /**
0173: * Map of the cmp fields of this entity by field name.
0174: */
0175: private final Map cmpFieldsByName = new HashMap();
0176: private final List cmpFields = new ArrayList();
0177:
0178: /**
0179: * A map of all the load groups by name.
0180: */
0181: private final Map loadGroups = new HashMap();
0182:
0183: /**
0184: * The fields which should always be loaded when an entity of this type
0185: * is loaded.
0186: */
0187: private final String eagerLoadGroup;
0188:
0189: /**
0190: * A list of groups (also lists) of the fields that should be lazy
0191: * loaded together.
0192: */
0193: private final List lazyLoadGroups = new ArrayList();
0194:
0195: /**
0196: * Map of the queries on this entity by the Method that invokes the query.
0197: */
0198: private final Map queries = new HashMap();
0199:
0200: /**
0201: * The factory used to used to create query meta data
0202: */
0203: private final JDBCQueryMetaDataFactory queryFactory;
0204:
0205: /**
0206: * The read ahead meta data
0207: */
0208: private final JDBCReadAheadMetaData readAhead;
0209:
0210: /**
0211: * clean-read-ahead-on-load
0212: * Since 3.2.5RC1, previously read ahead cache was cleaned after loading.
0213: */
0214: private final boolean cleanReadAheadOnLoad;
0215:
0216: /**
0217: * The maximum number of read ahead lists that can be tracked for this
0218: * entity.
0219: */
0220: private final int listCacheMax;
0221:
0222: /**
0223: * The number of entities to read in one round-trip to the
0224: * underlying data store.
0225: */
0226: private final int fetchSize;
0227:
0228: /**
0229: * entity command meta data
0230: */
0231: private final JDBCEntityCommandMetaData entityCommand;
0232:
0233: /**
0234: * optimistic locking metadata
0235: */
0236:
0237: private final JDBCOptimisticLockingMetaData optimisticLocking;
0238:
0239: /**
0240: * audit metadata
0241: */
0242:
0243: private final JDBCAuditMetaData audit;
0244:
0245: private final Class qlCompiler;
0246:
0247: /**
0248: * throw runtime exception metadata
0249: */
0250: private final boolean throwRuntimeExceptions;
0251:
0252: /**
0253: * Constructs jdbc entity meta data defined in the jdbcApplication and
0254: * with the data from the entity meta data which is loaded from the
0255: * ejb-jar.xml file.
0256: *
0257: * @param jdbcApplication the application in which this entity is defined
0258: * @param entity the entity meta data for this entity that is loaded
0259: * from the ejb-jar.xml file
0260: * @throws DeploymentException if an problem occures while loading the
0261: * classes or if data in the ejb-jar.xml is inconsistent with data
0262: * from jbosscmp-jdbc.xml file
0263: */
0264: public JDBCEntityMetaData(JDBCApplicationMetaData jdbcApplication,
0265: EntityMetaData entity) throws DeploymentException {
0266: this .jdbcApplication = jdbcApplication;
0267: entityName = entity.getEjbName();
0268: listCacheMax = 1000;
0269: fetchSize = 0;
0270:
0271: try {
0272: entityClass = getClassLoader().loadClass(
0273: entity.getEjbClass());
0274: } catch (ClassNotFoundException e) {
0275: throw new DeploymentException("entity class not found: "
0276: + entityName);
0277: }
0278:
0279: try {
0280: primaryKeyClass = getClassLoader().loadClass(
0281: entity.getPrimaryKeyClass());
0282: } catch (ClassNotFoundException e) {
0283: throw new DeploymentException(
0284: "could not load primary key class: "
0285: + entity.getPrimaryKeyClass());
0286: }
0287:
0288: isCMP1x = entity.isCMP1x();
0289: if (isCMP1x) {
0290: abstractSchemaName = (entity.getAbstractSchemaName() == null ? entityName
0291: : entity.getAbstractSchemaName());
0292: } else {
0293: abstractSchemaName = entity.getAbstractSchemaName();
0294: }
0295:
0296: primaryKeyFieldName = entity.getPrimKeyField();
0297:
0298: String home = entity.getHome();
0299: if (home != null) {
0300: try {
0301: homeClass = getClassLoader().loadClass(home);
0302: } catch (ClassNotFoundException e) {
0303: throw new DeploymentException("home class not found: "
0304: + home);
0305: }
0306: try {
0307: remoteClass = getClassLoader().loadClass(
0308: entity.getRemote());
0309: } catch (ClassNotFoundException e) {
0310: throw new DeploymentException(
0311: "remote class not found: " + entity.getRemote());
0312: }
0313: } else {
0314: homeClass = null;
0315: remoteClass = null;
0316: }
0317:
0318: String localHome = entity.getLocalHome();
0319: if (localHome != null) {
0320: try {
0321: localHomeClass = getClassLoader().loadClass(localHome);
0322: } catch (ClassNotFoundException e) {
0323: throw new DeploymentException(
0324: "local home class not found: " + localHome);
0325: }
0326: try {
0327: localClass = getClassLoader().loadClass(
0328: entity.getLocal());
0329: } catch (ClassNotFoundException e) {
0330: throw new DeploymentException("local class not found: "
0331: + entity.getLocal());
0332: }
0333: } else {
0334: // we must have a home or local home
0335: if (home == null) {
0336: throw new DeploymentException(
0337: "Entity must have atleast a home "
0338: + "or local home: " + entityName);
0339: }
0340:
0341: localHomeClass = null;
0342: localClass = null;
0343: }
0344:
0345: // we replace the . by _ because some dbs die on it...
0346: // the table name may be overridden in importXml(jbosscmp-jdbc.xml)
0347: tableName = entityName.replace('.', '_');
0348:
0349: // Warn: readTimeOut should be setup before cmp fields are created
0350: // otherwise readTimeOut in cmp fields will be set to 0 by default
0351: dataSourceName = null;
0352: datasourceMapping = null;
0353: datasourceMappingName = null;
0354: createTable = false;
0355: removeTable = false;
0356: alterTable = false;
0357: rowLocking = false;
0358: primaryKeyConstraint = false;
0359: readOnly = false;
0360: readTimeOut = -1;
0361: tablePostCreateCmd = null;
0362: qlCompiler = null;
0363: throwRuntimeExceptions = false;
0364:
0365: // build the metadata for the cmp fields now in case there is
0366: // no jbosscmp-jdbc.xml
0367: List nonPkFieldNames = new ArrayList();
0368: for (Iterator i = entity.getCMPFields(); i.hasNext();) {
0369: String cmpFieldName = (String) i.next();
0370: JDBCCMPFieldMetaData cmpField = new JDBCCMPFieldMetaData(
0371: this , cmpFieldName);
0372: cmpFields.add(cmpField);
0373: cmpFieldsByName.put(cmpFieldName, cmpField);
0374: if (!cmpField.isPrimaryKeyMember()) {
0375: nonPkFieldNames.add(cmpFieldName);
0376: }
0377: }
0378:
0379: // AL: add unknown primary key if primaryKeyClass is Object
0380: // AL: this is set up only in this constructor
0381: // AL: because, AFAIK, others are called with default value
0382: // AL: produced by this one
0383: if (primaryKeyClass == java.lang.Object.class) {
0384: JDBCCMPFieldMetaData upkField = new JDBCCMPFieldMetaData(
0385: this );
0386: cmpFields.add(upkField);
0387: cmpFieldsByName.put(upkField.getFieldName(), upkField);
0388: }
0389:
0390: // set eager load fields to all group.
0391: eagerLoadGroup = "*";
0392:
0393: // Create no lazy load groups. By default every thing is eager loaded.
0394: // build the metadata for the queries now in case there is no
0395: // jbosscmp-jdbc.xml
0396: queryFactory = new JDBCQueryMetaDataFactory(this );
0397:
0398: for (Iterator queriesIterator = entity.getQueries(); queriesIterator
0399: .hasNext();) {
0400: QueryMetaData queryData = (QueryMetaData) queriesIterator
0401: .next();
0402: Map newQueries = queryFactory
0403: .createJDBCQueryMetaData(queryData);
0404: // overrides defaults added above
0405: queries.putAll(newQueries);
0406: }
0407:
0408: // Create no relationship roles for this entity, will be added
0409: // by the relation meta data
0410:
0411: readAhead = JDBCReadAheadMetaData.DEFAULT;
0412: cleanReadAheadOnLoad = false;
0413: entityCommand = null;
0414: optimisticLocking = null;
0415: audit = null;
0416: }
0417:
0418: /**
0419: * Constructs entity meta data with the data contained in the entity xml
0420: * element from a jbosscmp-jdbc xml file. Optional values of the xml element
0421: * that are not present are loaded from the defalutValues parameter.
0422: *
0423: * @param jdbcApplication the application in which this entity is defined
0424: * @param element the xml Element which contains the metadata about
0425: * this entity
0426: * @param defaultValues the JDBCEntityMetaData which contains the values
0427: * for optional elements of the element
0428: * @throws DeploymentException if the xml element is not semantically correct
0429: */
0430: public JDBCEntityMetaData(JDBCApplicationMetaData jdbcApplication,
0431: Element element, JDBCEntityMetaData defaultValues)
0432: throws DeploymentException {
0433: // store passed in application... application in defaultValues may
0434: // be different because jdbcApplication is imutable
0435: this .jdbcApplication = jdbcApplication;
0436:
0437: // set default values
0438: entityName = defaultValues.getName();
0439: entityClass = defaultValues.getEntityClass();
0440: primaryKeyClass = defaultValues.getPrimaryKeyClass();
0441: isCMP1x = defaultValues.isCMP1x;
0442: primaryKeyFieldName = defaultValues.getPrimaryKeyFieldName();
0443: homeClass = defaultValues.getHomeClass();
0444: remoteClass = defaultValues.getRemoteClass();
0445: localHomeClass = defaultValues.getLocalHomeClass();
0446: localClass = defaultValues.getLocalClass();
0447: queryFactory = new JDBCQueryMetaDataFactory(this );
0448:
0449: if (isCMP1x) {
0450: abstractSchemaName = (defaultValues.getAbstractSchemaName() == null ? entityName
0451: : defaultValues.getAbstractSchemaName());
0452: } else {
0453: abstractSchemaName = defaultValues.getAbstractSchemaName();
0454: }
0455:
0456: // datasource name
0457: String dataSourceNameString = MetaData.getOptionalChildContent(
0458: element, "datasource");
0459: if (dataSourceNameString != null) {
0460: dataSourceName = dataSourceNameString;
0461: } else {
0462: dataSourceName = defaultValues.getDataSourceName();
0463: }
0464:
0465: // get the datasource mapping for this datasource (optional, but always
0466: // set in standardjbosscmp-jdbc.xml)
0467: String datasourceMappingString = MetaData
0468: .getOptionalChildContent(element, "datasource-mapping");
0469: if (datasourceMappingString != null) {
0470: datasourceMappingName = datasourceMappingString;
0471: datasourceMapping = jdbcApplication
0472: .getTypeMappingByName(datasourceMappingString);
0473: if (datasourceMapping == null) {
0474: throw new DeploymentException(
0475: "Error in jbosscmp-jdbc.xml : "
0476: + "datasource-mapping "
0477: + datasourceMappingString
0478: + " not found");
0479: }
0480: } else if (defaultValues.datasourceMappingName != null
0481: && defaultValues.datasourceMapping != null) {
0482: datasourceMappingName = null;
0483: datasourceMapping = defaultValues.datasourceMapping;
0484: } else {
0485: datasourceMappingName = null;
0486: datasourceMapping = obtainTypeMappingFromLibrary(dataSourceName);
0487: }
0488:
0489: // get table name
0490: String tableStr = MetaData.getOptionalChildContent(element,
0491: "table-name");
0492: if (tableStr != null) {
0493: tableName = tableStr;
0494: } else {
0495: tableName = defaultValues.getDefaultTableName();
0496: }
0497:
0498: // create table? If not provided, keep default.
0499: String createStr = MetaData.getOptionalChildContent(element,
0500: "create-table");
0501: if (createStr != null) {
0502: createTable = Boolean.valueOf(createStr).booleanValue();
0503: } else {
0504: createTable = defaultValues.getCreateTable();
0505: }
0506:
0507: // remove table? If not provided, keep default.
0508: String removeStr = MetaData.getOptionalChildContent(element,
0509: "remove-table");
0510: if (removeStr != null) {
0511: removeTable = Boolean.valueOf(removeStr).booleanValue();
0512: } else {
0513: removeTable = defaultValues.getRemoveTable();
0514: }
0515:
0516: // alter table? If not provided, keep default.
0517: String alterStr = MetaData.getOptionalChildContent(element,
0518: "alter-table");
0519: if (alterStr != null) {
0520: alterTable = Boolean.valueOf(alterStr).booleanValue();
0521: } else {
0522: alterTable = defaultValues.getAlterTable();
0523: }
0524:
0525: // get the SQL command to execute after table creation
0526: Element posttc = MetaData.getOptionalChild(element,
0527: "post-table-create");
0528: if (posttc != null) {
0529: Iterator it = MetaData.getChildrenByTagName(posttc,
0530: "sql-statement");
0531: tablePostCreateCmd = new ArrayList();
0532: while (it.hasNext()) {
0533: Element etmp = (Element) it.next();
0534: tablePostCreateCmd
0535: .add(MetaData.getElementContent(etmp));
0536: }
0537: } else {
0538: tablePostCreateCmd = defaultValues
0539: .getDefaultTablePostCreateCmd();
0540: }
0541:
0542: // read-only
0543: String readOnlyStr = MetaData.getOptionalChildContent(element,
0544: "read-only");
0545: if (readOnlyStr != null) {
0546: readOnly = Boolean.valueOf(readOnlyStr).booleanValue();
0547: } else {
0548: readOnly = defaultValues.isReadOnly();
0549: }
0550:
0551: // read-time-out
0552: String readTimeOutStr = MetaData.getOptionalChildContent(
0553: element, "read-time-out");
0554: if (readTimeOutStr != null) {
0555: try {
0556: readTimeOut = Integer.parseInt(readTimeOutStr);
0557: } catch (NumberFormatException e) {
0558: throw new DeploymentException(
0559: "Invalid number format in " + "read-time-out '"
0560: + readTimeOutStr + "': " + e);
0561: }
0562: } else {
0563: readTimeOut = defaultValues.getReadTimeOut();
0564: }
0565:
0566: String sForUpStr = MetaData.getOptionalChildContent(element,
0567: "row-locking");
0568: if (sForUpStr != null) {
0569: rowLocking = !isReadOnly()
0570: && (Boolean.valueOf(sForUpStr).booleanValue());
0571: } else {
0572: rowLocking = defaultValues.hasRowLocking();
0573: }
0574:
0575: // primary key constraint? If not provided, keep default.
0576: String pkStr = MetaData.getOptionalChildContent(element,
0577: "pk-constraint");
0578: if (pkStr != null) {
0579: primaryKeyConstraint = Boolean.valueOf(pkStr)
0580: .booleanValue();
0581: } else {
0582: primaryKeyConstraint = defaultValues
0583: .hasPrimaryKeyConstraint();
0584: }
0585:
0586: // list-cache-max
0587: String listCacheMaxStr = MetaData.getOptionalChildContent(
0588: element, "list-cache-max");
0589: if (listCacheMaxStr != null) {
0590: try {
0591: listCacheMax = Integer.parseInt(listCacheMaxStr);
0592: } catch (NumberFormatException e) {
0593: throw new DeploymentException(
0594: "Invalid number format in read-"
0595: + "ahead list-cache-max '"
0596: + listCacheMaxStr + "': " + e);
0597: }
0598: if (listCacheMax < 0) {
0599: throw new DeploymentException(
0600: "Negative value for read ahead "
0601: + "list-cache-max '" + listCacheMaxStr
0602: + "'.");
0603: }
0604: } else {
0605: listCacheMax = defaultValues.getListCacheMax();
0606: }
0607:
0608: // fetch-size
0609: String fetchSizeStr = MetaData.getOptionalChildContent(element,
0610: "fetch-size");
0611: if (fetchSizeStr != null) {
0612: try {
0613: fetchSize = Integer.parseInt(fetchSizeStr);
0614: } catch (NumberFormatException e) {
0615: throw new DeploymentException(
0616: "Invalid number format in " + "fetch-size '"
0617: + fetchSizeStr + "': " + e);
0618: }
0619: if (fetchSize < 0) {
0620: throw new DeploymentException(
0621: "Negative value for fetch size "
0622: + "fetch-size '" + fetchSizeStr + "'.");
0623: }
0624: } else {
0625: fetchSize = defaultValues.getFetchSize();
0626: }
0627:
0628: String compiler = MetaData.getOptionalChildContent(element,
0629: "ql-compiler");
0630: if (compiler == null) {
0631: qlCompiler = defaultValues.qlCompiler;
0632: } else {
0633: try {
0634: qlCompiler = GetTCLAction.getContextClassLoader()
0635: .loadClass(compiler);
0636: } catch (ClassNotFoundException e) {
0637: throw new DeploymentException(
0638: "Failed to load compiler implementation: "
0639: + compiler);
0640: }
0641: }
0642:
0643: // throw runtime exceptions ? If not provided, keep default.
0644: String throwRuntimeExceptionsStr = MetaData
0645: .getOptionalChildContent(element,
0646: "throw-runtime-exceptions");
0647: if (throwRuntimeExceptionsStr != null) {
0648: throwRuntimeExceptions = Boolean.valueOf(
0649: throwRuntimeExceptionsStr).booleanValue();
0650: } else {
0651: throwRuntimeExceptions = defaultValues
0652: .getThrowRuntimeExceptions();
0653: }
0654:
0655: //
0656: // cmp fields
0657: //
0658:
0659: // update all existing queries with the new read ahead value
0660: for (Iterator cmpFieldIterator = defaultValues.cmpFields
0661: .iterator(); cmpFieldIterator.hasNext();) {
0662:
0663: JDBCCMPFieldMetaData cmpField = new JDBCCMPFieldMetaData(
0664: this , (JDBCCMPFieldMetaData) cmpFieldIterator
0665: .next());
0666: cmpFields.add(cmpField);
0667: cmpFieldsByName.put(cmpField.getFieldName(), cmpField);
0668: }
0669:
0670: // apply new configurations to the cmpfields
0671: for (Iterator i = MetaData.getChildrenByTagName(element,
0672: "cmp-field"); i.hasNext();) {
0673: Element cmpFieldElement = (Element) i.next();
0674: String fieldName = MetaData.getUniqueChildContent(
0675: cmpFieldElement, "field-name");
0676:
0677: JDBCCMPFieldMetaData oldCMPField = (JDBCCMPFieldMetaData) cmpFieldsByName
0678: .get(fieldName);
0679: if (oldCMPField == null) {
0680: throw new DeploymentException(
0681: "CMP field not found : fieldName=" + fieldName);
0682: }
0683: JDBCCMPFieldMetaData cmpFieldMetaData = new JDBCCMPFieldMetaData(
0684: this , cmpFieldElement, oldCMPField);
0685:
0686: // replace the old cmp meta data with the new
0687: cmpFieldsByName.put(fieldName, cmpFieldMetaData);
0688: int index = cmpFields.indexOf(oldCMPField);
0689: cmpFields.remove(oldCMPField);
0690: cmpFields.add(index, cmpFieldMetaData);
0691: }
0692:
0693: // unknown primary key field
0694: if (primaryKeyClass == java.lang.Object.class) {
0695: Element upkElement = MetaData.getOptionalChild(element,
0696: "unknown-pk");
0697: if (upkElement != null) {
0698: // assume now there is only one upk field
0699: JDBCCMPFieldMetaData oldUpkField = null;
0700: for (Iterator iter = cmpFields.iterator(); iter
0701: .hasNext();) {
0702: JDBCCMPFieldMetaData cmpField = (JDBCCMPFieldMetaData) iter
0703: .next();
0704: if (cmpField.isUnknownPkField()) {
0705: oldUpkField = cmpField;
0706: break;
0707: }
0708: }
0709:
0710: // IMO, this is a redundant check
0711: if (oldUpkField == null) {
0712: oldUpkField = new JDBCCMPFieldMetaData(this );
0713: }
0714:
0715: JDBCCMPFieldMetaData upkField = new JDBCCMPFieldMetaData(
0716: this , upkElement, oldUpkField);
0717:
0718: // remove old upk field
0719: cmpFieldsByName.remove(oldUpkField.getFieldName());
0720: cmpFieldsByName.put(upkField.getFieldName(), upkField);
0721:
0722: int oldUpkFieldInd = cmpFields.indexOf(oldUpkField);
0723: cmpFields.remove(oldUpkField);
0724: cmpFields.add(oldUpkFieldInd, upkField);
0725: }
0726: }
0727:
0728: // load-loads
0729: loadGroups.putAll(defaultValues.loadGroups);
0730: loadLoadGroupsXml(element);
0731:
0732: // eager-load
0733: Element eagerLoadGroupElement = MetaData.getOptionalChild(
0734: element, "eager-load-group");
0735: if (eagerLoadGroupElement != null) {
0736: String eagerLoadGroupTmp = MetaData
0737: .getElementContent(eagerLoadGroupElement);
0738: if (eagerLoadGroupTmp != null
0739: && eagerLoadGroupTmp.trim().length() == 0) {
0740: eagerLoadGroupTmp = null;
0741: }
0742: if (eagerLoadGroupTmp != null
0743: && !eagerLoadGroupTmp.equals("*")
0744: && !loadGroups.containsKey(eagerLoadGroupTmp)) {
0745: throw new DeploymentException(
0746: "Eager load group not found: "
0747: + "eager-load-group="
0748: + eagerLoadGroupTmp);
0749: }
0750: eagerLoadGroup = eagerLoadGroupTmp;
0751: } else {
0752: eagerLoadGroup = defaultValues.getEagerLoadGroup();
0753: }
0754:
0755: // lazy-loads
0756: lazyLoadGroups.addAll(defaultValues.lazyLoadGroups);
0757: loadLazyLoadGroupsXml(element);
0758:
0759: // read-ahead
0760: Element readAheadElement = MetaData.getOptionalChild(element,
0761: "read-ahead");
0762: if (readAheadElement != null) {
0763: readAhead = new JDBCReadAheadMetaData(readAheadElement,
0764: defaultValues.getReadAhead());
0765: } else {
0766: readAhead = defaultValues.readAhead;
0767: }
0768:
0769: String value = MetaData.getOptionalChildContent(element,
0770: "clean-read-ahead-on-load");
0771: if ("true".equalsIgnoreCase(value)) {
0772: cleanReadAheadOnLoad = true;
0773: } else if ("false".equalsIgnoreCase(value)) {
0774: cleanReadAheadOnLoad = false;
0775: } else if (value == null) {
0776: cleanReadAheadOnLoad = defaultValues.cleanReadAheadOnLoad;
0777: } else {
0778: throw new DeploymentException(
0779: "Failed to deploy "
0780: + entityName
0781: + ": allowed values for clean-read-ahead-on-load are true and false but got "
0782: + value);
0783: }
0784:
0785: // optimistic locking group
0786: Element optimisticLockingEl = MetaData.getOptionalChild(
0787: element, "optimistic-locking");
0788: if (optimisticLockingEl != null) {
0789: optimisticLocking = new JDBCOptimisticLockingMetaData(this ,
0790: optimisticLockingEl);
0791: } else {
0792: optimisticLocking = defaultValues.getOptimisticLocking();
0793: }
0794:
0795: // audit
0796: Element auditElement = MetaData.getOptionalChild(element,
0797: "audit");
0798: if (auditElement != null) {
0799: audit = new JDBCAuditMetaData(this , auditElement);
0800: } else {
0801: audit = defaultValues.getAudit();
0802: }
0803:
0804: // queries
0805:
0806: // update all existing queries with the new read ahead value
0807: for (Iterator queriesIterator = defaultValues.queries.values()
0808: .iterator(); queriesIterator.hasNext();) {
0809: JDBCQueryMetaData query = JDBCQueryMetaDataFactory
0810: .createJDBCQueryMetaData(
0811: (JDBCQueryMetaData) queriesIterator.next(),
0812: readAhead, qlCompiler);
0813: queries.put(query.getMethod(), query);
0814: }
0815:
0816: // apply new configurations to the queries
0817: for (Iterator queriesIterator = MetaData.getChildrenByTagName(
0818: element, "query"); queriesIterator.hasNext();) {
0819: Element queryElement = (Element) queriesIterator.next();
0820: Map newQueries = queryFactory.createJDBCQueryMetaData(
0821: queryElement, defaultValues.queries, readAhead);
0822:
0823: // overrides defaults added above
0824: queries.putAll(newQueries);
0825: }
0826:
0827: // get the entity command for this entity
0828: Element entityCommandEl = MetaData.getOptionalChild(element,
0829: "entity-command");
0830: if (entityCommandEl != null) {
0831: // command name in xml
0832: String entityCommandName = entityCommandEl
0833: .getAttribute("name");
0834:
0835: // default entity command
0836: // The logic to assign the default value:
0837: // - if entity-command isn't specified in the entity element,
0838: // then it is assigned from the defaults;
0839: // - if command name in entity equals command name in defaults
0840: // then it is assigned from the defaults
0841: // - else try to find a command in entity-commands with the command
0842: // name specified in the entity.
0843: // if the match is found it'll be the default, else default is null
0844: JDBCEntityCommandMetaData defaultEntityCommand = defaultValues
0845: .getEntityCommand();
0846:
0847: if ((defaultEntityCommand == null)
0848: || (!entityCommandName.equals(defaultEntityCommand
0849: .getCommandName()))) {
0850: defaultEntityCommand = jdbcApplication
0851: .getEntityCommandByName(entityCommandName);
0852: }
0853:
0854: if (defaultEntityCommand != null) {
0855: entityCommand = new JDBCEntityCommandMetaData(
0856: entityCommandEl, defaultEntityCommand);
0857: } else {
0858: entityCommand = new JDBCEntityCommandMetaData(
0859: entityCommandEl);
0860: }
0861: } else {
0862: entityCommand = defaultValues.getEntityCommand();
0863: }
0864: }
0865:
0866: /**
0867: * Loads the load groups of cmp fields from the xml element
0868: */
0869: private void loadLoadGroupsXml(Element element)
0870: throws DeploymentException {
0871:
0872: Element loadGroupsElement = MetaData.getOptionalChild(element,
0873: "load-groups");
0874: if (loadGroupsElement == null) {
0875: // no info, default work already done in constructor
0876: return;
0877: }
0878:
0879: // only allowed for cmp 2.x
0880: if (isCMP1x) {
0881: throw new DeploymentException(
0882: "load-groups are only allowed " + "for CMP 2.x");
0883: }
0884:
0885: // load each group
0886: Iterator groups = MetaData.getChildrenByTagName(
0887: loadGroupsElement, "load-group");
0888: while (groups.hasNext()) {
0889: Element groupElement = (Element) groups.next();
0890:
0891: // get the load-group-name
0892: String loadGroupName = MetaData.getUniqueChildContent(
0893: groupElement, "load-group-name");
0894: if (loadGroups.containsKey(loadGroupName)) {
0895: throw new DeploymentException(
0896: "Load group already defined: "
0897: + " load-group-name=" + loadGroupName);
0898: }
0899: if (loadGroupName.equals("*")) {
0900: throw new DeploymentException(
0901: "The * load group is automatically "
0902: + "defined and can't be overriden");
0903: }
0904: ArrayList group = new ArrayList();
0905:
0906: // add each field
0907: Iterator fields = MetaData.getChildrenByTagName(
0908: groupElement, "field-name");
0909: while (fields.hasNext()) {
0910: String fieldName = MetaData
0911: .getElementContent((Element) fields.next());
0912:
0913: // check if the field is a cmp field that it is not a pk memeber
0914: JDBCCMPFieldMetaData field = getCMPFieldByName(fieldName);
0915: if (field != null && field.isPrimaryKeyMember()) {
0916: throw new DeploymentException(
0917: "Primary key fields can not be"
0918: + " a member of a load group: "
0919: + " load-group-name="
0920: + loadGroupName + " field-name="
0921: + fieldName);
0922: }
0923:
0924: group.add(fieldName);
0925: }
0926:
0927: loadGroups.put(loadGroupName, Collections
0928: .unmodifiableList(group));
0929: }
0930: }
0931:
0932: /**
0933: * Loads the list of lazy load groups from the xml element.
0934: */
0935: private void loadLazyLoadGroupsXml(Element element)
0936: throws DeploymentException {
0937: Element lazyLoadGroupsElement = MetaData.getOptionalChild(
0938: element, "lazy-load-groups");
0939:
0940: // If no info, we're done. Default work was already done in constructor.
0941: if (lazyLoadGroupsElement == null) {
0942: return;
0943: }
0944:
0945: // only allowed for cmp 2.x
0946: if (isCMP1x) {
0947: throw new DeploymentException(
0948: "lazy-load-groups is only allowed for CMP 2.x");
0949: }
0950:
0951: // get the fields
0952: Iterator loadGroupNames = MetaData.getChildrenByTagName(
0953: lazyLoadGroupsElement, "load-group-name");
0954: while (loadGroupNames.hasNext()) {
0955: String loadGroupName = MetaData
0956: .getElementContent((Element) loadGroupNames.next());
0957: if (!loadGroupName.equals("*")
0958: && !loadGroups.containsKey(loadGroupName)) {
0959: throw new DeploymentException(
0960: "Lazy load group not found: "
0961: + "load-group-name=" + loadGroupName);
0962: }
0963:
0964: lazyLoadGroups.add(loadGroupName);
0965: }
0966:
0967: }
0968:
0969: /**
0970: * Gets the meta data for the application of which this entity is a member.
0971: *
0972: * @return the meta data for the application that this entity is a memeber
0973: */
0974: public JDBCApplicationMetaData getJDBCApplication() {
0975: return jdbcApplication;
0976: }
0977:
0978: /**
0979: * Gets the name of the datasource in jndi for this entity
0980: *
0981: * @return the name of datasource in jndi
0982: */
0983: public String getDataSourceName() {
0984: return dataSourceName;
0985: }
0986:
0987: /**
0988: * Gets the jdbc type mapping for this entity
0989: *
0990: * @return the jdbc type mapping for this entity
0991: */
0992: public JDBCTypeMappingMetaData getTypeMapping()
0993: throws DeploymentException {
0994: if (datasourceMapping == null) {
0995: throw new DeploymentException(
0996: "type-mapping is not initialized: "
0997: + dataSourceName
0998: + " was not deployed or type-mapping was not configured.");
0999: }
1000: return datasourceMapping;
1001: }
1002:
1003: /**
1004: * Gets the name of this entity. The name come from the ejb-jar.xml file.
1005: *
1006: * @return the name of this entity
1007: */
1008: public String getName() {
1009: return entityName;
1010: }
1011:
1012: /**
1013: * Gets the abstract shcema name of this entity. The name come from
1014: * the ejb-jar.xml file.
1015: *
1016: * @return the abstract schema name of this entity
1017: */
1018: public String getAbstractSchemaName() {
1019: return abstractSchemaName;
1020: }
1021:
1022: /**
1023: * Gets the class loaded which is used to load all classes used by this
1024: * entity
1025: *
1026: * @return the class loader which is used to load all classes used by
1027: * this entity
1028: */
1029: public ClassLoader getClassLoader() {
1030: return jdbcApplication.getClassLoader();
1031: }
1032:
1033: /**
1034: * Gets the implementation class of this entity
1035: *
1036: * @return the implementation class of this entity
1037: */
1038: public Class getEntityClass() {
1039: return entityClass;
1040: }
1041:
1042: /**
1043: * Gets the home class of this entity
1044: *
1045: * @return the home class of this entity
1046: */
1047: public Class getHomeClass() {
1048: return homeClass;
1049: }
1050:
1051: /**
1052: * Gets the remote class of this entity
1053: *
1054: * @return the remote class of this entity
1055: */
1056: public Class getRemoteClass() {
1057: return remoteClass;
1058: }
1059:
1060: /**
1061: * Gets the local home class of this entity
1062: *
1063: * @return the local home class of this entity
1064: */
1065: public Class getLocalHomeClass() {
1066: return localHomeClass;
1067: }
1068:
1069: /**
1070: * Gets the local class of this entity
1071: *
1072: * @return the local class of this entity
1073: */
1074: public Class getLocalClass() {
1075: return localClass;
1076: }
1077:
1078: /**
1079: * Does this entity use CMP version 1.x
1080: *
1081: * @return true if this entity used CMP version 1.x; otherwise false
1082: */
1083: public boolean isCMP1x() {
1084: return isCMP1x;
1085: }
1086:
1087: /**
1088: * Gets the cmp fields of this entity
1089: *
1090: * @return an unmodifiable collection of JDBCCMPFieldMetaData objects
1091: */
1092: public List getCMPFields() {
1093: return Collections.unmodifiableList(cmpFields);
1094: }
1095:
1096: /**
1097: * Gets the name of the eager load group. This name can be used to
1098: * look up the load group.
1099: *
1100: * @return the name of the eager load group
1101: */
1102: public String getEagerLoadGroup() {
1103: return eagerLoadGroup;
1104: }
1105:
1106: /**
1107: * Gets the collection of lazy load group names.
1108: *
1109: * @return an unmodifiable collection of load group names
1110: */
1111: public List getLazyLoadGroups() {
1112: return Collections.unmodifiableList(lazyLoadGroups);
1113: }
1114:
1115: /**
1116: * Gets the map from load grou name to a List of field names, which
1117: * forms a logical load group.
1118: *
1119: * @return an unmodifiable map of load groups (Lists) by group name.
1120: */
1121: public Map getLoadGroups() {
1122: return Collections.unmodifiableMap(loadGroups);
1123: }
1124:
1125: /**
1126: * Gets the load group with the specified name.
1127: *
1128: * @return the load group with the specified name
1129: * @throws DeploymentException if group with the specified name is not found
1130: */
1131: public List getLoadGroup(String name) throws DeploymentException {
1132: List group = (List) loadGroups.get(name);
1133: if (group == null) {
1134: throw new DeploymentException("Unknown load group: name="
1135: + name);
1136: }
1137: return group;
1138: }
1139:
1140: /**
1141: * Returns optimistic locking metadata
1142: */
1143: public JDBCOptimisticLockingMetaData getOptimisticLocking() {
1144: return optimisticLocking;
1145: }
1146:
1147: /**
1148: * Returns audit metadata
1149: */
1150: public JDBCAuditMetaData getAudit() {
1151: return audit;
1152: }
1153:
1154: /**
1155: * Gets the cmp field with the specified name
1156: *
1157: * @param name the name of the desired field
1158: * @return the cmp field with the specified name or null if not found
1159: */
1160: public JDBCCMPFieldMetaData getCMPFieldByName(String name) {
1161: return (JDBCCMPFieldMetaData) cmpFieldsByName.get(name);
1162: }
1163:
1164: /**
1165: * Gets the name of the table to which this entity is persisted
1166: *
1167: * @return the name of the table to which this entity is persisted
1168: */
1169: public String getDefaultTableName() {
1170: return tableName;
1171: }
1172:
1173: /**
1174: * Gets the flag used to determine if the store manager should attempt to
1175: * create database table when the entity is deployed.
1176: *
1177: * @return true if the store manager should attempt to create the table
1178: */
1179: public boolean getCreateTable() {
1180: return createTable;
1181: }
1182:
1183: /**
1184: * Gets the flag used to determine if the store manager should attempt to
1185: * remove database table when the entity is undeployed.
1186: *
1187: * @return true if the store manager should attempt to remove the table
1188: */
1189: public boolean getRemoveTable() {
1190: return removeTable;
1191: }
1192:
1193: /**
1194: * Gets the flag used to determine if the store manager should attempt to
1195: * alter table when the entity is deployed.
1196: */
1197: public boolean getAlterTable() {
1198: return alterTable;
1199: }
1200:
1201: /**
1202: * Get the (user-defined) SQL commands that sould be issued after table
1203: * creation
1204: *
1205: * @return the SQL command to issue to the DB-server
1206: */
1207: public ArrayList getDefaultTablePostCreateCmd() {
1208: return tablePostCreateCmd;
1209: }
1210:
1211: /**
1212: * Gets the flag used to determine if the store manager should add a
1213: * priary key constraint when creating the table
1214: *
1215: * @return true if the store manager should add a primary key constraint to
1216: * the create table sql statement
1217: */
1218: public boolean hasPrimaryKeyConstraint() {
1219: return primaryKeyConstraint;
1220: }
1221:
1222: /**
1223: * Gets the flag used to determine if the store manager should do row locking
1224: * when loading entity beans
1225: *
1226: * @return true if the store manager should add a row locking
1227: * clause when selecting data from the table
1228: */
1229: public boolean hasRowLocking() {
1230: return rowLocking;
1231: }
1232:
1233: /**
1234: * The maximum number of qurey result lists that will be tracked.
1235: */
1236: public int getListCacheMax() {
1237: return listCacheMax;
1238: }
1239:
1240: /**
1241: * The number of rows that the database driver should get in a single
1242: * trip to the database.
1243: */
1244: public int getFetchSize() {
1245: return fetchSize;
1246: }
1247:
1248: /**
1249: * Gets the queries defined on this entity
1250: *
1251: * @return an unmodifiable collection of JDBCQueryMetaData objects
1252: */
1253: public Collection getQueries() {
1254: return Collections.unmodifiableCollection(queries.values());
1255: }
1256:
1257: /**
1258: * @param method finder method name.
1259: * @return corresponding query metadata or null.
1260: */
1261: public JDBCQueryMetaData getQueryMetaDataForMethod(Method method) {
1262: return (JDBCQueryMetaData) queries.get(method);
1263: }
1264:
1265: /**
1266: * Get the relationsip roles of this entity.
1267: * Items are instance of JDBCRelationshipRoleMetaData.
1268: *
1269: * @return an unmodifiable collection of the relationship roles defined
1270: * for this entity
1271: */
1272: public Collection getRelationshipRoles() {
1273: return jdbcApplication.getRolesForEntity(entityName);
1274: }
1275:
1276: /**
1277: * Gets the primary key class for this entity
1278: *
1279: * @return the primary key class for this entity
1280: */
1281: public Class getPrimaryKeyClass() {
1282: return primaryKeyClass;
1283: }
1284:
1285: /**
1286: * Gets the entity command metadata
1287: *
1288: * @return the entity command metadata
1289: */
1290: public JDBCEntityCommandMetaData getEntityCommand() {
1291: return entityCommand;
1292: }
1293:
1294: /**
1295: * Is this entity read only? A readonly entity will never be stored into
1296: * the database.
1297: *
1298: * @return true if this entity is read only
1299: */
1300: public boolean isReadOnly() {
1301: return readOnly;
1302: }
1303:
1304: /**
1305: * How long is a read of this entity valid. This property should only be
1306: * used on read only entities, and determines how long the data read from
1307: * the database is valid. When the read times out it should be reread from
1308: * the database. If the value is -1 and the entity is not using commit
1309: * option a, the read is only valid for the length of the transaction in
1310: * which it was loaded.
1311: *
1312: * @return the length of time that a read is valid or -1 if the read is only
1313: * valid for the length of the transaction
1314: */
1315: public int getReadTimeOut() {
1316: return readTimeOut;
1317: }
1318:
1319: /**
1320: * Gets the name of the primary key field of this entity or null if
1321: * the primary key is multivalued
1322: *
1323: * @return the name of the primary key field of this entity or null
1324: * if the primary key is multivalued
1325: */
1326: public String getPrimaryKeyFieldName() {
1327: return primaryKeyFieldName;
1328: }
1329:
1330: /**
1331: * Gets the read ahead meta data for this entity.
1332: *
1333: * @return the read ahead meta data for this entity.
1334: */
1335: public JDBCReadAheadMetaData getReadAhead() {
1336: return readAhead;
1337: }
1338:
1339: public Class getQLCompiler() {
1340: return qlCompiler;
1341: }
1342:
1343: /**
1344: * Is the throw-runtime-exceptions meta data for this entity is true.
1345: *
1346: * @return the throw-runtime-exceptions meta data for this entity.
1347: */
1348: public boolean isThrowRuntimeExceptions() {
1349: return throwRuntimeExceptions;
1350: }
1351:
1352: /**
1353: * Gets the throw-runtime-exceptions meta data for this entity.
1354: *
1355: * @return the throw-runtime-exceptions meta data for this entity.
1356: */
1357: public boolean getThrowRuntimeExceptions() {
1358: return throwRuntimeExceptions;
1359: }
1360:
1361: public boolean isCleanReadAheadOnLoad() {
1362: return cleanReadAheadOnLoad;
1363: }
1364:
1365: public static JDBCTypeMappingMetaData obtainTypeMappingFromLibrary(
1366: String dataSourceName) throws DeploymentException {
1367: JDBCTypeMappingMetaData typeMapping = null;
1368:
1369: String datasource;
1370: if (dataSourceName.startsWith("java:")) {
1371: datasource = dataSourceName.substring("java:".length());
1372: if (datasource.startsWith("/")) {
1373: datasource = datasource.substring(1);
1374: }
1375: } else {
1376: datasource = dataSourceName;
1377: }
1378:
1379: final ObjectName metadataService;
1380: final String str = "jboss.jdbc:service=metadata,datasource="
1381: + datasource;
1382: try {
1383: metadataService = new ObjectName(str);
1384: } catch (MalformedObjectNameException e) {
1385: throw new DeploymentException(
1386: "Failed to create ObjectName for datasource metadata MBean: "
1387: + str, e);
1388: }
1389:
1390: try {
1391: final MBeanServer server = MBeanServerLocator.locateJBoss();
1392: if (server.isRegistered(metadataService)) {
1393: typeMapping = (JDBCTypeMappingMetaData) server
1394: .getAttribute(metadataService,
1395: "TypeMappingMetaData");
1396: }
1397: } catch (Exception e) {
1398: throw new DeploymentException(
1399: "Failed to obtain type-mapping metadata from the metadata library MBean: "
1400: + e.getMessage(), e);
1401: }
1402:
1403: /*
1404: if(typeMapping == null)
1405: {
1406: throw new DeploymentException(
1407: "type-mapping for datasource " + datasource + " not found in the library. " +
1408: "Check the value of metadata/type-mapping in the -ds.xml file."
1409: );
1410: }
1411: */
1412:
1413: return typeMapping;
1414: }
1415:
1416: /**
1417: * Compares this JDBCEntityMetaData against the specified object. Returns
1418: * true if the objects are the same. Two JDBCEntityMetaData are the same
1419: * if they both have the same name and are defined in the same application.
1420: *
1421: * @param o the reference object with which to compare
1422: * @return true if this object is the same as the object argument;
1423: * false otherwise
1424: */
1425: public boolean equals(Object o) {
1426: if (o instanceof JDBCEntityMetaData) {
1427: JDBCEntityMetaData entity = (JDBCEntityMetaData) o;
1428: return entityName.equals(entity.entityName)
1429: && jdbcApplication.equals(entity.jdbcApplication);
1430: }
1431: return false;
1432: }
1433:
1434: /**
1435: * Returns a hashcode for this JDBCEntityMetaData. The hashcode is computed
1436: * based on the hashCode of the declaring application and the hashCode of
1437: * the entityName
1438: *
1439: * @return a hash code value for this object
1440: */
1441: public int hashCode() {
1442: int result = 17;
1443: result = 37 * result + jdbcApplication.hashCode();
1444: result = 37 * result + entityName.hashCode();
1445: return result;
1446: }
1447:
1448: /**
1449: * Returns a string describing this JDBCEntityMetaData. The exact details
1450: * of the representation are unspecified and subject to change, but the
1451: * following may be regarded as typical:
1452: * <p/>
1453: * "[JDBCEntityMetaData: entityName=UserEJB]"
1454: *
1455: * @return a string representation of the object
1456: */
1457: public String toString() {
1458: return "[JDBCEntityMetaData : entityName=" + entityName + "]";
1459: }
1460: }
|