/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/jms/jdbc/JdbcManager.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jms.jdbc;

import com.caucho.config.ConfigException;
import com.caucho.config.types.Period;
import com.caucho.jdbc.JdbcMetaData;
import com.caucho.util.L10N;
import com.caucho.util.Log;

import javax.sql.DataSource;
import javax.annotation.PostConstruct;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;

/**
 * Manages the JDBC configuration.
 */
public class JdbcManager {
  static final Logger log = Log.open(JdbcManager.class);
  static final L10N L = new L10N(JdbcManager.class);
  
  private DataSource _dataSource;

  private String _messageTable = "resin_jms_message";
  
  private String _destinationTable = "resin_jms_destination";
  private String _destinationSequence;
  
  private String _consumerTable = "resin_jms_consumer";
  private String _consumerSequence;
  
  private String _blob;
  private String _longType;
  
  private String _tablespace;  // oracle tablespace for blobs

  private boolean _isTruncateBlob;

  private long _purgeInterval = 60000L;
  private long _pollInterval = -1;

  private JdbcMessage _jdbcMessage;

  private volatile boolean _isInit;

  public JdbcManager()
  {
  }

  /**
   * Sets the data source.
   */
  public void setDataSource(DataSource dataSource)
  {
    _dataSource = dataSource;
  }

  /**
   * Gets the data source.
   */
  public DataSource getDataSource()
  {
    return _dataSource;
  }

  /**
   * Returns the message table
   */
  public String getMessageTable()
  {
    return _messageTable;
  }

  /**
   * Sets the message table
   */
  public void setMessageTable(String tableName)
  {
    _messageTable = tableName;
  }

  /**
   * Returns the destination table
   */
  public String getDestinationTable()
  {
    return _destinationTable;
  }

  /**
   * Sets the destination table
   */
  public void setDestinationTable(String tableName)
  {
    _destinationTable = tableName;
  }

  /**
   * Returns the destination sequence
   */
  public String getDestinationSequence()
  {
    return _destinationSequence;
  }

  /**
   * Returns the consumer table
   */
  public String getConsumerTable()
  {
    return _consumerTable;
  }

  /**
   * Sets the consumer table
   */
  public void setConsumerTable(String tableName)
  {
    _consumerTable = tableName;
  }

  /**
   * Returns the consumer sequence
   */
  public String getConsumerSequence()
  {
    return _consumerSequence;
  }

  /**
   * Returns the meta-data.
   */
  public JdbcMetaData getMetaData()
  {
    return JdbcMetaData.create(_dataSource);
  }

  /**
   * Returns the blob type.
   */
  public String getBlob()
  {
    if (_blob == null)
      _blob = getMetaData().getBlobType();

    return _blob;
  }

  /**
   * Sets the oracle tablespace.
   */
  public void setTablespace(String tablespace)
  {
    _tablespace = tablespace;
  }

  /**
   * Gets the oracle tablespace
   */
  public String getTablespace()
  {
    return _tablespace;
  }

  /**
   * Returns the blob type.
   */
  public String getLongType()
  {
    if (_longType == null)
      _longType = getMetaData().getLongType();

    return _longType;
  }

  /**
   * Sets the purge interval.
   */
  public void setPurgeInterval(Period period)
  {
    _purgeInterval = period.getPeriod();
  }

  /**
   * Gets the purge interval.
   */
  public long getPurgeInterval()
  {
    return _purgeInterval;
  }

  /**
   * Sets the poll interval for destinations that have a listener, default
   * is to do no polling.
   */
  public void setPollInterval(Period pollInterval)
  {
    _pollInterval = pollInterval.getPeriod();
  }

  /**
   * Returns the poll interval.
   */
  public long getPollInterval()
  {
    return _pollInterval;
  }

  /**
   * Returns the JDBC message manager.
   */
  public JdbcMessage getJdbcMessage()
  {
    return _jdbcMessage;
  }

  /**
   * Return true if blobs need to be truncated before deletion.
   */
  public boolean isTruncateBlob()
  {
      return _isTruncateBlob;
  }

