View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.common.database;
22  
23  import org.apache.commons.pool.impl.GenericObjectPool;
24  import org.waarp.common.database.properties.DbProperties;
25  import org.waarp.common.database.properties.H2Properties;
26  import org.waarp.common.database.properties.MariaDBProperties;
27  import org.waarp.common.database.properties.MySQLProperties;
28  import org.waarp.common.database.properties.OracleProperties;
29  import org.waarp.common.database.properties.PostgreSQLProperties;
30  import org.waarp.common.logging.WaarpLogger;
31  import org.waarp.common.logging.WaarpLoggerFactory;
32  import org.waarp.common.utility.SystemPropertyUtil;
33  import org.waarp.common.utility.Version;
34  
35  import java.sql.Connection;
36  import java.sql.SQLException;
37  
38  /**
39   * A singleton wrapper of Datasource to get database connection object
40   */
41  public class ConnectionFactory {
42  
43    /**
44     * Internal Logger
45     */
46    private static final WaarpLogger logger =
47        WaarpLoggerFactory.getLogger(ConnectionFactory.class);
48  
49    /**
50     * The singleton instance
51     */
52    private static ConnectionFactory instance;
53  
54    /**
55     * DbModel
56     */
57    private final DbProperties properties;
58  
59    /**
60     * The connection url to be passed to the JDBC driver to establish a
61     * connection.
62     */
63    private final String server;
64  
65    /**
66     * The connection username to be passed to the JDBC driver to establish a
67     * connection.
68     */
69    private final String user;
70  
71    /**
72     * The connection password to be passed to the JDBC driver to establish a
73     * connection.
74     */
75    private final String password;
76  
77    private static final boolean AUTOCOMMIT = true;
78  
79    private static final boolean READONLY = false;
80  
81    private static final int MAX_CONNECTIONS_DEFAULT = 10;
82    private static final int MAX_IDLE_DEFAULT = 2;
83  
84    private int maxConnections = MAX_CONNECTIONS_DEFAULT;
85    private int maxIdle = MAX_IDLE_DEFAULT;
86  
87    /**
88     * The datasource for connection pooling
89     */
90    private final WaarpBasicDataSource ds;
91  
92    /**
93     * @param server the connection url of the database
94     *
95     * @return the DbProperties Object associated with the requested URL
96     *
97     * @throws UnsupportedOperationException if the requested database
98     *     is not supported
99     */
100   protected static DbProperties propertiesFor(final String server)
101       throws UnsupportedOperationException {
102     if (server.contains(H2Properties.getProtocolID())) {
103       return new H2Properties();
104     } else if (server.contains(MariaDBProperties.getProtocolID())) {
105       return new MariaDBProperties();
106     } else if (server.contains(MySQLProperties.getProtocolID())) {
107       return new MySQLProperties();
108     } else if (server.contains(OracleProperties.getProtocolID())) {
109       return new OracleProperties();
110     } else if (server.contains(PostgreSQLProperties.getProtocolID())) {
111       return new PostgreSQLProperties();
112     } else {
113       throw new UnsupportedOperationException(
114           "The requested database is not supported");
115     }
116   }
117 
118   /**
119    * Initialize the ConnectionFactory
120    *
121    * @throws UnsupportedOperationException if the requested database
122    *     is not supported
123    * @throws SQLException if a error occurs while connecting to the
124    *     database
125    */
126   public static void initialize(final String server, final String user,
127                                 final String password)
128       throws SQLException, UnsupportedOperationException {
129     if (instance == null) {
130       instance =
131           new ConnectionFactory(propertiesFor(server), server, user, password);
132     }
133   }
134 
135   /**
136    * @return the initialized ConnectionFactory or null if the
137    *     ConnectionFactory is not initialized
138    */
139   public static ConnectionFactory getInstance() {
140     return instance;
141   }
142 
143   /**
144    * @return a connection to the Database
145    *
146    * @throws SQLException if the ConnectionPool is not initialized or
147    *     if an error occurs while accessing the
148    *     database
149    */
150   public Connection getConnection() throws SQLException {
151     if (ds == null) {
152       throw new SQLException("ConnectionFactory is not inialized.");
153     }
154     try {
155       return ds.getConnection();
156     } catch (final SQLException e) {
157       throw new SQLException("Cannot access database", e);
158     }
159   }
160 
161   /**
162    * @param properties
163    * @param server
164    * @param user
165    * @param password
166    */
167   private ConnectionFactory(final DbProperties properties, final String server,
168                             final String user, final String password)
169       throws SQLException {
170     this.server = server;
171     this.user = user;
172     this.password = password;
173     this.properties = properties;
174 
175     if (Version.version().contains("-jre")) {
176       ds = new WaarpBasicDataSourceJava8();
177     } else {
178       ds = new WaarpBasicDataSourceJava6();
179     }
180 
181     // Initialize DataSource
182     ds.setDriverClassName(this.properties.getDriverName());
183     ds.setUrl(this.server);
184     ds.setUsername(this.user);
185     ds.setPassword(this.password);
186     ds.setDefaultAutoCommit(AUTOCOMMIT);
187     ds.setDefaultReadOnly(READONLY);
188     ds.setValidationQuery(this.properties.getValidationQuery());
189 
190     // Get maximum connections
191     Connection con = null;
192     // default
193     maxConnections = MAX_CONNECTIONS_DEFAULT;
194     try {
195       con = getConnection();
196       // Max of user value (if not set, default 10) and current maximum
197       // connections
198       maxConnections = Math.min(properties.getMaximumConnections(con),
199                                 SystemPropertyUtil.get(
200                                     SystemPropertyUtil.WAARP_DATABASE_CONNECTION_MAX,
201                                     MAX_CONNECTIONS_DEFAULT));
202     } catch (final SQLException e) {
203       logger.warn(
204           "Cannot fetch maximum connection allowed from database" + " : {}",
205           e.getMessage());
206     } finally {
207       if (con != null) {
208         con.close();
209       }
210     }
211     setMaxConnections(maxConnections);
212     logger.info("{}", this);
213   }
214 
215   /**
216    * Mainly for Junit
217    *
218    * @param max
219    */
220   public void setMaxConnections(final int max) {
221     // Minimal value should be 2
222     maxConnections = Math.max(max, MAX_IDLE_DEFAULT);
223     ds.setMaxActive(maxConnections);
224     maxIdle = Math.min(Math.max(maxConnections / 2, MAX_IDLE_DEFAULT * 2),
225                        maxConnections);
226     if (maxIdle < GenericObjectPool.DEFAULT_MAX_IDLE) {
227       ds.setMaxIdle(maxIdle);
228     }
229     ds.setInitialSize(MAX_IDLE_DEFAULT);
230   }
231 
232   /**
233    * Closes and releases all registered connections and connection pool
234    */
235   public void close() {
236     logger.info("Closing ConnectionFactory");
237     ds.close();
238   }
239 
240   /**
241    * @return the current configuration of the database maximum connections
242    */
243   public int getMaxConnections() {
244     return maxConnections;
245   }
246 
247   /**
248    * @return the JDBC connection url property
249    */
250   public String getServer() {
251     return server;
252   }
253 
254   /**
255    * @return the JDBC connection username property
256    */
257   public String getUser() {
258     return user;
259   }
260 
261   /**
262    * @return the JDBC connection password property
263    */
264   public String getPassword() {
265     return password;
266   }
267 
268   /**
269    * @return the associated DbProperties
270    */
271   public DbProperties getProperties() {
272     return properties;
273   }
274 
275   @Override
276   public String toString() {
277     final StringBuilder sb = new StringBuilder();
278     sb.append("Datapool:");
279     sb.append(server);
280     sb.append(", with user:");
281     sb.append(user);
282     sb.append(", AutoCommit:");
283     sb.append(AUTOCOMMIT);
284     sb.append(", DefaultReadOnly:");
285     sb.append(READONLY);
286     sb.append(", max connecions:");
287     sb.append(maxConnections);
288     return sb.toString();
289   }
290 }