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.util.Properties;
33  import java.util.concurrent.Callable;
34  
35  import org.dbunit.database.DatabaseConfig;
36  import org.dbunit.database.DatabaseConnection;
37  import org.dbunit.database.IDatabaseConnection;
38  import org.dbunit.dataset.IDataSet;
39  import org.dbunit.dataset.xml.XmlDataSet;
40  import org.dbunit.testutil.TestUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  /**
45   * @author Manuel Laflamme
46   * @version $Revision$
47   * @since Feb 18, 2002
48   */
49  public class DatabaseEnvironment
50  {
51      private static final String DBUNIT_PROPERTIES_FILENAME =
52              "dbunit.properties";
53  
54      private static final Logger logger =
55              LoggerFactory.getLogger(DatabaseEnvironment.class);
56  
57      private static DatabaseEnvironment INSTANCE = null;
58  
59      private DatabaseProfile _profile = null;
60      private IDatabaseConnection _connection = null;
61      private IDataSet _dataSet = null;
62      private IDatabaseTester _databaseTester = null;
63  
64      /**
65       * Optional "dbunit.properties" is loaded (if present and the
66       * "dbunit.profile" property is null) and merged with System properties and
67       * the whole set is returned.
68       * <p>
69       * If absent (which is the normal scenario), only System properties are
70       * returned.
71       * <p>
72       * "dbunit.properties" is useful for environment which make it difficult if
73       * not impossible to use Maven's profiles. Example is IntelliJ IDEA which
74       * when calling Junit tests, bypass Maven completely. Since profiles are not
75       * used, database configuration is not properly set and tests fail.
76       * "dbunit.properties" contains the missing properties which a profile would
77       * set.
78       * <p>
79       * Following is a few properties as an example of the content of
80       * "dbunit.properties":
81       * <pre>
82       * database.profile=h2
83       * dbunit.profile.driverClass=org.hsqldb.jdbcDriver
84       * dbunit.profile.url=jdbc:hsqldb:mem:.
85       * </pre>
86       * <p>
87       * Simply create "dbunit.properties" under "src/test/resources".
88       *
89       * @return Merged DbUnit and System properties.
90       * @throws IOException
91       *             Thrown if an error occurs when attempting to read
92       *             "dbunit.properties".
93       */
94      protected static Properties getProperties() throws IOException
95      {
96          final Properties properties = System.getProperties();
97  
98          final String profileName =
99                  properties.getProperty(DatabaseProfile.DATABASE_PROFILE);
100 
101         // only load from file if not already set
102         if (profileName == null)
103         {
104             loadDbunitPropertiesFromFile(properties);
105         }
106 
107         return properties;
108     }
109 
110     protected static void loadDbunitPropertiesFromFile(
111             final Properties properties) throws IOException
112     {
113         final InputStream inputStream =
114                 DatabaseEnvironment.class.getClassLoader()
115                         .getResourceAsStream(DBUNIT_PROPERTIES_FILENAME);
116         if (inputStream != null)
117         {
118             logger.info("Loaded properties from file '{}'",
119                     DBUNIT_PROPERTIES_FILENAME);
120             properties.load(inputStream);
121             inputStream.close();
122         }
123     }
124 
125     public static DatabaseEnvironment getInstance() throws Exception
126     {
127         if (INSTANCE == null)
128         {
129             final DatabaseProfile profile =
130                     new DatabaseProfile(getProperties());
131 
132             final String activeProfile = profile.getActiveProfile();
133             final String profileName =
134                     (activeProfile == null) ? "hsqldb" : activeProfile;
135 
136             logger.info("getInstance: activeProfile={}", profileName);
137 
138             if (profileName.equals("hsqldb"))
139             {
140                 INSTANCE = new HypersonicEnvironment(profile);
141             } else if (profileName.equals("oracle"))
142             {
143                 INSTANCE = new OracleEnvironment(profile);
144             } else if (profileName.equals("oracle10"))
145             {
146                 INSTANCE = new Oracle10Environment(profile);
147             } else if (profileName.equals("postgresql"))
148             {
149                 INSTANCE = new PostgresqlEnvironment(profile);
150             } else if (profileName.equals("mysql"))
151             {
152                 INSTANCE = new MySqlEnvironment(profile);
153             } else if (profileName.equals("mariadb"))
154             {
155                 INSTANCE = new MariaDbEnvironment(profile);
156             } else if (profileName.equals("derby"))
157             {
158                 INSTANCE = new DerbyEnvironment(profile);
159             } else if (profileName.equals("h2"))
160             {
161                 INSTANCE = new H2Environment(profile);
162             } else if (profileName.equals("mssql"))
163             {
164                 INSTANCE = new MsSqlEnvironment(profile);
165             } else if (profileName.equals("db2"))
166             {
167                 INSTANCE = new Db2Environment(profile);
168             } else
169             {
170                 logger.warn("getInstance: activeProfile={} not known,"
171                         + " using generic profile", profileName);
172                 INSTANCE = new DatabaseEnvironment(profile);
173             }
174         }
175 
176         return INSTANCE;
177     }
178 
179     public DatabaseEnvironment(final DatabaseProfile profile,
180             final Callable<Void> preDdlFunction) throws Exception
181     {
182         if (null != preDdlFunction)
183         {
184             preDdlFunction.call();
185         }
186 
187         _profile = profile;
188         final File file = TestUtils.getFile("xml/dataSetTest.xml");
189         try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8))
190         {
191             _dataSet = new XmlDataSet(reader);
192         }
193         _databaseTester = new JdbcDatabaseTester(_profile.getDriverClass(),
194                 _profile.getConnectionUrl(), _profile.getUser(),
195                 _profile.getPassword(), _profile.getSchema());
196 
197         DdlExecutor.execute("sql/" + _profile.getProfileDdl(),
198                 getConnection().getConnection(),
199                 profile.getProfileMultilineSupport(), true);
200     }
201 
202     public DatabaseEnvironment(final DatabaseProfile profile) throws Exception
203     {
204         this(profile, null);
205     }
206 
207     public IDatabaseConnection getConnection() throws Exception
208     {
209         // First check if the current connection is still valid and open
210         // The connection may have been closed by a consumer
211         if (_connection != null && _connection.getConnection().isClosed())
212         {
213             // Reset the member so that a new connection will be created
214             _connection = null;
215         }
216 
217         if (_connection == null)
218         {
219             final String name = _profile.getDriverClass();
220             Class.forName(name);
221             final Connection connection =
222                     DriverManager.getConnection(_profile.getConnectionUrl(),
223                             _profile.getUser(), _profile.getPassword());
224             _connection =
225                     new DatabaseConnection(connection, _profile.getSchema());
226         }
227         return _connection;
228     }
229 
230     protected void setupDatabaseConfig(final DatabaseConfig config)
231     {
232         // Override in subclasses as necessary.
233     }
234 
235     public IDatabaseTester getDatabaseTester()
236     {
237         return _databaseTester;
238     }
239 
240     public void closeConnection() throws Exception
241     {
242         if (_connection != null)
243         {
244             _connection.close();
245             _connection = null;
246         }
247     }
248 
249     public IDataSet getInitDataSet() throws Exception
250     {
251         return _dataSet;
252     }
253 
254     public DatabaseProfile getProfile() throws Exception
255     {
256         return _profile;
257     }
258 
259     public boolean support(final TestFeature feature)
260     {
261         final String[] unsupportedFeatures = _profile.getUnsupportedFeatures();
262         for (int i = 0; i < unsupportedFeatures.length; i++)
263         {
264             final String unsupportedFeature = unsupportedFeatures[i];
265             if (feature.toString().equals(unsupportedFeature))
266             {
267                 return false;
268             }
269         }
270 
271         return true;
272     }
273 
274     /**
275      * Returns the string converted as an identifier according to the metadata
276      * rules of the database environment. Most databases convert all metadata
277      * identifiers to uppercase. PostgreSQL converts identifiers to lowercase.
278      * MySQL preserves case.
279      *
280      * @param str
281      *            The identifier.
282      * @return The identifier converted according to database rules.
283      */
284     public String convertString(final String str)
285     {
286         return str == null ? null : str.toUpperCase();
287     }
288 
289     @Override
290     public String toString()
291     {
292         final StringBuilder sb = new StringBuilder();
293         sb.append(getClass().getName()).append("[");
294         sb.append("_profile=").append(_profile);
295         sb.append(", _connection=").append(_connection);
296         sb.append(", _dataSet=").append(_dataSet);
297         sb.append(", _databaseTester=").append(_databaseTester);
298         sb.append("]");
299         return sb.toString();
300     }
301 }