KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > virtualdatabase > VirtualDatabase


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
6  * Copyright (C) 2005-2006 Continuent, Inc.
7  * Contact: sequoia@continuent.org
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
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.continuent.sequoia.controller.virtualdatabase;
26
27 import java.sql.SQLException JavaDoc;
28 import java.sql.SQLWarning JavaDoc;
29 import java.text.SimpleDateFormat JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.ConcurrentModificationException JavaDoc;
32 import java.util.Date JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.LinkedList JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37
38 import javax.management.MalformedObjectNameException JavaDoc;
39 import javax.management.Notification JavaDoc;
40 import javax.management.NotificationBroadcasterSupport JavaDoc;
41 import javax.management.ObjectName JavaDoc;
42
43 import org.continuent.sequoia.common.exceptions.BackupException;
44 import org.continuent.sequoia.common.exceptions.NoMoreBackendException;
45 import org.continuent.sequoia.common.exceptions.VirtualDatabaseException;
46 import org.continuent.sequoia.common.i18n.Translate;
47 import org.continuent.sequoia.common.jmx.JmxConstants;
48 import org.continuent.sequoia.common.jmx.management.BackendInfo;
49 import org.continuent.sequoia.common.jmx.management.BackendState;
50 import org.continuent.sequoia.common.jmx.management.DumpInfo;
51 import org.continuent.sequoia.common.jmx.monitoring.backend.BackendStatistics;
52 import org.continuent.sequoia.common.jmx.notifications.SequoiaNotificationList;
53 import org.continuent.sequoia.common.log.Trace;
54 import org.continuent.sequoia.common.users.AdminUser;
55 import org.continuent.sequoia.common.users.VirtualDatabaseUser;
56 import org.continuent.sequoia.common.util.Constants;
57 import org.continuent.sequoia.common.xml.DatabasesXmlTags;
58 import org.continuent.sequoia.common.xml.XmlComponent;
59 import org.continuent.sequoia.controller.authentication.AuthenticationManager;
60 import org.continuent.sequoia.controller.backend.DatabaseBackend;
61 import org.continuent.sequoia.controller.backend.result.ControllerResultSet;
62 import org.continuent.sequoia.controller.backend.result.ExecuteResult;
63 import org.continuent.sequoia.controller.backend.result.ExecuteUpdateResult;
64 import org.continuent.sequoia.controller.backend.result.GeneratedKeysResult;
65 import org.continuent.sequoia.controller.backup.Backuper;
66 import org.continuent.sequoia.controller.cache.result.AbstractResultCache;
67 import org.continuent.sequoia.controller.core.Controller;
68 import org.continuent.sequoia.controller.core.shutdown.VirtualDatabaseForceShutdownThread;
69 import org.continuent.sequoia.controller.core.shutdown.VirtualDatabaseSafeShutdownThread;
70 import org.continuent.sequoia.controller.core.shutdown.VirtualDatabaseShutdownThread;
71 import org.continuent.sequoia.controller.core.shutdown.VirtualDatabaseWaitShutdownThread;
72 import org.continuent.sequoia.controller.jmx.MBeanServerManager;
73 import org.continuent.sequoia.controller.loadbalancer.AllBackendsFailedException;
74 import org.continuent.sequoia.controller.locks.ReadPrioritaryFIFOWriteLock;
75 import org.continuent.sequoia.controller.monitoring.SQLMonitoring;
76 import org.continuent.sequoia.controller.recoverylog.BackendRecoveryInfo;
77 import org.continuent.sequoia.controller.recoverylog.RecoverThread;
78 import org.continuent.sequoia.controller.recoverylog.RecoveryLog;
79 import org.continuent.sequoia.controller.requestmanager.RAIDbLevels;
80 import org.continuent.sequoia.controller.requestmanager.RequestManager;
81 import org.continuent.sequoia.controller.requests.AbstractRequest;
82 import org.continuent.sequoia.controller.requests.AbstractWriteRequest;
83 import org.continuent.sequoia.controller.requests.SelectRequest;
84 import org.continuent.sequoia.controller.requests.StoredProcedure;
85 import org.continuent.sequoia.controller.semantic.SemanticManager;
86 import org.continuent.sequoia.controller.sql.schema.DatabaseSchema;
87 import org.continuent.sequoia.controller.sql.schema.DynamicDatabaseSchema;
88 import org.continuent.sequoia.controller.virtualdatabase.management.AbstractAdminOperation;
89 import org.continuent.sequoia.controller.virtualdatabase.management.BackupBackendOperation;
90 import org.continuent.sequoia.controller.virtualdatabase.management.EnableBackendOperation;
91 import org.continuent.sequoia.controller.virtualdatabase.management.RestoreDumpOperation;
92
93 /**
94  * A <code>VirtualDatabase</code> represents a database from client point of
95  * view and hide the complexity of the cluster distribution to the client. The
96  * client always uses the virtual database name and the Sequoia Controller will
97  * use the real connections when an SQL request comes in.
98  *
99  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
100  * @author <a HREF="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
101  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
102  * @author <a HREF="mailto:vadim@kase.kz">Vadim Kassin </a>
103  * @author <a HREF="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
104  * </a>
105  * @version 1.0
106  */

