1 /**
2 * This file is part of GoldenGate Project (named also GoldenGate or GG).
3 *
4 * Copyright 2009, Frederic Bregier, 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 GoldenGate 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 * GoldenGate 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 * GoldenGate . If not, see <http://www.gnu.org/licenses/>.
19 */
20 package goldengate.common.database;
21
22 import goldengate.common.logging.GgInternalLogger;
23 import goldengate.common.logging.GgInternalLoggerFactory;
24
25 import java.sql.Connection;
26 import java.sql.SQLException;
27 import java.util.concurrent.ConcurrentHashMap;
28
29 import goldengate.common.database.exception.GoldenGateDatabaseNoConnectionException;
30 import goldengate.common.database.exception.GoldenGateDatabaseSqlException;
31 import goldengate.common.database.model.DbType;
32 import goldengate.common.database.model.DbModelFactory;
33
34 /**
35 * Class for access to Database
36 *
37 * @author Frederic Bregier
38 *
39 */
40 public class DbAdmin {
41 /**
42 * Internal Logger
43 */
44 private static final GgInternalLogger logger = GgInternalLoggerFactory
45 .getLogger(DbAdmin.class);
46
47 public static int RETRYNB = 3;
48
49 public static long WAITFORNETOP = 100;
50
51 /**
52 * Database type
53 */
54 public DbType typeDriver;
55
56 /**
57 * DB Server
58 */
59 private String server = null;
60
61 /**
62 * DB User
63 */
64 private String user = null;
65
66 /**
67 * DB Password
68 */
69 private String passwd = null;
70
71 /**
72 * Is this DB Admin connected
73 */
74 public boolean isConnected = false;
75
76 /**
77 * Is this DB Admin Read Only
78 */
79 public boolean isReadOnly = false;
80
81 /**
82 * Is this DB Admin accessed by only one thread at a time (no concurrency
83 * and no lock/unlock problem)
84 */
85 public boolean isMultipleDBAccess = false;
86
87 /**
88 * session is the Session object for all type of requests
89 */
90 public DbSession session = null;
91
92 /**
93 * Validate connection
94 *
95 * @throws GoldenGateDatabaseNoConnectionException
96 */
97 public void validConnection() throws GoldenGateDatabaseNoConnectionException {
98 try {
99 DbModelFactory.dbModel.validConnection(session);
100 } catch (GoldenGateDatabaseNoConnectionException e) {
101 session.isDisconnected = true;
102 isConnected = false;
103 throw e;
104 }
105 session.isDisconnected = false;
106 isConnected = true;
107 }
108
109 /**
110 * Use a default server for basic connection. Later on, specific connection
111 * to database for the scheme that provides access to the table R66DbIndex
112 * for one specific Legacy could be done.
113 *
114 * A this time, only one driver is possible! If a new driver is needed, then
115 * we need to create a new DbSession object. Be aware that
116 * DbSession.initialize should be call only once for each driver, whatever
117 * the number of DbSession objects that could be created (=> need a
118 * hashtable for specific driver when created). Also, don't know if two
119 * drivers at the same time (two different DbSession) is allowed by JDBC.
120 *
121 * @param driver
122 * @param server
123 * @param user
124 * @param passwd
125 * @throws GoldenGateDatabaseNoConnectionException
126 */
127 public DbAdmin(DbType driver, String server, String user, String passwd)
128 throws GoldenGateDatabaseNoConnectionException {
129 this.server = server;
130 this.user = user;
131 this.passwd = passwd;
132 this.typeDriver = driver;
133 if (typeDriver == null) {
134 logger.error("Cannot find TypeDriver:" + driver.name());
135 throw new GoldenGateDatabaseNoConnectionException(
136 "Cannot find database drive:" + driver.name());
137 }
138 session = new DbSession(this.server, this.user, this.passwd, false);
139 session.admin = this;
140 isReadOnly = false;
141 validConnection();
142 session.useConnection(); // default since this is the top connection
143 }
144
145 /**
146 * Use a default server for basic connection. Later on, specific connection
147 * to database for the scheme that provides access to the table R66DbIndex
148 * for one specific Legacy could be done.
149 *
150 * A this time, only one driver is possible! If a new driver is needed, then
151 * we need to create a new DbSession object. Be aware that
152 * DbSession.initialize should be call only once for each driver, whatever
153 * the number of DbSession objects that could be created (=> need a
154 * hashtable for specific driver when created). Also, don't know if two
155 * drivers at the same time (two different DbSession) is allowed by JDBC.
156 *
157 * @param driver
158 * @param server
159 * @param user
160 * @param passwd
161 * @param write
162 * @throws GoldenGateDatabaseSqlException
163 * @throws GoldenGateDatabaseNoConnectionException
164 */
165 public DbAdmin(DbType driver, String server, String user, String passwd,
166 boolean write) throws GoldenGateDatabaseNoConnectionException {
167 this.server = server;
168 this.user = user;
169 this.passwd = passwd;
170 this.typeDriver = driver;
171 if (typeDriver == null) {
172 logger.error("Cannot find TypeDriver");
173 throw new GoldenGateDatabaseNoConnectionException(
174 "Cannot find database driver");
175 }
176 if (write) {
177 for (int i = 0; i < RETRYNB; i ++) {
178 try {
179 session = new DbSession(this.server, this.user,
180 this.passwd, false);
181 } catch (GoldenGateDatabaseNoConnectionException e) {
182 logger.warn("Attempt of connection in error: " + i);
183 continue;
184 }
185 isReadOnly = false;
186 session.admin = this;
187 validConnection();
188 session.useConnection(); // default since this is the top
189 // connection
190 return;
191 }
192 } else {
193 for (int i = 0; i < RETRYNB; i ++) {
194 try {
195 session = new DbSession(this.server, this.user,
196 this.passwd, true);
197 } catch (GoldenGateDatabaseNoConnectionException e) {
198 logger.warn("Attempt of connection in error: " + i);
199 continue;
200 }
201 isReadOnly = true;
202 session.admin = this;
203 validConnection();
204 session.useConnection(); // default since this is the top
205 // connection
206 return;
207 }
208 }
209 session = null;
210 isConnected = false;
211 logger.error("Cannot connect to Database!");
212 throw new GoldenGateDatabaseNoConnectionException(
213 "Cannot connect to database");
214 }
215
216 /**
217 * Use a default server for basic connection. Later on, specific connection
218 * to database for the scheme that provides access to the table R66DbIndex
219 * for one specific Legacy could be done.
220 *
221 * A this time, only one driver is possible! If a new driver is needed, then
222 * we need to create a new DbSession object. Be aware that
223 * DbSession.initialize should be call only once for each driver, whatever
224 * the number of DbSession objects that could be created (=> need a
225 * hashtable for specific driver when created). Also, don't know if two
226 * drivers at the same time (two different DbSession) is allowed by JDBC.<BR>
227 *
228 * <B>This version use given connection. typeDriver must be set before !</B>
229 *
230 * @param conn
231 * @param isread
232 * @throws GoldenGateDatabaseNoConnectionException
233 */
234 public DbAdmin(Connection conn, boolean isread)
235 throws GoldenGateDatabaseNoConnectionException {
236 server = null;
237 if (conn == null) {
238 session = null;
239 isConnected = false;
240 logger.error("Cannot Get a Connection from Datasource");
241 throw new GoldenGateDatabaseNoConnectionException(
242 "Cannot Get a Connection from Datasource");
243 }
244 session = new DbSession(conn, isread);
245 session.admin = this;
246 isReadOnly = isread;
247 isConnected = true;
248 validConnection();
249 session.useConnection(); // default since this is the top connection
250 }
251
252 /**
253 * Empty constructor for no Database support (very thin client)
254 */
255 public DbAdmin() {
256 // not true but to enable pseudo database functions
257 DbModelFactory.classLoaded = true;
258 isConnected = false;
259 }
260
261 /**
262 * Close the underlying session. Can be call even for connection given from
263 * the constructor DbAdmin(Connection, boolean).
264 *
265 */
266 public void close() {
267 if (session != null) {
268 session.endUseConnection(); // default since this is the top
269 // connection
270 session.disconnect();
271 session = null;
272 }
273 isConnected = false;
274 }
275
276 /**
277 * Commit on connection (since in autocommit, should not be used)
278 *
279 * @throws GoldenGateDatabaseNoConnectionException
280 * @throws GoldenGateDatabaseSqlException
281 *
282 */
283 public void commit() throws GoldenGateDatabaseSqlException,
284 GoldenGateDatabaseNoConnectionException {
285 if (session != null) {
286 session.commit();
287 }
288 }
289
290 /**
291 * @return the server
292 */
293 public String getServer() {
294 return server;
295 }
296
297 /**
298 * @return the user
299 */
300 public String getUser() {
301 return user;
302 }
303
304 /**
305 * @return the passwd
306 */
307 public String getPasswd() {
308 return passwd;
309 }
310
311 @Override
312 public String toString() {
313 return "Admin: "+typeDriver.name()+":"+server+":"+user+":"+(passwd.length());
314 }
315
316 /**
317 * List all Connection to enable the close call on them
318 */
319 private static ConcurrentHashMap<Long, DbSession> listConnection = new ConcurrentHashMap<Long, DbSession>();
320
321 /**
322 * Number of HttpSession
323 */
324 public static int nbHttpSession = 0;
325
326 /**
327 * Add a Connection into the list
328 *
329 * @param id
330 * @param session
331 */
332 public static void addConnection(long id, DbSession session) {
333 listConnection.put(Long.valueOf(id), session);
334 }
335
336 /**
337 * Remove a Connection from the list
338 *
339 * @param id
340 * Id of the connection
341 */
342 public static void removeConnection(long id) {
343 listConnection.remove(Long.valueOf(id));
344 }
345
346 /**
347 *
348 * @return the number of connection (so number of network channels)
349 */
350 public static int getNbConnection() {
351 return listConnection.size() - 1;
352 }
353
354 /**
355 * Close all database connections
356 */
357 public static void closeAllConnection() {
358 for (DbSession session: listConnection.values()) {
359 try {
360 session.conn.close();
361 } catch (SQLException e) {
362 }
363 }
364 listConnection.clear();
365 if (DbModelFactory.dbModel != null) {
366 DbModelFactory.dbModel.releaseResources();
367 }
368 }
369
370 /**
371 * Check all database connections and try to reopen them if disconnected
372 */
373 public static void checkAllConnections() {
374 for (DbSession session: listConnection.values()) {
375 try {
376 session.checkConnection();
377 } catch (GoldenGateDatabaseNoConnectionException e) {
378 logger.error("Database Connection cannot be reinitialized");
379 }
380 }
381 }
382 }