Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » ejb » txtimer » [javadoc | source]
    1   /*
    2    * JBoss, Home of Professional Open Source.
    3    * Copyright 2006, Red Hat Middleware LLC, and individual contributors
    4    * as indicated by the @author tags. See the copyright.txt file in the
    5    * distribution for a 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.txtimer;
   23   
   24   // $Id: GeneralPurposeDatabasePersistencePlugin.java 62320 2007-04-13 10:56:45Z dimitris@jboss.org $
   25   
   26   import java.io.ByteArrayInputStream;
   27   import java.io.ByteArrayOutputStream;
   28   import java.io.IOException;
   29   import java.io.InputStream;
   30   import java.io.ObjectInputStream;
   31   import java.io.ObjectOutputStream;
   32   import java.io.Serializable;
   33   import java.sql.Connection;
   34   import java.sql.PreparedStatement;
   35   import java.sql.ResultSet;
   36   import java.sql.SQLException;
   37   import java.sql.Statement;
   38   import java.sql.Timestamp;
   39   import java.util.ArrayList;
   40   import java.util.Date;
   41   import java.util.List;
   42   
   43   import javax.management.MBeanServer;
   44   import javax.management.ObjectName;
   45   import javax.naming.InitialContext;
   46   import javax.sql.DataSource;
   47   
   48   import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil;
   49   import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
   50   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
   51   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCTypeMappingMetaData;
   52   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCMappingMetaData;
   53   import org.jboss.invocation.MarshalledValueInputStream;
   54   import org.jboss.logging.Logger;
   55   import org.jboss.mx.util.ObjectNameFactory;
   56   
   57   /**
   58    * This DatabasePersistencePlugin uses getBytes/setBytes to persist the
   59    * serializable objects associated with the timer.
   60    *
   61    * @author Thomas.Diesler@jboss.org
   62    * @author Dimitris.Andreadis@jboss.org
   63    * @version $Revision: 62320 $
   64    * @since 23-Sep-2004
   65    */
   66   public class GeneralPurposeDatabasePersistencePlugin implements DatabasePersistencePluginExt
   67   {
   68      /** logging support */
   69      private static Logger log = Logger.getLogger(GeneralPurposeDatabasePersistencePlugin.class);
   70   
   71      /** The mbean server */
   72      protected MBeanServer server;
   73      
   74      /** The service attributes */
   75      protected ObjectName dataSourceName;
   76   
   77      /** The timers table name */
   78      protected String tableName;
   79      
   80      /** The data source the timers will be persisted to */
   81      protected DataSource ds;
   82      
   83      /** datasource meta data */
   84      protected ObjectName metaDataName;
   85      
   86      // default JDBC type code for binary data
   87      private int binarySqlType;
   88   
   89      /**
   90       * Initialize the plugin and set also the timers tablename
   91       */
   92      public void init(MBeanServer server, ObjectName dataSource, String tableName) throws SQLException
   93      {
   94         if (tableName == null)
   95            throw new IllegalArgumentException("Timers tableName is null");
   96         if (tableName.length() == 0)
   97            throw new IllegalArgumentException("Timers tableName is empty");
   98   
   99         this.tableName = tableName;
  100         init(server, dataSource);
  101      }
  102      
  103      /** Initialize the plugin */
  104      public void init(MBeanServer server, ObjectName dataSourceName) throws SQLException
  105      {
  106         this.server = server;
  107         this.dataSourceName = dataSourceName;
  108   
  109         // Get the DataSource from JNDI
  110         try
  111         {
  112            String dsJndiTx = (String)server.getAttribute(dataSourceName, "BindName");
  113            ds = (DataSource)new InitialContext().lookup(dsJndiTx);
  114         }
  115         catch (Exception e)
  116         {
  117            throw new SQLException("Failed to lookup data source: " + dataSourceName);
  118         }
  119   
  120         // Get the DataSource meta data
  121         String dsName = dataSourceName.getKeyProperty("name");
  122         metaDataName = ObjectNameFactory.create("jboss.jdbc:datasource=" + dsName + ",service=metadata");
  123         if (this.server.isRegistered(metaDataName) == false)
  124            throw new IllegalStateException("Cannot find datasource meta data: " + metaDataName);
  125      }
  126   
  127      /** Create the timer table if it does not exist already */
  128      public void createTableIfNotExists()
  129              throws SQLException
  130      {
  131         Connection con = null;
  132         Statement st = null;
  133         try
  134         {        
  135            JDBCTypeMappingMetaData typeMapping = (JDBCTypeMappingMetaData)server.getAttribute(metaDataName, "TypeMappingMetaData");
  136            if (typeMapping == null)
  137               throw new IllegalStateException("Cannot obtain type mapping from: " + metaDataName);
  138   
  139            JDBCMappingMetaData objectMetaData = typeMapping.getTypeMappingMetaData(Object.class);
  140            binarySqlType = objectMetaData.getJdbcType();
  141            
  142            if (!SQLUtil.tableExists(getTableName(), ds))
  143            {
  144               con = ds.getConnection();
  145   
  146               String dateType = typeMapping.getTypeMappingMetaData(Timestamp.class).getSqlType();
  147               String longType = typeMapping.getTypeMappingMetaData(Long.class).getSqlType();
  148               String objectType = objectMetaData.getSqlType();
  149   
  150               // The create table DDL
  151               StringBuffer createTableDDL = new StringBuffer("create table " + getTableName() + " (" +
  152                       " " + getColumnTimerID() + " varchar(80) not null," +
  153                       " " + getColumnTargetID() + " varchar(250) not null," +
  154                       " " + getColumnInitialDate() + " " + dateType + " not null," +
  155                       " " + getColumnTimerInterval() + " " + longType + "," +
  156                       " " + getColumnInstancePK() + " " + objectType + "," +
  157                       " " + getColumnInfo() + " " + objectType + ", ");
  158   
  159               // Add the primary key constraint using the pk-constraint-template
  160               JDBCFunctionMappingMetaData pkConstraint = typeMapping.getPkConstraintTemplate();
  161               String[] templateParams = new String[] {
  162                     getTableName() + "_PK",
  163                     getColumnTimerID() + ", " + getColumnTargetID()
  164                     };
  165               pkConstraint.getFunctionSql(templateParams, createTableDDL);
  166               
  167               // Complete the statement
  168               createTableDDL.append(" )");
  169   
  170               log.debug("Executing DDL: " + createTableDDL);
  171   
  172               st = con.createStatement();
  173               st.executeUpdate(createTableDDL.toString());
  174            }
  175         }
  176         catch (SQLException e)
  177         {
  178            throw e;
  179         }
  180         catch (Exception e)
  181         {
  182            log.error("Cannot create timer table", e);
  183         }
  184         finally
  185         {
  186            JDBCUtil.safeClose(st);
  187            JDBCUtil.safeClose(con);
  188         }
  189      }
  190   
  191      /** Insert a timer object */
  192      public void insertTimer(String timerId, TimedObjectId timedObjectId, Date initialExpiration, long intervalDuration, Serializable info)
  193              throws SQLException
  194      {
  195         Connection con = null;
  196         PreparedStatement st = null;
  197         try
  198         {
  199            con = ds.getConnection();
  200   
  201            String sql = "insert into " + getTableName() + " " +
  202                    "(" + getColumnTimerID() + "," + getColumnTargetID() + "," + getColumnInitialDate() + "," + getColumnTimerInterval() + "," + getColumnInstancePK() + "," + getColumnInfo() + ") " +
  203                    "values (?,?,?,?,?,?)";
  204            st = con.prepareStatement(sql);
  205   
  206            st.setString(1, timerId);
  207            st.setString(2, timedObjectId.toString());
  208            st.setTimestamp(3, new Timestamp(initialExpiration.getTime()));
  209            st.setLong(4, intervalDuration);
  210   
  211            byte[] bytes = serialize(timedObjectId.getInstancePk());
  212            if(bytes == null)
  213            {
  214               st.setNull(5, binarySqlType);
  215            }
  216            else
  217            {
  218               st.setBytes(5, bytes);
  219            }
  220   
  221            bytes = serialize(info);
  222            if(bytes == null)
  223            {
  224               st.setNull(6, binarySqlType);
  225            }
  226            else
  227            {
  228               st.setBytes(6, bytes);
  229            }
  230   
  231            int rows = st.executeUpdate();
  232            if (rows != 1)
  233               log.error("Unable to insert timer for: " + timedObjectId);
  234         }
  235         finally
  236         {
  237            JDBCUtil.safeClose(st);
  238            JDBCUtil.safeClose(con);
  239         }
  240      }
  241   
  242      /** Select a list of currently persisted timer handles
  243       * @return List<TimerHandleImpl>
  244       */
  245      public List selectTimers(ObjectName containerId) throws SQLException
  246      {
  247         Connection con = null;
  248         Statement st = null;
  249         ResultSet rs = null;
  250         try
  251         {
  252            con = ds.getConnection();
  253   
  254            List list = new ArrayList();
  255   
  256            st = con.createStatement();
  257            rs = st.executeQuery("select * from " + getTableName());
  258            while (rs.next())
  259            {
  260               String timerId = rs.getString(getColumnTimerID());
  261               TimedObjectId targetId = TimedObjectId.parse(rs.getString(getColumnTargetID()));
  262               
  263               // add this handle to the returned list, if a null containerId was used
  264               // or the containerId filter matches 
  265               if (containerId == null || containerId.equals(targetId.getContainerId()))
  266               {
  267                  Date initialDate = rs.getTimestamp(getColumnInitialDate());
  268                  long interval = rs.getLong(getColumnTimerInterval());
  269                  Serializable pKey = (Serializable)deserialize(rs.getBytes(getColumnInstancePK()));
  270                  Serializable info = null;
  271                  try
  272                  {
  273                     info = (Serializable)deserialize(rs.getBytes(getColumnInfo()));
  274                  }
  275                  catch (Exception e)
  276                  {
  277                     // may happen if listing all handles (containerId is null)
  278                     // with a stored custom info object coming from a scoped
  279                     // deployment.
  280                     log.warn("Cannot deserialize custom info object", e);
  281                  }
  282                  // is this really needed? targetId encapsulates pKey as well!
  283                  targetId = new TimedObjectId(targetId.getContainerId(), pKey);
  284                  TimerHandleImpl handle = new TimerHandleImpl(timerId, targetId, initialDate, interval, info);
  285                  list.add(handle);
  286               }
  287            }
  288   
  289            return list;
  290         }
  291         finally
  292         {
  293            JDBCUtil.safeClose(rs);
  294            JDBCUtil.safeClose(st);
  295            JDBCUtil.safeClose(con);
  296         }
  297      }
  298   
  299      /** Delete a timer. */
  300      public void deleteTimer(String timerId, TimedObjectId timedObjectId)
  301              throws SQLException
  302      {
  303         Connection con = null;
  304         PreparedStatement st = null;
  305         ResultSet rs = null;
  306   
  307         try
  308         {
  309            con = ds.getConnection();
  310   
  311            String sql = "delete from " + getTableName() + " where " + getColumnTimerID() + "=? and " + getColumnTargetID() + "=?";
  312            st = con.prepareStatement(sql);
  313   
  314            st.setString(1, timerId);
  315            st.setString(2, timedObjectId.toString());
  316   
  317            int rows = st.executeUpdate();
  318            
  319            // This appears when a timer is created & persisted inside a tx,
  320            // but then the tx is rolled back, at which point we go back
  321            // to remove the entry, but no entry is found.
  322            // Is this because we are "enlisting" the datasource in the tx, too?
  323            if (rows != 1)
  324            {
  325               log.debug("Unable to remove timer for: " + timerId);
  326            }
  327         }
  328         finally
  329         {
  330            JDBCUtil.safeClose(rs);
  331            JDBCUtil.safeClose(st);
  332            JDBCUtil.safeClose(con);
  333         }
  334      }
  335   
  336      /** Clear all persisted timers */
  337      public void clearTimers()
  338              throws SQLException
  339      {
  340         Connection con = null;
  341         PreparedStatement st = null;
  342         ResultSet rs = null;
  343         try
  344         {
  345            con = ds.getConnection();
  346            st = con.prepareStatement("delete from " + getTableName());
  347            st.executeUpdate();
  348         }
  349         finally
  350         {
  351            JDBCUtil.safeClose(rs);
  352            JDBCUtil.safeClose(st);
  353            JDBCUtil.safeClose(con);
  354         }
  355      }
  356   
  357      /** Get the timer table name */
  358      public String getTableName()
  359      {
  360         return tableName;
  361      }
  362   
  363      /** Get the timer ID column name */
  364      public String getColumnTimerID()
  365      {
  366         return "TIMERID";
  367      }
  368   
  369      /** Get the target ID column name */
  370      public String getColumnTargetID()
  371      {
  372         return "TARGETID";
  373      }
  374   
  375      /** Get the initial date column name */
  376      public String getColumnInitialDate()
  377      {
  378         return "INITIALDATE";
  379      }
  380   
  381      /** Get the timer interval column name */
  382      public String getColumnTimerInterval()
  383      {
  384         // Note 'INTERVAL' is a reserved word in MySQL
  385         return "TIMERINTERVAL";
  386      }
  387   
  388      /** Get the instance PK column name */
  389      public String getColumnInstancePK()
  390      {
  391         return "INSTANCEPK";
  392      }
  393   
  394      /** Get the info column name */
  395      public String getColumnInfo()
  396      {
  397         return "INFO";
  398      }
  399   
  400      /** Serialize an object */
  401      protected byte[] serialize(Object obj)
  402      {
  403         if (obj == null)
  404            return null;
  405   
  406         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
  407         try
  408         {
  409            ObjectOutputStream oos = new ObjectOutputStream(baos);
  410            oos.writeObject(obj);
  411            oos.close();
  412         }
  413         catch (IOException e)
  414         {
  415            log.error("Cannot serialize: " + obj, e);
  416         }
  417         return baos.toByteArray();
  418      }
  419   
  420      /** Deserialize an object */
  421      protected Object deserialize(byte[] bytes)
  422      {
  423         if (bytes == null)
  424            return null;
  425   
  426         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
  427         try
  428         {
  429            // Use an ObjectInputStream that instantiates objects
  430            // using the Thread Context ClassLoader (TCL)
  431            ObjectInputStream oos = new MarshalledValueInputStream(bais);
  432            return oos.readObject();
  433         }
  434         catch (Exception e)
  435         {
  436            log.error("Cannot deserialize", e);
  437            return null;
  438         }
  439      }
  440   
  441      /** Deserialize an object */
  442      protected Object deserialize(InputStream input)
  443      {
  444   
  445         if (input == null)
  446            return null;
  447   
  448         byte[] barr = new byte[1024];
  449         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
  450         try
  451         {
  452            for (int b = 0; (b = input.read(barr)) > 0;)
  453            {
  454               baos.write(barr, 0, b);
  455            }
  456            return deserialize(baos.toByteArray());
  457         }
  458         catch (Exception e)
  459         {
  460            log.error("Cannot deserialize", e);
  461            return null;
  462         }
  463      }
  464   }
  465   

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