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  package org.waarp.common.database.model;
21  
22  import org.h2.Driver;
23  import org.h2.jdbcx.JdbcConnectionPool;
24  import org.waarp.common.database.DbAdmin;
25  import org.waarp.common.database.DbConstant;
26  import org.waarp.common.database.DbPreparedStatement;
27  import org.waarp.common.database.DbRequest;
28  import org.waarp.common.database.DbSession;
29  import org.waarp.common.database.data.DbDataModel;
30  import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
31  import org.waarp.common.database.exception.WaarpDatabaseNoDataException;
32  import org.waarp.common.database.exception.WaarpDatabaseSqlException;
33  import org.waarp.common.logging.WaarpLogger;
34  import org.waarp.common.logging.WaarpLoggerFactory;
35  
36  import java.sql.Connection;
37  import java.sql.DriverManager;
38  import java.sql.SQLException;
39  import java.sql.Types;
40  
41  /**
42   * H2 Database Model implementation
43   */
44  public abstract class DbModelH2 extends DbModelAbstract {
45    /**
46     * Internal Logger
47     */
48    private static final WaarpLogger logger =
49        WaarpLoggerFactory.getLogger(DbModelH2.class);
50  
51    private static final DbType type = DbType.H2;
52  
53    protected static class DbTypeResolverH2
54        extends DbModelAbstract.DbTypeResolver {
55  
56      @Override
57      public final String getType(final int sqlType) {
58        return DBType.getType(sqlType);
59      }
60  
61      @Override
62      public final String getCreateTable() {
63        return "CREATE TABLE IF NOT EXISTS ";
64      }
65  
66      @Override
67      public final String getCreateIndex() {
68        return "CREATE INDEX IF NOT EXISTS ";
69      }
70  
71      @Override
72      public final DbType getDbType() {
73        return type;
74      }
75    }
76  
77    static {
78      dbTypeResolver = new DbTypeResolverH2();
79    }
80  
81    protected JdbcConnectionPool pool;
82  
83    @Override
84    public final DbType getDbType() {
85      return type;
86    }
87  
88    /**
89     * Create the object and initialize if necessary the driver
90     *
91     * @param dbserver
92     * @param dbuser
93     * @param dbpasswd
94     *
95     * @throws WaarpDatabaseNoConnectionException
96     */
97    protected DbModelH2(final String dbserver, final String dbuser,
98                        final String dbpasswd)
99        throws WaarpDatabaseNoConnectionException {
100     this();
101     pool = JdbcConnectionPool.create(dbserver, dbuser, dbpasswd);
102     pool.setMaxConnections(DbConstant.MAXCONNECTION);
103     pool.setLoginTimeout(DbConstant.DELAYMAXCONNECTION);
104     logger.info("Some info: MaxConn: {} LogTimeout: {}",
105                 pool.getMaxConnections(), pool.getLoginTimeout());
106   }
107 
108   /**
109    * Create the object and initialize if necessary the driver
110    *
111    * @throws WaarpDatabaseNoConnectionException
112    */
113   protected DbModelH2() throws WaarpDatabaseNoConnectionException {
114     if (DbModelFactory.classLoaded.contains(type.name())) {
115       return;
116     }
117     try {
118       DriverManager.registerDriver(new Driver());
119       DbModelFactory.classLoaded.add(type.name());
120     } catch (final SQLException e) {
121       // SQLException
122       logger.error(
123           "Cannot register Driver " + type.name() + ' ' + e.getMessage());
124       DbConstant.error(e);
125       throw new WaarpDatabaseNoConnectionException(
126           "Cannot load database drive:" + type.name(), e);
127     }
128   }
129 
130   @Override
131   public final void releaseResources() {
132     if (pool != null) {
133       pool.dispose();
134     }
135     pool = null;
136   }
137 
138   @Override
139   public synchronized int currentNumberOfPooledConnections() {
140     if (pool != null) {
141       return pool.getActiveConnections();
142     }
143     return DbAdmin.getNbConnection();
144   }
145 
146   @Override
147   public final Connection getDbConnection(final String server,
148                                           final String user,
149                                           final String passwd)
150       throws SQLException {
151     synchronized (this) {
152       if (pool != null) {
153         try {
154           return pool.getConnection();
155         } catch (final SQLException e) {
156           // try to renew the pool
157           pool.dispose();
158           pool = JdbcConnectionPool.create(server, user, passwd);
159           pool.setMaxConnections(DbConstant.MAXCONNECTION);
160           pool.setLoginTimeout(DbConstant.DELAYMAXCONNECTION);
161           logger.info("Some info: MaxConn: {} LogTimeout: {}",
162                       pool.getMaxConnections(), pool.getLoginTimeout());
163           return pool.getConnection();
164         }
165       }
166     }
167     return super.getDbConnection(server, user, passwd);
168   }
169 
170   protected enum DBType {
171     CHAR(Types.CHAR, " CHAR(3) "),
172     VARCHAR(Types.VARCHAR, " VARCHAR(" + MAX_VARCHAR + ") "),
173     NVARCHAR(Types.NVARCHAR, " VARCHAR(" + MAX_KEY_VARCHAR + ") "),
174     LONGVARCHAR(Types.LONGVARCHAR, " VARCHAR(" + MAX_LONGVARCHAR + ") "),
175     BIT(Types.BIT, " BOOLEAN "), TINYINT(Types.TINYINT, " TINYINT "),
176     SMALLINT(Types.SMALLINT, " SMALLINT "), INTEGER(Types.INTEGER, " INTEGER "),
177     BIGINT(Types.BIGINT, " BIGINT "), REAL(Types.REAL, " REAL "),
178     DOUBLE(Types.DOUBLE, " DOUBLE "),
179     VARBINARY(Types.VARBINARY, " VARBINARY(" + (MAX_BINARY * 2) + ") "),
180     DATE(Types.DATE, " DATE "), TIMESTAMP(Types.TIMESTAMP, " TIMESTAMP(3) "),
181     CLOB(Types.CLOB, " CLOB "), BLOB(Types.BLOB, " BLOB ");
182 
183     public final int type;
184 
185     public final String constructor;
186 
187     DBType(final int type, final String constructor) {
188       this.type = type;
189       this.constructor = constructor;
190     }
191 
192     public static String getType(final int sqltype) {
193       switch (sqltype) {
194         case Types.CHAR:
195           return CHAR.constructor;
196         case Types.VARCHAR:
197           return VARCHAR.constructor;
198         case Types.NVARCHAR:
199           return NVARCHAR.constructor;
200         case Types.LONGVARCHAR:
201           return LONGVARCHAR.constructor;
202         case Types.BIT:
203           return BIT.constructor;
204         case Types.TINYINT:
205           return TINYINT.constructor;
206         case Types.SMALLINT:
207           return SMALLINT.constructor;
208         case Types.INTEGER:
209           return INTEGER.constructor;
210         case Types.BIGINT:
211           return BIGINT.constructor;
212         case Types.REAL:
213           return REAL.constructor;
214         case Types.DOUBLE:
215           return DOUBLE.constructor;
216         case Types.VARBINARY:
217           return VARBINARY.constructor;
218         case Types.DATE:
219           return DATE.constructor;
220         case Types.TIMESTAMP:
221           return TIMESTAMP.constructor;
222         case Types.CLOB:
223           return CLOB.constructor;
224         case Types.BLOB:
225           return BLOB.constructor;
226         default:
227           return null;
228       }
229     }
230   }
231 
232   @Override
233   public void resetSequence(final DbSession session, final long newvalue)
234       throws WaarpDatabaseNoConnectionException {
235     final String action =
236         "ALTER SEQUENCE " + DbDataModel.fieldseq + " RESTART WITH " + newvalue;
237     final DbRequest request = new DbRequest(session);
238     try {
239       request.query(action);
240     } catch (final WaarpDatabaseNoConnectionException e) {
241       logger.warn("ResetSequences Error", e);
242     } catch (final WaarpDatabaseSqlException e) {
243       logger.warn("ResetSequences Error", e);
244     } finally {
245       request.close();
246     }
247     logger.warn(action);
248   }
249 
250   @Override
251   public long nextSequence(final DbSession dbSession)
252       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException,
253              WaarpDatabaseNoDataException {
254     long result;
255     final String action = "SELECT NEXTVAL('" + DbDataModel.fieldseq + "')";
256     final DbPreparedStatement preparedStatement =
257         new DbPreparedStatement(dbSession);
258     try {
259       preparedStatement.createPrepareStatement(action);
260       // Limit the search
261       preparedStatement.executeQuery();
262       if (preparedStatement.getNext()) {
263         try {
264           result = preparedStatement.getResultSet().getLong(1);
265         } catch (final SQLException e) {
266           throw new WaarpDatabaseSqlException(e);
267         }
268         return result;
269       } else {
270         throw new WaarpDatabaseNoDataException(
271             "No sequence found. Must be initialized first");
272       }
273     } finally {
274       preparedStatement.realClose();
275     }
276   }
277 
278   @Override
279   protected final String validConnectionString() {
280     return "select 1";
281   }
282 
283   @Override
284   public final String limitRequest(final String allfields, final String request,
285                                    final int nb) {
286     if (nb == 0) {
287       return request;
288     }
289     return request + " LIMIT " + nb;
290   }
291 
292 }