KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > virtualdatabase > VirtualDatabase


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2005 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Mathieu Peltier, Nicolas Modrzyk, Vadim Kassin, Olivier Fambon, Jean-Bernard van Zuylen.
23  */

24
25 package org.objectweb.cjdbc.controller.virtualdatabase;
26
27 import java.sql.SQLException JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.Hashtable JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.LinkedList JavaDoc;
33 import java.util.Map JavaDoc;
34
35 import javax.management.NotCompliantMBeanException JavaDoc;
36 import javax.management.ObjectName JavaDoc;
37
38 import org.objectweb.cjdbc.common.exceptions.BackupException;
39 import org.objectweb.cjdbc.common.exceptions.VirtualDatabaseException;
40 import org.objectweb.cjdbc.common.i18n.Translate;
41 import org.objectweb.cjdbc.common.jmx.JmxConstants;
42 import org.objectweb.cjdbc.common.jmx.JmxException;
43 import org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean;
44 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList;
45 import org.objectweb.cjdbc.common.log.Trace;
46 import org.objectweb.cjdbc.common.monitor.backend.BackendStatistics;
47 import org.objectweb.cjdbc.common.shared.BackendInfo;
48 import org.objectweb.cjdbc.common.shared.BackendState;
49 import org.objectweb.cjdbc.common.shared.DumpInfo;
50 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
51 import org.objectweb.cjdbc.common.sql.SelectRequest;
52 import org.objectweb.cjdbc.common.sql.StoredProcedure;
53 import org.objectweb.cjdbc.common.sql.filters.AbstractBlobFilter;
54 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema;
55 import org.objectweb.cjdbc.common.users.AdminUser;
56 import org.objectweb.cjdbc.common.users.VirtualDatabaseUser;
57 import org.objectweb.cjdbc.common.util.Constants;
58 import org.objectweb.cjdbc.common.util.ReadPrioritaryFIFOWriteLock;
59 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
60 import org.objectweb.cjdbc.common.xml.XmlComponent;
61 import org.objectweb.cjdbc.common.xml.XmlTools;
62 import org.objectweb.cjdbc.controller.authentication.AuthenticationManager;
63 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
64 import org.objectweb.cjdbc.controller.backup.Backuper;
65 import org.objectweb.cjdbc.controller.cache.result.AbstractResultCache;
66 import org.objectweb.cjdbc.controller.core.Controller;
67 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseForceShutdownThread;
68 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseSafeShutdownThread;
69 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseShutdownThread;
70 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseWaitShutdownThread;
71 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean;
72 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager;
73 import org.objectweb.cjdbc.controller.jmx.RmiConnector;
74 import org.objectweb.cjdbc.controller.loadbalancer.AllBackendsFailedException;
75 import org.objectweb.cjdbc.controller.monitoring.SQLMonitoring;
76 import org.objectweb.cjdbc.controller.recoverylog.BackendRecoveryInfo;
77 import org.objectweb.cjdbc.controller.recoverylog.RecoverThread;
78 import org.objectweb.cjdbc.controller.recoverylog.RecoveryLog;
79 import org.objectweb.cjdbc.controller.requestmanager.RAIDbLevels;
80 import org.objectweb.cjdbc.controller.requestmanager.RequestManager;
81
82 /**
83  * A <code>VirtualDatabase</code> represents a database from client point of
84  * view and hide the complexity of the cluster distribution to the client. The
85  * client always uses the virtual database name and the C-JDBC Controller will
86  * use the real connections when an SQL request comes in.
87  *
88  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
89  * @author <a HREF="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
90  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
91  * @author <a HREF="mailto:vadim@kase.kz">Vadim Kassin </a>
92  * @author <a HREF="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
93  * </a>
94  * @version 1.0
95  */

96 public class VirtualDatabase extends AbstractStandardMBean
97     implements
98       VirtualDatabaseMBean,
99       XmlComponent
100 {
101   private static final long serialVersionUID = 1399418136380336827L;
102
103   //
104
// How the code is organized ?
105
//
106
// 1. Member variables
107
// 2. Constructor(s)
108
// 3. Request handling
109
// 4. Transaction handling
110
// 5. Database backend management
111
// 6. Checkpoint management
112
// 7. Getter/Setter (possibly in alphabetical order)
113
// 8. Shutdown
114
//
115

116   /** Virtual database name */
117   protected String JavaDoc name;
118
119   /**
120    * Authentification manager matching virtual database login/password to
121    * backends login/password
122    */

123   protected AuthenticationManager authenticationManager;
124
125   /** <code>ArrayList</code> of <code>DatabaseBackend</code> objects */
126   protected ArrayList JavaDoc backends;
127
128   /** Read/Write lock for backend list */
129   protected ReadPrioritaryFIFOWriteLock rwLock;
130
131   /** The request manager to use for this database */
132   protected RequestManager requestManager;
133
134   /** ArrayList to store the order of requests */
135   protected LinkedList JavaDoc totalOrderQueue = null;
136
137   /** Virtual database logger */
138   public Trace logger = null;
139   protected Trace requestLogger = null;
140
141   // List of current active Worker Threads
142
private ArrayList JavaDoc activeThreads = new ArrayList JavaDoc();
143   // List of current idle Worker Threads
144
private int idleThreads = 0;
145   // List of current pending connections (Socket objects)
146
private ArrayList JavaDoc pendingConnections = new ArrayList JavaDoc();
147
148   /** Maximum number of concurrent accepted for this virtual database */
149   protected int maxNbOfConnections;
150
151   /** If false one worker thread is forked per connection else */
152   protected boolean poolConnectionThreads;
153
154   /** Maximum time a worker thread can remain idle before dying */
155   protected long maxThreadIdleTime;
156
157   /**
158    * Minimum number of worker threads to keep in the pool if
159    * poolConnectionThreads is true
160    */

161   protected int minNbOfThreads;
162
163   /** Maximum number of worker threads to fork */
164   protected int maxNbOfThreads;
165
166   /** Current number of worker threads */
167   protected int currentNbOfThreads;
168
169   /** Virtual Database MetaData */
170   protected VirtualDatabaseDynamicMetaData metadata;
171   private VirtualDatabaseStaticMetaData staticMetadata;
172
173   private SQLMonitoring sqlMonitor = null;
174
175   /** Use for method getAndCheck */
176   public static final int CHECK_BACKEND_ENABLE = 1;
177   /** Use for method getAndCheck */
178   public static final int CHECK_BACKEND_DISABLE = 0;
179   /** Use for method getAndCheck */
180   public static final int NO_CHECK_BACKEND = -1;
181
182   /** Short form of SQL statements to include in traces and exceptions */
183   private int sqlShortFormLength;
184
185   /** The filter used to store blobs in the database */
186   private AbstractBlobFilter blobFilter;
187
188   /** The controller we belong to */
189   Controller controller;
190
191   /** Comma separated list of database product names (one instance per name) */
192   private String JavaDoc databaseProductNames = "C-JDBC";
193
194   /** Marker to see if the database is shutting down */
195   private boolean shuttingDown = false;
196
197   /* Constructors */
198
199   /**
200    * Creates a new <code>VirtualDatabase</code> instance.
201    *
202    * @param name the virtual database name.
203    * @param maxConnections maximum number of concurrent connections.
204    * @param pool should we use a pool of threads for handling connections?
205    * @param minThreads minimum number of threads in the pool
206    * @param maxThreads maximum number of threads in the pool
207    * @param maxThreadIdleTime maximum time a thread can remain idle before being
208    * removed from the pool.
209    * @param sqlShortFormLength maximum number of characters of an SQL statement
210    * to diplay in traces or exceptions
211    * @param blobFilter encoding method for blobs
212    * @param controller the controller we belong to
213    * @exception NotCompliantMBeanException in case the bean does not comply with
214    * jmx
215    * @exception JmxException could not register mbean
216    */

217   public VirtualDatabase(Controller controller, String JavaDoc name,
218       int maxConnections, boolean pool, int minThreads, int maxThreads,
219       long maxThreadIdleTime, int sqlShortFormLength,
220       AbstractBlobFilter blobFilter) throws NotCompliantMBeanException JavaDoc,
221       JmxException
222   {
223     super(VirtualDatabaseMBean.class);
224     this.controller = controller;
225     this.name = name;
226     this.maxNbOfConnections = maxConnections;
227     this.poolConnectionThreads = pool;
228     this.minNbOfThreads = minThreads;
229     this.maxNbOfThreads = maxThreads;
230     this.maxThreadIdleTime = maxThreadIdleTime;
231     this.sqlShortFormLength = sqlShortFormLength;
232     this.blobFilter = blobFilter;
233     backends = new ArrayList JavaDoc();
234
235     ObjectName JavaDoc objectName = JmxConstants.getVirtualDbObjectName(name);
236     MBeanServerManager.registerMBean(this, objectName);
237
238     rwLock = new ReadPrioritaryFIFOWriteLock();
239     logger = Trace.getLogger("org.objectweb.cjdbc.controller.virtualdatabase."
240         + name);
241     requestLogger = Trace
242         .getLogger("org.objectweb.cjdbc.controller.virtualdatabase.request."
243             + name);
244   }
245
246   /**
247    * Acquires a read lock on the backend lists (both enabled and disabled
248    * backends). This should be called prior traversing the backend
249    * <code>ArrayList</code>.
250    *
251    * @throws InterruptedException if an error occurs
252    */

253   public final void acquireReadLockBackendLists() throws InterruptedException JavaDoc
254   {
255     rwLock.acquireRead();
256   }
257
258   /**
259    * Releases the read lock on the backend lists (both enabled and disabled
260    * backends). This should be called after traversing the backend
261    * <code>ArrayList</code>.
262    */

263   public final void releaseReadLockBackendLists()
264   {
265     rwLock.releaseRead();
266   }
267
268   /**
269    * Is this virtual database distributed ?
270    *
271    * @return false
272    */

273   public boolean isDistributed()
274   {
275     return false;
276   }
277
278   /* Request Handling */
279
280   /**
281    * Checks if a given virtual login/password is ok.
282    *
283    * @param virtualLogin the virtual user login
284    * @param virtualPassword the virtual user password
285    * @return <code>true</code> if the login/password is known from the
286    * <code>AuthenticationManager</code>. Returns <code>false</code>
287    * if no <code>AuthenticationManager</code> is defined.
288    */

289   public boolean checkUserAuthentication(String JavaDoc virtualLogin,
290       String JavaDoc virtualPassword)
291   {
292     if (authenticationManager == null)
293     {
294       logger.error("No authentification manager defined to check login '"
295           + virtualLogin + "'");
296       return false;
297     }
298     else
299       return authenticationManager.isValidVirtualUser(new VirtualDatabaseUser(
300           virtualLogin, virtualPassword));
301   }
302
303   /**
304    * Checks if a given admin login/password is ok.
305    *
306    * @param adminLogin admin user login
307    * @param adminPassword admin user password
308    * @return <code>true</code> if the login/password is known from the
309    * <code>AuthenticationManager</code>. Returns <code>false</code>
310    * if no <code>AuthenticationManager</code> is defined.
311    */

312   public boolean checkAdminAuthentication(String JavaDoc adminLogin,
313       String JavaDoc adminPassword)
314   {
315     if (authenticationManager == null)
316     {
317       logger.error("No authentification manager defined to check admin login '"
318           + adminLogin + "'");
319       return false;
320     }
321     else
322       return authenticationManager.isValidAdminUser(new AdminUser(adminLogin,
323           adminPassword));
324   }
325
326   /**
327    * Performs a read request and returns the reply.
328    *
329    * @param request the request to execute
330    * @return a <code>ControllerResultSet</code> value
331    * @exception SQLException if the request fails
332    */

333   public ControllerResultSet execReadRequest(SelectRequest request)
334       throws SQLException JavaDoc
335   {
336     if (request == null)
337     {
338       String JavaDoc msg = "Request failed (null read request received)";
339       logger.warn(msg);
340       throw new SQLException JavaDoc(msg);
341     }
342
343     try
344     {
345       if (requestLogger.isInfoEnabled())
346         requestLogger.info("S " + request.getTransactionId() + " "
347             + request.getSQL());
348
349       long start = 0;
350       if (sqlMonitor != null && sqlMonitor.isActive())
351         start = System.currentTimeMillis();
352
353       ControllerResultSet rs = requestManager.execReadRequest(request);
354
355       if (sqlMonitor != null && sqlMonitor.isActive())
356         sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start);
357
358       return rs;
359     }
360     catch (SQLException JavaDoc e)
361     {
362       String JavaDoc msg = "Request '" + request.getId() + "' failed ("
363           + e.getMessage() + ")";
364       logger.warn(msg);
365       if (sqlMonitor != null && sqlMonitor.isActive())
366         sqlMonitor.logError(request);
367       throw e;
368     }
369   }
370
371   /**
372    * Performs a write request and returns the number of rows affected.
373    *
374    * @param request the request to execute
375    * @return number of rows affected
376    * @exception SQLException if the request fails
377    */

