Save This Page
Home » jboss-5.0.0.CR1-src » org.jboss.ejb.plugins.cmp » jdbc » [javadoc | source]
    1   /*
    2   * JBoss, Home of Professional Open Source
    3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
    4   * by the @authors tag. See the copyright.txt in the distribution for a
    5   * full listing of individual contributors.
    6   *
    7   * This is free software; you can redistribute it and/or modify it
    8   * under the terms of the GNU Lesser General Public License as
    9   * published by the Free Software Foundation; either version 2.1 of
   10   * the License, or (at your option) any later version.
   11   *
   12   * This software is distributed in the hope that it will be useful,
   13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15   * Lesser General Public License for more details.
   16   *
   17   * You should have received a copy of the GNU Lesser General Public
   18   * License along with this software; if not, write to the Free
   19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21   */
   22   package org.jboss.ejb.plugins.cmp.jdbc;
   23   
   24   import java.lang.reflect.Method;
   25   import java.sql.Connection;
   26   import java.sql.PreparedStatement;
   27   import java.sql.SQLException;
   28   import java.util.ArrayList;
   29   import java.util.Date;
   30   import java.util.List;
   31   import javax.ejb.CreateException;
   32   import javax.ejb.FinderException;
   33   import javax.management.MalformedObjectNameException;
   34   import javax.sql.DataSource;
   35   
   36   import org.jboss.deployment.DeploymentException;
   37   import org.jboss.ejb.EntityEnterpriseContext;
   38   import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
   39   import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge;
   40   import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
   41   import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
   42   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityCommandMetaData;
   43   import org.jboss.logging.Logger;
   44   import org.jboss.mx.util.MBeanProxyExt;
   45   import org.jboss.security.AuthenticationManager;
   46   
   47   /**
   48    * Base class for create commands that drives the operation sequence.
   49    *
   50    * @author <a href="mailto:jeremy@boynes.com">Jeremy Boynes</a>
   51    * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
   52    */
   53   public abstract class JDBCAbstractCreateCommand implements JDBCCreateCommand
   54   {
   55      protected Logger log;
   56      protected boolean debug;
   57      protected boolean trace;
   58      protected JDBCEntityBridge entity;
   59      protected AuthenticationManager securityManager;
   60      protected boolean createAllowed;
   61      protected SQLExceptionProcessorMBean exceptionProcessor;
   62      protected String insertSQL;
   63      protected JDBCFieldBridge[] insertFields;
   64      protected boolean insertAfterEjbPostCreate;
   65   
   66      // Generated fields
   67      private JDBCCMPFieldBridge createdPrincipal;
   68      private JDBCCMPFieldBridge createdTime;
   69      private JDBCCMPFieldBridge updatedPrincipal;
   70      private JDBCCMPFieldBridge updatedTime;
   71   
   72      public void init(JDBCStoreManager manager) throws DeploymentException
   73      {
   74         log = Logger.getLogger(getClass().getName() + '.' + manager.getMetaData().getName());
   75         debug = log.isDebugEnabled();
   76         trace = log.isTraceEnabled();
   77   
   78         entity = (JDBCEntityBridge) manager.getEntityBridge();
   79         securityManager = manager.getContainer().getSecurityManager();
   80   
   81         insertAfterEjbPostCreate = manager.getContainer()
   82            .getBeanMetaData().getContainerConfiguration().isInsertAfterEjbPostCreate();
   83   
   84         // set create allowed
   85         createAllowed = true;
   86         JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
   87         for(int i = 0; i < pkFields.length; i++)
   88         {
   89            if(pkFields[i].isReadOnly())
   90            {
   91               createAllowed = false;
   92               log.debug("Create will not be allowed because pk field "
   93                  + pkFields[i].getFieldName() + "is read only.");
   94               break;
   95            }
   96         }
   97   
   98         initGeneratedFields();
   99   
  100         JDBCEntityCommandMetaData entityCommand = manager.getMetaData().getEntityCommand();
  101         if(entityCommand == null)
  102         {
  103            throw new DeploymentException("entity-command is null");
  104         }
  105         initEntityCommand(entityCommand);
  106   
  107         initInsertFields();
  108         initInsertSQL();
  109      }
  110   
  111      protected void initEntityCommand(JDBCEntityCommandMetaData entityCommand) throws DeploymentException
  112      {
  113         String objectName = entityCommand.getAttribute("SQLExceptionProcessor");
  114         if(objectName != null)
  115         {
  116            try
  117            {
  118               exceptionProcessor = (SQLExceptionProcessorMBean) MBeanProxyExt.create(SQLExceptionProcessorMBean.class, objectName);
  119            }
  120            catch(MalformedObjectNameException e)
  121            {
  122               throw new DeploymentException("Invalid object name for SQLExceptionProcessor: ", e);
  123            }
  124         }
  125      }
  126   
  127      public Object execute(Method m, Object[] args, EntityEnterpriseContext ctx) throws CreateException
  128      {
  129         // TODO: implement this logic nicer
  130         if(insertAfterEjbPostCreate)
  131         {
  132            if(!JDBCEntityBridge.isEjbCreateDone(ctx))
  133            {
  134               checkCreateAllowed();
  135               generateFields(ctx);
  136               JDBCEntityBridge.setEjbCreateDone(ctx);
  137            }
  138            else
  139            {
  140               beforeInsert(ctx);
  141               performInsert(ctx);
  142               afterInsert(ctx);
  143               JDBCEntityBridge.setCreated(ctx);
  144            }
  145         }
  146         else
  147         {
  148            checkCreateAllowed();
  149            generateFields(ctx);
  150            beforeInsert(ctx);
  151            performInsert(ctx);
  152            afterInsert(ctx);
  153            JDBCEntityBridge.setCreated(ctx);
  154         }
  155         return getPrimaryKey(ctx);
  156      }
  157   
  158      protected void checkCreateAllowed() throws CreateException
  159      {
  160         if(!createAllowed)
  161         {
  162            throw new CreateException("Creation is not allowed because a primary key field is read only.");
  163         }
  164      }
  165   
  166      protected JDBCCMPFieldBridge getGeneratedPKField() throws DeploymentException
  167      {
  168         // extract the pk field to be generated
  169         JDBCCMPFieldBridge pkField = null;
  170         JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
  171         for(int i = 0; i < pkFields.length; ++i)
  172         {
  173            if(pkField != null)
  174               throw new DeploymentException("Generation only supported with single PK field");
  175            pkField = (JDBCCMPFieldBridge)pkFields[i];
  176         }
  177         return pkField;
  178      }
  179   
  180      protected void initGeneratedFields() throws DeploymentException
  181      {
  182         createdPrincipal = entity.getCreatedPrincipalField();
  183         if(securityManager == null && createdPrincipal != null)
  184         {
  185            throw new DeploymentException("No security-domain configured but created-by specified");
  186         }
  187         updatedPrincipal = entity.getUpdatedPrincipalField();
  188         if(securityManager == null && updatedPrincipal != null)
  189         {
  190            throw new DeploymentException("No security-domain configured but updated-by specified");
  191         }
  192         createdTime = entity.getCreatedTimeField();
  193         updatedTime = entity.getUpdatedTimeField();
  194      }
  195   
  196      protected void generateFields(EntityEnterpriseContext ctx) throws CreateException
  197      {
  198         // Audit principal fields
  199         if(securityManager != null)
  200         {
  201            String principalName = ctx.getEJBContext().getCallerPrincipal().getName();
  202            if(createdPrincipal != null && createdPrincipal.getInstanceValue(ctx) == null)
  203            {
  204               createdPrincipal.setInstanceValue(ctx, principalName);
  205            }
  206            /*
  207            if(updatedPrincipal != null && updatedPrincipal.getInstanceValue(ctx) == null)
  208            {
  209               updatedPrincipal.setInstanceValue(ctx, principalName);
  210            }
  211            */
  212         }
  213   
  214         // Audit time fields
  215         Date date = null;
  216         if(createdTime != null && createdTime.getInstanceValue(ctx) == null)
  217         {
  218            date = new Date();
  219            createdTime.setInstanceValue(ctx, date);
  220         }
  221         /*
  222         if(updatedTime != null && updatedTime.getInstanceValue(ctx) == null)
  223         {
  224            if(date == null)
  225               date = new Date();
  226            updatedTime.setInstanceValue(ctx, date);
  227         }
  228         */
  229      }
  230   
  231      protected void initInsertFields()
  232      {
  233         JDBCFieldBridge[] fields = entity.getTableFields();
  234         List insertFieldsList = new ArrayList(fields.length);
  235         for(int i = 0; i < fields.length; i++)
  236         {
  237            JDBCFieldBridge field = fields[i];
  238            if(isInsertField(field))
  239               insertFieldsList.add(field);
  240         }
  241   
  242         insertFields = (JDBCFieldBridge[]) insertFieldsList.toArray(new JDBCFieldBridge[insertFieldsList.size()]);
  243      }
  244   
  245      protected boolean isInsertField(JDBCFieldBridge field)
  246      {
  247         boolean result =
  248            !(field instanceof JDBCCMRFieldBridge)
  249            && field.getJDBCType() != null
  250            && !field.isReadOnly();
  251         if(field instanceof JDBCCMPFieldBridge)
  252            result = result && !((JDBCCMPFieldBridge) field).isRelationTableField();
  253         return result;
  254      }
  255   
  256      protected void initInsertSQL()
  257      {
  258         StringBuffer sql = new StringBuffer(250);
  259         sql.append(SQLUtil.INSERT_INTO)
  260            .append(entity.getQualifiedTableName())
  261            .append(" (");
  262   
  263         SQLUtil.getColumnNamesClause(insertFields, sql);
  264   
  265         sql.append(')')
  266            .append(SQLUtil.VALUES).append('(');
  267         SQLUtil.getValuesClause(insertFields, sql)
  268            .append(')');
  269         insertSQL = sql.toString();
  270   
  271         if(debug)
  272            log.debug("Insert Entity SQL: " + insertSQL);
  273      }
  274   
  275      protected void beforeInsert(EntityEnterpriseContext ctx) throws CreateException
  276      {
  277      }
  278   
  279      protected void performInsert(EntityEnterpriseContext ctx) throws CreateException
  280      {
  281         Connection c = null;
  282         PreparedStatement ps = null;
  283         boolean throwRuntimeExceptions = entity.getMetaData().getThrowRuntimeExceptions();
  284   
  285         // if metadata is true, the getconnection is done inside 
  286         // its own try catch block to throw a runtime exception (EJBException)
  287         if (throwRuntimeExceptions)
  288         {
  289             try 
  290             {
  291                 c = entity.getDataSource().getConnection();
  292             } 
  293             catch (SQLException sqle) 
  294             {
  295                 javax.ejb.EJBException ejbe = new javax.ejb.EJBException("Could not get a connection; " + sqle);
  296                 ejbe.initCause(sqle);
  297                 throw ejbe;
  298             } 
  299         }
  300        
  301         try
  302         {
  303            if(debug)
  304               log.debug("Executing SQL: " + insertSQL);
  305   
  306   
  307            // if metadata is false, the getconnection is done inside this try catch block
  308            if ( ! throwRuntimeExceptions)
  309            {
  310                c = entity.getDataSource().getConnection();
  311            }
  312            ps = prepareStatement(c, insertSQL, ctx);
  313   
  314            // set the parameters
  315            int index = 1;
  316            for(int fieldInd = 0; fieldInd < insertFields.length; ++fieldInd)
  317            {
  318               index = insertFields[fieldInd].setInstanceParameters(ps, index, ctx);
  319            }
  320   
  321            // execute statement
  322            int rowsAffected = executeInsert(index, ps, ctx);
  323            if(rowsAffected != 1)
  324            {
  325               throw new CreateException("Expected one affected row but update returned" + rowsAffected +
  326                  " for id=" + ctx.getId());
  327            }
  328         }
  329         catch(SQLException e)
  330         {
  331            if(exceptionProcessor != null && exceptionProcessor.isDuplicateKey(e))
  332            {
  333               log.error("Failed to create instance.", e);
  334               throw new CreateException(
  335                  "Integrity constraint violation. Possibly unique key violation or invalid foreign key value."
  336               );
  337            }
  338            else
  339            {
  340               log.error("Could not create entity", e);
  341               CreateException ce = new CreateException("Could not create entity: " + e);
  342               ce.initCause(e);
  343               throw ce;
  344            }
  345         }
  346         finally
  347         {
  348            JDBCUtil.safeClose(ps);
  349            JDBCUtil.safeClose(c);
  350         }
  351   
  352         // Mark the inserted fields as clean.
  353         for(int fieldInd = 0; fieldInd < insertFields.length; ++fieldInd)
  354         {
  355            insertFields[fieldInd].setClean(ctx);
  356         }
  357      }
  358   
  359      protected PreparedStatement prepareStatement(Connection c, String sql, EntityEnterpriseContext ctx) throws SQLException
  360      {
  361         return c.prepareStatement(sql);
  362      }
  363   
  364      protected int executeInsert(int paramIndex, PreparedStatement ps, EntityEnterpriseContext ctx) throws SQLException
  365      {
  366         return ps.executeUpdate();
  367      }
  368   
  369      protected void afterInsert(EntityEnterpriseContext ctx) throws CreateException
  370      {
  371      }
  372   
  373      protected Object getPrimaryKey(EntityEnterpriseContext ctx)
  374      {
  375         return entity.extractPrimaryKeyFromInstance(ctx);
  376      }
  377   }

Save This Page
Home » jboss-5.0.0.CR1-src » org.jboss.ejb.plugins.cmp » jdbc » [javadoc | source]