View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.database;
17  
18  import james.SimSystem;
19  import james.core.data.DBConnectionData;
20  
21  import java.sql.Connection;
22  import java.sql.DriverManager;
23  import java.sql.ResultSet;
24  import java.sql.SQLException;
25  import java.util.Properties;
26  import java.util.logging.Level;
27  
28  import org.hibernate.cfg.Configuration;
29  
30  import p3j.database.hibernate.P3MDatabase;
31  import p3j.misc.Misc;
32  import p3j.misc.gui.GUI;
33  
34  /**
35   * A factory for the database layer. Not implemented properly yet.
36   * 
37   * Created on January 11, 2007
38   * 
39   * @author Christina Bohk
40   * @author Roland Ewald
41   * 
42   */
43  public final class DatabaseFactory {
44  
45    /** The hibernate property to read out the dialect that is used. */
46    private static final String HIBERNATE_DIALECT_PROPERTY = "dialect";
47  
48    /** The MySQL dialect supported for specific optimizations. */
49    private static final String HIBERNATE_MYSQL_DIALECT = "org.hibernate.dialect.MySQL5Dialect";
50  
51    /**
52     * The name of the index that is manually created for the 'matrices' table in
53     * case a MySQL database is used.
54     */
55    private static final String MYSQL_MATRIX_INDEX_NAME = "hash_index";
56  
57    /**
58     * This class should not be instantiated.
59     */
60    private DatabaseFactory() {
61    }
62  
63    /** Implementing singleton pattern. */
64    private static IP3MDatabase sqlDatabase;
65  
66    /** Connection data to be used. */
67    private static DBConnectionData dbConnData = Misc.DEFAULT_DB_CONN;
68  
69    /**
70     * Get database interface.
71     * 
72     * @return database interface
73     */
74    public static IP3MDatabase getDatabaseSingleton() {
75      if (sqlDatabase == null) {
76        sqlDatabase = createDatabase();
77      }
78      return sqlDatabase;
79    }
80  
81    /**
82     * Creates a new Database object.
83     * 
84     * @return the newly created database interface object
85     */
86    public static IP3MDatabase createDatabase() {
87      P3MDatabase database = new P3MDatabase();
88      database.init(dbConnData);
89      try {
90        database.open();
91        attemptDBSpecificOptimizations(database.getConfig(), dbConnData);
92      } catch (Exception ex) {
93        SimSystem.report(ex);
94      }
95      return database;
96    }
97  
98    /**
99     * Attempt some db-specific optimizations that cannot be easily covered by
100    * (the used version of) hibernate. Establishes a JDBC connection with the
101    * driver that is also used by hibernate and executes some raw SQL statements,
102    * e.g. to create multi-column indices.
103    * 
104    * @param config
105    *          the hibernate configuration
106    * @param connData
107    *          the connection data
108    */
109   private static void attemptDBSpecificOptimizations(Configuration config,
110       DBConnectionData connData) {
111     if (config.getProperty(HIBERNATE_DIALECT_PROPERTY).equals(
112         HIBERNATE_MYSQL_DIALECT)) {
113       createMatrixIndex(connData);
114     }
115   }
116 
117   /**
118    * Creates an index on the matrix table of a MySQL database schema (unless it
119    * already exists). The index is defined on the id (pk) and the hash code, to
120    * make retrieving matrices with certain hash codes faster.
121    * 
122    * @param connData
123    *          the database connection data
124    */
125   private static void createMatrixIndex(DBConnectionData connData) {
126 
127     Properties connectionProps = new Properties();
128     connectionProps.put("user", connData.getUser());
129     connectionProps.put("password", connData.getPassword());
130 
131     try (Connection conn = DriverManager.getConnection(dbConnData.getUrl(),
132         connectionProps)) {
133       ResultSet rs = conn.createStatement().executeQuery(
134           "SHOW INDEX FROM matrices WHERE KEY_NAME = '"
135               + MYSQL_MATRIX_INDEX_NAME + "'");
136       if (!rs.first()) {
137         SimSystem.report(Level.INFO, "Attempting to create index '"
138             + MYSQL_MATRIX_INDEX_NAME + "'");
139         conn.createStatement()
140             .executeUpdate(
141                 "CREATE INDEX " + MYSQL_MATRIX_INDEX_NAME
142                     + " ON matrices(ID,hash)");
143         SimSystem.report(Level.INFO, "Index creation successful.");
144       }
145     } catch (SQLException e) {
146       SimSystem.report(e);
147     }
148   }
149 
150   /**
151    * Creates a new Database object, given a certain hibernate configuration
152    * file.
153    * 
154    * @param hibernateConfigFile
155    *          the hibernate config file
156    * 
157    * @return the database
158    */
159   public static IP3MDatabase createDatabase(String hibernateConfigFile) {
160     P3MDatabase.setHibernateConfigFile(hibernateConfigFile);
161     return createDatabase();
162   }
163 
164   /**
165    * Gets the DB connection data.
166    * 
167    * @return the DB connection data
168    */
169   public static DBConnectionData getDbConnData() {
170     return dbConnData;
171   }
172 
173   /**
174    * Sets the DB connection data.
175    * 
176    * @param dbConnData
177    *          the new DB connection data
178    */
179   public static void setDbConnData(DBConnectionData dbConnData) {
180     try {
181       reset();
182     } catch (Exception ex) {
183       GUI.printErrorMessage("Error closing database connection", ex);
184     }
185     DatabaseFactory.dbConnData = dbConnData;
186   }
187 
188   /**
189    * Reset.
190    */
191   public static void reset() {
192     if (sqlDatabase != null) {
193       sqlDatabase.close();
194     }
195     sqlDatabase = null;
196   }
197 
198 }