001: /**
002: * Copyright (C) 2001-2004 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.speedo.mapper.rdb;
018:
019: import org.objectweb.jorm.api.PException;
020: import org.objectweb.jorm.api.PExceptionIO;
021: import org.objectweb.jorm.api.JormConfigurator;
022: import org.objectweb.jorm.mapper.rdb.lib.MapperJDBC;
023: import org.objectweb.jorm.mapper.rdb.lib.ConnectionSpecJDBC;
024: import org.objectweb.fractal.api.control.LifeCycleController;
025: import org.objectweb.fractal.api.control.BindingController;
026: import org.objectweb.util.monolog.api.LoggerFactory;
027: import org.objectweb.util.monolog.api.BasicLevel;
028: import org.objectweb.util.monolog.api.Logger;
029: import org.objectweb.util.monolog.wrapper.p6spy.P6SpyLogger;
030: import org.objectweb.speedo.api.ExceptionHelper;
031: import org.objectweb.speedo.mapper.rdb.JDBCConnectionHolder;
032: import org.objectweb.medor.eval.prefetch.lib.PrefetchCacheImpl;
033: import org.objectweb.perseus.pool.api.PoolMatchFactory;
034: import org.objectweb.perseus.pool.api.Pool;
035: import org.objectweb.perseus.pool.api.PoolException;
036: import org.objectweb.perseus.persistence.api.ConnectionHolderFactory;
037: import org.objectweb.perseus.persistence.api.ConnectionHolder;
038: import org.objectweb.perseus.persistence.api.PersistenceException;
039:
040: import java.sql.DriverManager;
041: import java.sql.Driver;
042: import java.util.ArrayList;
043:
044: /**
045: * A JDBC mapper component able to pool the JDBC connection in non managed
046: * environnement.
047: *
048: * @author S.Chassande-Barrioz
049: */
050: public class JDBCMapper extends MapperJDBC implements
051: BindingController, LifeCycleController, JDBCMapperAttributes,
052: PoolMatchFactory, ConnectionHolderFactory {
053:
054: public final static String POOL_BINDING = "pool";
055:
056: /**
057: * The pool of JDBC connection.
058: */
059: protected Pool connectionPool;
060:
061: /**
062: * The database url. This field is null when a Datasource is used.
063: */
064: private String url;
065:
066: /**
067: * The user name to access the database. This field is null when a
068: * Datasource is used.
069: */
070: private String userName;
071:
072: /**
073: * The user passwor to access the database. This field is null when a
074: * Datasource is used.
075: */
076: private String password;
077:
078: /**
079: * The class name of the JDBC driver. This field is null when a
080: * Datasource is used.
081: */
082: private String driverCN = null;
083:
084: /**
085: * Indicates if the component is started;
086: */
087: private boolean started = false;
088:
089: /**
090: * Indicates if JDBC connection must be pool or not.
091: */
092: private boolean poolConnection = false;
093:
094: /**
095: * Contains the connection allocated for a particular user, in a pooled mode
096: */
097: private ArrayList unpooledConnection;
098:
099: private boolean checkConnectivityAtStartup = true;
100:
101: public JDBCMapper() throws PException {
102: }
103:
104: // IMPLEMENTATION OF THE ConnectionHolderFactory INTERFACE //
105: //---------------------------------------------------------//
106:
107: public ConnectionHolder createConnectionHolder()
108: throws PersistenceException {
109: return new JDBCConnectionHolder(this , logger);
110: }
111:
112: // IMPLEMENTATION OF THE PoolMatchFactory INTERFACE //
113: //--------------------------------------------------//
114:
115: /**
116: * <b>createResource</b> creates a new PoolResource.
117: * @param hints The "properties" that the created PoolResource should
118: * conform to.
119: * @return The created PoolResource.
120: */
121: public Object createResource(Object hints) throws PoolException {
122: if (!started) {
123: startFc();
124: }
125: Object connection = null;
126: try {
127: if (hints == null) {
128: connection = super .getConnection();
129: } else {
130: connection = super .getConnection(hints);
131: }
132: } catch (PException e) {
133: throw new PoolException(
134: "Error during the allocation a new JDBC connection",
135: e);
136: }
137: if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
138: logger.log(BasicLevel.DEBUG,
139: "New JDBC connection allocated for the pool:"
140: + connection);
141: }
142: return connection;
143: }
144:
145: /**
146: * <b>matchResource</b> tests if a given resource of a Pool matches with
147: * the hints passed with the Pool getResource method.
148: * @param pr The PoolResource to test its matching with some
149: * "properties" specified by hints.
150: * @param hints The "properties" that the PoolResource specified by pr
151: * should match.
152: * @return <b>true</b> if the pr PoolResource matches the hints
153: * "properties".
154: */
155: public boolean matchResource(Object pr, Object hints) {
156: return true;
157: }
158:
159: public void destroyResource(Object resource) {
160: if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
161: logger.log(BasicLevel.DEBUG,
162: "Destroy the JDBC connection of the pool:"
163: + resource);
164: }
165: try {
166: super .closeConnection(resource);
167: } catch (PException e) {
168: if (logger != null) {
169: logger.log(BasicLevel.ERROR,
170: "ERROR during the closing of a connection of the pool:"
171: + resource, e);
172: }
173: }
174: }
175:
176: // IMPLEMENTATION OF THE JDBCMapperAttributes INTERFACE //
177: //------------------------------------------------------//
178:
179: public String getDriverClassName() {
180: return driverCN;
181: }
182:
183: public void setDriverClassName(String dcn) {
184: driverCN = dcn;
185: }
186:
187: public String getURL() {
188: return url;
189: }
190:
191: public void setURL(String url) {
192: this .url = url;
193: }
194:
195: public String getUserName() {
196: return userName;
197: }
198:
199: public void setUserName(String userName) {
200: this .userName = userName;
201: }
202:
203: public String getPassword() {
204: return password;
205: }
206:
207: public void setPassword(String password) {
208: this .password = password;
209: }
210:
211: public void setPoolConnection(boolean pc) {
212: poolConnection = pc;
213: if (poolConnection) {
214: unpooledConnection = new ArrayList(5);
215: }
216: }
217:
218: public boolean getPoolConnection() {
219: return poolConnection;
220: }
221:
222: // IMPLEMENTATION OF THE UserBindingController INTERFACE //
223: //-------------------------------------------------------//
224:
225: public String[] listFc() {
226: return new String[] { POOL_BINDING };
227: }
228:
229: public Object lookupFc(String s) {
230: if (POOL_BINDING.equals(s)) {
231: return connectionPool;
232: } else {
233: return null;
234: }
235: }
236:
237: public void bindFc(String s, Object o) {
238: if ("logger".equals(s)) {
239: logger = (Logger) o;
240: if (P6SpyLogger.logger == null) {
241: P6SpyLogger.logger = logger;
242: }
243: } else if ("monolog-factory".equals(s)) {
244: Logger l = logger;
245: setLoggerFactory((LoggerFactory) o);
246: if (l != null) {
247: logger = l;
248: }
249: P6SpyLogger.logger = getLoggerFactory().getLogger(
250: logger.getName() + ".sql");
251: } else if (POOL_BINDING.equals(s)) {
252: connectionPool = (Pool) o;
253: }
254: }
255:
256: public void unbindFc(String s) {
257: if (POOL_BINDING.equals(s)) {
258: connectionPool = null;
259: }
260: }
261:
262: // IMPLEMENTATION OF THE LifeCycleController INTERFACE //
263: //-----------------------------------------------------//
264:
265: public String getFcState() {
266: return started ? STARTED : STOPPED;
267: }
268:
269: public void startFc() {
270: if (!started) {
271: if (logger == null) {
272: String msg = "No logger assigned on the component before the start.";
273: System.err.println(msg);
274: throw new RuntimeException(msg);
275: }
276: started = true;
277: P6SpyLogger.level = BasicLevel.DEBUG;
278: Object cf = getConnectionFactory();
279: if (cf == null) {
280: try {
281: DriverManager.registerDriver((Driver) Class
282: .forName(driverCN).newInstance());
283: setConnectionFactory(new ConnectionSpecJDBC(url,
284: driverCN, userName, password));
285: } catch (Exception e) {
286: logger
287: .log(
288: BasicLevel.ERROR,
289: "Impossible to configure the jdbc access: ",
290: e);
291: throw new RuntimeException(
292: "Impossible to configure the jdbc access: "
293: + ExceptionHelper.getNested(e)
294: .getMessage());
295: }
296: }
297: if (checkConnectivityAtStartup) {
298: Object o = null;
299: try {
300: logger.log(BasicLevel.DEBUG,
301: "try to fetch a connection");
302: o = getConnection();
303: } catch (Exception e) {
304: Exception ie = ExceptionHelper.getNested(e);
305: logger.log(BasicLevel.ERROR,
306: "Impossible to fetch a connection", ie);
307: throw new RuntimeException(
308: "Impossible to fetch a connection: "
309: + ie.getMessage());
310: } finally {
311: if (o != null) {
312: try {
313: closeConnection(o);
314: } catch (PException e) {
315: }
316: }
317: }
318: }
319:
320: try {
321: JormConfigurator jc = getJormConfigurator();
322: jc.setLoggerFactory(getLoggerFactory());
323: PrefetchCacheImpl pc = new PrefetchCacheImpl(
324: getLoggerFactory()
325: .getLogger(
326: "org.objectweb.speedo.rt.query.prefetch"));
327: setPrefetchCache(pc);
328: start();
329: } catch (PException e) {
330: throw new RuntimeException(
331: "Impossible to configure the mapper: "
332: + ExceptionHelper.getNested(e)
333: .getMessage());
334: }
335: }
336: }
337:
338: public void stopFc() {
339: started = false;
340: }
341:
342: // IMPLEMENTATION OF THE PMapper INTERFACE //
343: //-----------------------------------------//
344:
345: /**
346: * The pool is used to fetch a connection.
347: */
348: public Object getConnection() throws PException {
349: if (poolConnection) {
350: try {
351: Object connection = connectionPool.getResource(null);
352: if (logger != null
353: && logger.isLoggable(BasicLevel.DEBUG)) {
354: logger.log(BasicLevel.DEBUG,
355: "Get a JDBC connection from the pool: "
356: + connection);
357: }
358: return connection;
359: } catch (Exception e) {
360: throw new PExceptionIO(ExceptionHelper.getNested(e),
361: "Impossible to fetch a jdbc connection on driver");
362: }
363: } else {
364: Object connection = super .getConnection();
365: if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
366: logger.log(BasicLevel.DEBUG,
367: "JDBC connection allocated: " + connection);
368: }
369: return connection;
370: }
371: }
372:
373: public Object getConnection(Object connectionContext, Object user)
374: throws PException {
375: if (poolConnection) {
376: if (connectionContext == null) {
377: try {
378: Object connection = connectionPool.getResource(
379: null, user);
380: if (logger != null
381: && logger.isLoggable(BasicLevel.DEBUG)) {
382: logger.log(BasicLevel.DEBUG,
383: "Get a JDBC connection from the pool: "
384: + connection);
385: }
386: return connection;
387: } catch (Exception e) {
388: throw new PExceptionIO(
389: ExceptionHelper.getNested(e),
390: "Impossible to fetch a jdbc connection on driver");
391: }
392: } else {
393: Object connection = super .getConnection(
394: connectionContext, user);
395: synchronized (unpooledConnection) {
396: unpooledConnection.add(connection);
397: }
398: if (logger != null
399: && logger.isLoggable(BasicLevel.DEBUG)) {
400: logger.log(BasicLevel.DEBUG,
401: "JDBC connection allocated with context, context= "
402: + connectionContext
403: + ", connection=" + connection);
404: }
405: return connection;
406: }
407: } else {
408: Object connection = super .getConnection(connectionContext,
409: user);
410: if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
411: logger.log(BasicLevel.DEBUG,
412: "JDBC connection allocated: " + connection);
413: }
414: return connection;
415: }
416: }
417:
418: /**
419: * The connection is release into the pool
420: * @param conn
421: * @throws org.objectweb.jorm.api.PException
422: */
423: public void closeConnection(Object conn) throws PException {
424: if (conn == null) {
425: return;
426: }
427: if (poolConnection) {
428: if (unpooledConnection != null) {
429: synchronized (unpooledConnection) {
430: if (unpooledConnection.remove(unpooledConnection)) {
431: if (logger != null
432: && logger.isLoggable(BasicLevel.DEBUG)) {
433: logger.log(BasicLevel.DEBUG,
434: "Closing the JDBC connection (context): "
435: + conn);
436: }
437: return;
438: } //else the connection has not been found
439: }
440: }
441: try {
442: connectionPool.releaseResource(conn);
443: } catch (Exception e) {
444: throw new PExceptionIO(ExceptionHelper.getNested(e),
445: "Impossible to release a jdbc connection");
446: }
447: } else {
448: if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
449: logger.log(BasicLevel.DEBUG,
450: "Closing the JDBC connection: " + conn);
451: }
452: super .closeConnection(conn);
453: }
454: }
455:
456: public boolean getCheckConnectivityAtStartup() {
457: return this .checkConnectivityAtStartup;
458: }
459:
460: public void setCheckConnectivityAtStartup(boolean b) {
461: checkConnectivityAtStartup = b;
462:
463: }
464: }
|