378   public int execWriteRequest(AbstractWriteRequest request) throws SQLException JavaDoc
379   {
380     if (request == null)
381     {
382       String JavaDoc msg = "Request failed (null write request received)";
383       logger.warn(msg);
384       throw new SQLException JavaDoc(msg);
385     }
386
387     try
388     {
389       if (requestLogger.isInfoEnabled())
390         requestLogger.info("W " + request.getTransactionId() + " "
391             + request.getSQL());
392
393       long start = 0;
394       if (sqlMonitor != null && sqlMonitor.isActive())
395         start = System.currentTimeMillis();
396
397       int result = requestManager.execWriteRequest(request);
398
399       if (sqlMonitor != null && sqlMonitor.isActive())
400         sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start);
401
402       return result;
403     }
404     catch (SQLException JavaDoc e)
405     {
406       String JavaDoc msg = "Request '" + request.getId() + "' failed ("
407           + e.getMessage() + ")";
408       logger.warn(msg);
409       if (sqlMonitor != null && sqlMonitor.isActive())
410         sqlMonitor.logError(request);
411       throw e;
412     }
413   }
414
415   /**
416    * Performs a write request and returns the auto generated keys.
417    *
418    * @param request the request to execute
419    * @return auto generated keys
420    * @exception SQLException if the request fails
421    */

422   public ControllerResultSet execWriteRequestWithKeys(
423       AbstractWriteRequest request) throws SQLException JavaDoc
424   {
425     if (request == null)
426     {
427       String JavaDoc msg = "Request failed (null write request received)";
428       logger.warn(msg);
429       throw new SQLException JavaDoc(msg);
430     }
431
432     try
433     {
434       if (requestLogger.isInfoEnabled())
435         requestLogger.info("W " + request.getTransactionId() + " "
436             + request.getSQL());
437
438       long start = 0;
439       if (sqlMonitor != null && sqlMonitor.isActive())
440         start = System.currentTimeMillis();
441
442       ControllerResultSet result = requestManager
443           .execWriteRequestWithKeys(request);
444
445       if (sqlMonitor != null && sqlMonitor.isActive())
446         sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start);
447
448       return result;
449     }
450     catch (SQLException JavaDoc e)
451     {
452       String JavaDoc msg = "Request '" + request.getId() + "' failed ("
453           + e.getMessage() + ")";
454       logger.warn(msg);
455       if (sqlMonitor != null && sqlMonitor.isActive())
456         sqlMonitor.logError(request);
457       throw e;
458     }
459   }
460
461   /**
462    * Call a stored procedure that returns a ResultSet.
463    *
464    * @param proc the stored procedure call
465    * @return a <code>java.sql.ResultSet</code> value
466    * @exception SQLException if an error occurs
467    */

468   public ControllerResultSet execReadStoredProcedure(StoredProcedure proc)
469       throws SQLException JavaDoc
470   {
471     if (proc == null)
472     {
473       String JavaDoc msg = "Request failed (null stored procedure received)";
474       logger.warn(msg);
475       throw new SQLException JavaDoc(msg);
476     }
477
478     try
479     {
480       if (requestLogger.isInfoEnabled())
481         requestLogger
482             .info("S " + proc.getTransactionId() + " " + proc.getSQL());
483
484       long start = 0;
485       if (sqlMonitor != null && sqlMonitor.isActive())
486         start = System.currentTimeMillis();
487
488       ControllerResultSet rs = requestManager.execReadStoredProcedure(proc);
489
490       if (sqlMonitor != null && sqlMonitor.isActive())
491         sqlMonitor.logRequestTime(proc, System.currentTimeMillis() - start);
492
493       return rs;
494     }
495     catch (AllBackendsFailedException e)
496     {
497       String JavaDoc msg = Translate.get(
498           "loadbalancer.storedprocedure.failed.on.all.backends", new String JavaDoc[]{
499               String.valueOf(proc.getId()), e.getMessage()});
500       logger.warn(msg);
501       if (sqlMonitor != null && sqlMonitor.isActive())
502         sqlMonitor.logError(proc);
503       throw new SQLException JavaDoc(msg);
504     }
505     catch (SQLException JavaDoc e)
506     {
507       String JavaDoc msg = Translate.get("loadbalancer.storedprocedure.failed",
508           new String JavaDoc[]{String.valueOf(proc.getId()), e.getMessage()});
509       logger.warn(msg);
510       if (sqlMonitor != null && sqlMonitor.isActive())
511         sqlMonitor.logError(proc);
512       throw e;
513     }
514   }
515
516   /**
517    * Call a stored procedure that performs an update.
518    *
519    * @param proc the stored procedure call
520    * @return number of rows affected
521    * @exception SQLException if an error occurs
522    */

523   protected int execWriteStoredProcedure(StoredProcedure proc)
524       throws SQLException JavaDoc
525   {
526     if (proc == null)
527     {
528       String JavaDoc msg = "Request failed (null stored procedure received)";
529       logger.warn(msg);
530       throw new SQLException JavaDoc(msg);
531     }
532
533     try
534     {
535       if (requestLogger.isInfoEnabled())
536         requestLogger
537             .info("W " + proc.getTransactionId() + " " + proc.getSQL());
538
539       long start = 0;
540       if (sqlMonitor != null && sqlMonitor.isActive())
541         start = System.currentTimeMillis();
542
543       int result = requestManager.execWriteStoredProcedure(proc);
544
545       if (sqlMonitor != null && sqlMonitor.isActive())
546         sqlMonitor.logRequestTime(proc, System.currentTimeMillis() - start);
547
548       return result;
549     }
550     catch (AllBackendsFailedException e)
551     {
552       String JavaDoc msg = Translate.get(
553           "loadbalancer.storedprocedure.failed.on.all.backends", new String JavaDoc[]{
554               String.valueOf(proc.getId()), e.getMessage()});
555       logger.warn(msg);
556       if (sqlMonitor != null && sqlMonitor.isActive())
557         sqlMonitor.logError(proc);
558       throw new SQLException JavaDoc(msg);
559     }
560     catch (SQLException JavaDoc e)
561     {
562       String JavaDoc msg = Translate.get("loadbalancer.storedprocedure.failed",
563           new String JavaDoc[]{String.valueOf(proc.getId()), e.getMessage()});
564       logger.warn(msg);
565       if (sqlMonitor != null && sqlMonitor.isActive())
566         sqlMonitor.logError(proc);
567       throw e;
568     }
569   }
570
571   /* Transaction management */
572
573   /**
574    * Begins a new transaction and returns the corresponding transaction
575    * identifier. This method is called from the driver when
576    * {@link org.objectweb.cjdbc.driver.Connection#setAutoCommit(boolean)}is
577    * called with <code>false</code> argument.
578    * <p>
579    * Note that the transaction begin is not logged in the recovery log by this
580    * method, you will have to call logLazyTransactionBegin.
581    *
582    * @param login the login used by the connection
583    * @return an unique transaction identifier
584    * @exception SQLException if an error occurs
585    * @see RequestManager#logLazyTransactionBegin(long)
586    */

587   public long begin(String JavaDoc login) throws SQLException JavaDoc
588   {
589     try
590     {
591       long tid = requestManager.begin(login);
592       if (requestLogger.isInfoEnabled())
593         requestLogger.info("B " + tid);
594       return tid;
595     }
596     catch (SQLException JavaDoc e)
597     {
598       String JavaDoc msg = "Begin failed (" + e.getMessage() + ")";
599       logger.warn(msg);
600       throw e;
601     }
602   }
603
604   /**
605    * Abort a transaction that has been started but in which no query was
606    * executed. As we use lazy transaction begin, there is no need to rollback
607    * such transaction but just to cleanup the metadata associated with this not
608    * effectively started transaction.
609    *
610    * @param transactionId id of the transaction to abort
611    * @param logAbort true if the abort (in fact rollback) should be logged in
612    * the recovery log
613    * @throws SQLException if an error occurs
614    */

615   public void abort(long transactionId, boolean logAbort) throws SQLException JavaDoc
616   {
617     requestManager.abort(transactionId, logAbort);
618     // Emulate this as a rollback for the RequestPlayer
619
if (requestLogger.isInfoEnabled())
620       requestLogger.info("R " + transactionId);
621   }
622
623   /**
624    * Commits a transaction given its id.
625    *
626    * @param transactionId the transaction id
627    * @param logCommit true if the commit should be logged in the recovery log
628    * @exception SQLException if an error occurs
629    */

630   public void commit(long transactionId, boolean logCommit) throws SQLException JavaDoc
631   {
632     try
633     {
634       if (requestLogger.isInfoEnabled())
635         requestLogger.info("C " + transactionId);
636       requestManager.commit(transactionId, logCommit);
637     }
638     catch (SQLException JavaDoc e)
639     {
640       String JavaDoc msg = "Commit of transaction '" + transactionId + "' failed ("
641           + e.getMessage() + ")";
642       logger.warn(msg);
643       throw e;
644     }
645   }
646
647   /**
648    * Rollbacks a transaction given its id.
649    *
650    * @param transactionId the transaction id
651    * @param logRollback true if the rollback should be logged in the recovery
652    * log
653    * @exception SQLException if an error occurs
654    */

655   public void rollback(long transactionId, boolean logRollback)
656       throws SQLException JavaDoc
657   {
658     try
659     {
660       if (requestLogger.isInfoEnabled())
661         requestLogger.info("R " + transactionId);
662       requestManager.rollback(transactionId, logRollback);
663     }
664     catch (SQLException JavaDoc e)
665     {
666       String JavaDoc msg = "Rollback of transaction '" + transactionId + "' failed ("
667           + e.getMessage() + ")";
668       logger.warn(msg);
669       throw e;
670     }
671   }
672
673   /**
674    * Rollbacks a transaction given its id to a savepoint given its name
675    *
676    * @param transactionId the transaction id
677    * @param savepointName the name of the savepoint
678    * @exception SQLException if an error occurs
679    */

