Package org.jboss.ejb.plugins.cmp.jdbc.metadata

Source Code of org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData

/*
* 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.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;

/**
* This class represents one ejb-relation element in the ejb-jar.xml file. Most
* properties of this class are immutable. The mutable properties have set
* methods.
*
* @author <a href="mailto:dain@daingroup.com">Dain Sundstrom </a>
* @author <a href="mailto:heiko.rupp@cellent.de">Heiko W. Rupp </a>
* @version $Revision: 81030 $
*/
public final class JDBCRelationMetaData
{
  private final static int TABLE = 1;

  private final static int FOREIGN_KEY = 2;

  /** Name of the relation. Loaded from the ejb-relation-name element. */
  private final String relationName;

  /**
   * The left jdbc relationship role. Loaded from an ejb-relationship-role.
   * Left/right assignment is completely arbitrary.
   */
  private final JDBCRelationshipRoleMetaData left;

  /**
   * The right relationship role. Loaded from an ejb-relationship-role.
   * Left/right assignment is completely arbitrary.
   */
  private final JDBCRelationshipRoleMetaData right;

  /**
   * The mapping style for this relation (i.e., TABLE or FOREIGN_KEY).
   */
  private final int mappingStyle;

  /** data source name in jndi */
  private final String dataSourceName;

   /** datasource type mapping name is defined in the deployment descriptor */
   private final String datasourceMappingName;

  /** This is a cache of the datasource object. */
  private transient DataSource dataSource;

  /** type mapping used for the relation table */
  private final JDBCTypeMappingMetaData datasourceMapping;

  /** the name of the table to use for this bean */
  private final String tableName;

  /** is table created */
  private boolean tableCreated;

  /** is table dropped */
  private boolean tableDropped;

  /** should we create the table when deployed */
  private final boolean createTable;

  /** should we drop the table when deployed */
  private final boolean removeTable;

  /** should we alter the table when deployed */
  private final boolean alterTable;

  /**
   * What commands should be issued directly after creation of a table?
   */
  private final ArrayList tablePostCreateCmd;

  /** should we use 'SELECT ... FOR UPDATE' syntax? */
  private final boolean rowLocking;

  /** should the table have a primary key constraint? */
  private final boolean primaryKeyConstraint;

  /** is the relationship read-only? */
  private final boolean readOnly;

  /** how long is read valid */
  private final int readTimeOut;

  /**
   * Constructs jdbc relation meta data with the data from the relation
   * metadata loaded from the ejb-jar.xml file.
   *
   * @param jdbcApplication used to retrieve the entities of this relation
   * @param relationMetaData relation meta data loaded from the ejb-jar.xml
   *           file
   */
  public JDBCRelationMetaData(JDBCApplicationMetaData jdbcApplication, RelationMetaData relationMetaData)
      throws DeploymentException
  {
      RelationshipRoleMetaData leftRole = relationMetaData.getLeftRelationshipRole();
    RelationshipRoleMetaData rightRole = relationMetaData.getRightRelationshipRole();

    // set the default mapping style
    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);

      relationName = getNonNullRelationName(left, right, relationMetaData.getRelationName());

