package org.jboss.ejb.plugins.cmp.jdbc.metadata;
import java.util.ArrayList;
import java.util.Iterator;
import javax.ejb.EJBException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.jboss.deployment.DeploymentException;
import org.jboss.metadata.MetaData;
import org.jboss.metadata.RelationMetaData;
import org.jboss.metadata.RelationshipRoleMetaData;
import org.w3c.dom.Element;
public final class JDBCRelationMetaData
{
private final static int TABLE = 1;
private final static int FOREIGN_KEY = 2;
private final String relationName;
private final JDBCRelationshipRoleMetaData left;
private final JDBCRelationshipRoleMetaData right;
private final int mappingStyle;
private final String dataSourceName;
private transient DataSource dataSource;
private final String datasourceMappingName;
private final JDBCTypeMappingMetaData datasourceMapping;
private final String tableName;
private boolean tableCreated;
private boolean tableDropped;
private final boolean createTable;
private final boolean removeTable;
private final boolean alterTable;
private final ArrayList tablePostCreateCmd;
private final boolean rowLocking;
private final boolean primaryKeyConstraint;
private final boolean readOnly;
private final int readTimeOut;
public JDBCRelationMetaData(JDBCApplicationMetaData jdbcApplication, RelationMetaData relationMetaData)
throws DeploymentException
{
relationName = relationMetaData.getRelationName();
RelationshipRoleMetaData leftRole = relationMetaData.getLeftRelationshipRole();
RelationshipRoleMetaData rightRole = relationMetaData.getRightRelationshipRole();
if (leftRole.isMultiplicityMany() && rightRole.isMultiplicityMany())
{
mappingStyle = TABLE;
}
else
{
mappingStyle = FOREIGN_KEY;
}
dataSourceName = null;
datasourceMappingName = null;
datasourceMapping = null;
createTable = false;
removeTable = false;
alterTable = false;
rowLocking = false;
primaryKeyConstraint = false;
readOnly = false;
readTimeOut = -1;
left = new JDBCRelationshipRoleMetaData(this, jdbcApplication, leftRole);
right = new JDBCRelationshipRoleMetaData(this, jdbcApplication, rightRole);
left.init(right);
right.init(left);
if (mappingStyle == TABLE)
{
tableName = createDefaultTableName();
tablePostCreateCmd = getDefaultTablePostCreateCmd();
}
else
{
tableName = null;
tablePostCreateCmd = null;
}
}
public JDBCRelationMetaData(JDBCApplicationMetaData jdbcApplication, Element element,
JDBCRelationMetaData defaultValues) throws DeploymentException
{
relationName = defaultValues.getRelationName();
mappingStyle = loadMappingStyle(element, defaultValues);
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();
}
String readOnlyString = MetaData.getOptionalChildContent(element, "read-only");
if (readOnlyString != null)
{
readOnly = Boolean.valueOf(readOnlyString).booleanValue();
}
else
{
readOnly = defaultValues.isReadOnly();
}
String readTimeOutString = MetaData.getOptionalChildContent(element, "read-time-out");
if (readTimeOutString != null)
{
try
{
readTimeOut = Integer.parseInt(readTimeOutString);
}
catch (NumberFormatException e)
{
throw new DeploymentException("Invalid number format in " + "read-time-out '" + readTimeOutString + "': "
+ e);
}
}
else
{
readTimeOut = defaultValues.getReadTimeOut();
}
Element mappingElement = getMappingElement(element);
String dataSourceNameString = MetaData.getOptionalChildContent(mappingElement, "datasource");
if (dataSourceNameString != null)
dataSourceName = dataSourceNameString;
else
dataSourceName = defaultValues.getDataSourceName();
String datasourceMappingString = MetaData.getOptionalChildContent(mappingElement, "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.getTypeMapping() != null)
{
datasourceMappingName = null;
datasourceMapping = defaultValues.getTypeMapping();
}
else
{
datasourceMappingName = null;
datasourceMapping = JDBCEntityMetaData.obtainTypeMappingFromLibrary(dataSourceName);
}
String tableNameString = MetaData.getOptionalChildContent(mappingElement, "table-name");
if (tableNameString == null)
{
tableNameString = defaultValues.getDefaultTableName();
if (tableNameString == null)
{
tableNameString = defaultValues.createDefaultTableName();
}
}
tableName = tableNameString;
String createString = MetaData.getOptionalChildContent(mappingElement, "create-table");
if (createString != null)
{
createTable = Boolean.valueOf(createString).booleanValue();
}
else
{
createTable = defaultValues.getCreateTable();
}
String removeString = MetaData.getOptionalChildContent(mappingElement, "remove-table");
if (removeString != null)
{
removeTable = Boolean.valueOf(removeString).booleanValue();
}
else
{
removeTable = defaultValues.getRemoveTable();
}
String alterString = MetaData.getOptionalChildContent(mappingElement, "alter-table");
if (alterString != null)
{
alterTable = Boolean.valueOf(alterString).booleanValue();
}
else
{
alterTable = defaultValues.getAlterTable();
}
String sForUpString = MetaData.getOptionalChildContent(mappingElement, "row-locking");
if (sForUpString != null)
{
rowLocking = !isReadOnly() && (Boolean.valueOf(sForUpString).booleanValue());
}
else
{
rowLocking = defaultValues.hasRowLocking();
}
String pkString = MetaData.getOptionalChildContent(mappingElement, "pk-constraint");
if (pkString != null)
{
primaryKeyConstraint = Boolean.valueOf(pkString).booleanValue();
}
else
{
primaryKeyConstraint = defaultValues.hasPrimaryKeyConstraint();
}
JDBCRelationshipRoleMetaData defaultLeft = defaultValues.getLeftRelationshipRole();
JDBCRelationshipRoleMetaData defaultRight = defaultValues.getRightRelationshipRole();
if (!MetaData.getChildrenByTagName(element, "ejb-relationship-role").hasNext())
{
left = new JDBCRelationshipRoleMetaData(this, jdbcApplication, element, defaultLeft);
right = new JDBCRelationshipRoleMetaData(this, jdbcApplication, element, defaultRight);
left.init(right);
right.init(left);
}
else
{
Element leftElement = getEJBRelationshipRoleElement(element, defaultLeft);
left = new JDBCRelationshipRoleMetaData(this, jdbcApplication, leftElement, defaultLeft);
Element rightElement = getEJBRelationshipRoleElement(element, defaultRight);
right = new JDBCRelationshipRoleMetaData(this, jdbcApplication, rightElement, defaultRight);
left.init(right, leftElement);
right.init(left, rightElement);
}
if (isForeignKeyMappingStyle() && left.getKeyFields().isEmpty() && right.getKeyFields().isEmpty())
{
throw new DeploymentException("Atleast one role of a foreign-key "
+ "mapped relationship must have key fields " + "(or <primkey-field> is missing from ejb-jar.xml): "
+ "ejb-relation-name=" + relationName);
}
if (isTableMappingStyle() && (left.getKeyFields().isEmpty() || right.getKeyFields().isEmpty()))
{
throw new DeploymentException("Both roles of a relation-table " + "mapped relationship must have key fields: "
+ "ejb-relation-name=" + relationName);
}
}
private int loadMappingStyle(Element element, JDBCRelationMetaData defaultValues) throws DeploymentException
{
if ("defaults".equals(element.getTagName()))
{
String perferredRelationMapping = MetaData.getOptionalChildContent(element, "preferred-relation-mapping");
if ("relation-table".equals(perferredRelationMapping) || defaultValues.isManyToMany())
{
return TABLE;
}
else
{
return FOREIGN_KEY;
}
}
if (MetaData.getOptionalChild(element, "relation-table-mapping") != null)
{
return TABLE;
}
if (MetaData.getOptionalChild(element, "foreign-key-mapping") != null)
{
if (defaultValues.isManyToMany())
{
throw new DeploymentException("Foreign key mapping-style "
+ "is not allowed for many-to-many relationsips.");
}
return FOREIGN_KEY;
}
return defaultValues.mappingStyle;
}
private static Element getMappingElement(Element element) throws DeploymentException
{
if ("defaults".equals(element.getTagName()))
{
return element;
}
Element tableMappingElement = MetaData.getOptionalChild(element, "relation-table-mapping");
if (tableMappingElement != null)
{
return tableMappingElement;
}
Element foreignKeyMappingElement = MetaData.getOptionalChild(element, "foreign-key-mapping");
if (foreignKeyMappingElement != null)
{
return foreignKeyMappingElement;
}
return null;
}
private static Element getEJBRelationshipRoleElement(Element element, JDBCRelationshipRoleMetaData defaultRole)
throws DeploymentException
{
String roleName = defaultRole.getRelationshipRoleName();
if (roleName == null)
throw new DeploymentException("No ejb-relationship-role-name element found");
Iterator iter = MetaData.getChildrenByTagName(element, "ejb-relationship-role");
if (!iter.hasNext())
{
throw new DeploymentException("No ejb-relationship-role " + "elements found");
}
Element roleElement = null;
for (int i = 0; iter.hasNext(); i++)
{
if (i > 1)
{
throw new DeploymentException("Expected only 2 " + "ejb-relationship-role but found more then 2");
}
Element tempElement = (Element) iter.next();
if (roleName.equals(MetaData.getUniqueChildContent(tempElement, "ejb-relationship-role-name")))
{
roleElement = tempElement;
}
}
if (roleElement == null)
{
throw new DeploymentException("An ejb-relationship-role element was " + "not found for role '" + roleName
+ "'");
}
return roleElement;
}
public String getRelationName()
{
return relationName;
}
public JDBCRelationshipRoleMetaData getLeftRelationshipRole()
{
return left;
}
public JDBCRelationshipRoleMetaData getRightRelationshipRole()
{
return right;
}
public JDBCRelationshipRoleMetaData getOtherRelationshipRole(JDBCRelationshipRoleMetaData role)
{
if (left == role)
{
return right;
}
else if (right == role)
{
return left;
}
else
{
throw new IllegalArgumentException("Specified role is not the left " + "or right role. role=" + role);
}
}
public boolean isTableMappingStyle()
{
return mappingStyle == TABLE;
}
public boolean isForeignKeyMappingStyle()
{
return mappingStyle == FOREIGN_KEY;
}
private String getDataSourceName()
{
return dataSourceName;
}
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;
}
public String getDefaultTableName()
{
return tableName;
}
private ArrayList getDefaultTablePostCreateCmd()
{
return tablePostCreateCmd;
}
public boolean isTableCreated()
{
return tableCreated;
}
public void setTableCreated()
{
tableCreated = true;
}
public void setTableDropped()
{
this.tableDropped = true;
}
public boolean isTableDropped()
{
return tableDropped;
}
public boolean getCreateTable()
{
return createTable;
}
public boolean getRemoveTable()
{
return removeTable;
}
public boolean getAlterTable()
{
return alterTable;
}
public boolean hasPrimaryKeyConstraint()
{
return primaryKeyConstraint;
}
public boolean isReadOnly()
{
return readOnly;
}
public int getReadTimeOut()
{
return readTimeOut;
}
public boolean hasRowLocking()
{
return rowLocking;
}
private String createDefaultTableName()
{
String defaultTableName = left.getEntity().getName();
if (left.getCMRFieldName() != null)
{
defaultTableName += "_" + left.getCMRFieldName();
}
defaultTableName += "_" + right.getEntity().getName();
if (right.getCMRFieldName() != null)
{
defaultTableName += "_" + right.getCMRFieldName();
}
return defaultTableName;
}
private boolean isManyToMany()
{
return left.isMultiplicityMany() && right.isMultiplicityMany();
}
public synchronized DataSource getDataSource()
{
if (dataSource == null)
{
try
{
InitialContext context = new InitialContext();
dataSource = (DataSource) context.lookup(dataSourceName);
}
catch (NamingException e)
{
throw new EJBException("Data source for relationship named " + relationName + " not found "
+ dataSourceName);
}
}
return dataSource;
}
}