107 public class VirtualDatabase implements XmlComponent
108 {
109   private static final long serialVersionUID = 1399418136380336827L;
110
111   //
112
// How the code is organized ?
113
//
114
// 1. Member variables
115
// 2. Constructor(s)
116
// 3. Request handling
117
// 4. Transaction handling
118
// 5. Database backend management
119
// 6. Checkpoint management
120
// 7. Getter/Setter (possibly in alphabetical order)
121
// 8. Shutdown
122
//
123

124   /** Virtual database name */
125   protected String JavaDoc name;
126
127   /**
128    * Authentification manager matching virtual database login/password to
129    * backends login/password
130    */

131   protected AuthenticationManager authenticationManager;
132
133   /** <code>ArrayList</code> of <code>DatabaseBackend</code> objects */
134   protected ArrayList JavaDoc backends;
135
136   /** Read/Write lock for backend list */
137   protected ReadPrioritaryFIFOWriteLock rwLock;
138
139   /** The request manager to use for this database */
140   protected RequestManager requestManager;
141
142   /** The settings for fetching the dynamic schema */
143   protected DynamicDatabaseSchema dynamicDatabaseSchema;
144
145   /** The semantic information manager for this virtual database */
146   protected SemanticManager semanticManager = new SemanticManager();
147
148   /** ArrayList to store the order of requests */
149   protected LinkedList JavaDoc totalOrderQueue = null;
150
151   /** Virtual database logger */
152   protected Trace logger = null;
153   protected Trace requestLogger = null;
154
155   /** end user logger */
156   static Trace endUserLogger = Trace
157                                                                           .getLogger("org.continuent.sequoia.enduser");
158
159   // List of current active Worker Threads
160
private ArrayList JavaDoc activeThreads = new ArrayList JavaDoc();
161   // List of current idle Worker Threads
162
private int idleThreads = 0;
163   // List of current pending connections (Socket objects)
164
private ArrayList JavaDoc pendingConnections = new ArrayList JavaDoc();
165
166   /** Maximum number of concurrent accepted for this virtual database */
167   protected int maxNbOfConnections;
168
169   /** If false one worker thread is forked per connection else */
170   protected boolean poolConnectionThreads;
171
172   /** Maximum time a worker thread can remain idle before dying */
173   protected long maxThreadIdleTime;
174
175   /**
176    * Minimum number of worker threads to keep in the pool if
177    * poolConnectionThreads is true
178    */

179   protected int minNbOfThreads;
180
181   /** Maximum number of worker threads to fork */
182   protected int maxNbOfThreads;
183
184   /** Current number of worker threads */
185   protected int currentNbOfThreads;
186
187   /** Virtual Database MetaData */
188   protected VirtualDatabaseDynamicMetaData metadata;
189   private boolean useStaticResultSetMetaData = true;
190   protected VirtualDatabaseStaticMetaData staticMetadata;
191
192   private SQLMonitoring sqlMonitor = null;
193
194   /** Use for method getAndCheck */
195   public static final int CHECK_BACKEND_ENABLE = 1;
196   /** Use for method getAndCheck */
197   public static final int CHECK_BACKEND_DISABLE = 0;
198   /** Use for method getAndCheck */
199   public static final int NO_CHECK_BACKEND = -1;
200
201   /** Short form of SQL statements to include in traces and exceptions */
202   private int sqlShortFormLength;
203
204   /** The controller we belong to */
205   Controller controller;
206
207   /** Comma separated list of database product names (one instance per name) */
208   private String JavaDoc databaseProductNames = "Sequoia";
209
210   /** Marker to see if the database is shutting down */
211   protected boolean shuttingDown = false;
212   private boolean refusingNewTransaction = false;
213   /** List of currently executing admin operations (preventing shutdown) */
214   private List JavaDoc currentAdminOperations;
215
216   protected NotificationBroadcasterSupport JavaDoc notificationBroadcasterSupport;
217
218   protected int notificationSequence = 0;
219
220   protected long connectionId = 0;
221
222   /**
223    * Creates a new <code>VirtualDatabase</code> instance.
224    *
225    * @param name the virtual database name.
226    * @param maxConnections maximum number of concurrent connections.
227    * @param pool should we use a pool of threads for handling connections?
228    * @param minThreads minimum number of threads in the pool
229    * @param maxThreads maximum number of threads in the pool
230    * @param maxThreadIdleTime maximum time a thread can remain idle before being
231    * removed from the pool.
232    * @param sqlShortFormLength maximum number of characters of an SQL statement
233    * to diplay in traces or exceptions
234    * @param useStaticResultSetMetaData true if DatabaseResultSetMetaData should
235    * use static fields or try to fetch the metadata from the underlying
236    * database
237    * @param controller the controller we belong to
238    */

239   public VirtualDatabase(Controller controller, String JavaDoc name,
240       int maxConnections, boolean pool, int minThreads, int maxThreads,
241       long maxThreadIdleTime, int sqlShortFormLength,
242       boolean useStaticResultSetMetaData)
243   {
244     this.controller = controller;
245     this.name = name;
246     this.maxNbOfConnections = maxConnections;
247     this.poolConnectionThreads = pool;
248     this.minNbOfThreads = minThreads;
249     this.maxNbOfThreads = maxThreads;
250     this.maxThreadIdleTime = maxThreadIdleTime;
251     this.sqlShortFormLength = sqlShortFormLength;
252     this.useStaticResultSetMetaData = useStaticResultSetMetaData;
253     backends = new ArrayList JavaDoc();
254     currentAdminOperations = new LinkedList JavaDoc();
255
256     rwLock = new ReadPrioritaryFIFOWriteLock();
257     logger = Trace
258         .getLogger("org.continuent.sequoia.controller.virtualdatabase." + name);
259     requestLogger = Trace
260         .getLogger("org.continuent.sequoia.controller.virtualdatabase.request."
261             + name);
262   }
263
264   /**
265    * Sets the NotificationBroadcasterSupport associated with the MBean managing
266    * this virtual database.
267    *
268    * @param notificationBroadcasterSupport the notificationBroadcasterSuppor
269    * associated with the mbean managing this virtual database
270    */

271   public void setNotificationBroadcasterSupport(
272       NotificationBroadcasterSupport JavaDoc notificationBroadcasterSupport)
273   {
274     this.notificationBroadcasterSupport = notificationBroadcasterSupport;
275   }
276
277   /**
278    * Sends a JMX Notification on behalf of the MBean associated with this
279    * virtual database
280    *
281    * @param type type of the JMX notification
282    * @param message message associated with the notification
283    * @see SequoiaNotificationList
284    */

285   protected void sendJmxNotification(String JavaDoc type, String JavaDoc message)
286   {
287     try
288     {
289       notificationBroadcasterSupport.sendNotification(new Notification JavaDoc(type,
290           JmxConstants.getVirtualDataBaseObjectName(name),
291           notificationSequence++, message));
292     }
293     catch (MalformedObjectNameException JavaDoc e)
294     {
295       // unable to get a correct vdb object name: do nothing
296
logger.warn("Unable to send JMX notification", e);
297     }
298   }
299
300   /**
301    * Acquires a read lock on the backend lists (both enabled and disabled
302    * backends). This should be called prior traversing the backend
303    * <code>ArrayList</code>.
304    *
305    * @throws InterruptedException if an error occurs
306    */

307   public final void acquireReadLockBackendLists() throws InterruptedException JavaDoc
308   {
309     rwLock.acquireRead();
310   }
311
312   /**
313    * Releases the read lock on the backend lists (both enabled and disabled
314    * backends). This should be called after traversing the backend
315    * <code>ArrayList</code>.
316    */

317   public final void releaseReadLockBackendLists()
318   {
319     rwLock.releaseRead();
320   }
321
322   /**
323    * Is this virtual database distributed ?
324    *
325    * @return false
326    */

327   public boolean isDistributed()
328   {
329     return false;
330   }
331
332   /* Request Handling */
333
334   /**
335    * Checks if a given virtual login/password is ok.
336    *
337    * @param virtualLogin the virtual user login
338    * @param virtualPassword the virtual user password
339    * @return <code>true</code> if the login/password is known from the
340    * <code>AuthenticationManager</code>. Returns <code>false</code>
341    * if no <code>AuthenticationManager</code> is defined.
342    */

343   public boolean checkUserAuthentication(String JavaDoc virtualLogin,
344       String JavaDoc virtualPassword)
345   {
346     if (authenticationManager == null)
347     {
348       logger.error("No authentification manager defined to check login '"
349           + virtualLogin + "'");
350       return false;
351     }
352     else
353     {
354       boolean result = authenticationManager
355           .isValidVirtualUser(new VirtualDatabaseUser(virtualLogin,
356               virtualPassword));
357       if (!result)
358         endUserLogger.error(Translate.get(
359             "virtualdatabase.authentication.failed", virtualLogin));
360       return result;
361     }
362   }
363
364   /**
365    * Checks if a given admin login/password is ok.
366    *
367    * @param adminLogin admin user login
368    * @param adminPassword admin user password
369    * @return <code>true</code> if the login/password is known from the
370    * <code>AuthenticationManager</code>. Returns <code>false</code>
371    * if no <code>AuthenticationManager</code> is defined.
372    */

373   public boolean checkAdminAuthentication(String JavaDoc adminLogin,
374       String JavaDoc adminPassword)
375   {
376     if (authenticationManager == null)
377     {
378       endUserLogger.info(Translate.get(
379           "virtualdatabase.checking.authentication", new String JavaDoc[]{adminLogin,
380               this.getVirtualDatabaseName()}));
381       String JavaDoc msg = "No authentification manager defined to check admin login '"
382           + adminLogin + "'";
383       logger.error(msg);
384       endUserLogger.error(Translate.get(
385           "virtualdatabase.check.authentication.failed", new String JavaDoc[]{
386               adminLogin, this.getVirtualDatabaseName(), msg}));
387       return false;
388     }
389     else
390     {
391       boolean result = authenticationManager.isValidAdminUser(new AdminUser(
392           adminLogin, adminPassword));
393       if (!result)
394         endUserLogger.error(Translate.get(
395             "virtualdatabase.authentication.failed", adminLogin));
396       return result;
397     }
398   }
399
400   /**
401    * Adds a new vdb user. Uses the vdb login/password as real user
402    * login/password. Only creates the user if vdb login/password are valid for
403    * all backends hosting the vdb.
404    *
405    * @param vdbUser vdb user to be added.
406    */

407   public void checkAndAddVirtualDatabaseUser(VirtualDatabaseUser vdbUser)
408   {
409     // If user does not exist in all backends leave
410
if (!isValidUserForAllBackends(vdbUser))
411     {
412       if (logger.isWarnEnabled())
413       {
414         logger.warn("Could not create new vdb user " + vdbUser.getLogin()
415             + " because it does not exist on all backends");
416       }
417       return;
418     }
419
420     // Add user
421
try
422     {
423       performAddVirtualDatabaseUser(vdbUser);
424       if (logger.isInfoEnabled())
425       {
426         logger.info("Added new vdb user " + vdbUser.getLogin());
427       }
428     }
429     catch (SQLException JavaDoc e)
430     {
431       if (logger.isWarnEnabled())
432       {
433         logger.warn("Problem when adding default connection manager for user "
434             + vdbUser.getLogin() + ", trying to clean-up...");
435         removeVirtualDatabaseUser(vdbUser);
436       }
437     }
438   }
439
440   /**
441    * Removes vdb user.
442    *
443    * @param vdbUser vdb user to be removed.
444    */

445   private void removeVirtualDatabaseUser(VirtualDatabaseUser vdbUser)
446   {
447     performRemoveVirtualDatabaseUser(vdbUser);
448   }
449
450   /**
451    * Adds new vdb user and its corresponding connection managers.
452    *
453    * @param vdbUser vdb user to be added.
454    * @throws SQLException thrown if problem occurred when adding connection
455    * manager
456    */

457   public void performAddVirtualDatabaseUser(VirtualDatabaseUser vdbUser)
458       throws SQLException JavaDoc
459   {
460     for (Iterator JavaDoc iter = backends.iterator(); iter.hasNext();)
461     {
462       DatabaseBackend backend = (DatabaseBackend) iter.next();
463       backend.addDefaultConnectionManager(vdbUser);
464     }
465     authenticationManager.addVirtualUser(vdbUser);
466     // Should we invoke authenticationManager.addRealUser() here?
467
}
468
469   /**
470    * Removes vdb user and its corresponding connection managers.
471    *
472    * @param vdbUser vdb user to be removed.
473    */

474   public void performRemoveVirtualDatabaseUser(VirtualDatabaseUser vdbUser)
475   {
476     authenticationManager.removeVirtualUser(vdbUser);
477     for (Iterator JavaDoc iter = backends.iterator(); iter.hasNext();)
478     {
479       DatabaseBackend backend = (DatabaseBackend) iter.next();
480       try
481       {
482         backend.removeConnectionManager(vdbUser);
483       }
484       catch (SQLException JavaDoc e)
485       {
486         if (logger.isWarnEnabled())
487         {
488           logger
489               .warn("Problem when removing default connection manager for user "
490                   + vdbUser.getLogin());
491         }
492       }
493     }
494     if (logger.isInfoEnabled())
495     {
496       logger.info("Removed vdb user " + vdbUser.getLogin());
497     }
498   }
499
500   /**
501    * Checks if a vdb user is valid as a user for allbackends.
502    *
503    * @param vdbUser vdb user to be checked.
504    * @return true if vdb user is valid for all backends, false otherwise.
505    */

506   public boolean isValidUserForAllBackends(VirtualDatabaseUser vdbUser)
507   {
508     boolean result = true;
509     for (Iterator JavaDoc iter = backends.iterator(); iter.hasNext();)
510     {
511       DatabaseBackend backend = (DatabaseBackend) iter.next();
512       if (!backend.isValidBackendUser(vdbUser))
513       {
514         result = false;
515         break;
516       }
517     }
518     return result;
519   }
520
521   /**
522    * Performs a read request and returns the reply.
523    *
524    * @param request the request to execute
525    * @return a <code>ControllerResultSet</code> value
526    * @exception SQLException if the request fails
527    */

528   protected ControllerResultSet statementExecuteQuery(SelectRequest request)
529       throws SQLException JavaDoc
530   {
531     if (request == null)
532     {
533       String JavaDoc msg = "Request failed (null read request received)";
534       logger.warn(msg);
535       throw new SQLException JavaDoc(msg);
536     }
537
538     try
539     {
540       if (requestLogger.isInfoEnabled())
541         requestLogger.info("S " + request.getId() + " "
542             + request.getTransactionId() + " " + request.getUniqueKey());
543
544       request.setStartTime(System.currentTimeMillis());
545
546       ControllerResultSet rs = requestManager.statementExecuteQuery(request);
547
548       request.setEndTime(System.currentTimeMillis());
549       if (sqlMonitor != null && sqlMonitor.isActive())
550         sqlMonitor.logRequestTime(request);
551
552       return rs;
553     }
554     catch (SQLException JavaDoc e)
555     {
556       String JavaDoc msg = "Request '" + request.getId() + "' failed ("
557           + e.getMessage() + ")";
558       if (!request.isAutoCommit())
559       {
560         // If the request fails in a transaction, the transaction is likely
561
// to be rollbacked by the underlying database. Then we have to abort
562
// the transaction.
563
msg = Translate.get("loadbalancer.request.failed.and.abort",
564             new String JavaDoc[]{request.getSqlShortForm(getSqlShortFormLength()),
565                 e.getMessage()});
566         try
567         {
568           abort(request.getTransactionId(), true, false);
569         }
570         catch (SQLException JavaDoc e1)
571         {
572           if (logger.isInfoEnabled())
573             logger
574                 .info(
575                     "Abort after request failure in transaction did not succeed probably because the transaction has already been aborted",
576                     e);
577         }
578       }
579       logger.warn(msg);
580       if (sqlMonitor != null && sqlMonitor.isActive())
581         sqlMonitor.logError(request);
582       throw e;
583     }
584   }
585
586   /**
587    * Performs a write request and returns the number of rows affected.
588    *
589    * @param request the request to execute
590    * @return number of rows affected
591    * @exception SQLException if the request fails
592    */

593   protected ExecuteUpdateResult statementExecuteUpdate(
594       AbstractWriteRequest request) throws SQLException JavaDoc
595   {
596     if (request == null)
597     {
598       String JavaDoc msg = "Request failed (null write request received)";
599       logger.warn(msg);
600       throw new SQLException JavaDoc(msg);
601     }
602
603     try
604     {
605       if (requestLogger.isInfoEnabled())
606         requestLogger.info("W " + request.getId() + " "
607             + request.getTransactionId() + " " + request.getUniqueKey());
608
609       request.setStartTime(System.currentTimeMillis());
610
611       ExecuteUpdateResult result = requestManager
612           .statementExecuteUpdate(request);
613
614       request.setEndTime(System.currentTimeMillis());
615       if (sqlMonitor != null && sqlMonitor.isActive())
616         sqlMonitor.logRequestTime(request);
617
618       return result;
619     }
620     catch (SQLException JavaDoc e)
621     {
622       String JavaDoc msg = "Request '" + request.getId() + "' failed ("
623           + e.getMessage() + ")";
624
625       if (!request.isAutoCommit())
626       {
627         // If the request fails in a transaction, the transaction is likely
628
// to be rollbacked by the underlying database. Then we have to abort
629
// the transaction.
630
msg = Translate.get("loadbalancer.request.failed.and.abort",
631             new String JavaDoc[]{request.getSqlShortForm(getSqlShortFormLength()),
632                 e.getMessage()});
633         try
634         {
635           if (requestManager.getTransactionMetaData(new Long JavaDoc(request
636               .getTransactionId())) != null)
637             abort(request.getTransactionId(), true, false);
638         }
639         catch (SQLException JavaDoc e1)
640         {
641           if (logger.isInfoEnabled())
642             logger
643                 .info(
644                     "Abort after request failure in transaction did not succeed probably because the transaction has already been aborted",
645                     e);
646         }
647       }
648
649       // This maybe just invalid SQL, see SEQUOIA-809
650
logger.debug(msg);
651       if (sqlMonitor != null && sqlMonitor.isActive())
652         sqlMonitor.logError(request);
653       throw e;
654     }
655   }
656
657   /**
658    * Performs a write request and returns the auto generated keys.
659    *
660    * @param request the request to execute
661    * @return auto generated keys
662    * @exception SQLException if the request fails
663    */

664   protected GeneratedKeysResult statementExecuteUpdateWithKeys(
665       AbstractWriteRequest request) throws SQLException JavaDoc
666   {
667     if (request == null)
668     {
669       String JavaDoc msg = "Request failed (null write request received)";
670       logger.warn(msg);
671       throw new SQLException JavaDoc(msg);
672     }
673
674     try
675     {
676       if (requestLogger.isInfoEnabled())
677         requestLogger.info("W " + request.getId() + " "
678             + request.getTransactionId() + " " + request.getUniqueKey());
679
680       request.setStartTime(System.currentTimeMillis());
681
682       GeneratedKeysResult result = requestManager
683           .statementExecuteUpdateWithKeys(request);
684
685       request.setEndTime(System.currentTimeMillis());
686       if (sqlMonitor != null && sqlMonitor.isActive())
687         sqlMonitor.logRequestTime(request);
688
689       return result;
690     }
691     catch (SQLException JavaDoc e)
692     {
693       String JavaDoc msg = "Request '" + request.getId() + "' failed ("
694           + e.getMessage() + ")";
695       if (!request.isAutoCommit())
696       {
697         // If the request fails in a transaction, the transaction is likely
698
// to be rollbacked by the underlying database. Then we have to abort
699
// the transaction.
700
abort(request.getTransactionId(), true, false);
701         msg = Translate.get("loadbalancer.request.failed.and.abort",
702             new String JavaDoc[]{request.getSqlShortForm(getSqlShortFormLength()),
703                 e.getMessage()});
704       }
705       logger.warn(msg);
706       if (sqlMonitor != null && sqlMonitor.isActive())
707         sqlMonitor.logError(request);
708       throw e;
709     }
710   }
711
712   /**
713    * Execute a request using Statement.execute(). Handle this as a stored
714    * procedure for which we have no metadata information.
715    *
716    * @param request the request to execute
717    * @return an <code>ExecuteResult</code> object
718    * @exception SQLException if an error occurs
719    */

720   protected ExecuteResult statementExecute(AbstractRequest request)
721       throws SQLException JavaDoc
722   {
723     if (request == null)
724     {
725       String JavaDoc msg = "Statement.execute() failed (null request received)";
726       logger.warn(msg);
727       throw new SQLException JavaDoc(msg);
728     }
729
730     try
731     {
732       if (requestLogger.isInfoEnabled())
733         requestLogger.info("E " + request.getId() + " "
734             + request.getTransactionId() + " " + request.getUniqueKey());
735
736       request.setStartTime(System.currentTimeMillis());
737
738       ExecuteResult result = requestManager.statementExecute(request);
739
740       request.setEndTime(System.currentTimeMillis());
741       if (sqlMonitor != null && sqlMonitor.isActive())
742         sqlMonitor.logRequestTime(request);
743
744       return result;
745     }
746     catch (AllBackendsFailedException e)
747     {
748       String JavaDoc msg = Translate.get(
749           "loadbalancer.storedprocedure.failed.on.all.backends", new String JavaDoc[]{
750               String.valueOf(request.getId()), e.getMessage()});
751       if (!request.isAutoCommit())
752       {
753         // If the request fails in a transaction, the transaction is likely
754
// to be rollbacked by the underlying database. Then we have to abort
755
// the transaction.
756
abort(request.getTransactionId(), true, false);
757         msg = Translate.get("loadbalancer.request.failed.and.abort",
758             new String JavaDoc[]{request.getSqlShortForm(getSqlShortFormLength()),
759                 e.getMessage()});
760       }
761       logger.warn(msg);
762       if (sqlMonitor != null && sqlMonitor.isActive())
763         sqlMonitor.logError(request);
764       throw new SQLException JavaDoc(msg);
765     }
766     catch (SQLException JavaDoc e)
767     {
768       String JavaDoc msg = Translate.get("loadbalancer.storedprocedure.failed",
769           new String JavaDoc[]{String.valueOf(request.getId()), e.getMessage()});
770       if (!request.isAutoCommit())
771       {
772         // If the request fails in a transaction, the transaction is likely
773
// to be rollbacked by the underlying database. Then we have to abort
774
// the transaction.
775
abort(request.getTransactionId(), true, false);
776         msg = Translate.get("loadbalancer.request.failed.and.abort",
777             new String JavaDoc[]{request.getSqlShortForm(getSqlShortFormLength()),
778                 e.getMessage()});
779       }
780       logger.warn(msg);
781       if (sqlMonitor != null && sqlMonitor.isActive())
782         sqlMonitor.logError(request);
783       throw e;
784     }
785   }
786
787   /**
788    * Call a stored procedure that returns a ResultSet.
789    *
790    * @param proc the stored procedure call
791    * @return a <code>java.sql.ResultSet</code> value
792    * @exception SQLException if an error occurs
793    */

794   protected ControllerResultSet callableStatementExecuteQuery(
795       StoredProcedure proc) throws SQLException JavaDoc
796   {
797     if (proc == null)
798     {
799       String JavaDoc msg = "Request failed (null stored procedure received)";
800       logger.warn(msg);
801       throw new SQLException JavaDoc(msg);
802     }
803
804     try
805     {
806       if (requestLogger.isInfoEnabled())
807         requestLogger.info("S " + proc.getId() + " " + proc.getTransactionId()
808             + " " + proc.getUniqueKey());
809
810       proc.setStartTime(System.currentTimeMillis());
811
812       ControllerResultSet rs = requestManager
813           .callableStatementExecuteQuery(proc);
814
815       proc.setEndTime(System.currentTimeMillis());
816       if (sqlMonitor != null && sqlMonitor.isActive())
817         sqlMonitor.logRequestTime(proc);
818
819       return rs;
820     }
821     catch (AllBackendsFailedException e)
822     {
823       String JavaDoc msg = Translate.get(
824           "loadbalancer.storedprocedure.failed.on.all.backends", new String JavaDoc[]{
825               String.valueOf(proc.getId()), e.getMessage()});
826       if (!proc.isAutoCommit())
827       {
828         // If the request fails in a transaction, the transaction is likely
829
// to be rollbacked by the underlying database. Then we have to abort
830
// the transaction.
831
abort(proc.getTransactionId(), true, false);
832         msg = Translate.get("loadbalancer.request.failed.and.abort",
833             new String JavaDoc[]{proc.getSqlShortForm(getSqlShortFormLength()),
834                 e.getMessage()});
835       }
836       logger.warn(msg);
837       if (sqlMonitor != null && sqlMonitor.isActive())
838         sqlMonitor.logError(proc);
839       throw new SQLException JavaDoc(msg);
840     }
841     catch (SQLException JavaDoc e)
842     {
843       String JavaDoc msg = Translate.get("loadbalancer.storedprocedure.failed",
844           new String JavaDoc[]{String.valueOf(proc.getId()), e.getMessage()});
845       if (!proc.isAutoCommit())
846       {
847         // If the request fails in a transaction, the transaction is likely
848
// to be rollbacked by the underlying database. Then we have to abort
849
// the transaction.
850
abort(proc.getTransactionId(), true, false);
851         msg = Translate.get("loadbalancer.request.failed.and.abort",
852             new String JavaDoc[]{proc.getSqlShortForm(getSqlShortFormLength()),
853                 e.getMessage()});
854       }
855       logger.warn(msg);
856       if (sqlMonitor != null && sqlMonitor.isActive())
857         sqlMonitor.logError(proc);
858       throw e;
859     }
860   }
861
862   /**
863    * Call a stored procedure that performs an update.
864    *
865    * @param proc the stored procedure call
866    * @return number of rows affected
867    * @exception SQLException if an error occurs
868    */

869   protected ExecuteUpdateResult callableStatementExecuteUpdate(
870       StoredProcedure proc) throws SQLException JavaDoc
871   {
872     if (proc == null)
873     {
874       String JavaDoc msg = "Request failed (null stored procedure received)";
875       logger.warn(msg);
876       throw new SQLException JavaDoc(msg);
877     }
878
879     try
880     {
881       if (requestLogger.isInfoEnabled())
882         requestLogger.info("W " + proc.getId() + " " + proc.getTransactionId()
883             + " " + proc.getUniqueKey());
884
885       proc.setStartTime(System.currentTimeMillis());
886
887       ExecuteUpdateResult result = requestManager
888           .callableStatementExecuteUpdate(proc);
889
890       proc.setEndTime(System.currentTimeMillis());
891       if (sqlMonitor != null && sqlMonitor.isActive())
892         sqlMonitor.logRequestTime(proc);
893
894       return result;
895     }
896     catch (AllBackendsFailedException e)
897     {
898       String JavaDoc msg = Translate.get(
899           "loadbalancer.storedprocedure.failed.on.all.backends", new String JavaDoc[]{
900               String.valueOf(proc.getId()), e.getMessage()});
901       if (!proc.isAutoCommit())
902       {
903         // If the request fails in a transaction, the transaction is likely
904
// to be rollbacked by the underlying database. Then we have to abort
905
// the transaction.
906
abort(proc.getTransactionId(), true, false);
907         msg = Translate.get("loadbalancer.request.failed.and.abort",
908             new String JavaDoc[]{proc.getSqlShortForm(getSqlShortFormLength()),
909                 e.getMessage()});
910       }
911       logger.warn(msg);
912       if (sqlMonitor != null && sqlMonitor.isActive())
913         sqlMonitor.logError(proc);
914       throw new SQLException JavaDoc(msg);
915     }
916     catch (SQLException JavaDoc e)
917     {
918       String JavaDoc msg = Translate.get("loadbalancer.storedprocedure.failed",
919           new String JavaDoc[]{String.valueOf(proc.getId()), e.getMessage()});
920       if (!proc.isAutoCommit())
921       {
922         // If the request fails in a transaction, the transaction is likely
923
// to be rollbacked by the underlying database. Then we have to abort
924
// the transaction.
925
abort(proc.getTransactionId(), true, false);
926         msg = Translate.get("loadbalancer.request.failed.and.abort",
927             new String JavaDoc[]{proc.getSqlShortForm(getSqlShortFormLength()),
928                 e.getMessage()});
929       }
930       logger.warn(msg);
931       if (sqlMonitor != null && sqlMonitor.isActive())
932         sqlMonitor.logError(proc);
933       throw e;
934     }
935   }
936
937   /**
938    * Execute a call to CallableStatement.execute() and returns a suite of
939    * updateCount and/or ResultSets.
940    *
941    * @param proc the stored procedure to execute
942    * @return an <code>ExecuteResult</code> object
943    * @exception SQLException if an error occurs
944    */

945   protected ExecuteResult callableStatementExecute(StoredProcedure proc)
946       throws SQLException JavaDoc
947   {
948     if (proc == null)
949     {
950       String JavaDoc msg = "Request failed (null stored procedure received)";
951       logger.warn(msg);
952       throw new SQLException JavaDoc(msg);
953     }
954
955     try
956     {
957       if (requestLogger.isInfoEnabled())
958         requestLogger.info("E " + proc.getId() + " " + proc.getTransactionId()
959             + " " + proc.getUniqueKey());
960
961       proc.setStartTime(System.currentTimeMillis());
962
963       ExecuteResult result = requestManager.callableStatementExecute(proc);
964
965       proc.setEndTime(System.currentTimeMillis());
966       if (sqlMonitor != null && sqlMonitor.isActive())
967         sqlMonitor.logRequestTime(proc);
968
969       return result;
970     }
971     catch (AllBackendsFailedException e)
972     {
973       String JavaDoc msg = Translate.get(
974           "loadbalancer.storedprocedure.failed.on.all.backends", new String JavaDoc[]{
975               String.valueOf(proc.getId()), e.getMessage()});
976       if (!proc.isAutoCommit())
977       {
978         // If the request fails in a transaction, the transaction is likely
979
// to be rollbacked by the underlying database. Then we have to abort
980
// the transaction.
981
abort(proc.getTransactionId(), true, false);
982         msg = Translate.get("loadbalancer.request.failed.and.abort",
983             new String JavaDoc[]{proc.getSqlShortForm(getSqlShortFormLength()),
984                 e.getMessage()});
985       }
986       logger.warn(msg);
987       if (sqlMonitor != null && sqlMonitor.isActive())
988         sqlMonitor.logError(proc);
989       throw new SQLException JavaDoc(msg);
990     }
991     catch (SQLException JavaDoc e)
992     {
993       String JavaDoc msg = Translate.get("loadbalancer.storedprocedure.failed",
994           new String JavaDoc[]{String.valueOf(proc.getId()), e.getMessage()});
995       if (!proc.isAutoCommit())
996       {
997         // If the request fails in a transaction, the transaction is likely
998
// to be rollbacked by the underlying database. Then we have to abort
999
// the transaction.
1000
abort(proc.getTransactionId(), true, false);
1001        msg = Translate.get("loadbalancer.request.failed.and.abort",
1002            new String JavaDoc[]{proc.getSqlShortForm(getSqlShortFormLength()),
1003                e.getMessage()});
1004      }
1005      logger.warn(msg);
1006      if (sqlMonitor != null && sqlMonitor.isActive())
1007        sqlMonitor.logError(proc);
1008      throw e;
1009    }
1010  }
1011
1012  /**
1013   * Close the given persistent connection.
1014   *
1015   * @param login login to use to retrieve the right connection pool
1016   * @param persistentConnectionId id of the persistent connection to close
1017   */

1018  public void closePersistentConnection(String JavaDoc login,
1019      long persistentConnectionId)
1020  {
1021    requestManager.closePersistentConnection(login, persistentConnectionId);
1022  }
1023
1024  /**
1025   * Returns true if the virtual database has opened the given persistent
1026   * connection.
1027   *
1028   * @param persistentConnectionId id of the persistent connection to check
1029   * @return true if the connection is open
1030   */

1031  public boolean hasPersistentConnection(long persistentConnectionId)
1032  {
1033    return requestManager.hasPersistentConnection(persistentConnectionId);
1034  }
1035
1036  /**
1037   * Open the given persistent connection.
1038   *
1039   * @param login login to use to retrieve the right connection pool
1040   * @param persistentConnectionId id of the persistent connection to open
1041   * @throws SQLException if an error occurs while opening the connection
1042   */

1043  public void openPersistentConnection(String JavaDoc login, long persistentConnectionId)
1044      throws SQLException JavaDoc
1045  {
1046    requestManager.openPersistentConnection(login, persistentConnectionId);
1047  }
1048
1049  /**
1050   * Notify the failover of the given transaction (really useful for
1051   * DistributedVirtualDatabase)
1052   *
1053   * @param currentTid transaction id
1054   */

1055  public void failoverForTransaction(long currentTid)
1056  {
1057    logger.info("Transparent client failover operated for transaction "
1058        + currentTid);
1059  }
1060
1061  /**
1062   * Notify the failover of the given persistent connection (really useful for
1063   * DistributedVirtualDatabase)
1064   *
1065   * @param persistentConnectionId the persistent connection id
1066   */

1067  public void failoverForPersistentConnection(long persistentConnectionId)
1068  {
1069    // This should never happen when we have a single controller since when we
1070
// die there is no one to fail over.
1071
logger
1072        .info("Unexpected transparent client failover operated for persistent connection "
1073            + persistentConnectionId);
1074  }
1075
1076  private static final Object JavaDoc CONNECTION_ID_SYNC_OBJECT = new Object JavaDoc();
1077
1078  /**
1079   * Return the next connection identifier (monotically increasing number).
1080   *
1081   * @return a connection identifier
1082   */

1083  public long getNextConnectionId()
1084  {
1085    synchronized (CONNECTION_ID_SYNC_OBJECT)
1086    {
1087      return connectionId++;
1088    }
1089  }
1090
1091  /**
1092   * Return the next request identifier (monotically increasing number).
1093   *
1094   * @return a request identifier
1095   */

1096  public long getNextRequestId()
1097  {
1098    return requestManager.getNextRequestId();
1099  }
1100
1101  /**
1102   * Return a ControllerResultSet containing the PreparedStatement metaData of
1103   * the given sql template
1104   *
1105   * @param request the request containing the sql template
1106   * @return an empty ControllerResultSet with the metadata
1107   * @throws SQLException if a database error occurs
1108   */

1109  public ControllerResultSet getPreparedStatementGetMetaData(
1110      AbstractRequest request) throws SQLException JavaDoc
1111  {
1112    try
1113    {
1114      return requestManager.getPreparedStatementGetMetaData(request);
1115    }
1116    catch (NoMoreBackendException e)
1117    {
1118      throw e;
1119    }
1120  }
1121
1122  /*
1123   * Transaction management
1124   */

1125
1126  /**
1127   * Begins a new transaction and returns the corresponding transaction
1128   * identifier. This method is called from the driver when
1129   * {@link org.continuent.sequoia.driver.Connection#setAutoCommit(boolean)}is
1130   * called with <code>false</code> argument.
1131   * <p>
1132   * Note that the transaction begin is not logged in the recovery log by this
1133   * method, you will have to call logLazyTransactionBegin.
1134   *
1135   * @param login the login used by the connection
1136   * @param isPersistentConnection true if the transaction is started on a
1137   * persistent connection
1138   * @param persistentConnectionId persistent connection id if the transaction
1139   * must be started on a persistent connection
1140   * @return an unique transaction identifier
1141   * @exception SQLException if an error occurs
1142   * @see RequestManager#logLazyTransactionBegin(long)
1143   */

1144  public long begin(String JavaDoc login, boolean isPersistentConnection,
1145      long persistentConnectionId) throws SQLException JavaDoc
1146  {
1147    try
1148    {
1149      long tid = requestManager.begin(login, isPersistentConnection,
1150          persistentConnectionId);
1151      if (requestLogger.isInfoEnabled())
1152        requestLogger.info("B " + tid);
1153      return tid;
1154    }
1155    catch (SQLException JavaDoc e)
1156    {
1157      String JavaDoc msg = "Begin failed (" + e.getMessage() + ")";
1158      logger.warn(msg);
1159      throw e;
1160    }
1161  }
1162
1163  /**
1164   * Abort a transaction that has been started but in which no query was
1165   * executed. As we use lazy transaction begin, there is no need to rollback
1166   * such transaction but just to cleanup the metadata associated with this not
1167   * effectively started transaction.
1168   *
1169   * @param transactionId id of the transaction to abort
1170   * @param logAbort true if the abort (in fact rollback) should be logged in
1171   * the recovery log
1172   * @param forceAbort true if the abort will be forced. Actually, abort will do
1173   * nothing when a transaction has savepoints (we do not abort the
1174   * whole transaction, so that the user can rollback to a previous
1175   * savepoint), except when the connection is closed. In this last
1176   * case, if the transaction is not aborted, it prevents future
1177   * maintenance operations such as shutdowns, enable/disable from
1178   * completing, so we have to force this abort operation. It also
1179   * applies to the DeadlockDetectionThread and the cleanup of the
1180   * VirtualDatabaseWorkerThread.
1181   * @throws SQLException if an error occurs
1182   */

1183  public void abort(long transactionId, boolean logAbort, boolean forceAbort)
1184      throws SQLException JavaDoc
1185  {
1186    requestManager.abort(transactionId, logAbort, forceAbort);
1187    // Emulate this as a rollback for the RequestPlayer
1188
if (requestLogger.isInfoEnabled())
1189      requestLogger.info("R " + transactionId);
1190  }
1191
1192  /**
1193   * Commits a transaction given its id.
1194   *
1195   * @param transactionId the transaction id
1196   * @param logCommit true if the commit should be logged in the recovery log
1197   * @param emptyTransaction true if this transaction has not executed any
1198   * request
1199   * @exception SQLException if an error occurs
1200   */

1201  public void commit(long transactionId, boolean logCommit,
1202      boolean emptyTransaction) throws SQLException JavaDoc
1203  {
1204    try
1205    {
1206      if (requestLogger.isInfoEnabled())
1207        requestLogger.info("C " + transactionId);
1208      requestManager.commit(transactionId, logCommit, emptyTransaction);
1209    }
1210    catch (SQLException JavaDoc e)
1211    {
1212
1213      String JavaDoc msg = "Commit of transaction '" + transactionId + "' failed ("
1214          + e.getMessage() + ")";
1215
1216      // If the commit fails in a transaction, the transaction is likely
1217
// to be rollbacked by the underlying database. Then we have to abort
1218
// the transaction.
1219
msg = Translate.get("loadbalancer.commit.failed.and.abort", new String JavaDoc[]{
1220          Long.toString(transactionId), e.getMessage()});
1221      try
1222      {
1223        abort(transactionId, true, false);
1224      }
1225      catch (SQLException JavaDoc e1)
1226      {
1227        if (logger.isInfoEnabled())
1228          logger
1229              .info(
1230                  "Abort after commit failure in transaction did not succeed probably because the transaction has already been aborted",
1231                  e);
1232      }
1233
1234      logger.warn(msg);
1235      throw e;
1236    }
1237  }
1238
1239  /**
1240   * Rollbacks a transaction given its id.
1241   *
1242   * @param transactionId the transaction id
1243   * @param logRollback true if the rollback should be logged in the recovery
1244   * log
1245   * @exception SQLException if an error occurs
1246   */

1247  public void rollback(long transactionId, boolean logRollback)
1248      throws SQLException JavaDoc
1249  {
1250    try
1251    {
1252      if (requestLogger.isInfoEnabled())
1253        requestLogger.info("R " + transactionId);
1254      requestManager.rollback(transactionId, logRollback);
1255    }
1256    catch (SQLException JavaDoc e)
1257    {
1258      String JavaDoc msg = "Rollback of transaction '" + transactionId + "' failed ("
1259          + e.getMessage() + ")";
1260      logger.warn(msg);
1261      throw e;
1262    }
1263  }
1264
1265  /**
1266   * Rollbacks a transaction given its id to a savepoint given its name
1267   *
1268   * @param transactionId the transaction id
1269   * @param savepointName the name of the savepoint
1270   * @exception SQLException if an error occurs
1271   */

1272  public void rollback(long transactionId, String JavaDoc savepointName)
1273      throws SQLException JavaDoc
1274  {
1275    try
1276    {
1277      if (requestLogger.isInfoEnabled())
1278        requestLogger.info("R " + transactionId + " " + savepointName);
1279      requestManager.rollback(transactionId, savepointName);
1280    }
1281    catch (SQLException JavaDoc e)
1282    {
1283      String JavaDoc msg = "Rollback to savepoint '" + savepointName + "' for "
1284          + "transaction '" + transactionId + "' failed (" + e.getMessage()
1285          + ")";
1286      logger.warn(msg);
1287      throw e;
1288    }
1289  }
1290
1291  /**
1292   * Sets a unnamed savepoint to a transaction given its id.
1293   *
1294   * @param transactionId the transaction id
1295   * @return the savepoint id
1296   * @exception SQLException if an error occurs
1297   */

1298  public int setSavepoint(long transactionId) throws SQLException JavaDoc
1299  {
1300    try
1301    {
1302      int savepointId = requestManager.setSavepoint(transactionId);
1303      if (requestLogger.isInfoEnabled())
1304        requestLogger.info("P " + transactionId + " " + savepointId);
1305      return savepointId;
1306    }
1307    catch (SQLException JavaDoc e)
1308    {
1309      String JavaDoc msg = "Setting unnamed savepoint to transaction '" + transactionId
1310          + "' failed (" + e.getMessage() + ")";
1311      logger.warn(msg);
1312      throw e;
1313    }
1314  }
1315
1316  /**
1317   * Sets a savepoint given its desired name to a transaction given its id.
1318   *
1319   * @param transactionId the transaction id
1320   * @param name the desired name of the savepoint
1321   * @exception SQLException if an error occurs
1322   */

1323  public void setSavepoint(long transactionId, String JavaDoc name) throws SQLException JavaDoc
1324  {
1325    try
1326    {
1327      if (requestLogger.isInfoEnabled())
1328        requestLogger.info("P " + transactionId + " " + name);
1329      requestManager.setSavepoint(transactionId, name);
1330    }
1331    catch (SQLException JavaDoc e)
1332    {
1333      String JavaDoc msg = "Setting savepoint with name '" + name + "' to transaction "
1334          + "'" + transactionId + "' failed (" + e.getMessage() + ")";
1335      logger.warn(msg);
1336      throw e;
1337    }
1338  }
1339
1340  /**
1341   * Releases a savepoint given its name from a transaction given its id.
1342   *
1343   * @param transactionId the transaction id
1344   * @param savepointName the name of the savepoint
1345   * @exception SQLException if an error occurs
1346   */

1347  public void releaseSavepoint(long transactionId, String JavaDoc savepointName)
1348      throws SQLException JavaDoc
1349  {
1350    try
1351    {
1352      if (requestLogger.isInfoEnabled())
1353        requestLogger.info("F " + transactionId + " " + savepointName);
1354      requestManager.releaseSavepoint(transactionId, savepointName);
1355    }
1356    catch (SQLException JavaDoc e)
1357    {
1358      String JavaDoc msg = "Releasing savepoint with name '" + savepointName
1359          + "' from " + "transaction '" + transactionId + "' failed ("
1360          + e.getMessage() + ")";
1361      logger.warn(msg);
1362      throw e;
1363    }
1364  }
1365
1366  //
1367
// Database backends management
1368
//
1369

1370  /**
1371   * Add a backend to this virtual database.
1372   *
1373   * @param db the database backend to add
1374   * @throws VirtualDatabaseException if an error occurs
1375   */

1376  public void addBackend(DatabaseBackend db) throws VirtualDatabaseException
1377  {
1378    this.addBackend(db, true);
1379  }
1380
1381  /**
1382   * Add a backend to this virtual database.
1383   *
1384   * @param db the database backend to add
1385   * @param checkForCompliance should load the driver ?
1386   * @throws VirtualDatabaseException if an error occurs
1387   */

1388  public void addBackend(DatabaseBackend db, boolean checkForCompliance)
1389      throws VirtualDatabaseException
1390  {
1391    if (db == null)
1392    {
1393      String JavaDoc msg = "Illegal null database backend in addBackend(DatabaseBackend) method";
1394      logger.error(msg);
1395      throw new VirtualDatabaseException(msg);
1396    }
1397
1398    if (db.isReadEnabled())
1399    {
1400      String JavaDoc msg = "It is not allowed to add an enabled database.";
1401      logger.error(msg);
1402      throw new VirtualDatabaseException(msg);
1403    }
1404
1405    // Get the lock on the list of backends
1406
try
1407    {
1408      rwLock.acquireWrite();
1409    }
1410    catch (InterruptedException JavaDoc e)
1411    {
1412      String JavaDoc msg = Translate.get(
1413          "loadbalancer.backendlist.acquire.writelock.failed", e);
1414      logger.error(msg);
1415      throw new VirtualDatabaseException(msg);
1416    }
1417
1418    // Check that the backend is not already up
1419
if (backends.indexOf(db) != -1)
1420    {
1421      rwLock.releaseWrite();
1422      String JavaDoc msg = "Duplicate backend " + db.getURL();
1423      logger.warn(msg);
1424      throw new VirtualDatabaseException(msg);
1425    }
1426
1427    // Check the authentication manager has all virtual logins defined
1428
ArrayList JavaDoc logins = authenticationManager.getVirtualLogins();
1429    VirtualDatabaseUser vdu;
1430    String JavaDoc login;
1431    for (int i = 0; i < logins.size(); i++)
1432    {
1433      vdu = (VirtualDatabaseUser) logins.get(i);
1434      login = vdu.getLogin();
1435      if (db.getConnectionManager(login) == null)
1436      {
1437        rwLock.releaseWrite();
1438        throw new VirtualDatabaseException(Translate.get(
1439            "backend.missing.connection.manager", login));
1440      }
1441    }
1442
1443    // Initialize the driver and check the compliance
1444
try
1445    {
1446      if (logger.isDebugEnabled())
1447        logger.debug("Checking driver compliance");
1448      if (checkForCompliance)
1449        db.checkDriverCompliance(); // Also loads the driver
1450
}
1451    catch (Exception JavaDoc e)
1452    {
1453      rwLock.releaseWrite();
1454      String JavaDoc msg = "Error while adding database backend " + db.getName() + " ("
1455          + e + ")";
1456      logger.warn(msg);
1457      throw new VirtualDatabaseException(msg);
1458    }
1459
1460    db.setSqlShortFormLength(getSqlShortFormLength());
1461
1462    // Add the backend to the list
1463
backends.add(db);
1464    if (logger.isDebugEnabled())
1465      logger.debug("Backend " + db.getName() + " added successfully");
1466
1467    // Set the backend state listener so that the state is logged into the
1468
// recovery log - if any. When there is no recovery log,
1469
// getBackendStateListener returns null, and stateListener is consequently
1470
// set to null. Looks like the only state listner ever is the recoveryLog.
1471
/*
1472     * Note: getRequestManager() is null, at load time, thus the test. At load
1473     * time, if there is a recovery log, the state listner eventually gets set,
1474     * but in a different fashion: it is set by RequestManager c'tor, when
1475     * calling setRecoveryLog().
1476     */

1477    if (getRequestManager() != null)
1478    {
1479      db.setStateListener(getRequestManager().getBackendStateListener());
1480    }
1481
1482    // Release the lock
1483
rwLock.releaseWrite();
1484
1485    // Notify Jmx listeners of the backend addition
1486
sendJmxNotification(SequoiaNotificationList.VIRTUALDATABASE_BACKEND_ADDED,
1487        Translate.get("notification.backend.added", db.getName()));
1488
1489    // Add backend mbean to jmx server
1490
try
1491    {
1492      ObjectName JavaDoc objectName = JmxConstants.getDatabaseBackendObjectName(name,
1493          db.getName());
1494      org.continuent.sequoia.controller.backend.management.DatabaseBackend managingBackend = new org.continuent.sequoia.controller.backend.management.DatabaseBackend(
1495          db);
1496      db.setNotificationBroadcaster(managingBackend.getBroadcaster());
1497      MBeanServerManager.registerMBean(managingBackend, objectName);
1498    }
1499    catch (Exception JavaDoc e)
1500    {
1501      logger.error(Translate.get("virtualdatabase.fail.register.backend.mbean",
1502          db.getName()), e);
1503    }
1504  }
1505
1506  /**
1507   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#forceDisableBackend(String)
1508   */

1509  public void forceDisableBackend(String JavaDoc backendName)
1510      throws VirtualDatabaseException
1511  {
1512    try
1513    {
1514      DatabaseBackend db = getAndCheckBackend(backendName,
1515          CHECK_BACKEND_DISABLE);
1516      requestManager.disableBackend(db, true);
1517      requestManager
1518          .setDatabaseSchema(getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames());
1519
1520      sendJmxNotification(
1521          SequoiaNotificationList.VIRTUALDATABASE_BACKEND_DISABLED, Translate
1522              .get("notification.backend.disabled", db.getName()));
1523    }
1524    catch (Exception JavaDoc e)
1525    {
1526      logger.error("An error occured while disabling backend " + backendName
1527          + " (" + e + ")");
1528      throw new VirtualDatabaseException(e.getMessage(), e);
1529    }
1530  }
1531
1532  /**
1533   * Prepare this virtual database for shutdown. This turns off all the backends
1534   * by cutting communication from this database. This does not prevents other
1535   * virtual database to use shared backends. This doesn't create checkpoints
1536   * either.
1537   *
1538   * @param forceEnable true if backend disabling must be forced, false for
1539   * regular/clean disabling
1540   * @throws VirtualDatabaseException if an error occurs
1541   */

1542  public void disableAllBackends(boolean forceEnable)
1543      throws VirtualDatabaseException
1544  {
1545    try
1546    {
1547      int size = this.backends.size();
1548      DatabaseBackend dbe;
1549      for (int i = 0; i < size; i++)
1550      {
1551        dbe = (DatabaseBackend) backends.get(i);
1552        if (dbe.isReadEnabled())
1553          requestManager.disableBackend(getAndCheckBackend(dbe.getName(),
1554              CHECK_BACKEND_DISABLE), forceEnable);
1555      }
1556    }
1557    catch (Exception JavaDoc e)
1558    {
1559      throw new VirtualDatabaseException(e.getMessage(), e);
1560    }
1561  }
1562
1563  /**
1564   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#disableBackendWithCheckpoint(String)
1565   */

1566  public void disableBackendWithCheckpoint(String JavaDoc backendName)
1567      throws VirtualDatabaseException
1568  {
1569    try
1570    {
1571      DatabaseBackend backend = getAndCheckBackend(backendName,
1572          NO_CHECK_BACKEND);
1573      if (backend.isDisabled())
1574      {
1575        logger.info("Backend " + backendName + " is already disabled.");
1576        // disabling a disabled backend is a no-op
1577
return;
1578      }
1579      requestManager.disableBackendWithCheckpoint(backend,
1580          buildCheckpointName("disable " + backendName));
1581      // Force a schema refresh if we are not in RAIDb-1
1582
if (requestManager.getLoadBalancer().getRAIDbLevel() != RAIDbLevels.RAIDb1)
1583        requestManager
1584            .setDatabaseSchema(getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames());
1585    }
1586    catch (Exception JavaDoc e)
1587    {
1588      logger.error("An error occured while disabling backend " + backendName
1589          + " (" + e + ")");
1590      throw new VirtualDatabaseException(e.getMessage(), e);
1591    }
1592  }
1593
1594  /**
1595   * Disable all backends and store a checkpoint
1596   *
1597   * @param checkpoint the name of the checkpoitn
1598   * @throws VirtualDatabaseException if fails
1599   */

1600  public void disableAllBackendsWithCheckpoint(String JavaDoc checkpoint)
1601      throws VirtualDatabaseException
1602  {
1603    if (checkpoint == null)
1604    {
1605      disableAllBackends(false);
1606      return;
1607    }
1608
1609    try
1610    {
1611      this.acquireReadLockBackendLists();
1612    }
1613    catch (InterruptedException JavaDoc e)
1614    {
1615      throw new VirtualDatabaseException(e.getMessage(), e);
1616    }
1617
1618    try
1619    {
1620      ArrayList JavaDoc backendInfos = new ArrayList JavaDoc();
1621      Iterator JavaDoc iter = backends.iterator();
1622      while (iter.hasNext())
1623      {
1624        DatabaseBackend backend = (DatabaseBackend) iter.next();
1625        backendInfos.add(new BackendInfo(backend));
1626      }
1627      requestManager.disableBackendsWithCheckpoint(backendInfos, checkpoint);
1628    }
1629    catch (Exception JavaDoc e)
1630    {
1631      throw new VirtualDatabaseException(e.getMessage(), e);
1632    }
1633    finally
1634    {
1635      this.releaseReadLockBackendLists();
1636    }
1637  }
1638
1639  /**
1640   * Check that the virtual database is not resyncing its recovery log or
1641   * shutting down. This would prevent enable operations to take place.
1642   *
1643   * @throws VirtualDatabaseException if virtual database is resyncing or
1644   * shutting down.
1645   */

1646  private void enableBackendSanityChecks() throws VirtualDatabaseException
1647  {
1648    if (isResyncing())
1649    {
1650      String JavaDoc msg = Translate.get("virtualdatabase.fail.enable.cause.resyncing");
1651      logger.warn(msg);
1652      throw new VirtualDatabaseException(msg);
1653    }
1654
1655    if (isShuttingDown())
1656    {
1657      String JavaDoc msg = Translate.get("virtualdatabase.fail.enable.cause.shutdown ");
1658      logger.warn(msg);
1659      throw new VirtualDatabaseException(msg);
1660    }
1661  }
1662
1663  /**
1664   * Enable the given backend from the given checkpoint. This method returns
1665   * once the recovery is complete.
1666   *
1667   * @param backendName backend to enable
1668   * @param checkpointName checkpoint to enable from
1669   * @throws VirtualDatabaseException if an error occurs
1670   */

1671  public void enableBackendFromCheckpoint(String JavaDoc backendName,
1672      String JavaDoc checkpointName) throws VirtualDatabaseException
1673  {
1674    enableBackendSanityChecks();
1675
1676    EnableBackendOperation enableOperation = new EnableBackendOperation(
1677        backendName);
1678    addAdminOperation(enableOperation);
1679
1680    // Call the Request Manager
1681
try
1682    {
1683      DatabaseBackend backend = getAndCheckBackend(backendName,
1684          CHECK_BACKEND_ENABLE);
1685      RecoverThread recoverThread = requestManager.enableBackendFromCheckpoint(
1686          backend, checkpointName);
1687      // Wait for recovery to complete
1688
recoverThread.join();
1689      if (recoverThread.getException() != null)
1690      {
1691        throw recoverThread.getException();
1692      }
1693      requestManager.setSchemaIsDirty(true);
1694
1695      // Update the static metadata
1696
getStaticMetaData().gatherStaticMetadata(backend);
1697
1698      // Update the list of database product names
1699
if (databaseProductNames.indexOf(backend.getDatabaseProductName()) == -1)
1700        databaseProductNames += "," + backend.getDatabaseProductName();
1701    }
1702    catch (Exception JavaDoc e)
1703    {
1704      String JavaDoc msg = Translate.get(
1705          "virtualdatabase.enable.from.checkpoint.failed", new Object JavaDoc[]{name,
1706              backendName, e});
1707      logger.warn(msg, e);
1708      throw new VirtualDatabaseException(msg, e);
1709    }
1710    finally
1711    {
1712      removeAdminOperation(enableOperation);
1713    }
1714  }
1715
1716  /**
1717   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#enableBackendFromCheckpoint(java.lang.String)
1718   */

1719  public void enableBackendFromCheckpoint(String JavaDoc backendName)
1720      throws VirtualDatabaseException
1721  {
1722    enableBackendSanityChecks();
1723
1724    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
1725    String JavaDoc checkpoint = backend.getLastKnownCheckpoint();
1726    if ((checkpoint == null) || ("".equals(checkpoint)))
1727      throw new VirtualDatabaseException(
1728          "Cannot enable backend "
1729              + backendName
1730              + " from a known state. Resynchronize this backend by restoring a dump.");
1731    else
1732    {
1733      if (logger.isDebugEnabled())
1734        logger.debug("Enabling backend " + backendName
1735            + " from its last checkpoint " + backend.getLastKnownCheckpoint());
1736    }
1737    enableBackendFromCheckpoint(backendName, backend.getLastKnownCheckpoint());
1738  }
1739
1740  /**
1741   * Enable all the backends without any check.
1742   *
1743   * @throws VirtualDatabaseException if fails
1744   */

1745  public void enableAllBackends() throws VirtualDatabaseException
1746  {
1747    enableBackendSanityChecks();
1748
1749    try
1750    {
1751      int size = this.backends.size();
1752      DatabaseBackend dbe;
1753      for (int i = 0; i < size; i++)
1754      {
1755        dbe = (DatabaseBackend) backends.get(i);
1756        if (!dbe.isReadEnabled())
1757          forceEnableBackend(((DatabaseBackend) backends.get(i)).getName());
1758      }
1759    }
1760    catch (RuntimeException JavaDoc e)
1761    {
1762      logger.error("Runtime error in enableAllBackends", e);
1763      throw new VirtualDatabaseException(e.getMessage(), e);
1764    }
1765  }
1766
1767  /**
1768   * Enable all backends from their last known states that has been recorded in
1769   * the recovery log, and enable only the backends which where properly
1770   * disabled.
1771   *
1772   * @throws VirtualDatabaseException if fails
1773   * @deprecated to enable all backends, you have to iterate on the backend
1774   * names and enable one backend at the time
1775   * @see #getAllBackendNames()
1776   * @see #enableBackendFromCheckpoint(String)
1777   */

1778  public void enableAllBackendsFromCheckpoint() throws VirtualDatabaseException
1779  {
1780    RecoveryLog log = requestManager.getRecoveryLog();
1781    if (log == null)
1782    {// If no recovery log is defined ignore fallback to a forced enable
1783
logger
1784          .warn("No recovery log has been configured, enabling backend without checkpoint.");
1785      enableAllBackends();
1786    }
1787    else
1788    {
1789      enableBackendSanityChecks();
1790
1791      try
1792      {
1793        int size = this.backends.size();
1794        DatabaseBackend dbe;
1795        String JavaDoc backendName;
1796        BackendRecoveryInfo info;
1797        for (int i = 0; i < size; i++)
1798        {
1799          dbe = (DatabaseBackend) backends.get(i);
1800          backendName = dbe.getName();
1801          info = log.getBackendRecoveryInfo(name, backendName);
1802          switch (info.getBackendState())
1803          {
1804            case BackendState.DISABLED :
1805              String JavaDoc checkpoint = info.getCheckpoint();
1806              if (checkpoint == null || checkpoint.equals(""))
1807              {
1808                logger
1809                    .warn("Cannot enable backend "
1810                        + backendName
1811                        + " from a known state. Resynchronize this backend by restoring a dump.");
1812              }
1813              else
1814              {
1815                logger.info("Enabling backend " + backendName
1816                    + " from checkpoint " + checkpoint);
1817                enableBackendFromCheckpoint(dbe.getName(), checkpoint);
1818              }
1819              continue;
1820            case BackendState.UNKNOWN :
1821              logger.info("Unknown last state for backend " + backendName
1822                  + ". Leaving node in "
1823                  + (dbe.isReadEnabled() ? "enabled" : "disabled") + " state.");
1824              continue;
1825            case BackendState.BACKUPING :
1826            case BackendState.DISABLING :
1827            case BackendState.RESTORING :
1828            case BackendState.REPLAYING :
1829              if (!dbe.isReadEnabled())
1830              {
1831                logger.info("Unexpected transition state ("
1832                    + info.getBackendState() + ") for backend " + backendName
1833                    + ". Forcing backend to disabled state.");
1834                info.setBackendState(BackendState.DISABLED);
1835                log.storeBackendRecoveryInfo(name, info);
1836              }
1837              else
1838                logger.info("Unexpected transition state ("
1839                    + info.getBackendState() + ") for backend " + backendName
1840                    + ". Leaving backend in its current state.");
1841              continue;
1842            default :
1843              if (!dbe.isReadEnabled())
1844              {
1845                logger.info("Unexpected enabled state ("
1846                    + info.getBackendState() + ") for backend " + backendName
1847                    + ". Forcing backend to disabled state.");
1848                info.setBackendState(BackendState.DISABLED);
1849                log.storeBackendRecoveryInfo(name, info);
1850              }
1851              else
1852                logger.info("Unexpected enabled state ("
1853                    + info.getBackendState() + ") for backend " + backendName
1854                    + ". Leaving backend in its current state.");
1855              continue;
1856          }
1857        }
1858      }
1859      catch (Exception JavaDoc e)
1860      {
1861        throw new VirtualDatabaseException(e.getMessage(), e);
1862      }
1863    }
1864  }
1865
1866  /**
1867   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#forceEnableBackend(String)
1868   */

1869  public void forceEnableBackend(String JavaDoc backendName)
1870      throws VirtualDatabaseException
1871  {
1872    enableBackendSanityChecks();
1873
1874    EnableBackendOperation enableOperation = new EnableBackendOperation(
1875        backendName + " (force)");
1876    addAdminOperation(enableOperation);
1877
1878    // Call the Request Manager
1879
try
1880    {
1881      DatabaseBackend backend = getAndCheckBackend(backendName,
1882          CHECK_BACKEND_ENABLE);
1883
1884      requestManager.enableBackend(backend);
1885      requestManager.setSchemaIsDirty(true);
1886
1887      // Update the list of database product names
1888
if (databaseProductNames.indexOf(backend.getDatabaseProductName()) == -1)
1889        databaseProductNames += "," + backend.getDatabaseProductName();
1890
1891      // Update the static metadata
1892
getStaticMetaData().gatherStaticMetadata(backend);
1893
1894      sendJmxNotification(
1895          SequoiaNotificationList.VIRTUALDATABASE_BACKEND_ENABLED, Translate
1896              .get("notification.backend.enabled"));
1897    }
1898    catch (Exception JavaDoc e)
1899    {
1900      throw new VirtualDatabaseException(e.getMessage(), e);
1901    }
1902    finally
1903    {
1904      removeAdminOperation(enableOperation);
1905    }
1906  }
1907
1908  /**
1909   * Prepare this virtual database for startup. This turns on all the backends
1910   * from the given checkpoint. If the checkpoint is null or an empty String,
1911   * the backends are enabled without further check else the backend states are
1912   * overriden to use the provided checkpoint.
1913   *
1914   * @param checkpoint checkpoint for recovery log
1915   * @throws VirtualDatabaseException if fails
1916   */

1917  public void forceEnableAllBackendsFromCheckpoint(String JavaDoc checkpoint)
1918      throws VirtualDatabaseException
1919  {
1920    enableBackendSanityChecks();
1921
1922    if (checkpoint == null || checkpoint.equals(""))
1923      enableAllBackends();
1924    else
1925    {
1926      try
1927      {
1928        int size = this.backends.size();
1929        DatabaseBackend backend;
1930        for (int i = 0; i < size; i++)
1931        {
1932          backend = (DatabaseBackend) backends.get(i);
1933          if (!backend.isReadEnabled())
1934          {
1935            backend.setLastKnownCheckpoint(checkpoint);
1936            enableBackendFromCheckpoint(backend.getName(), checkpoint);
1937          }
1938        }
1939      }
1940      catch (RuntimeException JavaDoc e)
1941      {
1942        logger
1943            .error("Runtime error in forceEnableAllBackendsFromCheckpoint", e);
1944        throw new VirtualDatabaseException(e.getMessage(), e);
1945      }
1946    }
1947  }
1948
1949  /**
1950   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getAllBackendNames()
1951   */

1952  public ArrayList JavaDoc getAllBackendNames() throws VirtualDatabaseException
1953  {
1954    try
1955    {
1956      acquireReadLockBackendLists();
1957    }
1958    catch (InterruptedException JavaDoc e)
1959    {
1960      String JavaDoc msg = "Unable to acquire read lock on backend list in getAllBackendNames ("
1961          + e + ")";
1962      logger.error(msg);
1963      throw new VirtualDatabaseException(msg);
1964    }
1965
1966    int size = backends.size();
1967    ArrayList JavaDoc result = new ArrayList JavaDoc();
1968    for (int i = 0; i < size; i++)
1969    {
1970      result.add(((DatabaseBackend) backends.get(i)).getName());
1971    }
1972
1973    releaseReadLockBackendLists();
1974    return result;
1975  }
1976
1977  /**
1978   * Find the DatabaseBackend corresponding to the given backend name and check
1979   * if it is possible to disable this backend. In the case enable, this method
1980   * also updates the virtual database schema by merging it with the one
1981   * provided by this backend.
1982   *
1983   * @param backendName backend to look for
1984   * @param testEnable NO_CHECK_BACKEND no check is done, CHECK_BACKEND_DISABLE
1985   * check if it is possible to disable the backend,
1986   * CHECK_BACKEND_ENABLE check if it is possible to enable the backend
1987   * @return the backend to disable
1988   * @throws VirtualDatabaseException if an error occurs
1989   */

1990  public DatabaseBackend getAndCheckBackend(String JavaDoc backendName, int testEnable)
1991      throws VirtualDatabaseException
1992  {
1993    try
1994    {
1995      acquireReadLockBackendLists();
1996    }
1997    catch (InterruptedException JavaDoc e)
1998    {
1999      String JavaDoc msg = "Unable to acquire read lock on backend list in getAndCheckBackend ("
2000          + e + ")";
2001      logger.error(msg);
2002      throw new VirtualDatabaseException(msg);
2003    }
2004
2005    DatabaseBackend b;
2006    try
2007    {
2008      // Find the backend
2009
int size = backends.size();
2010      b = null;
2011      for (int i = 0; i < size; i++)
2012      {
2013        b = (DatabaseBackend) backends.get(i);
2014        if (b.getName().equals(backendName))
2015          break;
2016        else
2017          b = null;
2018      }
2019
2020      // Check not null
2021
if (b == null)
2022      {
2023        String JavaDoc msg = "Trying to access a non-existing backend " + backendName;
2024        logger.warn(msg);
2025        throw new VirtualDatabaseException(msg);
2026      }
2027
2028      // Check enable/disable
2029
switch (testEnable)
2030      {
2031        case NO_CHECK_BACKEND :
2032          break;
2033        case CHECK_BACKEND_DISABLE :
2034          if (!b.isReadEnabled())
2035          {
2036            String JavaDoc msg = "Backend " + backendName + " is already disabled";
2037            logger.warn(msg);
2038            throw new VirtualDatabaseException(msg);
2039          }
2040          break;
2041        case CHECK_BACKEND_ENABLE :
2042          if (b.isReadEnabled())
2043          {
2044            String JavaDoc msg = "Backend " + backendName + " is already enabled";
2045            logger.warn(msg);
2046            throw new VirtualDatabaseException(msg);
2047          }
2048          break;
2049        default :
2050          String JavaDoc msg = "Unexpected parameter in getAndCheckBackend(...)";
2051          logger.error(msg);
2052          throw new VirtualDatabaseException(msg);
2053      }
2054    }
2055    finally
2056    {
2057      releaseReadLockBackendLists();
2058    }
2059
2060    if (testEnable == CHECK_BACKEND_ENABLE)
2061    {
2062      // Initialize backend for enable
2063
try
2064      {
2065        if (logger.isDebugEnabled())
2066          logger.debug("Initializing connections for backend " + b.getName());
2067        b.initializeConnections();
2068
2069        b.checkDriverCompliance();
2070
2071        if (logger.isDebugEnabled())
2072          logger.debug("Checking schema for backend " + b.getName());
2073        b.checkDatabaseSchema(null);
2074
2075        DatabaseSchema backendSchema = b.getDatabaseSchema();
2076
2077        if (backendSchema != null)
2078          requestManager.mergeDatabaseSchema(backendSchema);
2079        else
2080          logger.warn("Backend " + b.getName() + " has no defined schema.");
2081      }
2082      catch (SQLException JavaDoc e)
2083      {
2084        String JavaDoc msg = "Error while initalizing database backend " + b.getName()
2085            + " (" + e + ")";
2086        logger.warn(msg, e);
2087        throw new VirtualDatabaseException(msg, e);
2088      }
2089    }
2090
2091    return b;
2092  }
2093
2094  /**
2095   * Returns true if this vdb is resynching. This can only happen for
2096   * distributed vdbs that have recovery logs.
2097   *
2098   * @return true if db is resynching
2099   */

2100  protected boolean isResyncing()
2101  {
2102    return false;
2103  }
2104
2105  /**
2106   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#replicateBackend(java.lang.String,
2107   * java.lang.String, java.util.Map)
2108   */

2109  public void replicateBackend(String JavaDoc backendName, String JavaDoc newBackendName,
2110      Map JavaDoc parameters) throws VirtualDatabaseException
2111  {
2112    // Access the backend we want to replicate
2113
DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
2114    DatabaseBackend newBackend = null;
2115
2116    // Create a clone of the backend with additionnal parameters
2117
try
2118    {
2119      newBackend = backend.copy(newBackendName, parameters);
2120    }
2121    catch (Exception JavaDoc e)
2122    {
2123      String JavaDoc msg = Translate.get("virtualdatabase.fail.backend.copy", e);
2124      logger.warn(msg, e);
2125      throw new VirtualDatabaseException(msg, e);
2126    }
2127
2128    // Add the backend to the virtual database.
2129
addBackend(newBackend);
2130  }
2131
2132  /**
2133   * Remove a backend from the virtual database list. Do not check whether it is
2134   * enabled or not, and do not perform backup operation
2135   *
2136   * @param backend the name of the backend to remove
2137   * @throws VirtualDatabaseException if the backend does not exist
2138   */

2139  public void removeBackend(String JavaDoc backend) throws VirtualDatabaseException
2140  {
2141    removeBackend(getAndCheckBackend(backend, NO_CHECK_BACKEND));
2142  }
2143
2144  /**
2145   * Remove a backend from this virtual database.
2146   *
2147   * @param db the database backend to remove
2148   * @throws VirtualDatabaseException if an error occurs
2149   */

2150  public void removeBackend(DatabaseBackend db) throws VirtualDatabaseException
2151  {
2152    if (db == null)
2153    {
2154      String JavaDoc msg = "Illegal null database backend in removeBackend(DatabaseBackend) method";
2155      logger.error(msg);
2156      throw new VirtualDatabaseException(msg);
2157    }
2158
2159    try
2160    {
2161      rwLock.acquireWrite();
2162    }
2163    catch (InterruptedException JavaDoc e)
2164    {
2165      String JavaDoc msg = Translate.get(
2166          "loadbalancer.backendlist.acquire.writelock.failed", e);
2167      logger.error(msg);
2168      throw new VirtualDatabaseException(msg);
2169    }
2170
2171    // Sanity checks
2172
int idx = backends.indexOf(db);
2173    if (idx == -1)
2174    {
2175      rwLock.releaseWrite(); // Release the lock
2176
String JavaDoc msg = "Trying to remove a non-existing backend " + db.getName();
2177      logger.warn(msg);
2178      throw new VirtualDatabaseException(msg);
2179    }
2180
2181    if (((DatabaseBackend) backends.get(idx)).isReadEnabled())
2182    {
2183      rwLock.releaseWrite(); // Release the lock
2184
String JavaDoc msg = "Trying to remove an enabled backend " + db.getName();
2185      logger.error(msg);
2186      throw new VirtualDatabaseException(msg);
2187    }
2188
2189    // Remove it
2190
backends.remove(idx);
2191    rwLock.releaseWrite(); // Relase the lock
2192

2193    sendJmxNotification(
2194        SequoiaNotificationList.VIRTUALDATABASE_BACKEND_REMOVED, Translate
2195            .get("notification.backend.removed"));
2196
2197    // Remove backend mbean to jmx server
2198
try
2199    {
2200      ObjectName JavaDoc objectName = JmxConstants.getDatabaseBackendObjectName(name,
2201          db.getName());
2202      MBeanServerManager.unregister(objectName);
2203    }
2204    catch (Exception JavaDoc e)
2205    {
2206      logger.error(Translate.get(
2207          "virtualdatabase.fail.unregister.backend.mbean", db.getName()), e);
2208    }
2209
2210    if (logger.isDebugEnabled())
2211      logger.debug("Backend " + db.getName() + " removed successfully");
2212  }
2213
2214  /**
2215   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#transferBackend(java.lang.String,
2216   * java.lang.String)
2217   */

2218  public void transferBackend(String JavaDoc backend, String JavaDoc controllerDestination)
2219      throws VirtualDatabaseException
2220  {
2221    throw new VirtualDatabaseException("Cannot transfer backend to controller:"
2222        + controllerDestination + " because database is not distributed");
2223  }
2224
2225  //
2226
// Backup & Checkpoint management
2227
//
2228

2229  /**
2230   * Returns a cluster-wide unique checkpoint name.
2231   * <p>
2232   * Unicity is needed since checkpoints are always set cluster-wide.
2233   *
2234   * @param event the reason why this checkpoint is being set
2235   * @return a cluster-wide unique checkpoint name
2236   */

2237  public String JavaDoc buildCheckpointName(String JavaDoc event)
2238  {
2239    /*
2240     * Checkpoints name are now built such as they can be sorted alphabetically
2241     */

2242    SimpleDateFormat JavaDoc dateFormat = new SimpleDateFormat JavaDoc("yyyyMMddHHmmssSSSZ");
2243    return (event + "-" + controller.getControllerName() + "-" + dateFormat
2244        .format(new Date JavaDoc(System.currentTimeMillis())));
2245  }
2246
2247  /**
2248   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#backupBackend(String,
2249   * String, String, String, String, String, boolean, ArrayList)
2250   */

2251  public void backupBackend(String JavaDoc backendName, String JavaDoc login, String JavaDoc password,
2252      String JavaDoc dumpName, String JavaDoc backuperName, String JavaDoc path, boolean force,
2253      ArrayList JavaDoc tables) throws VirtualDatabaseException
2254  {
2255    // Sanity checks
2256
if (!isDumpNameAvailable(dumpName))
2257    {
2258      throw new VirtualDatabaseException(Translate.get(
2259          "virtualdatabase.backup.dumpNameError", dumpName));
2260    }
2261    if (!force && (getNumberOfEnabledBackends() == 1))
2262    {
2263      throw new VirtualDatabaseException(Translate
2264          .get("virtualdatabase.backup.onlyOneBackendLeft"));
2265    }
2266
2267    // Perform the backup
2268
BackupBackendOperation backupOperation = new BackupBackendOperation(
2269        backendName, dumpName);
2270    try
2271    {
2272      addAdminOperation(backupOperation);
2273      DatabaseBackend db = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
2274      requestManager.backupBackend(db, login, password, dumpName, backuperName,
2275          path, tables);
2276    }
2277    catch (SQLException JavaDoc sql)
2278    {
2279      throw new VirtualDatabaseException(sql);
2280    }
2281    finally
2282    {
2283      removeAdminOperation(backupOperation);
2284    }
2285  }
2286
2287  protected int getNumberOfEnabledBackends() throws VirtualDatabaseException
2288  {
2289    // This check is not sufficient. Disable functionality. (see SEQUOIA-556)
2290
if (true)
2291      return -1;
2292
2293    try
2294    {
2295      acquireReadLockBackendLists();
2296    }
2297    catch (InterruptedException JavaDoc e)
2298    {
2299      String JavaDoc msg = Translate.get("virtualdatabase.fail.read.lock");
2300      logger.error(msg, e);
2301      throw new VirtualDatabaseException(msg, e);
2302    }
2303
2304    int nbActive = 0;
2305    DatabaseBackend b;
2306    int size = backends.size();
2307    b = null;
2308    for (int i = 0; i < size; i++)
2309    {
2310      b = (DatabaseBackend) backends.get(i);
2311      if (b.isReadEnabled() || b.isWriteEnabled())
2312        // test symetrical to RequestManager.backupBackend()
2313
nbActive++;
2314    }
2315
2316    releaseReadLockBackendLists();
2317
2318    return nbActive;
2319  }
2320
2321  /**
2322   * Checks if the dump name is available. If the <code>dumpName</code> is
2323   * already taken by an existing dump, return <code>false</code>; else
2324   * return <code>true</code>
2325   *
2326   * @param tentativeDumpName tentative dump name we want to check availability
2327   * @return <code>true</code> is the name is available, <code>false</code>
2328   * else.
2329   */

2330  public boolean isDumpNameAvailable(String JavaDoc tentativeDumpName)
2331  {
2332    DumpInfo[] dumps;
2333    try
2334    {
2335      dumps = getAvailableDumps();
2336    }
2337    catch (VirtualDatabaseException e)
2338    {
2339      return true;
2340    }
2341    for (int i = 0; i < dumps.length; i++)
2342    {
2343      DumpInfo dump = dumps[i];
2344      if (dump.getDumpName().equals(tentativeDumpName))
2345      {
2346        return false;
2347      }
2348    }
2349    return true;
2350  }
2351
2352  /**
2353   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#copyLogFromCheckpoint(java.lang.String,
2354   * java.lang.String)
2355   */

2356  public void copyLogFromCheckpoint(String JavaDoc dumpName, String JavaDoc controllerName)
2357      throws VirtualDatabaseException
2358  {
2359    if (!hasRecoveryLog())
2360      throw new VirtualDatabaseException(Translate
2361          .get("virtualdatabase.no.recovery.log"));
2362    if (!isDistributed())
2363      throw new VirtualDatabaseException(Translate
2364          .get("virtualdatabase.not.distributed"));
2365
2366    /**
2367     * Implemented in the distributed incarnation of the vdb.
2368     *
2369     * @see org.continuent.sequoia.controller.virtualdatabase.DistributedVirtualDatabase#copyLogFromCheckpoint(String,
2370     * String)
2371     */

2372  }
2373
2374  /**
2375   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#deleteLogUpToCheckpoint(java.lang.String)
2376   */

2377  public void deleteLogUpToCheckpoint(String JavaDoc checkpointName)
2378      throws VirtualDatabaseException
2379  {
2380    if (!hasRecoveryLog())
2381      throw new VirtualDatabaseException(Translate
2382          .get("virtualdatabase.no.recovery.log"));
2383
2384    try
2385    {
2386      getRequestManager().getRecoveryLog().deleteLogEntriesBeforeCheckpoint(
2387          checkpointName);
2388    }
2389    catch (SQLException JavaDoc e)
2390    {
2391      throw new VirtualDatabaseException(e);
2392    }
2393  }
2394
2395  /**
2396   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getBackuperNames()
2397   */

2398  public String JavaDoc[] getBackuperNames()
2399  {
2400    return requestManager.getBackupManager().getBackuperNames();
2401  }
2402
2403  /**
2404   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getAvailableDumps()
2405   */

2406  public DumpInfo[] getAvailableDumps() throws VirtualDatabaseException
2407  {
2408    try
2409    {
2410      RecoveryLog recoveryLog = requestManager.getRecoveryLog();
2411      if (recoveryLog == null)
2412      {
2413        return new DumpInfo[0];
2414      }
2415      else
2416      {
2417        ArrayList JavaDoc dumps = recoveryLog.getDumpList();
2418        return (DumpInfo[]) dumps.toArray(new DumpInfo[dumps.size()]);
2419      }
2420    }
2421    catch (SQLException JavaDoc e)
2422    {
2423      throw new VirtualDatabaseException(e);
2424    }
2425  }
2426
2427  /**
2428   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getDumpFormatForBackuper(java.lang.String)
2429   */

2430  public String JavaDoc getDumpFormatForBackuper(String JavaDoc backuperName)
2431  {
2432    Backuper backuper = requestManager.getBackupManager().getBackuperByName(
2433        backuperName);
2434    if (backuper == null)
2435    {
2436      return null;
2437    }
2438    return backuper.getDumpFormat();
2439  }
2440
2441  /**
2442   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#initializeFromBackend(java.lang.String)
2443   */

2444  public void initializeFromBackend(String JavaDoc databaseBackendName, boolean force)
2445      throws VirtualDatabaseException
2446  {
2447    RecoveryLog log = requestManager.getRecoveryLog();
2448    if (log == null)
2449      throw new VirtualDatabaseException(Translate
2450          .get("virtualdatabase.no.recovery.log"));
2451
2452    try
2453    {
2454      // Check that all backends are in a disabled state without any last known
2455
// checkpoint
2456
int size = this.backends.size();
2457      DatabaseBackend backendToInitializeFrom = null;
2458      for (int i = 0; i < size; i++)
2459      {
2460        DatabaseBackend dbe = (DatabaseBackend) backends.get(i);
2461        String JavaDoc backendName = dbe.getName();
2462        if (backendName.equals(databaseBackendName))
2463        {
2464          backendToInitializeFrom = dbe;
2465        }
2466        if (force)
2467          continue;
2468        BackendRecoveryInfo info = log
2469            .getBackendRecoveryInfo(name, backendName);
2470        if ((info.getBackendState() != BackendState.DISABLED)
2471            && (info.getBackendState() != BackendState.UNKNOWN))
2472          throw new VirtualDatabaseException("Backend " + backendName
2473              + " is not in a disabled state (current state is "
2474              + BackendState.description(info.getBackendState()) + ")");
2475        String JavaDoc checkpoint = info.getCheckpoint();
2476        if ((checkpoint != null) && !checkpoint.equals(""))
2477          throw new VirtualDatabaseException("Backend " + backendName
2478              + " has a last known checkpoint (" + checkpoint + ")");
2479      }
2480      if (backendToInitializeFrom == null)
2481      {
2482        throw new VirtualDatabaseException("backend " + databaseBackendName
2483            + " does not exist");
2484      }
2485      // Ok, the backends are in a clean state, clean the recovery log
2486
log.resetRecoveryLog();
2487      // set the last known checkpoint to Initial_empty_recovery_log
2488
BackendRecoveryInfo info = log.getBackendRecoveryInfo(name,
2489          databaseBackendName);
2490      backendToInitializeFrom
2491          .setLastKnownCheckpoint("Initial_empty_recovery_log");
2492      backendToInitializeFrom.setState(BackendState.DISABLED);
2493      info.setCheckpoint("Initial_empty_recovery_log");
2494      info.setBackendState(BackendState.DISABLED);
2495      log.storeBackendRecoveryInfo(name, info);
2496    }
2497    catch (SQLException JavaDoc e)
2498    {
2499      throw new VirtualDatabaseException(e.getMessage());
2500    }
2501  }
2502
2503  /**
2504   * Remove a checkpoint from the recovery log of this virtual database
2505   *
2506   * @param checkpointName to remove
2507   * @throws VirtualDatabaseException if an error occurs
2508   */

2509  public void removeCheckpoint(String JavaDoc checkpointName)
2510      throws VirtualDatabaseException
2511  {
2512    try
2513    {
2514      requestManager.removeCheckpoint(checkpointName);
2515    }
2516    catch (Exception JavaDoc e)
2517    {
2518      throw new VirtualDatabaseException(e.getMessage());
2519    }
2520  }
2521
2522  /**
2523   * Delete the dump entry associated to the <code>dumpName</code>.<br />
2524   * If <code>keepsFile</code> is false, the dump file is also removed from
2525   * the file system.
2526   *
2527   * @param dumpName name of the dump entry to remove
2528   * @param keepsFile <code>true</code> if the dump should be also removed
2529   * from the file system, <code>false</code> else
2530   * @throws VirtualDatabaseException if an exception occured while removing the
2531   * dump entry or the dump file
2532   */

2533  public void deleteDump(String JavaDoc dumpName, boolean keepsFile)
2534      throws VirtualDatabaseException
2535  {
2536    if (dumpName == null)
2537    {
2538      throw new VirtualDatabaseException("dump name can not be null");
2539    }
2540    RecoveryLog recoveryLog = requestManager.getRecoveryLog();
2541    if (recoveryLog == null)
2542    {
2543      throw new VirtualDatabaseException(
2544          "no recovery log for the virtual database" + getVirtualDatabaseName());
2545    }
2546    DumpInfo dumpInfo;
2547    try
2548    {
2549      dumpInfo = recoveryLog.getDumpInfo(dumpName);
2550    }
2551    catch (SQLException JavaDoc e)
2552    {
2553      throw new VirtualDatabaseException(e);
2554    }
2555    if (dumpInfo == null)
2556    {
2557      throw new VirtualDatabaseException("Dump of name " + dumpName
2558          + " not found in the recovery log of virtual database "
2559          + getVirtualDatabaseName());
2560    }
2561    Backuper backuper = requestManager.getBackupManager().getBackuperByFormat(
2562        dumpInfo.getDumpFormat());
2563    if (backuper == null)
2564    {
2565      throw new VirtualDatabaseException("No backuper found for format "
2566          + dumpInfo.getDumpFormat() + " for the virtual database "
2567          + getVirtualDatabaseName());
2568    }
2569    try
2570    {
2571      recoveryLog.removeDump(dumpInfo);
2572    }
2573    catch (SQLException JavaDoc e)
2574    {
2575      throw new VirtualDatabaseException(e);
2576    }
2577    if (!keepsFile)
2578    {
2579      try
2580      {
2581        backuper.deleteDump(dumpInfo.getDumpPath(), dumpInfo.getDumpName());
2582      }
2583      catch (BackupException e)
2584      {
2585        throw new VirtualDatabaseException(e);
2586      }
2587    }
2588  }
2589
2590  /**
2591   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#renameDump(java.lang.String,
2592   * java.lang.String)
2593   */

2594  public void renameDump(String JavaDoc oldDumpName, String JavaDoc newDumpName)
2595      throws VirtualDatabaseException
2596  {
2597    try
2598    {
2599      RecoveryLog recoveryLog = requestManager.getRecoveryLog();
2600      if (recoveryLog == null)
2601        throw new VirtualDatabaseException("No recovery log"); // TODO I18N
2602

2603      if (isDumpNameAvailable(oldDumpName))
2604        throw new VirtualDatabaseException(oldDumpName + " does not exist");
2605
2606      if (!isDumpNameAvailable(newDumpName))
2607        throw new VirtualDatabaseException(newDumpName + " already exists");
2608
2609      recoveryLog.updateDumpName(oldDumpName, newDumpName);
2610    }
2611    catch (SQLException JavaDoc e)
2612    {
2613      throw new VirtualDatabaseException(e);
2614    }
2615  }
2616
2617  /**
2618   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#restoreDumpOnBackend(String,
2619   * String, String, String, ArrayList)
2620   */

2621  public void restoreDumpOnBackend(String JavaDoc databaseBackendName, String JavaDoc login,
2622      String JavaDoc password, String JavaDoc dumpName, ArrayList JavaDoc tables)
2623      throws VirtualDatabaseException
2624  {
2625    DatabaseBackend backend = getAndCheckBackend(databaseBackendName,
2626        NO_CHECK_BACKEND);
2627    // Backend cannot be null, otherwise the above throws a
2628
// VirtualDatabaseException
2629

2630    RestoreDumpOperation restoreOperation = new RestoreDumpOperation(
2631        databaseBackendName, dumpName);
2632    try
2633    {
2634      addAdminOperation(restoreOperation);
2635      requestManager.restoreBackendFromBackupCheckpoint(backend, login,
2636          password, dumpName, tables);
2637    }
2638    catch (BackupException e)
2639    {
2640      throw new VirtualDatabaseException(e);
2641    }
2642    finally
2643    {
2644      removeAdminOperation(restoreOperation);
2645    }
2646  }
2647
2648  /**
2649   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#setBackendLastKnownCheckpoint
2650   */

2651  public void setBackendLastKnownCheckpoint(String JavaDoc backendName,
2652      String JavaDoc checkpoint) throws VirtualDatabaseException
2653  {
2654    RecoveryLog log = requestManager.getRecoveryLog();
2655    DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
2656    if (log == null)
2657      throw new VirtualDatabaseException("No recovery log has been defined");
2658    else
2659    {
2660      if (!backend.isDisabled())
2661        throw new VirtualDatabaseException(
2662            "Cannot setLastKnownCheckpoint on a non-disabled backend");
2663      else
2664      {
2665        try
2666        {
2667          log.storeBackendRecoveryInfo(this.name,
2668              new BackendRecoveryInfo(backend.getName(), checkpoint, backend
2669                  .getStateValue(), this.name));
2670
2671          backend.setLastKnownCheckpoint(checkpoint);
2672        }
2673        catch (SQLException JavaDoc e)
2674        {
2675          throw new VirtualDatabaseException(
2676              "Failed to store recovery info for backend '" + backendName
2677                  + "' (" + e + ")");
2678        }
2679      }
2680    }
2681  }
2682
2683  /**
2684   * Sets a checkpoint indicating that this vdb has shutdown.
2685   */

2686  public void setShutdownCheckpoint()
2687  {
2688    RecoveryLog recoveryLog = requestManager.getRecoveryLog();
2689    if (recoveryLog != null)
2690      try
2691      {
2692        recoveryLog
2693            .storeCheckpoint("shutdown" + controller.getControllerName());
2694      }
2695      catch (SQLException JavaDoc e)
2696      {
2697        logger.warn("Error while setting shutdown checkpoint", e);
2698      }
2699  }
2700
2701  /**
2702   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#transferDump(java.lang.String,
2703   * java.lang.String, boolean)
2704   */

2705  public void transferDump(String JavaDoc dumpName, String JavaDoc remoteControllerName,
2706      boolean noCopy) throws VirtualDatabaseException
2707  {
2708    if (!isDistributed())
2709      throw new VirtualDatabaseException(
2710          "can not transfer dumps on non-distributed virtual database");
2711  }
2712
2713  /**
2714   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#updateDumpPath(java.lang.String,
2715   * java.lang.String)
2716   */

2717  public void updateDumpPath(String JavaDoc dumpName, String JavaDoc newPath)
2718      throws VirtualDatabaseException
2719  {
2720    try
2721    {
2722      RecoveryLog recoveryLog = requestManager.getRecoveryLog();
2723      if (recoveryLog == null)
2724      {
2725        throw new VirtualDatabaseException("no recovery log"); // TODO I18N
2726
}
2727      else
2728      {
2729        recoveryLog.updateDumpPath(dumpName, newPath);
2730      }
2731    }
2732    catch (SQLException JavaDoc e)
2733    {
2734      throw new VirtualDatabaseException(e);
2735    }
2736  }
2737
2738  /**
2739   * Add a VirtualDatabaseWorkerThread to the list of active threads.
2740   *
2741   * @param thread the VirtualDatabaseWorkerThread to add
2742   */

2743  public void addVirtualDatabaseWorkerThread(VirtualDatabaseWorkerThread thread)
2744  {
2745    synchronized (activeThreads)
2746    {
2747      activeThreads.add(thread);
2748      incrementCurrentNbOfThread();
2749    }
2750  }
2751
2752  /**
2753   * Substract one to currentNbOfThreads. Warning! This method is not
2754   * synchronized.
2755   */

2756  protected void decreaseCurrentNbOfThread()
2757  {
2758    currentNbOfThreads--;
2759  }
2760
2761  /**
2762   * Remove an idle thread. Warning! This method must be called in a
2763   * synchronized block on activeThreads.
2764   */

2765  protected void decreaseIdleThread()
2766  {
2767    idleThreads--;
2768  }
2769
2770  /**
2771   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getCurrentNbOfThreads()
2772   */

2773  public int getCurrentNbOfThreads()
2774  {
2775    return currentNbOfThreads;
2776  }
2777
2778  /**
2779   * Returns the number of idle worker threads. Warning! This method must be
2780   * called in a synchronized block on activeThreads.
2781   *
2782   * @return int number of idle worker threads
2783   */

2784  public int getIdleThreads()
2785  {
2786    return idleThreads;
2787  }
2788
2789  /**
2790   * Returns the maxNbOfThreads.
2791   *
2792   * @return int maximum number of threads
2793   */

2794  public int getMaxNbOfThreads()
2795  {
2796    return maxNbOfThreads;
2797  }
2798
2799  /**
2800   * Returns the maxThreadIdleTime.
2801   *
2802   * @return long maximum thread idle time in ms
2803   */

2804  public long getMaxThreadIdleTime()
2805  {
2806    return maxThreadIdleTime;
2807  }
2808
2809  /**
2810   * Returns the minNbOfThreads.
2811   *
2812   * @return int minimum number of threads
2813   */

2814  public int getMinNbOfThreads()
2815  {
2816    return minNbOfThreads;
2817  }
2818
2819  //
2820
// Thread management mainly used by controller and monitoring
2821
//
2822

2823  /**
2824   * Return the VirtualDatabaseWorkerThread that is currently executing the
2825   * transaction identified by the provided id.
2826   *
2827   * @param transactionId the transaction id to look for
2828   * @return the corresponding VirtualDatabaseWorkerThread or null if none is
2829   * found
2830   */

2831  public VirtualDatabaseWorkerThread getVirtualDatabaseWorkerThreadForTransaction(
2832      long transactionId)
2833  {
2834    synchronized (activeThreads)
2835    {
2836      for (Iterator JavaDoc iter = activeThreads.iterator(); iter.hasNext();)
2837      {
2838        VirtualDatabaseWorkerThread vdbwt = (VirtualDatabaseWorkerThread) iter
2839            .next();
2840        if (vdbwt.getCurrentTransactionId() == transactionId)
2841          return vdbwt;
2842      }
2843    }
2844    return null;
2845  }
2846
2847  /**
2848   * Adds one to currentNbOfThreads. Warning! This method is not synchronized.
2849   */

2850  protected void incrementCurrentNbOfThread()
2851  {
2852    currentNbOfThreads++;
2853  }
2854
2855  /**
2856   * Method add an idle thread. Warning! This method must be called in a
2857   * synchronized block on activeThreads.
2858   */

2859  protected void incrementIdleThreadCount()
2860  {
2861    idleThreads++;
2862  }
2863
2864  /**
2865   * Returns the poolConnectionThreads.
2866   *
2867   * @return boolean true if threads are pooled
2868   */

2869  public boolean isPoolConnectionThreads()
2870  {
2871    return poolConnectionThreads;
2872  }
2873
2874  /**
2875   * Sets the maxThreadIdleTime.
2876   *
2877   * @param maxThreadIdleTime The maxThreadIdleTime to set
2878   */

2879  public void setMaxThreadIdleTime(long maxThreadIdleTime)
2880  {
2881    this.maxThreadIdleTime = maxThreadIdleTime;
2882  }
2883
2884  /**
2885   * Sets the minNbOfThreads.
2886   *
2887   * @param minNbOfThreads The minNbOfThreads to set
2888   */

2889  public void setMinNbOfThreads(int minNbOfThreads)
2890  {
2891    this.minNbOfThreads = minNbOfThreads;
2892  }
2893
2894  /**
2895   * Sets the poolConnectionThreads.
2896   *
2897   * @param poolConnectionThreads The poolConnectionThreads to set
2898   */

2899  public void setPoolConnectionThreads(boolean poolConnectionThreads)
2900  {
2901    this.poolConnectionThreads = poolConnectionThreads;
2902  }
2903
2904  //
2905
// Getter/Setter and tools (equals, ...)
2906
//
2907

2908  /**
2909   * Returns the activeThreads list.
2910   *
2911   * @return ArrayList of <code>VirtualDatabaseWorkerThread</code>
2912   */

2913  public ArrayList JavaDoc getActiveThreads()
2914  {
2915    return activeThreads;
2916  }
2917
2918  /**
2919   * Returns the authentication manager of this virtual database.
2920   *
2921   * @return an <code>AuthenticationManager</code> instance
2922   */

2923  public AuthenticationManager getAuthenticationManager()
2924  {
2925    return authenticationManager;
2926  }
2927
2928  /**
2929   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getBackendInformation(String)
2930   */

2931  public String JavaDoc getBackendInformation(String JavaDoc backendName)
2932      throws VirtualDatabaseException
2933  {
2934    try
2935    {
2936      acquireReadLockBackendLists();
2937    }
2938    catch (InterruptedException JavaDoc e)
2939    {
2940      String JavaDoc msg = "Unable to acquire read lock on backend list in getBackendInformation ("
2941          + e + ")";
2942      logger.error(msg);
2943      throw new VirtualDatabaseException(msg);
2944    }
2945
2946    // Find the backend
2947
int size = backends.size();
2948    DatabaseBackend b = null;
2949    for (int i = 0; i < size; i++)
2950    {
2951      b = (DatabaseBackend) backends.get(i);
2952      if (b.getName().equals(backendName))
2953        break;
2954      else
2955        b = null;
2956    }
2957
2958    if (b == null)
2959    {
2960      releaseReadLockBackendLists();
2961      String JavaDoc msg = "Backend " + backendName + " does not exists.";
2962      logger.warn(msg);
2963      throw new VirtualDatabaseException(msg);
2964    }
2965
2966    releaseReadLockBackendLists();
2967    return b.getXml();
2968  }
2969
2970  /**
2971   * Return the list of all backends
2972   *
2973   * @return <code>ArrayList</code> of <code>DatabaseBackend</code> Objects
2974   */

2975  public ArrayList JavaDoc getBackends()
2976  {
2977    return backends;
2978  }
2979
2980  /**
2981   * Retrieves the warnings for the given connection on the first available
2982   * backend
2983   *
2984   * @param connId the persistent connection id to retrieve warnings from
2985   * @exception SQLException if a database access error occurs or this method is
2986   * called on a closed connection
2987   * @return connection SQL warnings or null
2988   */

2989  public SQLWarning JavaDoc getConnectionWarnings(long connId) throws SQLException JavaDoc
2990  {
2991    DatabaseBackend b = getFirstAvailableBackend();
2992    return b.getPersistentConnectionWarnings(connId);
2993  }
2994
2995  /**
2996   * Forwards call to clearWarnings() to the given persistent connections on
2997   * each backend.
2998   *
2999   * @param connId the persistent connection id to clear warnings from
3000   * @exception SQLException if a database access error occurs
3001   */

3002  public void clearConnectionWarnings(long connId) throws SQLException JavaDoc
3003  {
3004    // Loop round the list in a failfast manner
3005
if (backends == null)
3006      return;
3007    try
3008    {
3009      for (Iterator JavaDoc iter = backends.iterator(); iter.hasNext();)
3010      {
3011        DatabaseBackend b = (DatabaseBackend) iter.next();
3012        if (b.isWriteEnabled() && b.isJDBCConnected())
3013          b.clearPersistentConnectionWarnings(connId);
3014      }
3015    }
3016    catch (ConcurrentModificationException JavaDoc e)
3017    {
3018      // loop until available
3019
clearConnectionWarnings(connId);
3020    }
3021  }
3022
3023  /**
3024   * Gets the virtual database name to be used by the client (Sequoia driver)
3025   * This method should be used for local references only (it is faster). For
3026   * remote RMI calls, use {@link #getVirtualDatabaseName()}.
3027   *
3028   * @return the virtual database name
3029   * @see VirtualDatabase#getVirtualDatabaseName()
3030   */

3031  public String JavaDoc getDatabaseName()
3032  {
3033    return name;
3034  }
3035
3036  /**
3037   * @see org.continuent.sequoia.driver.DatabaseMetaData#getDatabaseProductName()
3038   */

3039  public String JavaDoc getDatabaseProductName()
3040  {
3041    return databaseProductNames;
3042  }
3043
3044  /**
3045   * @see org.continuent.sequoia.driver.DatabaseMetaData
3046   * @return associated metada for this database
3047   */

3048  public VirtualDatabaseDynamicMetaData getDynamicMetaData()
3049  {
3050    if (metadata == null)
3051    {
3052      metadata = new VirtualDatabaseDynamicMetaData(this);
3053    }
3054    return metadata;
3055  }
3056
3057  /**
3058   * Returns the dynamicDatabaseSchema value.
3059   *
3060   * @return Returns the dynamicDatabaseSchema.
3061   */

3062  public final DynamicDatabaseSchema getDynamicDatabaseSchema()
3063  {
3064    return dynamicDatabaseSchema;
3065  }
3066
3067  /**
3068   * Sets the dynamicDatabaseSchema value.
3069   *
3070   * @param dynamicDatabaseSchema The dynamicDatabaseSchema to set.
3071   */

3072  public final void setDynamicDatabaseSchema(
3073      DynamicDatabaseSchema dynamicDatabaseSchema)
3074  {
3075    this.dynamicDatabaseSchema = dynamicDatabaseSchema;
3076  }
3077
3078  /**
3079   * Get the current database schema from merging the schemas of all active
3080   * backends.
3081   *
3082   * @return the current database schema dynamically gathered
3083   * @throws SQLException if an error occurs
3084   */

3085  public DatabaseSchema getDatabaseSchemaFromActiveBackends()
3086      throws SQLException JavaDoc
3087  {
3088    boolean isRaidb1 = requestManager.getLoadBalancer().getRAIDbLevel() == RAIDbLevels.RAIDb1;
3089
3090    try
3091    {
3092      acquireReadLockBackendLists();
3093    }
3094    catch (InterruptedException JavaDoc e)
3095    {
3096      String JavaDoc msg = "Unable to acquire read lock on backend list in getDatabaseSchemaFromActiveBackends ("
3097          + e + ")";
3098      logger.error(msg);
3099      throw new SQLException JavaDoc(msg);
3100    }
3101
3102    DatabaseSchema schema = null;
3103    try
3104    {
3105      // Build the new schema from all active backend's schemas
3106
int size = backends.size();
3107      DatabaseBackend b = null;
3108      for (int i = 0; i < size; i++)
3109      {
3110        b = (DatabaseBackend) backends.get(i);
3111        if (b.isReadEnabled())
3112        {
3113          DatabaseSchema backendSchema = b.getDatabaseSchema();
3114          if (backendSchema != null)
3115          { // The backend schema might be null during the recovery phase
3116
if (schema == null)
3117              schema = new DatabaseSchema(backendSchema);
3118            else
3119              schema.mergeSchema(backendSchema);
3120
3121            // In RAIDb-1 all backends are the same so there is no need to merge
3122
// and we just take the schema of the first backend
3123
if (isRaidb1)
3124              break;
3125          }
3126        }
3127      }
3128
3129      // Note that if the RecoveryLog points to the same database it will appear
3130
// in the database schema but this is a normal behavior.
3131
}
3132    finally
3133    {
3134      releaseReadLockBackendLists();
3135    }
3136
3137    return schema;
3138  }
3139
3140  /**
3141   * Get the current database schema from merging the schemas of all active
3142   * backends. This is needed when a backend is disabled.
3143   *
3144   * @return the current database schema dynamically gathered
3145   * @throws SQLException if an error occurs
3146   */

3147  public DatabaseSchema getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames()
3148      throws SQLException JavaDoc
3149  {
3150    try
3151    {
3152      acquireReadLockBackendLists();
3153    }
3154    catch (InterruptedException JavaDoc e)
3155    {
3156      String JavaDoc msg = "Unable to acquire read lock on backend list in getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames ("
3157          + e + ")";
3158      logger.error(msg);
3159      throw new SQLException JavaDoc(msg);
3160    }
3161
3162    DatabaseSchema schema = null;
3163    String JavaDoc dbProductNames = "Sequoia";
3164    try
3165    {
3166      // Build the new schema from all active backend's schemas
3167
int size = backends.size();
3168      DatabaseBackend b = null;
3169      for (int i = 0; i < size; i++)
3170      {
3171        b = (DatabaseBackend) backends.get(i);
3172        if (b.isReadEnabled())
3173        {
3174          DatabaseSchema backendSchema = b.getDatabaseSchema();
3175          if (backendSchema != null)
3176          { // The backend schema might be null during the recovery phase
3177
if (schema == null)
3178              schema = new DatabaseSchema(backendSchema);
3179            else
3180              schema.mergeSchema(backendSchema);
3181          }
3182        }
3183
3184        // Update the list of database product names
3185
if (dbProductNames.indexOf(b.getDatabaseProductName()) == -1)
3186          dbProductNames += "," + b.getDatabaseProductName();
3187      }
3188    }
3189    finally
3190    {
3191      releaseReadLockBackendLists();
3192    }
3193    databaseProductNames = dbProductNames;
3194
3195    // Note that if the RecoveryLog points to the same database it will appear
3196
// in the database schema but this is a normal behavior.
3197

3198    return schema;
3199  }
3200
3201  /**
3202   * Retrieves the first available backend from this virtual database
3203   *
3204   * @return the first available backend or null if no backend enabled is found
3205   */

3206  DatabaseBackend getFirstAvailableBackend()
3207  {
3208    // Parse the list in a failfast manner
3209
if (backends == null)
3210      return null;
3211    try
3212    {
3213      for (Iterator JavaDoc iter = backends.iterator(); iter.hasNext();)
3214      {
3215        DatabaseBackend b = (DatabaseBackend) iter.next();
3216        if (b.isReadEnabled() && b.isJDBCConnected())
3217          return b;
3218      }
3219    }
3220    catch (ConcurrentModificationException JavaDoc e)
3221    {
3222      return getFirstAvailableBackend();
3223    }
3224
3225    return null;
3226  }
3227
3228  /**
3229   * Returns the logger value.
3230   *
3231   * @return Returns the logger.
3232   */

3233  public final Trace getLogger()
3234  {
3235    return logger;
3236  }
3237
3238  /**
3239   * Returns the maxNbOfConnections.
3240   *
3241   * @return int
3242   */

3243  public int getMaxNbOfConnections()
3244  {
3245    return maxNbOfConnections;
3246  }
3247
3248  /**
3249   * Returns the number of savepoints that are defined for a given transaction
3250   * by asking to the request manager.
3251   *
3252   * @param tId the transaction id
3253   * @return the number of savepoints that are defined in the transaction whose
3254   * id is tId
3255   */

3256  public int getNumberOfSavepointsInTransaction(long tId)
3257  {
3258    return requestManager.getNumberOfSavepointsInTransaction(tId);
3259  }
3260
3261  /**
3262   * Returns the pendingConnections.
3263   *
3264   * @return ArrayList
3265   */

3266  public ArrayList JavaDoc getPendingConnections()
3267  {
3268    return pendingConnections;
3269  }
3270
3271  /**
3272   * Gets the request manager associated to this database.
3273   *
3274   * @return a <code>RequestManager</code> instance
3275   */

3276  public RequestManager getRequestManager()
3277  {
3278    return requestManager;
3279  }
3280
3281  /**
3282   * Returns the semanticManager value.
3283   *
3284   * @return Returns the semanticManager.
3285   */

3286  public final SemanticManager getSemanticManager()
3287  {
3288    return semanticManager;
3289  }
3290
3291  /**
3292   * Get the whole static metadata for this virtual database. A new empty
3293   * metadata object is created if there was none yet. It will be filled later
3294   * by gatherStaticMetadata() when the backend is enabled.
3295   *
3296   * @return Virtual database static metadata
3297   */

3298  public VirtualDatabaseStaticMetaData getStaticMetaData()
3299  {
3300    return doGetStaticMetaData();
3301  }
3302
3303  /**
3304   * @see #getStaticMetaData()
3305   */

3306  public VirtualDatabaseStaticMetaData doGetStaticMetaData()
3307  {
3308    if (staticMetadata == null)
3309    {
3310      staticMetadata = new VirtualDatabaseStaticMetaData(this);
3311    }
3312    return staticMetadata;
3313  }
3314
3315  /**
3316   * Gets the virtual database name to be used by the client (Sequoia driver)
3317   *
3318   * @return the virtual database name
3319   */

3320  public String JavaDoc getVirtualDatabaseName()
3321  {
3322    return name;
3323  }
3324
3325  /**
3326   * Returns the current SQL monitor
3327   *
3328   * @return a <code>SQLMonitoring</code> instance or null if no monitor is
3329   * defined
3330   */

3331  public SQLMonitoring getSQLMonitor()
3332  {
3333    return sqlMonitor;
3334  }
3335
3336  /**
3337   * Return the sql short form length to use when reporting an error.
3338   *
3339   * @return sql short form length
3340   * @see org.continuent.sequoia.controller.requests.AbstractRequest#getSqlShortForm(int)
3341   */

3342  public int getSqlShortFormLength()
3343  {
3344    return sqlShortFormLength;
3345  }
3346
3347  /**
3348   * Returns the totalOrderQueue value.
3349   *
3350   * @return Returns the totalOrderQueue.
3351   */

3352  public LinkedList JavaDoc getTotalOrderQueue()
3353  {
3354    return totalOrderQueue;
3355  }
3356
3357  /**
3358   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#hasRecoveryLog()
3359   */

3360  public boolean hasRecoveryLog()
3361  {
3362    RecoveryLog log = requestManager.getRecoveryLog();
3363    if (log == null)
3364      return false;
3365    else
3366      return true;
3367  }
3368
3369  /**
3370   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#hasResultCache()
3371   */

3372  public boolean hasResultCache()
3373  {
3374    AbstractResultCache cache = requestManager.getResultCache();
3375    if (cache == null)
3376      return false;
3377    else
3378      return true;
3379  }
3380
3381  /**
3382   * Sets the authentication manager for this virtual database.
3383   *
3384   * @param authenticationManager the <code>AuthenticationManager</code> to
3385   * set
3386   */

3387  public void setAuthenticationManager(
3388      AuthenticationManager authenticationManager)
3389  {
3390    this.authenticationManager = authenticationManager;
3391  }
3392
3393  /**
3394   * Sets the maxNbOfConnections.
3395   *
3396   * @param maxNbOfConnections The maxNbOfConnections to set
3397   */

3398  public void setMaxNbOfConnections(int maxNbOfConnections)
3399  {
3400    this.maxNbOfConnections = maxNbOfConnections;
3401  }
3402
3403  /**
3404   * Sets the maxNbOfThreads.
3405   *
3406   * @param maxNbOfThreads The maxNbOfThreads to set
3407   */

3408  public void setMaxNbOfThreads(int maxNbOfThreads)
3409  {
3410    this.maxNbOfThreads = maxNbOfThreads;
3411  }
3412
3413  /**
3414   * Sets a new request manager for this database.
3415   *
3416   * @param requestManager the new request manager.
3417   */

3418  public void setRequestManager(RequestManager requestManager)
3419  {
3420    this.requestManager = requestManager;
3421  }
3422
3423  /**
3424   * Sets a new SQL Monitor
3425   *
3426   * @param sqlMonitor the new SQL monitor
3427   */

3428  public void setSQLMonitor(SQLMonitoring sqlMonitor)
3429  {
3430    this.sqlMonitor = sqlMonitor;
3431  }
3432
3433  /**
3434   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#setMonitoringToActive(boolean)
3435   */

3436  public void setMonitoringToActive(boolean active)
3437      throws VirtualDatabaseException
3438  {
3439    if (sqlMonitor == null)
3440      throw new VirtualDatabaseException(Translate
3441          .get("virtualdatabase.monitoring.not.defined"));
3442    else
3443      sqlMonitor.setActive(active);
3444  }
3445
3446  /**
3447   * Returns the useStaticResultSetMetaData value.
3448   *
3449   * @return Returns the useStaticResultSetMetaData.
3450   */

3451  public final boolean useStaticResultSetMetaData()
3452  {
3453    return useStaticResultSetMetaData;
3454  }
3455
3456  /**
3457   * Two virtual databases are equal if they have the same name and group.
3458   *
3459   * @param other the object to compare with
3460   * @return <code>true</code> if the two virtual databases are equals
3461   */

3462  public boolean equals(Object JavaDoc other)
3463  {
3464    if ((other == null) || (!(other instanceof VirtualDatabase)))
3465      return false;
3466    else
3467    {
3468      VirtualDatabase db = (VirtualDatabase) other;
3469      return name.equals(db.getDatabaseName());
3470    }
3471  }
3472
3473  // /////////////////////////////////////////
3474
// JMX
3475
// ////////////////////////////////////////
3476

3477  /**
3478   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#getBackendStatistics(java.lang.String)
3479   */

3480  public BackendStatistics getBackendStatistics(String JavaDoc backendName)
3481      throws VirtualDatabaseException
3482  {
3483    try
3484    {
3485      acquireReadLockBackendLists();
3486    }
3487    catch (InterruptedException JavaDoc e)
3488    {
3489      String JavaDoc msg = Translate.get("virtualdatabase.fail.read.lock", e);
3490      throw new VirtualDatabaseException(msg);
3491    }
3492    BackendStatistics stat = null;
3493    ArrayList JavaDoc backendList = this.getBackends();
3494    for (Iterator JavaDoc iter = backendList.iterator(); iter.hasNext();)
3495    {
3496      DatabaseBackend backend = (DatabaseBackend) iter.next();
3497      if (backend.getName().equals(backendName))
3498      {
3499        stat = backend.getBackendStats();
3500      }
3501    }
3502    releaseReadLockBackendLists();
3503    return stat;
3504  }
3505
3506  /**
3507   * Add an admin operation to the list of current admin operations
3508   *
3509   * @param operation the operation to add
3510   */

3511  protected void addAdminOperation(AbstractAdminOperation operation)
3512  {
3513    synchronized (currentAdminOperations)
3514    {
3515      currentAdminOperations.add(operation);
3516    }
3517  }
3518
3519  /**
3520   * Remove a currently executing admin operation. Returns true if the command
3521   * was successfully removed from the list.
3522   *
3523   * @param operation the admin operation to remove.
3524   * @return true if operation was found and removed from the list
3525   */

3526  protected boolean removeAdminOperation(AbstractAdminOperation operation)
3527  {
3528    synchronized (currentAdminOperations)
3529    {
3530      currentAdminOperations.notify();
3531      return currentAdminOperations.remove(operation);
3532    }
3533  }
3534
3535  /**
3536   * Wait for all current admin operations to complete.
3537   */

3538  private void waitForAdminOperationsToComplete()
3539  {
3540    synchronized (currentAdminOperations)
3541    {
3542      while (!currentAdminOperations.isEmpty())
3543      {
3544        for (Iterator JavaDoc iter = currentAdminOperations.iterator(); iter.hasNext();)
3545        {
3546          AbstractAdminOperation op = (AbstractAdminOperation) iter.next();
3547          logger.info("Waiting for command '" + op + "' to complete");
3548        }
3549        try
3550        {
3551          currentAdminOperations.wait();
3552        }
3553        catch (InterruptedException JavaDoc ignore)
3554        {
3555        }
3556      }
3557    }
3558  }
3559
3560  //
3561
// Shutdown
3562
//
3563

3564  /**
3565   * Return true if this database is shutting down.
3566   *
3567   * @return true if this database is shutting down.
3568   */

3569  public boolean isShuttingDown()
3570  {
3571    return shuttingDown;
3572  }
3573
3574  /**
3575   * Set the VDB shutting down state
3576   *
3577   * @param shuttingDown TRUE when vdb is shutting down
3578   */

3579  public void setShuttingDown(boolean shuttingDown)
3580  {
3581    this.shuttingDown = shuttingDown;
3582  }
3583
3584  public boolean isRejectingNewTransaction()
3585  {
3586    return refusingNewTransaction;
3587  }
3588
3589  public void setRejectingNewTransaction(boolean canAcceptNewTransaction)
3590  {
3591    this.refusingNewTransaction = canAcceptNewTransaction;
3592  }
3593
3594  /**
3595   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#shutdown(int)
3596   */

3597  public void shutdown(int level)
3598  {
3599    VirtualDatabaseShutdownThread vdst = null;
3600    String JavaDoc msg = Translate.get("virtualdatabase.shutting.down", this
3601        .getVirtualDatabaseName());
3602    synchronized (this)
3603    {
3604      if (shuttingDown && !(level == Constants.SHUTDOWN_FORCE))
3605        return;
3606      switch (level)
3607      {
3608        case Constants.SHUTDOWN_WAIT :
3609          vdst = new VirtualDatabaseWaitShutdownThread(this);
3610          msg = Translate.get("virtualdatabase.shutdown.type.wait", this
3611              .getVirtualDatabaseName());
3612          logger.info(msg);
3613          endUserLogger.info(msg);
3614          break;
3615        case Constants.SHUTDOWN_SAFE :
3616          shuttingDown = true;
3617          vdst = new VirtualDatabaseSafeShutdownThread(this);
3618          msg = Translate.get("virtualdatabase.shutdown.type.safe", this
3619              .getVirtualDatabaseName());
3620          logger.info(msg);
3621          endUserLogger.info(msg);
3622          break;
3623        case Constants.SHUTDOWN_FORCE :
3624          shuttingDown = true;
3625          vdst = new VirtualDatabaseForceShutdownThread(this);
3626          msg = Translate.get("virtualdatabase.shutdown.type.force", this
3627              .getVirtualDatabaseName());
3628          logger.warn(msg);
3629          endUserLogger.info(msg);
3630          break;
3631        default :
3632          msg = Translate.get("virtualdatabase.shutdown.unknown.level",
3633              new Object JavaDoc[]{new Integer JavaDoc(level), this.getVirtualDatabaseName()});
3634          logger.error(msg);
3635          endUserLogger.error(msg);
3636          throw new RuntimeException JavaDoc(msg);
3637      }
3638    }
3639
3640    if (level != Constants.SHUTDOWN_FORCE)
3641    {
3642      // Wait for all blocking admin operations to complete
3643
waitForAdminOperationsToComplete();
3644    }
3645
3646    Thread JavaDoc thread = new Thread JavaDoc(vdst.getShutdownGroup(), vdst,
3647        "VirtualDatabase Shutdown Thread");
3648    thread.start();
3649    try
3650    {
3651      logger.info("Waiting for virtual database " + name + " shutdown");
3652      thread.join();
3653      controller.removeVirtualDatabase(name);
3654      msg = Translate.get("notification.virtualdatabase.shutdown", name);
3655      logger.info(msg);
3656      endUserLogger.info(msg);
3657
3658      try
3659      {
3660        MBeanServerManager.unregister(JmxConstants
3661            .getVirtualDataBaseObjectName(name));
3662        if (hasRecoveryLog()) // SEQUOIA-867 fix
3663
MBeanServerManager.unregister(JmxConstants
3664              .getRecoveryLogObjectName(name));
3665        MBeanServerManager.unregister(JmxConstants
3666            .getAbstractSchedulerObjectName(name));
3667        MBeanServerManager.unregister(JmxConstants
3668            .getLoadBalancerObjectName(name));
3669        MBeanServerManager.unregister(JmxConstants
3670            .getRequestManagerObjectName(name));
3671        ArrayList JavaDoc backendNames = getAllBackendNames();
3672        for (int i = 0; i < backendNames.size(); i++)
3673        {
3674          String JavaDoc backendName = (String JavaDoc) backendNames.get(i);
3675          MBeanServerManager.unregister(JmxConstants
3676              .getDatabaseBackendObjectName(name, backendName));
3677        }
3678      }
3679      catch (Exception JavaDoc e)
3680      {
3681        logger.error(Translate.get("virtualdatabase.fail.unregister.mbean",
3682            name), e);
3683      }
3684    }
3685    catch (InterruptedException JavaDoc e)
3686    {
3687      e.printStackTrace();
3688    }
3689  }
3690
3691  /**
3692   * Write the checkpoints for all backends on the recovery log
3693   */

3694  public void storeBackendsInfo()
3695  {
3696    requestManager.storeBackendsInfo(this.name, getBackends());
3697  }
3698
3699  /**
3700   * Get all users connected to that database
3701   *
3702   * @return an <code>ArrayList</code> of strings containing the clients
3703   * username
3704   */

3705  public ArrayList JavaDoc viewAllClientNames()
3706  {
3707    ArrayList JavaDoc list = this.getActiveThreads();
3708    int size = list.size();
3709    ArrayList JavaDoc clients = new ArrayList JavaDoc(size);
3710    for (int i = 0; i < list.size(); i++)
3711      clients.add(((VirtualDatabaseWorkerThread) list.get(i)).getUser());
3712    return clients;
3713  }
3714
3715  /**
3716   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#viewControllerList()
3717   */

3718  public String JavaDoc[] viewControllerList()
3719  {
3720    return new String JavaDoc[]{viewOwningController()};
3721  }
3722
3723  /**
3724   * @see org.continuent.sequoia.common.jmx.mbeans.VirtualDatabaseMBean#viewOwningController()
3725   */

3726  public String JavaDoc viewOwningController()
3727  {
3728    return controller.getJmxName();
3729  }
3730
3731  /**
3732   * Retrieves this <code>VirtualDatabase</code> object in xml format
3733   *
3734   * @return xml formatted string that conforms to sequoia.dtd
3735   */

3736  public String JavaDoc getXml()
3737  {
3738    StringBuffer JavaDoc info = new StringBuffer JavaDoc();
3739    info.append("<" + DatabasesXmlTags.ELT_VirtualDatabase + " "
3740        + DatabasesXmlTags.ATT_name + "=\"" + this.getVirtualDatabaseName()
3741        + "\" " + DatabasesXmlTags.ATT_maxNbOfConnections + "=\""
3742        + this.getMaxNbOfConnections() + "\" "
3743        + DatabasesXmlTags.ATT_poolThreads + "=\""
3744        + this.isPoolConnectionThreads() + "\" "
3745        + DatabasesXmlTags.ATT_minNbOfThreads + "=\""
3746        + this.getMinNbOfThreads() + "\" "
3747        + DatabasesXmlTags.ATT_maxNbOfThreads + "=\""
3748        + this.getMaxNbOfThreads() + "\" "
3749        + DatabasesXmlTags.ATT_maxThreadIdleTime + "=\""
3750        + this.getMaxThreadIdleTime() / 1000 + "\" "
3751        + DatabasesXmlTags.ATT_sqlDumpLength + "=\"" + this.sqlShortFormLength
3752        + "\">");
3753
3754    info.append(getDistributionXml());
3755
3756    if (this.getSQLMonitor() != null)
3757      info.append(sqlMonitor.getXml());
3758
3759    info.append(requestManager.getBackupManager().getXml());
3760
3761    if (this.getAuthenticationManager() != null)
3762      info.append(authenticationManager.getXml());
3763
3764    try
3765    {
3766      acquireReadLockBackendLists();
3767      int size = backends.size();
3768      for (int i = 0; i < size; i++)
3769        info.append(((DatabaseBackend) backends.get(i)).getXml());
3770      releaseReadLockBackendLists();
3771    }
3772    catch (InterruptedException JavaDoc e)
3773    {
3774      logger.error(Translate.get("virtualdatabase.fail.read.lock", e));
3775    }
3776
3777    if (this.getDynamicDatabaseSchema() != null)
3778      info.append(getDynamicDatabaseSchema().getXml());
3779
3780    if (this.getSemanticManager() != null)
3781      info.append(getSemanticManager().getXml());
3782
3783    if (requestManager != null)
3784      info.append(requestManager.getXml());
3785    info.append("</" + DatabasesXmlTags.ELT_VirtualDatabase + ">");
3786    return info.toString();
3787  }
3788
3789  /**
3790   * Get the XML dump of the Distribution element if any.
3791   *
3792   * @return ""
3793   */

3794  protected String JavaDoc getDistributionXml()
3795  {
3796    return "";
3797  }
3798}
Popular Tags