680   public void rollback(long transactionId, String JavaDoc savepointName)
681       throws SQLException JavaDoc
682   {
683     try
684     {
685       if (requestLogger.isInfoEnabled())
686         requestLogger.info("R " + transactionId + " " + savepointName);
687       requestManager.rollback(transactionId, savepointName);
688     }
689     catch (SQLException JavaDoc e)
690     {
691       String JavaDoc msg = "Rollback to savepoint '" + savepointName + "' for "
692           + "transaction '" + transactionId + "' failed (" + e.getMessage()
693           + ")";
694       logger.warn(msg);
695       throw e;
696     }
697   }
698
699   /**
700    * Sets a unnamed savepoint to a transaction given its id.
701    *
702    * @param transactionId the transaction id
703    * @return the savepoint id
704    * @exception SQLException if an error occurs
705    */

706   public int setSavepoint(long transactionId) throws SQLException JavaDoc
707   {
708     try
709     {
710       int savepointId = requestManager.setSavepoint(transactionId);
711       if (requestLogger.isInfoEnabled())
712         requestLogger.info("P " + transactionId + " " + savepointId);
713       return savepointId;
714     }
715     catch (SQLException JavaDoc e)
716     {
717       String JavaDoc msg = "Setting unnamed savepoint to transaction '" + transactionId
718           + "' failed (" + e.getMessage() + ")";
719       logger.warn(msg);
720       throw e;
721     }
722   }
723
724   /**
725    * Sets a savepoint given its desired name to a transaction given its id.
726    *
727    * @param transactionId the transaction id
728    * @param name the desired name of the savepoint
729    * @exception SQLException if an error occurs
730    */

731   public void setSavepoint(long transactionId, String JavaDoc name) throws SQLException JavaDoc
732   {
733     try
734     {
735       if (requestLogger.isInfoEnabled())
736         requestLogger.info("P " + transactionId + " " + name);
737       requestManager.setSavepoint(transactionId, name);
738     }
739     catch (SQLException JavaDoc e)
740     {
741       String JavaDoc msg = "Setting savepoint with name '" + name + "' to transaction "
742           + "'" + transactionId + "' failed (" + e.getMessage() + ")";
743       logger.warn(msg);
744       throw e;
745     }
746   }
747
748   /**
749    * Releases a savepoint given its name from a transaction given its id.
750    *
751    * @param transactionId the transaction id
752    * @param name the name of the savepoint
753    * @exception SQLException if an error occurs
754    */

755   public void releaseSavepoint(long transactionId, String JavaDoc name)
756       throws SQLException JavaDoc
757   {
758     try
759     {
760       if (requestLogger.isInfoEnabled())
761         requestLogger.info("F " + transactionId + " " + name);
762       requestManager.releaseSavepoint(transactionId, name);
763     }
764     catch (SQLException JavaDoc e)
765     {
766       String JavaDoc msg = "Releasing savepoint with name '" + name + "' from "
767           + "transaction '" + transactionId + "' failed (" + e.getMessage()
768           + ")";
769       logger.warn(msg);
770       throw e;
771     }
772   }
773
774   //
775
// Database backends management
776
//
777

778   /**
779    * Add a backend to this virtual database.
780    *
781    * @param db the database backend to add
782    * @throws VirtualDatabaseException if an error occurs
783    */

784   public void addBackend(DatabaseBackend db) throws VirtualDatabaseException
785   {
786     this.addBackend(db, true);
787   }
788
789   /**
790    * Add a backend to this virtual database.
791    *
792    * @param db the database backend to add
793    * @param checkForCompliance should load the driver ?
794    * @throws VirtualDatabaseException if an error occurs
795    */

796   public void addBackend(DatabaseBackend db, boolean checkForCompliance)
797       throws VirtualDatabaseException
798   {
799     if (db == null)
800     {
801       String JavaDoc msg = "Illegal null database backend in addBackend(DatabaseBackend) method";
802       logger.error(msg);
803       throw new VirtualDatabaseException(msg);
804     }
805
806     if (db.isReadEnabled())
807     {
808       String JavaDoc msg = "It is not allowed to add an enabled database.";
809       logger.error(msg);
810       throw new VirtualDatabaseException(msg);
811     }
812
813     // Get the lock on the list of backends
814
try
815     {
816       rwLock.acquireWrite();
817     }
818     catch (InterruptedException JavaDoc e)
819     {
820       String JavaDoc msg = Translate.get(
821           "loadbalancer.backendlist.acquire.writelock.failed", e);
822       logger.error(msg);
823       throw new VirtualDatabaseException(msg);
824     }
825
826     // Check that the backend is not already up
827
if (backends.indexOf(db) != -1)
828     {
829       rwLock.releaseWrite();
830       String JavaDoc msg = "Duplicate backend " + db.getURL();
831       logger.warn(msg);
832       throw new VirtualDatabaseException(msg);
833     }
834
835     // Check the authentication manager has all virtual logins defined
836
ArrayList JavaDoc logins = authenticationManager.getVirtualLogins();
837     VirtualDatabaseUser vdu;
838     String JavaDoc login;
839     for (int i = 0; i < logins.size(); i++)
840     {
841       vdu = (VirtualDatabaseUser) logins.get(i);
842       login = vdu.getLogin();
843       if (db.getConnectionManager(login) == null)
844       {
845         rwLock.releaseWrite();
846         throw new VirtualDatabaseException(Translate.get(
847             "backend.missing.connection.manager", login));
848       }
849     }
850
851     // Initialize the driver and check the compliance
852
try
853     {
854       if (logger.isDebugEnabled())
855         logger.debug("Checking driver compliance");
856       if (checkForCompliance)
857         db.checkDriverCompliance(); // Also loads the driver
858
}
859     catch (Exception JavaDoc e)
860     {
861       rwLock.releaseWrite();
862       String JavaDoc msg = "Error while adding database backend " + db.getName() + " ("
863           + e + ")";
864       logger.warn(msg);
865       throw new VirtualDatabaseException(msg);
866     }
867
868     db.setSqlShortFormLength(getSQLShortFormLength());
869
870     // Add the backend to the list
871
backends.add(db);
872     if (logger.isDebugEnabled())
873       logger.debug("Backend " + db.getName() + " added successfully");
874
875     // Set the backend state listener so that the state is logged into the
876
// recovery log - if any. When there is no recovery log,
877
// getBackendStateListener returns null, and stateListener is consequently
878
// set to null. Looks like the only state listner ever is the recoveryLog.
879
/*
880      * Note: getRequestManager() is null, at load time, thus the test. At load
881      * time, if there is a recovery log, the state listner eventually gets set,
882      * but in a different fashion: it is set by RequestManager c'tor, when
883      * calling setRecoveryLog().
884      */

885     if (getRequestManager() != null)
886     {
887       db.setStateListener(getRequestManager().getBackendStateListener());
888     }
889
890     // Release the lock
891
rwLock.releaseWrite();
892
893     // Notify Jmx listeners of the backend addition
894
if (MBeanServerManager.isJmxEnabled())
895     {
896       // Send notification
897
Hashtable JavaDoc data = new Hashtable JavaDoc();
898       data.put(CjdbcNotificationList.DATA_DATABASE, this.name);
899       data.put(CjdbcNotificationList.DATA_DRIVER, db.getDriverClassName());
900       String JavaDoc checkpoint = db.getLastKnownCheckpoint();
901       checkpoint = (checkpoint == null) ? "" : checkpoint;
902       data.put(CjdbcNotificationList.DATA_CHECKPOINT, checkpoint);
903       data.put(CjdbcNotificationList.DATA_NAME, db.getName());
904       data.put(CjdbcNotificationList.DATA_URL, db.getURL());
905       RmiConnector.broadcastNotification(this,
906           CjdbcNotificationList.VIRTUALDATABASE_BACKEND_ADDED,
907           CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
908               "notification.backend.added", db.getName()), data);
909
910       // Add backend mbean to jmx server
911
ObjectName JavaDoc objectName = JmxConstants.getDatabaseBackendObjectName(name,
912           db.getName());
913       try
914       {
915         MBeanServerManager.registerMBean(db, objectName);
916       }
917       catch (JmxException e1)
918       {
919         logger.error(Translate.get(
920             "virtualdatabase.fail.register.backend.mbean", db.getName()), e1);
921       }
922     }
923   }
924
925   /**
926    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#forceDisableBackend(String)
927    */

928   public void forceDisableBackend(String JavaDoc backendName)
929       throws VirtualDatabaseException
930   {
931     try
932     {
933       DatabaseBackend db = getAndCheckBackend(backendName,
934           CHECK_BACKEND_DISABLE);
935       requestManager.disableBackend(db);
936       requestManager.setDatabaseSchema(
937           getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames(),
938           false);
939
940       // Send notification
941
if (MBeanServerManager.isJmxEnabled())
942       {
943         Hashtable JavaDoc data = new Hashtable JavaDoc();
944         data.put("driver", db.getDriverClassName());
945         String JavaDoc checkpoint = db.getLastKnownCheckpoint();
946         checkpoint = (checkpoint == null) ? "" : checkpoint;
947         data.put("checkpoint", checkpoint);
948         data.put("name", db.getName());
949         data.put("url", db.getURL());
950         RmiConnector.broadcastNotification(this,
951             CjdbcNotificationList.VIRTUALDATABASE_BACKEND_DISABLED,
952             CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
953                 "notification.backend.disabled", db.getName()), data);
954       }
955     }
956     catch (Exception JavaDoc e)
957     {
958       logger.error("An error occured while disabling backend " + backendName
959           + " (" + e + ")");
960       throw new VirtualDatabaseException(e.getMessage());
961     }
962   }
963
964   /**
965    * Prepare this virtual database for shutdown. This turns off all the backends
966    * by cutting communication from this database. This does not prevents other
967    * virtual database to use shared backends. This doesn't create checkpoints
968    * either.
969    *
970    * @throws VirtualDatabaseException if an error occurs
971    */

972   public void disableAllBackends() throws VirtualDatabaseException
973   {
974     try
975     {
976       int size = this.backends.size();
977       DatabaseBackend dbe;
978       for (int i = 0; i < size; i++)
979       {
980         dbe = (DatabaseBackend) backends.get(i);
981         if (dbe.isReadEnabled())
982           requestManager.disableBackend(getAndCheckBackend(dbe.getName(),
983               CHECK_BACKEND_DISABLE));
984       }
985     }
986     catch (Exception JavaDoc e)
987     {
988       throw new VirtualDatabaseException(e.getMessage());
989     }
990   }
991
992   /**
993    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#disableBackendWithCheckpoint(String)
994    */

995   public void disableBackendWithCheckpoint(String JavaDoc backendName)
996       throws VirtualDatabaseException
997   {
998     try
999     {
1000      requestManager.disableBackendForCheckpoint(getAndCheckBackend(
1001          backendName, CHECK_BACKEND_DISABLE), "disable_backend_" + backendName
1002          + "_" + new Date JavaDoc(System.currentTimeMillis()).toString());
1003      requestManager.setDatabaseSchema(
1004          getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames(),
1005          false);
1006    }
1007    catch (Exception JavaDoc e)
1008    {
1009      logger.error("An error occured while disabling backend " + backendName
1010          + " (" + e + ")");
1011      throw new VirtualDatabaseException(e.getMessage());
1012    }
1013  }
1014
1015  /**
1016   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#disableAllBackendsWithCheckpoint(java.lang.String)
1017   */

1018  public void disableAllBackendsWithCheckpoint(String JavaDoc checkpoint)
1019      throws VirtualDatabaseException
1020  {
1021    if (checkpoint == null)
1022    {
1023      disableAllBackends();
1024      return;
1025    }
1026
1027    try
1028    {
1029      this.acquireReadLockBackendLists();
1030      requestManager.disableBackendsForCheckpoint(backends, checkpoint);
1031      this.releaseReadLockBackendLists();
1032    }
1033    catch (Exception JavaDoc e)
1034    {
1035      throw new VirtualDatabaseException(e.getMessage());
1036    }
1037  }
1038
1039  /**
1040   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#forceEnableBackend(String)
1041   */