      if (mappingStyle == TABLE)
    {
      tableName = createDefaultTableName();
      tablePostCreateCmd = getDefaultTablePostCreateCmd();
    }
    else
    {
      tableName = null;
      tablePostCreateCmd = null;
    }
  }

   /**
   * Constructs relation meta data with the data contained in the ejb-relation
   * element or the defaults element from a jbosscmp-jdbc xml file. Optional
   * values of the xml element that are not present are loaded from the
   * defaultValues parameter.
   *
   * @param jdbcApplication used to retrieve type mappings in table mapping
   *           style
   * @param element the xml Element which contains the metadata about this
   *           relation
   * @param defaultValues the JDBCApplicationMetaData which contains the
   *           values for optional elements of the element
   * @throws DeploymentException if the xml element is not semantically
   *            correct
   */
  public JDBCRelationMetaData(JDBCApplicationMetaData jdbcApplication, Element element,
      JDBCRelationMetaData defaultValues) throws DeploymentException
  {
    mappingStyle = loadMappingStyle(element, defaultValues);

    // read-only
    String readOnlyString = MetaData.getOptionalChildContent(element, "read-only");
    if (readOnlyString != null)
    {
      readOnly = Boolean.valueOf(readOnlyString).booleanValue();
    }
    else
    {
      readOnly = defaultValues.isReadOnly();
    }

    // read-time-out
    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();
    }

    //
    // Load all of the table options. defaults and relation-table-mapping
    // will have these elements, and foreign-key will get the default values.
    //
    Element mappingElement = getMappingElement(element);

    // datasource name
    String dataSourceNameString = MetaData.getOptionalChildContent(mappingElement, "datasource");
    if (dataSourceNameString != null)
         dataSourceName = dataSourceNameString;
    else
      dataSourceName = defaultValues.getDataSourceName();

    // get the type mapping for this datasource (optional, but always
    // set in standardjbosscmp-jdbc.xml)
    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);
      }

    // get table name
    String tableNameString = MetaData.getOptionalChildContent(mappingElement, "table-name");
    if (tableNameString == null)
    {
      tableNameString = defaultValues.getDefaultTableName();
      if (tableNameString == null)
      {
        // use defaultValues to create default, because left/right
        // have not been assigned yet, and values used to generate
        // default table name never change
        tableNameString = defaultValues.createDefaultTableName();
      }
    }
    tableName = tableNameString;

    // create table? If not provided, keep default.
    String createString = MetaData.getOptionalChildContent(mappingElement, "create-table");
    if (createString != null)
    {
      createTable = Boolean.valueOf(createString).booleanValue();
    }
    else
    {
      createTable = defaultValues.getCreateTable();
    }

    // remove table? If not provided, keep default.
    String removeString = MetaData.getOptionalChildContent(mappingElement, "remove-table");
    if (removeString != null)
    {
      removeTable = Boolean.valueOf(removeString).booleanValue();
    }
    else
    {
      removeTable = defaultValues.getRemoveTable();
    }

      // post-table-create commands
      Element posttc = MetaData.getOptionalChild(mappingElement, "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();
      }

    // alter table? If not provided, keep default.
    String alterString = MetaData.getOptionalChildContent(mappingElement, "alter-table");
    if (alterString != null)
    {
      alterTable = Boolean.valueOf(alterString).booleanValue();
    }
    else
    {
      alterTable = defaultValues.getAlterTable();
    }

    // select for update
    String sForUpString = MetaData.getOptionalChildContent(mappingElement, "row-locking");
    if (sForUpString != null)
    {
      rowLocking = !isReadOnly() && (Boolean.valueOf(sForUpString).booleanValue());
    }
    else
    {
      rowLocking = defaultValues.hasRowLocking();
    }

    // primary key constraint? If not provided, keep default.
    String pkString = MetaData.getOptionalChildContent(mappingElement, "pk-constraint");
    if (pkString != null)
    {
      primaryKeyConstraint = Boolean.valueOf(pkString).booleanValue();
    }
    else
    {
      primaryKeyConstraint = defaultValues.hasPrimaryKeyConstraint();
    }

    //
    // load metadata for each specified role
    //
    JDBCRelationshipRoleMetaData defaultLeft = defaultValues.getLeftRelationshipRole();
    JDBCRelationshipRoleMetaData defaultRight = defaultValues.getRightRelationshipRole();

    if (!MetaData.getChildrenByTagName(element, "ejb-relationship-role").hasNext())
    {

      // no roles specified use the defaults
      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);
    }

      this.relationName = getNonNullRelationName(left, right, defaultValues.getRelationName());

      // at least one side of a fk relation must have keys
    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);
    }

    // both sides of a table relation must have keys
    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 check for preferred-relation-mapping
    if ("defaults".equals(element.getTagName()))
    {
      // set mapping style based on preferred-relation-mapping (if possible)
      String perferredRelationMapping = MetaData.getOptionalChildContent(element, "preferred-relation-mapping");

      if ("relation-table".equals(perferredRelationMapping) || defaultValues.isManyToMany())
      {
        return TABLE;
      }
      else
      {
        return FOREIGN_KEY;
      }
    }

    // check for table mapping style
    if (MetaData.getOptionalChild(element, "relation-table-mapping") != null)
    {
      return TABLE;
    }

    // check for foreign-key mapping style
    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;
    }

    // no mapping style element, will use defaultValues
    return defaultValues.mappingStyle;
  }

  private static Element getMappingElement(Element element) throws DeploymentException
  {

    // if defaults check for preferred-relation-mapping
    if ("defaults".equals(element.getTagName()))
    {
      return element;
    }

    // check for table mapping style
    Element tableMappingElement = MetaData.getOptionalChild(element, "relation-table-mapping");
    if (tableMappingElement != null)
    {
      return tableMappingElement;
    }

    // check for foreign-key mapping style
    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++)
    {
      // only 2 roles are allowed
      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;
  }

  /**
   * Gets the relation name. Relation name is loaded from the
   * ejb-relation-name element.
   *
   * @return the name of this relation
   */
  public String getRelationName()
  {
    return relationName;
  }

  /**
   * Gets the left jdbc relationship role. The relationship role is loaded
   * from an ejb-relationship-role. Left/right assignment is completely
   * arbitrary.
   *
   * @return the left JDBCRelationshipRoleMetaData
   */
  public JDBCRelationshipRoleMetaData getLeftRelationshipRole()
  {
    return left;
  }

  /**
   * Gets the right jdbc relationship role. The relationship role is loaded
   * from an ejb-relationship-role. Left/right assignment is completely
   * arbitrary.
   *
   * @return the right JDBCRelationshipRoleMetaData
   */
  public JDBCRelationshipRoleMetaData getRightRelationshipRole()
  {
    return right;
  }

  /**
   * Gets the relationship role related to the specified role.
   *
   * @param role the relationship role that the related role is desired
   * @return the relationship role related to the specified role. right role
   *         of this relation
   */
  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);
    }
  }

  /**
   * Should this relation be mapped to a relation table.
   *
   * @return true if this relation is mapped to a table
   */
  public boolean isTableMappingStyle()
  {
    return mappingStyle == TABLE;
  }

  /**
   * Should this relation use foreign keys for storage.
   *
   * @return true if this relation is mapped to foreign keys
   */
  public boolean isForeignKeyMappingStyle()
  {
    return mappingStyle == FOREIGN_KEY;
  }

  /**
   * Gets the name of the datasource in jndi for this entity
   *
   * @return the name of datasource in jndi
   */
  private 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 the relation table.
   *
   * @return the name of the relation table to which is relation is mapped
   */
  public String getDefaultTableName()
  {
    return tableName;
  }

  /**
   * Gets the (user-defined) SQL commands that should be issued to the db
   * after table creation.
   *
   * @return the SQL command
   */
  public ArrayList getDefaultTablePostCreateCmd()
  {
    return tablePostCreateCmd;
  }

  /**
   * Does the table exist yet? This does not mean that table has been created
   * by the appilcation, or the the database metadata has been checked for the
   * existance of the table, but that at this point the table is assumed to
   * exist.
   *
   * @return true if the table exists
   */
  public boolean isTableCreated()
  {
    return tableCreated;
  }

  public void setTableCreated()
  {
    tableCreated = true;
  }

  /**
   * Sets table dropped flag.
   */
  public void setTableDropped()
  {
    this.tableDropped = true;
  }

  public boolean isTableDropped()
  {
    return tableDropped;
  }

  /**
   * Should the relation table be created on startup.
   *
   * @return true if the store mananager should attempt to create the relation
   *         table
   */
  public boolean getCreateTable()
  {
    return createTable;
  }

  /**
   * Should the relation table be removed on shutdown.
   *
   * @return true if the store mananager should attempt to remove the relation
   *         table
   */
  public boolean getRemoveTable()
  {
    return removeTable;
  }

  /**
   * Should the relation table be altered on deploy.
   */
  public boolean getAlterTable()
  {
    return alterTable;
  }

  /**
   * When the relation table is created, should it have a primary key
   * constraint.
   *
   * @return true if the store mananager should add a primary key constraint
   *         to the the create table sql statement
   */
  public boolean hasPrimaryKeyConstraint()
  {
    return primaryKeyConstraint;
  }

  /**
   * Is this relation read-only?
   */
  public boolean isReadOnly()
  {
    return readOnly;
  }

  /**
   * Gets the read time out length.
   */
  public int getReadTimeOut()
  {
    return readTimeOut;
  }

  /**
   * Should select queries do row locking
   */
  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;
  }

   private String getNonNullRelationName(JDBCRelationshipRoleMetaData left,
                                         JDBCRelationshipRoleMetaData right,
                                         String relationName)
   {
      // JBossCMP needs ejb-relation-name if jbosscmp-jdbc.xml is used to map relationships.
      if(relationName == null)
      {
         // generate unique name, we can't rely on ejb-relationship-role-name being unique
         relationName = left.getEntity().getName() +
            (!left.isNavigable() ? "" : "_" + left.getCMRFieldName()) +
            "-" +
            right.getEntity().getName() +
            (!right.isNavigable() ? "" : "_" + right.getCMRFieldName());
      }
      return relationName;
   }
}
TOP

Related Classes of org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.