/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb.plugins.cmp.jdbc.metadata;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Method;
import org.jboss.deployment.DeploymentException;
import org.jboss.metadata.EntityMetaData;
import org.jboss.metadata.MetaData;
import org.jboss.metadata.QueryMetaData;
import org.jboss.mx.util.MBeanServerLocator;
import org.w3c.dom.Element;
import javax.management.ObjectName;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanServer;
/**
* This immutable class contains information about an entity
*
* @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
* @author <a href="sebastien.alborini@m4x.org">Sebastien Alborini</a>
* @author <a href="mailto:dirk@jboss.de">Dirk Zimmermann</a>
* @author <a href="mailto:loubyansky@hotmail.com">Alex Loubyansky</a>
* @author <a href="mailto:heiko.rupp@cellent.de">Heiko W. Rupp</a>
* @version $Revision: 81030 $
*/
public final class JDBCEntityMetaData
{
/**
* application metadata in which this entity is defined
*/
private final JDBCApplicationMetaData jdbcApplication;
/**
* data source name in jndi
*/
private final String dataSourceName;
/**
* type mapping name as specified in the deployment descriptor
*/
private final String datasourceMappingName;
/**
* type mapping used for this entity
*/
private final JDBCTypeMappingMetaData datasourceMapping;
/**
* the name of this entity
*/
private final String entityName;
/**
* the abstract schema name of this entity
*/
private final String abstractSchemaName;
/**
* the implementation class of this entity
*/
private final Class entityClass;
/**
* the home class of this entity
*/
private final Class homeClass;
/**
* the remote class of this entity
*/
private final Class remoteClass;
/**
* the local home class of this entity
*/
private final Class localHomeClass;
/**
* the local class of this entity
*/
private final Class localClass;
/**
* Does this entity use cmp 1.x?
*/
private final boolean isCMP1x;
/**
* the name of the table to which this entity is persisted
*/
private final String tableName;
/**
* Should we try and create the table when deployed?
*/
private final boolean createTable;
/**
* Should we drop the table when undeployed?
*/
private final boolean removeTable;
/**
* Should we alter the table when deployed?
*/
private final boolean alterTable;
/**
* What command should be issued directly after creation
* of a table?
*/
private final ArrayList tablePostCreateCmd;
/**
* Should we use 'SELECT ... FOR UPDATE' syntax when loading?
*/
private final boolean rowLocking;
/**
* Is this entity read-only?
*/
private final boolean readOnly;
/**
* how long is a read valid
*/
private final int readTimeOut;
/**
* Should the table have a primary key constraint?
*/
private final boolean primaryKeyConstraint;
/**
* the java class of the primary key
*/
private final Class primaryKeyClass;
/**
* the name of the primary key field or null if the primary key field
* is multivalued
*/
private final String primaryKeyFieldName;
/**
* Map of the cmp fields of this entity by field name.
*/
private final Map cmpFieldsByName = new HashMap();
private final List cmpFields = new ArrayList();
/**
* A map of all the load groups by name.
*/
private final Map loadGroups = new HashMap();
/**
* The fields which should always be loaded when an entity of this type
* is loaded.
*/
private final String eagerLoadGroup;
/**
* A list of groups (also lists) of the fields that should be lazy
* loaded together.
*/
private final List lazyLoadGroups = new ArrayList();
/**
* Map of the queries on this entity by the Method that invokes the query.
*/
private final Map queries = new HashMap();
/**
* The factory used to used to create query meta data
*/
private final JDBCQueryMetaDataFactory queryFactory;
/**
* The read ahead meta data
*/
private final JDBCReadAheadMetaData readAhead;
/**
* clean-read-ahead-on-load
* Since 3.2.5RC1, previously read ahead cache was cleaned after loading.
*/
private final boolean cleanReadAheadOnLoad;
/**
* The maximum number of read ahead lists that can be tracked for this
* entity.
*/
private final int listCacheMax;
/**
* The number of entities to read in one round-trip to the
* underlying data store.
*/
private final int fetchSize;
/**
* entity command meta data
*/
private final JDBCEntityCommandMetaData entityCommand;
/**
* optimistic locking metadata
*/
private final JDBCOptimisticLockingMetaData optimisticLocking;
/**
* audit metadata
*/
private final JDBCAuditMetaData audit;
private final Class qlCompiler;
/**
* throw runtime exception metadata
*/
private final boolean throwRuntimeExceptions;
/**
* Constructs jdbc entity meta data defined in the jdbcApplication and
* with the data from the entity meta data which is loaded from the
* ejb-jar.xml file.
*
* @param jdbcApplication the application in which this entity is defined
* @param entity the entity meta data for this entity that is loaded
* from the ejb-jar.xml file
* @throws DeploymentException if an problem occures while loading the
* classes or if data in the ejb-jar.xml is inconsistent with data
* from jbosscmp-jdbc.xml file
*/
public JDBCEntityMetaData(JDBCApplicationMetaData jdbcApplication,
EntityMetaData entity)
throws DeploymentException
{
this.jdbcApplication = jdbcApplication;
entityName = entity.getEjbName();
listCacheMax = 1000;
fetchSize = 0;
try
{
entityClass = getClassLoader().loadClass(entity.getEjbClass());
}
catch(ClassNotFoundException e)
{
throw new DeploymentException("entity class not found for ejb-name: " + entityName, e);
}
try
{
primaryKeyClass = getClassLoader().loadClass(entity.getPrimaryKeyClass());
}
catch(ClassNotFoundException e)
{
throw new DeploymentException(
"could not load primary key class: " +
entity.getPrimaryKeyClass()
);
}
isCMP1x = entity.isCMP1x();
if(isCMP1x)
{
abstractSchemaName = (entity.getAbstractSchemaName() == null ? entityName : entity.getAbstractSchemaName());
}
else
{
abstractSchemaName = entity.getAbstractSchemaName();
}
primaryKeyFieldName = entity.getPrimKeyField();
String home = entity.getHome();
if(home != null)
{
try
{
homeClass = getClassLoader().loadClass(home);
}
catch(ClassNotFoundException e)
{
throw new DeploymentException("home class not found: " + home);
}
try
{
remoteClass = getClassLoader().loadClass(entity.getRemote());
}
catch(ClassNotFoundException e)
{
throw new DeploymentException(
"remote class not found: " +
entity.getRemote()
);
}
}
else
{
homeClass = null;
remoteClass = null;
}
String localHome = entity.getLocalHome();
if(localHome != null)
{
try
{
localHomeClass = getClassLoader().loadClass(localHome);
}
catch(ClassNotFoundException e)
{
throw new DeploymentException(
"local home class not found: " +
localHome
);
}
try
{
localClass = getClassLoader().loadClass(entity.getLocal());
}
catch(ClassNotFoundException e)
{
throw new DeploymentException(
"local class not found: " +
entity.getLocal()
);
}
}
else
{
// we must have a home or local home
if(home == null)
{
throw new DeploymentException(
"Entity must have atleast a home " +
"or local home: " + entityName
);
}
localHomeClass = null;
localClass = null;
}
// we replace the . by _ because some dbs die on it...
// the table name may be overridden in importXml(jbosscmp-jdbc.xml)
tableName = entityName.replace('.', '_');
// Warn: readTimeOut should be setup before cmp fields are created
// otherwise readTimeOut in cmp fields will be set to 0 by default
dataSourceName = null;
datasourceMappingName = null;
datasourceMapping = null;
createTable = false;
removeTable = false;
alterTable = false;
rowLocking = false;
primaryKeyConstraint = false;
readOnly = false;
readTimeOut = -1;
tablePostCreateCmd = null;
qlCompiler = null;
throwRuntimeExceptions = false;
// build the metadata for the cmp fields now in case there is
// no jbosscmp-jdbc.xml
List nonPkFieldNames = new ArrayList();
for(Iterator i = entity.getCMPFields(); i.hasNext();)
{
String cmpFieldName = (String) i.next();
JDBCCMPFieldMetaData cmpField = new JDBCCMPFieldMetaData(this, cmpFieldName);
cmpFields.add(cmpField);
cmpFieldsByName.put(cmpFieldName, cmpField);
if(!cmpField.isPrimaryKeyMember())
{
nonPkFieldNames.add(cmpFieldName);
}
}
// AL: add unknown primary key if primaryKeyClass is Object
// AL: this is set up only in this constructor
// AL: because, AFAIK, others are called with default value
// AL: produced by this one
if(primaryKeyClass == java.lang.Object.class)
{
JDBCCMPFieldMetaData upkField = new JDBCCMPFieldMetaData(this);
cmpFields.add(upkField);
cmpFieldsByName.put(upkField.getFieldName(), upkField);
}
// set eager load fields to all group.
eagerLoadGroup = "*";
// Create no lazy load groups. By default every thing is eager loaded.
// build the metadata for the queries now in case there is no
// jbosscmp-jdbc.xml
queryFactory = new JDBCQueryMetaDataFactory(this);
for(Iterator queriesIterator = entity.getQueries(); queriesIterator.hasNext();)
{
QueryMetaData queryData = (QueryMetaData) queriesIterator.next();
Map newQueries = queryFactory.createJDBCQueryMetaData(queryData);
// overrides defaults added above
queries.putAll(newQueries);
}
// Create no relationship roles for this entity, will be added
// by the relation meta data
readAhead = JDBCReadAheadMetaData.DEFAULT;
cleanReadAheadOnLoad = false;
entityCommand = null;
optimisticLocking = null;
audit = null;
}
/**
* Constructs entity meta data with the data contained in the entity xml
* element from a jbosscmp-jdbc xml file. Optional values of the xml element
* that are not present are loaded from the defalutValues parameter.
*
* @param jdbcApplication the application in which this entity is defined
* @param element the xml Element which contains the metadata about
* this entity
* @param defaultValues the JDBCEntityMetaData which contains the values
* for optional elements of the element
* @throws DeploymentException if the xml element is not semantically correct
*/
public JDBCEntityMetaData(JDBCApplicationMetaData jdbcApplication,
Element element,
JDBCEntityMetaData defaultValues)
throws DeploymentException
{
// store passed in application... application in defaultValues may
// be different because jdbcApplication is imutable
this.jdbcApplication = jdbcApplication;
// set default values
entityName = defaultValues.getName();
entityClass = defaultValues.getEntityClass();
primaryKeyClass = defaultValues.getPrimaryKeyClass();
isCMP1x = defaultValues.isCMP1x;
primaryKeyFieldName = defaultValues.getPrimaryKeyFieldName();
homeClass = defaultValues.getHomeClass();
remoteClass = defaultValues.getRemoteClass();
localHomeClass = defaultValues.getLocalHomeClass();
localClass = defaultValues.getLocalClass();
queryFactory = new JDBCQueryMetaDataFactory(this);
if(isCMP1x)
{
abstractSchemaName = (defaultValues.getAbstractSchemaName() == null ?
entityName : defaultValues.getAbstractSchemaName());
}
else
{
abstractSchemaName = defaultValues.getAbstractSchemaName();
}
// datasource name
String dataSourceNameString = MetaData.getOptionalChildContent(element, "datasource");
if(dataSourceNameString != null)
{
dataSourceName = dataSourceNameString;
}
else
{
dataSourceName = defaultValues.getDataSourceName();
}
// get the datasource mapping for this datasource (optional, but always
// set in standardjbosscmp-jdbc.xml)
String datasourceMappingString = MetaData.getOptionalChildContent(element, "datasource-mapping");
if(datasourceMappingString != null)
{
datasourceMappingName = datasourceMappingString;
datasourceMapping = jdbcApplication.getTypeMappingByName(datasourceMappingString);
if(datasourceMapping == null)
{
throw new DeploymentException(
"Error in jbosscmp-jdbc.xml : "
+
"datasource-mapping "
+ datasourceMappingString +
" not found"
);
}
}
else if(defaultValues.datasourceMappingName != null && defaultValues.datasourceMapping != null)
{
datasourceMappingName = null;
datasourceMapping = defaultValues.datasourceMapping;
}
else
{
datasourceMappingName = null;
datasourceMapping = obtainTypeMappingFromLibrary(dataSourceName);
}
// get table name
String tableStr = MetaData.getOptionalChildContent(element, "table-name");
if(tableStr != null)
{
tableName = tableStr;
}
else
{
tableName = defaultValues.getDefaultTableName();
}
// create table? If not provided, keep default.
String createStr = MetaData.getOptionalChildContent(element, "create-table");
if(createStr != null)
{
createTable = Boolean.valueOf(createStr).booleanValue();
}
else
{
createTable = defaultValues.getCreateTable();
}
// remove table? If not provided, keep default.
String removeStr = MetaData.getOptionalChildContent(element, "remove-table");
if(removeStr != null)
{
removeTable = Boolean.valueOf(removeStr).booleanValue();
}
else
{
removeTable = defaultValues.getRemoveTable();
}
// alter table? If not provided, keep default.
String alterStr = MetaData.getOptionalChildContent(element, "alter-table");
if(alterStr != null)
{
alterTable = Boolean.valueOf(alterStr).booleanValue();
}
else
{
alterTable = defaultValues.getAlterTable();
}
// get the SQL command to execute after table creation
Element posttc = MetaData.getOptionalChild(element, "post-table-create");
if(posttc != null)
{
Iterator it = MetaData.getChildrenByTagName(posttc, "sql-statement");
tablePostCreateCmd = new ArrayList();
while(it.hasNext())
{
Element etmp = (Element) it.next();
tablePostCreateCmd.add(MetaData.getElementContent(etmp));
}
}
else
{
tablePostCreateCmd = defaultValues.getDefaultTablePostCreateCmd();
}
// read-only
String readOnlyStr = MetaData.getOptionalChildContent(element, "read-only");
if(readOnlyStr != null)
{
readOnly = Boolean.valueOf(readOnlyStr).booleanValue();
}
else
{
readOnly = defaultValues.isReadOnly();
}
// read-time-out
String readTimeOutStr = MetaData.getOptionalChildContent(element, "read-time-out");
if(readTimeOutStr != null)
{
try
{
readTimeOut = Integer.parseInt(readTimeOutStr);
}
catch(NumberFormatException e)
{
throw new DeploymentException(
"Invalid number format in " +
"read-time-out '" + readTimeOutStr + "': " + e
);
}
}
else
{
readTimeOut = defaultValues.getReadTimeOut();
}
String sForUpStr = MetaData.getOptionalChildContent(element, "row-locking");
if(sForUpStr != null)
{
rowLocking = !isReadOnly() && (Boolean.valueOf(sForUpStr).booleanValue());
}
else
{
rowLocking = defaultValues.hasRowLocking();
}
// primary key constraint? If not provided, keep default.
String pkStr = MetaData.getOptionalChildContent(element, "pk-constraint");
if(pkStr != null)
{
primaryKeyConstraint = Boolean.valueOf(pkStr).booleanValue();
}
else
{
primaryKeyConstraint = defaultValues.hasPrimaryKeyConstraint();
}
// list-cache-max
String listCacheMaxStr = MetaData.getOptionalChildContent(element, "list-cache-max");
if(listCacheMaxStr != null)
{
try
{
listCacheMax = Integer.parseInt(listCacheMaxStr);
}
catch(NumberFormatException e)
{
throw new DeploymentException(
"Invalid number format in read-" +
"ahead list-cache-max '" + listCacheMaxStr + "': " + e
);
}
if(listCacheMax < 0)
{
throw new DeploymentException(
"Negative value for read ahead " +
"list-cache-max '" + listCacheMaxStr + "'."
);
}
}
else
{
listCacheMax = defaultValues.getListCacheMax();
}
// fetch-size
String fetchSizeStr = MetaData.getOptionalChildContent(element, "fetch-size");
if(fetchSizeStr != null)
{
try
{
fetchSize = Integer.parseInt(fetchSizeStr);
}
catch(NumberFormatException e)
{
throw new DeploymentException(
"Invalid number format in " +
"fetch-size '" + fetchSizeStr + "': " + e
);
}
if(fetchSize < 0)
{
throw new DeploymentException(
"Negative value for fetch size " +
"fetch-size '" + fetchSizeStr + "'."
);
}
}
else
{
fetchSize = defaultValues.getFetchSize();
}
String compiler = MetaData.getOptionalChildContent(element, "ql-compiler");
if(compiler == null)
{
qlCompiler = defaultValues.qlCompiler;
}
else
{
try
{
qlCompiler = GetTCLAction.getContextClassLoader().loadClass(compiler);
}
catch(ClassNotFoundException e)
{
throw new DeploymentException("Failed to load compiler implementation: " + compiler);
}
}
// throw runtime exceptions ? If not provided, keep default.
String throwRuntimeExceptionsStr = MetaData.getOptionalChildContent(element, "throw-runtime-exceptions");
if(throwRuntimeExceptionsStr != null)
{
throwRuntimeExceptions = Boolean.valueOf(throwRuntimeExceptionsStr).booleanValue();
}
else
{
throwRuntimeExceptions = defaultValues.getThrowRuntimeExceptions();
}
//
// cmp fields
//
// update all existing queries with the new read ahead value
for(Iterator cmpFieldIterator = defaultValues.cmpFields.iterator();
cmpFieldIterator.hasNext();)
{
JDBCCMPFieldMetaData cmpField = new JDBCCMPFieldMetaData(this, (JDBCCMPFieldMetaData) cmpFieldIterator.next());
cmpFields.add(cmpField);
cmpFieldsByName.put(cmpField.getFieldName(), cmpField);
}
// apply new configurations to the cmpfields
for(Iterator i = MetaData.getChildrenByTagName(element, "cmp-field"); i.hasNext();)
{
Element cmpFieldElement = (Element) i.next();
String fieldName = MetaData.getUniqueChildContent(cmpFieldElement, "field-name");
JDBCCMPFieldMetaData oldCMPField = (JDBCCMPFieldMetaData) cmpFieldsByName.get(fieldName);
if(oldCMPField == null)
{
throw new DeploymentException("CMP field not found : fieldName=" + fieldName);
}
JDBCCMPFieldMetaData cmpFieldMetaData = new JDBCCMPFieldMetaData(
this,
cmpFieldElement,
oldCMPField
);
// replace the old cmp meta data with the new
cmpFieldsByName.put(fieldName, cmpFieldMetaData);
int index = cmpFields.indexOf(oldCMPField);
cmpFields.remove(oldCMPField);
cmpFields.add(index, cmpFieldMetaData);
}
// unknown primary key field
if(primaryKeyClass == java.lang.Object.class)
{
Element upkElement = MetaData.getOptionalChild(element, "unknown-pk");
if(upkElement != null)
{
// assume now there is only one upk field
JDBCCMPFieldMetaData oldUpkField = null;
for(Iterator iter = cmpFields.iterator(); iter.hasNext();)
{
JDBCCMPFieldMetaData cmpField = (JDBCCMPFieldMetaData) iter.next();
if(cmpField.isUnknownPkField())
{
oldUpkField = cmpField;
break;
}
}
// IMO, this is a redundant check
if(oldUpkField == null)
{
oldUpkField = new JDBCCMPFieldMetaData(this);
}
JDBCCMPFieldMetaData upkField = new JDBCCMPFieldMetaData(this, upkElement, oldUpkField);
// remove old upk field
cmpFieldsByName.remove(oldUpkField.getFieldName());
cmpFieldsByName.put(upkField.getFieldName(), upkField);
int oldUpkFieldInd = cmpFields.indexOf(oldUpkField);
cmpFields.remove(oldUpkField);
cmpFields.add(oldUpkFieldInd, upkField);
}
}
// load-loads
loadGroups.putAll(defaultValues.loadGroups);
loadLoadGroupsXml(element);
// eager-load
Element eagerLoadGroupElement = MetaData.getOptionalChild(element, "eager-load-group");
if(eagerLoadGroupElement != null)
{
String eagerLoadGroupTmp = MetaData.getElementContent(eagerLoadGroupElement);
if(eagerLoadGroupTmp != null && eagerLoadGroupTmp.trim().length() == 0)
{
eagerLoadGroupTmp = null;
}
if(eagerLoadGroupTmp != null
&& !eagerLoadGroupTmp.equals("*")
&& !loadGroups.containsKey(eagerLoadGroupTmp))
{
throw new DeploymentException(
"Eager load group not found: " +
"eager-load-group=" + eagerLoadGroupTmp
);
}
eagerLoadGroup = eagerLoadGroupTmp;
}
else
{
eagerLoadGroup = defaultValues.getEagerLoadGroup();
}
// lazy-loads
lazyLoadGroups.addAll(defaultValues.lazyLoadGroups);
loadLazyLoadGroupsXml(element);
// read-ahead
Element readAheadElement = MetaData.getOptionalChild(element, "read-ahead");
if(readAheadElement != null)
{
readAhead = new JDBCReadAheadMetaData(readAheadElement, defaultValues.getReadAhead());
}
else
{
readAhead = defaultValues.readAhead;
}
String value = MetaData.getOptionalChildContent(element, "clean-read-ahead-on-load");
if("true".equalsIgnoreCase(value))
{
cleanReadAheadOnLoad = true;
}
else if("false".equalsIgnoreCase(value))
{
cleanReadAheadOnLoad = false;
}
else if(value == null)
{
cleanReadAheadOnLoad = defaultValues.cleanReadAheadOnLoad;
}
else
{
throw new DeploymentException("Failed to deploy " + entityName +
": allowed values for clean-read-ahead-on-load are true and false but got " + value);
}
// optimistic locking group
Element optimisticLockingEl = MetaData.getOptionalChild(element, "optimistic-locking");
if(optimisticLockingEl != null)
{
optimisticLocking = new JDBCOptimisticLockingMetaData(this, optimisticLockingEl);
}
else
{
optimisticLocking = defaultValues.getOptimisticLocking();
}
// audit
Element auditElement = MetaData.getOptionalChild(element, "audit");
if(auditElement != null)
{
audit = new JDBCAuditMetaData(this, auditElement);
}
else
{
audit = defaultValues.getAudit();
}
// queries
// update all existing queries with the new read ahead value
for(Iterator queriesIterator = defaultValues.queries.values().iterator();
queriesIterator.hasNext();)
{
JDBCQueryMetaData query = JDBCQueryMetaDataFactory.createJDBCQueryMetaData(
(JDBCQueryMetaData) queriesIterator.next(),
readAhead, qlCompiler
);
queries.put(query.getMethod(), query);
}
// apply new configurations to the queries
for(Iterator queriesIterator = MetaData.getChildrenByTagName(element, "query");
queriesIterator.hasNext();)
{
Element queryElement = (Element) queriesIterator.next();
Map newQueries = queryFactory.createJDBCQueryMetaData(
queryElement,
defaultValues.queries,
readAhead
);
// overrides defaults added above
queries.putAll(newQueries);
}
// get the entity command for this entity
Element entityCommandEl = MetaData.getOptionalChild(element, "entity-command");
if(entityCommandEl != null)
{
// command name in xml
String entityCommandName = entityCommandEl.getAttribute("name");
// default entity command
// The logic to assign the default value:
// - if entity-command isn't specified in the entity element,
// then it is assigned from the defaults;
// - if command name in entity equals command name in defaults
// then it is assigned from the defaults
// - else try to find a command in entity-commands with the command
// name specified in the entity.
// if the match is found it'll be the default, else default is null
JDBCEntityCommandMetaData defaultEntityCommand = defaultValues.getEntityCommand();
if((defaultEntityCommand == null)
|| (!entityCommandName.equals(defaultEntityCommand.getCommandName())))
{
defaultEntityCommand = jdbcApplication.getEntityCommandByName(entityCommandName);
}
if(defaultEntityCommand != null)
{
entityCommand = new JDBCEntityCommandMetaData(entityCommandEl, defaultEntityCommand);
}
else
{
entityCommand = new JDBCEntityCommandMetaData(entityCommandEl);
}
}
else
{
entityCommand = defaultValues.getEntityCommand();
}
}
/**
* Loads the load groups of cmp fields from the xml element
*/
private void loadLoadGroupsXml(Element element)
throws DeploymentException
{
Element loadGroupsElement = MetaData.getOptionalChild(element, "load-groups");
if(loadGroupsElement == null)
{
// no info, default work already done in constructor
return;
}
// only allowed for cmp 2.x
if(isCMP1x)
{
throw new DeploymentException(
"load-groups are only allowed " +
"for CMP 2.x"
);
}
// load each group
Iterator groups = MetaData.getChildrenByTagName(loadGroupsElement, "load-group");
while(groups.hasNext())
{
Element groupElement = (Element) groups.next();
// get the load-group-name
String loadGroupName = MetaData.getUniqueChildContent(groupElement, "load-group-name");
if(loadGroups.containsKey(loadGroupName))
{
throw new DeploymentException(
"Load group already defined: " +
" load-group-name=" + loadGroupName
);
}
if(loadGroupName.equals("*"))
{
throw new DeploymentException(
"The * load group is automatically " +
"defined and can't be overriden"
);
}
ArrayList group = new ArrayList();
// add each field
Iterator fields = MetaData.getChildrenByTagName(groupElement, "field-name");
while(fields.hasNext())
{
String fieldName = MetaData.getElementContent((Element) fields.next());
// check if the field is a cmp field that it is not a pk memeber
JDBCCMPFieldMetaData field = getCMPFieldByName(fieldName);
if(field != null && field.isPrimaryKeyMember())
{
throw new DeploymentException(
"Primary key fields can not be"
+
" a member of a load group: "
+
" load-group-name="
+ loadGroupName +
" field-name=" + fieldName
);
}
group.add(fieldName);
}
loadGroups.put(loadGroupName, Collections.unmodifiableList(group));
}
}
/**
* Loads the list of lazy load groups from the xml element.
*/
private void loadLazyLoadGroupsXml(Element element)
throws DeploymentException
{
Element lazyLoadGroupsElement = MetaData.getOptionalChild(element, "lazy-load-groups");
// If no info, we're done. Default work was already done in constructor.
if(lazyLoadGroupsElement == null)
{
return;
}
// only allowed for cmp 2.x
if(isCMP1x)
{
throw new DeploymentException("lazy-load-groups is only allowed for CMP 2.x");
}
// get the fields
Iterator loadGroupNames = MetaData.getChildrenByTagName(lazyLoadGroupsElement, "load-group-name");
while(loadGroupNames.hasNext())
{
String loadGroupName = MetaData.getElementContent((Element) loadGroupNames.next());
if(!loadGroupName.equals("*") && !loadGroups.containsKey(loadGroupName))
{
throw new DeploymentException(
"Lazy load group not found: " +
"load-group-name=" + loadGroupName
);
}
lazyLoadGroups.add(loadGroupName);
}
}
/**
* Gets the meta data for the application of which this entity is a member.
*
* @return the meta data for the application that this entity is a memeber
*/
public JDBCApplicationMetaData getJDBCApplication()
{
return jdbcApplication;
}
/**
* Gets the name of the datasource in jndi for this entity
*
* @return the name of datasource in jndi
*/
public String getDataSourceName()
{
return dataSourceName;
}
/**
* Gets the jdbc type mapping for this entity
*
* @return the jdbc type mapping for this entity
*/
public JDBCTypeMappingMetaData getTypeMapping() throws DeploymentException
{
if(datasourceMapping == null)
{
throw new DeploymentException("type-mapping is not initialized: " + dataSourceName
+ " was not deployed or type-mapping was not configured.");
}
return datasourceMapping;
}
/**
* Gets the name of this entity. The name come from the ejb-jar.xml file.
*
* @return the name of this entity
*/
public String getName()
{
return entityName;
}
/**
* Gets the abstract shcema name of this entity. The name come from
* the ejb-jar.xml file.
*
* @return the abstract schema name of this entity
*/
public String getAbstractSchemaName()
{
return abstractSchemaName;
}
/**
* Gets the class loaded which is used to load all classes used by this
* entity
*
* @return the class loader which is used to load all classes used by
* this entity
*/
public ClassLoader getClassLoader()
{
return jdbcApplication.getClassLoader();
}
/**
* Gets the implementation class of this entity
*
* @return the implementation class of this entity
*/
public Class getEntityClass()
{
return entityClass;
}
/**
* Gets the home class of this entity
*
* @return the home class of this entity
*/
public Class getHomeClass()
{
return homeClass;
}
/**
* Gets the remote class of this entity
*
* @return the remote class of this entity
*/
public Class getRemoteClass()
{
return remoteClass;
}
/**
* Gets the local home class of this entity
*
* @return the local home class of this entity
*/
public Class getLocalHomeClass()
{
return localHomeClass;
}
/**
* Gets the local class of this entity
*
* @return the local class of this entity
*/
public Class getLocalClass()
{
return localClass;
}
/**
* Does this entity use CMP version 1.x
*
* @return true if this entity used CMP version 1.x; otherwise false
*/
public boolean isCMP1x()
{
return isCMP1x;
}
/**
* Gets the cmp fields of this entity
*
* @return an unmodifiable collection of JDBCCMPFieldMetaData objects
*/
public List getCMPFields()
{
return Collections.unmodifiableList(cmpFields);
}
/**
* Gets the name of the eager load group. This name can be used to
* look up the load group.
*
* @return the name of the eager load group
*/
public String getEagerLoadGroup()
{
return eagerLoadGroup;
}
/**
* Gets the collection of lazy load group names.
*
* @return an unmodifiable collection of load group names
*/
public List getLazyLoadGroups()
{
return Collections.unmodifiableList(lazyLoadGroups);
}
/**
* Gets the map from load grou name to a List of field names, which
* forms a logical load group.
*
* @return an unmodifiable map of load groups (Lists) by group name.
*/
public Map getLoadGroups()
{
return Collections.unmodifiableMap(loadGroups);
}
/**
* Gets the load group with the specified name.
*
* @return the load group with the specified name
* @throws DeploymentException if group with the specified name is not found
*/
public List getLoadGroup(String name) throws DeploymentException
{
List group = (List) loadGroups.get(name);
if(group == null)
{
throw new DeploymentException("Unknown load group: name=" + name);
}
return group;
}
/**
* Returns optimistic locking metadata
*/
public JDBCOptimisticLockingMetaData getOptimisticLocking()
{
return optimisticLocking;
}
/**
* Returns audit metadata
*/
public JDBCAuditMetaData getAudit()
{
return audit;
}
/**
* Gets the cmp field with the specified name
*
* @param name the name of the desired field
* @return the cmp field with the specified name or null if not found
*/
public JDBCCMPFieldMetaData getCMPFieldByName(String name)
{
return (JDBCCMPFieldMetaData) cmpFieldsByName.get(name);
}
/**
* Gets the name of the table to which this entity is persisted
*
* @return the name of the table to which this entity is persisted
*/
public String getDefaultTableName()
{
return tableName;
}
/**
* Gets the flag used to determine if the store manager should attempt to
* create database table when the entity is deployed.
*
* @return true if the store manager should attempt to create the table
*/
public boolean getCreateTable()
{
return createTable;
}
/**
* Gets the flag used to determine if the store manager should attempt to
* remove database table when the entity is undeployed.
*
* @return true if the store manager should attempt to remove the table
*/
public boolean getRemoveTable()
{
return removeTable;
}
/**
* Gets the flag used to determine if the store manager should attempt to
* alter table when the entity is deployed.
*/
public boolean getAlterTable()
{
return alterTable;
}
/**
* Get the (user-defined) SQL commands that sould be issued after table
* creation
*
* @return the SQL command to issue to the DB-server
*/
public ArrayList getDefaultTablePostCreateCmd()
{
return tablePostCreateCmd;
}
/**
* Gets the flag used to determine if the store manager should add a
* priary key constraint when creating the table
*
* @return true if the store manager should add a primary key constraint to
* the create table sql statement
*/
public boolean hasPrimaryKeyConstraint()
{
return primaryKeyConstraint;
}
/**
* Gets the flag used to determine if the store manager should do row locking
* when loading entity beans
*
* @return true if the store manager should add a row locking
* clause when selecting data from the table
*/
public boolean hasRowLocking()
{
return rowLocking;
}
/**
* The maximum number of qurey result lists that will be tracked.
*/
public int getListCacheMax()
{
return listCacheMax;
}
/**
* The number of rows that the database driver should get in a single
* trip to the database.
*/
public int getFetchSize()
{
return fetchSize;
}
/**
* Gets the queries defined on this entity
*
* @return an unmodifiable collection of JDBCQueryMetaData objects
*/
public Collection getQueries()
{
return Collections.unmodifiableCollection(queries.values());
}
/**
* @param method finder method name.
* @return corresponding query metadata or null.
*/
public JDBCQueryMetaData getQueryMetaDataForMethod(Method method)
{
return (JDBCQueryMetaData) queries.get(method);
}
/**
* Get the relationsip roles of this entity.
* Items are instance of JDBCRelationshipRoleMetaData.
*
* @return an unmodifiable collection of the relationship roles defined
* for this entity
*/
public Collection getRelationshipRoles()
{
return jdbcApplication.getRolesForEntity(entityName);
}
/**
* Gets the primary key class for this entity
*
* @return the primary key class for this entity
*/
public Class getPrimaryKeyClass()
{
return primaryKeyClass;
}
/**
* Gets the entity command metadata
*
* @return the entity command metadata
*/
public JDBCEntityCommandMetaData getEntityCommand()
{
return entityCommand;
}
/**
* Is this entity read only? A readonly entity will never be stored into
* the database.
*
* @return true if this entity is read only
*/
public boolean isReadOnly()
{
return readOnly;
}
/**
* How long is a read of this entity valid. This property should only be
* used on read only entities, and determines how long the data read from
* the database is valid. When the read times out it should be reread from
* the database. If the value is -1 and the entity is not using commit
* option a, the read is only valid for the length of the transaction in
* which it was loaded.
*
* @return the length of time that a read is valid or -1 if the read is only
* valid for the length of the transaction
*/
public int getReadTimeOut()
{
return readTimeOut;
}
/**
* Gets the name of the primary key field of this entity or null if
* the primary key is multivalued
*
* @return the name of the primary key field of this entity or null
* if the primary key is multivalued
*/
public String getPrimaryKeyFieldName()
{
return primaryKeyFieldName;
}
/**
* Gets the read ahead meta data for this entity.
*
* @return the read ahead meta data for this entity.
*/
public JDBCReadAheadMetaData getReadAhead()
{
return readAhead;
}
public Class getQLCompiler()
{
return qlCompiler;
}
/**
* Is the throw-runtime-exceptions meta data for this entity is true.
*
* @return the throw-runtime-exceptions meta data for this entity.
*/
public boolean isThrowRuntimeExceptions()
{
return throwRuntimeExceptions;
}
/**
* Gets the throw-runtime-exceptions meta data for this entity.
*
* @return the throw-runtime-exceptions meta data for this entity.
*/
public boolean getThrowRuntimeExceptions()
{
return throwRuntimeExceptions;
}
public boolean isCleanReadAheadOnLoad()
{
return cleanReadAheadOnLoad;
}
public static JDBCTypeMappingMetaData obtainTypeMappingFromLibrary(String dataSourceName)
throws DeploymentException
{
JDBCTypeMappingMetaData typeMapping = null;
String datasource;
if(dataSourceName.startsWith("java:"))
{
datasource = dataSourceName.substring("java:".length());
if(datasource.startsWith("/"))
{
datasource = datasource.substring(1);
}
}
else
{
datasource = dataSourceName;
}
final ObjectName metadataService;
final String str = "jboss.jdbc:service=metadata,datasource=" + datasource;
try
{
metadataService = new ObjectName(str);
}
catch(MalformedObjectNameException e)
{
throw new DeploymentException("Failed to create ObjectName for datasource metadata MBean: " + str, e);
}
try
{
final MBeanServer server = MBeanServerLocator.locateJBoss();
if(server.isRegistered(metadataService))
{
typeMapping = (JDBCTypeMappingMetaData)server.getAttribute(metadataService, "TypeMappingMetaData");
}
}
catch(Exception e)
{
throw new DeploymentException("Failed to obtain type-mapping metadata from the metadata library MBean: " +
e.getMessage(), e);
}
/*
if(typeMapping == null)
{
throw new DeploymentException(
"type-mapping for datasource " + datasource + " not found in the library. " +
"Check the value of metadata/type-mapping in the -ds.xml file."
);
}
*/
return typeMapping;
}
/**
* Compares this JDBCEntityMetaData against the specified object. Returns
* true if the objects are the same. Two JDBCEntityMetaData are the same
* if they both have the same name and are defined in the same application.
*
* @param o the reference object with which to compare
* @return true if this object is the same as the object argument;
* false otherwise
*/
public boolean equals(Object o)
{
if(o instanceof JDBCEntityMetaData)
{
JDBCEntityMetaData entity = (JDBCEntityMetaData) o;
return entityName.equals(entity.entityName) &&
jdbcApplication.equals(entity.jdbcApplication);
}
return false;
}
/**
* Returns a hashcode for this JDBCEntityMetaData. The hashcode is computed
* based on the hashCode of the declaring application and the hashCode of
* the entityName
*
* @return a hash code value for this object
*/
public int hashCode()
{
int result = 17;
result = 37 * result + jdbcApplication.hashCode();
result = 37 * result + entityName.hashCode();
return result;
}
/**
* Returns a string describing this JDBCEntityMetaData. The exact details
* of the representation are unspecified and subject to change, but the
* following may be regarded as typical:
* <p/>
* "[JDBCEntityMetaData: entityName=UserEJB]"
*
* @return a string representation of the object
*/
public String toString()
{
return "[JDBCEntityMetaData : entityName=" + entityName + "]";
}
}