1042  public void forceEnableBackend(String JavaDoc backendName)
1043      throws VirtualDatabaseException
1044  {
1045    // Call the Request Manager
1046
try
1047    {
1048      DatabaseBackend backend = getAndCheckBackend(backendName,
1049          CHECK_BACKEND_ENABLE);
1050
1051      requestManager.enableBackend(backend);
1052      requestManager.setSchemaIsDirty(true);
1053
1054      // Update the list of database product names
1055
if (databaseProductNames.indexOf(backend.getDatabaseProductName()) == -1)
1056        databaseProductNames += "," + backend.getDatabaseProductName();
1057
1058      // Update the static metadata
1059
getStaticMetaData().gatherStaticMetadata(backend);
1060
1061      // Send notification
1062
if (MBeanServerManager.isJmxEnabled())
1063      {
1064        Hashtable JavaDoc data = new Hashtable JavaDoc();
1065        data.put("driver", backend.getDriverClassName());
1066        String JavaDoc checkpoint = backend.getLastKnownCheckpoint();
1067        checkpoint = (checkpoint == null) ? "" : checkpoint;
1068        data.put("checkpoint", checkpoint);
1069        data.put("name", backend.getName());
1070        data.put("url", backend.getURL());
1071        RmiConnector.broadcastNotification(this,
1072            CjdbcNotificationList.VIRTUALDATABASE_BACKEND_ENABLED,
1073            CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
1074                "notification.backend.enabled", backend.getName()), data);
1075      }
1076    }
1077    catch (Exception JavaDoc e)
1078    {
1079      e.printStackTrace();
1080      throw new VirtualDatabaseException(e.getMessage());
1081    }
1082  }
1083
1084  /**
1085   * Enable the given backend from the given checkpoint. This method returns
1086   * once the recovery is complete.
1087   *
1088   * @param backendName backend to enable
1089   * @param checkpointName checkpoint to enable from
1090   * @throws VirtualDatabaseException if an error occurs
1091   */

1092  public void enableBackendFromCheckpoint(String JavaDoc backendName,
1093      String JavaDoc checkpointName) throws VirtualDatabaseException
1094  {
1095    // Call the Request Manager
1096
try
1097    {
1098      DatabaseBackend backend = getAndCheckBackend(backendName,
1099          CHECK_BACKEND_ENABLE);
1100      RecoverThread recoverThread = requestManager.enableBackendFromCheckpoint(
1101          backend, checkpointName);
1102      // Wait for recovery to complete
1103
recoverThread.join();
1104      requestManager.setSchemaIsDirty(true);
1105
1106      // Update the static metadata
1107
getStaticMetaData().gatherStaticMetadata(backend);
1108
1109      // Update the list of database product names
1110
if (databaseProductNames.indexOf(backend.getDatabaseProductName()) == -1)
1111        databaseProductNames += "," + backend.getDatabaseProductName();
1112    }
1113    catch (Exception JavaDoc e)
1114    {
1115      throw new VirtualDatabaseException(
1116          "Failed to enable backend from checkpoint: " + e);
1117    }
1118  }
1119
1120  /**
1121   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#enableBackendFromCheckpoint(java.lang.String)
1122   */

1123  public void enableBackendFromCheckpoint(String JavaDoc backendName)
1124      throws VirtualDatabaseException
1125  {
1126    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
1127    String JavaDoc checkpoint = backend.getLastKnownCheckpoint();
1128    if (checkpoint == null)
1129      throw new VirtualDatabaseException("No last checkpoint for backend:"
1130          + backendName);
1131    else
1132    {
1133      if (logger.isDebugEnabled())
1134        logger.debug("Enabling backend:" + backendName
1135            + " from its last checkpoint " + backend.getLastKnownCheckpoint());
1136    }
1137    enableBackendFromCheckpoint(backendName, backend.getLastKnownCheckpoint());
1138  }
1139
1140  /**
1141   * Enable all the backends without any check.
1142   *
1143   * @throws VirtualDatabaseException if fails
1144   */

1145  public void enableAllBackends() throws VirtualDatabaseException
1146  {
1147    try
1148    {
1149      int size = this.backends.size();
1150      DatabaseBackend dbe;
1151      for (int i = 0; i < size; i++)
1152      {
1153        dbe = (DatabaseBackend) backends.get(i);
1154        if (!dbe.isReadEnabled())
1155          forceEnableBackend(((DatabaseBackend) backends.get(i)).getName());
1156      }
1157    }
1158    catch (Exception JavaDoc e)
1159    {
1160      logger.error(e);
1161      throw new VirtualDatabaseException(e.getMessage());
1162    }
1163  }
1164
1165  /**
1166   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#enableAllBackendsFromCheckpoint()
1167   */

1168  public void enableAllBackendsFromCheckpoint() throws VirtualDatabaseException
1169  {
1170    RecoveryLog log = requestManager.getRecoveryLog();
1171    if (log == null)
1172    {// If no recovery log is defined ignore fallback to a forced enable
1173
logger
1174          .warn("No recovery log has been configured, enabling backend without checkpoint.");
1175      enableAllBackends();
1176    }
1177    else
1178    {
1179      try
1180      {
1181        int size = this.backends.size();
1182        DatabaseBackend dbe;
1183        String JavaDoc backendName;
1184        BackendRecoveryInfo info;
1185        for (int i = 0; i < size; i++)
1186        {
1187          dbe = (DatabaseBackend) backends.get(i);
1188          backendName = dbe.getName();
1189          info = log.getBackendRecoveryInfo(name, backendName);
1190          switch (info.getBackendState())
1191          {
1192            case BackendState.DISABLED :
1193              String JavaDoc checkpoint = info.getCheckpoint();
1194              if (checkpoint == null || checkpoint.equals(""))
1195              {
1196                logger.warn("Enabling backend " + backendName
1197                    + " with no checkpoint.");
1198                forceEnableBackend(dbe.getName());
1199              }
1200              else
1201              {
1202                logger.info("Enabling backend " + backendName
1203                    + " from checkpoint " + checkpoint);
1204                enableBackendFromCheckpoint(dbe.getName(), checkpoint);
1205              }
1206              continue;
1207            case BackendState.UNKNOWN :
1208              logger.info("Unknown last state for backend " + backendName
1209                  + ". Leaving node in "
1210                  + (dbe.isReadEnabled() ? "enabled" : "disabled") + " state.");
1211              continue;
1212            case BackendState.BACKUPING :
1213            case BackendState.DISABLING :
1214            case BackendState.RECOVERING :
1215            case BackendState.REPLAYING :
1216              if (!dbe.isReadEnabled())
1217              {
1218                logger.info("Unexpected transition state ("
1219                    + info.getBackendState() + ") for backend " + backendName
1220                    + ". Forcing backend to disabled state.");
1221                info.setBackendState(BackendState.DISABLED);
1222                log.storeBackendRecoveryInfo(name, info);
1223              }
1224              else
1225                logger.info("Unexpected transition state ("
1226                    + info.getBackendState() + ") for backend " + backendName
1227                    + ". Leaving backend in its current state.");
1228              continue;
1229            default :
1230              if (!dbe.isReadEnabled())
1231              {
1232                logger.info("Unexpected enabled state ("
1233                    + info.getBackendState() + ") for backend " + backendName
1234                    + ". Forcing backend to disabled state.");
1235                info.setBackendState(BackendState.DISABLED);
1236                log.storeBackendRecoveryInfo(name, info);
1237              }
1238              else
1239                logger.info("Unexpected enabled state ("
1240                    + info.getBackendState() + ") for backend " + backendName
1241                    + ". Leaving backend in its current state.");
1242              continue;
1243          }
1244        }
1245      }
1246      catch (Exception JavaDoc e)
1247      {
1248        throw new VirtualDatabaseException(e.getMessage());
1249      }
1250    }
1251
1252  }
1253
1254  /**
1255   * Prepare this virtual database for startup. This turns on all the backends
1256   * from the given checkpoint. If the checkpoint is null or an empty String,
1257   * the backends are enabled without further check else the backend states are
1258   * overriden to use the provided checkpoint.
1259   *
1260   * @param checkpoint checkpoint for recovery log
1261   * @throws VirtualDatabaseException if fails
1262   */

1263  public void forceEnableAllBackendsFromCheckpoint(String JavaDoc checkpoint)
1264      throws VirtualDatabaseException
1265  {
1266    if (checkpoint == null || checkpoint.equals(""))
1267      enableAllBackends();
1268    else
1269    {
1270      try
1271      {
1272        int size = this.backends.size();
1273        DatabaseBackend backend;
1274        for (int i = 0; i < size; i++)
1275        {
1276          backend = (DatabaseBackend) backends.get(i);
1277          if (!backend.isReadEnabled())
1278          {
1279            backend.setLastKnownCheckpoint(checkpoint);
1280            enableBackendFromCheckpoint(backend.getName(), checkpoint);
1281          }
1282        }
1283      }
1284      catch (Exception JavaDoc e)
1285      {
1286        throw new VirtualDatabaseException(e.getMessage());
1287      }
1288    }
1289  }
1290
1291  /**
1292   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getAllBackendNames()
1293   */

1294  public ArrayList JavaDoc getAllBackendNames() throws VirtualDatabaseException
1295  {
1296    try
1297    {
1298      acquireReadLockBackendLists();
1299    }
1300    catch (InterruptedException JavaDoc e)
1301    {
1302      String JavaDoc msg = "Unable to acquire read lock on backend list in getAllBackendNames ("
1303          + e + ")";
1304      logger.error(msg);
1305      throw new VirtualDatabaseException(msg);
1306    }
1307
1308    int size = backends.size();
1309    ArrayList JavaDoc result = new ArrayList JavaDoc();
1310    for (int i = 0; i < size; i++)
1311    {
1312      result.add(((DatabaseBackend) backends.get(i)).getName());
1313    }
1314
1315    releaseReadLockBackendLists();
1316    return result;
1317  }
1318
1319  /**
1320   * Find the DatabaseBackend corresponding to the given backend name and check
1321   * if it is possible to disable this backend. In the case enable, this method
1322   * also updates the virtual database schema by merging it with the one
1323   * provided by this backend.
1324   *
1325   * @param backendName backend to look for
1326   * @param testEnable NO_CHECK_BACKEND no check is done, CHECK_BACKEND_DISABLE
1327   * check if it is possible to disable the backend,
1328   * CHECK_BACKEND_ENABLE check if it is possible to enable the backend
1329   * @return the backend to disable
1330   * @throws VirtualDatabaseException if an error occurs
1331   */

