DatabaseResultListener.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 11k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    DatabaseResultListener.java
  18.  *    Copyright (C) 1999 Len Trigg
  19.  *
  20.  */
  21. package weka.experiment;
  22. import java.io.*;
  23. import java.util.*;
  24. import java.sql.*;
  25. import java.net.*;
  26. import weka.core.FastVector;
  27. /**
  28.  * DatabaseResultListener takes the results from a ResultProducer
  29.  * and submits them to a central database.
  30.  *
  31.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  32.  * @version $Revision: 1.7 $
  33.  */
  34. public class DatabaseResultListener extends DatabaseUtils
  35.   implements ResultListener  {
  36.   /** The ResultProducer to listen to */
  37.   protected ResultProducer m_ResultProducer;
  38.   
  39.   /** The name of the current results table */
  40.   protected String m_ResultsTableName;
  41.   /** True if debugging output should be printed */
  42.   protected boolean m_Debug = true;
  43.   /** Holds the name of the key field to cache upon, or null if no caching */
  44.   protected String m_CacheKeyName = "";
  45.   /** Stores the index of the key column holding the cache key data */
  46.   protected int m_CacheKeyIndex;
  47.   /** Stores the key for which the cache is valid */
  48.   protected Object [] m_CacheKey;
  49.   /** Stores the cached values */
  50.   protected FastVector m_Cache = new FastVector();
  51.   /**
  52.    * Returns a string describing this result listener
  53.    * @return a description of the result listener suitable for
  54.    * displaying in the explorer/experimenter gui
  55.    */
  56.   public String globalInfo() {
  57.     return "Takes results from a result producer and sends them to a "
  58.       +"database.";
  59.   }
  60.   /**
  61.    * Sets up the database drivers
  62.    *
  63.    * @exception Exception if an error occurs
  64.    */
  65.   public DatabaseResultListener() throws Exception {
  66.     super();
  67.   }
  68.   /**
  69.    * Prepare for the results to be received.
  70.    *
  71.    * @param rp the ResultProducer that will generate the results
  72.    * @exception Exception if an error occurs during preprocessing.
  73.    */
  74.   public void preProcess(ResultProducer rp) throws Exception {
  75.     m_ResultProducer = rp;
  76.     // Connect to the database and find out what table corresponds to this
  77.     //   ResultProducer
  78.     updateResultsTableName(m_ResultProducer);
  79.   }
  80.   
  81.   /**
  82.    * Perform any postprocessing. When this method is called, it indicates
  83.    * that no more results will be sent that need to be grouped together
  84.    * in any way.
  85.    *
  86.    * @param rp the ResultProducer that generated the results
  87.    * @exception Exception if an error occurs
  88.    */
  89.   public void postProcess(ResultProducer rp) throws Exception {
  90.     if (m_ResultProducer != rp) {
  91.       throw new Error("Unrecognized ResultProducer calling postProcess!!");
  92.     }
  93.     disconnectFromDatabase();
  94.   }
  95.   
  96.   /**
  97.    * Determines if there are any constraints (imposed by the
  98.    * destination) on any additional measures produced by
  99.    * resultProducers. Null should be returned if there are NO
  100.    * constraints, otherwise a list of column names should be
  101.    * returned as an array of Strings. In the case of
  102.    * DatabaseResultListener, the structure of an existing database
  103.    * will impose constraints.
  104.    * @param rp the ResultProducer to which the constraints will apply
  105.    * @return an array of column names to which resutltProducer's
  106.    * results will be restricted.
  107.    * @exception Exception if an error occurs.
  108.    */
  109.   public String [] determineColumnConstraints(ResultProducer rp) 
  110.     throws Exception {
  111.     FastVector cNames = new FastVector();
  112.     updateResultsTableName(rp);
  113.     DatabaseMetaData dbmd = m_Connection.getMetaData();
  114.     // gets a result set where each row is info on a column
  115.     ResultSet rs = dbmd.getColumns(null, null, m_ResultsTableName, null);
  116.     boolean tableExists=false;
  117.     int numColumns = 0;
  118.     while (rs.next()) {
  119.       tableExists = true;
  120.       // column four contains the column name
  121.       if (rs.getString(4).startsWith("measure")) {
  122. numColumns++;
  123. cNames.addElement(rs.getString(4));
  124.       }
  125.     }
  126.     // no constraints on any additional measures if the table does not exist
  127.     if (!tableExists) {
  128.       return null;
  129.     }
  130.     // a zero element array indicates maximum constraint
  131.     String [] columnNames = new String [numColumns];
  132.     for (int i=0;i<numColumns;i++) {
  133.       columnNames[i] = (String)(cNames.elementAt(i));
  134.     }
  135.     return columnNames;
  136.   }
  137.   /**
  138.    * Submit the result to the appropriate table of the database
  139.    *
  140.    * @param rp the ResultProducer that generated the result
  141.    * @param key The key for the results.
  142.    * @param result The actual results.
  143.    * @exception Exception if the result couldn't be sent to the database
  144.    */
  145.   public void acceptResult(ResultProducer rp, Object[] key, Object[] result) 
  146.     throws Exception {
  147.     if (m_ResultProducer != rp) {
  148.       throw new Error("Unrecognized ResultProducer calling acceptResult!!");
  149.     }
  150.     // null result could occur from a chain of doRunKeys calls
  151.     if (result != null) {
  152.       putResultInTable(m_ResultsTableName, rp, key, result);      
  153.     }
  154.   }
  155.   /**
  156.    * Always says a result is required. If this is the first call,
  157.    * prints out the header for the Database output.
  158.    *
  159.    * @param rp the ResultProducer wanting to generate the result
  160.    * @param key The key for which a result may be needed.
  161.    * @return true if the result should be calculated.
  162.    * @exception Exception if the database couldn't be queried
  163.    */
  164.   public boolean isResultRequired(ResultProducer rp, Object[] key)
  165.     throws Exception {
  166.     
  167.     if (m_ResultProducer != rp) {
  168.       throw new Error("Unrecognized ResultProducer calling isResultRequired!");
  169.     }
  170.     if (m_Debug) {
  171.       System.err.print("Is result required...");
  172.       for (int i = 0; i < key.length; i++) {
  173. System.err.print(" " + key[i]);
  174.       }
  175.       System.err.flush();
  176.     }
  177.     boolean retval = false;
  178.     // Check the key cache first
  179.     if (!m_CacheKeyName.equals("")) {
  180.       if (!isCacheValid(key)) {
  181. loadCache(rp, key);
  182.       }
  183.       retval = !isKeyInCache(rp, key);
  184.     } else {
  185.       // Ask whether the results are needed
  186.       retval = !isKeyInTable(m_ResultsTableName,
  187.      rp, key);
  188.     }
  189.     
  190.     if (m_Debug) {
  191.       System.err.println(" ..." + (retval ? "required" : "not required")
  192.  + (m_CacheKeyName.equals("") ? "" : " (cache)"));
  193.       System.err.flush();
  194.     }
  195.     return retval;
  196.   }
  197.   
  198.   /**
  199.    * Determines the table name that results will be inserted into. If
  200.    * required: a connection will be opened, an experiment index table created,
  201.    * and the results table created.
  202.    *
  203.    * @param rp the ResultProducer
  204.    * @exception Exception if an error occurs
  205.    */
  206.   protected void updateResultsTableName(ResultProducer rp) throws Exception {
  207.     if (!isConnected()) {
  208.       connectToDatabase();
  209.     }
  210.     if (!experimentIndexExists()) {
  211.       createExperimentIndex();
  212.     }
  213.     String tableName = getResultsTableName(rp);
  214.     if (tableName == null) {
  215.       tableName = createExperimentIndexEntry(rp);
  216.     }
  217.     if (!tableExists(tableName)) {
  218.       createResultsTable(rp, tableName);
  219.     }
  220.     m_ResultsTableName = tableName;
  221.   }
  222.   /**
  223.    * Returns the tip text for this property
  224.    * @return tip text for this property suitable for
  225.    * displaying in the explorer/experimenter gui
  226.    */
  227.   public String cacheKeyNameTipText() {
  228.     return "Set the name of the key field by which to cache.";
  229.   }
  230.   
  231.   /**
  232.    * Get the value of CacheKeyName.
  233.    *
  234.    * @return Value of CacheKeyName.
  235.    */
  236.   public String getCacheKeyName() {
  237.     
  238.     return m_CacheKeyName;
  239.   }
  240.   
  241.   /**
  242.    * Set the value of CacheKeyName.
  243.    *
  244.    * @param newCacheKeyName Value to assign to CacheKeyName.
  245.    */
  246.   public void setCacheKeyName(String newCacheKeyName) {
  247.     
  248.     m_CacheKeyName = newCacheKeyName;
  249.   }
  250.   
  251.   /**
  252.    * Checks whether the current cache contents are valid for the supplied
  253.    * key.
  254.    *
  255.    * @param key the results key
  256.    * @return true if the cache contents are valid for the key given
  257.    */
  258.   protected boolean isCacheValid(Object []key) {
  259.     if (m_CacheKey == null) {
  260.       return false;
  261.     }
  262.     if (m_CacheKey.length != key.length) {
  263.       return false;
  264.     }
  265.     for (int i = 0; i < key.length; i++) {
  266.       if ((i != m_CacheKeyIndex) && (!m_CacheKey[i].equals(key[i]))) {
  267. return false;
  268.       }
  269.     }
  270.     return true;
  271.   }
  272.   /**
  273.    * Returns true if the supplied key is in the key cache (and thus
  274.    * we do not need to execute a database query).
  275.    *
  276.    * @param rp the ResultProducer the key belongs to.
  277.    * @param key the result key
  278.    * @return true if the key is in the key cache
  279.    * @exception Exception if an error occurs
  280.    */
  281.   protected boolean isKeyInCache(ResultProducer rp, Object[] key)
  282.     throws Exception {
  283.     for (int i = 0; i < m_Cache.size(); i++) {
  284.       if (m_Cache.elementAt(i).equals(key[m_CacheKeyIndex])) {
  285. return true;
  286.       }
  287.     }
  288.     return false;
  289.   }
  290.   
  291.   /**
  292.    * Executes a database query to fill the key cache
  293.    *
  294.    * @param rp the ResultProducer the key belongs to
  295.    * @param key the key
  296.    * @exception Exception if an error occurs
  297.    */
  298.   protected void loadCache(ResultProducer rp, Object[] key)
  299.     throws Exception {
  300.     System.err.print(" (updating cache)"); System.err.flush();
  301.     m_Cache.removeAllElements();
  302.     m_CacheKey = null;
  303.     String query = "SELECT Key_" + m_CacheKeyName
  304.       + " FROM " + m_ResultsTableName;
  305.     String [] keyNames = rp.getKeyNames();
  306.     if (keyNames.length != key.length) {
  307.       throw new Exception("Key names and key values of different lengths");
  308.     }
  309.     m_CacheKeyIndex = -1;
  310.     for (int i = 0; i < keyNames.length; i++) {
  311.       if (keyNames[i].equalsIgnoreCase(m_CacheKeyName)) {
  312. m_CacheKeyIndex = i;
  313. break;
  314.       }
  315.     }
  316.     if (m_CacheKeyIndex == -1) {
  317.       throw new Exception("No key field named " + m_CacheKeyName
  318.   + " (as specified for caching)");
  319.     }
  320.     boolean first = true;
  321.     for (int i = 0; i < key.length; i++) {
  322.       if ((key[i] != null) && (i != m_CacheKeyIndex)) {
  323. if (first) {
  324.   query += " WHERE ";
  325.   first = false;
  326. } else {
  327.   query += " AND ";
  328. }
  329. query += "Key_" + keyNames[i] + '=';
  330. if (key[i] instanceof String) {
  331.   query += ''' + key[i].toString() + ''';
  332. } else {
  333.   query += key[i].toString();
  334. }
  335.       }
  336.     }
  337.     if (execute(query)) {
  338.       ResultSet rs = getResultSet();
  339.       while (rs.next()) {
  340. String keyVal = rs.getString(1);
  341. if (!rs.wasNull()) {
  342.   m_Cache.addElement(keyVal);
  343. }
  344.       }
  345.       rs.close();
  346.     }
  347.     m_CacheKey = (Object [])key.clone();
  348.   }
  349.   
  350. } // DatabaseResultListener