View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  
22  package org.dbunit;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.Reader;
28  import java.nio.charset.StandardCharsets;
29  import java.nio.file.Files;
30  import java.sql.Connection;
31  import java.sql.DriverManager;
32  import java.sql.Statement;
33  import java.util.Properties;
34  import java.util.concurrent.Callable;
35  
36  import org.dbunit.database.DatabaseConfig;
37  import org.dbunit.database.DatabaseConnection;
38  import org.dbunit.database.IDatabaseConnection;
39  import org.dbunit.dataset.IDataSet;
40  import org.dbunit.dataset.xml.XmlDataSet;
41  import org.dbunit.testutil.TestUtils;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * @author Manuel Laflamme
47   * @version $Revision$
48   * @since Feb 18, 2002
49   */
50  public class DatabaseEnvironment
51  {
52      private static final String DBUNIT_PROPERTIES_FILENAME =
53              "dbunit.properties";
54  
55      private static final Logger logger =
56              LoggerFactory.getLogger(DatabaseEnvironment.class);
57  
58      private static DatabaseEnvironment INSTANCE = null;
59  
60      private DatabaseProfile _profile = null;
61      private IDatabaseConnection _connection = null;
62      private IDataSet _dataSet = null;
63      private IDatabaseTester _databaseTester = null;
64  
65      /**
66       * Optional "dbunit.properties" is loaded (if present and the
67       * "dbunit.profile" property is null) and merged with System properties and
68       * the whole set is returned.
69       * <p>
70       * If absent (which is the normal scenario), only System properties are
71       * returned.
72       * <p>
73       * "dbunit.properties" is useful for environment which make it difficult if
74       * not impossible to use Maven's profiles. Example is IntelliJ IDEA which
75       * when calling Junit tests, bypass Maven completely. Since profiles are not
76       * used, database configuration is not properly set and tests fail.
77       * "dbunit.properties" contains the missing properties which a profile would
78       * set.
79       * <p>
80       * Following is a few properties as an example of the content of
81       * "dbunit.properties":
82       * <pre>
83       * database.profile=h2
84       * dbunit.profile.driverClass=org.hsqldb.jdbcDriver
85       * dbunit.profile.url=jdbc:hsqldb:mem:.
86       * </pre>
87       * <p>
88       * Simply create "dbunit.properties" under "src/test/resources".
89       *
90       * @return Merged DbUnit and System properties.
91       * @throws IOException
92       *             Thrown if an error occurs when attempting to read
93       *             "dbunit.properties".
94       */
95      protected static Properties getProperties() throws IOException
96      {
97          final Properties properties = System.getProperties();
98  
99          final String profileName =
100                 properties.getProperty(DatabaseProfile.DATABASE_PROFILE);
101 
102         // only load from file if not already set
103         if (profileName == null)
104         {
105             loadDbunitPropertiesFromFile(properties);
106         }
107 
108         return properties;
109     }
110 
111     protected static void loadDbunitPropertiesFromFile(
112             final Properties properties) throws IOException
113     {
114         final InputStream inputStream =
115                 DatabaseEnvironment.class.getClassLoader()
116                         .getResourceAsStream(DBUNIT_PROPERTIES_FILENAME);
117         if (inputStream != null)
118         {
119             logger.info("Loaded properties from file '{}'",
120                     DBUNIT_PROPERTIES_FILENAME);
121             properties.load(inputStream);
122             inputStream.close();
123         }
124     }
125 
126     public static DatabaseEnvironment getInstance() throws Exception
127     {
128         if (INSTANCE == null)
129         {
130             final DatabaseProfile profile =
131                     new DatabaseProfile(getProperties());
132 
133             final String activeProfile = profile.getActiveProfile();
134             final String profileName =
135                     (activeProfile == null) ? "hsqldb" : activeProfile;
136 
137             logger.info("getInstance: activeProfile={}", profileName);
138 
139             if (profileName.equals("hsqldb"))
140             {
141                 INSTANCE = new HypersonicEnvironment(profile);
142             } else if (profileName.equals("oracle"))
143             {
144                 INSTANCE = new OracleEnvironment(profile);
145             } else if (profileName.equals("oracle10"))
146             {
147                 INSTANCE = new Oracle10Environment(profile);
148             } else if (profileName.equals("postgresql"))
149             {
150                 INSTANCE = new PostgresqlEnvironment(profile);
151             } else if (profileName.equals("mysql"))
152             {
153                 INSTANCE = new MySqlEnvironment(profile);
154             } else if (profileName.equals("mariadb"))
155             {
156                 INSTANCE = new MariaDbEnvironment(profile);
157             } else if (profileName.equals("derby"))
158             {
159                 INSTANCE = new DerbyEnvironment(profile);
160             } else if (profileName.equals("h2"))
161             {
162                 INSTANCE = new H2Environment(profile);
163             } else if (profileName.equals("mssql"))
164             {
165                 INSTANCE = new MsSqlEnvironment(profile);
166             } else if (profileName.equals("db2"))
167             {
168                 INSTANCE = new Db2Environment(profile);
169             } else
170             {
171                 logger.warn("getInstance: activeProfile={} not known,"
172                         + " using generic profile", profileName);
173                 INSTANCE = new DatabaseEnvironment(profile);
174             }
175         }
176 
177         return INSTANCE;
178     }
179 
180     public DatabaseEnvironment(final DatabaseProfile profile,
181             final Callable<Void> preDdlFunction) throws Exception
182     {
183         if (null != preDdlFunction)
184         {
185             preDdlFunction.call();
186         }
187 
188         _profile = profile;
189         final File file = TestUtils.getFile("xml/dataSetTest.xml");
190         try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8))
191         {
192             _dataSet = new XmlDataSet(reader);
193         }
194         _databaseTester = new JdbcDatabaseTester(_profile.getDriverClass(),
195                 _profile.getConnectionUrl(), _profile.getUser(),
196                 _profile.getPassword(), _profile.getSchema());
197 
198         DdlExecutor.execute("sql/" + _profile.getProfileDdl(),
199                 getConnection().getConnection(),
200                 profile.getProfileMultilineSupport(), true);
201     }
202 
203     public DatabaseEnvironment(final DatabaseProfile profile) throws Exception
204     {
205         this(profile, null);
206     }
207 
208     public IDatabaseConnection getConnection() throws Exception
209     {
210         // First check if the current connection is still valid and open
211         // The connection may have been closed by a consumer
212         if (_connection != null && _connection.getConnection().isClosed())
213         {
214             // Reset the member so that a new connection will be created
215             _connection = null;
216         }
217 
218         if (_connection == null)
219         {
220             final String name = _profile.getDriverClass();
221             Class.forName(name);
222             final Connection connection =
223                     DriverManager.getConnection(_profile.getConnectionUrl(),
224                             _profile.getUser(), _profile.getPassword());
225             _connection =
226                     new DatabaseConnection(connection, _profile.getSchema());
227         }
228         return _connection;
229     }
230 
231     protected void setupDatabaseConfig(final DatabaseConfig config)
232     {
233         // Override in subclasses as necessary.
234     }
235 
236     public IDatabaseTester getDatabaseTester()
237     {
238         return _databaseTester;
239     }
240 
241     /**
242      * Deletes every row of each given table, so a test whose assertions depend on the table
243      * starting empty does not rely on whichever test class ran before it having cleaned up.
244      * Unlike a best-effort cleanup, a failure here propagates: a test that could not establish
245      * its starting state must not run on an unknown one.
246      *
247      * @param tableNames The names of the tables to empty.
248      * @throws Exception If emptying any of the tables fails.
249      */
250     public void deleteAllRows(final String... tableNames) throws Exception
251     {
252         try (Statement statement = getConnection().getConnection().createStatement())
253         {
254             for (final String tableName : tableNames)
255             {
256                 statement.execute("DELETE FROM " + tableName);
257             }
258         }
259     }
260 
261     public void closeConnection() throws Exception
262     {
263         if (_connection != null)
264         {
265             _connection.close();
266             _connection = null;
267         }
268     }
269 
270     public IDataSet getInitDataSet() throws Exception
271     {
272         return _dataSet;
273     }
274 
275     public DatabaseProfile getProfile() throws Exception
276     {
277         return _profile;
278     }
279 
280     public boolean support(final TestFeature feature)
281     {
282         final String[] unsupportedFeatures = _profile.getUnsupportedFeatures();
283         for (int i = 0; i < unsupportedFeatures.length; i++)
284         {
285             final String unsupportedFeature = unsupportedFeatures[i];
286             if (feature.toString().equals(unsupportedFeature))
287             {
288                 return false;
289             }
290         }
291 
292         return true;
293     }
294 
295     /**
296      * Returns the string converted as an identifier according to the metadata
297      * rules of the database environment. Most databases convert all metadata
298      * identifiers to uppercase. PostgreSQL converts identifiers to lowercase.
299      * MySQL preserves case.
300      *
301      * @param str
302      *            The identifier.
303      * @return The identifier converted according to database rules.
304      */
305     public String convertString(final String str)
306     {
307         return str == null ? null : str.toUpperCase();
308     }
309 
310     @Override
311     public String toString()
312     {
313         final StringBuilder sb = new StringBuilder();
314         sb.append(getClass().getName()).append("[");
315         sb.append("_profile=").append(_profile);
316         sb.append(", _connection=").append(_connection);
317         sb.append(", _dataSet=").append(_dataSet);
318         sb.append(", _databaseTester=").append(_databaseTester);
319         sb.append("]");
320         return sb.toString();
321     }
322 }