1332  public DatabaseBackend getAndCheckBackend(String JavaDoc backendName, int testEnable)
1333      throws VirtualDatabaseException
1334  {
1335    try
1336    {
1337      acquireReadLockBackendLists();
1338    }
1339    catch (InterruptedException JavaDoc e)
1340    {
1341      String JavaDoc msg = "Unable to acquire read lock on backend list in getAndCheckBackend ("
1342          + e + ")";
1343      logger.error(msg);
1344      throw new VirtualDatabaseException(msg);
1345    }
1346
1347    // Find the backend
1348
int size = backends.size();
1349    DatabaseBackend b = null;
1350    for (int i = 0; i < size; i++)
1351    {
1352      b = (DatabaseBackend) backends.get(i);
1353      if (b.getName().equals(backendName))
1354        break;
1355      else
1356        b = null;
1357    }
1358
1359    // Check not null
1360
if (b == null)
1361    {
1362      releaseReadLockBackendLists();
1363      String JavaDoc msg = "Trying to access a non-existing backend " + backendName;
1364      logger.warn(msg);
1365      throw new VirtualDatabaseException(msg);
1366    }
1367
1368    // Check enable/disable
1369
switch (testEnable)
1370    {
1371      case NO_CHECK_BACKEND :
1372        break;
1373      case CHECK_BACKEND_DISABLE :
1374        if (!b.isReadEnabled())
1375        {
1376          releaseReadLockBackendLists();
1377          String JavaDoc msg = "Backend " + backendName + " is already disabled";
1378          logger.warn(msg);
1379          throw new VirtualDatabaseException(msg);
1380        }
1381        break;
1382      case CHECK_BACKEND_ENABLE :
1383        if (b.isReadEnabled())
1384        {
1385          releaseReadLockBackendLists();
1386          String JavaDoc msg = "Backend " + backendName + " is already enabled";
1387          logger.warn(msg);
1388          throw new VirtualDatabaseException(msg);
1389        }
1390        break;
1391      default :
1392        releaseReadLockBackendLists();
1393        String JavaDoc msg = "Unexpected parameter in getAndCheckBackend(...)";
1394        logger.error(msg);
1395        throw new VirtualDatabaseException(msg);
1396    }
1397
1398    releaseReadLockBackendLists();
1399
1400    if (testEnable == CHECK_BACKEND_ENABLE)
1401    {
1402      // Initialize backend for enable
1403
try
1404      {
1405        if (logger.isDebugEnabled())
1406          logger.debug("Initializing connections for backend " + b.getName());
1407        b.initializeConnections();
1408
1409        b.checkDriverCompliance();
1410
1411        if (logger.isDebugEnabled())
1412          logger.debug("Checking schema for backend " + b.getName());
1413        b.checkDatabaseSchema();
1414
1415        DatabaseSchema backendSchema = b.getDatabaseSchema();
1416
1417        if (backendSchema != null)
1418          requestManager.mergeDatabaseSchema(backendSchema);
1419        else
1420          logger.warn("Backend " + b.getName() + " has no defined schema.");
1421      }
1422      catch (SQLException JavaDoc e)
1423      {
1424        String JavaDoc msg = "Error while initalizing database backend " + b.getName()
1425            + " (" + e + ")";
1426        logger.warn(msg, e);
1427        throw new VirtualDatabaseException(msg);
1428      }
1429    }
1430
1431    return b;
1432  }
1433
1434  /**
1435   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#replicateBackend(java.lang.String,
1436   * java.lang.String, java.util.Map)
1437   */

1438  public void replicateBackend(String JavaDoc backendName, String JavaDoc newBackendName,
1439      Map JavaDoc parameters) throws VirtualDatabaseException
1440  {
1441    // Access the backend we want to replicate
1442
DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
1443    DatabaseBackend newBackend = null;
1444
1445    // Create a clone of the backend with additionnal parameters
1446
try
1447    {
1448      newBackend = backend.copy(newBackendName, parameters);
1449    }
1450    catch (Exception JavaDoc e)
1451    {
1452      String JavaDoc msg = Translate.get("virtualdatabase.fail.backend.copy");
1453      logger.warn(msg, e);
1454      throw new VirtualDatabaseException(msg);
1455    }
1456
1457    // Add the backend to the virtual database.
1458
addBackend(newBackend);
1459  }
1460
1461  /**
1462   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#removeBackend(java.lang.String)
1463   */

1464  public void removeBackend(String JavaDoc backend) throws VirtualDatabaseException
1465  {
1466    removeBackend(getAndCheckBackend(backend, NO_CHECK_BACKEND));
1467  }
1468
1469  /**
1470   * Remove a backend from this virtual database.
1471   *
1472   * @param db the database backend to remove
1473   * @throws VirtualDatabaseException if an error occurs
1474   */

1475  public void removeBackend(DatabaseBackend db) throws VirtualDatabaseException
1476  {
1477    if (db == null)
1478    {
1479      String JavaDoc msg = "Illegal null database backend in removeBackend(DatabaseBackend) method";
1480      logger.error(msg);
1481      throw new VirtualDatabaseException(msg);
1482    }
1483
1484    try
1485    {
1486      rwLock.acquireWrite();
1487    }
1488    catch (InterruptedException JavaDoc e)
1489    {
1490      String JavaDoc msg = Translate.get(
1491          "loadbalancer.backendlist.acquire.writelock.failed", e);
1492      logger.error(msg);
1493      throw new VirtualDatabaseException(msg);
1494    }
1495
1496    // Sanity checks
1497
int idx = backends.indexOf(db);
1498    if (idx == -1)
1499    {
1500      rwLock.releaseWrite(); // Release the lock
1501
String JavaDoc msg = "Trying to remove a non-existing backend " + db.getName();
1502      logger.warn(msg);
1503      throw new VirtualDatabaseException(msg);
1504    }
1505
1506    if (((DatabaseBackend) backends.get(idx)).isReadEnabled())
1507    {
1508      rwLock.releaseWrite(); // Release the lock
1509
String JavaDoc msg = "Trying to remove an enabled backend " + db.getName();
1510      logger.error(msg);
1511      throw new VirtualDatabaseException(msg);
1512    }
1513
1514    // Remove it
1515
backends.remove(idx);
1516    rwLock.releaseWrite(); // Relase the lock
1517

1518    // Send notification
1519
if (MBeanServerManager.isJmxEnabled())
1520    {
1521      // Send notification
1522
Hashtable JavaDoc data = new Hashtable JavaDoc();
1523      data.put(CjdbcNotificationList.DATA_DATABASE, this.name);
1524      data.put(CjdbcNotificationList.DATA_DRIVER, db.getDriverClassName());
1525      String JavaDoc checkpoint = db.getLastKnownCheckpoint();
1526      checkpoint = (checkpoint == null) ? "" : checkpoint;
1527      data.put(CjdbcNotificationList.DATA_CHECKPOINT, checkpoint);
1528      data.put(CjdbcNotificationList.DATA_NAME, db.getName());
1529      data.put(CjdbcNotificationList.DATA_URL, db.getURL());
1530      RmiConnector.broadcastNotification(this,
1531          CjdbcNotificationList.VIRTUALDATABASE_BACKEND_REMOVED,
1532          CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
1533              "notification.backend.removed", db.getName()), data);
1534
1535      // Remove backend mbean to jmx server
1536
ObjectName JavaDoc objectName = JmxConstants.getDatabaseBackendObjectName(name,
1537          db.getName());
1538      try
1539      {
1540        MBeanServerManager.unregister(objectName);
1541      }
1542      catch (JmxException e1)
1543      {
1544        logger.error(Translate.get(
1545            "virtualdatabase.fail.unregister.backend.mbean", db.getName()), e1);
1546      }
1547    }
1548
1549    if (logger.isDebugEnabled())
1550      logger.debug("Backend " + db.getName() + " removed successfully");
1551  }
1552
1553  /**
1554   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#transferBackend(java.lang.String,
1555   * java.lang.String)
1556   */

1557  public void transferBackend(String JavaDoc backend, String JavaDoc controllerDestination)
1558      throws VirtualDatabaseException
1559  {
1560    throw new VirtualDatabaseException("Cannot transfer backend to controller:"
1561        + controllerDestination + " because database is not distributed");
1562  }
1563
1564  //
1565
// Backup & Checkpoint management
1566
//
1567

1568  /**
1569   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackuperNames()
1570   */

1571  public String JavaDoc[] getBackuperNames()
1572  {
1573    return requestManager.getBackupManager().getBackuperNames();
1574  }
1575
1576  /**
1577   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getDumpFormatForBackuper(java.lang.String)
1578   */

1579  public String JavaDoc getDumpFormatForBackuper(String JavaDoc backuperName)
1580  {
1581    Backuper backuper = requestManager.getBackupManager().getBackuperByName(
1582        backuperName);
1583    if (backuper == null)
1584    {
1585      return null;
1586    }
1587    return backuper.getDumpFormat();
1588  }
1589
1590  /**
1591   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#backupBackend(String,
1592   * String, String, String, String, String, ArrayList)
1593   */

1594  public void backupBackend(String JavaDoc backendName, String JavaDoc login, String JavaDoc password,
1595      String JavaDoc dumpName, String JavaDoc backuperName, String JavaDoc path, ArrayList JavaDoc tables)
1596      throws VirtualDatabaseException
1597  {
1598    try
1599    {
1600      DatabaseBackend db = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
1601      requestManager.backupBackend(db, login, password, dumpName, backuperName,
1602          path, tables);
1603    }
1604    catch (SQLException JavaDoc sql)
1605    {
1606      throw new VirtualDatabaseException(sql);
1607    }
1608  }
1609
1610  /**
1611   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getAvailableDumps()
1612   */

1613  public DumpInfo[] getAvailableDumps() throws VirtualDatabaseException
1614  {
1615    try
1616    {
1617      RecoveryLog recoveryLog = requestManager.getRecoveryLog();
1618      if (recoveryLog == null)
1619      {
1620        return new DumpInfo[0];
1621      }
1622      else
1623      {
1624        ArrayList JavaDoc dumps = recoveryLog.getDumpList();
1625        return (DumpInfo[]) dumps.toArray(new DumpInfo[dumps.size()]);
1626      }
1627    }
1628    catch (SQLException JavaDoc e)
1629    {
1630      throw new VirtualDatabaseException(e);
1631    }
1632  }
1633
1634  /**
1635   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#updateDumpPath(java.lang.String,
1636   * java.lang.String)
1637   */

1638  public void updateDumpPath(String JavaDoc dumpName, String JavaDoc newPath)
1639      throws VirtualDatabaseException
1640  {
1641    try
1642    {
1643      RecoveryLog recoveryLog = requestManager.getRecoveryLog();
1644      if (recoveryLog == null)
1645      {
1646        throw new VirtualDatabaseException("no recovery log"); // TODO I18N
1647
}
1648      else
1649      {
1650        recoveryLog.updateDumpPath(dumpName, newPath);
1651      }
1652    }
1653    catch (SQLException JavaDoc e)
1654    {
1655      throw new VirtualDatabaseException(e);
1656    }
1657  }
1658
1659  /**
1660   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#removeDump(String)
1661   */

1662  public boolean removeDump(String JavaDoc dumpName)
1663  {
1664    if (dumpName == null)
1665    {
1666      return false;
1667    }
1668    RecoveryLog recoveryLog = requestManager.getRecoveryLog();
1669    if (recoveryLog == null)
1670    {
1671      return false;
1672    }
1673    else
1674    {
1675      try
1676      {
1677        DumpInfo dumpInfo = recoveryLog.getDumpInfo(dumpName);
1678        if (dumpInfo == null)
1679        {
1680          return false;
1681        }
1682        Backuper backuper = requestManager.getBackupManager()
1683            .getBackuperByFormat(dumpInfo.getDumpFormat());
1684        if (backuper == null)
1685        {
1686          return false;
1687        }
1688        recoveryLog.removeDump(dumpInfo);
1689        backuper.deleteDump(dumpInfo.getDumpPath(), dumpInfo.getDumpName());
1690        return true;
1691      }
1692      catch (Exception JavaDoc e)
1693      {
1694        String JavaDoc msg = Translate.get("virtualdatabase.removeDump.failure",
1695            new String JavaDoc[]{dumpName, e.getMessage()});
1696        logger.error(msg);
1697        return false;
1698      }
1699    }
1700  }
1701
1702  /**
1703   * Remove a checkpoint from the recovery log of this virtual database
1704   *
1705   * @param checkpointName to remove
1706   * @throws VirtualDatabaseException if an error occurs
1707   */