  /**
   * Initializes the JdbcManager
   */
  @PostConstruct
  public void init()
    throws ConfigException, SQLException
  {
    if (_isInit)
      return;
    _isInit = true;
    
    if (_dataSource == null)
      throw new ConfigException(L.l("JdbcManager requires a <data-source> element."));

    _jdbcMessage = new JdbcMessage(this);

    _jdbcMessage.init();

    initDestinationTable();
    initConsumerTable();
    
    _isTruncateBlob = getMetaData().isTruncateBlobBeforeDelete();
  }

  /**
   * Initializes the destination table.
   */
  protected void initDestinationTable()
    throws SQLException
  {
    Connection conn = _dataSource.getConnection();
    
    if (! getMetaData().supportsIdentity()) {
      if (! getMetaData().supportsSequences())
        throw new ConfigException(L.l("JdbcManager requires a datasource that supports either identity or sequences"));

      _destinationSequence = _destinationTable + "_cseq";
    }

    try {
      Statement stmt = conn.createStatement();
      String sql = "SELECT 1 FROM " + _destinationTable + " WHERE 1=0";

      try {
        ResultSet rs = stmt.executeQuery(sql);
        rs.next();
        rs.close();
        stmt.close();

        return;
      } catch (SQLException e) {
        log.finest(e.toString());
      }

      log.info(L.l("creating JMS destination table {0}", _destinationTable));

      String longType = getLongType();
      String identity = longType + " PRIMARY KEY";

      if (getMetaData().supportsIdentity())
        identity = getMetaData().createIdentitySQL(identity);

      sql = ("CREATE TABLE " + _destinationTable + " (" +
             "  id " + identity + "," +
             "  name VARCHAR(255)," +
             "  is_topic INTEGER" +
             ")");

      stmt.executeUpdate(sql);

      if (! getMetaData().supportsIdentity()) {
        _destinationSequence = _destinationTable + "_cseq";

        stmt.executeUpdate(getMetaData().createSequenceSQL(_destinationSequence, 1));
      }
    } finally {
      conn.close();
    }
  }

  /**
   * Initializes the consumer table.
   */
  protected void initConsumerTable()
    throws SQLException
  {
    if (! getMetaData().supportsIdentity())
      _consumerSequence = _consumerTable + "_cseq";
      
    Connection conn = _dataSource.getConnection();
    try {
      Statement stmt = conn.createStatement();
      String sql = "SELECT 1 FROM " + _consumerTable + " WHERE 1=0";

      try {
        ResultSet rs = stmt.executeQuery(sql);
        rs.next();
        rs.close();
        stmt.close();

        return;
      } catch (SQLException e) {
        log.finest(e.toString());
      }

      log.info(L.l("creating JMS consumer table {0}", _consumerTable));

      String longType = getLongType();
      String identity = longType + " PRIMARY KEY";

      if (getMetaData().supportsIdentity())
        identity = getMetaData().createIdentitySQL(identity);

      sql = ("CREATE TABLE " + _consumerTable + " (" +
             "  s_id " + identity + "," +
             "  queue " + longType + "," +
             "  client VARCHAR(255)," +
             "  name VARCHAR(255)," +
             "  expire " + longType + "," +
             "  read_id " + longType + "," +
             "  ack_id " + longType +
             ")");

      stmt.executeUpdate(sql);

      if (_consumerSequence != null)
        stmt.executeUpdate(getMetaData().createSequenceSQL(_consumerSequence, 1));
    } finally {
      conn.close();
    }
  }

  /**
   * Returns a hash code.
   */
  public int hashCode()
  {
    if (_dataSource == null)
      return 0;
    else
      return _dataSource.hashCode();
  }

  /**
   * Test for equality.
   */
  public boolean equals(Object o)
  {
    if (this == o)
      return true;
    else if (! (o instanceof JdbcManager))
      return false;

    JdbcManager manager = (JdbcManager) o;

    return _dataSource != null && _dataSource.equals(manager._dataSource);
  }
}

