Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

VirtualDatabase.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2005 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet.
00022  * Contributor(s): Mathieu Peltier, Nicolas Modrzyk, Vadim Kassin.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.virtualdatabase;
00026 
00027 import java.io.File;
00028 import java.io.FilenameFilter;
00029 import java.io.Serializable;
00030 import java.sql.SQLException;
00031 import java.util.ArrayList;
00032 import java.util.Hashtable;
00033 import java.util.Map;
00034 
00035 import javax.management.NotCompliantMBeanException;
00036 import javax.management.ObjectName;
00037 
00038 import org.objectweb.cjdbc.common.exceptions.BackupException;
00039 import org.objectweb.cjdbc.common.exceptions.ExceptionTypes;
00040 import org.objectweb.cjdbc.common.exceptions.OctopusException;
00041 import org.objectweb.cjdbc.common.exceptions.VirtualDatabaseException;
00042 import org.objectweb.cjdbc.common.i18n.Translate;
00043 import org.objectweb.cjdbc.common.jmx.JmxConstants;
00044 import org.objectweb.cjdbc.common.jmx.JmxException;
00045 import org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean;
00046 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList;
00047 import org.objectweb.cjdbc.common.log.Trace;
00048 import org.objectweb.cjdbc.common.shared.BackendState;
00049 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
00050 import org.objectweb.cjdbc.common.sql.ParsingGranularities;
00051 import org.objectweb.cjdbc.common.sql.SelectRequest;
00052 import org.objectweb.cjdbc.common.sql.StoredProcedure;
00053 import org.objectweb.cjdbc.common.sql.filters.AbstractBlobFilter;
00054 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema;
00055 import org.objectweb.cjdbc.common.sql.schema.DatabaseTable;
00056 import org.objectweb.cjdbc.common.users.AdminUser;
00057 import org.objectweb.cjdbc.common.users.VirtualDatabaseUser;
00058 import org.objectweb.cjdbc.common.util.Constants;
00059 import org.objectweb.cjdbc.common.util.ReadPrioritaryFIFOWriteLock;
00060 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00061 import org.objectweb.cjdbc.common.xml.XmlComponent;
00062 import org.objectweb.cjdbc.common.xml.XmlTools;
00063 import org.objectweb.cjdbc.controller.authentication.AuthenticationManager;
00064 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
00065 import org.objectweb.cjdbc.controller.cache.result.AbstractResultCache;
00066 import org.objectweb.cjdbc.controller.core.Controller;
00067 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseForceShutdownThread;
00068 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseSafeShutdownThread;
00069 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseShutdownThread;
00070 import org.objectweb.cjdbc.controller.core.shutdown.VirtualDatabaseWaitShutdownThread;
00071 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean;
00072 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager;
00073 import org.objectweb.cjdbc.controller.jmx.RmiConnector;
00074 import org.objectweb.cjdbc.controller.monitoring.SQLMonitoring;
00075 import org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog;
00076 import org.objectweb.cjdbc.controller.recoverylog.BackendRecoveryInfo;
00077 import org.objectweb.cjdbc.controller.recoverylog.JDBCRecoveryLog;
00078 import org.objectweb.cjdbc.controller.requestmanager.RequestManager;
00079 
00080 /**
00081  * A <code>VirtualDatabase</code> represents a database from client point of
00082  * view and hide the complexity of the cluster distribution to the client. The
00083  * client always uses the virtual database name and the C-JDBC Controller will
00084  * use the real connections when an SQL request comes in.
00085  * 
00086  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00087  * @author <a href="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
00088  * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
00089  * @author <a href="mailto:vadim@kase.kz">Vadim Kassin </a>
00090  * @version 1.0
00091  */
00092 public class VirtualDatabase extends AbstractStandardMBean
00093     implements
00094       Serializable,
00095       VirtualDatabaseMBean,
00096       XmlComponent
00097 {
00098   //
00099   // How the code is organized ?
00100   //
00101   // 1. Member variables
00102   // 2. Constructor(s)
00103   // 3. Request handling
00104   // 4. Transaction handling
00105   // 5. Database backend management
00106   // 6. Checkpoint management
00107   // 7. Getter/Setter (possibly in alphabetical order)
00108   // 8. Shutdown
00109   //
00110 
00111   /** Virtual database name */
00112   protected String                         name;
00113 
00114   /**
00115    * Authentification manager matching virtual database login/password to
00116    * backends login/password
00117    */
00118   protected AuthenticationManager          authenticationManager;
00119 
00120   /** <code>ArrayList</code> of <code>DatabaseBackend</code> objects */
00121   protected ArrayList                      backends;
00122 
00123   /** Read/Write lock for backend list */
00124   protected ReadPrioritaryFIFOWriteLock    rwLock;
00125 
00126   /** The request manager to use for this database */
00127   protected RequestManager                 requestManager;
00128 
00129   /** Virtual database logger */
00130   public Trace                             logger                = null;
00131   protected Trace                          requestLogger         = null;
00132 
00133   // List of current active Worker Threads
00134   private ArrayList                        activeThreads         = new ArrayList();
00135   // List of current idle Worker Threads
00136   private int                              idleThreads           = 0;
00137   // List of current pending connections (Socket objects)
00138   private ArrayList                        pendingConnections    = new ArrayList();
00139 
00140   /** Maximum number of concurrent accepted for this virtual database */
00141   protected int                            maxNbOfConnections;
00142 
00143   /** If false one worker thread is forked per connection else */
00144   protected boolean                        poolConnectionThreads;
00145 
00146   /** Maximum time a worker thread can remain idle before dying */
00147   protected long                           maxThreadIdleTime;
00148 
00149   /**
00150    * Minimum number of worker threads to keep in the pool if
00151    * poolConnectionThreads is true
00152    */
00153   protected int                            minNbOfThreads;
00154 
00155   /** Maximum number of worker threads to fork */
00156   protected int                            maxNbOfThreads;
00157 
00158   /** Current number of worker threads */
00159   protected int                            currentNbOfThreads;
00160 
00161   /** Virtual Database MetaData */
00162   protected VirtualDatabaseDynamicMetaData metadata;
00163   private VirtualDatabaseStaticMetaData    staticMetadata;
00164 
00165   private SQLMonitoring                    sqlMonitor            = null;
00166 
00167   /** Use for method getAndCheck */
00168   public static final int                  CHECK_BACKEND_ENABLE  = 1;
00169   /** Use for method getAndCheck */
00170   public static final int                  CHECK_BACKEND_DISABLE = 0;
00171   /** Use for method getAndCheck */
00172   public static final int                  NO_CHECK_BACKEND      = -1;
00173 
00174   /** Short form of SQL statements to include in traces and exceptions */
00175   private int                              sqlShortFormLength;
00176 
00177   /** The filter used to store blobs in the database */
00178   private AbstractBlobFilter               blobFilter;
00179 
00180   /** The controller we belong to */
00181   Controller                               controller;
00182 
00183   /** Comma separated list of database product names (one instance per name) */
00184   private String                           databaseProductNames  = "C-JDBC";
00185 
00186   /** Marker to see if the database is shutting down */
00187   private boolean                          shuttingDown          = false;
00188 
00189   /* Constructors */
00190 
00191   /**
00192    * Creates a new <code>VirtualDatabase</code> instance.
00193    * 
00194    * @param name the virtual database name.
00195    * @param maxConnections maximum number of concurrent connections.
00196    * @param pool should we use a pool of threads for handling connections?
00197    * @param minThreads minimum number of threads in the pool
00198    * @param maxThreads maximum number of threads in the pool
00199    * @param maxThreadIdleTime maximum time a thread can remain idle before being
00200    *          removed from the pool.
00201    * @param sqlShortFormLength maximum number of characters of an SQL statement
00202    *          to diplay in traces or exceptions
00203    * @param blobFilter encoding method for blobs
00204    * @param controller the controller we belong to
00205    * @exception NotCompliantMBeanException in case the bean does not comply with
00206    *              jmx
00207    * @exception JmxException could not register mbean
00208    */
00209   public VirtualDatabase(Controller controller, String name,
00210       int maxConnections, boolean pool, int minThreads, int maxThreads,
00211       long maxThreadIdleTime, int sqlShortFormLength,
00212       AbstractBlobFilter blobFilter) throws NotCompliantMBeanException,
00213       JmxException
00214   {
00215     super(VirtualDatabaseMBean.class);
00216     this.controller = controller;
00217     this.name = name;
00218     this.maxNbOfConnections = maxConnections;
00219     this.poolConnectionThreads = pool;
00220     this.minNbOfThreads = minThreads;
00221     this.maxNbOfThreads = maxThreads;
00222     this.maxThreadIdleTime = maxThreadIdleTime;
00223     this.sqlShortFormLength = sqlShortFormLength;
00224     this.blobFilter = blobFilter;
00225     backends = new ArrayList();
00226 
00227     ObjectName objectName = JmxConstants.getVirtualDbObjectName(name);
00228     MBeanServerManager.registerMBean(this, objectName);
00229 
00230     rwLock = new ReadPrioritaryFIFOWriteLock();
00231     logger = Trace.getLogger("org.objectweb.cjdbc.controller.virtualdatabase."
00232         + name);
00233     requestLogger = Trace
00234         .getLogger("org.objectweb.cjdbc.controller.virtualdatabase.request."
00235             + name);
00236   }
00237 
00238   /**
00239    * Acquires a read lock on the backend lists (both enabled and disabled
00240    * backends). This should be called prior traversing the backend
00241    * <code>ArrayList</code>.
00242    * 
00243    * @throws InterruptedException if an error occurs
00244    */
00245   public final void acquireReadLockBackendLists() throws InterruptedException
00246   {
00247     rwLock.acquireRead();
00248   }
00249 
00250   /**
00251    * Releases the read lock on the backend lists (both enabled and disabled
00252    * backends). This should be called after traversing the backend
00253    * <code>ArrayList</code>.
00254    */
00255   public final void releaseReadLockBackendLists()
00256   {
00257     rwLock.releaseRead();
00258   }
00259 
00260   /**
00261    * Is this virtual database distributed ?
00262    * 
00263    * @return false
00264    */
00265   public boolean isDistributed()
00266   {
00267     return false;
00268   }
00269 
00270   /* Request Handling */
00271 
00272   /**
00273    * Checks if a given virtual login/password is ok.
00274    * 
00275    * @param virtualLogin the virtual user login
00276    * @param virtualPassword the virtual user password
00277    * @return <code>true</code> if the login/password is known from the
00278    *         <code>AuthenticationManager</code>. Returns <code>false</code>
00279    *         if no <code>AuthenticationManager</code> is defined.
00280    */
00281   public boolean checkUserAuthentication(String virtualLogin,
00282       String virtualPassword)
00283   {
00284     if (authenticationManager == null)
00285     {
00286       logger.error("No authentification manager defined to check login '"
00287           + virtualLogin + "'");
00288       return false;
00289     }
00290     else
00291       return authenticationManager.isValidVirtualUser(new VirtualDatabaseUser(
00292           virtualLogin, virtualPassword));
00293   }
00294 
00295   /**
00296    * Checks if a given admin login/password is ok.
00297    * 
00298    * @param adminLogin admin user login
00299    * @param adminPassword admin user password
00300    * @return <code>true</code> if the login/password is known from the
00301    *         <code>AuthenticationManager</code>. Returns <code>false</code>
00302    *         if no <code>AuthenticationManager</code> is defined.
00303    */
00304   public boolean checkAdminAuthentication(String adminLogin,
00305       String adminPassword)
00306   {
00307     if (authenticationManager == null)
00308     {
00309       logger.error("No authentification manager defined to check admin login '"
00310           + adminLogin + "'");
00311       return false;
00312     }
00313     else
00314       return authenticationManager.isValidAdminUser(new AdminUser(adminLogin,
00315           adminPassword));
00316   }
00317 
00318   /**
00319    * Performs a read request and returns the reply.
00320    * 
00321    * @param request the request to execute
00322    * @return a <code>ControllerResultSet</code> value
00323    * @exception SQLException if the request fails
00324    */
00325   public ControllerResultSet execReadRequest(SelectRequest request)
00326       throws SQLException
00327   {
00328     if (request == null)
00329     {
00330       String msg = "Request failed (null read request received)";
00331       logger.warn(msg);
00332       throw new SQLException(msg);
00333     }
00334 
00335     try
00336     {
00337       if (requestLogger.isInfoEnabled())
00338         requestLogger.info("S " + request.getTransactionId() + " "
00339             + request.getSQL());
00340 
00341       long start = 0;
00342       if (sqlMonitor != null && sqlMonitor.isActive())
00343         start = System.currentTimeMillis();
00344 
00345       ControllerResultSet rs = requestManager.execReadRequest(request);
00346 
00347       if (sqlMonitor != null && sqlMonitor.isActive())
00348         sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start);
00349 
00350       return rs;
00351     }
00352     catch (SQLException e)
00353     {
00354       String msg = "Request '" + request.getId() + "' failed ("
00355           + e.getMessage() + ")";
00356       logger.warn(msg);
00357       if (sqlMonitor != null && sqlMonitor.isActive())
00358         sqlMonitor.logError(request);
00359       throw e;
00360     }
00361   }
00362 
00363   /**
00364    * Performs a write request and returns the number of rows affected.
00365    * 
00366    * @param request the request to execute
00367    * @return number of rows affected
00368    * @exception SQLException if the request fails
00369    */
00370   public int execWriteRequest(AbstractWriteRequest request) throws SQLException
00371   {
00372     if (request == null)
00373     {
00374       String msg = "Request failed (null write request received)";
00375       logger.warn(msg);
00376       throw new SQLException(msg);
00377     }
00378 
00379     try
00380     {
00381       if (requestLogger.isInfoEnabled())
00382         requestLogger.info("W " + request.getTransactionId() + " "
00383             + request.getSQL());
00384 
00385       long start = 0;
00386       if (sqlMonitor != null && sqlMonitor.isActive())
00387         start = System.currentTimeMillis();
00388 
00389       int result = requestManager.execWriteRequest(request);
00390 
00391       if (sqlMonitor != null && sqlMonitor.isActive())
00392         sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start);
00393 
00394       return result;
00395     }
00396     catch (SQLException e)
00397     {
00398       String msg = "Request '" + request.getId() + "' failed ("
00399           + e.getMessage() + ")";
00400       logger.warn(msg);
00401       if (sqlMonitor != null && sqlMonitor.isActive())
00402         sqlMonitor.logError(request);
00403       throw e;
00404     }
00405   }
00406 
00407   /**
00408    * Performs a write request and returns the auto generated keys.
00409    * 
00410    * @param request the request to execute
00411    * @return auto generated keys
00412    * @exception SQLException if the request fails
00413    */
00414   public ControllerResultSet execWriteRequestWithKeys(
00415       AbstractWriteRequest request) throws SQLException
00416   {
00417     if (request == null)
00418     {
00419       String msg = "Request failed (null write request received)";
00420       logger.warn(msg);
00421       throw new SQLException(msg);
00422     }
00423 
00424     try
00425     {
00426       if (requestLogger.isInfoEnabled())
00427         requestLogger.info("W " + request.getTransactionId() + " "
00428             + request.getSQL());
00429 
00430       long start = 0;
00431       if (sqlMonitor != null && sqlMonitor.isActive())
00432         start = System.currentTimeMillis();
00433 
00434       ControllerResultSet result = requestManager
00435           .execWriteRequestWithKeys(request);
00436 
00437       if (sqlMonitor != null && sqlMonitor.isActive())
00438         sqlMonitor.logRequestTime(request, System.currentTimeMillis() - start);
00439 
00440       return result;
00441     }
00442     catch (SQLException e)
00443     {
00444       String msg = "Request '" + request.getId() + "' failed ("
00445           + e.getMessage() + ")";
00446       logger.warn(msg);
00447       if (sqlMonitor != null && sqlMonitor.isActive())
00448         sqlMonitor.logError(request);
00449       throw e;
00450     }
00451   }
00452 
00453   /**
00454    * Call a stored procedure that returns a ResultSet.
00455    * 
00456    * @param proc the stored procedure call
00457    * @return a <code>java.sql.ResultSet</code> value
00458    * @exception SQLException if an error occurs
00459    */
00460   public ControllerResultSet execReadStoredProcedure(StoredProcedure proc)
00461       throws SQLException
00462   {
00463     if (proc == null)
00464     {
00465       String msg = "Request failed (null stored procedure received)";
00466       logger.warn(msg);
00467       throw new SQLException(msg);
00468     }
00469 
00470     try
00471     {
00472       if (requestLogger.isInfoEnabled())
00473         requestLogger
00474             .info("S " + proc.getTransactionId() + " " + proc.getSQL());
00475 
00476       long start = 0;
00477       if (sqlMonitor != null && sqlMonitor.isActive())
00478         start = System.currentTimeMillis();
00479 
00480       ControllerResultSet rs = requestManager.execReadStoredProcedure(proc);
00481 
00482       if (sqlMonitor != null && sqlMonitor.isActive())
00483         sqlMonitor.logRequestTime(proc, System.currentTimeMillis() - start);
00484 
00485       return rs;
00486     }
00487     catch (SQLException e)
00488     {
00489       String msg = Translate.get("loadbalancer.storedprocedure.failed",
00490           new String[]{String.valueOf(proc.getId()), e.getMessage()});
00491       logger.warn(msg);
00492       if (sqlMonitor != null && sqlMonitor.isActive())
00493         sqlMonitor.logError(proc);
00494       throw e;
00495     }
00496   }
00497 
00498   /**
00499    * Call a stored procedure that performs an update.
00500    * 
00501    * @param proc the stored procedure call
00502    * @return number of rows affected
00503    * @exception SQLException if an error occurs
00504    */
00505   protected int execWriteStoredProcedure(StoredProcedure proc)
00506       throws SQLException
00507   {
00508     if (proc == null)
00509     {
00510       String msg = "Request failed (null stored procedure received)";
00511       logger.warn(msg);
00512       throw new SQLException(msg);
00513     }
00514 
00515     try
00516     {
00517       if (requestLogger.isInfoEnabled())
00518         requestLogger
00519             .info("W " + proc.getTransactionId() + " " + proc.getSQL());
00520 
00521       long start = 0;
00522       if (sqlMonitor != null && sqlMonitor.isActive())
00523         start = System.currentTimeMillis();
00524 
00525       int result = requestManager.execWriteStoredProcedure(proc);
00526 
00527       if (sqlMonitor != null && sqlMonitor.isActive())
00528         sqlMonitor.logRequestTime(proc, System.currentTimeMillis() - start);
00529 
00530       return result;
00531     }
00532     catch (SQLException e)
00533     {
00534       String msg = Translate.get("loadbalancer.storedprocedure.failed",
00535           new String[]{String.valueOf(proc.getId()), e.getMessage()});
00536       logger.warn(msg);
00537       if (sqlMonitor != null && sqlMonitor.isActive())
00538         sqlMonitor.logError(proc);
00539       throw e;
00540     }
00541   }
00542 
00543   /* Transaction management */
00544 
00545   /**
00546    * Begins a new transaction and returns the corresponding transaction
00547    * identifier. This method is called from the driver when
00548    * {@link org.objectweb.cjdbc.driver.Connection#setAutoCommit(boolean)}is
00549    * called with <code>false</code> argument.
00550    * 
00551    * @param login the login used by the connection
00552    * @return an unique transaction identifier
00553    * @exception SQLException if an error occurs
00554    */
00555   public long begin(String login) throws SQLException
00556   {
00557     try
00558     {
00559       long tid = requestManager.begin(login);
00560       if (requestLogger.isInfoEnabled())
00561         requestLogger.info("B " + tid);
00562       return tid;
00563     }
00564     catch (SQLException e)
00565     {
00566       String msg = "Begin failed (" + e.getMessage() + ")";
00567       logger.warn(msg);
00568       throw e;
00569     }
00570   }
00571 
00572   /**
00573    * Abort a transaction that has been started but in which no query was
00574    * executed. As we use lazy transaction begin, there is no need to rollback
00575    * such transaction but just to cleanup the metadata associated with this not
00576    * effectively started transaction.
00577    * 
00578    * @param transactionId id of the transaction to abort
00579    * @throws SQLException if an error occurs
00580    */
00581   public void abort(long transactionId) throws SQLException
00582   {
00583     requestManager.abort(transactionId);
00584     // Emulate this as a rollback for the RequestPlayer
00585     if (requestLogger.isInfoEnabled())
00586       requestLogger.info("R " + transactionId);
00587   }
00588 
00589   /**
00590    * Commits a transaction given its id.
00591    * 
00592    * @param transactionId the transaction id
00593    * @exception SQLException if an error occurs
00594    */
00595   public void commit(long transactionId) throws SQLException
00596   {
00597     try
00598     {
00599       if (requestLogger.isInfoEnabled())
00600         requestLogger.info("C " + transactionId);
00601       requestManager.commit(transactionId);
00602     }
00603     catch (SQLException e)
00604     {
00605       String msg = "Commit of transaction '" + transactionId + "' failed ("
00606           + e.getMessage() + ")";
00607       logger.warn(msg);
00608       throw e;
00609     }
00610   }
00611 
00612   /**
00613    * Rollbacks a transaction given its id.
00614    * 
00615    * @param transactionId the transaction id
00616    * @exception SQLException if an error occurs
00617    */
00618   public void rollback(long transactionId) throws SQLException
00619   {
00620     try
00621     {
00622       if (requestLogger.isInfoEnabled())
00623         requestLogger.info("R " + transactionId);
00624       requestManager.rollback(transactionId);
00625     }
00626     catch (SQLException e)
00627     {
00628       String msg = "Rollback of transaction '" + transactionId + "' failed ("
00629           + e.getMessage() + ")";
00630       logger.warn(msg);
00631       throw e;
00632     }
00633   }
00634 
00635   //
00636   // Database backends management
00637   //
00638 
00639   /**
00640    * Add a backend to this virtual database.
00641    * 
00642    * @param db the database backend to add
00643    * @throws VirtualDatabaseException if an error occurs
00644    */
00645   public void addBackend(DatabaseBackend db) throws VirtualDatabaseException
00646   {
00647     this.addBackend(db, true);
00648   }
00649 
00650   /**
00651    * Add a backend to this virtual database.
00652    * 
00653    * @param db the database backend to add
00654    * @param checkForCompliance should load the driver ?
00655    * @throws VirtualDatabaseException if an error occurs
00656    */
00657   public void addBackend(DatabaseBackend db, boolean checkForCompliance)
00658       throws VirtualDatabaseException
00659   {
00660     if (db == null)
00661     {
00662       String msg = "Illegal null database backend in addBackend(DatabaseBackend) method";
00663       logger.error(msg);
00664       throw new VirtualDatabaseException(msg);
00665     }
00666 
00667     if (db.isReadEnabled())
00668     {
00669       String msg = "It is not allowed to add an enabled database.";
00670       logger.error(msg);
00671       throw new VirtualDatabaseException(msg);
00672     }
00673 
00674     try
00675     {
00676       rwLock.acquireWrite();
00677     }
00678     catch (InterruptedException e)
00679     {
00680       String msg = Translate.get(
00681           "loadbalancer.backendlist.acquire.writelock.failed", e);
00682       logger.error(msg);
00683       throw new VirtualDatabaseException(msg);
00684     }
00685 
00686     if (backends.indexOf(db) == -1)
00687     {
00688       // Check the authentication manager has all virtual logins defined
00689       ArrayList logins = authenticationManager.getVirtualLogins();
00690       VirtualDatabaseUser vdu;
00691       String login;
00692       for (int i = 0; i < logins.size(); i++)
00693       {
00694         vdu = (VirtualDatabaseUser) logins.get(i);
00695         login = vdu.getLogin();
00696         if (db.getConnectionManager(login) == null)
00697           throw new VirtualDatabaseException(Translate.get(
00698               "backend.missing.connection.manager", login));
00699       }
00700 
00701       // Initialize the driver and check the compliance
00702       try
00703       {
00704         if (logger.isDebugEnabled())
00705           logger.debug("Checking driver compliance");
00706         if (checkForCompliance)
00707           db.checkDriverCompliance(); // Also loads
00708         // the
00709         // driver
00710       }
00711       catch (Exception e)
00712       {
00713         rwLock.releaseWrite();
00714         String msg = "Error while adding database backend " + db.getName()
00715             + " (" + e + ")";
00716         logger.warn(msg);
00717         throw new VirtualDatabaseException(msg);
00718       }
00719       db.setSqlShortFormLength(getSQLShortFormLength());
00720       backends.add(db);
00721 
00722       // Add the backend to the list
00723       if (logger.isDebugEnabled())
00724         logger.debug("Backend " + db.getName() + " added successfully");
00725 
00726       rwLock.releaseWrite(); // Relase the lock
00727 
00728       // Jmx
00729       if (MBeanServerManager.isJmxEnabled())
00730       {
00731         // Send notification
00732         Hashtable data = new Hashtable();
00733         data.put(CjdbcNotificationList.DATA_DATABASE, this.name);
00734         data.put(CjdbcNotificationList.DATA_DRIVER, db.getDriverClassName());
00735         String checkpoint = db.getLastKnownCheckpoint();
00736         checkpoint = (checkpoint == null) ? "" : checkpoint;
00737         data.put(CjdbcNotificationList.DATA_CHECKPOINT, checkpoint);
00738         data.put(CjdbcNotificationList.DATA_NAME, db.getName());
00739         data.put(CjdbcNotificationList.DATA_URL, db.getURL());
00740         RmiConnector.broadcastNotification(this,
00741             CjdbcNotificationList.VIRTUALDATABASE_BACKEND_ADDED,
00742             CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
00743                 "notification.backend.added", db.getName()), data);
00744 
00745         // Add backend mbean to jmx server
00746         ObjectName objectName = JmxConstants.getDatabaseBackendObjectName(name,
00747             db.getName());
00748         try
00749         {
00750           MBeanServerManager.registerMBean(db, objectName);
00751         }
00752         catch (JmxException e1)
00753         {
00754           logger.error(Translate.get(
00755               "virtualdatabase.fail.register.backend.mbean", db.getName()), e1);
00756         }
00757       }
00758 
00759     }
00760     else
00761     {
00762       rwLock.releaseWrite();
00763       String msg = "Duplicate backend " + db.getURL();
00764       logger.warn(msg);
00765       throw new VirtualDatabaseException(msg);
00766     }
00767 
00768   }
00769 
00770   /**
00771    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#disableBackend(String)
00772    */
00773   public void disableBackend(String backendName)
00774       throws VirtualDatabaseException
00775   {
00776     try
00777     {
00778       DatabaseBackend db = getAndCheckBackend(backendName,
00779           CHECK_BACKEND_DISABLE);
00780       requestManager.disableBackend(db);
00781       requestManager.setDatabaseSchema(
00782           getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames(),
00783           false);
00784 
00785       // Send notification
00786       if (MBeanServerManager.isJmxEnabled())
00787       {
00788         Hashtable data = new Hashtable();
00789         data.put("driver", db.getDriverClassName());
00790         String checkpoint = db.getLastKnownCheckpoint();
00791         checkpoint = (checkpoint == null) ? "" : checkpoint;
00792         data.put("checkpoint", checkpoint);
00793         data.put("name", db.getName());
00794         data.put("url", db.getURL());
00795         RmiConnector.broadcastNotification(this,
00796             CjdbcNotificationList.VIRTUALDATABASE_BACKEND_DISABLED,
00797             CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
00798                 "notification.backend.disabled", db.getName()), data);
00799       }
00800     }
00801     catch (Exception e)
00802     {
00803       logger.error("An error occured while disabling backend " + backendName
00804           + " (" + e + ")");
00805       throw new VirtualDatabaseException(e.getMessage());
00806     }
00807   }
00808 
00809   /**
00810    * Prepare this virtual database for shutdown. This turns off all the backends
00811    * by cutting communication from this database. This does not prevents other
00812    * virtual database to use shared backends. This doesn't create checkpoints
00813    * either.
00814    * 
00815    * @throws VirtualDatabaseException if an error occurs
00816    */
00817   public void disableAllBackends() throws VirtualDatabaseException
00818   {
00819     try
00820     {
00821       int size = this.backends.size();
00822       DatabaseBackend dbe;
00823       for (int i = 0; i < size; i++)
00824       {
00825         dbe = (DatabaseBackend) backends.get(i);
00826         if (dbe.isReadEnabled())
00827           requestManager.disableBackend(getAndCheckBackend(dbe.getName(),
00828               CHECK_BACKEND_DISABLE));
00829       }
00830     }
00831     catch (Exception e)
00832     {
00833       throw new VirtualDatabaseException(e.getMessage());
00834     }
00835   }
00836 
00837   /**
00838    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#disableBackendWithCheckpoint(String,
00839    *      String)
00840    */
00841   public void disableBackendWithCheckpoint(String backendName,
00842       String checkpointName) throws VirtualDatabaseException
00843   {
00844     try
00845     {
00846       requestManager.disableBackendForCheckpoint(getAndCheckBackend(
00847           backendName, CHECK_BACKEND_DISABLE), checkpointName);
00848       requestManager.setDatabaseSchema(
00849           getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames(),
00850           false);
00851     }
00852     catch (Exception e)
00853     {
00854       logger.error("An error occured while disabling backend " + backendName
00855           + " (" + e + ")");
00856       throw new VirtualDatabaseException(e.getMessage());
00857     }
00858   }
00859 
00860   /**
00861    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#disableAllBackendsWithCheckpoint(java.lang.String)
00862    */
00863   public void disableAllBackendsWithCheckpoint(String checkpoint)
00864       throws VirtualDatabaseException
00865   {
00866     if (checkpoint == null)
00867     {
00868       disableAllBackends();
00869       return;
00870     }
00871 
00872     try
00873     {
00874       this.acquireReadLockBackendLists();
00875       requestManager.disableBackendsForCheckpoint(backends, checkpoint);
00876       this.releaseReadLockBackendLists();
00877     }
00878     catch (Exception e)
00879     {
00880       throw new VirtualDatabaseException(e.getMessage());
00881     }
00882   }
00883 
00884   /**
00885    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#enableBackend(String)
00886    */
00887   public void enableBackend(String backendName) throws VirtualDatabaseException
00888   {
00889     // Call the Request Manager
00890     try
00891     {
00892       DatabaseBackend backend = getAndCheckBackend(backendName,
00893           CHECK_BACKEND_ENABLE);
00894 
00895       requestManager.enableBackend(backend);
00896       requestManager.setDatabaseSchema(
00897           getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames(),
00898           false);
00899 
00900       // Update the list of database product names
00901       if (databaseProductNames.indexOf(backend.getDatabaseProductName()) == -1)
00902         databaseProductNames += "," + backend.getDatabaseProductName();
00903 
00904       // Update the static metadata
00905       getStaticMetaData().gatherStaticMetadata(backend);
00906 
00907       // Send notification
00908       if (MBeanServerManager.isJmxEnabled())
00909       {
00910         Hashtable data = new Hashtable();
00911         data.put("driver", backend.getDriverClassName());
00912         String checkpoint = backend.getLastKnownCheckpoint();
00913         checkpoint = (checkpoint == null) ? "" : checkpoint;
00914         data.put("checkpoint", checkpoint);
00915         data.put("name", backend.getName());
00916         data.put("url", backend.getURL());
00917         RmiConnector.broadcastNotification(this,
00918             CjdbcNotificationList.VIRTUALDATABASE_BACKEND_ENABLED,
00919             CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
00920                 "notification.backend.enabled", backend.getName()), data);
00921       }
00922     }
00923     catch (Exception e)
00924     {
00925       e.printStackTrace();
00926       throw new VirtualDatabaseException(e.getMessage());
00927     }
00928   }
00929 
00930   /**
00931    * Enable the given backend from the given checkpoint.
00932    * 
00933    * @param backendName backend to enable
00934    * @param checkpointName checkpoint to enable from
00935    * @throws VirtualDatabaseException if an error occurs
00936    */
00937   public void enableBackendFromCheckpoint(String backendName,
00938       String checkpointName) throws VirtualDatabaseException
00939   {
00940     // Call the Request Manager
00941     try
00942     {
00943       DatabaseBackend backend = getAndCheckBackend(backendName,
00944           CHECK_BACKEND_ENABLE);
00945       requestManager.enableBackendFromCheckpoint(backend, checkpointName);
00946       requestManager.setDatabaseSchema(
00947           getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames(),
00948           false);
00949 
00950       // Update the static metadata
00951       getStaticMetaData().gatherStaticMetadata(backend);
00952 
00953       // Update the list of database product names
00954       if (databaseProductNames.indexOf(backend.getDatabaseProductName()) == -1)
00955         databaseProductNames += "," + backend.getDatabaseProductName();
00956     }
00957     catch (Exception e)
00958     {
00959       throw new VirtualDatabaseException(
00960           "Failed to enable backend from checkpoint: " + e);
00961     }
00962   }
00963 
00964   /**
00965    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#enableBackendFromCheckpoint(java.lang.String)
00966    */
00967   public void enableBackendFromCheckpoint(String backendName)
00968       throws VirtualDatabaseException
00969   {
00970     DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
00971     String checkpoint = backend.getLastKnownCheckpoint();
00972     if (checkpoint == null)
00973       throw new VirtualDatabaseException("No last checkpoint for backend:"
00974           + backendName);
00975     else
00976     {
00977       if (logger.isDebugEnabled())
00978         logger.debug("Enabling backend:" + backendName
00979             + " from its last checkpoint " + backend.getLastKnownCheckpoint());
00980     }
00981     enableBackendFromCheckpoint(backendName, backend.getLastKnownCheckpoint());
00982   }
00983 
00984   /**
00985    * Enable all the backends without any check.
00986    * 
00987    * @throws VirtualDatabaseException if fails
00988    */
00989   public void enableAllBackends() throws VirtualDatabaseException
00990   {
00991     try
00992     {
00993       int size = this.backends.size();
00994       DatabaseBackend dbe;
00995       for (int i = 0; i < size; i++)
00996       {
00997         dbe = (DatabaseBackend) backends.get(i);
00998         if (!dbe.isReadEnabled())
00999           enableBackend(((DatabaseBackend) backends.get(i)).getName());
01000       }
01001     }
01002     catch (Exception e)
01003     {
01004       logger.error(e);
01005       throw new VirtualDatabaseException(e.getMessage());
01006     }
01007   }
01008 
01009   /**
01010    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#enableAllBackendsFromCheckpoint()
01011    */
01012   public void enableAllBackendsFromCheckpoint() throws VirtualDatabaseException
01013   {
01014     AbstractRecoveryLog log = requestManager.getRecoveryLog();
01015     if (log == null)
01016     {// If no recovery log is defined ignore fallback to a forced enable
01017       logger
01018           .warn("No recovery log has been configured, enabling backend without checkpoint.");
01019       enableAllBackends();
01020     }
01021     else
01022     {
01023       try
01024       {
01025         int size = this.backends.size();
01026         DatabaseBackend dbe;
01027         String backendName;
01028         BackendRecoveryInfo info;
01029         for (int i = 0; i < size; i++)
01030         {
01031           dbe = (DatabaseBackend) backends.get(i);
01032           backendName = dbe.getName();
01033           info = log.getBackendRecoveryInfo(name, backendName);
01034           switch (info.getBackendState())
01035           {
01036             case BackendState.DISABLED :
01037               String checkpoint = info.getCheckpoint();
01038               if (checkpoint == null || checkpoint.equals(""))
01039               {
01040                 logger.warn("Enabling backend " + backendName
01041                     + " with no checkpoint.");
01042                 enableBackend(dbe.getName());
01043               }
01044               else
01045               {
01046                 logger.info("Enabling backend " + backendName
01047                     + " from checkpoint " + checkpoint);
01048                 enableBackendFromCheckpoint(dbe.getName(), checkpoint);
01049               }
01050               continue;
01051             case BackendState.UNKNOWN :
01052               logger.info("Unknown last state for backend " + backendName
01053                   + ". Leaving node in "
01054                   + (dbe.isReadEnabled() ? "enabled" : "disabled") + " state.");
01055               continue;
01056             case BackendState.BACKUPING :
01057             case BackendState.DISABLING :
01058             case BackendState.RECOVERING :
01059             case BackendState.REPLAYING :
01060               logger.info("Unexpected transition state ("
01061                   + info.getBackendState() + ") for backend " + backendName
01062                   + ". Leaving node in "
01063                   + (dbe.isReadEnabled() ? "enabled" : "disabled") + " state.");
01064               continue;
01065             default :
01066               logger.info("Trying to enable a backend " + backendName
01067                   + " that is already in an enabled state.");
01068               break;
01069           }
01070         }
01071       }
01072       catch (Exception e)
01073       {
01074         throw new VirtualDatabaseException(e.getMessage());
01075       }
01076     }
01077 
01078   }
01079 
01080   /**
01081    * Prepare this virtual database for startup. This turns on all the backends
01082    * from the given checkpoint. If the checkpoint is null or an empty String,
01083    * the backends are enabled without further check else the backend states are
01084    * overriden to use the provided checkpoint.
01085    * 
01086    * @param checkpoint checkpoint for recovery log
01087    * @throws VirtualDatabaseException if fails
01088    */
01089   public void forceEnableAllBackendsFromCheckpoint(String checkpoint)
01090       throws VirtualDatabaseException
01091   {
01092     if (checkpoint == null || checkpoint.equals(""))
01093       enableAllBackends();
01094     else
01095     {
01096       try
01097       {
01098         int size = this.backends.size();
01099         DatabaseBackend backend;
01100         for (int i = 0; i < size; i++)
01101         {
01102           backend = (DatabaseBackend) backends.get(i);
01103           if (!backend.isReadEnabled())
01104           {
01105             backend.setLastKnownCheckpoint(checkpoint);
01106             enableBackendFromCheckpoint(backend.getName(), checkpoint);
01107           }
01108         }
01109       }
01110       catch (Exception e)
01111       {
01112         throw new VirtualDatabaseException(e.getMessage());
01113       }
01114     }
01115   }
01116 
01117   /**
01118    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getAllBackendNames()
01119    */
01120   public ArrayList getAllBackendNames() throws VirtualDatabaseException
01121   {
01122     try
01123     {
01124       acquireReadLockBackendLists();
01125     }
01126     catch (InterruptedException e)
01127     {
01128       String msg = "Unable to acquire read lock on backend list in getAllBackendNames ("
01129           + e + ")";
01130       logger.error(msg);
01131       throw new VirtualDatabaseException(msg);
01132     }
01133 
01134     int size = backends.size();
01135     ArrayList result = new ArrayList();
01136     for (int i = 0; i < size; i++)
01137     {
01138       result.add(((DatabaseBackend) backends.get(i)).getName());
01139     }
01140 
01141     releaseReadLockBackendLists();
01142     return result;
01143   }
01144 
01145   /**
01146    * Find the DatabaseBackend corresponding to the given backend name and check
01147    * if it is possible to disable this backend. In the case enable, this method
01148    * also updates the virtual database schema by merging it with the one
01149    * provided by this backend.
01150    * 
01151    * @param backendName backend to look for
01152    * @param testEnable NO_CHECK_BACKEND no check is done, if
01153    *          CHECK_BACKEND_DISABLE check for disable, if CHECK_BACKEND_ENABLE
01154    *          check for enable, else do not check
01155    * @return the backend to disable
01156    * @throws VirtualDatabaseException if an error occurs
01157    */
01158   public DatabaseBackend getAndCheckBackend(String backendName, int testEnable)
01159       throws VirtualDatabaseException
01160   {
01161     try
01162     {
01163       acquireReadLockBackendLists();
01164     }
01165     catch (InterruptedException e)
01166     {
01167       String msg = "Unable to acquire read lock on backend list in getAndCheckBackend ("
01168           + e + ")";
01169       logger.error(msg);
01170       throw new VirtualDatabaseException(msg);
01171     }
01172 
01173     // Find the backend
01174     int size = backends.size();
01175     DatabaseBackend b = null;
01176     for (int i = 0; i < size; i++)
01177     {
01178       b = (DatabaseBackend) backends.get(i);
01179       if (b.getName().equals(backendName))
01180         break;
01181       else
01182         b = null;
01183     }
01184 
01185     // Check not null
01186     if (b == null)
01187     {
01188       releaseReadLockBackendLists();
01189       String msg = "Trying to access a non-existing backend " + backendName;
01190       logger.warn(msg);
01191       throw new VirtualDatabaseException(msg);
01192     }
01193 
01194     // Check enable/disable
01195     switch (testEnable)
01196     {
01197       case NO_CHECK_BACKEND :
01198         break;
01199       case CHECK_BACKEND_DISABLE :
01200         if (!b.isReadEnabled())
01201         {
01202           releaseReadLockBackendLists();
01203           String msg = "Backend " + backendName + " is already disabled";
01204           logger.warn(msg);
01205           throw new VirtualDatabaseException(msg);
01206         }
01207         break;
01208       case CHECK_BACKEND_ENABLE :
01209         if (b.isReadEnabled())
01210         {
01211           releaseReadLockBackendLists();
01212           String msg = "Backend " + backendName + " is already enabled";
01213           logger.warn(msg);
01214           throw new VirtualDatabaseException(msg);
01215         }
01216         break;
01217       default :
01218         releaseReadLockBackendLists();
01219         String msg = "Unexpected parameter in getAndCheckBackend(...)";
01220         logger.error(msg);
01221         throw new VirtualDatabaseException(msg);
01222     }
01223 
01224     releaseReadLockBackendLists();
01225 
01226     if (testEnable == CHECK_BACKEND_ENABLE)
01227     {
01228       // Initialize backend for enable
01229       try
01230       {
01231         if (logger.isDebugEnabled())
01232           logger.debug("Initializing connections for backend " + b.getName());
01233         b.initializeConnections();
01234 
01235         b.checkDriverCompliance();
01236 
01237         if (logger.isDebugEnabled())
01238           logger.debug("Checking schema for backend " + b.getName());
01239         b.checkDatabaseSchema();
01240 
01241         DatabaseSchema backendSchema = b.getDatabaseSchema();
01242 
01243         if (backendSchema != null)
01244           requestManager.mergeDatabaseSchema(backendSchema);
01245         else
01246           logger.warn("Backend " + b.getName() + " has no defined schema.");
01247       }
01248       catch (SQLException e)
01249       {
01250         String msg = "Error while initalizing database backend " + b.getName()
01251             + " (" + e + ")";
01252         logger.warn(msg, e);
01253         throw new VirtualDatabaseException(msg);
01254       }
01255     }
01256 
01257     return b;
01258   }
01259 
01260   /**
01261    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#replicateBackend(java.lang.String,
01262    *      java.lang.String, java.util.Map)
01263    */
01264   public void replicateBackend(String backendName, String newBackendName,
01265       Map parameters) throws VirtualDatabaseException
01266   {
01267     // Access the backend we want to replicate
01268     DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
01269     DatabaseBackend newBackend = null;
01270 
01271     // Create a clone of the backend with additionnal parameters
01272     try
01273     {
01274       newBackend = backend.copy(newBackendName, parameters);
01275     }
01276     catch (Exception e)
01277     {
01278       String msg = Translate.get("virtualdatabase.fail.backend.copy");
01279       logger.warn(msg, e);
01280       throw new VirtualDatabaseException(msg);
01281     }
01282 
01283     // Add the backend to the virtual database.
01284     addBackend(newBackend);
01285   }
01286 
01287   /**
01288    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#removeBackend(java.lang.String)
01289    */
01290   public void removeBackend(String backend) throws VirtualDatabaseException
01291   {
01292     removeBackend(getAndCheckBackend(backend, NO_CHECK_BACKEND));
01293   }
01294 
01295   /**
01296    * Remove a backend from this virtual database.
01297    * 
01298    * @param db the database backend to remove
01299    * @throws VirtualDatabaseException if an error occurs
01300    */
01301   public void removeBackend(DatabaseBackend db) throws VirtualDatabaseException
01302   {
01303     if (db == null)
01304     {
01305       String msg = "Illegal null database backend in removeBackend(DatabaseBackend) method";
01306       logger.error(msg);
01307       throw new VirtualDatabaseException(msg);
01308     }
01309 
01310     try
01311     {
01312       rwLock.acquireWrite();
01313     }
01314     catch (InterruptedException e)
01315     {
01316       String msg = Translate.get(
01317           "loadbalancer.backendlist.acquire.writelock.failed", e);
01318       logger.error(msg);
01319       throw new VirtualDatabaseException(msg);
01320     }
01321 
01322     // Sanity checks
01323     int idx = backends.indexOf(db);
01324     if (idx == -1)
01325     {
01326       rwLock.releaseWrite(); // Release the lock
01327       String msg = "Trying to remove a non-existing backend " + db.getName();
01328       logger.warn(msg);
01329       throw new VirtualDatabaseException(msg);
01330     }
01331 
01332     if (((DatabaseBackend) backends.get(idx)).isReadEnabled())
01333     {
01334       rwLock.releaseWrite(); // Release the lock
01335       String msg = "Trying to remove an enabled backend " + db.getName();
01336       logger.error(msg);
01337       throw new VirtualDatabaseException(msg);
01338     }
01339 
01340     // Remove it
01341     backends.remove(idx);
01342     rwLock.releaseWrite(); // Relase the lock
01343 
01344     // Send notification
01345     if (MBeanServerManager.isJmxEnabled())
01346     {
01347       // Send notification
01348       Hashtable data = new Hashtable();
01349       data.put(CjdbcNotificationList.DATA_DATABASE, this.name);
01350       data.put(CjdbcNotificationList.DATA_DRIVER, db.getDriverClassName());
01351       String checkpoint = db.getLastKnownCheckpoint();
01352       checkpoint = (checkpoint == null) ? "" : checkpoint;
01353       data.put(CjdbcNotificationList.DATA_CHECKPOINT, checkpoint);
01354       data.put(CjdbcNotificationList.DATA_NAME, db.getName());
01355       data.put(CjdbcNotificationList.DATA_URL, db.getURL());
01356       RmiConnector.broadcastNotification(this,
01357           CjdbcNotificationList.VIRTUALDATABASE_BACKEND_REMOVED,
01358           CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
01359               "notification.backend.removed", db.getName()), data);
01360 
01361       // Remove backend mbean to jmx server
01362       ObjectName objectName = JmxConstants.getDatabaseBackendObjectName(name,
01363           db.getName());
01364       try
01365       {
01366         MBeanServerManager.unregister(objectName);
01367       }
01368       catch (JmxException e1)
01369       {
01370         logger.error(Translate.get(
01371             "virtualdatabase.fail.unregister.backend.mbean", db.getName()), e1);
01372       }
01373     }
01374 
01375     if (logger.isDebugEnabled())
01376       logger.debug("Backend " + db.getName() + " removed successfully");
01377   }
01378 
01379   /**
01380    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#transferBackend(java.lang.String,
01381    *      java.lang.String)
01382    */
01383   public void transferBackend(String backend, String controllerDestination)
01384       throws VirtualDatabaseException
01385   {
01386     throw new VirtualDatabaseException("Cannot transfer backend to controller:"
01387         + controllerDestination + " because database is not distributed");
01388   }
01389 
01390   //
01391   // Backup & Checkpoint management
01392   //
01393 
01394   /**
01395    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#backupBackendWithCheckpoint(String,
01396    *      String, ArrayList)
01397    */
01398   public void backupBackendWithCheckpoint(String backendName,
01399       String checkpointName, ArrayList tables) throws VirtualDatabaseException
01400   {
01401     try
01402     {
01403       DatabaseBackend db = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
01404       requestManager.backupBackendWithCheckpoint(db, checkpointName, tables, db
01405           .isReadEnabled(), true, null);
01406     }
01407     catch (SQLException sql)
01408     {
01409       throw new VirtualDatabaseException(sql.getMessage());
01410     }
01411   }
01412 
01413   /**
01414    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getAvailableDumpFiles()
01415    */
01416   public File[] getAvailableDumpFiles()
01417   {
01418     File f = new File(getRequestManager().getBackupManager().getBackupDir());
01419     if (f.exists())
01420     {
01421       return f.listFiles(new FilenameFilter()
01422       {
01423         /**
01424          * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
01425          */
01426         public boolean accept(File dir, String name)
01427         {
01428           if (name.endsWith(Constants.ZIP_EXT))
01429             return true;
01430           else
01431             return false;
01432         }
01433       });
01434     }
01435     else
01436       return new File[0];
01437   }
01438 
01439   /**
01440    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#removeDumpFile(File)
01441    */
01442   public boolean removeDumpFile(File dumpFile)
01443   {
01444     logger.info(Translate.get("controller.remove.dump", dumpFile.getName()));
01445     if (!dumpFile.delete())
01446     {
01447       logger.info(Translate.get("controller.remove.dump.failed", dumpFile
01448           .getName()));
01449       dumpFile = new File(getRequestManager().getBackupManager().getBackupDir()
01450           + File.separatorChar + dumpFile.getName());
01451       logger.info(Translate.get("controller.remove.dump", dumpFile.getName()));
01452       if (!dumpFile.delete())
01453       {
01454         logger.info(Translate.get("controller.remove.dump.failed", dumpFile
01455             .getName()));
01456         return false;
01457       }
01458       else
01459         return true;
01460     }
01461     else
01462       return true;
01463   }
01464 
01465   /**
01466    * Remove a checkpoint from the recovery log of this virtual database
01467    * 
01468    * @param checkpointName to remove
01469    * @throws VirtualDatabaseException if an error occurs
01470    */
01471   public void removeCheckpoint(String checkpointName)
01472       throws VirtualDatabaseException
01473   {
01474     try
01475     {
01476       requestManager.removeCheckpoint(checkpointName);
01477     }
01478     catch (Exception e)
01479     {
01480       throw new VirtualDatabaseException(e.getMessage());
01481     }
01482   }
01483 
01484   /**
01485    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#restoreDumpOnBackend(java.lang.String,
01486    *      java.lang.String)
01487    */
01488   public void restoreDumpOnBackend(String databaseBackendName,
01489       String checkpointName) throws VirtualDatabaseException, BackupException,
01490       OctopusException
01491   {
01492     DatabaseBackend backend = getAndCheckBackend(databaseBackendName,
01493         NO_CHECK_BACKEND);
01494     // Backend cannot be null, otherwise the above throws an
01495     // VirtualDatabaseException
01496     requestManager.restoreBackendFromBackupCheckpoint(backend, checkpointName,
01497         true, null);
01498   }
01499 
01500   /**
01501    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#setBackendLastKnownCheckpoint
01502    */
01503   public void setBackendLastKnownCheckpoint(String backendName,
01504       String checkpoint) throws VirtualDatabaseException
01505   {
01506     AbstractRecoveryLog log = requestManager.getRecoveryLog();
01507     DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
01508     if (log == null)
01509       throw new VirtualDatabaseException(ExceptionTypes.NO_RECOVERY_LOG);
01510     else
01511     {
01512       if (!backend.isDisabled())
01513         throw new VirtualDatabaseException(
01514             "Cannot setLastKnownCheckpoint on a non-disabled backend");
01515       else
01516       {
01517         try
01518         {
01519           log.storeBackendRecoveryInfo(this.name,
01520               new BackendRecoveryInfo(backend.getName(), checkpoint, backend
01521                   .getStateValue(), this.name));
01522 
01523           backend.setLastKnownCheckpoint(checkpoint);
01524         }
01525         catch (SQLException e)
01526         {
01527           throw new VirtualDatabaseException(
01528               "Failed to store recovery info for backend '" + backendName
01529                   + "' (" + e + ")");
01530         }
01531       }
01532     }
01533   }
01534 
01535   /**
01536    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewCheckpointNames()
01537    */
01538   public ArrayList viewCheckpointNames()
01539   {
01540     try
01541     {
01542       AbstractRecoveryLog recoveryLog = requestManager.getRecoveryLog();
01543       if (recoveryLog == null)
01544         return new ArrayList();
01545       else
01546         return recoveryLog.getCheckpointNames();
01547     }
01548     catch (SQLException e)
01549     {
01550       return new ArrayList();
01551     }
01552   }
01553 
01554   //
01555   // Thread management mainly used by controller and monitoring
01556   //
01557 
01558   /**
01559    * Adds one to currentNbOfThreads. Warning! This method is not synchronized.
01560    */
01561   public void addCurrentNbOfThread()
01562   {
01563     currentNbOfThreads++;
01564   }
01565 
01566   /**
01567    * Method add an idle thread. Warning! This method must be called in a
01568    * synchronized block on activeThreads.
01569    */
01570   public void addIdleThread()
01571   {
01572     idleThreads++;
01573   }
01574 
01575   /**
01576    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getCurrentNbOfThreads()
01577    */
01578   public int getCurrentNbOfThreads()
01579   {
01580     return currentNbOfThreads;
01581   }
01582 
01583   /**
01584    * Returns the number of idle zorker threads. Warning! This method must be
01585    * called in a synchronized block on activeThreads.
01586    * 
01587    * @return int
01588    */
01589   public int getIdleThreads()
01590   {
01591     return idleThreads;
01592   }
01593 
01594   /**
01595    * Returns the maxNbOfThreads.
01596    * 
01597    * @return int maximum number of threads
01598    */
01599   public int getMaxNbOfThreads()
01600   {
01601     return maxNbOfThreads;
01602   }
01603 
01604   /**
01605    * Returns the maxThreadIdleTime.
01606    * 
01607    * @return long maximum thread idle time in ms
01608    */
01609   public long getMaxThreadIdleTime()
01610   {
01611     return maxThreadIdleTime;
01612   }
01613 
01614   /**
01615    * Returns the minNbOfThreads.
01616    * 
01617    * @return int minimum number of threads
01618    */
01619   public int getMinNbOfThreads()
01620   {
01621     return minNbOfThreads;
01622   }
01623 
01624   /**
01625    * Returns the poolConnectionThreads.
01626    * 
01627    * @return boolean true if threads are pooled
01628    */
01629   public boolean isPoolConnectionThreads()
01630   {
01631     return poolConnectionThreads;
01632   }
01633 
01634   /**
01635    * Substract one to currentNbOfThreads. Warning! This method is not
01636    * synchronized.
01637    */
01638   public void removeCurrentNbOfThread()
01639   {
01640     currentNbOfThreads--;
01641   }
01642 
01643   /**
01644    * Remove an idle thread. Warning! This method must be called in a
01645    * synchronized block on activeThreads.
01646    */
01647   public void removeIdleThread()
01648   {
01649     idleThreads--;
01650   }
01651 
01652   /**
01653    * Sets the maxThreadIdleTime.
01654    * 
01655    * @param maxThreadIdleTime The maxThreadIdleTime to set
01656    */
01657   public void setMaxThreadIdleTime(long maxThreadIdleTime)
01658   {
01659     this.maxThreadIdleTime = maxThreadIdleTime;
01660   }
01661 
01662   /**
01663    * Sets the minNbOfThreads.
01664    * 
01665    * @param minNbOfThreads The minNbOfThreads to set
01666    */
01667   public void setMinNbOfThreads(int minNbOfThreads)
01668   {
01669     this.minNbOfThreads = minNbOfThreads;
01670   }
01671 
01672   /**
01673    * Sets the poolConnectionThreads.
01674    * 
01675    * @param poolConnectionThreads The poolConnectionThreads to set
01676    */
01677   public void setPoolConnectionThreads(boolean poolConnectionThreads)
01678   {
01679     this.poolConnectionThreads = poolConnectionThreads;
01680   }
01681 
01682   //
01683   // Getter/Setter and tools (equals, ...)
01684   //
01685 
01686   /**
01687    * Returns the activeThreads.
01688    * 
01689    * @return ArrayList
01690    */
01691   public ArrayList getActiveThreads()
01692   {
01693     return activeThreads;
01694   }
01695 
01696   /**
01697    * Returns the authentication manager of this virtual database.
01698    * 
01699    * @return an <code>AuthenticationManager</code> instance
01700    */
01701   public AuthenticationManager getAuthenticationManager()
01702   {
01703     return authenticationManager;
01704   }
01705 
01706   /**
01707    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendInformation(String)
01708    */
01709   public String getBackendInformation(String backendName)
01710       throws VirtualDatabaseException
01711   {
01712     try
01713     {
01714       acquireReadLockBackendLists();
01715     }
01716     catch (InterruptedException e)
01717     {
01718       String msg = "Unable to acquire read lock on backend list in getBackendInformation ("
01719           + e + ")";
01720       logger.error(msg);
01721       throw new VirtualDatabaseException(msg);
01722     }
01723 
01724     // Find the backend
01725     int size = backends.size();
01726     DatabaseBackend b = null;
01727     for (int i = 0; i < size; i++)
01728     {
01729       b = (DatabaseBackend) backends.get(i);
01730       if (b.getName().equals(backendName))
01731         break;
01732       else
01733         b = null;
01734     }
01735 
01736     if (b == null)
01737     {
01738       releaseReadLockBackendLists();
01739       String msg = "Backend " + backendName + " does not exists.";
01740       logger.warn(msg);
01741       throw new VirtualDatabaseException(msg);
01742     }
01743 
01744     releaseReadLockBackendLists();
01745     return b.getXml();
01746   }
01747 
01748   /**
01749    * Return the list of all backends
01750    * 
01751    * @return <code>ArrayList</code> of <code>DatabaseBackend</code> Objects
01752    */
01753   public ArrayList getBackends()
01754   {
01755     return backends;
01756   }
01757 
01758   /**
01759    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendSchema(java.lang.String)
01760    */
01761   public String getBackendSchema(String backendName)
01762       throws VirtualDatabaseException
01763   {
01764     DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
01765     // we know the backend is not null, otherwise we have a
01766     // VirtualDatabaseException ...
01767     try
01768     {
01769       return XmlTools.prettyXml(backend.getSchemaXml(true));
01770     }
01771     catch (Exception e)
01772     {
01773       throw new VirtualDatabaseException(e.getMessage());
01774     }
01775   }
01776 
01777   /**
01778    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#getBackendState(java.lang.String)
01779    */
01780   public String getBackendState(String backendName)
01781       throws VirtualDatabaseException
01782   {
01783     DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
01784     return backend.getState();
01785   }
01786 
01787   /**
01788    * Return the BLOB filter used for this database
01789    * 
01790    * @return the BLOB filter used for this database.
01791    */
01792   public AbstractBlobFilter getBlobFilter()
01793   {
01794     return blobFilter;
01795   }
01796 
01797   /**
01798    * Gets the virtual database name to be used by the client (C-JDBC driver)
01799    * This method should be used for local references only (it is faster). For
01800    * remote RMI calls, use {@link #getVirtualDatabaseName()}.
01801    * 
01802    * @return the virtual database name
01803    * @see VirtualDatabase#getVirtualDatabaseName()
01804    */
01805   public String getDatabaseName()
01806   {
01807     return name;
01808   }
01809 
01810   /**
01811    * @see org.objectweb.cjdbc.driver.DatabaseMetaData#getDatabaseProductName()
01812    */
01813   public String getDatabaseProductName()
01814   {
01815     return databaseProductNames;
01816   }
01817 
01818   /**
01819    * @see org.objectweb.cjdbc.driver.DatabaseMetaData
01820    * @return associated metada for this database
01821    */
01822   public VirtualDatabaseDynamicMetaData getDynamicMetaData()
01823   {
01824     if (metadata == null)
01825     {
01826       metadata = new VirtualDatabaseDynamicMetaData(this);
01827     }
01828     return metadata;
01829   }
01830 
01831   /**
01832    * Get the current database schema from merging the schemas of all active
01833    * backends. This is needed when a backend is disabled.
01834    * 
01835    * @return the current database schema dynamically gathered
01836    * @throws SQLException if an error occurs
01837    */
01838   public DatabaseSchema getDatabaseSchemaFromActiveBackendsAndRefreshDatabaseProductNames()
01839       throws SQLException
01840   {
01841     try
01842     {
01843       acquireReadLockBackendLists();
01844     }
01845     catch (InterruptedException e)
01846     {
01847       String msg = "Unable to acquire read lock on backend list in getDatabaseSchemaFromActiveBackends ("
01848           + e + ")";
01849       logger.error(msg);
01850       throw new SQLException(msg);
01851     }
01852     // Build the new schema from all active backend's schemas
01853     int size = backends.size();
01854     DatabaseSchema schema = null;
01855     DatabaseBackend b = null;
01856     String dbProductNames = "C-JDBC";
01857     for (int i = 0; i < size; i++)
01858     {
01859       b = (DatabaseBackend) backends.get(i);
01860       if (b.isReadEnabled())
01861       {
01862         // When no parsing is required, the schema is not dynamically updated
01863         // and therefore we need to force the refresh.
01864         if (requestManager.getRequiredParsingGranularity() == ParsingGranularities.NO_PARSING)
01865           b.refreshSchema();
01866         if (schema == null)
01867           schema = new DatabaseSchema(b.getDatabaseSchema());
01868         else
01869           schema.mergeSchema(b.getDatabaseSchema());
01870       }
01871 
01872       // Update the list of database product names
01873       if (dbProductNames.indexOf(b.getDatabaseProductName()) == -1)
01874         dbProductNames += "," + b.getDatabaseProductName();
01875     }
01876 
01877     releaseReadLockBackendLists();
01878     databaseProductNames = dbProductNames;
01879     if (logger.isDebugEnabled())
01880       logger.debug("getDatabaseSchemaFromActiveBackends - end");
01881     if (requestManager.getRecoveryLog() != null)
01882     {
01883       try
01884       {
01885         JDBCRecoveryLog log = (JDBCRecoveryLog) requestManager.getRecoveryLog();
01886         // Remove recovery log tables from schema
01887         if (schema.hasTable(log.getBackendTableName()))
01888           schema.removeTable(new DatabaseTable(log.getBackendTableName()));
01889         if (schema.hasTable(log.getCheckpointTableName()))
01890           schema.removeTable(new DatabaseTable(log.getCheckpointTableName()));
01891         if (schema.hasTable(log.getLogTableName()))
01892           schema.removeTable(new DatabaseTable(log.getLogTableName()));
01893       }
01894       catch (RuntimeException ignore)
01895       {
01896         // Should be a ClassCastException
01897       }
01898     }
01899     return schema;
01900   }
01901 
01902   /**
01903    * Returns the maxNbOfConnections.
01904    * 
01905    * @return int
01906    */
01907   public int getMaxNbOfConnections()
01908   {
01909     return maxNbOfConnections;
01910   }
01911 
01912   /**
01913    * Returns the pendingConnections.
01914    * 
01915    * @return ArrayList
01916    */
01917   public ArrayList getPendingConnections()
01918   {
01919     return pendingConnections;
01920   }
01921 
01922   /**
01923    * Gets the request manager associated to this database.
01924    * 
01925    * @return a <code>RequestManager</code> instance
01926    */
01927   public RequestManager getRequestManager()
01928   {
01929     return requestManager;
01930   }
01931 
01932   /**
01933    * Get the static metadata for this virtual database. A new one is created
01934    * lazily if none is found.
01935    * 
01936    * @return Virtual database static metadata
01937    */
01938   public VirtualDatabaseStaticMetaData getStaticMetaData()
01939   {
01940     if (staticMetadata == null)
01941     {
01942       staticMetadata = new VirtualDatabaseStaticMetaData(this);
01943     }
01944     return staticMetadata;
01945   }
01946 
01947   /**
01948    * Gets the virtual database name to be used by the client (C-JDBC driver)
01949    * 
01950    * @return the virtual database name
01951    */
01952   public String getVirtualDatabaseName()
01953   {
01954     return name;
01955   }
01956 
01957   /**
01958    * Returns the current SQL monitor
01959    * 
01960    * @return a <code>SQLMonitoring</code> instance or null if no monitor is
01961    *         defined
01962    */
01963   public SQLMonitoring getSQLMonitor()
01964   {
01965     return sqlMonitor;
01966   }
01967 
01968   /**
01969    * Return the sql short form length to use when reporting an error.
01970    * 
01971    * @return sql short form length
01972    * @see org.objectweb.cjdbc.common.sql.AbstractRequest#getSQLShortForm(int)
01973    */
01974   public int getSQLShortFormLength()
01975   {
01976     return sqlShortFormLength;
01977   }
01978 
01979   /**
01980    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#hasRecoveryLog()
01981    */
01982   public boolean hasRecoveryLog()
01983   {
01984     AbstractRecoveryLog log = requestManager.getRecoveryLog();
01985     if (log == null)
01986       return false;
01987     else
01988       return true;
01989   }
01990 
01991   /**
01992    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#hasResultCache()
01993    */
01994   public boolean hasResultCache()
01995   {
01996     AbstractResultCache cache = requestManager.getResultCache();
01997     if (cache == null)
01998       return false;
01999     else
02000       return true;
02001   }
02002 
02003   /**
02004    * Sets the authentication manager for this virtual database.
02005    * 
02006    * @param authenticationManager the <code>AuthenticationManager</code> to
02007    *          set
02008    */
02009   public void setAuthenticationManager(
02010       AuthenticationManager authenticationManager)
02011   {
02012     this.authenticationManager = authenticationManager;
02013   }
02014 
02015   /**
02016    * Set the BLOB filter to use for this database.
02017    * 
02018    * @param filter the filter to use.
02019    */
02020   public void setBlobFilter(AbstractBlobFilter filter)
02021   {
02022     this.blobFilter = filter;
02023   }
02024 
02025   /**
02026    * Sets a new database schema for this database if no one exist or merge the
02027    * given schema to the existing one. A static schema can only be replaced by
02028    * another static schema.
02029    * 
02030    * @param schema the new database shema
02031    * @param isStatic <code>true</code> if the schema should be static
02032    */
02033   public void setDatabaseSchema(DatabaseSchema schema, boolean isStatic)
02034   {
02035     if (requestManager != null)
02036       requestManager.setDatabaseSchema(schema, isStatic);
02037     else
02038       logger
02039           .warn("Unable to set database schema, no request manager has been defined.");
02040   }
02041 
02042   /**
02043    * Sets the maxNbOfConnections.
02044    * 
02045    * @param maxNbOfConnections The maxNbOfConnections to set
02046    */
02047   public void setMaxNbOfConnections(int maxNbOfConnections)
02048   {
02049     this.maxNbOfConnections = maxNbOfConnections;
02050   }
02051 
02052   /**
02053    * Sets the maxNbOfThreads.
02054    * 
02055    * @param maxNbOfThreads The maxNbOfThreads to set
02056    */
02057   public void setMaxNbOfThreads(int maxNbOfThreads)
02058   {
02059     this.maxNbOfThreads = maxNbOfThreads;
02060   }
02061 
02062   /**
02063    * Sets a new request manager for this database.
02064    * 
02065    * @param requestManager the new request manager.
02066    */
02067   public void setRequestManager(RequestManager requestManager)
02068   {
02069     this.requestManager = requestManager;
02070   }
02071 
02072   /**
02073    * Sets a new SQL Monitor
02074    * 
02075    * @param sqlMonitor the new SQL monitor
02076    */
02077   public void setSQLMonitor(SQLMonitoring sqlMonitor)
02078   {
02079     this.sqlMonitor = sqlMonitor;
02080   }
02081 
02082   /**
02083    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#setMonitoringToActive(boolean)
02084    */
02085   public void setMonitoringToActive(boolean active)
02086       throws VirtualDatabaseException
02087   {
02088     if (sqlMonitor == null)
02089       throw new VirtualDatabaseException(Translate
02090           .get("virtualdatabase.monitoring.not.defined"));
02091     else
02092       sqlMonitor.setActive(active);
02093   }
02094 
02095   /**
02096    * Two virtual databases are equal if they have the same name and group.
02097    * 
02098    * @param other the object to compare with
02099    * @return <code>true</code> if the two virtual databases are equals
02100    */
02101   public boolean equals(Object other)
02102   {
02103     if ((other == null) || (!(other instanceof VirtualDatabase)))
02104       return false;
02105     else
02106     {
02107       VirtualDatabase db = (VirtualDatabase) other;
02108       return name.equals(db.getDatabaseName());
02109     }
02110   }
02111 
02112   ///////////////////////////////////////////
02113   // JMX
02114   //////////////////////////////////////////
02115 
02116   /**
02117    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#cleanMonitoringData()
02118    */
02119   public void cleanMonitoringData() throws VirtualDatabaseException
02120   {
02121     if (sqlMonitor == null)
02122       throw new VirtualDatabaseException(Translate
02123           .get("virtualdatabase.monitoring.not.defined"));
02124     else
02125       sqlMonitor.cleanStats();
02126   }
02127 
02128   /**
02129    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#retrieveBackendsData()
02130    */
02131   public String[][] retrieveBackendsData() throws VirtualDatabaseException
02132   {
02133     try
02134     {
02135       acquireReadLockBackendLists();
02136     }
02137     catch (InterruptedException e)
02138     {
02139       String msg = Translate.get("virtualdatabase.fail.read.lock", e);
02140       throw new VirtualDatabaseException(msg);
02141     }
02142     ArrayList backends = this.getBackends();
02143     int backendListSize = backends.size();
02144     String[][] data = new String[backendListSize][];
02145     for (int i = 0; i < backendListSize; i++)
02146     {
02147       data[i] = ((DatabaseBackend) backends.get(i)).getBackendData();
02148     }
02149     releaseReadLockBackendLists();
02150     return data;
02151   }
02152 
02153   /**
02154    * Return true if this database is shutting down.
02155    * 
02156    * @return true if this database is shutting down.
02157    */
02158   public boolean isShuttingDown()
02159   {
02160     return shuttingDown;
02161   }
02162 
02163   /**
02164    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#shutdown(int)
02165    */
02166   public void shutdown(int level)
02167   {
02168     VirtualDatabaseShutdownThread vdst = null;
02169     synchronized (this)
02170     {
02171       if (shuttingDown)
02172         return;
02173       switch (level)
02174       {
02175         case Constants.SHUTDOWN_WAIT :
02176           vdst = new VirtualDatabaseWaitShutdownThread(this);
02177           logger.info(Translate.get("controller.shutdown.type.wait", this
02178               .getVirtualDatabaseName()));
02179           break;
02180         case Constants.SHUTDOWN_SAFE :
02181           shuttingDown = true;
02182           vdst = new VirtualDatabaseSafeShutdownThread(this);
02183           logger.info(Translate.get("controller.shutdown.type.safe", this
02184               .getVirtualDatabaseName()));
02185           break;
02186         case Constants.SHUTDOWN_FORCE :
02187           shuttingDown = true;
02188           vdst = new VirtualDatabaseForceShutdownThread(this);
02189           logger.warn(Translate.get("controller.shutdown.type.force", this
02190               .getVirtualDatabaseName()));
02191           break;
02192         default :
02193           String msg = Translate
02194               .get("controller.shutdown.unknown.level", level);
02195           logger.error(msg);
02196           throw new RuntimeException(msg);
02197       }
02198     }
02199 
02200     new Thread(vdst.getShutdownGroup(), vdst, "VirtualDatabase Shutdown Thread")
02201         .start();
02202   }
02203 
02204   /**
02205    * Write the checkpoints for all backends on the recovery log
02206    */
02207   public void storeBackendsInfo()
02208   {
02209     requestManager.storeBackendsInfo(this.name, getBackends());
02210   }
02211 
02212   /**
02213    * Get all users connected to that database
02214    * 
02215    * @return an <code>ArrayList</code> of strings containing the clients
02216    *         username
02217    */
02218   public ArrayList viewAllClientNames()
02219   {
02220     ArrayList list = this.getActiveThreads();
02221     int size = list.size();
02222     ArrayList clients = new ArrayList(size);
02223     for (int i = 0; i < list.size(); i++)
02224       clients.add(((VirtualDatabaseWorkerThread) list.get(i)).getUser());
02225     return clients;
02226   }
02227 
02228   /**
02229    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewBackendInformation(java.lang.String)
02230    */
02231   public String[] viewBackendInformation(String backendName)
02232       throws VirtualDatabaseException
02233   {
02234     DatabaseBackend backend = getAndCheckBackend(backendName, NO_CHECK_BACKEND);
02235     return backend.getBackendData();
02236   }
02237 
02238   /**
02239    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewControllerList()
02240    */
02241   public String[] viewControllerList()
02242   {
02243     return new String[]{viewOwningController()};
02244   }
02245 
02246   /**
02247    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewGroupBackends()
02248    */
02249   public Hashtable viewGroupBackends() throws VirtualDatabaseException
02250   {
02251     Hashtable map = new Hashtable();
02252     map.put(controller.getJmxName(), getAllBackendNames());
02253     return map;
02254   }
02255 
02256   /**
02257    * @see org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean#viewOwningController()
02258    */
02259   public String viewOwningController()
02260   {
02261     return controller.getJmxName();
02262   }
02263 
02264   /**
02265    * @see org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean#getAssociatedString()
02266    */
02267   public String getAssociatedString()
02268   {
02269     return "virtualdatabase";
02270   }
02271 
02272   /**
02273    * Retrieves this <code>VirtualDatabase</code> object in xml format
02274    * 
02275    * @return xml formatted string that conforms to c-jdbc.dtd
02276    */
02277   public String getXml()
02278   {
02279     StringBuffer info = new StringBuffer();
02280     info.append("<C-JDBC>");
02281     info.append("<" + DatabasesXmlTags.ELT_VirtualDatabase + " "
02282         + DatabasesXmlTags.ATT_name + "=\"" + this.getVirtualDatabaseName()
02283         + "\" " + DatabasesXmlTags.ATT_maxNbOfConnections + "=\""
02284         + this.getMaxNbOfConnections() + "\" "
02285         + DatabasesXmlTags.ATT_poolThreads + "=\""
02286         + this.isPoolConnectionThreads() + "\" "
02287         + DatabasesXmlTags.ATT_minNbOfThreads + "=\""
02288         + this.getMinNbOfThreads() + "\" "
02289         + DatabasesXmlTags.ATT_maxNbOfThreads + "=\""
02290         + this.getMaxNbOfThreads() + "\" "
02291         + DatabasesXmlTags.ATT_maxThreadIdleTime + "=\""
02292         + this.getMaxThreadIdleTime() / 1000 + "\" "
02293         + DatabasesXmlTags.ATT_sqlDumpLength + "=\"" + this.sqlShortFormLength
02294         + "\" " + DatabasesXmlTags.ATT_blobEncodingMethod + "=\""
02295         + this.blobFilter.getXml() + "\">");
02296 
02297     info.append(getDistributionXml());
02298 
02299     if (this.getSQLMonitor() != null)
02300       info.append(sqlMonitor.getXml());
02301 
02302     info.append(requestManager.getBackupManager().getXml());
02303 
02304     if (this.getAuthenticationManager() != null)
02305       info.append(authenticationManager.getXml());
02306 
02307     try
02308     {
02309       acquireReadLockBackendLists();
02310       int size = backends.size();
02311       for (int i = 0; i < size; i++)
02312         info.append(((DatabaseBackend) backends.get(i)).getXml());
02313       releaseReadLockBackendLists();
02314     }
02315     catch (InterruptedException e)
02316     {
02317       logger.error(Translate.get("virtualdatabase.fail.read.lock", e));
02318     }
02319     if (requestManager != null)
02320       info.append(requestManager.getXml());
02321     info.append("</" + DatabasesXmlTags.ELT_VirtualDatabase + ">");
02322     info.append("</C-JDBC>");
02323     return info.toString();
02324   }
02325 
02326   /**
02327    * Get the XML dump of the Distribution element if any.
02328    * 
02329    * @return ""
02330    */
02331   protected String getDistributionXml()
02332   {
02333     return "";
02334   }
02335 
02336 }

Generated on Mon Apr 11 22:01:35 2005 for C-JDBC by  doxygen 1.3.9.1