1708  public void removeCheckpoint(String JavaDoc checkpointName)
1709      throws VirtualDatabaseException
1710  {
1711    try
1712    {
1713      requestManager.removeCheckpoint(checkpointName);
1714    }
1715    catch (Exception JavaDoc e)
1716    {
1717      throw new VirtualDatabaseException(e.getMessage());
1718    }
1719  }
1720
1721  /**
1722   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#restoreDumpOnBackend(String,
1723   * String, String, String, ArrayList)
1724   */

1725  public void restoreDumpOnBackend(String JavaDoc databaseBackendName, String JavaDoc login,
1726      String JavaDoc password, String JavaDoc dumpName, ArrayList JavaDoc tables)
1727      throws VirtualDatabaseException
1728  {
1729    DatabaseBackend backend = getAndCheckBackend(databaseBackendName,
1730        NO_CHECK_BACKEND);
1731    // Backend cannot be null, otherwise the above throws an
1732
// VirtualDatabaseException
1733
try
1734    {
1735      requestManager.restoreBackendFromBackupCheckpoint(backend, login,
1736          password, dumpName, tables);
1737    }
1738    catch (BackupException e)
1739    {
1740      throw new VirtualDatabaseException(e);
1741    }
1742  }
1743
1744  /**
1745   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#setBackendLastKnownCheckpoint
1746   */

1747  public void setBackendLastKnownCheckpoint(String JavaDoc backendName,
1748      String JavaDoc checkpoint) throws VirtualDatabaseException
1749  {
1750    RecoveryLog log = requestManager.getRecoveryLog();
1751    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
1752    if (log == null)
1753      throw new VirtualDatabaseException("No recovery log has been defined");
1754    else
1755    {
1756      if (!backend.isDisabled())
1757        throw new VirtualDatabaseException(
1758            "Cannot setLastKnownCheckpoint on a non-disabled backend");
1759      else
1760      {
1761        try
1762        {
1763          log.storeBackendRecoveryInfo(this.name,
1764              new BackendRecoveryInfo(backend.getName(), checkpoint, backend
1765                  .getStateValue(), this.name));
1766
1767          backend.setLastKnownCheckpoint(checkpoint);
1768        }
1769        catch (SQLException JavaDoc e)
1770        {
1771          throw new VirtualDatabaseException(
1772              "Failed to store recovery info for backend '" + backendName
1773                  + "' (" + e + ")");
1774        }
1775      }
1776    }
1777  }
1778
1779  /**
1780   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewCheckpointNames()
1781   */

1782  public ArrayList JavaDoc viewCheckpointNames()
1783  {
1784    try
1785    {
1786      RecoveryLog recoveryLog = requestManager.getRecoveryLog();
1787      if (recoveryLog == null)
1788        return new ArrayList JavaDoc();
1789      else
1790        return recoveryLog.getCheckpointNames();
1791    }
1792    catch (SQLException JavaDoc e)
1793    {
1794      return new ArrayList JavaDoc();
1795    }
1796  }
1797
1798  //
1799
// Thread management mainly used by controller and monitoring
1800
//
1801

1802  /**
1803   * Adds one to currentNbOfThreads. Warning! This method is not synchronized.
1804   */

1805  public void addCurrentNbOfThread()
1806  {
1807    currentNbOfThreads++;
1808  }
1809
1810  /**
1811   * Method add an idle thread. Warning! This method must be called in a
1812   * synchronized block on activeThreads.
1813   */

1814  public void addIdleThread()
1815  {
1816    idleThreads++;
1817  }
1818
1819  /**
1820   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getCurrentNbOfThreads()
1821   */

1822  public int getCurrentNbOfThreads()
1823  {
1824    return currentNbOfThreads;
1825  }
1826
1827  /**
1828   * Returns the number of idle zorker threads. Warning! This method must be
1829   * called in a synchronized block on activeThreads.
1830   *
1831   * @return int
1832   */

1833  public int getIdleThreads()
1834  {
1835    return idleThreads;
1836  }
1837
1838  /**
1839   * Returns the maxNbOfThreads.
1840   *
1841   * @return int maximum number of threads
1842   */

1843  public int getMaxNbOfThreads()
1844  {
1845    return maxNbOfThreads;
1846  }
1847
1848  /**
1849   * Returns the maxThreadIdleTime.
1850   *
1851   * @return long maximum thread idle time in ms
1852   */

1853  public long getMaxThreadIdleTime()
1854  {
1855    return maxThreadIdleTime;
1856  }
1857
1858  /**
1859   * Returns the minNbOfThreads.
1860   *
1861   * @return int minimum number of threads
1862   */

1863  public int getMinNbOfThreads()
1864  {
1865    return minNbOfThreads;
1866  }
1867
1868  /**
1869   * Returns the poolConnectionThreads.
1870   *
1871   * @return boolean true if threads are pooled
1872   */

1873  public boolean isPoolConnectionThreads()
1874  {
1875    return poolConnectionThreads;
1876  }
1877
1878  /**
1879   * Substract one to currentNbOfThreads. Warning! This method is not
1880   * synchronized.
1881   */

1882  public void removeCurrentNbOfThread()
1883  {
1884    currentNbOfThreads--;
1885  }
1886
1887  /**
1888   * Remove an idle thread. Warning! This method must be called in a
1889   * synchronized block on activeThreads.
1890   */

1891  public void removeIdleThread()
1892  {
1893    idleThreads--;
1894  }
1895
1896  /**
1897   * Sets the maxThreadIdleTime.
1898   *
1899   * @param maxThreadIdleTime The maxThreadIdleTime to set
1900   */

1901  public void setMaxThreadIdleTime(long maxThreadIdleTime)
1902  {
1903    this.maxThreadIdleTime = maxThreadIdleTime;
1904  }
1905
1906  /**
1907   * Sets the minNbOfThreads.
1908   *
1909   * @param minNbOfThreads The minNbOfThreads to set
1910   */

1911  public void setMinNbOfThreads(int minNbOfThreads)
1912  {
1913    this.minNbOfThreads = minNbOfThreads;
1914  }
1915
1916  /**
1917   * Sets the poolConnectionThreads.
1918   *
1919   * @param poolConnectionThreads The poolConnectionThreads to set
1920   */

1921  public void setPoolConnectionThreads(boolean poolConnectionThreads)
1922  {
1923    this.poolConnectionThreads = poolConnectionThreads;
1924  }
1925
1926  //
1927
// Getter/Setter and tools (equals, ...)
1928
//
1929

1930  /**
1931   * Returns the activeThreads.
1932   *
1933   * @return ArrayList
1934   */

1935  public ArrayList JavaDoc getActiveThreads()
1936  {
1937    return activeThreads;
1938  }
1939
1940  /**
1941   * Returns the authentication manager of this virtual database.
1942   *
1943   * @return an <code>AuthenticationManager</code> instance
1944   */

1945  public AuthenticationManager getAuthenticationManager()
1946  {
1947    return authenticationManager;
1948  }
1949
1950  /**
1951   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendInformation(String)
1952   */

1953  public String JavaDoc getBackendInformation(String JavaDoc backendName)
1954      throws VirtualDatabaseException
1955  {
1956    try
1957    {
1958      acquireReadLockBackendLists();
1959    }
1960    catch (InterruptedException JavaDoc e)
1961    {
1962      String JavaDoc msg = "Unable to acquire read lock on backend list in getBackendInformation ("
1963          + e + ")";
1964      logger.error(msg);
1965      throw new VirtualDatabaseException(msg);
1966    }
1967
1968    // Find the backend
1969
int size = backends.size();
1970    DatabaseBackend b = null;
1971    for (int i = 0; i < size; i++)
1972    {
1973      b = (DatabaseBackend) backends.get(i);
1974      if (b.getName().equals(backendName))
1975        break;
1976      else
1977        b = null;
1978    }
1979
1980    if (b == null)
1981    {
1982      releaseReadLockBackendLists();
1983      String JavaDoc msg = "Backend " + backendName + " does not exists.";
1984      logger.warn(msg);
1985      throw new VirtualDatabaseException(msg);
1986    }
1987
1988    releaseReadLockBackendLists();
1989    return b.getXml();
1990  }
1991
1992  /**
1993   * Return the list of all backends
1994   *
1995   * @return <code>ArrayList</code> of <code>DatabaseBackend</code> Objects
1996   */

1997  public ArrayList JavaDoc getBackends()
1998  {
1999    return backends;
2000  }
2001
2002  /**
2003   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendSchema(java.lang.String)
2004   */

2005  public String JavaDoc getBackendSchema(String JavaDoc backendName)
2006      throws VirtualDatabaseException
2007  {
2008    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
2009    // we know the backend is not null, otherwise we have a
2010
// VirtualDatabaseException ...
2011
try
2012    {
2013      return XmlTools.prettyXml(backend.getSchemaXml(true));
2014    }
2015    catch (Exception JavaDoc e)
2016    {
2017      throw new VirtualDatabaseException(e.getMessage());
2018    }
2019  }
2020
2021  /**
2022   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendState(java.lang.String)
2023   */

2024  public String JavaDoc getBackendState(String JavaDoc backendName)
2025      throws VirtualDatabaseException
2026  {
2027    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
2028    return backend.getState();
2029  }
2030
2031  /**
2032   * Return the BLOB filter used for this database
2033   *
2034   * @return the BLOB filter used for this database.
2035   */

2036  public AbstractBlobFilter getBlobFilter()
2037  {
2038    return blobFilter;
2039  }
2040
2041  /**
2042   * Gets the virtual database name to be used by the client (C-JDBC driver)
2043   * This method should be used for local references only (it is faster). For
2044   * remote RMI calls, use {@link #getVirtualDatabaseName()}.
2045   *
2046   * @return the virtual database name
2047   * @see VirtualDatabase#getVirtualDatabaseName()
2048   */

2049  public String JavaDoc getDatabaseName()
2050  {
2051    return name;
2052  }
2053
2054  /**
2055   * @see org.objectweb.cjdbc.driver.DatabaseMetaData#getDatabaseProductName()
2056   */

2057  public String JavaDoc getDatabaseProductName()
2058  {
2059    return databaseProductNames;
2060  }
2061
2062  /**
2063   * @see org.objectweb.cjdbc.driver.DatabaseMetaData
2064   * @return associated metada for this database
2065   */

2066  public VirtualDatabaseDynamicMetaData getDynamicMetaData()
2067  {
2068    if (metadata == null)
2069    {
2070      metadata = new VirtualDatabaseDynamicMetaData(this);
2071    }
2072    return metadata;
2073  }
2074
2075  /**
2076   * Get the current database schema from merging the schemas of all active
2077   * backends.
2078   *
2079   * @return the current database schema dynamically gathered
2080   * @throws SQLException if an error occurs
2081   */

2082  public DatabaseSchema getDatabaseSchemaFromActiveBackends()
2083      throws SQLException JavaDoc
2084  {
2085    boolean isRaidb1 = requestManager.getLoadBalancer().getRAIDbLevel() == RAIDbLevels.RAIDb1;
2086
2087    try
2088    {
2089      acquireReadLockBackendLists();
2090    }
2091    catch (InterruptedException JavaDoc e)
2092    {
2093      String JavaDoc msg = "Unable to acquire read lock on backend list in getDatabaseSchemaFromActiveBackends ("
2094          + e + ")";
2095      logger.error(msg);
2096      throw new SQLException JavaDoc(msg);
2097    }
2098
2099    // Build the new schema from all active backend's schemas
2100
int size = backends.size();
2101    DatabaseSchema schema = null;
2102    DatabaseBackend b = null;
2103    for (int i = 0; i < size; i++)
2104    {
2105      b = (DatabaseBackend) backends.get(i);
2106      if (b.isReadEnabled())
2107      {
2108        if (schema == null)
2109          schema = new DatabaseSchema(b.getDatabaseSchema());
2110        else
2111          schema.mergeSchema(b.getDatabaseSchema());
2112
2113        // In RAIDb-1 all backends are the same so there is no need to merge and
2114
// we just take the schema of the first backend
2115
if (isRaidb1)
2116          break;
2117      }
2118    }
2119
2120    // Note that if the RecoveryLog points to the same database it will appear
2121
// in the database schema but this is a normal behavior.
2122

2123    releaseReadLockBackendLists();
2124
2125    return schema;
2126  }
2127
2128  /**
2129   * Get the current database schema from merging the schemas of all active
2130   * backends. This is needed when a backend is disabled.
2131   *
2132   * @return the current database schema dynamically gathered
2133   * @throws SQLException if an error occurs
2134   */

2135  public DatabaseSchema getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames()
2136      throws SQLException JavaDoc
2137  {
2138    try
2139    {
2140      acquireReadLockBackendLists();
2141    }
2142    catch (InterruptedException JavaDoc e)
2143    {
2144      String JavaDoc msg = "Unable to acquire read lock on backend list in getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames ("
2145          + e + ")";
2146      logger.error(msg);
2147      throw new SQLException JavaDoc(msg);
2148    }
2149
2150    // Build the new schema from all active backend's schemas
2151
int size = backends.size();
2152    DatabaseSchema schema = null;
2153    DatabaseBackend b = null;
2154    String JavaDoc dbProductNames = "C-JDBC";
2155    for (int i = 0; i < size; i++)
2156    {
2157      b = (DatabaseBackend) backends.get(i);
2158      if (b.isReadEnabled())
2159      {
2160        if (schema == null)
2161          schema = new DatabaseSchema(b.getDatabaseSchema());
2162        else
2163          schema.mergeSchema(b.getDatabaseSchema());
2164      }
2165
2166      // Update the list of database product names
2167
if (dbProductNames.indexOf(b.getDatabaseProductName()) == -1)
2168        dbProductNames += "," + b.getDatabaseProductName();
2169    }
2170
2171    releaseReadLockBackendLists();
2172    databaseProductNames = dbProductNames;
2173
2174    // Note that if the RecoveryLog points to the same database it will appear
2175
// in the database schema but this is a normal behavior.
2176

2177    return schema;
2178  }
2179
2180  /**
2181   * Returns the maxNbOfConnections.
2182   *
2183   * @return int
2184   */

2185  public int getMaxNbOfConnections()
2186  {
2187    return maxNbOfConnections;
2188  }
2189
2190  /**
2191   * Returns the pendingConnections.
2192   *
2193   * @return ArrayList
2194   */

2195  public ArrayList JavaDoc getPendingConnections()
2196  {
2197    return pendingConnections;
2198  }
2199
2200  /**
2201   * Gets the request manager associated to this database.
2202   *
2203   * @return a <code>RequestManager</code> instance
2204   */

2205  public RequestManager getRequestManager()
2206  {
2207    return requestManager;
2208  }
2209
2210  /**
2211   * Get the whole static metadata for this virtual database. A new empty
2212   * metadata object is created if there was none yet. It will be filled later
2213   * by gatherStaticMetadata() when the backend is enabled.
2214   *
2215   * @return Virtual database static metadata
2216   */

2217  public VirtualDatabaseStaticMetaData getStaticMetaData()
2218  {
2219    if (staticMetadata == null)
2220    {
2221      staticMetadata = new VirtualDatabaseStaticMetaData(this);
2222    }
2223    return staticMetadata;
2224  }
2225
2226  /**
2227   * Gets the virtual database name to be used by the client (C-JDBC driver)
2228   *
2229   * @return the virtual database name
2230   */

2231  public String JavaDoc getVirtualDatabaseName()
2232  {
2233    return name;
2234  }
2235
2236  /**
2237   * Returns the current SQL monitor
2238   *
2239   * @return a <code>SQLMonitoring</code> instance or null if no monitor is
2240   * defined
2241   */

2242  public SQLMonitoring getSQLMonitor()
2243  {
2244    return sqlMonitor;
2245  }
2246
2247  /**
2248   * Return the sql short form length to use when reporting an error.
2249   *
2250   * @return sql short form length
2251   * @see org.objectweb.cjdbc.common.sql.AbstractRequest#getSQLShortForm(int)
2252   */

2253  public int getSQLShortFormLength()
2254  {
2255    return sqlShortFormLength;
2256  }
2257
2258  /**
2259   * Returns the totalOrderQueue value.
2260   *
2261   * @return Returns the totalOrderQueue.
2262   */

2263  public LinkedList JavaDoc getTotalOrderQueue()
2264  {
2265    return totalOrderQueue;
2266  }
2267
2268  /**
2269   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#hasRecoveryLog()
2270   */

2271  public boolean hasRecoveryLog()
2272  {
2273    RecoveryLog log = requestManager.getRecoveryLog();
2274    if (log == null)
2275      return false;
2276    else
2277      return true;
2278  }
2279
2280  /**
2281   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#hasResultCache()
2282   */

2283  public boolean hasResultCache()
2284  {
2285    AbstractResultCache cache = requestManager.getResultCache();
2286    if (cache == null)
2287      return false;
2288    else
2289      return true;
2290  }
2291
2292  /**
2293   * Sets the authentication manager for this virtual database.
2294   *
2295   * @param authenticationManager the <code>AuthenticationManager</code> to
2296   * set
2297   */

2298  public void setAuthenticationManager(
2299      AuthenticationManager authenticationManager)
2300  {
2301    this.authenticationManager = authenticationManager;
2302  }
2303
2304  /**
2305   * Set the BLOB filter to use for this database.
2306   *
2307   * @param filter the filter to use.
2308   */

2309  public void setBlobFilter(AbstractBlobFilter filter)
2310  {
2311    this.blobFilter = filter;
2312  }
2313
2314  /**
2315   * Sets a new static database schema for this database if no one exist or
2316   * merge the given schema to the existing one. A static schema can only be
2317   * replaced by another static schema.
2318   *
2319   * @param schema the new database shema
2320   */

2321  public void setStaticDatabaseSchema(DatabaseSchema schema)
2322  {
2323    if (requestManager != null)
2324      requestManager.setDatabaseSchema(schema, true);
2325    else
2326      logger
2327          .warn("Unable to set database schema, no request manager has been defined.");
2328  }
2329
2330  /**
2331   * Sets the maxNbOfConnections.
2332   *
2333   * @param maxNbOfConnections The maxNbOfConnections to set
2334   */

2335  public void setMaxNbOfConnections(int maxNbOfConnections)
2336  {
2337    this.maxNbOfConnections = maxNbOfConnections;
2338  }
2339
2340  /**
2341   * Sets the maxNbOfThreads.
2342   *
2343   * @param maxNbOfThreads The maxNbOfThreads to set
2344   */

2345  public void setMaxNbOfThreads(int maxNbOfThreads)
2346  {
2347    this.maxNbOfThreads = maxNbOfThreads;
2348  }
2349
2350  /**
2351   * Sets a new request manager for this database.
2352   *
2353   * @param requestManager the new request manager.
2354   */

2355  public void setRequestManager(RequestManager requestManager)
2356  {
2357    this.requestManager = requestManager;
2358  }
2359
2360  /**
2361   * Sets a new SQL Monitor
2362   *
2363   * @param sqlMonitor the new SQL monitor
2364   */

2365  public void setSQLMonitor(SQLMonitoring sqlMonitor)
2366  {
2367    this.sqlMonitor = sqlMonitor;
2368  }
2369
2370  /**
2371   * Sets the totalOrderQueue.
2372   *
2373   * @param newQueue the new queue to use
2374   */

2375  public void setTotalOrderQueue(LinkedList JavaDoc newQueue)
2376  {
2377    if (totalOrderQueue != null)
2378    {
2379      if (totalOrderQueue.isEmpty())
2380        logger.info("Overriding local total order queue");
2381      else
2382        logger.error("Non-empty local order queue redefined (still contains"
2383            + totalOrderQueue.size() + " entries)");
2384    }
2385    totalOrderQueue = newQueue;
2386  }
2387
2388  /**
2389   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#setMonitoringToActive(boolean)
2390   */

2391  public void setMonitoringToActive(boolean active)
2392      throws VirtualDatabaseException
2393  {
2394    if (sqlMonitor == null)
2395      throw new VirtualDatabaseException(Translate
2396          .get("virtualdatabase.monitoring.not.defined"));
2397    else
2398      sqlMonitor.setActive(active);
2399  }
2400
2401  /**
2402   * Two virtual databases are equal if they have the same name and group.
2403   *
2404   * @param other the object to compare with
2405   * @return <code>true</code> if the two virtual databases are equals
2406   */

2407  public boolean equals(Object JavaDoc other)
2408  {
2409    if ((other == null) || (!(other instanceof VirtualDatabase)))
2410      return false;
2411    else
2412    {
2413      VirtualDatabase db = (VirtualDatabase) other;
2414      return name.equals(db.getDatabaseName());
2415    }
2416  }
2417
2418  // /////////////////////////////////////////
2419
// JMX
2420
// ////////////////////////////////////////
2421

2422  /**
2423   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#cleanMonitoringData()
2424   */

2425  public void cleanMonitoringData() throws VirtualDatabaseException
2426  {
2427    if (sqlMonitor == null)
2428      throw new VirtualDatabaseException(Translate
2429          .get("virtualdatabase.monitoring.not.defined"));
2430    else
2431      sqlMonitor.cleanStats();
2432  }
2433
2434  /**
2435   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#retrieveBackendsData()
2436   */

2437  public String JavaDoc[][] retrieveBackendsData() throws VirtualDatabaseException
2438  {
2439    try
2440    {
2441      acquireReadLockBackendLists();
2442    }
2443    catch (InterruptedException JavaDoc e)
2444    {
2445      String JavaDoc msg = Translate.get("virtualdatabase.fail.read.lock", e);
2446      throw new VirtualDatabaseException(msg);
2447    }
2448    ArrayList JavaDoc localBackends = this.getBackends();
2449    int backendListSize = localBackends.size();
2450    String JavaDoc[][] data = new String JavaDoc[backendListSize][];
2451    for (int i = 0; i < backendListSize; i++)
2452    {
2453      data[i] = ((DatabaseBackend) localBackends.get(i)).getBackendData();
2454    }
2455    releaseReadLockBackendLists();
2456    return data;
2457  }
2458
2459  /**
2460   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendStatistics(java.lang.String)
2461   */

2462  public BackendStatistics getBackendStatistics(String JavaDoc backendName)
2463      throws VirtualDatabaseException
2464  {
2465    try
2466    {
2467      acquireReadLockBackendLists();
2468    }
2469    catch (InterruptedException JavaDoc e)
2470    {
2471      String JavaDoc msg = Translate.get("virtualdatabase.fail.read.lock", e);
2472      throw new VirtualDatabaseException(msg);
2473    }
2474    BackendStatistics stat = null;
2475    ArrayList JavaDoc backendList = this.getBackends();
2476    for (Iterator JavaDoc iter = backendList.iterator(); iter.hasNext();)
2477    {
2478      DatabaseBackend backend = (DatabaseBackend) iter.next();
2479      if (backend.getName().equals(backendName))
2480      {
2481        stat = backend.getBackendStats();
2482      }
2483    }
2484    releaseReadLockBackendLists();
2485    return stat;
2486  }
2487
2488  /**
2489   * Return true if this database is shutting down.
2490   *
2491   * @return true if this database is shutting down.
2492   */

2493  public boolean isShuttingDown()
2494  {
2495    return shuttingDown;
2496  }
2497
2498  /**
2499   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#shutdown(int)
2500   */

2501  public void shutdown(int level)
2502  {
2503    VirtualDatabaseShutdownThread vdst = null;
2504    synchronized (this)
2505    {
2506      if (shuttingDown)
2507        return;
2508      switch (level)
2509      {
2510        case Constants.SHUTDOWN_WAIT :
2511          vdst = new VirtualDatabaseWaitShutdownThread(this);
2512          logger.info(Translate.get("controller.shutdown.type.wait", this
2513              .getVirtualDatabaseName()));
2514          break;
2515        case Constants.SHUTDOWN_SAFE :
2516          shuttingDown = true;
2517          vdst = new VirtualDatabaseSafeShutdownThread(this);
2518          logger.info(Translate.get("controller.shutdown.type.safe", this
2519              .getVirtualDatabaseName()));
2520          break;
2521        case Constants.SHUTDOWN_FORCE :
2522          shuttingDown = true;
2523          vdst = new VirtualDatabaseForceShutdownThread(this);
2524          logger.warn(Translate.get("controller.shutdown.type.force", this
2525              .getVirtualDatabaseName()));
2526          break;
2527        default :
2528          String JavaDoc msg = Translate
2529              .get("controller.shutdown.unknown.level", level);
2530          logger.error(msg);
2531          throw new RuntimeException JavaDoc(msg);
2532      }
2533    }
2534
2535    new Thread JavaDoc(vdst.getShutdownGroup(), vdst, "VirtualDatabase Shutdown Thread")
2536        .start();
2537  }
2538
2539  /**
2540   * Write the checkpoints for all backends on the recovery log
2541   */

2542  public void storeBackendsInfo()
2543  {
2544    requestManager.storeBackendsInfo(this.name, getBackends());
2545  }
2546
2547  /**
2548   * Get all users connected to that database
2549   *
2550   * @return an <code>ArrayList</code> of strings containing the clients
2551   * username
2552   */

2553  public ArrayList JavaDoc viewAllClientNames()
2554  {
2555    ArrayList JavaDoc list = this.getActiveThreads();
2556    int size = list.size();
2557    ArrayList JavaDoc clients = new ArrayList JavaDoc(size);
2558    for (int i = 0; i < list.size(); i++)
2559      clients.add(((VirtualDatabaseWorkerThread) list.get(i)).getUser());
2560    return clients;
2561  }
2562
2563  /**
2564   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewBackendInformation(java.lang.String)
2565   */

2566  public String JavaDoc[] viewBackendInformation(String JavaDoc backendName)
2567      throws VirtualDatabaseException
2568  {
2569    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
2570    return backend.getBackendData();
2571  }
2572
2573  /**
2574   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewControllerList()
2575   */

2576  public String JavaDoc[] viewControllerList()
2577  {
2578    return new String JavaDoc[]{viewOwningController()};
2579  }
2580
2581  /**
2582   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewGroupBackends()
2583   */

2584  public Hashtable JavaDoc viewGroupBackends() throws VirtualDatabaseException
2585  {
2586    Hashtable JavaDoc map = new Hashtable JavaDoc();
2587    try
2588    {
2589      acquireReadLockBackendLists();
2590    }
2591    catch (InterruptedException JavaDoc e)
2592    {
2593      String JavaDoc msg = "Unable to acquire read lock on backend list in getAllBackendNames ("
2594          + e + ")";
2595      logger.error(msg);
2596      throw new VirtualDatabaseException(msg);
2597    }
2598
2599    // Create an ArrayList<BackendInfo> from the backend list
2600
int size = backends.size();
2601    ArrayList JavaDoc backendInfos = new ArrayList JavaDoc(size);
2602    for (int i = 0; i < size; i++)
2603      backendInfos.add(new BackendInfo(((DatabaseBackend) backends.get(i))));
2604
2605    releaseReadLockBackendLists();
2606
2607    // Return a map with the controller JMX name and its ArrayList<BackendInfo>
2608
map.put(controller.getJmxName(), backendInfos);
2609    return map;
2610  }
2611
2612  /**
2613   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewOwningController()
2614   */

2615  public String JavaDoc viewOwningController()
2616  {
2617    return controller.getJmxName();
2618  }
2619
2620  /**
2621   * @see org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean#getAssociatedString()
2622   */

2623  public String JavaDoc getAssociatedString()
2624  {
2625    return "virtualdatabase";
2626  }
2627
2628  /**
2629   * Retrieves this <code>VirtualDatabase</code> object in xml format
2630   *
2631   * @return xml formatted string that conforms to c-jdbc.dtd
2632   */

2633  public String JavaDoc getXml()
2634  {
2635    StringBuffer JavaDoc info = new StringBuffer JavaDoc();
2636    info.append("<" + DatabasesXmlTags.ELT_VirtualDatabase + " "
2637        + DatabasesXmlTags.ATT_name + "=\"" + this.getVirtualDatabaseName()
2638        + "\" " + DatabasesXmlTags.ATT_maxNbOfConnections + "=\""
2639        + this.getMaxNbOfConnections() + "\" "
2640        + DatabasesXmlTags.ATT_poolThreads + "=\""
2641        + this.isPoolConnectionThreads() + "\" "
2642        + DatabasesXmlTags.ATT_minNbOfThreads + "=\""
2643        + this.getMinNbOfThreads() + "\" "
2644        + DatabasesXmlTags.ATT_maxNbOfThreads + "=\""
2645        + this.getMaxNbOfThreads() + "\" "
2646        + DatabasesXmlTags.ATT_maxThreadIdleTime + "=\""
2647        + this.getMaxThreadIdleTime() / 1000 + "\" "
2648        + DatabasesXmlTags.ATT_sqlDumpLength + "=\"" + this.sqlShortFormLength
2649        + "\" " + DatabasesXmlTags.ATT_blobEncodingMethod + "=\""
2650        + this.blobFilter.getXml() + "\">");
2651
2652    info.append(getDistributionXml());
2653
2654    if (this.getSQLMonitor() != null)
2655      info.append(sqlMonitor.getXml());
2656
2657    info.append(requestManager.getBackupManager().getXml());
2658
2659    if (this.getAuthenticationManager() != null)
2660      info.append(authenticationManager.getXml());
2661
2662    try
2663    {
2664      acquireReadLockBackendLists();
2665      int size = backends.size();
2666      for (int i = 0; i < size; i++)
2667        info.append(((DatabaseBackend) backends.get(i)).getXml());
2668      releaseReadLockBackendLists();
2669    }
2670    catch (InterruptedException JavaDoc e)
2671    {
2672      logger.error(Translate.get("virtualdatabase.fail.read.lock", e));
2673    }
2674    if (requestManager != null)
2675      info.append(requestManager.getXml());
2676    info.append("</" + DatabasesXmlTags.ELT_VirtualDatabase + ">");
2677    return info.toString();
2678  }
2679
2680  /**
2681   * Get the XML dump of the Distribution element if any.
2682   *
2683   * @return ""
2684   */

2685  protected String JavaDoc getDistributionXml()
2686  {
2687    return "";
2688  }
2689
2690  /**
2691   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#deleteLogUpToCheckpoint(java.lang.String)
2692   */

2693  public void deleteLogUpToCheckpoint(String JavaDoc checkpointName)
2694      throws VirtualDatabaseException
2695  {
2696    if (!hasRecoveryLog())
2697      throw new VirtualDatabaseException(Translate
2698          .get("virtualdatabase.no.recovery.log"));
2699
2700    try
2701    {
2702      getRequestManager().getRecoveryLog().deleteLogEntriesBeforeCheckpoint(
2703          checkpointName);
2704    }
2705    catch (SQLException JavaDoc e)
2706    {
2707      throw new VirtualDatabaseException(e);
2708    }
2709  }
2710
2711  /**
2712   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#copyLogFromCheckpoint(java.lang.String,
2713   * java.lang.String)
2714   */

2715  public void copyLogFromCheckpoint(String JavaDoc dumpName, String JavaDoc controllerName)
2716      throws VirtualDatabaseException
2717  {
2718    if (!hasRecoveryLog())
2719      throw new VirtualDatabaseException(Translate
2720          .get("virtualdatabase.no.recovery.log"));
2721    if (!isDistributed())
2722      throw new VirtualDatabaseException(Translate
2723          .get("virtualdatabase.not.distributed"));
2724
2725    /**
2726     * Implemented in the distributed incarnation of the vdb.
2727     *
2728     * @see org.objectweb.cjdbc.controller.virtualdatabase.DistributedVirtualDatabase#copyLogFromCheckpoint(String,
2729     * String)
2730     */

2731  }
2732
2733  /**
2734   * Set a checkpoint for this virtual database. This operation requires that a
2735   * recovery log be defined for this vdb, as a checkpoint is a mark in the
2736   * recovery log. This operation blocks writes vdb-wide (in scheduler), to
2737   * allow a clean write of the checkpoint mark. This is the local part of the
2738   * distributed, cluster-wide checkpoint procedure. Overriden with a
2739   * distributed operation on all controllers recovery logs if the vdb is
2740   * distributed.
2741   *
2742   * @param checkpointName the desired name of the checkpoint
2743   * @throws VirtualDatabaseException in case of error. Wraps any error: local
2744   * recovery log failure, comm failure, remote failure.
2745   */

2746  public void setCheckpoint(String JavaDoc checkpointName)
2747      throws VirtualDatabaseException
2748  {
2749    if (!hasRecoveryLog())
2750      throw new VirtualDatabaseException(Translate
2751          .get("virtualdatabase.no.recovery.log"));
2752
2753    try
2754    {
2755      // Wait for all pending writes to finish
2756
logger.info(Translate.get("requestmanager.wait.pending.writes"));
2757      getRequestManager().getScheduler().suspendWrites();
2758
2759      // Store checkpoint
2760
getRequestManager().getRecoveryLog().storeCheckpoint(checkpointName);
2761      logger.info(Translate.get("recovery.checkpoint.stored", checkpointName));
2762
2763      // Resume writes
2764
logger.info(Translate.get("requestmanager.resume.pending.writes"));
2765      getRequestManager().getScheduler().resumeWrites();
2766
2767    }
2768    catch (SQLException JavaDoc e)
2769    {
2770      String JavaDoc msg = "set checkpoint failed";
2771      logger.error(msg, e);
2772      throw new VirtualDatabaseException(msg, e);
2773    }
2774  }
2775
2776  /**
2777   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#copyDump(java.lang.String,
2778   * java.lang.String)
2779   */

2780  public void copyDump(String JavaDoc dumpName, String JavaDoc remoteControllerName)
2781      throws VirtualDatabaseException
2782  {
2783    if (!isDistributed())
2784      throw new VirtualDatabaseException(
2785          "can not copy dumps on non-distributed virtual database");
2786  }
2787
2788  /**
2789   * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#transferDump(java.lang.String,
2790   * java.lang.String, boolean)
2791   */

2792  public void transferDump(String JavaDoc dumpName, String JavaDoc remoteControllerName,
2793      boolean noCopy) throws VirtualDatabaseException
2794  {
2795    if (!isDistributed())
2796      throw new VirtualDatabaseException(
2797          "can not transfer dumps on non-distributed virtual database");
2798  }
2799
2800}
Popular Tags